summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-09-09 13:32:58 -0700
committerJakub Kicinski <kuba@kernel.org>2026-09-09 13:32:59 -0700
commit9a1599eeb8d18a2113e4cabcbd3bec5a8377dbed (patch)
tree92e0cddb72622090ff5b173e752cab2f03e818b1
parent5be081b83abd3f17d908953b4bb77279f5a149e3 (diff)
parentf01b8275745efe611284f6c3628099a81a421f0d (diff)
downloadlinux-next-9a1599eeb8d18a2113e4cabcbd3bec5a8377dbed.tar.gz
linux-next-9a1599eeb8d18a2113e4cabcbd3bec5a8377dbed.zip
Merge branch 'mptcp-misc-fixes-for-v7-3-rc1'
Matthieu Baerts says: ==================== mptcp: misc fixes for v7.3-rc1 Here are various unrelated fixes: - Patch 1: Do not reschedule the RTX timer for sockets that fell back to TCP. A fix for v5.7. - Patch 2: Avoid copying thmac which will not be used and could be uninitialised. A fix for v5.7. - Patch 3: Re-set the request backup flag when SYN cookies are used. A fix for v5.9. - Patch 4: Drop pending ADD_ADDR when removing ID0, and avoid a WARN. A fix for v5.13. - Patch 5: Handle invalid suboptions where the checksum is requested in the MP_CAPABLE 4th ACK with data, but not added in the option. A fix for v5.14. - Patch 6: Prevent a race between mptcp_disconnect() and the retransmit timer. A fix for v5.17. - Patch 7: Fix a use-after-free in the selftests that could lead to false positive. A fix for v5.17. - Patch 8: Limit new addresses with the userspace PM to avoid an address ID overflow. A fix for v5.19. - Patch 9: Reset the ADD_ADDR retransmission counter when the timer is reused. A fix for v5.19. - Patch 10: Remove unneeded and confusing READ_ONCE() annotations. A fix for v6.13. - Patches 11-12: Get nstat counters for the current test, not since the creation of the netns. A fix for v6.19. - Patch 13: Fix an uninit-value in mptcp_write_data_fin for a corner case now that only a part of the tcp_out_options struct is reset. A fix for v7.1. - Patches 14-15: Two follow-up patches addressing minor comments discovered after the human review. A fix for v7.3-rc1. ==================== Link: https://patch.msgid.link/20260908-net-mptcp-misc-fixes-7-3-rc1-v2-0-df1de70348b6@kernel.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--net/mptcp/options.c8
-rw-r--r--net/mptcp/pm.c2
-rw-r--r--net/mptcp/pm_kernel.c8
-rw-r--r--net/mptcp/pm_userspace.c18
-rw-r--r--net/mptcp/protocol.c41
-rw-r--r--net/mptcp/protocol.h1
-rw-r--r--net/mptcp/subflow.c1
-rw-r--r--net/mptcp/syncookies.c5
-rw-r--r--tools/testing/selftests/net/mptcp/mptcp_connect.c4
-rw-r--r--tools/testing/selftests/net/mptcp/mptcp_lib.sh26
10 files changed, 78 insertions, 36 deletions
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index b8318e030138..ce0de02f5a3a 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -93,7 +93,8 @@ static void mptcp_parse_option(const struct sk_buff *skb,
* In other words, the only way for checksums not to be used
* is if both hosts in their SYNs set A=0."
*/
- if (flags & MPTCP_CAP_CHECKSUM_REQD)
+ if ((flags & MPTCP_CAP_CHECKSUM_REQD) &&
+ opsize < TCPOLEN_MPTCP_MPC_ACK_DATA)
mp_opt->suboptions |= OPTION_MPTCP_CSUMREQD;
mp_opt->deny_join_id0 = !!(flags & MPTCP_CAP_DENY_JOIN_ID0);
@@ -529,7 +530,7 @@ static bool mptcp_established_options_mp(struct sock *sk, struct sk_buff *skb,
return false;
/* MPC/MPJ needed only on 3rd ack packet, DATA_FIN and TCP shutdown take precedence */
- if (READ_ONCE(subflow->fully_established) || snd_data_fin_enable ||
+ if (subflow->fully_established || snd_data_fin_enable ||
subflow->snd_isn != TCP_SKB_CB(skb)->seq ||
sk->sk_state != TCP_ESTABLISHED)
return false;
@@ -611,6 +612,7 @@ static void mptcp_write_data_fin(struct mptcp_subflow_context *subflow,
ext->data_seq = data_fin_tx_seq;
ext->subflow_seq = 0;
ext->data_len = 1;
+ ext->csum = 0;
} else if (ext->data_seq + ext->data_len == data_fin_tx_seq) {
/* If there's an existing DSS mapping and it is the
* final mapping, DATA_FIN consumes 1 additional byte of
@@ -980,7 +982,7 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk,
/* here we can process OoO, in-window pkts, only in-sequence 4th ack
* will make the subflow fully established
*/
- if (likely(READ_ONCE(subflow->fully_established))) {
+ if (likely(subflow->fully_established)) {
/* on passive sockets, check for 3rd ack retransmission
* note that msk is always set by subflow_syn_recv_sock()
* for mp_join subflows
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 8b68868255c5..b0b71adefb8f 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -462,10 +462,10 @@ bool mptcp_pm_announced_alloc(struct mptcp_sock *msk,
add_entry->addr = *addr;
add_entry->sock = msk;
- add_entry->retrans_times = 0;
timer_setup(&add_entry->timer, mptcp_pm_add_addr_timer, 0);
reset_timer:
+ add_entry->retrans_times = 0;
add_entry->timer_done = false;
timeout = mptcp_adjust_add_addr_timeout(msk);
if (timeout)
diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c
index 424f1a7f9248..1a7750813235 100644
--- a/net/mptcp/pm_kernel.c
+++ b/net/mptcp/pm_kernel.c
@@ -1137,6 +1137,8 @@ static int mptcp_nl_remove_id_zero_address(struct net *net,
while ((msk = mptcp_token_iter_next(net, &s_slot, &s_num)) != NULL) {
struct sock *sk = (struct sock *)msk;
struct mptcp_addr_info msk_local;
+ struct mptcp_addr_info anno_addr;
+ bool announced;
if (list_empty(&msk->conn_list) || mptcp_pm_is_userspace(msk))
goto next;
@@ -1146,7 +1148,13 @@ static int mptcp_nl_remove_id_zero_address(struct net *net,
goto next;
lock_sock(sk);
+ /* Drop a possibly pending ADD_ADDR for this address. */
+ anno_addr = msk_local;
+ anno_addr.port = 0;
+ announced = mptcp_pm_announced_remove(msk, &anno_addr);
spin_lock_bh(&msk->pm.lock);
+ if (announced)
+ msk->pm.add_addr_signaled--;
mptcp_pm_remove_addr(msk, &list);
mptcp_pm_rm_subflow(msk, &list);
__mark_subflow_endp_available(msk, 0);
diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c
index b94fbb483bf9..fab16d953dbf 100644
--- a/net/mptcp/pm_userspace.c
+++ b/net/mptcp/pm_userspace.c
@@ -69,6 +69,19 @@ static int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk,
}
if (!addr_match && !id_match) {
+ unsigned int id;
+
+ if (!entry->addr.id && needs_id) {
+ id = find_next_zero_bit(id_bitmap,
+ MPTCP_PM_MAX_ADDR_ID + 1, 1);
+ if (id > MPTCP_PM_MAX_ADDR_ID) {
+ ret = -ENOSPC;
+ goto append_err;
+ }
+ } else {
+ id = entry->addr.id;
+ }
+
/* Memory for the entry is allocated from the
* sock option buffer.
*/
@@ -78,10 +91,7 @@ static int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk,
goto append_err;
}
- if (!e->addr.id && needs_id)
- e->addr.id = find_next_zero_bit(id_bitmap,
- MPTCP_PM_MAX_ADDR_ID + 1,
- 1);
+ e->addr.id = id;
list_add_tail_rcu(&e->list, &msk->pm.userspace_pm_local_addr_list);
msk->pm.local_addr_used++;
ret = e->addr.id;
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index e1f08f71cdb1..0098e2830931 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -95,6 +95,7 @@ bool __mptcp_try_fallback(struct mptcp_sock *msk, int fb_mib)
msk->allow_subflows = false;
set_bit(MPTCP_FALLBACK_DONE, &msk->flags);
+ clear_bit(MPTCP_RTX_ENABLED, &msk->flags);
__MPTCP_INC_STATS(net, fb_mib);
spin_unlock_bh(&msk->fallback_lock);
return true;
@@ -288,8 +289,8 @@ static void mptcp_prune_ofo_queue(struct sock *sk,
*/
static bool mptcp_can_ingest(const struct sock *sk)
{
- return unlikely(sk_rmem_alloc_get(sk) <= READ_ONCE(sk->sk_rcvbuf)) ||
- __mptcp_check_fallback(mptcp_sk(sk));
+ return likely(sk_rmem_alloc_get(sk) <= READ_ONCE(sk->sk_rcvbuf)) ||
+ __mptcp_check_fallback(mptcp_sk(sk));
}
static bool mptcp_try_rmem_schedule(struct sock *sk, const struct sk_buff *skb)
@@ -312,12 +313,6 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
u64 seq, end_seq, max_seq;
struct sk_buff *skb1;
- if (!mptcp_try_rmem_schedule(sk, skb)) {
- MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RCVPRUNED);
- mptcp_drop(sk, skb);
- return;
- }
-
seq = MPTCP_SKB_CB(skb)->map_seq;
end_seq = MPTCP_SKB_CB(skb)->end_seq;
max_seq = atomic64_read(&msk->rcv_wnd_sent);
@@ -334,6 +329,12 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
return;
}
+ if (!mptcp_try_rmem_schedule(sk, skb)) {
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RCVPRUNED);
+ mptcp_drop(sk, skb);
+ return;
+ }
+
p = &msk->out_of_order_queue.rb_node;
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOQUEUE);
if (RB_EMPTY_ROOT(&msk->out_of_order_queue)) {
@@ -1084,13 +1085,14 @@ static bool mptcp_rtx_timer_pending(struct sock *sk)
static void mptcp_reset_rtx_timer(struct sock *sk)
{
+ struct mptcp_sock *msk = mptcp_sk(sk);
unsigned long tout;
- /* prevent rescheduling on close */
- if (unlikely(inet_sk_state_load(sk) == TCP_CLOSE))
+ /* Prevent rescheduling on close and in case of fallback. */
+ if (!test_bit(MPTCP_RTX_ENABLED, &msk->flags))
return;
- tout = mptcp_sk(sk)->timer_ival;
+ tout = msk->timer_ival;
sk_reset_timer(sk, &sk->mptcp_retransmit_timer, jiffies + tout);
}
@@ -3323,6 +3325,9 @@ void mptcp_set_state(struct sock *sk, int state)
* transition from TCP_SYN_RECV to TCP_CLOSE_WAIT.
*/
break;
+ case TCP_CLOSE:
+ clear_bit(MPTCP_RTX_ENABLED, &mptcp_sk(sk)->flags);
+ fallthrough;
default:
if (oldstate == TCP_ESTABLISHED || oldstate == TCP_CLOSE_WAIT)
MPTCP_DEC_STATS(sock_net(sk), MPTCP_MIB_CURRESTAB);
@@ -3583,6 +3588,7 @@ static void mptcp_destroy_common(struct mptcp_sock *msk)
static int mptcp_disconnect(struct sock *sk, int flags)
{
+ struct inet_connection_sock *icsk = inet_csk(sk);
struct mptcp_sock *msk = mptcp_sk(sk);
/* We are on the fastopen error path. We can't call straight into the
@@ -3595,8 +3601,13 @@ static int mptcp_disconnect(struct sock *sk, int flags)
mptcp_check_listen_stop(sk);
mptcp_set_state(sk, TCP_CLOSE);
- mptcp_stop_rtx_timer(sk);
- mptcp_stop_tout_timer(sk);
+ /* The later subflow close can not kick again the tout timer,
+ * as the msk is already in closed status.
+ */
+ msk->timer_ival = icsk->icsk_rto_min;
+ sk_stop_timer_sync(sk, &sk->mptcp_retransmit_timer);
+ icsk->icsk_mtup.probe_timestamp = 0;
+ sk_stop_timer_sync(sk, &icsk->mptcp_tout_timer);
mptcp_pm_connection_closed(msk);
@@ -3875,7 +3886,7 @@ static void schedule_3rdack_retransmission(struct sock *ssk)
struct tcp_sock *tp = tcp_sk(ssk);
unsigned long timeout;
- if (READ_ONCE(mptcp_subflow_ctx(ssk)->fully_established))
+ if (mptcp_subflow_ctx(ssk)->fully_established)
return;
/* reschedule with a timeout above RTT, as we must look only for drop */
@@ -4141,6 +4152,7 @@ static int mptcp_connect(struct sock *sk, struct sockaddr_unsized *uaddr,
if (IS_ERR(ssk))
return PTR_ERR(ssk);
+ set_bit(MPTCP_RTX_ENABLED, &msk->flags);
mptcp_set_state(sk, TCP_SYN_SENT);
subflow = mptcp_subflow_ctx(ssk);
#ifdef CONFIG_TCP_MD5SIG
@@ -4288,6 +4300,7 @@ static int mptcp_listen(struct socket *sock, int backlog)
goto unlock;
}
+ set_bit(MPTCP_RTX_ENABLED, &msk->flags);
mptcp_set_state(sk, TCP_LISTEN);
sock_set_flag(sk, SOCK_RCU_FREE);
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 87ccb84e9927..2b4c27426477 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -116,6 +116,7 @@
#define MPTCP_WORK_RTX 1
#define MPTCP_FALLBACK_DONE 2
#define MPTCP_WORK_CLOSE_SUBFLOW 3
+#define MPTCP_RTX_ENABLED 4
/* MPTCP socket release cb flags */
#define MPTCP_PUSH_PENDING 1
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index af81ad5e699d..01db7edce18a 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -2084,7 +2084,6 @@ static void subflow_ulp_clone(const struct request_sock *req,
new_ctx->request_bkup = subflow_req->request_bkup;
WRITE_ONCE(new_ctx->remote_id, subflow_req->remote_id);
new_ctx->token = subflow_req->token;
- new_ctx->thmac = subflow_req->thmac;
/* the subflow req id is valid, fetched via subflow_check_req()
* and subflow_token_join_request()
diff --git a/net/mptcp/syncookies.c b/net/mptcp/syncookies.c
index b5cac5701122..9474706641c1 100644
--- a/net/mptcp/syncookies.c
+++ b/net/mptcp/syncookies.c
@@ -26,7 +26,8 @@ struct join_entry {
u32 local_nonce;
u8 join_id;
u8 local_id;
- u8 backup;
+ u8 backup:1,
+ request_bkup:1;
u8 valid;
};
@@ -63,6 +64,7 @@ static void mptcp_join_store_state(struct join_entry *entry,
entry->remote_nonce = subflow_req->remote_nonce;
entry->local_nonce = subflow_req->local_nonce;
entry->backup = subflow_req->backup;
+ entry->request_bkup = subflow_req->request_bkup;
entry->join_id = subflow_req->remote_id;
entry->local_id = subflow_req->local_id;
entry->valid = 1;
@@ -117,6 +119,7 @@ bool mptcp_token_join_cookie_init_state(struct mptcp_subflow_request_sock *subfl
subflow_req->remote_nonce = e->remote_nonce;
subflow_req->local_nonce = e->local_nonce;
subflow_req->backup = e->backup;
+ subflow_req->request_bkup = e->request_bkup;
subflow_req->remote_id = e->join_id;
subflow_req->local_id = e->local_id;
subflow_req->token = e->token;
diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c
index ea4cb6c1bd5e..178d98d91fea 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_connect.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c
@@ -381,6 +381,9 @@ static int sock_connect_mptcp(const char * const remoteaddr,
hints.ai_family = pf;
+ /* Keep the resolved address alive for the whole execution: it is
+ * used again when reconnecting, and will be released at exit time.
+ */
xgetaddrinfo(remoteaddr, port, &hints, &addr);
for (a = addr; a; a = a->ai_next) {
sock = socket(a->ai_family, a->ai_socktype, proto);
@@ -421,7 +424,6 @@ static int sock_connect_mptcp(const char * const remoteaddr,
sock = -1;
}
- freeaddrinfo(addr);
if (sock != -1)
SOCK_TEST_TCPULP(sock, proto);
return sock;
diff --git a/tools/testing/selftests/net/mptcp/mptcp_lib.sh b/tools/testing/selftests/net/mptcp/mptcp_lib.sh
index 5ef6033775c8..b9d14647f401 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_lib.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_lib.sh
@@ -108,12 +108,14 @@ mptcp_lib_pr_info() {
mptcp_lib_pr_nstat() {
local ns="${1}"
- local hist="/tmp/${ns}.out"
+ local cache="/tmp/${ns}.out"
+ local hist="/tmp/${ns}.nstat"
- if [ -f "${hist}" ]; then
- awk '$2 != 0 { print " "$0 }' "${hist}"
+ if [ -f "${cache}" ]; then
+ awk '$2 != 0 { print " "$0 }' "${cache}"
else
- ip netns exec "${ns}" nstat -as | grep Tcp
+ NSTAT_HISTORY="${hist}" ip netns exec "${ns}" nstat -s |
+ grep Tcp
fi
}
@@ -414,19 +416,21 @@ mptcp_lib_nstat_get() {
}
# $1: ns, $2: MIB counter
-# Get the counter from the history (mptcp_lib_nstat_{init,get}()) if available.
-# If not, get the counter from nstat ignoring any history.
+# Get the counter from the cache (mptcp_lib_nstat_{init,get}()) if available.
+# If not, get the counter from nstat ignoring any cache, but using the history.
mptcp_lib_get_counter() {
local ns="${1}"
local counter="${2}"
- local hist="/tmp/${ns}.out"
+ local cache="/tmp/${ns}.out"
+ local hist="/tmp/${ns}.nstat"
local count
- if [[ -s "${hist}" && "${counter}" == *"Tcp"* ]]; then
- count=$(awk "/^${counter} / {print \$2; exit}" "${hist}")
+ if [[ -s "${cache}" && "${counter}" == *"Tcp"* ]]; then
+ count=$(awk "/^${counter} / {print \$2; exit}" "${cache}")
else
- count=$(ip netns exec "${ns}" nstat -asz "${counter}" |
- awk 'NR==1 {next} {print $2}')
+ count=$(NSTAT_HISTORY="${hist}" ip netns exec "${ns}" \
+ nstat -sz "${counter}" |
+ awk 'NR==1 {next} {print $2}')
fi
if [ -z "${count}" ]; then
mptcp_lib_fail_if_expected_feature "${counter} counter"