[PATCH net-next v3] mptcp: normalize seq numbers reported in mptcp_info

Kalpan Jani posted 1 patch 2 days, 18 hours ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/multipath-tcp/mptcp_net-next tags/patchew/20260902102125.2035540-1-kalpan.jani@mpiricsoftware.com
There is a newer version of this series
net/mptcp/sockopt.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
[PATCH net-next v3] mptcp: normalize seq numbers reported in mptcp_info
Posted by Kalpan Jani 2 days, 18 hours ago
mptcpi_write_seq, mptcpi_snd_una and mptcpi_rcv_nxt report the raw
64-bit data sequence numbers, seeded from the connection's IDSN/IASN.
Since the IDSN/IASN come from mptcp_crypto_key_sha(), these fields
carry an effectively random offset and are not useful to userspace as
absolute values: a caller has to snapshot two getsockopt(MPTCP_INFO)
calls and subtract to get anything meaningful, which is exactly what
tools/testing/selftests/net/mptcp/mptcp_sockopt.c already does.

mptcp_info also reports mptcpi_bytes_sent, mptcpi_bytes_received and
mptcpi_bytes_acked, which give the same information as a plain byte
count starting at 0. The snapshot-and-diff workaround for the seq
fields is redundant once those are available.

Normalize mptcpi_write_seq, mptcpi_snd_una and mptcpi_rcv_nxt in
mptcp_diag_fill_info() by subtracting the local and remote initial
sequence numbers, read from msk->first's subflow context rather than
caching them on mptcp_sock, to avoid growing every mptcp_sock for a
diag-only need.

msk->first only changes when the whole msk is already in TCP_CLOSE,
confirmed by testing with a debug print in __mptcp_close_ssk() under
mptcp_join.sh: every NULL transition observed had msk_state ==
TCP_CLOSE, none while the connection was still established. So a
NULL msk->first at diag time just means write_seq/snd_una/ack_seq
are no longer meaningful anyway, and the subtraction safely no-ops
to 0 in that case.

subflow->idsn is set once at handshake time and never modified
afterwards, so it can be read directly. subflow->iasn is incremented
by one in subflow_set_remote_key() to account for the peer's virtual
SYN, and ack_seq carries that same increment, so the increment is
undone here (iasn - 1) to keep rcv_nxt normalized against the same
baseline write_seq and snd_una use. This mirrors the fix from v2,
which cached the pre-increment value directly instead.

Link: https://github.com/multipath-tcp/mptcp_net-next/issues/445
Signed-off-by: Kalpan Jani <kalpan.jani@mpiricsoftware.com>

---
Changes since v2:
- Dropped the msk->local_idsn/msk->remote_idsn fields entirely.
  Read idsn/iasn from msk->first's subflow context in
  mptcp_diag_fill_info() instead, per Matt's suggestion, to avoid
  growing mptcp_sock for a diag-only need. Verified msk->first only
  goes NULL as part of whole-msk teardown, not during a live
  multi-subflow connection.

Changes since v1:
- Cached msk->remote_idsn before subflow->iasn++ instead of after:
  the increment accounts for the peer's virtual SYN, and caching
  remote_idsn post-increment left mptcpi_rcv_nxt starting at 0 while
  mptcpi_write_seq/mptcpi_snd_una started at 1 for the same
  connection (reported by Sashiko).

v1: https://lore.kernel.org/all/20260825113355.3573376-1-kalpan.jani@mpiricsoftware.com/
v2: https://lore.kernel.org/all/20260827041058.2833707-1-kalpan.jani@mpiricsoftware.com/

---
 net/mptcp/sockopt.c | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index 922f6ae5c80cb..5026ccc55e230 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -1047,6 +1047,7 @@ static int mptcp_getsockopt_first_sf_only(struct mptcp_sock *msk, int level, int
 void mptcp_diag_fill_info(struct mptcp_sock *msk, struct mptcp_info *info)
 {
 	struct sock *sk = (struct sock *)msk;
+	u64 local_idsn = 0, remote_idsn = 0;
 	u32 flags = 0;
 	bool slow;
 	u32 now;
@@ -1084,9 +1085,22 @@ void mptcp_diag_fill_info(struct mptcp_sock *msk, struct mptcp_info *info)
 	info->mptcpi_flags = flags;
 
 	slow = lock_sock_fast(sk);
+
+	if (msk->first) {
+		struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(msk->first);
+
+		local_idsn = subflow->idsn;
+		/* subflow->iasn is incremented once in subflow_set_remote_key()
+		 * to account for the peer's virtual SYN; undo that here so
+		 * rcv_nxt normalizes against the same baseline write_seq and
+		 * snd_una use.
+		 */
+		remote_idsn = subflow->remote_key_valid ? subflow->iasn - 1 : 0;
+	}
+
 	info->mptcpi_csum_enabled = READ_ONCE(msk->csum_enabled);
 	info->mptcpi_token = msk->token;
-	info->mptcpi_write_seq = msk->write_seq;
+	info->mptcpi_write_seq = msk->write_seq - local_idsn;
 	info->mptcpi_retransmits = inet_csk(sk)->icsk_retransmits;
 	info->mptcpi_bytes_sent = msk->bytes_sent;
 	info->mptcpi_bytes_received = msk->bytes_received;
@@ -1100,8 +1114,8 @@ void mptcp_diag_fill_info(struct mptcp_sock *msk, struct mptcp_info *info)
 
 	mptcp_data_lock(sk);
 	info->mptcpi_last_ack_recv = jiffies_to_msecs(now - msk->last_ack_recv);
-	info->mptcpi_snd_una = msk->snd_una;
-	info->mptcpi_rcv_nxt = msk->ack_seq;
+	info->mptcpi_snd_una = msk->snd_una - local_idsn;
+	info->mptcpi_rcv_nxt = msk->ack_seq - remote_idsn;
 	info->mptcpi_bytes_acked = msk->bytes_acked;
 	mptcp_data_unlock(sk);
 }
-- 
2.43.0
Re: [PATCH net-next v3] mptcp: normalize seq numbers reported in mptcp_info
Posted by MPTCP CI 2 days, 17 hours ago
Hi Kalpan,

Thank you for your modifications, that's great!

Our CI did some validations and here is its report:

- KVM Validation: normal (except selftest_mptcp_join): Success! ✅
- KVM Validation: normal (only selftest_mptcp_join): Success! ✅
- KVM Validation: debug (except selftest_mptcp_join): Success! ✅
- KVM Validation: debug (only selftest_mptcp_join): Success! ✅
- KVM Validation: btf-normal (only bpftest_all): Success! ✅
- KVM Validation: btf-debug (only bpftest_all): Success! ✅
- Perf: 
- Task: https://github.com/multipath-tcp/mptcp_net-next/actions/runs/33621202423

Initiator: Patchew Applier
Commits: https://github.com/multipath-tcp/mptcp_net-next/commits/a9bc776c7de0
Patchwork: https://patchwork.kernel.org/project/mptcp/list/?series=1156037


If there are some issues, you can reproduce them using the same environment as
the one used by the CI thanks to a docker image, e.g.:

    $ cd [kernel source code]
    $ docker run -v "${PWD}:${PWD}:rw" -w "${PWD}" --privileged --rm -it \
        --pull always mptcp/mptcp-upstream-virtme-docker:latest \
        auto-normal

For more details:

    https://github.com/multipath-tcp/mptcp-upstream-virtme-docker


Please note that despite all the efforts that have been already done to have a
stable tests suite when executed on a public CI like here, it is possible some
reported issues are not due to your modifications. Still, do not hesitate to
help us improve that ;-)

Cheers,
MPTCP GH Action bot
Bot operated by Matthieu Baerts (NGI0 Core)
Re: [PATCH net-next v3] mptcp: normalize seq numbers reported in mptcp_info
Posted by sashiko-bot@kernel.org 2 days, 18 hours ago
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Critical] Changing existing UAPI sequence fields from absolute to relative breaks backwards compatibility.
- [High] Normalization falls back to 0 when `msk->first` is NULL, causing massive UAPI metric jumps for closed sockets.
- [Medium] Data race on `subflow->iasn` and `subflow->remote_key_valid` yields corrupted UAPI values.
--

commit 630c5d1d1841bed28f7903466f4319e79bf43c4c
Author: Kalpan Jani <kalpan.jani@mpiricsoftware.com>

mptcp: normalize seq numbers reported in mptcp_info

mptcpi_write_seq, mptcpi_snd_una and mptcpi_rcv_nxt report the raw
64-bit data sequence numbers, seeded from the connection's IDSN/IASN.
Since the IDSN/IASN come from mptcp_crypto_key_sha(), these fields
carry an effectively random offset and are not useful to userspace as
absolute values. This commit normalizes mptcpi_write_seq, mptcpi_snd_una
and mptcpi_rcv_nxt in mptcp_diag_fill_info() by subtracting the local and
remote initial sequence numbers.

Link: https://github.com/multipath-tcp/mptcp_net-next/issues/445

> diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
> index fcf6feb2a9eb5..88bd75672c96e 100644
> --- a/net/mptcp/sockopt.c
> +++ b/net/mptcp/sockopt.c
[ ... ]
> @@ -1008,9 +1009,22 @@ void mptcp_diag_fill_info(struct mptcp_sock *msk, struct mptcp_info *info)
>  	info->mptcpi_flags = flags;
>  
>  	slow = lock_sock_fast(sk);
> +
> +	if (msk->first) {
> +		struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(msk->first);
> +
> +		local_idsn = subflow->idsn;
> +		/* subflow->iasn is incremented once in subflow_set_remote_key()
> +		 * to account for the peer's virtual SYN; undo that here so
> +		 * rcv_nxt normalizes against the same baseline write_seq and
> +		 * snd_una use.
> +		 */
> +		remote_idsn = subflow->remote_key_valid ? subflow->iasn - 1 : 0;

[Severity: High]
When the MPTCP connection enters TCP_CLOSE, msk->first can be set to NULL.
When diagnostic queries execute mptcp_diag_fill_info(), local_idsn and
remote_idsn will remain 0.

Does subtracting 0 cause the UAPI output to abruptly revert to the raw 64-bit
absolute sequence numbers?

For diff-based monitoring tools relying on the new relative behavior, could
this cause an astronomical jump in reported sequence values?

[Severity: Medium]
Is there a data race when accessing subflow->remote_key_valid and subflow->iasn
here?

mptcp_diag_fill_info() accesses these fields under lock_sock_fast(), which only
acquires the process context lock. Concurrently, subflow_set_remote_key()
writes to these fields in softirq context under mptcp_data_lock():

net/mptcp/subflow.c:subflow_set_remote_key() {
	...
	subflow->remote_key_valid = 1;
	subflow->remote_key = mp_opt->sndr_key;
	mptcp_crypto_key_sha(subflow->remote_key, NULL, &subflow->iasn);
	subflow->iasn++;
	...
}

Because remote_key_valid is set to 1 before mptcp_crypto_key_sha() populates
iasn, can this diagnostic code read iasn while it contains garbage?

> +	}
> +
>  	info->mptcpi_csum_enabled = READ_ONCE(msk->csum_enabled);
>  	info->mptcpi_token = msk->token;
> -	info->mptcpi_write_seq = msk->write_seq;
> +	info->mptcpi_write_seq = msk->write_seq - local_idsn;

[Severity: Critical]
Does changing the established UAPI semantics of mptcpi_write_seq,
mptcpi_snd_una, and mptcpi_rcv_nxt from absolute cryptographic sequence numbers
to 0-indexed relative values cause a regression in the kernel's ABI?

Could any userspace application or tracing tool that relies on these fields to
correlate socket state with on-the-wire MPTCP packets (e.g., using tcpdump to
match DSS sequence numbers) break immediately?

>  	info->mptcpi_retransmits = inet_csk(sk)->icsk_retransmits;
>  	info->mptcpi_bytes_sent = msk->bytes_sent;
>  	info->mptcpi_bytes_received = msk->bytes_received;
[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260902102125.2035540-1-kalpan.jani@mpiricsoftware.com?part=1
Re: [PATCH net-next v3] mptcp: normalize seq numbers reported in mptcp_info
Posted by Matthieu Baerts 2 days, 18 hours ago
Hi Kalpan,

On 02/09/2026 12:36, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> - [Critical] Changing existing UAPI sequence fields from absolute to relative breaks backwards compatibility.
> - [High] Normalization falls back to 0 when `msk->first` is NULL, causing massive UAPI metric jumps for closed sockets.
> - [Medium] Data race on `subflow->iasn` and `subflow->remote_key_valid` yields corrupted UAPI values.
> --
> 
> commit 630c5d1d1841bed28f7903466f4319e79bf43c4c
> Author: Kalpan Jani <kalpan.jani@mpiricsoftware.com>
> 
> mptcp: normalize seq numbers reported in mptcp_info
> 
> mptcpi_write_seq, mptcpi_snd_una and mptcpi_rcv_nxt report the raw
> 64-bit data sequence numbers, seeded from the connection's IDSN/IASN.
> Since the IDSN/IASN come from mptcp_crypto_key_sha(), these fields
> carry an effectively random offset and are not useful to userspace as
> absolute values. This commit normalizes mptcpi_write_seq, mptcpi_snd_una
> and mptcpi_rcv_nxt in mptcp_diag_fill_info() by subtracting the local and
> remote initial sequence numbers.
> 
> Link: https://github.com/multipath-tcp/mptcp_net-next/issues/445
> 
>> diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
>> index fcf6feb2a9eb5..88bd75672c96e 100644
>> --- a/net/mptcp/sockopt.c
>> +++ b/net/mptcp/sockopt.c
> [ ... ]
>> @@ -1008,9 +1009,22 @@ void mptcp_diag_fill_info(struct mptcp_sock *msk, struct mptcp_info *info)
>>  	info->mptcpi_flags = flags;
>>  
>>  	slow = lock_sock_fast(sk);
>> +
>> +	if (msk->first) {
>> +		struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(msk->first);
>> +
>> +		local_idsn = subflow->idsn;
>> +		/* subflow->iasn is incremented once in subflow_set_remote_key()
>> +		 * to account for the peer's virtual SYN; undo that here so
>> +		 * rcv_nxt normalizes against the same baseline write_seq and
>> +		 * snd_una use.
>> +		 */
>> +		remote_idsn = subflow->remote_key_valid ? subflow->iasn - 1 : 0;
> 
> [Severity: High]
> When the MPTCP connection enters TCP_CLOSE, msk->first can be set to NULL.
> When diagnostic queries execute mptcp_diag_fill_info(), local_idsn and
> remote_idsn will remain 0.
> 
> Does subtracting 0 cause the UAPI output to abruptly revert to the raw 64-bit
> absolute sequence numbers?
> 
> For diff-based monitoring tools relying on the new relative behavior, could
> this cause an astronomical jump in reported sequence values?

I would say (without checking the details), in this case:

- either we return 0 for the 3 different values below

- or, when closing the first subflow, the 3 fields in the msk are
updated with the offset.

But, before that, is it still possible to retrieve info from this socket
after the destroy part? If I'm not mistaken, this destroy part is
reached only after a "close(fd)", not a disconnect or anything else. So
the userspace shouldn't reach it via a `getsockopt(MPTCP_INFO)` (or
similar).

I guess there is still possible to reach this code after the destroy
part when diag info are obtained from netlink (e.g. `ss`) in parallel.
But that seems to be more a race than a specific "post-close" query, no?
If yes, then the main point is not to crash. Then maybe a workaround is
enough, e.g. resetting fields, or modifying them with an offset.

> [Severity: Medium]
> Is there a data race when accessing subflow->remote_key_valid and subflow->iasn
> here?
> 
> mptcp_diag_fill_info() accesses these fields under lock_sock_fast(), which only
> acquires the process context lock. Concurrently, subflow_set_remote_key()
> writes to these fields in softirq context under mptcp_data_lock():
> 
> net/mptcp/subflow.c:subflow_set_remote_key() {
> 	...
> 	subflow->remote_key_valid = 1;
> 	subflow->remote_key = mp_opt->sndr_key;
> 	mptcp_crypto_key_sha(subflow->remote_key, NULL, &subflow->iasn);
> 	subflow->iasn++;
> 	...
> }
> 
> Because remote_key_valid is set to 1 before mptcp_crypto_key_sha() populates
> iasn, can this diagnostic code read iasn while it contains garbage?

Set "remote_key_valid = 1" after having set iasn?

> 
>> +	}
>> +
>>  	info->mptcpi_csum_enabled = READ_ONCE(msk->csum_enabled);
>>  	info->mptcpi_token = msk->token;
>> -	info->mptcpi_write_seq = msk->write_seq;
>> +	info->mptcpi_write_seq = msk->write_seq - local_idsn;
> 
> [Severity: Critical]
> Does changing the established UAPI semantics of mptcpi_write_seq,
> mptcpi_snd_una, and mptcpi_rcv_nxt from absolute cryptographic sequence numbers
> to 0-indexed relative values cause a regression in the kernel's ABI?
> 
> Could any userspace application or tracing tool that relies on these fields to
> correlate socket state with on-the-wire MPTCP packets (e.g., using tcpdump to
> match DSS sequence numbers) break immediately?

That's on purpose, adding a specific note in the commit message is
enough? People shouldn't expect these counters to be exactly the same as
on the wire.

Cheers,
Matt
-- 
Sponsored by the NGI0 Core fund.