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