1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4. "$(dirname "${0}")/mptcp_lib.sh"
5
6sec=$(date +%s)
7rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
8ns="ns1-$rndh"
9ksft_skip=4
10test_cnt=1
11timeout_poll=100
12timeout_test=$((timeout_poll * 2 + 1))
13ret=0
14
15flush_pids()
16{
17	# mptcp_connect in join mode will sleep a bit before completing,
18	# give it some time
19	sleep 1.1
20
21	ip netns pids "${ns}" | xargs --no-run-if-empty kill -SIGUSR1 &>/dev/null
22
23	for _ in $(seq 10); do
24		[ -z "$(ip netns pids "${ns}")" ] && break
25		sleep 0.1
26	done
27}
28
29cleanup()
30{
31	ip netns pids "${ns}" | xargs --no-run-if-empty kill -SIGKILL &>/dev/null
32
33	ip netns del $ns
34}
35
36mptcp_lib_check_mptcp
37
38ip -Version > /dev/null 2>&1
39if [ $? -ne 0 ];then
40	echo "SKIP: Could not run test without ip tool"
41	exit $ksft_skip
42fi
43ss -h | grep -q MPTCP
44if [ $? -ne 0 ];then
45	echo "SKIP: ss tool does not support MPTCP"
46	exit $ksft_skip
47fi
48
49get_msk_inuse()
50{
51	ip netns exec $ns cat /proc/net/protocols | awk '$1~/^MPTCP$/{print $3}'
52}
53
54__chk_nr()
55{
56	local command="$1"
57	local expected=$2
58	local msg nr
59
60	shift 2
61	msg=$*
62	nr=$(eval $command)
63
64	printf "%-50s" "$msg"
65	if [ $nr != $expected ]; then
66		echo "[ fail ] expected $expected found $nr"
67		ret=$test_cnt
68	else
69		echo "[  ok  ]"
70	fi
71	test_cnt=$((test_cnt+1))
72}
73
74__chk_msk_nr()
75{
76	local condition=$1
77	shift 1
78
79	__chk_nr "ss -inmHMN $ns | $condition" $*
80}
81
82chk_msk_nr()
83{
84	__chk_msk_nr "grep -c token:" $*
85}
86
87wait_msk_nr()
88{
89	local condition="grep -c token:"
90	local expected=$1
91	local timeout=20
92	local msg nr
93	local max=0
94	local i=0
95
96	shift 1
97	msg=$*
98
99	while [ $i -lt $timeout ]; do
100		nr=$(ss -inmHMN $ns | $condition)
101		[ $nr == $expected ] && break;
102		[ $nr -gt $max ] && max=$nr
103		i=$((i + 1))
104		sleep 1
105	done
106
107	printf "%-50s" "$msg"
108	if [ $i -ge $timeout ]; then
109		echo "[ fail ] timeout while expecting $expected max $max last $nr"
110		ret=$test_cnt
111	elif [ $nr != $expected ]; then
112		echo "[ fail ] expected $expected found $nr"
113		ret=$test_cnt
114	else
115		echo "[  ok  ]"
116	fi
117	test_cnt=$((test_cnt+1))
118}
119
120chk_msk_fallback_nr()
121{
122		__chk_msk_nr "grep -c fallback" $*
123}
124
125chk_msk_remote_key_nr()
126{
127		__chk_msk_nr "grep -c remote_key" $*
128}
129
130__chk_listen()
131{
132	local filter="$1"
133	local expected=$2
134
135	shift 2
136	msg=$*
137
138	nr=$(ss -N $ns -Ml "$filter" | grep -c LISTEN)
139	printf "%-50s" "$msg"
140
141	if [ $nr != $expected ]; then
142		echo "[ fail ] expected $expected found $nr"
143		ret=$test_cnt
144	else
145		echo "[  ok  ]"
146	fi
147}
148
149chk_msk_listen()
150{
151	lport=$1
152	local msg="check for listen socket"
153
154	# destination port search should always return empty list
155	__chk_listen "dport $lport" 0 "listen match for dport $lport"
156
157	# should return 'our' mptcp listen socket
158	__chk_listen "sport $lport" 1 "listen match for sport $lport"
159
160	__chk_listen "src inet:0.0.0.0:$lport" 1 "listen match for saddr and sport"
161
162	__chk_listen "" 1 "all listen sockets"
163
164	nr=$(ss -Ml $filter | wc -l)
165}
166
167chk_msk_inuse()
168{
169	local expected=$1
170	local listen_nr
171
172	shift 1
173
174	listen_nr=$(ss -N "${ns}" -Ml | grep -c LISTEN)
175	expected=$((expected + listen_nr))
176
177	for _ in $(seq 10); do
178		if [ $(get_msk_inuse) -eq $expected ];then
179			break
180		fi
181		sleep 0.1
182	done
183
184	__chk_nr get_msk_inuse $expected $*
185}
186
187# $1: ns, $2: port
188wait_local_port_listen()
189{
190	local listener_ns="${1}"
191	local port="${2}"
192
193	local port_hex i
194
195	port_hex="$(printf "%04X" "${port}")"
196	for i in $(seq 10); do
197		ip netns exec "${listener_ns}" cat /proc/net/tcp | \
198			awk "BEGIN {rc=1} {if (\$2 ~ /:${port_hex}\$/ && \$4 ~ /0A/) {rc=0; exit}} END {exit rc}" &&
199			break
200		sleep 0.1
201	done
202}
203
204wait_connected()
205{
206	local listener_ns="${1}"
207	local port="${2}"
208
209	local port_hex i
210
211	port_hex="$(printf "%04X" "${port}")"
212	for i in $(seq 10); do
213		ip netns exec ${listener_ns} grep -q " 0100007F:${port_hex} " /proc/net/tcp && break
214		sleep 0.1
215	done
216}
217
218trap cleanup EXIT
219ip netns add $ns
220ip -n $ns link set dev lo up
221
222echo "a" | \
223	timeout ${timeout_test} \
224		ip netns exec $ns \
225			./mptcp_connect -p 10000 -l -t ${timeout_poll} -w 20 \
226				0.0.0.0 >/dev/null &
227wait_local_port_listen $ns 10000
228chk_msk_nr 0 "no msk on netns creation"
229chk_msk_listen 10000
230
231echo "b" | \
232	timeout ${timeout_test} \
233		ip netns exec $ns \
234			./mptcp_connect -p 10000 -r 0 -t ${timeout_poll} -w 20 \
235				127.0.0.1 >/dev/null &
236wait_connected $ns 10000
237chk_msk_nr 2 "after MPC handshake "
238chk_msk_remote_key_nr 2 "....chk remote_key"
239chk_msk_fallback_nr 0 "....chk no fallback"
240chk_msk_inuse 2 "....chk 2 msk in use"
241flush_pids
242
243chk_msk_inuse 0 "....chk 0 msk in use after flush"
244
245echo "a" | \
246	timeout ${timeout_test} \
247		ip netns exec $ns \
248			./mptcp_connect -p 10001 -l -s TCP -t ${timeout_poll} -w 20 \
249				0.0.0.0 >/dev/null &
250wait_local_port_listen $ns 10001
251echo "b" | \
252	timeout ${timeout_test} \
253		ip netns exec $ns \
254			./mptcp_connect -p 10001 -r 0 -t ${timeout_poll} -w 20 \
255				127.0.0.1 >/dev/null &
256wait_connected $ns 10001
257chk_msk_fallback_nr 1 "check fallback"
258chk_msk_inuse 1 "....chk 1 msk in use"
259flush_pids
260
261chk_msk_inuse 0 "....chk 0 msk in use after flush"
262
263NR_CLIENTS=100
264for I in `seq 1 $NR_CLIENTS`; do
265	echo "a" | \
266		timeout ${timeout_test} \
267			ip netns exec $ns \
268				./mptcp_connect -p $((I+10001)) -l -w 20 \
269					-t ${timeout_poll} 0.0.0.0 >/dev/null &
270done
271wait_local_port_listen $ns $((NR_CLIENTS + 10001))
272
273for I in `seq 1 $NR_CLIENTS`; do
274	echo "b" | \
275		timeout ${timeout_test} \
276			ip netns exec $ns \
277				./mptcp_connect -p $((I+10001)) -w 20 \
278					-t ${timeout_poll} 127.0.0.1 >/dev/null &
279done
280
281wait_msk_nr $((NR_CLIENTS*2)) "many msk socket present"
282chk_msk_inuse $((NR_CLIENTS*2)) "....chk many msk in use"
283flush_pids
284
285chk_msk_inuse 0 "....chk 0 msk in use after flush"
286
287exit $ret
288