162306a36Sopenharmony_ci#!/bin/bash
262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci. "$(dirname "${0}")/mptcp_lib.sh"
562306a36Sopenharmony_ci
662306a36Sopenharmony_cisec=$(date +%s)
762306a36Sopenharmony_cirndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
862306a36Sopenharmony_cins1="ns1-$rndh"
962306a36Sopenharmony_cins2="ns2-$rndh"
1062306a36Sopenharmony_cins3="ns3-$rndh"
1162306a36Sopenharmony_cicapture=false
1262306a36Sopenharmony_ciksft_skip=4
1362306a36Sopenharmony_citimeout_poll=30
1462306a36Sopenharmony_citimeout_test=$((timeout_poll * 2 + 1))
1562306a36Sopenharmony_citest_cnt=1
1662306a36Sopenharmony_ciret=0
1762306a36Sopenharmony_cibail=0
1862306a36Sopenharmony_cislack=50
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciusage() {
2162306a36Sopenharmony_ci	echo "Usage: $0 [ -b ] [ -c ] [ -d ]"
2262306a36Sopenharmony_ci	echo -e "\t-b: bail out after first error, otherwise runs al testcases"
2362306a36Sopenharmony_ci	echo -e "\t-c: capture packets for each test using tcpdump (default: no capture)"
2462306a36Sopenharmony_ci	echo -e "\t-d: debug this script"
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cicleanup()
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	rm -f "$cout" "$sout"
3062306a36Sopenharmony_ci	rm -f "$large" "$small"
3162306a36Sopenharmony_ci	rm -f "$capout"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	local netns
3462306a36Sopenharmony_ci	for netns in "$ns1" "$ns2" "$ns3";do
3562306a36Sopenharmony_ci		ip netns del $netns
3662306a36Sopenharmony_ci	done
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cimptcp_lib_check_mptcp
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ciip -Version > /dev/null 2>&1
4262306a36Sopenharmony_ciif [ $? -ne 0 ];then
4362306a36Sopenharmony_ci	echo "SKIP: Could not run test without ip tool"
4462306a36Sopenharmony_ci	exit $ksft_skip
4562306a36Sopenharmony_cifi
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#  "$ns1"              ns2                    ns3
4862306a36Sopenharmony_ci#     ns1eth1    ns2eth1   ns2eth3      ns3eth1
4962306a36Sopenharmony_ci#            netem
5062306a36Sopenharmony_ci#     ns1eth2    ns2eth2
5162306a36Sopenharmony_ci#            netem
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cisetup()
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	large=$(mktemp)
5662306a36Sopenharmony_ci	small=$(mktemp)
5762306a36Sopenharmony_ci	sout=$(mktemp)
5862306a36Sopenharmony_ci	cout=$(mktemp)
5962306a36Sopenharmony_ci	capout=$(mktemp)
6062306a36Sopenharmony_ci	size=$((2 * 2048 * 4096))
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	dd if=/dev/zero of=$small bs=4096 count=20 >/dev/null 2>&1
6362306a36Sopenharmony_ci	dd if=/dev/zero of=$large bs=4096 count=$((size / 4096)) >/dev/null 2>&1
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	trap cleanup EXIT
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	for i in "$ns1" "$ns2" "$ns3";do
6862306a36Sopenharmony_ci		ip netns add $i || exit $ksft_skip
6962306a36Sopenharmony_ci		ip -net $i link set lo up
7062306a36Sopenharmony_ci		ip netns exec $i sysctl -q net.ipv4.conf.all.rp_filter=0
7162306a36Sopenharmony_ci		ip netns exec $i sysctl -q net.ipv4.conf.default.rp_filter=0
7262306a36Sopenharmony_ci	done
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	ip link add ns1eth1 netns "$ns1" type veth peer name ns2eth1 netns "$ns2"
7562306a36Sopenharmony_ci	ip link add ns1eth2 netns "$ns1" type veth peer name ns2eth2 netns "$ns2"
7662306a36Sopenharmony_ci	ip link add ns2eth3 netns "$ns2" type veth peer name ns3eth1 netns "$ns3"
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	ip -net "$ns1" addr add 10.0.1.1/24 dev ns1eth1
7962306a36Sopenharmony_ci	ip -net "$ns1" addr add dead:beef:1::1/64 dev ns1eth1 nodad
8062306a36Sopenharmony_ci	ip -net "$ns1" link set ns1eth1 up mtu 1500
8162306a36Sopenharmony_ci	ip -net "$ns1" route add default via 10.0.1.2
8262306a36Sopenharmony_ci	ip -net "$ns1" route add default via dead:beef:1::2
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	ip -net "$ns1" addr add 10.0.2.1/24 dev ns1eth2
8562306a36Sopenharmony_ci	ip -net "$ns1" addr add dead:beef:2::1/64 dev ns1eth2 nodad
8662306a36Sopenharmony_ci	ip -net "$ns1" link set ns1eth2 up mtu 1500
8762306a36Sopenharmony_ci	ip -net "$ns1" route add default via 10.0.2.2 metric 101
8862306a36Sopenharmony_ci	ip -net "$ns1" route add default via dead:beef:2::2 metric 101
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	ip netns exec "$ns1" ./pm_nl_ctl limits 1 1
9162306a36Sopenharmony_ci	ip netns exec "$ns1" ./pm_nl_ctl add 10.0.2.1 dev ns1eth2 flags subflow
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	ip -net "$ns2" addr add 10.0.1.2/24 dev ns2eth1
9462306a36Sopenharmony_ci	ip -net "$ns2" addr add dead:beef:1::2/64 dev ns2eth1 nodad
9562306a36Sopenharmony_ci	ip -net "$ns2" link set ns2eth1 up mtu 1500
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	ip -net "$ns2" addr add 10.0.2.2/24 dev ns2eth2
9862306a36Sopenharmony_ci	ip -net "$ns2" addr add dead:beef:2::2/64 dev ns2eth2 nodad
9962306a36Sopenharmony_ci	ip -net "$ns2" link set ns2eth2 up mtu 1500
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	ip -net "$ns2" addr add 10.0.3.2/24 dev ns2eth3
10262306a36Sopenharmony_ci	ip -net "$ns2" addr add dead:beef:3::2/64 dev ns2eth3 nodad
10362306a36Sopenharmony_ci	ip -net "$ns2" link set ns2eth3 up mtu 1500
10462306a36Sopenharmony_ci	ip netns exec "$ns2" sysctl -q net.ipv4.ip_forward=1
10562306a36Sopenharmony_ci	ip netns exec "$ns2" sysctl -q net.ipv6.conf.all.forwarding=1
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	ip -net "$ns3" addr add 10.0.3.3/24 dev ns3eth1
10862306a36Sopenharmony_ci	ip -net "$ns3" addr add dead:beef:3::3/64 dev ns3eth1 nodad
10962306a36Sopenharmony_ci	ip -net "$ns3" link set ns3eth1 up mtu 1500
11062306a36Sopenharmony_ci	ip -net "$ns3" route add default via 10.0.3.2
11162306a36Sopenharmony_ci	ip -net "$ns3" route add default via dead:beef:3::2
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	ip netns exec "$ns3" ./pm_nl_ctl limits 1 1
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	# debug build can slow down measurably the test program
11662306a36Sopenharmony_ci	# we use quite tight time limit on the run-time, to ensure
11762306a36Sopenharmony_ci	# maximum B/W usage.
11862306a36Sopenharmony_ci	# Use kmemleak/lockdep/kasan/prove_locking presence as a rough
11962306a36Sopenharmony_ci	# estimate for this being a debug kernel and increase the
12062306a36Sopenharmony_ci	# maximum run-time accordingly. Observed run times for CI builds
12162306a36Sopenharmony_ci	# running selftests, including kbuild, were used to determine the
12262306a36Sopenharmony_ci	# amount of time to add.
12362306a36Sopenharmony_ci	grep -q ' kmemleak_init$\| lockdep_init$\| kasan_init$\| prove_locking$' /proc/kallsyms && slack=$((slack+550))
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci# $1: ns, $2: port
12762306a36Sopenharmony_ciwait_local_port_listen()
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	local listener_ns="${1}"
13062306a36Sopenharmony_ci	local port="${2}"
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	local port_hex i
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	port_hex="$(printf "%04X" "${port}")"
13562306a36Sopenharmony_ci	for i in $(seq 10); do
13662306a36Sopenharmony_ci		ip netns exec "${listener_ns}" cat /proc/net/tcp* | \
13762306a36Sopenharmony_ci			awk "BEGIN {rc=1} {if (\$2 ~ /:${port_hex}\$/ && \$4 ~ /0A/) {rc=0; exit}} END {exit rc}" &&
13862306a36Sopenharmony_ci			break
13962306a36Sopenharmony_ci		sleep 0.1
14062306a36Sopenharmony_ci	done
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cido_transfer()
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	local cin=$1
14662306a36Sopenharmony_ci	local sin=$2
14762306a36Sopenharmony_ci	local max_time=$3
14862306a36Sopenharmony_ci	local port
14962306a36Sopenharmony_ci	port=$((10000+$test_cnt))
15062306a36Sopenharmony_ci	test_cnt=$((test_cnt+1))
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	:> "$cout"
15362306a36Sopenharmony_ci	:> "$sout"
15462306a36Sopenharmony_ci	:> "$capout"
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if $capture; then
15762306a36Sopenharmony_ci		local capuser
15862306a36Sopenharmony_ci		if [ -z $SUDO_USER ] ; then
15962306a36Sopenharmony_ci			capuser=""
16062306a36Sopenharmony_ci		else
16162306a36Sopenharmony_ci			capuser="-Z $SUDO_USER"
16262306a36Sopenharmony_ci		fi
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		local capfile="${rndh}-${port}"
16562306a36Sopenharmony_ci		local capopt="-i any -s 65535 -B 32768 ${capuser}"
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		ip netns exec ${ns3}  tcpdump ${capopt} -w "${capfile}-listener.pcap"  >> "${capout}" 2>&1 &
16862306a36Sopenharmony_ci		local cappid_listener=$!
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		ip netns exec ${ns1} tcpdump ${capopt} -w "${capfile}-connector.pcap" >> "${capout}" 2>&1 &
17162306a36Sopenharmony_ci		local cappid_connector=$!
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		sleep 1
17462306a36Sopenharmony_ci	fi
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	timeout ${timeout_test} \
17762306a36Sopenharmony_ci		ip netns exec ${ns3} \
17862306a36Sopenharmony_ci			./mptcp_connect -jt ${timeout_poll} -l -p $port -T $max_time \
17962306a36Sopenharmony_ci				0.0.0.0 < "$sin" > "$sout" &
18062306a36Sopenharmony_ci	local spid=$!
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	wait_local_port_listen "${ns3}" "${port}"
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	timeout ${timeout_test} \
18562306a36Sopenharmony_ci		ip netns exec ${ns1} \
18662306a36Sopenharmony_ci			./mptcp_connect -jt ${timeout_poll} -p $port -T $max_time \
18762306a36Sopenharmony_ci				10.0.3.3 < "$cin" > "$cout" &
18862306a36Sopenharmony_ci	local cpid=$!
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	wait $cpid
19162306a36Sopenharmony_ci	local retc=$?
19262306a36Sopenharmony_ci	wait $spid
19362306a36Sopenharmony_ci	local rets=$?
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if $capture; then
19662306a36Sopenharmony_ci		sleep 1
19762306a36Sopenharmony_ci		kill ${cappid_listener}
19862306a36Sopenharmony_ci		kill ${cappid_connector}
19962306a36Sopenharmony_ci	fi
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	cmp $sin $cout > /dev/null 2>&1
20262306a36Sopenharmony_ci	local cmps=$?
20362306a36Sopenharmony_ci	cmp $cin $sout > /dev/null 2>&1
20462306a36Sopenharmony_ci	local cmpc=$?
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	printf "%-16s" " max $max_time "
20762306a36Sopenharmony_ci	if [ $retc -eq 0 ] && [ $rets -eq 0 ] && \
20862306a36Sopenharmony_ci	   [ $cmpc -eq 0 ] && [ $cmps -eq 0 ]; then
20962306a36Sopenharmony_ci		echo "[ OK ]"
21062306a36Sopenharmony_ci		cat "$capout"
21162306a36Sopenharmony_ci		return 0
21262306a36Sopenharmony_ci	fi
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	echo " [ fail ]"
21562306a36Sopenharmony_ci	echo "client exit code $retc, server $rets" 1>&2
21662306a36Sopenharmony_ci	echo -e "\nnetns ${ns3} socket stat for $port:" 1>&2
21762306a36Sopenharmony_ci	ip netns exec ${ns3} ss -nita 1>&2 -o "sport = :$port"
21862306a36Sopenharmony_ci	echo -e "\nnetns ${ns1} socket stat for $port:" 1>&2
21962306a36Sopenharmony_ci	ip netns exec ${ns1} ss -nita 1>&2 -o "dport = :$port"
22062306a36Sopenharmony_ci	ls -l $sin $cout
22162306a36Sopenharmony_ci	ls -l $cin $sout
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	cat "$capout"
22462306a36Sopenharmony_ci	return 1
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cirun_test()
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	local rate1=$1
23062306a36Sopenharmony_ci	local rate2=$2
23162306a36Sopenharmony_ci	local delay1=$3
23262306a36Sopenharmony_ci	local delay2=$4
23362306a36Sopenharmony_ci	local lret
23462306a36Sopenharmony_ci	local dev
23562306a36Sopenharmony_ci	shift 4
23662306a36Sopenharmony_ci	local msg=$*
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	[ $delay1 -gt 0 ] && delay1="delay $delay1" || delay1=""
23962306a36Sopenharmony_ci	[ $delay2 -gt 0 ] && delay2="delay $delay2" || delay2=""
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	for dev in ns1eth1 ns1eth2; do
24262306a36Sopenharmony_ci		tc -n $ns1 qdisc del dev $dev root >/dev/null 2>&1
24362306a36Sopenharmony_ci	done
24462306a36Sopenharmony_ci	for dev in ns2eth1 ns2eth2; do
24562306a36Sopenharmony_ci		tc -n $ns2 qdisc del dev $dev root >/dev/null 2>&1
24662306a36Sopenharmony_ci	done
24762306a36Sopenharmony_ci	tc -n $ns1 qdisc add dev ns1eth1 root netem rate ${rate1}mbit $delay1
24862306a36Sopenharmony_ci	tc -n $ns1 qdisc add dev ns1eth2 root netem rate ${rate2}mbit $delay2
24962306a36Sopenharmony_ci	tc -n $ns2 qdisc add dev ns2eth1 root netem rate ${rate1}mbit $delay1
25062306a36Sopenharmony_ci	tc -n $ns2 qdisc add dev ns2eth2 root netem rate ${rate2}mbit $delay2
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	# time is measured in ms, account for transfer size, aggregated link speed
25362306a36Sopenharmony_ci	# and header overhead (10%)
25462306a36Sopenharmony_ci	#              ms    byte -> bit   10%        mbit      -> kbit -> bit  10%
25562306a36Sopenharmony_ci	local time=$((1000 * size  *  8  * 10 / ((rate1 + rate2) * 1000 * 1000 * 9) ))
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	# mptcp_connect will do some sleeps to allow the mp_join handshake
25862306a36Sopenharmony_ci	# completion (see mptcp_connect): 200ms on each side, add some slack
25962306a36Sopenharmony_ci	time=$((time + 400 + slack))
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	printf "%-60s" "$msg"
26262306a36Sopenharmony_ci	do_transfer $small $large $time
26362306a36Sopenharmony_ci	lret=$?
26462306a36Sopenharmony_ci	mptcp_lib_result_code "${lret}" "${msg}"
26562306a36Sopenharmony_ci	if [ $lret -ne 0 ]; then
26662306a36Sopenharmony_ci		ret=$lret
26762306a36Sopenharmony_ci		[ $bail -eq 0 ] || exit $ret
26862306a36Sopenharmony_ci	fi
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	msg+=" - reverse direction"
27162306a36Sopenharmony_ci	printf "%-60s" "${msg}"
27262306a36Sopenharmony_ci	do_transfer $large $small $time
27362306a36Sopenharmony_ci	lret=$?
27462306a36Sopenharmony_ci	mptcp_lib_result_code "${lret}" "${msg}"
27562306a36Sopenharmony_ci	if [ $lret -ne 0 ]; then
27662306a36Sopenharmony_ci		ret=$lret
27762306a36Sopenharmony_ci		[ $bail -eq 0 ] || exit $ret
27862306a36Sopenharmony_ci	fi
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ciwhile getopts "bcdh" option;do
28262306a36Sopenharmony_ci	case "$option" in
28362306a36Sopenharmony_ci	"h")
28462306a36Sopenharmony_ci		usage $0
28562306a36Sopenharmony_ci		exit 0
28662306a36Sopenharmony_ci		;;
28762306a36Sopenharmony_ci	"b")
28862306a36Sopenharmony_ci		bail=1
28962306a36Sopenharmony_ci		;;
29062306a36Sopenharmony_ci	"c")
29162306a36Sopenharmony_ci		capture=true
29262306a36Sopenharmony_ci		;;
29362306a36Sopenharmony_ci	"d")
29462306a36Sopenharmony_ci		set -x
29562306a36Sopenharmony_ci		;;
29662306a36Sopenharmony_ci	"?")
29762306a36Sopenharmony_ci		usage $0
29862306a36Sopenharmony_ci		exit 1
29962306a36Sopenharmony_ci		;;
30062306a36Sopenharmony_ci	esac
30162306a36Sopenharmony_cidone
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cisetup
30462306a36Sopenharmony_cirun_test 10 10 0 0 "balanced bwidth"
30562306a36Sopenharmony_cirun_test 10 10 1 25 "balanced bwidth with unbalanced delay"
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci# we still need some additional infrastructure to pass the following test-cases
30862306a36Sopenharmony_cirun_test 10 3 0 0 "unbalanced bwidth"
30962306a36Sopenharmony_cirun_test 10 3 1 25 "unbalanced bwidth with unbalanced delay"
31062306a36Sopenharmony_cirun_test 10 3 25 1 "unbalanced bwidth with opposed, unbalanced delay"
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cimptcp_lib_result_print_all_tap
31362306a36Sopenharmony_ciexit $ret
314