xref: /OK3568_Linux_fs/kernel/tools/testing/selftests/net/mptcp/mptcp_connect.sh (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#!/bin/bash
2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0
3*4882a593Smuzhiyun
4*4882a593Smuzhiyuntime_start=$(date +%s)
5*4882a593Smuzhiyun
6*4882a593Smuzhiyunoptstring="S:R:d:e:l:r:h4cm:f:t"
7*4882a593Smuzhiyunret=0
8*4882a593Smuzhiyunsin=""
9*4882a593Smuzhiyunsout=""
10*4882a593Smuzhiyuncin=""
11*4882a593Smuzhiyuncout=""
12*4882a593Smuzhiyunksft_skip=4
13*4882a593Smuzhiyuncapture=false
14*4882a593Smuzhiyuntimeout=30
15*4882a593Smuzhiyunipv6=true
16*4882a593Smuzhiyunethtool_random_on=true
17*4882a593Smuzhiyuntc_delay="$((RANDOM%50))"
18*4882a593Smuzhiyuntc_loss=$((RANDOM%101))
19*4882a593Smuzhiyuntestmode=""
20*4882a593Smuzhiyunsndbuf=0
21*4882a593Smuzhiyunrcvbuf=0
22*4882a593Smuzhiyunoptions_log=true
23*4882a593Smuzhiyundo_tcp=0
24*4882a593Smuzhiyunfilesize=0
25*4882a593Smuzhiyun
26*4882a593Smuzhiyunif [ $tc_loss -eq 100 ];then
27*4882a593Smuzhiyun	tc_loss=1%
28*4882a593Smuzhiyunelif [ $tc_loss -ge 10 ]; then
29*4882a593Smuzhiyun	tc_loss=0.$tc_loss%
30*4882a593Smuzhiyunelif [ $tc_loss -ge 1 ]; then
31*4882a593Smuzhiyun	tc_loss=0.0$tc_loss%
32*4882a593Smuzhiyunelse
33*4882a593Smuzhiyun	tc_loss=""
34*4882a593Smuzhiyunfi
35*4882a593Smuzhiyun
36*4882a593Smuzhiyunusage() {
37*4882a593Smuzhiyun	echo "Usage: $0 [ -a ]"
38*4882a593Smuzhiyun	echo -e "\t-d: tc/netem delay in milliseconds, e.g. \"-d 10\" (default random)"
39*4882a593Smuzhiyun	echo -e "\t-l: tc/netem loss percentage, e.g. \"-l 0.02\" (default random)"
40*4882a593Smuzhiyun	echo -e "\t-r: tc/netem reorder mode, e.g. \"-r 25% 50% gap 5\", use "-r 0" to disable reordering (default random)"
41*4882a593Smuzhiyun	echo -e "\t-e: ethtool features to disable, e.g.: \"-e tso -e gso\" (default: randomly disable any of tso/gso/gro)"
42*4882a593Smuzhiyun	echo -e "\t-4: IPv4 only: disable IPv6 tests (default: test both IPv4 and IPv6)"
43*4882a593Smuzhiyun	echo -e "\t-c: capture packets for each test using tcpdump (default: no capture)"
44*4882a593Smuzhiyun	echo -e "\t-f: size of file to transfer in bytes (default random)"
45*4882a593Smuzhiyun	echo -e "\t-S: set sndbuf value (default: use kernel default)"
46*4882a593Smuzhiyun	echo -e "\t-R: set rcvbuf value (default: use kernel default)"
47*4882a593Smuzhiyun	echo -e "\t-m: test mode (poll, sendfile; default: poll)"
48*4882a593Smuzhiyun	echo -e "\t-t: also run tests with TCP (use twice to non-fallback tcp)"
49*4882a593Smuzhiyun}
50*4882a593Smuzhiyun
51*4882a593Smuzhiyunwhile getopts "$optstring" option;do
52*4882a593Smuzhiyun	case "$option" in
53*4882a593Smuzhiyun	"h")
54*4882a593Smuzhiyun		usage $0
55*4882a593Smuzhiyun		exit 0
56*4882a593Smuzhiyun		;;
57*4882a593Smuzhiyun	"d")
58*4882a593Smuzhiyun		if [ $OPTARG -ge 0 ];then
59*4882a593Smuzhiyun			tc_delay="$OPTARG"
60*4882a593Smuzhiyun		else
61*4882a593Smuzhiyun			echo "-d requires numeric argument, got \"$OPTARG\"" 1>&2
62*4882a593Smuzhiyun			exit 1
63*4882a593Smuzhiyun		fi
64*4882a593Smuzhiyun		;;
65*4882a593Smuzhiyun	"e")
66*4882a593Smuzhiyun		ethtool_args="$ethtool_args $OPTARG off"
67*4882a593Smuzhiyun		ethtool_random_on=false
68*4882a593Smuzhiyun		;;
69*4882a593Smuzhiyun	"l")
70*4882a593Smuzhiyun		tc_loss="$OPTARG"
71*4882a593Smuzhiyun		;;
72*4882a593Smuzhiyun	"r")
73*4882a593Smuzhiyun		tc_reorder="$OPTARG"
74*4882a593Smuzhiyun		;;
75*4882a593Smuzhiyun	"4")
76*4882a593Smuzhiyun		ipv6=false
77*4882a593Smuzhiyun		;;
78*4882a593Smuzhiyun	"c")
79*4882a593Smuzhiyun		capture=true
80*4882a593Smuzhiyun		;;
81*4882a593Smuzhiyun	"S")
82*4882a593Smuzhiyun		if [ $OPTARG -ge 0 ];then
83*4882a593Smuzhiyun			sndbuf="$OPTARG"
84*4882a593Smuzhiyun		else
85*4882a593Smuzhiyun			echo "-S requires numeric argument, got \"$OPTARG\"" 1>&2
86*4882a593Smuzhiyun			exit 1
87*4882a593Smuzhiyun		fi
88*4882a593Smuzhiyun		;;
89*4882a593Smuzhiyun	"R")
90*4882a593Smuzhiyun		if [ $OPTARG -ge 0 ];then
91*4882a593Smuzhiyun			rcvbuf="$OPTARG"
92*4882a593Smuzhiyun		else
93*4882a593Smuzhiyun			echo "-R requires numeric argument, got \"$OPTARG\"" 1>&2
94*4882a593Smuzhiyun			exit 1
95*4882a593Smuzhiyun		fi
96*4882a593Smuzhiyun		;;
97*4882a593Smuzhiyun	"m")
98*4882a593Smuzhiyun		testmode="$OPTARG"
99*4882a593Smuzhiyun		;;
100*4882a593Smuzhiyun	"f")
101*4882a593Smuzhiyun		filesize="$OPTARG"
102*4882a593Smuzhiyun		;;
103*4882a593Smuzhiyun	"t")
104*4882a593Smuzhiyun		do_tcp=$((do_tcp+1))
105*4882a593Smuzhiyun		;;
106*4882a593Smuzhiyun	"?")
107*4882a593Smuzhiyun		usage $0
108*4882a593Smuzhiyun		exit 1
109*4882a593Smuzhiyun		;;
110*4882a593Smuzhiyun	esac
111*4882a593Smuzhiyundone
112*4882a593Smuzhiyun
113*4882a593Smuzhiyunsec=$(date +%s)
114*4882a593Smuzhiyunrndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
115*4882a593Smuzhiyunns1="ns1-$rndh"
116*4882a593Smuzhiyunns2="ns2-$rndh"
117*4882a593Smuzhiyunns3="ns3-$rndh"
118*4882a593Smuzhiyunns4="ns4-$rndh"
119*4882a593Smuzhiyun
120*4882a593SmuzhiyunTEST_COUNT=0
121*4882a593Smuzhiyun
122*4882a593Smuzhiyuncleanup()
123*4882a593Smuzhiyun{
124*4882a593Smuzhiyun	rm -f "$cin" "$cout"
125*4882a593Smuzhiyun	rm -f "$sin" "$sout"
126*4882a593Smuzhiyun	rm -f "$capout"
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun	local netns
129*4882a593Smuzhiyun	for netns in "$ns1" "$ns2" "$ns3" "$ns4";do
130*4882a593Smuzhiyun		ip netns del $netns
131*4882a593Smuzhiyun	done
132*4882a593Smuzhiyun}
133*4882a593Smuzhiyun
134*4882a593Smuzhiyunip -Version > /dev/null 2>&1
135*4882a593Smuzhiyunif [ $? -ne 0 ];then
136*4882a593Smuzhiyun	echo "SKIP: Could not run test without ip tool"
137*4882a593Smuzhiyun	exit $ksft_skip
138*4882a593Smuzhiyunfi
139*4882a593Smuzhiyun
140*4882a593Smuzhiyunsin=$(mktemp)
141*4882a593Smuzhiyunsout=$(mktemp)
142*4882a593Smuzhiyuncin=$(mktemp)
143*4882a593Smuzhiyuncout=$(mktemp)
144*4882a593Smuzhiyuncapout=$(mktemp)
145*4882a593Smuzhiyuntrap cleanup EXIT
146*4882a593Smuzhiyun
147*4882a593Smuzhiyunfor i in "$ns1" "$ns2" "$ns3" "$ns4";do
148*4882a593Smuzhiyun	ip netns add $i || exit $ksft_skip
149*4882a593Smuzhiyun	ip -net $i link set lo up
150*4882a593Smuzhiyundone
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun#  "$ns1"              ns2                    ns3                     ns4
153*4882a593Smuzhiyun# ns1eth2    ns2eth1   ns2eth3      ns3eth2   ns3eth4       ns4eth3
154*4882a593Smuzhiyun#                           - drop 1% ->            reorder 25%
155*4882a593Smuzhiyun#                           <- TSO off -
156*4882a593Smuzhiyun
157*4882a593Smuzhiyunip link add ns1eth2 netns "$ns1" type veth peer name ns2eth1 netns "$ns2"
158*4882a593Smuzhiyunip link add ns2eth3 netns "$ns2" type veth peer name ns3eth2 netns "$ns3"
159*4882a593Smuzhiyunip link add ns3eth4 netns "$ns3" type veth peer name ns4eth3 netns "$ns4"
160*4882a593Smuzhiyun
161*4882a593Smuzhiyunip -net "$ns1" addr add 10.0.1.1/24 dev ns1eth2
162*4882a593Smuzhiyunip -net "$ns1" addr add dead:beef:1::1/64 dev ns1eth2 nodad
163*4882a593Smuzhiyun
164*4882a593Smuzhiyunip -net "$ns1" link set ns1eth2 up
165*4882a593Smuzhiyunip -net "$ns1" route add default via 10.0.1.2
166*4882a593Smuzhiyunip -net "$ns1" route add default via dead:beef:1::2
167*4882a593Smuzhiyun
168*4882a593Smuzhiyunip -net "$ns2" addr add 10.0.1.2/24 dev ns2eth1
169*4882a593Smuzhiyunip -net "$ns2" addr add dead:beef:1::2/64 dev ns2eth1 nodad
170*4882a593Smuzhiyunip -net "$ns2" link set ns2eth1 up
171*4882a593Smuzhiyun
172*4882a593Smuzhiyunip -net "$ns2" addr add 10.0.2.1/24 dev ns2eth3
173*4882a593Smuzhiyunip -net "$ns2" addr add dead:beef:2::1/64 dev ns2eth3 nodad
174*4882a593Smuzhiyunip -net "$ns2" link set ns2eth3 up
175*4882a593Smuzhiyunip -net "$ns2" route add default via 10.0.2.2
176*4882a593Smuzhiyunip -net "$ns2" route add default via dead:beef:2::2
177*4882a593Smuzhiyunip netns exec "$ns2" sysctl -q net.ipv4.ip_forward=1
178*4882a593Smuzhiyunip netns exec "$ns2" sysctl -q net.ipv6.conf.all.forwarding=1
179*4882a593Smuzhiyun
180*4882a593Smuzhiyunip -net "$ns3" addr add 10.0.2.2/24 dev ns3eth2
181*4882a593Smuzhiyunip -net "$ns3" addr add dead:beef:2::2/64 dev ns3eth2 nodad
182*4882a593Smuzhiyunip -net "$ns3" link set ns3eth2 up
183*4882a593Smuzhiyun
184*4882a593Smuzhiyunip -net "$ns3" addr add 10.0.3.2/24 dev ns3eth4
185*4882a593Smuzhiyunip -net "$ns3" addr add dead:beef:3::2/64 dev ns3eth4 nodad
186*4882a593Smuzhiyunip -net "$ns3" link set ns3eth4 up
187*4882a593Smuzhiyunip -net "$ns3" route add default via 10.0.2.1
188*4882a593Smuzhiyunip -net "$ns3" route add default via dead:beef:2::1
189*4882a593Smuzhiyunip netns exec "$ns3" sysctl -q net.ipv4.ip_forward=1
190*4882a593Smuzhiyunip netns exec "$ns3" sysctl -q net.ipv6.conf.all.forwarding=1
191*4882a593Smuzhiyun
192*4882a593Smuzhiyunip -net "$ns4" addr add 10.0.3.1/24 dev ns4eth3
193*4882a593Smuzhiyunip -net "$ns4" addr add dead:beef:3::1/64 dev ns4eth3 nodad
194*4882a593Smuzhiyunip -net "$ns4" link set ns4eth3 up
195*4882a593Smuzhiyunip -net "$ns4" route add default via 10.0.3.2
196*4882a593Smuzhiyunip -net "$ns4" route add default via dead:beef:3::2
197*4882a593Smuzhiyun
198*4882a593Smuzhiyunset_ethtool_flags() {
199*4882a593Smuzhiyun	local ns="$1"
200*4882a593Smuzhiyun	local dev="$2"
201*4882a593Smuzhiyun	local flags="$3"
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun	ip netns exec $ns ethtool -K $dev $flags 2>/dev/null
204*4882a593Smuzhiyun	[ $? -eq 0 ] && echo "INFO: set $ns dev $dev: ethtool -K $flags"
205*4882a593Smuzhiyun}
206*4882a593Smuzhiyun
207*4882a593Smuzhiyunset_random_ethtool_flags() {
208*4882a593Smuzhiyun	local flags=""
209*4882a593Smuzhiyun	local r=$RANDOM
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun	local pick1=$((r & 1))
212*4882a593Smuzhiyun	local pick2=$((r & 2))
213*4882a593Smuzhiyun	local pick3=$((r & 4))
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun	[ $pick1 -ne 0 ] && flags="tso off"
216*4882a593Smuzhiyun	[ $pick2 -ne 0 ] && flags="$flags gso off"
217*4882a593Smuzhiyun	[ $pick3 -ne 0 ] && flags="$flags gro off"
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun	[ -z "$flags" ] && return
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun	set_ethtool_flags "$1" "$2" "$flags"
222*4882a593Smuzhiyun}
223*4882a593Smuzhiyun
224*4882a593Smuzhiyunif $ethtool_random_on;then
225*4882a593Smuzhiyun	set_random_ethtool_flags "$ns3" ns3eth2
226*4882a593Smuzhiyun	set_random_ethtool_flags "$ns4" ns4eth3
227*4882a593Smuzhiyunelse
228*4882a593Smuzhiyun	set_ethtool_flags "$ns3" ns3eth2 "$ethtool_args"
229*4882a593Smuzhiyun	set_ethtool_flags "$ns4" ns4eth3 "$ethtool_args"
230*4882a593Smuzhiyunfi
231*4882a593Smuzhiyun
232*4882a593Smuzhiyunprint_file_err()
233*4882a593Smuzhiyun{
234*4882a593Smuzhiyun	ls -l "$1" 1>&2
235*4882a593Smuzhiyun	echo "Trailing bytes are: "
236*4882a593Smuzhiyun	tail -c 27 "$1"
237*4882a593Smuzhiyun}
238*4882a593Smuzhiyun
239*4882a593Smuzhiyuncheck_transfer()
240*4882a593Smuzhiyun{
241*4882a593Smuzhiyun	local in=$1
242*4882a593Smuzhiyun	local out=$2
243*4882a593Smuzhiyun	local what=$3
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun	cmp "$in" "$out" > /dev/null 2>&1
246*4882a593Smuzhiyun	if [ $? -ne 0 ] ;then
247*4882a593Smuzhiyun		echo "[ FAIL ] $what does not match (in, out):"
248*4882a593Smuzhiyun		print_file_err "$in"
249*4882a593Smuzhiyun		print_file_err "$out"
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun		return 1
252*4882a593Smuzhiyun	fi
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun	return 0
255*4882a593Smuzhiyun}
256*4882a593Smuzhiyun
257*4882a593Smuzhiyuncheck_mptcp_disabled()
258*4882a593Smuzhiyun{
259*4882a593Smuzhiyun	local disabled_ns
260*4882a593Smuzhiyun	disabled_ns="ns_disabled-$sech-$(mktemp -u XXXXXX)"
261*4882a593Smuzhiyun	ip netns add ${disabled_ns} || exit $ksft_skip
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun	# net.mptcp.enabled should be enabled by default
264*4882a593Smuzhiyun	if [ "$(ip netns exec ${disabled_ns} sysctl net.mptcp.enabled | awk '{ print $3 }')" -ne 1 ]; then
265*4882a593Smuzhiyun		echo -e "net.mptcp.enabled sysctl is not 1 by default\t\t[ FAIL ]"
266*4882a593Smuzhiyun		ret=1
267*4882a593Smuzhiyun		return 1
268*4882a593Smuzhiyun	fi
269*4882a593Smuzhiyun	ip netns exec ${disabled_ns} sysctl -q net.mptcp.enabled=0
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun	local err=0
272*4882a593Smuzhiyun	LANG=C ip netns exec ${disabled_ns} ./mptcp_connect -t $timeout -p 10000 -s MPTCP 127.0.0.1 < "$cin" 2>&1 | \
273*4882a593Smuzhiyun		grep -q "^socket: Protocol not available$" && err=1
274*4882a593Smuzhiyun	ip netns delete ${disabled_ns}
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun	if [ ${err} -eq 0 ]; then
277*4882a593Smuzhiyun		echo -e "New MPTCP socket cannot be blocked via sysctl\t\t[ FAIL ]"
278*4882a593Smuzhiyun		ret=1
279*4882a593Smuzhiyun		return 1
280*4882a593Smuzhiyun	fi
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun	echo -e "New MPTCP socket can be blocked via sysctl\t\t[ OK ]"
283*4882a593Smuzhiyun	return 0
284*4882a593Smuzhiyun}
285*4882a593Smuzhiyun
286*4882a593Smuzhiyuncheck_mptcp_ulp_setsockopt()
287*4882a593Smuzhiyun{
288*4882a593Smuzhiyun	local t retval
289*4882a593Smuzhiyun	t="ns_ulp-$sech-$(mktemp -u XXXXXX)"
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun	ip netns add ${t} || exit $ksft_skip
292*4882a593Smuzhiyun	if ! ip netns exec ${t} ./mptcp_connect -u -p 10000 -s TCP 127.0.0.1 2>&1; then
293*4882a593Smuzhiyun		printf "setsockopt(..., TCP_ULP, \"mptcp\", ...) allowed\t[ FAIL ]\n"
294*4882a593Smuzhiyun		retval=1
295*4882a593Smuzhiyun		ret=$retval
296*4882a593Smuzhiyun	else
297*4882a593Smuzhiyun		printf "setsockopt(..., TCP_ULP, \"mptcp\", ...) blocked\t[ OK ]\n"
298*4882a593Smuzhiyun		retval=0
299*4882a593Smuzhiyun	fi
300*4882a593Smuzhiyun	ip netns del ${t}
301*4882a593Smuzhiyun	return $retval
302*4882a593Smuzhiyun}
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun# $1: IP address
305*4882a593Smuzhiyunis_v6()
306*4882a593Smuzhiyun{
307*4882a593Smuzhiyun	[ -z "${1##*:*}" ]
308*4882a593Smuzhiyun}
309*4882a593Smuzhiyun
310*4882a593Smuzhiyundo_ping()
311*4882a593Smuzhiyun{
312*4882a593Smuzhiyun	local listener_ns="$1"
313*4882a593Smuzhiyun	local connector_ns="$2"
314*4882a593Smuzhiyun	local connect_addr="$3"
315*4882a593Smuzhiyun	local ping_args="-q -c 1"
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun	if is_v6 "${connect_addr}"; then
318*4882a593Smuzhiyun		$ipv6 || return 0
319*4882a593Smuzhiyun		ping_args="${ping_args} -6"
320*4882a593Smuzhiyun	fi
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun	ip netns exec ${connector_ns} ping ${ping_args} $connect_addr >/dev/null
323*4882a593Smuzhiyun	if [ $? -ne 0 ] ; then
324*4882a593Smuzhiyun		echo "$listener_ns -> $connect_addr connectivity [ FAIL ]" 1>&2
325*4882a593Smuzhiyun		ret=1
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun		return 1
328*4882a593Smuzhiyun	fi
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun	return 0
331*4882a593Smuzhiyun}
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun# $1: ns, $2: port
334*4882a593Smuzhiyunwait_local_port_listen()
335*4882a593Smuzhiyun{
336*4882a593Smuzhiyun	local listener_ns="${1}"
337*4882a593Smuzhiyun	local port="${2}"
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun	local port_hex i
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun	port_hex="$(printf "%04X" "${port}")"
342*4882a593Smuzhiyun	for i in $(seq 10); do
343*4882a593Smuzhiyun		ip netns exec "${listener_ns}" cat /proc/net/tcp* | \
344*4882a593Smuzhiyun			awk "BEGIN {rc=1} {if (\$2 ~ /:${port_hex}\$/ && \$4 ~ /0A/) {rc=0; exit}} END {exit rc}" &&
345*4882a593Smuzhiyun			break
346*4882a593Smuzhiyun		sleep 0.1
347*4882a593Smuzhiyun	done
348*4882a593Smuzhiyun}
349*4882a593Smuzhiyun
350*4882a593Smuzhiyundo_transfer()
351*4882a593Smuzhiyun{
352*4882a593Smuzhiyun	local listener_ns="$1"
353*4882a593Smuzhiyun	local connector_ns="$2"
354*4882a593Smuzhiyun	local cl_proto="$3"
355*4882a593Smuzhiyun	local srv_proto="$4"
356*4882a593Smuzhiyun	local connect_addr="$5"
357*4882a593Smuzhiyun	local local_addr="$6"
358*4882a593Smuzhiyun	local extra_args=""
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun	local port
361*4882a593Smuzhiyun	port=$((10000+$TEST_COUNT))
362*4882a593Smuzhiyun	TEST_COUNT=$((TEST_COUNT+1))
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun	if [ "$rcvbuf" -gt 0 ]; then
365*4882a593Smuzhiyun		extra_args="$extra_args -R $rcvbuf"
366*4882a593Smuzhiyun	fi
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun	if [ "$sndbuf" -gt 0 ]; then
369*4882a593Smuzhiyun		extra_args="$extra_args -S $sndbuf"
370*4882a593Smuzhiyun	fi
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun	if [ -n "$testmode" ]; then
373*4882a593Smuzhiyun		extra_args="$extra_args -m $testmode"
374*4882a593Smuzhiyun	fi
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun	if [ -n "$extra_args" ] && $options_log; then
377*4882a593Smuzhiyun		options_log=false
378*4882a593Smuzhiyun		echo "INFO: extra options: $extra_args"
379*4882a593Smuzhiyun	fi
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun	:> "$cout"
382*4882a593Smuzhiyun	:> "$sout"
383*4882a593Smuzhiyun	:> "$capout"
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun	local addr_port
386*4882a593Smuzhiyun	addr_port=$(printf "%s:%d" ${connect_addr} ${port})
387*4882a593Smuzhiyun	printf "%.3s %-5s -> %.3s (%-20s) %-5s\t" ${connector_ns} ${cl_proto} ${listener_ns} ${addr_port} ${srv_proto}
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun	if $capture; then
390*4882a593Smuzhiyun		local capuser
391*4882a593Smuzhiyun		if [ -z $SUDO_USER ] ; then
392*4882a593Smuzhiyun			capuser=""
393*4882a593Smuzhiyun		else
394*4882a593Smuzhiyun			capuser="-Z $SUDO_USER"
395*4882a593Smuzhiyun		fi
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun		local capfile="${rndh}-${connector_ns:0:3}-${listener_ns:0:3}-${cl_proto}-${srv_proto}-${connect_addr}-${port}"
398*4882a593Smuzhiyun		local capopt="-i any -s 65535 -B 32768 ${capuser}"
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun		ip netns exec ${listener_ns}  tcpdump ${capopt} -w "${capfile}-listener.pcap"  >> "${capout}" 2>&1 &
401*4882a593Smuzhiyun		local cappid_listener=$!
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun		ip netns exec ${connector_ns} tcpdump ${capopt} -w "${capfile}-connector.pcap" >> "${capout}" 2>&1 &
404*4882a593Smuzhiyun		local cappid_connector=$!
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun		sleep 1
407*4882a593Smuzhiyun	fi
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun	local stat_synrx_last_l=$(ip netns exec ${listener_ns} nstat -z -a MPTcpExtMPCapableSYNRX | while read a count c rest ;do  echo $count;done)
410*4882a593Smuzhiyun	local stat_ackrx_last_l=$(ip netns exec ${listener_ns} nstat -z -a MPTcpExtMPCapableACKRX | while read a count c rest ;do  echo $count;done)
411*4882a593Smuzhiyun	local stat_cookietx_last=$(ip netns exec ${listener_ns} nstat -z -a TcpExtSyncookiesSent | while read a count c rest ;do  echo $count;done)
412*4882a593Smuzhiyun	local stat_cookierx_last=$(ip netns exec ${listener_ns} nstat -z -a TcpExtSyncookiesRecv | while read a count c rest ;do  echo $count;done)
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun	ip netns exec ${listener_ns} ./mptcp_connect -t $timeout -l -p $port -s ${srv_proto} $extra_args $local_addr < "$sin" > "$sout" &
415*4882a593Smuzhiyun	local spid=$!
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun	wait_local_port_listen "${listener_ns}" "${port}"
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun	local start
420*4882a593Smuzhiyun	start=$(date +%s%3N)
421*4882a593Smuzhiyun	ip netns exec ${connector_ns} ./mptcp_connect -t $timeout -p $port -s ${cl_proto} $extra_args $connect_addr < "$cin" > "$cout" &
422*4882a593Smuzhiyun	local cpid=$!
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun	wait $cpid
425*4882a593Smuzhiyun	local retc=$?
426*4882a593Smuzhiyun	wait $spid
427*4882a593Smuzhiyun	local rets=$?
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun	local stop
430*4882a593Smuzhiyun	stop=$(date +%s%3N)
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun	if $capture; then
433*4882a593Smuzhiyun		sleep 1
434*4882a593Smuzhiyun		kill ${cappid_listener}
435*4882a593Smuzhiyun		kill ${cappid_connector}
436*4882a593Smuzhiyun	fi
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun	local duration
439*4882a593Smuzhiyun	duration=$((stop-start))
440*4882a593Smuzhiyun	duration=$(printf "(duration %05sms)" $duration)
441*4882a593Smuzhiyun	if [ ${rets} -ne 0 ] || [ ${retc} -ne 0 ]; then
442*4882a593Smuzhiyun		echo "$duration [ FAIL ] client exit code $retc, server $rets" 1>&2
443*4882a593Smuzhiyun		echo -e "\nnetns ${listener_ns} socket stat for ${port}:" 1>&2
444*4882a593Smuzhiyun		ip netns exec ${listener_ns} ss -nita 1>&2 -o "sport = :$port"
445*4882a593Smuzhiyun		echo -e "\nnetns ${connector_ns} socket stat for ${port}:" 1>&2
446*4882a593Smuzhiyun		ip netns exec ${connector_ns} ss -nita 1>&2 -o "dport = :$port"
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun		cat "$capout"
449*4882a593Smuzhiyun		return 1
450*4882a593Smuzhiyun	fi
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun	check_transfer $sin $cout "file received by client"
453*4882a593Smuzhiyun	retc=$?
454*4882a593Smuzhiyun	check_transfer $cin $sout "file received by server"
455*4882a593Smuzhiyun	rets=$?
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun	local stat_synrx_now_l=$(ip netns exec ${listener_ns} nstat -z -a MPTcpExtMPCapableSYNRX  | while read a count c rest ;do  echo $count;done)
458*4882a593Smuzhiyun	local stat_ackrx_now_l=$(ip netns exec ${listener_ns} nstat -z -a MPTcpExtMPCapableACKRX  | while read a count c rest ;do  echo $count;done)
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun	local stat_cookietx_now=$(ip netns exec ${listener_ns} nstat -z -a TcpExtSyncookiesSent | while read a count c rest ;do  echo $count;done)
461*4882a593Smuzhiyun	local stat_cookierx_now=$(ip netns exec ${listener_ns} nstat -z -a TcpExtSyncookiesRecv | while read a count c rest ;do  echo $count;done)
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun	expect_synrx=$((stat_synrx_last_l))
464*4882a593Smuzhiyun	expect_ackrx=$((stat_ackrx_last_l))
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun	cookies=$(ip netns exec ${listener_ns} sysctl net.ipv4.tcp_syncookies)
467*4882a593Smuzhiyun	cookies=${cookies##*=}
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun	if [ ${cl_proto} = "MPTCP" ] && [ ${srv_proto} = "MPTCP" ]; then
470*4882a593Smuzhiyun		expect_synrx=$((stat_synrx_last_l+1))
471*4882a593Smuzhiyun		expect_ackrx=$((stat_ackrx_last_l+1))
472*4882a593Smuzhiyun	fi
473*4882a593Smuzhiyun	if [ $cookies -eq 2 ];then
474*4882a593Smuzhiyun		if [ $stat_cookietx_last -ge $stat_cookietx_now ] ;then
475*4882a593Smuzhiyun			echo "${listener_ns} CookieSent: ${cl_proto} -> ${srv_proto}: did not advance"
476*4882a593Smuzhiyun		fi
477*4882a593Smuzhiyun		if [ $stat_cookierx_last -ge $stat_cookierx_now ] ;then
478*4882a593Smuzhiyun			echo "${listener_ns} CookieRecv: ${cl_proto} -> ${srv_proto}: did not advance"
479*4882a593Smuzhiyun		fi
480*4882a593Smuzhiyun	else
481*4882a593Smuzhiyun		if [ $stat_cookietx_last -ne $stat_cookietx_now ] ;then
482*4882a593Smuzhiyun			echo "${listener_ns} CookieSent: ${cl_proto} -> ${srv_proto}: changed"
483*4882a593Smuzhiyun		fi
484*4882a593Smuzhiyun		if [ $stat_cookierx_last -ne $stat_cookierx_now ] ;then
485*4882a593Smuzhiyun			echo "${listener_ns} CookieRecv: ${cl_proto} -> ${srv_proto}: changed"
486*4882a593Smuzhiyun		fi
487*4882a593Smuzhiyun	fi
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun	if [ $expect_synrx -ne $stat_synrx_now_l ] ;then
490*4882a593Smuzhiyun		echo "${listener_ns} SYNRX: ${cl_proto} -> ${srv_proto}: expect ${expect_synrx}, got ${stat_synrx_now_l}"
491*4882a593Smuzhiyun	fi
492*4882a593Smuzhiyun	if [ $expect_ackrx -ne $stat_ackrx_now_l ] ;then
493*4882a593Smuzhiyun		echo "${listener_ns} ACKRX: ${cl_proto} -> ${srv_proto}: expect ${expect_ackrx}, got ${stat_ackrx_now_l} "
494*4882a593Smuzhiyun	fi
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun	if [ $retc -eq 0 ] && [ $rets -eq 0 ];then
497*4882a593Smuzhiyun		echo "$duration [ OK ]"
498*4882a593Smuzhiyun		cat "$capout"
499*4882a593Smuzhiyun		return 0
500*4882a593Smuzhiyun	fi
501*4882a593Smuzhiyun
502*4882a593Smuzhiyun	cat "$capout"
503*4882a593Smuzhiyun	return 1
504*4882a593Smuzhiyun}
505*4882a593Smuzhiyun
506*4882a593Smuzhiyunmake_file()
507*4882a593Smuzhiyun{
508*4882a593Smuzhiyun	local name=$1
509*4882a593Smuzhiyun	local who=$2
510*4882a593Smuzhiyun	local SIZE=$filesize
511*4882a593Smuzhiyun	local ksize
512*4882a593Smuzhiyun	local rem
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun	if [ $SIZE -eq 0 ]; then
515*4882a593Smuzhiyun		local MAXSIZE=$((1024 * 1024 * 8))
516*4882a593Smuzhiyun		local MINSIZE=$((1024 * 256))
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun		SIZE=$(((RANDOM * RANDOM + MINSIZE) % MAXSIZE))
519*4882a593Smuzhiyun	fi
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun	ksize=$((SIZE / 1024))
522*4882a593Smuzhiyun	rem=$((SIZE - (ksize * 1024)))
523*4882a593Smuzhiyun
524*4882a593Smuzhiyun	dd if=/dev/urandom of="$name" bs=1024 count=$ksize 2> /dev/null
525*4882a593Smuzhiyun	dd if=/dev/urandom conv=notrunc of="$name" bs=1 count=$rem 2> /dev/null
526*4882a593Smuzhiyun	echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "$name"
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun	echo "Created $name (size $(du -b "$name")) containing data sent by $who"
529*4882a593Smuzhiyun}
530*4882a593Smuzhiyun
531*4882a593Smuzhiyunrun_tests_lo()
532*4882a593Smuzhiyun{
533*4882a593Smuzhiyun	local listener_ns="$1"
534*4882a593Smuzhiyun	local connector_ns="$2"
535*4882a593Smuzhiyun	local connect_addr="$3"
536*4882a593Smuzhiyun	local loopback="$4"
537*4882a593Smuzhiyun	local lret=0
538*4882a593Smuzhiyun
539*4882a593Smuzhiyun	# skip if test programs are running inside same netns for subsequent runs.
540*4882a593Smuzhiyun	if [ $loopback -eq 0 ] && [ ${listener_ns} = ${connector_ns} ]; then
541*4882a593Smuzhiyun		return 0
542*4882a593Smuzhiyun	fi
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun	# skip if we don't want v6
545*4882a593Smuzhiyun	if ! $ipv6 && is_v6 "${connect_addr}"; then
546*4882a593Smuzhiyun		return 0
547*4882a593Smuzhiyun	fi
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun	local local_addr
550*4882a593Smuzhiyun	if is_v6 "${connect_addr}"; then
551*4882a593Smuzhiyun		local_addr="::"
552*4882a593Smuzhiyun	else
553*4882a593Smuzhiyun		local_addr="0.0.0.0"
554*4882a593Smuzhiyun	fi
555*4882a593Smuzhiyun
556*4882a593Smuzhiyun	do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP ${connect_addr} ${local_addr}
557*4882a593Smuzhiyun	lret=$?
558*4882a593Smuzhiyun	if [ $lret -ne 0 ]; then
559*4882a593Smuzhiyun		ret=$lret
560*4882a593Smuzhiyun		return 1
561*4882a593Smuzhiyun	fi
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun	if [ $do_tcp -eq 0 ]; then
564*4882a593Smuzhiyun		# don't bother testing fallback tcp except for loopback case.
565*4882a593Smuzhiyun		if [ ${listener_ns} != ${connector_ns} ]; then
566*4882a593Smuzhiyun			return 0
567*4882a593Smuzhiyun		fi
568*4882a593Smuzhiyun	fi
569*4882a593Smuzhiyun
570*4882a593Smuzhiyun	do_transfer ${listener_ns} ${connector_ns} MPTCP TCP ${connect_addr} ${local_addr}
571*4882a593Smuzhiyun	lret=$?
572*4882a593Smuzhiyun	if [ $lret -ne 0 ]; then
573*4882a593Smuzhiyun		ret=$lret
574*4882a593Smuzhiyun		return 1
575*4882a593Smuzhiyun	fi
576*4882a593Smuzhiyun
577*4882a593Smuzhiyun	do_transfer ${listener_ns} ${connector_ns} TCP MPTCP ${connect_addr} ${local_addr}
578*4882a593Smuzhiyun	lret=$?
579*4882a593Smuzhiyun	if [ $lret -ne 0 ]; then
580*4882a593Smuzhiyun		ret=$lret
581*4882a593Smuzhiyun		return 1
582*4882a593Smuzhiyun	fi
583*4882a593Smuzhiyun
584*4882a593Smuzhiyun	if [ $do_tcp -gt 1 ] ;then
585*4882a593Smuzhiyun		do_transfer ${listener_ns} ${connector_ns} TCP TCP ${connect_addr} ${local_addr}
586*4882a593Smuzhiyun		lret=$?
587*4882a593Smuzhiyun		if [ $lret -ne 0 ]; then
588*4882a593Smuzhiyun			ret=$lret
589*4882a593Smuzhiyun			return 1
590*4882a593Smuzhiyun		fi
591*4882a593Smuzhiyun	fi
592*4882a593Smuzhiyun
593*4882a593Smuzhiyun	return 0
594*4882a593Smuzhiyun}
595*4882a593Smuzhiyun
596*4882a593Smuzhiyunrun_tests()
597*4882a593Smuzhiyun{
598*4882a593Smuzhiyun	run_tests_lo $1 $2 $3 0
599*4882a593Smuzhiyun}
600*4882a593Smuzhiyun
601*4882a593Smuzhiyunmake_file "$cin" "client"
602*4882a593Smuzhiyunmake_file "$sin" "server"
603*4882a593Smuzhiyun
604*4882a593Smuzhiyuncheck_mptcp_disabled
605*4882a593Smuzhiyun
606*4882a593Smuzhiyuncheck_mptcp_ulp_setsockopt
607*4882a593Smuzhiyun
608*4882a593Smuzhiyunecho "INFO: validating network environment with pings"
609*4882a593Smuzhiyunfor sender in "$ns1" "$ns2" "$ns3" "$ns4";do
610*4882a593Smuzhiyun	do_ping "$ns1" $sender 10.0.1.1
611*4882a593Smuzhiyun	do_ping "$ns1" $sender dead:beef:1::1
612*4882a593Smuzhiyun
613*4882a593Smuzhiyun	do_ping "$ns2" $sender 10.0.1.2
614*4882a593Smuzhiyun	do_ping "$ns2" $sender dead:beef:1::2
615*4882a593Smuzhiyun	do_ping "$ns2" $sender 10.0.2.1
616*4882a593Smuzhiyun	do_ping "$ns2" $sender dead:beef:2::1
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun	do_ping "$ns3" $sender 10.0.2.2
619*4882a593Smuzhiyun	do_ping "$ns3" $sender dead:beef:2::2
620*4882a593Smuzhiyun	do_ping "$ns3" $sender 10.0.3.2
621*4882a593Smuzhiyun	do_ping "$ns3" $sender dead:beef:3::2
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun	do_ping "$ns4" $sender 10.0.3.1
624*4882a593Smuzhiyun	do_ping "$ns4" $sender dead:beef:3::1
625*4882a593Smuzhiyundone
626*4882a593Smuzhiyun
627*4882a593Smuzhiyun[ -n "$tc_loss" ] && tc -net "$ns2" qdisc add dev ns2eth3 root netem loss random $tc_loss delay ${tc_delay}ms
628*4882a593Smuzhiyunecho -n "INFO: Using loss of $tc_loss "
629*4882a593Smuzhiyuntest "$tc_delay" -gt 0 && echo -n "delay $tc_delay ms "
630*4882a593Smuzhiyun
631*4882a593Smuzhiyunreorder_delay=$(($tc_delay / 4))
632*4882a593Smuzhiyun
633*4882a593Smuzhiyunif [ -z "${tc_reorder}" ]; then
634*4882a593Smuzhiyun	reorder1=$((RANDOM%10))
635*4882a593Smuzhiyun	reorder1=$((100 - reorder1))
636*4882a593Smuzhiyun	reorder2=$((RANDOM%100))
637*4882a593Smuzhiyun
638*4882a593Smuzhiyun	if [ $reorder_delay -gt 0 ] && [ $reorder1 -lt 100 ] && [ $reorder2 -gt 0 ]; then
639*4882a593Smuzhiyun		tc_reorder="reorder ${reorder1}% ${reorder2}%"
640*4882a593Smuzhiyun		echo -n "$tc_reorder with delay ${reorder_delay}ms "
641*4882a593Smuzhiyun	fi
642*4882a593Smuzhiyunelif [ "$tc_reorder" = "0" ];then
643*4882a593Smuzhiyun	tc_reorder=""
644*4882a593Smuzhiyunelif [ "$reorder_delay" -gt 0 ];then
645*4882a593Smuzhiyun	# reordering requires some delay
646*4882a593Smuzhiyun	tc_reorder="reorder $tc_reorder"
647*4882a593Smuzhiyun	echo -n "$tc_reorder with delay ${reorder_delay}ms "
648*4882a593Smuzhiyunfi
649*4882a593Smuzhiyun
650*4882a593Smuzhiyunecho "on ns3eth4"
651*4882a593Smuzhiyun
652*4882a593Smuzhiyuntc -net "$ns3" qdisc add dev ns3eth4 root netem delay ${reorder_delay}ms $tc_reorder
653*4882a593Smuzhiyun
654*4882a593Smuzhiyunfor sender in $ns1 $ns2 $ns3 $ns4;do
655*4882a593Smuzhiyun	run_tests_lo "$ns1" "$sender" 10.0.1.1 1
656*4882a593Smuzhiyun	if [ $ret -ne 0 ] ;then
657*4882a593Smuzhiyun		echo "FAIL: Could not even run loopback test" 1>&2
658*4882a593Smuzhiyun		exit $ret
659*4882a593Smuzhiyun	fi
660*4882a593Smuzhiyun	run_tests_lo "$ns1" $sender dead:beef:1::1 1
661*4882a593Smuzhiyun	if [ $ret -ne 0 ] ;then
662*4882a593Smuzhiyun		echo "FAIL: Could not even run loopback v6 test" 2>&1
663*4882a593Smuzhiyun		exit $ret
664*4882a593Smuzhiyun	fi
665*4882a593Smuzhiyun
666*4882a593Smuzhiyun	# ns1<->ns2 is not subject to reordering/tc delays. Use it to test
667*4882a593Smuzhiyun	# mptcp syncookie support.
668*4882a593Smuzhiyun	if [ $sender = $ns1 ]; then
669*4882a593Smuzhiyun		ip netns exec "$ns2" sysctl -q net.ipv4.tcp_syncookies=2
670*4882a593Smuzhiyun	else
671*4882a593Smuzhiyun		ip netns exec "$ns2" sysctl -q net.ipv4.tcp_syncookies=1
672*4882a593Smuzhiyun	fi
673*4882a593Smuzhiyun
674*4882a593Smuzhiyun	run_tests "$ns2" $sender 10.0.1.2
675*4882a593Smuzhiyun	run_tests "$ns2" $sender dead:beef:1::2
676*4882a593Smuzhiyun	run_tests "$ns2" $sender 10.0.2.1
677*4882a593Smuzhiyun	run_tests "$ns2" $sender dead:beef:2::1
678*4882a593Smuzhiyun
679*4882a593Smuzhiyun	run_tests "$ns3" $sender 10.0.2.2
680*4882a593Smuzhiyun	run_tests "$ns3" $sender dead:beef:2::2
681*4882a593Smuzhiyun	run_tests "$ns3" $sender 10.0.3.2
682*4882a593Smuzhiyun	run_tests "$ns3" $sender dead:beef:3::2
683*4882a593Smuzhiyun
684*4882a593Smuzhiyun	run_tests "$ns4" $sender 10.0.3.1
685*4882a593Smuzhiyun	run_tests "$ns4" $sender dead:beef:3::1
686*4882a593Smuzhiyundone
687*4882a593Smuzhiyun
688*4882a593Smuzhiyuntime_end=$(date +%s)
689*4882a593Smuzhiyuntime_run=$((time_end-time_start))
690*4882a593Smuzhiyun
691*4882a593Smuzhiyunecho "Time: ${time_run} seconds"
692*4882a593Smuzhiyun
693*4882a593Smuzhiyunexit $ret
694