xref: /linux/tools/testing/selftests/net/mptcp/diag.sh (revision 9112fc0109fc0037ac3b8b633a169e78b4e23ca1)
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=30
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 $((timeout_poll * 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="$3"
59	local skip="${4-SKIP}"
60	local nr
61
62	nr=$(eval $command)
63
64	printf "%-50s" "$msg"
65	if [ "$nr" != "$expected" ]; then
66		if [ "$nr" = "$skip" ] && ! mptcp_lib_expect_all_features; then
67			echo "[ skip ] Feature probably not supported"
68			mptcp_lib_result_skip "${msg}"
69		else
70			echo "[ fail ] expected $expected found $nr"
71			mptcp_lib_result_fail "${msg}"
72			ret=$test_cnt
73		fi
74	else
75		echo "[  ok  ]"
76		mptcp_lib_result_pass "${msg}"
77	fi
78	test_cnt=$((test_cnt+1))
79}
80
81__chk_msk_nr()
82{
83	local condition=$1
84	shift 1
85
86	__chk_nr "ss -inmHMN $ns | $condition" "$@"
87}
88
89chk_msk_nr()
90{
91	__chk_msk_nr "grep -c token:" "$@"
92}
93
94chk_listener_nr()
95{
96	local expected=$1
97	local msg="$2"
98
99	__chk_nr "ss -inmlHMON $ns | wc -l" "$expected" "$msg - mptcp" 0
100	__chk_nr "ss -inmlHtON $ns | wc -l" "$expected" "$msg - subflows"
101}
102
103wait_msk_nr()
104{
105	local condition="grep -c token:"
106	local expected=$1
107	local timeout=20
108	local msg nr
109	local max=0
110	local i=0
111
112	shift 1
113	msg=$*
114
115	while [ $i -lt $timeout ]; do
116		nr=$(ss -inmHMN $ns | $condition)
117		[ $nr == $expected ] && break;
118		[ $nr -gt $max ] && max=$nr
119		i=$((i + 1))
120		sleep 1
121	done
122
123	printf "%-50s" "$msg"
124	if [ $i -ge $timeout ]; then
125		echo "[ fail ] timeout while expecting $expected max $max last $nr"
126		mptcp_lib_result_fail "${msg} # timeout"
127		ret=$test_cnt
128	elif [ $nr != $expected ]; then
129		echo "[ fail ] expected $expected found $nr"
130		mptcp_lib_result_fail "${msg} # unexpected result"
131		ret=$test_cnt
132	else
133		echo "[  ok  ]"
134		mptcp_lib_result_pass "${msg}"
135	fi
136	test_cnt=$((test_cnt+1))
137}
138
139chk_msk_fallback_nr()
140{
141	__chk_msk_nr "grep -c fallback" "$@"
142}
143
144chk_msk_remote_key_nr()
145{
146	__chk_msk_nr "grep -c remote_key" "$@"
147}
148
149__chk_listen()
150{
151	local filter="$1"
152	local expected=$2
153	local msg="$3"
154
155	__chk_nr "ss -N $ns -Ml '$filter' | grep -c LISTEN" "$expected" "$msg" 0
156}
157
158chk_msk_listen()
159{
160	lport=$1
161
162	# destination port search should always return empty list
163	__chk_listen "dport $lport" 0 "listen match for dport $lport"
164
165	# should return 'our' mptcp listen socket
166	__chk_listen "sport $lport" 1 "listen match for sport $lport"
167
168	__chk_listen "src inet:0.0.0.0:$lport" 1 "listen match for saddr and sport"
169
170	__chk_listen "" 1 "all listen sockets"
171
172	nr=$(ss -Ml $filter | wc -l)
173}
174
175chk_msk_inuse()
176{
177	local expected=$1
178	local msg="....chk ${2:-${expected}} msk in use"
179	local listen_nr
180
181	if [ "${expected}" -eq 0 ]; then
182		msg+=" after flush"
183	fi
184
185	listen_nr=$(ss -N "${ns}" -Ml | grep -c LISTEN)
186	expected=$((expected + listen_nr))
187
188	for _ in $(seq 10); do
189		if [ $(get_msk_inuse) -eq $expected ];then
190			break
191		fi
192		sleep 0.1
193	done
194
195	__chk_nr get_msk_inuse $expected "${msg}" 0
196}
197
198# $1: cestab nr
199chk_msk_cestab()
200{
201	local expected=$1
202	local msg="....chk ${2:-${expected}} cestab"
203
204	if [ "${expected}" -eq 0 ]; then
205		msg+=" after flush"
206	fi
207
208	__chk_nr "mptcp_lib_get_counter ${ns} MPTcpExtMPCurrEstab" \
209		 "${expected}" "${msg}" ""
210}
211
212wait_connected()
213{
214	local listener_ns="${1}"
215	local port="${2}"
216
217	local port_hex i
218
219	port_hex="$(printf "%04X" "${port}")"
220	for i in $(seq 10); do
221		ip netns exec ${listener_ns} grep -q " 0100007F:${port_hex} " /proc/net/tcp && break
222		sleep 0.1
223	done
224}
225
226trap cleanup EXIT
227ip netns add $ns
228ip -n $ns link set dev lo up
229
230echo "a" | \
231	timeout ${timeout_test} \
232		ip netns exec $ns \
233			./mptcp_connect -p 10000 -l -t ${timeout_poll} -w 20 \
234				0.0.0.0 >/dev/null &
235mptcp_lib_wait_local_port_listen $ns 10000
236chk_msk_nr 0 "no msk on netns creation"
237chk_msk_listen 10000
238
239echo "b" | \
240	timeout ${timeout_test} \
241		ip netns exec $ns \
242			./mptcp_connect -p 10000 -r 0 -t ${timeout_poll} -w 20 \
243				127.0.0.1 >/dev/null &
244wait_connected $ns 10000
245chk_msk_nr 2 "after MPC handshake "
246chk_msk_remote_key_nr 2 "....chk remote_key"
247chk_msk_fallback_nr 0 "....chk no fallback"
248chk_msk_inuse 2
249chk_msk_cestab 2
250flush_pids
251
252chk_msk_inuse 0 "2->0"
253chk_msk_cestab 0 "2->0"
254
255echo "a" | \
256	timeout ${timeout_test} \
257		ip netns exec $ns \
258			./mptcp_connect -p 10001 -l -s TCP -t ${timeout_poll} -w 20 \
259				0.0.0.0 >/dev/null &
260mptcp_lib_wait_local_port_listen $ns 10001
261echo "b" | \
262	timeout ${timeout_test} \
263		ip netns exec $ns \
264			./mptcp_connect -p 10001 -r 0 -t ${timeout_poll} -w 20 \
265				127.0.0.1 >/dev/null &
266wait_connected $ns 10001
267chk_msk_fallback_nr 1 "check fallback"
268chk_msk_inuse 1
269chk_msk_cestab 1
270flush_pids
271
272chk_msk_inuse 0 "1->0"
273chk_msk_cestab 0 "1->0"
274
275NR_CLIENTS=100
276for I in `seq 1 $NR_CLIENTS`; do
277	echo "a" | \
278		timeout ${timeout_test} \
279			ip netns exec $ns \
280				./mptcp_connect -p $((I+10001)) -l -w 20 \
281					-t ${timeout_poll} 0.0.0.0 >/dev/null &
282done
283mptcp_lib_wait_local_port_listen $ns $((NR_CLIENTS + 10001))
284
285for I in `seq 1 $NR_CLIENTS`; do
286	echo "b" | \
287		timeout ${timeout_test} \
288			ip netns exec $ns \
289				./mptcp_connect -p $((I+10001)) -w 20 \
290					-t ${timeout_poll} 127.0.0.1 >/dev/null &
291done
292
293wait_msk_nr $((NR_CLIENTS*2)) "many msk socket present"
294chk_msk_inuse $((NR_CLIENTS*2)) "many"
295chk_msk_cestab $((NR_CLIENTS*2)) "many"
296flush_pids
297
298chk_msk_inuse 0 "many->0"
299chk_msk_cestab 0 "many->0"
300
301chk_listener_nr 0 "no listener sockets"
302NR_SERVERS=100
303for I in $(seq 1 $NR_SERVERS); do
304	ip netns exec $ns ./mptcp_connect -p $((I + 20001)) \
305		-t ${timeout_poll} -l 0.0.0.0 >/dev/null 2>&1 &
306done
307
308for I in $(seq 1 $NR_SERVERS); do
309	mptcp_lib_wait_local_port_listen $ns $((I + 20001))
310done
311
312chk_listener_nr $NR_SERVERS "many listener sockets"
313
314# graceful termination
315for I in $(seq 1 $NR_SERVERS); do
316	echo a | ip netns exec $ns ./mptcp_connect -p $((I + 20001)) 127.0.0.1 >/dev/null 2>&1 &
317done
318flush_pids
319
320mptcp_lib_result_print_all_tap
321exit $ret
322