From: Anil Kaushik <anilkaushikwireless@gmail.com>
To: mptcp@lists.linux.dev
Cc: Anil Kaushik <anilkaushikwireless@gmail.com>
Subject: [PATCH mptcp-next v7 6/6] selftests: mptcp: join: validate subflow retry after link failure
Date: Tue, 20 Oct 2026 03:17:11 +0000 [thread overview]
Message-ID: <20261020031711.1896660-7-anilkaushikwireless@gmail.com> (raw)
In-Reply-To: <20261020031711.1896660-1-anilkaushikwireless@gmail.com>
Test the subflow-retries limit, with its default value, and a long
connection in the background: drop the packets the client sends from
the additional subflow address with netfilter, without bringing the link
down, until the subflow is closed and re-created. Let them through again
once the SYN of the new subflow has been retransmitted more times than
tcp_syn_retries allows, and check the subflow comes back from the same
address. Then do the same with the initial subflow.
Finally, close the connection while a subflow is being re-created, and
check the netlink events. The big files used for the long connection
are emptied at the end.
The test is skipped if pm_nl_ctl does not show the new limit, or if the
filter rules cannot be set.
Assisted-by: LLM
Signed-off-by: Anil Kaushik <anilkaushikwireless@gmail.com>
---
Notes:
v6 (Sashiko):
- use the slowwait helpers from net/lib.sh
- longer first wait, while the 128 MB files are created
- skip if the filter rules cannot be set, check each drop rule
(netdev Sashiko)
- empty the big files at the end, cinsent too, keep the variables
(netdev Sashiko)
v5 (Matthieu):
- skipped if pm_nl_ctl does not show the new limit
- wait for the SYN retransmissions instead of a sleep, with
tcp_syn_linear_timeouts=0
- helper for the check after the close
- slower links with short queues, so the transfer lasts the whole
test on any machine, longer timeout (killed at the end)
v4:
- default limit, no sysctl or MIB counter
- more SYN retransmissions than tcp_syn_retries
- close the connection while a subflow is re-created
- check the netlink events
- check the waits and the new subflow port, remove the big files at
the end (Sashiko)
v3:
- check the subflow address, create the files before the background job
v2:
- one test, with netfilter
.../testing/selftests/net/mptcp/mptcp_join.sh | 178 +++++++++++++++++-
1 file changed, 174 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh
index f4d8afaeda4f..a99cee39da63 100755
--- a/tools/testing/selftests/net/mptcp/mptcp_join.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh
@@ -1137,7 +1137,7 @@ run_tests()
make_file "$cinfail" "client" $size
# create the input file for the failure test when
# the first failure test run
- elif [ "$test_linkfail" -ne 0 ] && [ -z "$cinfail" ]; then
+ elif [ "$test_linkfail" -ne 0 ] && [ ! -s "$cinfail" ]; then
# the client file must be considerably larger
# of the maximum expected cwin value, or the
# link utilization will be not predicable
@@ -1146,7 +1146,9 @@ run_tests()
size=$((size*8192))
size=$((size + ( RANDOM % 8192) ))
- cinfail=$(mktemp)
+ if [ -z "$cinfail" ]; then
+ cinfail=$(mktemp)
+ fi
make_file "$cinfail" "client" $size
fi
@@ -1157,12 +1159,14 @@ run_tests()
sinfail=$(mktemp)
fi
make_file "$sinfail" "server" $size
- elif [ "$test_linkfail" -eq 2 ] && [ -z "$sinfail" ]; then
+ elif [ "$test_linkfail" -eq 2 ] && [ ! -s "$sinfail" ]; then
size=$((RANDOM%16))
size=$((size+1))
size=$((size*2048))
- sinfail=$(mktemp)
+ if [ -z "$sinfail" ]; then
+ sinfail=$(mktemp)
+ fi
make_file "$sinfail" "server" $size
fi
@@ -2151,6 +2155,113 @@ wait_attempt_fail()
return 1
}
+# $1: timeout in seconds ; $2: ns ; $3: counter ; $4: increment, 1 by default
+wait_counter_inc()
+{
+ slowwait_for_counter "${1}" "${4:-1}" \
+ mptcp_lib_get_counter "${2}" "${3}" >/dev/null
+}
+
+# $1: client address ; prints the local address and port of its subflow
+sf_from()
+{
+ ip netns exec $ns2 ss -tnH state established src "${1}" |
+ awk '{ print $3; exit }'
+}
+
+has_subflow_retries()
+{
+ ip netns exec $ns2 ./pm_nl_ctl limits | grep -q "^subflow_retries"
+}
+
+# the client packets are dropped with rules in this chain
+init_nft_filter()
+{
+ init_nftables
+ if ! ip netns exec $ns2 nft list chain ip filter OUTPUT \
+ &>/dev/null; then
+ mark_as_skipped "unable to set the filter rules"
+ return 1
+ fi
+}
+
+# $1: client address
+nft_drop_from()
+{
+ ip netns exec $ns2 nft add rule ip filter OUTPUT ip saddr "${1}" drop
+}
+
+# $1: client address to block
+#
+# The link stays up, but the client packets from this address are dropped:
+# the subflow using it is closed after a few retransmissions, and re-created.
+# The packets go through again once the SYN has been retransmitted more times
+# than tcp_syn_retries allows: the new subflow still connects thanks to the
+# subflow-retries limit.
+sf_retry_after_block()
+{
+ local before ack_rx
+
+ before=$(sf_from "${1}")
+
+ print_check "subflow from ${1}"
+ if [ -z "${before}" ]; then
+ fail_test "no established subflow from ${1} before the block"
+ return
+ fi
+
+ if ! nft_drop_from "${1}"; then
+ fail_test "unable to drop the packets from ${1}"
+ return
+ fi
+ if ! wait_counter_inc "${timeout_poll}" $ns2 "MPTcpExtMPJoinSynTx"; then
+ ip netns exec $ns2 nft flush chain ip filter OUTPUT
+ fail_test "subflow from ${1} not re-created"
+ return
+ fi
+ # tcp_syn_retries=1: one more retransmission than allowed
+ if ! wait_counter_inc "${timeout_poll}" \
+ $ns2 "TcpExtTCPSynRetrans" 2; then
+ ip netns exec $ns2 nft flush chain ip filter OUTPUT
+ fail_test "SYN from ${1} not retransmitted enough"
+ return
+ fi
+ # read before the flush: the next SYN can be sent right after it
+ ack_rx=$(mptcp_lib_get_counter $ns1 "MPTcpExtMPJoinAckRx")
+ ip netns exec $ns2 nft flush chain ip filter OUTPUT
+
+ if ! slowwait "${timeout_poll}" until_counter_is ">= $((ack_rx + 1))" \
+ mptcp_lib_get_counter $ns1 "MPTcpExtMPJoinAckRx" >/dev/null; then
+ fail_test "re-created subflow from ${1} not established"
+ return
+ fi
+
+ if [ -z "$(sf_from "${1}")" ]; then
+ fail_test "no established subflow from ${1}"
+ else
+ print_ok
+ fi
+}
+
+no_sf_connecting()
+{
+ ! ip netns exec $ns2 ss -tnH state syn-sent | grep -q .
+}
+
+# $1: result of the wait for the subflow being re-created before the close
+chk_no_sf_connecting()
+{
+ print_check "no subflow connecting after close"
+ if [ "${1}" -ne 0 ]; then
+ fail_test "no subflow re-created before the close"
+ # closed with the MPTCP socket
+ elif slowwait "${timeout_poll}" no_sf_connecting; then
+ print_ok
+ else
+ fail_test "subflow still connecting after close"
+ fi
+}
+
set_userspace_pm()
{
local ns=$1
@@ -2585,6 +2696,65 @@ link_failure_tests()
chk_stale_nr $ns2 1 -1 2
chk_link_usage $ns2 ns2eth3 $cinsent 50
fi
+
+ # subflows closed after a silent link failure, then re-created: the
+ # additional one, then the initial one. Then close the connection
+ # while a subflow is being re-created.
+ if reset_with_events "subflow retry after link failure" &&
+ continue_if has_subflow_retries && init_nft_filter; then
+ # slow links, short queues: the transfer lasts the whole test on
+ # any machine, and the blocked subflow always has data in flight
+ local i
+ for i in 1 3; do
+ tc -n $ns1 qdisc add dev ns1eth$i root netem \
+ rate 4mbit limit 30 delay 1ms
+ tc -n $ns2 qdisc add dev ns2eth$i root netem \
+ rate 4mbit limit 30 delay 1ms
+ done
+ ip netns exec $ns2 sysctl -q net.ipv4.tcp_retries2=3
+ ip netns exec $ns2 sysctl -q net.ipv4.tcp_syn_retries=1
+ ip netns exec $ns2 sysctl -q net.ipv4.tcp_syn_linear_timeouts=0
+ pm_nl_set_limits $ns1 0 4
+ pm_nl_set_limits $ns2 0 2
+ pm_nl_add_endpoint $ns2 10.0.3.2 dev ns2eth3 flags subflow
+ # created here, not in the background job, for cleanup()
+ if [ -z "$cinfail" ]; then
+ cinfail=$(mktemp)
+ fi
+ if [ -z "$sinfail" ]; then
+ sinfail=$(mktemp)
+ fi
+ # killed at the end: the timeout only stops a stuck test
+ { timeout_test=600 test_linkfail=131072 \
+ run_tests $ns1 $ns2 10.0.1.1 & } 2>/dev/null
+ local tests_pid=$!
+
+ # the 128 MB files are created first, that can take a while
+ wait_counter_inc $((timeout_poll * 4)) \
+ $ns1 "MPTcpExtMPJoinAckRx"
+ sf_retry_after_block 10.0.3.2
+ sf_retry_after_block 10.0.1.2
+
+ local retrying=1
+ if nft_drop_from 10.0.3.2; then
+ wait_counter_inc "${timeout_poll}" \
+ $ns2 "MPTcpExtMPJoinSynTx"
+ retrying=$?
+ fi
+ mptcp_lib_kill_group_wait $tests_pid
+ chk_no_sf_connecting ${retrying}
+ ip netns exec $ns2 nft flush chain ip filter OUTPUT
+ kill_events_pids
+
+ join_syn_tx=4 chk_join_nr 3 3 3
+ chk_evt_nr ns2 MPTCP_LIB_EVENT_SUB_ESTABLISHED 3
+ chk_evt_nr ns2 MPTCP_LIB_EVENT_SUB_CLOSED 3 error 3
+
+ # big files: empty them, the next tests write their own content
+ : > "$cinfail"
+ : > "$sinfail"
+ : > "$cinsent"
+ fi
}
add_addr_timeout_tests()
--
2.25.1
next prev parent reply other threads:[~2026-10-20 3:17 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-20 3:17 [PATCH mptcp-next v7 0/6] mptcp: pm: in-kernel: retry subflows closed after an error Anil Kaushik
2026-10-20 3:17 ` [PATCH mptcp-next v7 1/6] selftests: mptcp: connect: retry write() on EAGAIN Anil Kaushik
2026-10-20 3:17 ` [PATCH mptcp-next v7 2/6] netlink: specs: mptcp: add the subflow-retries attribute Anil Kaushik
2026-10-20 3:17 ` [PATCH mptcp-next v7 3/6] mptcp: pm: in-kernel: add the subflow-retries limit Anil Kaushik
2026-10-22 10:19 ` Matthieu Baerts
2026-10-20 3:17 ` [PATCH mptcp-next v7 4/6] mptcp: pm: in-kernel: retry subflows closed after an error Anil Kaushik
2026-10-20 8:37 ` sashiko-bot
2026-10-21 16:36 ` netdev-bot+sashiko
2026-10-20 3:17 ` [PATCH mptcp-next v7 5/6] selftests: mptcp: pm_nl_ctl: support the subflow-retries limit Anil Kaushik
2026-10-20 3:17 ` Anil Kaushik [this message]
2026-10-21 16:36 ` [PATCH mptcp-next v7 6/6] selftests: mptcp: join: validate subflow retry after link failure netdev-bot+sashiko
2026-10-21 12:44 ` [PATCH mptcp-next v7 0/6] mptcp: pm: in-kernel: retry subflows closed after an error MPTCP CI
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261020031711.1896660-7-anilkaushikwireless@gmail.com \
--to=anilkaushikwireless@gmail.com \
--cc=mptcp@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.