1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# Double quotes to prevent globbing and word splitting is recommended in new
5# code but we accept it, especially because there were too many before having
6# address all other issues detected by shellcheck.
7#shellcheck disable=SC2086
8
9. "$(dirname "${0}")/mptcp_lib.sh"
10
11time_start=$(date +%s)
12
13optstring="S:R:d:e:l:r:h4cm:f:tC"
14ret=0
15final_ret=0
16sin=""
17sout=""
18cin_disconnect=""
19cin=""
20cout=""
21capture=false
22timeout_poll=30
23timeout_test=$((timeout_poll * 2 + 1))
24ipv6=true
25ethtool_random_on=true
26tc_delay="$((RANDOM%50))"
27tc_loss=$((RANDOM%101))
28testmode=""
29sndbuf=0
30rcvbuf=0
31options_log=true
32do_tcp=0
33checksum=false
34filesize=0
35connect_per_transfer=1
36port=$((10000 - 1))
37
38if [ $tc_loss -eq 100 ];then
39	tc_loss=1%
40elif [ $tc_loss -ge 10 ]; then
41	tc_loss=0.$tc_loss%
42elif [ $tc_loss -ge 1 ]; then
43	tc_loss=0.0$tc_loss%
44else
45	tc_loss=""
46fi
47
48usage() {
49	echo "Usage: $0 [ -a ]"
50	echo -e "\t-d: tc/netem delay in milliseconds, e.g. \"-d 10\" (default random)"
51	echo -e "\t-l: tc/netem loss percentage, e.g. \"-l 0.02\" (default random)"
52	echo -e "\t-r: tc/netem reorder mode, e.g. \"-r 25% 50% gap 5\", use "-r 0" to disable reordering (default random)"
53	echo -e "\t-e: ethtool features to disable, e.g.: \"-e tso -e gso\" (default: randomly disable any of tso/gso/gro)"
54	echo -e "\t-4: IPv4 only: disable IPv6 tests (default: test both IPv4 and IPv6)"
55	echo -e "\t-c: capture packets for each test using tcpdump (default: no capture)"
56	echo -e "\t-f: size of file to transfer in bytes (default random)"
57	echo -e "\t-S: set sndbuf value (default: use kernel default)"
58	echo -e "\t-R: set rcvbuf value (default: use kernel default)"
59	echo -e "\t-m: test mode (poll, sendfile; default: poll)"
60	echo -e "\t-t: also run tests with TCP (use twice to non-fallback tcp)"
61	echo -e "\t-C: enable the MPTCP data checksum"
62}
63
64while getopts "$optstring" option;do
65	case "$option" in
66	"h")
67		usage $0
68		exit ${KSFT_PASS}
69		;;
70	"d")
71		if [ $OPTARG -ge 0 ];then
72			tc_delay="$OPTARG"
73		else
74			echo "-d requires numeric argument, got \"$OPTARG\"" 1>&2
75			exit ${KSFT_FAIL}
76		fi
77		;;
78	"e")
79		ethtool_args="$ethtool_args $OPTARG off"
80		ethtool_random_on=false
81		;;
82	"l")
83		tc_loss="$OPTARG"
84		;;
85	"r")
86		tc_reorder="$OPTARG"
87		;;
88	"4")
89		ipv6=false
90		;;
91	"c")
92		capture=true
93		;;
94	"S")
95		if [ $OPTARG -ge 0 ];then
96			sndbuf="$OPTARG"
97		else
98			echo "-S requires numeric argument, got \"$OPTARG\"" 1>&2
99			exit ${KSFT_FAIL}
100		fi
101		;;
102	"R")
103		if [ $OPTARG -ge 0 ];then
104			rcvbuf="$OPTARG"
105		else
106			echo "-R requires numeric argument, got \"$OPTARG\"" 1>&2
107			exit ${KSFT_FAIL}
108		fi
109		;;
110	"m")
111		testmode="$OPTARG"
112		;;
113	"f")
114		filesize="$OPTARG"
115		;;
116	"t")
117		do_tcp=$((do_tcp+1))
118		;;
119	"C")
120		checksum=true
121		;;
122	"?")
123		usage $0
124		exit ${KSFT_FAIL}
125		;;
126	esac
127done
128
129ns1=""
130ns2=""
131ns3=""
132ns4=""
133
134TEST_GROUP=""
135
136# This function is used in the cleanup trap
137#shellcheck disable=SC2317
138cleanup()
139{
140	rm -f "$cin_disconnect"
141	rm -f "$cin" "$cout"
142	rm -f "$sin" "$sout"
143	rm -f "$capout"
144
145	mptcp_lib_ns_exit "${ns1}" "${ns2}" "${ns3}" "${ns4}"
146}
147
148mptcp_lib_check_mptcp
149mptcp_lib_check_kallsyms
150mptcp_lib_check_tools ip tc
151
152sin=$(mktemp)
153sout=$(mktemp)
154cin=$(mktemp)
155cout=$(mktemp)
156capout=$(mktemp)
157cin_disconnect="$cin".disconnect
158trap cleanup EXIT
159
160mptcp_lib_ns_init ns1 ns2 ns3 ns4
161
162#  "$ns1"              ns2                    ns3                     ns4
163# ns1eth2    ns2eth1   ns2eth3      ns3eth2   ns3eth4       ns4eth3
164#                           - drop 1% ->            reorder 25%
165#                           <- TSO off -
166
167ip link add ns1eth2 netns "$ns1" type veth peer name ns2eth1 netns "$ns2"
168ip link add ns2eth3 netns "$ns2" type veth peer name ns3eth2 netns "$ns3"
169ip link add ns3eth4 netns "$ns3" type veth peer name ns4eth3 netns "$ns4"
170
171ip -net "$ns1" addr add 10.0.1.1/24 dev ns1eth2
172ip -net "$ns1" addr add dead:beef:1::1/64 dev ns1eth2 nodad
173
174ip -net "$ns1" link set ns1eth2 up
175ip -net "$ns1" route add default via 10.0.1.2
176ip -net "$ns1" route add default via dead:beef:1::2
177
178ip -net "$ns2" addr add 10.0.1.2/24 dev ns2eth1
179ip -net "$ns2" addr add dead:beef:1::2/64 dev ns2eth1 nodad
180ip -net "$ns2" link set ns2eth1 up
181
182ip -net "$ns2" addr add 10.0.2.1/24 dev ns2eth3
183ip -net "$ns2" addr add dead:beef:2::1/64 dev ns2eth3 nodad
184ip -net "$ns2" link set ns2eth3 up
185ip -net "$ns2" route add default via 10.0.2.2
186ip -net "$ns2" route add default via dead:beef:2::2
187ip netns exec "$ns2" sysctl -q net.ipv4.ip_forward=1
188ip netns exec "$ns2" sysctl -q net.ipv6.conf.all.forwarding=1
189
190ip -net "$ns3" addr add 10.0.2.2/24 dev ns3eth2
191ip -net "$ns3" addr add dead:beef:2::2/64 dev ns3eth2 nodad
192ip -net "$ns3" link set ns3eth2 up
193
194ip -net "$ns3" addr add 10.0.3.2/24 dev ns3eth4
195ip -net "$ns3" addr add dead:beef:3::2/64 dev ns3eth4 nodad
196ip -net "$ns3" link set ns3eth4 up
197ip -net "$ns3" route add default via 10.0.2.1
198ip -net "$ns3" route add default via dead:beef:2::1
199ip netns exec "$ns3" sysctl -q net.ipv4.ip_forward=1
200ip netns exec "$ns3" sysctl -q net.ipv6.conf.all.forwarding=1
201
202ip -net "$ns4" addr add 10.0.3.1/24 dev ns4eth3
203ip -net "$ns4" addr add dead:beef:3::1/64 dev ns4eth3 nodad
204ip -net "$ns4" link set ns4eth3 up
205ip -net "$ns4" route add default via 10.0.3.2
206ip -net "$ns4" route add default via dead:beef:3::2
207
208if $checksum; then
209	for i in "$ns1" "$ns2" "$ns3" "$ns4";do
210		ip netns exec $i sysctl -q net.mptcp.checksum_enabled=1
211	done
212fi
213
214set_ethtool_flags() {
215	local ns="$1"
216	local dev="$2"
217	local flags="$3"
218
219	if ip netns exec $ns ethtool -K $dev $flags 2>/dev/null; then
220		mptcp_lib_pr_info "set $ns dev $dev: ethtool -K $flags"
221	fi
222}
223
224set_random_ethtool_flags() {
225	local flags=""
226	local r=$RANDOM
227
228	local pick1=$((r & 1))
229	local pick2=$((r & 2))
230	local pick3=$((r & 4))
231
232	[ $pick1 -ne 0 ] && flags="tso off"
233	[ $pick2 -ne 0 ] && flags="$flags gso off"
234	[ $pick3 -ne 0 ] && flags="$flags gro off"
235
236	[ -z "$flags" ] && return
237
238	set_ethtool_flags "$1" "$2" "$flags"
239}
240
241if $ethtool_random_on;then
242	set_random_ethtool_flags "$ns3" ns3eth2
243	set_random_ethtool_flags "$ns4" ns4eth3
244else
245	set_ethtool_flags "$ns3" ns3eth2 "$ethtool_args"
246	set_ethtool_flags "$ns4" ns4eth3 "$ethtool_args"
247fi
248
249print_larger_title() {
250	# here we don't have the time, a bit longer for the alignment
251	MPTCP_LIB_TEST_FORMAT="%02u %-69s" \
252		mptcp_lib_print_title "${@}"
253}
254
255check_mptcp_disabled()
256{
257	local disabled_ns
258	mptcp_lib_ns_init disabled_ns
259
260	print_larger_title "New MPTCP socket can be blocked via sysctl"
261
262	# mainly to cover more code
263	if ! ip netns exec ${disabled_ns} sysctl net.mptcp >/dev/null; then
264		mptcp_lib_pr_fail "not able to list net.mptcp sysctl knobs"
265		mptcp_lib_result_fail "not able to list net.mptcp sysctl knobs"
266		ret=${KSFT_FAIL}
267		return 1
268	fi
269
270	# net.mptcp.enabled should be enabled by default
271	if [ "$(ip netns exec ${disabled_ns} sysctl net.mptcp.enabled | awk '{ print $3 }')" -ne 1 ]; then
272		mptcp_lib_pr_fail "net.mptcp.enabled sysctl is not 1 by default"
273		mptcp_lib_result_fail "net.mptcp.enabled sysctl is not 1 by default"
274		ret=${KSFT_FAIL}
275		return 1
276	fi
277	ip netns exec ${disabled_ns} sysctl -q net.mptcp.enabled=0
278
279	local err=0
280	LC_ALL=C ip netns exec ${disabled_ns} ./mptcp_connect -p 10000 -s MPTCP 127.0.0.1 < "$cin" 2>&1 | \
281		grep -q "^socket: Protocol not available$" && err=1
282	mptcp_lib_ns_exit "${disabled_ns}"
283
284	if [ ${err} -eq 0 ]; then
285		mptcp_lib_pr_fail "New MPTCP socket cannot be blocked via sysctl"
286		mptcp_lib_result_fail "New MPTCP socket cannot be blocked via sysctl"
287		ret=${KSFT_FAIL}
288		return 1
289	fi
290
291	mptcp_lib_pr_ok
292	mptcp_lib_result_pass "New MPTCP socket can be blocked via sysctl"
293	return 0
294}
295
296do_ping()
297{
298	local listener_ns="$1"
299	local connector_ns="$2"
300	local connect_addr="$3"
301	local ping_args="-q -c 1"
302	local rc=0
303
304	if mptcp_lib_is_v6 "${connect_addr}"; then
305		$ipv6 || return 0
306		ping_args="${ping_args} -6"
307	fi
308
309	ip netns exec ${connector_ns} ping ${ping_args} $connect_addr >/dev/null || rc=1
310
311	if [ $rc -ne 0 ] ; then
312		mptcp_lib_pr_fail "$listener_ns -> $connect_addr connectivity"
313		ret=${KSFT_FAIL}
314
315		return 1
316	fi
317
318	return 0
319}
320
321do_transfer()
322{
323	local listener_ns="$1"
324	local connector_ns="$2"
325	local cl_proto="$3"
326	local srv_proto="$4"
327	local connect_addr="$5"
328	local local_addr="$6"
329	local extra_args="$7"
330
331	port=$((port + 1))
332
333	if [ "$rcvbuf" -gt 0 ]; then
334		extra_args+=" -R $rcvbuf"
335	fi
336
337	if [ "$sndbuf" -gt 0 ]; then
338		extra_args+=" -S $sndbuf"
339	fi
340
341	if [ -n "$testmode" ]; then
342		extra_args+=" -m $testmode"
343	fi
344
345	if [ -n "$extra_args" ] && $options_log; then
346		mptcp_lib_pr_info "extra options: $extra_args"
347	fi
348	options_log=false
349
350	:> "$cout"
351	:> "$sout"
352	:> "$capout"
353
354	local addr_port
355	addr_port=$(printf "%s:%d" ${connect_addr} ${port})
356	local pretty_title
357	pretty_title="$(printf "%.3s %-5s -> %.3s (%-20s) %-5s" ${connector_ns} ${cl_proto} ${listener_ns} ${addr_port} ${srv_proto})"
358	mptcp_lib_print_title "${pretty_title}"
359
360	local tap_title="${connector_ns:0:3} ${cl_proto} -> ${listener_ns:0:3} (${addr_port}) ${srv_proto}"
361
362	if $capture; then
363		local capuser
364		local rndh="${connector_ns:4}"
365		if [ -z $SUDO_USER ] ; then
366			capuser=""
367		else
368			capuser="-Z $SUDO_USER"
369		fi
370
371		local capfile="${rndh}-${connector_ns:0:3}-${listener_ns:0:3}-${cl_proto}-${srv_proto}-${connect_addr}-${port}"
372		local capopt="-i any -s 65535 -B 32768 ${capuser}"
373
374		ip netns exec ${listener_ns}  tcpdump ${capopt} -w "${capfile}-listener.pcap"  >> "${capout}" 2>&1 &
375		local cappid_listener=$!
376
377		ip netns exec ${connector_ns} tcpdump ${capopt} -w "${capfile}-connector.pcap" >> "${capout}" 2>&1 &
378		local cappid_connector=$!
379
380		sleep 1
381	fi
382
383	NSTAT_HISTORY=/tmp/${listener_ns}.nstat ip netns exec ${listener_ns} \
384		nstat -n
385	if [ ${listener_ns} != ${connector_ns} ]; then
386		NSTAT_HISTORY=/tmp/${connector_ns}.nstat ip netns exec ${connector_ns} \
387			nstat -n
388	fi
389
390	local stat_synrx_last_l
391	local stat_ackrx_last_l
392	local stat_cookietx_last
393	local stat_cookierx_last
394	local stat_csum_err_s
395	local stat_csum_err_c
396	local stat_tcpfb_last_l
397	stat_synrx_last_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableSYNRX")
398	stat_ackrx_last_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableACKRX")
399	stat_cookietx_last=$(mptcp_lib_get_counter "${listener_ns}" "TcpExtSyncookiesSent")
400	stat_cookierx_last=$(mptcp_lib_get_counter "${listener_ns}" "TcpExtSyncookiesRecv")
401	stat_csum_err_s=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtDataCsumErr")
402	stat_csum_err_c=$(mptcp_lib_get_counter "${connector_ns}" "MPTcpExtDataCsumErr")
403	stat_tcpfb_last_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableFallbackACK")
404
405	timeout ${timeout_test} \
406		ip netns exec ${listener_ns} \
407			./mptcp_connect -t ${timeout_poll} -l -p $port -s ${srv_proto} \
408				$extra_args $local_addr < "$sin" > "$sout" &
409	local spid=$!
410
411	mptcp_lib_wait_local_port_listen "${listener_ns}" "${port}"
412
413	local start
414	start=$(date +%s%3N)
415	timeout ${timeout_test} \
416		ip netns exec ${connector_ns} \
417			./mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \
418				$extra_args $connect_addr < "$cin" > "$cout" &
419	local cpid=$!
420
421	wait $cpid
422	local retc=$?
423	wait $spid
424	local rets=$?
425
426	local stop
427	stop=$(date +%s%3N)
428
429	if $capture; then
430		sleep 1
431		kill ${cappid_listener}
432		kill ${cappid_connector}
433	fi
434
435	NSTAT_HISTORY=/tmp/${listener_ns}.nstat ip netns exec ${listener_ns} \
436		nstat | grep Tcp > /tmp/${listener_ns}.out
437	if [ ${listener_ns} != ${connector_ns} ]; then
438		NSTAT_HISTORY=/tmp/${connector_ns}.nstat ip netns exec ${connector_ns} \
439			nstat | grep Tcp > /tmp/${connector_ns}.out
440	fi
441
442	local duration
443	duration=$((stop-start))
444	printf "(duration %05sms) " "${duration}"
445	if [ ${rets} -ne 0 ] || [ ${retc} -ne 0 ]; then
446		mptcp_lib_pr_fail "client exit code $retc, server $rets"
447		mptcp_lib_pr_err_stats "${listener_ns}" "${connector_ns}" "${port}" \
448			"/tmp/${listener_ns}.out" "/tmp/${connector_ns}.out"
449
450		echo
451		cat "$capout"
452		mptcp_lib_result_fail "${TEST_GROUP}: ${tap_title}"
453		return 1
454	fi
455
456	mptcp_lib_check_transfer $sin $cout "file received by client"
457	retc=$?
458	mptcp_lib_check_transfer $cin $sout "file received by server"
459	rets=$?
460
461	local extra=""
462	local stat_synrx_now_l
463	local stat_ackrx_now_l
464	local stat_cookietx_now
465	local stat_cookierx_now
466	local stat_ooo_now
467	local stat_tcpfb_now_l
468	stat_synrx_now_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableSYNRX")
469	stat_ackrx_now_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableACKRX")
470	stat_cookietx_now=$(mptcp_lib_get_counter "${listener_ns}" "TcpExtSyncookiesSent")
471	stat_cookierx_now=$(mptcp_lib_get_counter "${listener_ns}" "TcpExtSyncookiesRecv")
472	stat_ooo_now=$(mptcp_lib_get_counter "${listener_ns}" "TcpExtTCPOFOQueue")
473	stat_tcpfb_now_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableFallbackACK")
474
475	expect_synrx=$((stat_synrx_last_l))
476	expect_ackrx=$((stat_ackrx_last_l))
477
478	cookies=$(ip netns exec ${listener_ns} sysctl net.ipv4.tcp_syncookies)
479	cookies=${cookies##*=}
480
481	if [ ${cl_proto} = "MPTCP" ] && [ ${srv_proto} = "MPTCP" ]; then
482		expect_synrx=$((stat_synrx_last_l+connect_per_transfer))
483		expect_ackrx=$((stat_ackrx_last_l+connect_per_transfer))
484	fi
485
486	if [ ${stat_synrx_now_l} -lt ${expect_synrx} ]; then
487		mptcp_lib_pr_fail "lower MPC SYN rx (${stat_synrx_now_l})" \
488				  "than expected (${expect_synrx})"
489		retc=1
490	fi
491	if [ ${stat_ackrx_now_l} -lt ${expect_ackrx} ] && [ ${stat_ooo_now} -eq 0 ]; then
492		if [ ${stat_ooo_now} -eq 0 ]; then
493			mptcp_lib_pr_fail "lower MPC ACK rx (${stat_ackrx_now_l})" \
494					  "than expected (${expect_ackrx})"
495			rets=1
496		else
497			extra+=" [ Note ] fallback due to TCP OoO"
498		fi
499	fi
500
501	if $checksum; then
502		local csum_err_s
503		local csum_err_c
504		csum_err_s=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtDataCsumErr")
505		csum_err_c=$(mptcp_lib_get_counter "${connector_ns}" "MPTcpExtDataCsumErr")
506
507		local csum_err_s_nr=$((csum_err_s - stat_csum_err_s))
508		if [ $csum_err_s_nr -gt 0 ]; then
509			mptcp_lib_pr_fail "server got ${csum_err_s_nr} data checksum error[s]"
510			rets=1
511		fi
512
513		local csum_err_c_nr=$((csum_err_c - stat_csum_err_c))
514		if [ $csum_err_c_nr -gt 0 ]; then
515			mptcp_lib_pr_fail "client got ${csum_err_c_nr} data checksum error[s]"
516			retc=1
517		fi
518	fi
519
520	if [ ${stat_ooo_now} -eq 0 ] && [ ${stat_tcpfb_last_l} -ne ${stat_tcpfb_now_l} ]; then
521		mptcp_lib_pr_fail "unexpected fallback to TCP"
522		rets=1
523	fi
524
525	if [ $cookies -eq 2 ];then
526		if [ $stat_cookietx_last -ge $stat_cookietx_now ] ;then
527			extra+=" WARN: CookieSent: did not advance"
528		fi
529		if [ $stat_cookierx_last -ge $stat_cookierx_now ] ;then
530			extra+=" WARN: CookieRecv: did not advance"
531		fi
532	else
533		if [ $stat_cookietx_last -ne $stat_cookietx_now ] ;then
534			extra+=" WARN: CookieSent: changed"
535		fi
536		if [ $stat_cookierx_last -ne $stat_cookierx_now ] ;then
537			extra+=" WARN: CookieRecv: changed"
538		fi
539	fi
540
541	if [ ${stat_synrx_now_l} -gt ${expect_synrx} ]; then
542		extra+=" WARN: SYNRX: expect ${expect_synrx},"
543		extra+=" got ${stat_synrx_now_l} (probably retransmissions)"
544	fi
545	if [ ${stat_ackrx_now_l} -gt ${expect_ackrx} ]; then
546		extra+=" WARN: ACKRX: expect ${expect_ackrx},"
547		extra+=" got ${stat_ackrx_now_l} (probably retransmissions)"
548	fi
549
550	if [ $retc -eq 0 ] && [ $rets -eq 0 ]; then
551		mptcp_lib_pr_ok "${extra:1}"
552		mptcp_lib_result_pass "${TEST_GROUP}: ${tap_title}"
553	else
554		if [ -n "${extra}" ]; then
555			mptcp_lib_print_warn "${extra:1}"
556		fi
557		mptcp_lib_result_fail "${TEST_GROUP}: ${tap_title}"
558	fi
559
560	cat "$capout"
561	[ $retc -eq 0 ] && [ $rets -eq 0 ]
562}
563
564make_file()
565{
566	local name=$1
567	local who=$2
568	local SIZE=$filesize
569	local ksize
570	local rem
571
572	if [ $SIZE -eq 0 ]; then
573		local MAXSIZE=$((1024 * 1024 * 8))
574		local MINSIZE=$((1024 * 256))
575
576		SIZE=$(((RANDOM * RANDOM + MINSIZE) % MAXSIZE))
577	fi
578
579	ksize=$((SIZE / 1024))
580	rem=$((SIZE - (ksize * 1024)))
581
582	mptcp_lib_make_file $name 1024 $ksize
583	dd if=/dev/urandom conv=notrunc of="$name" oflag=append bs=1 count=$rem 2> /dev/null
584
585	echo "Created $name (size $(stat -c "%s" "$name") B) containing data sent by $who"
586}
587
588run_tests_lo()
589{
590	local listener_ns="$1"
591	local connector_ns="$2"
592	local connect_addr="$3"
593	local loopback="$4"
594	local extra_args="$5"
595	local lret=0
596
597	# skip if test programs are running inside same netns for subsequent runs.
598	if [ $loopback -eq 0 ] && [ ${listener_ns} = ${connector_ns} ]; then
599		return 0
600	fi
601
602	# skip if we don't want v6
603	if ! $ipv6 && mptcp_lib_is_v6 "${connect_addr}"; then
604		return 0
605	fi
606
607	local local_addr
608	if mptcp_lib_is_v6 "${connect_addr}"; then
609		local_addr="::"
610	else
611		local_addr="0.0.0.0"
612	fi
613
614	do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP \
615		    ${connect_addr} ${local_addr} "${extra_args}"
616	lret=$?
617	if [ $lret -ne 0 ]; then
618		ret=$lret
619		return 1
620	fi
621
622	if [ $do_tcp -eq 0 ]; then
623		# don't bother testing fallback tcp except for loopback case.
624		if [ ${listener_ns} != ${connector_ns} ]; then
625			return 0
626		fi
627	fi
628
629	do_transfer ${listener_ns} ${connector_ns} MPTCP TCP \
630		    ${connect_addr} ${local_addr} "${extra_args}"
631	lret=$?
632	if [ $lret -ne 0 ]; then
633		ret=$lret
634		return 1
635	fi
636
637	do_transfer ${listener_ns} ${connector_ns} TCP MPTCP \
638		    ${connect_addr} ${local_addr} "${extra_args}"
639	lret=$?
640	if [ $lret -ne 0 ]; then
641		ret=$lret
642		return 1
643	fi
644
645	if [ $do_tcp -gt 1 ] ;then
646		do_transfer ${listener_ns} ${connector_ns} TCP TCP \
647			    ${connect_addr} ${local_addr} "${extra_args}"
648		lret=$?
649		if [ $lret -ne 0 ]; then
650			ret=$lret
651			return 1
652		fi
653	fi
654
655	return 0
656}
657
658run_tests()
659{
660	run_tests_lo $1 $2 $3 0
661}
662
663run_test_transparent()
664{
665	local connect_addr="$1"
666	local msg="$2"
667
668	local connector_ns="$ns1"
669	local listener_ns="$ns2"
670	local lret=0
671	local r6flag=""
672
673	TEST_GROUP="${msg}"
674
675	# skip if we don't want v6
676	if ! $ipv6 && mptcp_lib_is_v6 "${connect_addr}"; then
677		return 0
678	fi
679
680	# IP(V6)_TRANSPARENT has been added after TOS support which came with
681	# the required infrastructure in MPTCP sockopt code. To support TOS, the
682	# following function has been exported (T). Not great but better than
683	# checking for a specific kernel version.
684	if ! mptcp_lib_kallsyms_has "T __ip_sock_set_tos$"; then
685		mptcp_lib_pr_skip "${msg} not supported by the kernel"
686		mptcp_lib_result_skip "${TEST_GROUP}"
687		return
688	fi
689
690	if ! ip netns exec "$listener_ns" nft -f /dev/stdin <<"EOF"
691flush ruleset
692table inet mangle {
693	chain divert {
694		type filter hook prerouting priority -150;
695
696		meta l4proto tcp socket transparent 1 meta mark set 1 accept
697		tcp dport 20000 tproxy to :20000 meta mark set 1 accept
698	}
699}
700EOF
701	then
702		mptcp_lib_pr_skip "$msg, could not load nft ruleset"
703		mptcp_lib_fail_if_expected_feature "nft rules"
704		mptcp_lib_result_skip "${TEST_GROUP}"
705		return
706	fi
707
708	local local_addr
709	if mptcp_lib_is_v6 "${connect_addr}"; then
710		local_addr="::"
711		r6flag="-6"
712	else
713		local_addr="0.0.0.0"
714	fi
715
716	if ! ip -net "$listener_ns" $r6flag rule add fwmark 1 lookup 100; then
717		ip netns exec "$listener_ns" nft flush ruleset
718		mptcp_lib_pr_skip "$msg, ip $r6flag rule failed"
719		mptcp_lib_fail_if_expected_feature "ip rule"
720		mptcp_lib_result_skip "${TEST_GROUP}"
721		return
722	fi
723
724	if ! ip -net "$listener_ns" route add local $local_addr/0 dev lo table 100; then
725		ip netns exec "$listener_ns" nft flush ruleset
726		ip -net "$listener_ns" $r6flag rule del fwmark 1 lookup 100
727		mptcp_lib_pr_skip "$msg, ip route add local $local_addr failed"
728		mptcp_lib_fail_if_expected_feature "ip route"
729		mptcp_lib_result_skip "${TEST_GROUP}"
730		return
731	fi
732
733	mptcp_lib_pr_info "test $msg"
734
735	port=$((20000 - 1))
736	local extra_args="-o TRANSPARENT"
737	do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP \
738		    ${connect_addr} ${local_addr} "${extra_args}"
739	lret=$?
740
741	ip netns exec "$listener_ns" nft flush ruleset
742	ip -net "$listener_ns" $r6flag rule del fwmark 1 lookup 100
743	ip -net "$listener_ns" route del local $local_addr/0 dev lo table 100
744
745	if [ $lret -ne 0 ]; then
746		mptcp_lib_pr_fail "$msg, mptcp connection error"
747		ret=$lret
748		return 1
749	fi
750
751	mptcp_lib_pr_info "$msg pass"
752	return 0
753}
754
755run_tests_peekmode()
756{
757	local peekmode="$1"
758
759	TEST_GROUP="peek mode: ${peekmode}"
760	mptcp_lib_pr_info "with peek mode: ${peekmode}"
761	run_tests_lo "$ns1" "$ns1" 10.0.1.1 1 "-P ${peekmode}"
762	run_tests_lo "$ns1" "$ns1" dead:beef:1::1 1 "-P ${peekmode}"
763}
764
765run_tests_mptfo()
766{
767	TEST_GROUP="MPTFO"
768
769	if ! mptcp_lib_kallsyms_has "mptcp_fastopen_"; then
770		mptcp_lib_pr_skip "TFO not supported by the kernel"
771		mptcp_lib_result_skip "${TEST_GROUP}"
772		return
773	fi
774
775	mptcp_lib_pr_info "with MPTFO start"
776	ip netns exec "$ns1" sysctl -q net.ipv4.tcp_fastopen=2
777	ip netns exec "$ns2" sysctl -q net.ipv4.tcp_fastopen=1
778
779	run_tests_lo "$ns1" "$ns2" 10.0.1.1 0 "-o MPTFO"
780	run_tests_lo "$ns1" "$ns2" 10.0.1.1 0 "-o MPTFO"
781
782	run_tests_lo "$ns1" "$ns2" dead:beef:1::1 0 "-o MPTFO"
783	run_tests_lo "$ns1" "$ns2" dead:beef:1::1 0 "-o MPTFO"
784
785	ip netns exec "$ns1" sysctl -q net.ipv4.tcp_fastopen=0
786	ip netns exec "$ns2" sysctl -q net.ipv4.tcp_fastopen=0
787	mptcp_lib_pr_info "with MPTFO end"
788}
789
790run_tests_disconnect()
791{
792	local old_cin=$cin
793	local old_sin=$sin
794
795	TEST_GROUP="full disconnect"
796
797	if ! mptcp_lib_kallsyms_has "mptcp_pm_data_reset$"; then
798		mptcp_lib_pr_skip "Full disconnect not supported"
799		mptcp_lib_result_skip "${TEST_GROUP}"
800		return
801	fi
802
803	cat $cin $cin $cin > "$cin".disconnect
804
805	# force do_transfer to cope with the multiple transmissions
806	sin="$cin.disconnect"
807	cin="$cin.disconnect"
808	cin_disconnect="$old_cin"
809	connect_per_transfer=3
810
811	mptcp_lib_pr_info "disconnect"
812	run_tests_lo "$ns1" "$ns1" 10.0.1.1 1 "-I 3 -i $old_cin"
813	run_tests_lo "$ns1" "$ns1" dead:beef:1::1 1 "-I 3 -i $old_cin"
814
815	# restore previous status
816	sin=$old_sin
817	cin=$old_cin
818	cin_disconnect="$cin".disconnect
819	connect_per_transfer=1
820}
821
822display_time()
823{
824	time_end=$(date +%s)
825	time_run=$((time_end-time_start))
826
827	echo "Time: ${time_run} seconds"
828}
829
830log_if_error()
831{
832	local msg="$1"
833
834	if [ ${ret} -ne 0 ]; then
835		mptcp_lib_pr_fail "${msg}"
836
837		final_ret=${ret}
838		ret=${KSFT_PASS}
839
840		return ${final_ret}
841	fi
842}
843
844stop_if_error()
845{
846	if ! log_if_error "${@}"; then
847		display_time
848		mptcp_lib_result_print_all_tap
849		exit ${final_ret}
850	fi
851}
852
853make_file "$cin" "client"
854make_file "$sin" "server"
855
856mptcp_lib_subtests_last_ts_reset
857
858check_mptcp_disabled
859
860stop_if_error "The kernel configuration is not valid for MPTCP"
861
862print_larger_title "Validating network environment with pings"
863for sender in "$ns1" "$ns2" "$ns3" "$ns4";do
864	do_ping "$ns1" $sender 10.0.1.1
865	do_ping "$ns1" $sender dead:beef:1::1
866
867	do_ping "$ns2" $sender 10.0.1.2
868	do_ping "$ns2" $sender dead:beef:1::2
869	do_ping "$ns2" $sender 10.0.2.1
870	do_ping "$ns2" $sender dead:beef:2::1
871
872	do_ping "$ns3" $sender 10.0.2.2
873	do_ping "$ns3" $sender dead:beef:2::2
874	do_ping "$ns3" $sender 10.0.3.2
875	do_ping "$ns3" $sender dead:beef:3::2
876
877	do_ping "$ns4" $sender 10.0.3.1
878	do_ping "$ns4" $sender dead:beef:3::1
879done
880
881mptcp_lib_result_code "${ret}" "ping tests"
882
883stop_if_error "Could not even run ping tests"
884mptcp_lib_pr_ok
885
886[ -n "$tc_loss" ] && tc -net "$ns2" qdisc add dev ns2eth3 root netem loss random $tc_loss delay ${tc_delay}ms
887tc_info="loss of $tc_loss "
888test "$tc_delay" -gt 0 && tc_info+="delay $tc_delay ms "
889
890reorder_delay=$((tc_delay / 4))
891
892if [ -z "${tc_reorder}" ]; then
893	reorder1=$((RANDOM%10))
894	reorder1=$((100 - reorder1))
895	reorder2=$((RANDOM%100))
896
897	if [ $reorder_delay -gt 0 ] && [ $reorder1 -lt 100 ] && [ $reorder2 -gt 0 ]; then
898		tc_reorder="reorder ${reorder1}% ${reorder2}%"
899		tc_info+="$tc_reorder with delay ${reorder_delay}ms "
900	fi
901elif [ "$tc_reorder" = "0" ];then
902	tc_reorder=""
903elif [ "$reorder_delay" -gt 0 ];then
904	# reordering requires some delay
905	tc_reorder="reorder $tc_reorder"
906	tc_info+="$tc_reorder with delay ${reorder_delay}ms "
907fi
908
909mptcp_lib_pr_info "Using ${tc_info}on ns3eth4"
910
911tc -net "$ns3" qdisc add dev ns3eth4 root netem delay ${reorder_delay}ms $tc_reorder
912
913TEST_GROUP="loopback v4"
914run_tests_lo "$ns1" "$ns1" 10.0.1.1 1
915stop_if_error "Could not even run loopback test"
916
917TEST_GROUP="loopback v6"
918run_tests_lo "$ns1" "$ns1" dead:beef:1::1 1
919stop_if_error "Could not even run loopback v6 test"
920
921TEST_GROUP="multihosts"
922for sender in $ns1 $ns2 $ns3 $ns4;do
923	# ns1<->ns2 is not subject to reordering/tc delays. Use it to test
924	# mptcp syncookie support.
925	if [ $sender = $ns1 ]; then
926		ip netns exec "$ns2" sysctl -q net.ipv4.tcp_syncookies=2
927	else
928		ip netns exec "$ns2" sysctl -q net.ipv4.tcp_syncookies=1
929	fi
930
931	run_tests "$ns1" $sender 10.0.1.1
932	run_tests "$ns1" $sender dead:beef:1::1
933
934	run_tests "$ns2" $sender 10.0.1.2
935	run_tests "$ns2" $sender dead:beef:1::2
936	run_tests "$ns2" $sender 10.0.2.1
937	run_tests "$ns2" $sender dead:beef:2::1
938
939	run_tests "$ns3" $sender 10.0.2.2
940	run_tests "$ns3" $sender dead:beef:2::2
941	run_tests "$ns3" $sender 10.0.3.2
942	run_tests "$ns3" $sender dead:beef:3::2
943
944	run_tests "$ns4" $sender 10.0.3.1
945	run_tests "$ns4" $sender dead:beef:3::1
946
947	log_if_error "Tests with $sender as a sender have failed"
948done
949
950run_tests_peekmode "saveWithPeek"
951run_tests_peekmode "saveAfterPeek"
952log_if_error "Tests with peek mode have failed"
953
954# MPTFO (MultiPath TCP Fatopen tests)
955run_tests_mptfo
956log_if_error "Tests with MPTFO have failed"
957
958# connect to ns4 ip address, ns2 should intercept/proxy
959run_test_transparent 10.0.3.1 "tproxy ipv4"
960run_test_transparent dead:beef:3::1 "tproxy ipv6"
961log_if_error "Tests with tproxy have failed"
962
963run_tests_disconnect
964log_if_error "Tests of the full disconnection have failed"
965
966display_time
967mptcp_lib_result_print_all_tap
968exit ${final_ret}
969