* [PATCH mptcp-next RFC v4 1/2] mptcp: add MPFailFallback MIB
2026-08-18 9:14 [PATCH mptcp-next RFC v4 0/2] mptcp: fallback to TCP on MP_FAIL with a single subflow Chenguang Zhao
@ 2026-08-18 9:14 ` Chenguang Zhao
2026-08-18 9:24 ` sashiko-bot
2026-08-18 9:14 ` [PATCH mptcp-next RFC v4 2/2] mptcp: send MP_FAIL and infinite mapping on the same ACK Chenguang Zhao
2026-08-18 10:21 ` [PATCH mptcp-next RFC v4 0/2] mptcp: fallback to TCP on MP_FAIL with a single subflow MPTCP CI
2 siblings, 1 reply; 7+ messages in thread
From: Chenguang Zhao @ 2026-08-18 9:14 UTC (permalink / raw)
To: mptcp; +Cc: chenguang.zhao, Chenguang Zhao
From: Chenguang Zhao <zhaochenguang@kylinos.cn>
Add a counter for TCP fallback after accepting MP_FAIL.
Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
---
net/mptcp/mib.c | 1 +
net/mptcp/mib.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/net/mptcp/mib.c b/net/mptcp/mib.c
index 608cb568897c..648d4dbca764 100644
--- a/net/mptcp/mib.c
+++ b/net/mptcp/mib.c
@@ -88,6 +88,7 @@ static const struct snmp_mib mptcp_snmp_list[] = {
SNMP_MIB_ITEM("MD5SigFallback", MPTCP_MIB_MD5SIGFALLBACK),
SNMP_MIB_ITEM("MD5SigReset", MPTCP_MIB_MD5SIGRESET),
SNMP_MIB_ITEM("DssFallback", MPTCP_MIB_DSSFALLBACK),
+ SNMP_MIB_ITEM("MPFailFallback", MPTCP_MIB_MPFAILFALLBACK),
SNMP_MIB_ITEM("DssReset", MPTCP_MIB_DSSRESET),
SNMP_MIB_ITEM("SimultConnectFallback", MPTCP_MIB_SIMULTCONNFALLBACK),
SNMP_MIB_ITEM("FallbackFailed", MPTCP_MIB_FALLBACKFAILED),
diff --git a/net/mptcp/mib.h b/net/mptcp/mib.h
index 1ebdb55e9534..075e21e20a80 100644
--- a/net/mptcp/mib.h
+++ b/net/mptcp/mib.h
@@ -91,6 +91,7 @@ enum linux_mptcp_mib_field {
MPTCP_MIB_MD5SIGFALLBACK, /* Conflicting TCP option enabled */
MPTCP_MIB_MD5SIGRESET, /* MP_RST: MD5SIG enabled on listener */
MPTCP_MIB_DSSFALLBACK, /* Bad or missing DSS */
+ MPTCP_MIB_MPFAILFALLBACK, /* Received MP_FAIL, fallback */
MPTCP_MIB_DSSRESET, /* MP_RST: bad data mapping */
MPTCP_MIB_SIMULTCONNFALLBACK, /* Simultaneous connect */
MPTCP_MIB_FALLBACKFAILED, /* Can't fallback due to msk status */
--
2.25.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH mptcp-next RFC v4 1/2] mptcp: add MPFailFallback MIB
2026-08-18 9:14 ` [PATCH mptcp-next RFC v4 1/2] mptcp: add MPFailFallback MIB Chenguang Zhao
@ 2026-08-18 9:24 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-08-18 9:24 UTC (permalink / raw)
To: Chenguang Zhao; +Cc: mptcp
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The newly added MIB counter `MPTCP_MIB_MPFAILFALLBACK` is defined and exposed to userspace but never incremented anywhere in the codebase.
--
--- Patch [1]: [PATCH mptcp-next RFC v4 1/2] mptcp: add MPFailFallback MIB ---
commit da81a414aec944549813cb91e3dbd055d9e87a03
Author: Chenguang Zhao <zhaochenguang@kylinos.cn>
mptcp: add MPFailFallback MIB
This commit adds a new MIB counter for tracking TCP fallback after accepting
MP_FAIL.
> diff --git a/net/mptcp/mib.c b/net/mptcp/mib.c
> index 608cb568897cf..648d4dbca7646 100644
> --- a/net/mptcp/mib.c
> +++ b/net/mptcp/mib.c
[ ... ]
> @@ -88,6 +88,7 @@ static const struct snmp_mib mptcp_snmp_list[] = {
> SNMP_MIB_ITEM("MD5SigFallback", MPTCP_MIB_MD5SIGFALLBACK),
> SNMP_MIB_ITEM("MD5SigReset", MPTCP_MIB_MD5SIGRESET),
> SNMP_MIB_ITEM("DssFallback", MPTCP_MIB_DSSFALLBACK),
> + SNMP_MIB_ITEM("MPFailFallback", MPTCP_MIB_MPFAILFALLBACK),
[Severity: Medium]
Is this counter actually incremented anywhere in the codebase?
It appears that MPTCP_MIB_MPFAILFALLBACK is defined and exposed here, but it
is never passed to MPTCP_INC_STATS() or used in tracking functions like
mptcp_try_fallback() within this patch.
Without being incremented when a fallback actually occurs, won't this counter
just permanently read zero when queried by userspace?
> SNMP_MIB_ITEM("DssReset", MPTCP_MIB_DSSRESET),
> SNMP_MIB_ITEM("SimultConnectFallback", MPTCP_MIB_SIMULTCONNFALLBACK),
> SNMP_MIB_ITEM("FallbackFailed", MPTCP_MIB_FALLBACKFAILED),
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260818091417.2200986-1-chenguang.zhao@linux.dev?part=1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH mptcp-next RFC v4 2/2] mptcp: send MP_FAIL and infinite mapping on the same ACK
2026-08-18 9:14 [PATCH mptcp-next RFC v4 0/2] mptcp: fallback to TCP on MP_FAIL with a single subflow Chenguang Zhao
2026-08-18 9:14 ` [PATCH mptcp-next RFC v4 1/2] mptcp: add MPFailFallback MIB Chenguang Zhao
@ 2026-08-18 9:14 ` Chenguang Zhao
2026-08-18 9:30 ` sashiko-bot
2026-08-18 16:19 ` Matthieu Baerts
2026-08-18 10:21 ` [PATCH mptcp-next RFC v4 0/2] mptcp: fallback to TCP on MP_FAIL with a single subflow MPTCP CI
2 siblings, 2 replies; 7+ messages in thread
From: Chenguang Zhao @ 2026-08-18 9:14 UTC (permalink / raw)
To: mptcp; +Cc: chenguang.zhao, Chenguang Zhao
From: Chenguang Zhao <zhaochenguang@kylinos.cn>
On a single subflow, accepting MP_FAIL must leave MPTCP mode at once
(RFC8684 §3.7). Emit the infinite mapping on the MP_FAIL reply ACK,
then call mptcp_try_fallback() immediately after.
Fixes: 1e39e5a32ad7 ("mptcp: infinite mapping sending")
Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
---
net/mptcp/options.c | 33 ++++++++++++++++++++++++++++++++-
net/mptcp/pm.c | 5 +++++
net/mptcp/protocol.c | 8 +-------
3 files changed, 38 insertions(+), 8 deletions(-)
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index 97da22668dbe..ecf77e77d2de 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -638,7 +638,29 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb,
opts->csum_reqd = READ_ONCE(msk->csum_enabled);
mpext = skb ? mptcp_get_ext(skb) : NULL;
- if (!skb || (mpext && mpext->use_map) || snd_data_fin_enable) {
+ if (unlikely(subflow->send_infinite_map)) {
+ unsigned int map_size = TCPOLEN_MPTCP_DSS_BASE + TCPOLEN_MPTCP_DSS_MAP64;
+
+ if (opts->csum_reqd)
+ map_size += TCPOLEN_MPTCP_DSS_CHECKSUM;
+
+ if (mpext) {
+ opts->ext_copy = *mpext;
+ opts->ext_copy.infinite_map = 1;
+ opts->ext_copy.data_len = 0;
+ } else {
+ opts->ext_copy.use_map = 1;
+ opts->ext_copy.dsn64 = 1;
+ opts->ext_copy.infinite_map = 1;
+ opts->ext_copy.data_len = 0;
+ opts->ext_copy.data_seq = READ_ONCE(msk->snd_nxt);
+ opts->ext_copy.subflow_seq = subflow->rel_write_seq;
+ }
+
+ dss_size = map_size;
+ opts->suboptions = OPTION_MPTCP_DSS;
+ ret = true;
+ } else if (!skb || (mpext && mpext->use_map) || snd_data_fin_enable) {
unsigned int map_size = TCPOLEN_MPTCP_DSS_BASE + TCPOLEN_MPTCP_DSS_MAP64;
if (mpext) {
@@ -1566,6 +1588,15 @@ void mptcp_write_options(struct tcphdr *th, __be32 *ptr, struct tcp_sock *tp,
TCPOPT_NOP << 8 | TCPOPT_NOP, ptr);
}
ptr += 1;
+
+ if (unlikely(mpext->infinite_map)) {
+ subflow = mptcp_subflow_ctx(ssk);
+ if (subflow->send_infinite_map) {
+ subflow->send_infinite_map = 0;
+ MPTCP_INC_STATS(sock_net(ssk),
+ MPTCP_MIB_INFINITEMAPTX);
+ }
+ }
}
/* We might need to add MP_FAIL options in rare cases */
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index ba7c6f80a183..69701ac7e436 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -895,6 +895,11 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
pr_debug("MP_FAIL response received\n");
WRITE_ONCE(subflow->fail_tout, 0);
}
+
+ if (!mptcp_try_fallback(sk, MPTCP_MIB_MPFAILFALLBACK)) {
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_FALLBACKFAILED);
+ mptcp_subflow_reset(sk);
+ }
}
static int mptcp_add_addr_len(int family, bool echo, bool port)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index f879b1061f2d..abe45a31ca5f 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -1417,13 +1417,7 @@ static void mptcp_update_infinite_map(struct mptcp_sock *msk,
mpext->infinite_map = 1;
mpext->data_len = 0;
-
- if (!mptcp_try_fallback(ssk, MPTCP_MIB_INFINITEMAPTX)) {
- MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_FALLBACKFAILED);
- mptcp_subflow_reset(ssk);
- return;
- }
-
+ MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_INFINITEMAPTX);
mptcp_subflow_ctx(ssk)->send_infinite_map = 0;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH mptcp-next RFC v4 2/2] mptcp: send MP_FAIL and infinite mapping on the same ACK
2026-08-18 9:14 ` [PATCH mptcp-next RFC v4 2/2] mptcp: send MP_FAIL and infinite mapping on the same ACK Chenguang Zhao
@ 2026-08-18 9:30 ` sashiko-bot
2026-08-18 16:19 ` Matthieu Baerts
1 sibling, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-08-18 9:30 UTC (permalink / raw)
To: Chenguang Zhao; +Cc: mptcp
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] MP_FAIL is permanently dropped due to TCP options space exhaustion when packed alongside DSS and Timestamps.
- [High] Dropped pure ACKs cause a fatal connection reset because immediate fallback disables MPTCP options on retransmissions.
- [High] The send_infinite_map flag is cleared when transmitting a pure ACK, omitting the map from the first data packet if the ACK drops.
--
--- Patch [2]: [PATCH mptcp-next RFC v4 2/2] mptcp: send MP_FAIL and infinite mapping on the same ACK ---
commit fa151aec0663a3e5b6abb8fb25a4fd411a972b0a
Author: Chenguang Zhao <zhaochenguang@kylinos.cn>
mptcp: send MP_FAIL and infinite mapping on the same ACK
This commit updates the handling of an MP_FAIL response on a single subflow.
It emits the infinite mapping on the MP_FAIL reply ACK and then immediately
transitions the socket into fallback mode.
> mptcp: send MP_FAIL and infinite mapping on the same ACK
>
> On a single subflow, accepting MP_FAIL must leave MPTCP mode at once
> (RFC8684 §3.7). Emit the infinite mapping on the MP_FAIL reply ACK,
> then call mptcp_try_fallback() immediately after.
[Severity: High]
If we pack both the MP_FAIL and the infinite mapping on the same ACK,
is there enough TCP options space if timestamps are enabled?
With standard TCP Timestamps (12 bytes), the remaining option space is 28
bytes.
The infinite mapping requires at least 24 bytes (base + 64-bit map + ACK),
leaving at most 4 bytes.
Since MP_FAIL requires 12 bytes, will it fail the space check in
mptcp_established_options() and be silently dropped?
> diff --git a/net/mptcp/options.c b/net/mptcp/options.c
> index 2f571766db440..e4f8e976ff163 100644
> --- a/net/mptcp/options.c
> +++ b/net/mptcp/options.c
[ ... ]
> @@ -1567,6 +1589,15 @@ void mptcp_write_options(struct tcphdr *th, __be32 *ptr, struct tcp_sock *tp,
> TCPOPT_NOP << 8 | TCPOPT_NOP, ptr);
> }
> ptr += 1;
> +
> + if (unlikely(mpext->infinite_map)) {
> + subflow = mptcp_subflow_ctx(ssk);
> + if (subflow->send_infinite_map) {
> + subflow->send_infinite_map = 0;
[Severity: High]
Does clearing this flag on a pure ACK risk losing the infinite mapping if
the network drops the packet?
Since TCP does not retransmit pure ACKs, the infinite mapping would need to
be piggybacked on the next data packet.
If we clear the send_infinite_map flag here, will the next data packet fail
to include the mapping and transmit as plain TCP instead?
> + MPTCP_INC_STATS(sock_net(ssk),
> + MPTCP_MIB_INFINITEMAPTX);
> + }
> + }
> }
>
> /* We might need to add MP_FAIL options in rare cases */
[ ... ]
> diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
> index d7c5b50b34cc9..351f9c998d7b6 100644
> --- a/net/mptcp/pm.c
> +++ b/net/mptcp/pm.c
> @@ -895,6 +895,11 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
> pr_debug("MP_FAIL response received\n");
> WRITE_ONCE(subflow->fail_tout, 0);
> }
> +
> + if (!mptcp_try_fallback(sk, MPTCP_MIB_MPFAILFALLBACK)) {
[Severity: High]
Does entering fallback immediately after sending the pure ACK prevent
retransmission of MPTCP options if the ACK is lost?
If the initial pure ACK drops, the remote peer will retransmit its MP_FAIL.
When the local stack responds with another pure ACK, skb is NULL.
Because the socket is already in fallback mode, it looks like
mptcp_established_options() will fail the fallback check:
if (unlikely(__mptcp_check_fallback(msk) && !mptcp_check_infinite_map(skb)))
return 0;
Could this permanently prevent recovery by omitting the MP_FAIL option on
subsequent ACKs?
> + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_FALLBACKFAILED);
> + mptcp_subflow_reset(sk);
> + }
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260818091417.2200986-1-chenguang.zhao@linux.dev?part=2
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH mptcp-next RFC v4 2/2] mptcp: send MP_FAIL and infinite mapping on the same ACK
2026-08-18 9:14 ` [PATCH mptcp-next RFC v4 2/2] mptcp: send MP_FAIL and infinite mapping on the same ACK Chenguang Zhao
2026-08-18 9:30 ` sashiko-bot
@ 2026-08-18 16:19 ` Matthieu Baerts
1 sibling, 0 replies; 7+ messages in thread
From: Matthieu Baerts @ 2026-08-18 16:19 UTC (permalink / raw)
To: Chenguang Zhao, mptcp; +Cc: Chenguang Zhao
Hi Chenguang,
On 18/08/2026 11:14, Chenguang Zhao wrote:
> From: Chenguang Zhao <zhaochenguang@kylinos.cn>
>
> On a single subflow, accepting MP_FAIL must leave MPTCP mode at once
> (RFC8684 §3.7). Emit the infinite mapping on the MP_FAIL reply ACK,
> then call mptcp_try_fallback() immediately after.
I think Sashiko is right about the issue of lacking option space to have
both the MP_FAIL and the infinite mapping sent together.
Please also note this from RFC 8684 §3.7.11 [1]:
> This infinite mapping will be a DSS option (Section 3.3) on the first
> new packet, containing a Data Sequence Mapping that acts retroactively
The "on the first new packet" seems to suggest the infinite mapping
shouldn't be attached to the MP_FAIL.
[1] https://datatracker.ietf.org/doc/html/rfc8684#section-3.7-11
Maybe a fallback for the single subflow without csum is enough? Or maybe
nothing needs to be done? Or maybe this fallback is just an
optimisation, and can be done in net-next (and both commits can be
combined)? WDYT?
> Fixes: 1e39e5a32ad7 ("mptcp: infinite mapping sending")
> Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
> ---
> net/mptcp/options.c | 33 ++++++++++++++++++++++++++++++++-
> net/mptcp/pm.c | 5 +++++
> net/mptcp/protocol.c | 8 +-------
> 3 files changed, 38 insertions(+), 8 deletions(-)
>
> diff --git a/net/mptcp/options.c b/net/mptcp/options.c
> index 97da22668dbe..ecf77e77d2de 100644
> --- a/net/mptcp/options.c
> +++ b/net/mptcp/options.c
> @@ -638,7 +638,29 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb,
> opts->csum_reqd = READ_ONCE(msk->csum_enabled);
> mpext = skb ? mptcp_get_ext(skb) : NULL;
>
> - if (!skb || (mpext && mpext->use_map) || snd_data_fin_enable) {
> + if (unlikely(subflow->send_infinite_map)) {
> + unsigned int map_size = TCPOLEN_MPTCP_DSS_BASE + TCPOLEN_MPTCP_DSS_MAP64;
> +
> + if (opts->csum_reqd)
> + map_size += TCPOLEN_MPTCP_DSS_CHECKSUM;
> +
> + if (mpext) {
> + opts->ext_copy = *mpext;
> + opts->ext_copy.infinite_map = 1;
> + opts->ext_copy.data_len = 0;
> + } else {
> + opts->ext_copy.use_map = 1;
> + opts->ext_copy.dsn64 = 1;> + opts->ext_copy.infinite_map = 1;
> + opts->ext_copy.data_len = 0;
> + opts->ext_copy.data_seq = READ_ONCE(msk->snd_nxt);
> + opts->ext_copy.subflow_seq = subflow->rel_write_seq;
> + }
You probably also need to reset "csum". Note that common fields
(infinite_map and data_len) can also be set once here.
(...)
Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH mptcp-next RFC v4 0/2] mptcp: fallback to TCP on MP_FAIL with a single subflow
2026-08-18 9:14 [PATCH mptcp-next RFC v4 0/2] mptcp: fallback to TCP on MP_FAIL with a single subflow Chenguang Zhao
2026-08-18 9:14 ` [PATCH mptcp-next RFC v4 1/2] mptcp: add MPFailFallback MIB Chenguang Zhao
2026-08-18 9:14 ` [PATCH mptcp-next RFC v4 2/2] mptcp: send MP_FAIL and infinite mapping on the same ACK Chenguang Zhao
@ 2026-08-18 10:21 ` MPTCP CI
2 siblings, 0 replies; 7+ messages in thread
From: MPTCP CI @ 2026-08-18 10:21 UTC (permalink / raw)
To: Chenguang Zhao; +Cc: mptcp
Hi Chenguang,
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! ✅
- Task: https://github.com/multipath-tcp/mptcp_net-next/actions/runs/32122351648
Initiator: Patchew Applier
Commits: https://github.com/multipath-tcp/mptcp_net-next/commits/02ed8cd94c7c
Patchwork: https://patchwork.kernel.org/project/mptcp/list/?series=1147672
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)
^ permalink raw reply [flat|nested] 7+ messages in thread