[PATCH mptcp-net v2 1/3] mptcp: fallback to TCP on MP_FAIL with a single subflow

Chenguang Zhao <[email protected]> Wed, 15 Jul 2026 14:18:28 +0800
Newsgroups dev.linux.lists.mptcp
Message-ID <[email protected]>
From: Chenguang Zhao <[email protected]>

When a valid MP_FAIL is received and infinite fallback is still allowed
(single contiguous subflow), RFC8684 §3.7 requires leaving MPTCP mode.
The stack only cleared allow_subflows and deferred the real fallback to
the later infinite-map transmit path. Before any data is sent, a peer
could still complete the 4th ACK as MPTCP and keep using MPTCP options.

Fall back immediately after sending the MP_FAIL response, and teach
mptcp_is_fully_established() to reject joins after fallback or when
subflows are disallowed. If the out-of-order queue is non-empty, reset
the subflow instead of leaving a half-fallback state.

Fixes: 1e39e5a32ad7 ("mptcp: infinite mapping sending")
Signed-off-by: Chenguang Zhao <[email protected]>
---
 net/mptcp/pm.c       | 51 +++++++++++++++++++++++++++++++++++++++++++-
 net/mptcp/protocol.c |  7 +++++-
 net/mptcp/protocol.h | 18 ++++++++++------
 3 files changed, 67 insertions(+), 9 deletions(-)

diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 6afd39aea110..c1f5c3ced4ee 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -870,7 +870,15 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
 
 	pr_debug("fail_seq=%llu\n", fail_seq);
 
-	/* After accepting the fail, we can't create any other subflows */
+	/* MP_FAIL on a single contiguous subflow: fall back to TCP.
+	 * allow_infinite_fallback is cleared once other subflows join or
+	 * non-contiguous data is retransmitted; in that case ignore MP_FAIL
+	 * here (the peer should reset the failing subflow instead).
+	 *
+	 * Send the MP_FAIL (+ DSS) response before setting FALLBACK_DONE,
+	 * otherwise mptcp_established_options() would drop all MPTCP options
+	 * on this ACK. InfiniteMapTx is accounted later when the map is sent.
+	 */
 	spin_lock_bh(&msk->fallback_lock);
 	if (!msk->allow_infinite_fallback) {
 		spin_unlock_bh(&msk->fallback_lock);
@@ -882,9 +890,50 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
 	if (!subflow->fail_tout) {
 		pr_debug("send MP_FAIL response and infinite map\n");
 
+		/* Infinite mapping requires contiguous data. With OoO still
+		 * queued, do not leave allow_subflows=false without
+		 * FALLBACK_DONE; tear the subflow down instead (RFC8684 §3.7).
+		 */
+		if (!RB_EMPTY_ROOT(&msk->out_of_order_queue)) {
+			MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_FALLBACKFAILED);
+			subflow->send_mp_fail = 1;
+			mptcp_subflow_reset(sk);
+			return;
+		}
+
 		subflow->send_mp_fail = 1;
 		subflow->send_infinite_map = 1;
 		tcp_send_ack(sk);
+
+		/* RFC8684 §3.7: after accepting MP_FAIL with a single
+		 * subflow, leave MPTCP mode and never revert. No dedicated
+		 * fallback MIB yet; InfiniteMapTx is counted when the map
+		 * is transmitted. Handle pending DATA_FIN like
+		 * mptcp_try_fallback().
+		 */
+		spin_lock_bh(&msk->fallback_lock);
+		if (__mptcp_check_fallback(msk)) {
+			spin_unlock_bh(&msk->fallback_lock);
+			return;
+		}
+		if (!msk->allow_infinite_fallback) {
+			spin_unlock_bh(&msk->fallback_lock);
+			MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_FALLBACKFAILED);
+			mptcp_subflow_reset(sk);
+			return;
+		}
+		set_bit(MPTCP_FALLBACK_DONE, &msk->flags);
+		spin_unlock_bh(&msk->fallback_lock);
+
+		if (READ_ONCE(msk->snd_data_fin_enable) &&
+		    !(sk->sk_shutdown & SEND_SHUTDOWN)) {
+			gfp_t saved_allocation = sk->sk_allocation;
+
+			sk->sk_allocation = GFP_ATOMIC;
+			sk->sk_shutdown |= SEND_SHUTDOWN;
+			tcp_shutdown(sk, SEND_SHUTDOWN);
+			sk->sk_allocation = saved_allocation;
+		}
 	} else {
 		pr_debug("MP_FAIL response received\n");
 		WRITE_ONCE(subflow->fail_tout, 0);
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index cb9515f505aa..5b9522caaf43 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -1299,7 +1299,12 @@ 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)) {
+	/* Fallback may already have been completed on MP_FAIL reception;
+	 * still account for the infinite mapping being transmitted.
+	 */
+	if (__mptcp_check_fallback(msk)) {
+		MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_INFINITEMAPTX);
+	} else if (!mptcp_try_fallback(ssk, MPTCP_MIB_INFINITEMAPTX)) {
 		MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_FALLBACKFAILED);
 		mptcp_subflow_reset(ssk);
 		return;
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 4a2d40cd7b13..03f0b33694d7 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -369,7 +369,7 @@ struct mptcp_sock {
 
 	spinlock_t	fallback_lock;	/* protects fallback,
 					 * allow_infinite_fallback and
-					 * allow_join
+					 * allow_subflows
 					 */
 
 	struct list_head backlog_list;	/* protected by the data lock */
@@ -947,12 +947,6 @@ static inline void mptcp_start_tout_timer(struct sock *sk)
 	mptcp_reset_tout_timer(mptcp_sk(sk), 0);
 }
 
-static inline bool mptcp_is_fully_established(struct sock *sk)
-{
-	return inet_sk_state_load(sk) == TCP_ESTABLISHED &&
-	       READ_ONCE(mptcp_sk(sk)->fully_established);
-}
-
 static inline u64 mptcp_stamp(void)
 {
 	return div_u64(tcp_clock_ns(), NSEC_PER_USEC);
@@ -1290,6 +1284,16 @@ static inline bool mptcp_check_fallback(const struct sock *sk)
 	return __mptcp_check_fallback(msk);
 }
 
+static inline bool mptcp_is_fully_established(struct sock *sk)
+{
+	struct mptcp_sock *msk = mptcp_sk(sk);
+
+	return inet_sk_state_load(sk) == TCP_ESTABLISHED &&
+	       READ_ONCE(msk->fully_established) &&
+	       !__mptcp_check_fallback(msk) &&
+	       msk->allow_subflows;
+}
+
 static inline bool __mptcp_has_initial_subflow(const struct mptcp_sock *msk)
 {
 	struct sock *ssk = READ_ONCE(msk->first);
-- 
2.25.1