xref: /linux/tools/testing/selftests/net/xfrm_state.sh (revision 5ea5880764cbb164afb17a62e76ca75dc371409d)
1#!/bin/bash -e
2# SPDX-License-Identifier: GPL-2.0
3#
4# xfrm/IPsec tests.
5# Currently implemented:
6# - ICMP error source address verification (IETF RFC 4301 section 6)
7# - ICMP MTU exceeded handling over IPsec tunnels.
8#
9# Addresses and topology:
10# IPv4 prefix 10.1.c.d IPv6 prefix fc00:c::d/64 where c is the segment number
11# and d is the interface identifier.
12# IPv6 uses the same c:d as IPv4, and start with IPv6 prefix instead ipv4 prefix
13#
14# Network topology default: ns_set_v4 or ns_set_v6
15#   1.1   1.2   2.1   2.2   3.1   3.2   4.1   4.2   5.1   5.2  6.1  6.2
16#  eth0  eth1  eth0  eth1  eth0  eth1  eth0  eth1  eth0  eth1 eth0  eth1
17# a -------- r1 -------- s1 -------- r2 -------- s2 -------- r3 -------- b
18# a, b = Alice and Bob hosts without IPsec.
19# r1, r2, r3 routers, without IPsec
20# s1, s2, IPsec gateways/routers that setup tunnel(s).
21
22# Network topology x: IPsec gateway that generates ICMP response - ns_set_v4x or ns_set_v6x
23#   1.1   1.2   2.1   2.2   3.1   3.2   4.1   4.2   5.1   5.2
24#  eth0  eth1  eth0  eth1  eth0  eth1  eth0  eth1  eth0  eth1
25# a -------- r1 -------- s1 -------- r2 -------- s2 -------- b
26
27. lib.sh
28
29EXIT_ON_TEST_FAIL=no
30PAUSE=no
31VERBOSE=${VERBOSE:-0}
32DEBUG=0
33
34#	Name				Description
35tests="
36	unreachable_ipv4		IPv4 unreachable from router r3
37	unreachable_ipv6		IPv6 unreachable from router r3
38	unreachable_gw_ipv4		IPv4 unreachable from IPsec gateway s2
39	unreachable_gw_ipv6		IPv6 unreachable from IPsec gateway s2
40	mtu_ipv4_s2			IPv4 MTU exceeded from IPsec gateway s2
41	mtu_ipv6_s2			IPv6 MTU exceeded from IPsec gateway s2
42	mtu_ipv4_r2			IPv4 MTU exceeded from ESP router r2
43	mtu_ipv6_r2			IPv6 MTU exceeded from ESP router r2
44	mtu_ipv4_r3			IPv4 MTU exceeded from router r3
45	mtu_ipv6_r3			IPv6 MTU exceeded from router r3"
46
47prefix4="10.1"
48prefix6="fc00"
49
50run_cmd_err() {
51	cmd="$*"
52
53	if [ "$VERBOSE" -gt 0 ]; then
54		printf "  COMMAND: %s\n" "$cmd"
55	fi
56
57	out="$($cmd 2>&1)" && rc=0 || rc=$?
58	if [ "$VERBOSE" -gt 1 ] && [ -n "$out" ]; then
59		echo "  $out"
60		echo
61	fi
62	return 0
63}
64
65run_cmd() {
66	run_cmd_err "$@" || exit 1
67}
68
69run_test() {
70	# If errexit is set, unset it for sub-shell and restore after test
71	errexit=0
72	if [[ $- =~ "e" ]]; then
73		errexit=1
74		set +e
75	fi
76
77	(
78		unset IFS
79
80		# shellcheck disable=SC2030 # fail is read by trap/cleanup within this subshell
81		fail="yes"
82
83		# Since cleanup() relies on variables modified by this sub shell,
84		# it has to run in this context.
85		trap 'log_test_error $?; cleanup' EXIT INT TERM
86
87		if [ "$VERBOSE" -gt 0 ]; then
88			printf "\n#############################################################\n\n"
89		fi
90
91		ret=0
92		case "${name}" in
93		# can't use eval and test names shell check will complain about unused code
94		unreachable_ipv4)    test_unreachable_ipv4 ;;
95		unreachable_ipv6)    test_unreachable_ipv6 ;;
96		unreachable_gw_ipv4) test_unreachable_gw_ipv4 ;;
97		unreachable_gw_ipv6) test_unreachable_gw_ipv6 ;;
98		mtu_ipv4_s2)         test_mtu_ipv4_s2 ;;
99		mtu_ipv6_s2)         test_mtu_ipv6_s2 ;;
100		mtu_ipv4_r2)         test_mtu_ipv4_r2 ;;
101		mtu_ipv6_r2)         test_mtu_ipv6_r2 ;;
102		mtu_ipv4_r3)         test_mtu_ipv4_r3 ;;
103		mtu_ipv6_r3)         test_mtu_ipv6_r3 ;;
104		esac
105		ret=$?
106
107		if [ $ret -eq 0 ]; then
108			fail="no"
109
110			if [ "$VERBOSE" -gt 1 ]; then
111				show_icmp_filter
112			fi
113
114			printf "TEST: %-60s [ PASS ]\n" "${desc}"
115		elif [ $ret -eq "$ksft_skip" ]; then
116			fail="no"
117			printf "TEST: %-60s [SKIP]\n" "${desc}"
118		fi
119
120		return $ret
121	)
122	ret=$?
123
124	[ $errexit -eq 1 ] && set -e
125
126	case $ret in
127	0)
128		all_skipped=false
129		[ "$exitcode" -eq "$ksft_skip" ] && exitcode=0
130		;;
131	"$ksft_skip")
132		[ $all_skipped = true ] && exitcode=$ksft_skip
133		;;
134	*)
135		all_skipped=false
136		exitcode=1
137		;;
138	esac
139
140	return 0 # don't trigger errexit (-e); actual status in exitcode
141}
142
143setup_namespaces() {
144	local namespaces=""
145
146	NS_A=""
147	NS_B=""
148	NS_R1=""
149	NS_R2=""
150	NS_R3=""
151	NS_S1=""
152	NS_S2=""
153
154	for ns in ${ns_set}; do
155		namespaces="$namespaces NS_${ns^^}"
156	done
157
158	# shellcheck disable=SC2086 # setup_ns expects unquoted list
159	setup_ns $namespaces
160
161	ns_active= #ordered list of namespaces for this test.
162
163	[ -n "${NS_A}" ] && ns_a=(ip netns exec "${NS_A}") && ns_active="${ns_active} $NS_A"
164	[ -n "${NS_R1}" ] && ns_active="${ns_active} $NS_R1"
165	[ -n "${NS_S1}" ] && ns_s1=(ip netns exec "${NS_S1}") && ns_active="${ns_active} $NS_S1"
166	[ -n "${NS_R2}" ] && ns_r2=(ip netns exec "${NS_R2}") && ns_active="${ns_active} $NS_R2"
167	[ -n "${NS_S2}" ] && ns_s2=(ip netns exec "${NS_S2}") && ns_active="${ns_active} $NS_S2"
168	[ -n "${NS_R3}" ] && ns_r3=(ip netns exec "${NS_R3}") && ns_active="${ns_active} $NS_R3"
169	[ -n "${NS_B}" ] && ns_active="${ns_active} $NS_B"
170}
171
172addr_add() {
173	local -a ns_cmd=(ip netns exec "$1")
174	local addr="$2"
175	local dev="$3"
176
177	run_cmd "${ns_cmd[@]}" ip addr add "${addr}" dev "${dev}"
178	run_cmd "${ns_cmd[@]}" ip link set up "${dev}"
179}
180
181veth_add() {
182	local ns=$2
183	local pns=$1
184	local -a ns_cmd=(ip netns exec "${pns}")
185	local ln="eth0"
186	local rn="eth1"
187
188	run_cmd "${ns_cmd[@]}" ip link add "${ln}" type veth peer name "${rn}" netns "${ns}"
189}
190
191show_icmp_filter() {
192	run_cmd "${ns_r2[@]}" nft list ruleset
193	echo "$out"
194}
195
196setup_icmp_filter() {
197	run_cmd "${ns_r2[@]}" nft add table inet filter
198	run_cmd "${ns_r2[@]}" nft add chain inet filter FORWARD \
199		'{ type filter hook forward priority filter; policy drop ; }'
200	run_cmd "${ns_r2[@]}" nft add rule inet filter FORWARD counter ip protocol esp \
201		counter log accept
202	run_cmd "${ns_r2[@]}" nft add rule inet filter FORWARD counter ip protocol \
203		icmp counter log drop
204
205	if [ "$VERBOSE" -gt 0 ]; then
206		run_cmd "${ns_r2[@]}" nft list ruleset
207		echo "$out"
208	fi
209}
210
211setup_icmpv6_filter() {
212	run_cmd "${ns_r2[@]}" nft add table inet filter
213	run_cmd "${ns_r2[@]}" nft add chain inet filter FORWARD \
214		'{ type filter hook forward priority filter; policy drop ; }'
215	run_cmd "${ns_r2[@]}" nft add rule inet filter FORWARD ip6 nexthdr \
216		ipv6-icmp icmpv6 type echo-request counter log drop
217	run_cmd "${ns_r2[@]}" nft add rule inet filter FORWARD ip6 nexthdr esp \
218		counter log accept
219	run_cmd "${ns_r2[@]}" nft add rule inet filter FORWARD ip6 nexthdr \
220		ipv6-icmp icmpv6 type \
221		'{nd-neighbor-solicit,nd-neighbor-advert,nd-router-solicit,nd-router-advert}' \
222		counter log drop
223	if [ "$VERBOSE" -gt 0 ]; then
224		run_cmd "${ns_r2[@]}" nft list ruleset
225		echo "$out"
226	fi
227}
228
229set_xfrm_params() {
230	s1_src=${src}
231	s1_dst=${dst}
232	s1_src_net=${src_net}
233	s1_dst_net=${dst_net}
234}
235
236setup_ns_set_v4() {
237	ns_set="a r1 s1 r2 s2 r3 b"    # Network topology default
238	imax=$(echo "$ns_set" | wc -w) # number of namespaces in this topology
239
240	src="10.1.3.1"
241	dst="10.1.4.2"
242	src_net="10.1.1.0/24"
243	dst_net="10.1.6.0/24"
244
245	prefix=${prefix4}
246	prefix_len=24
247	s="."
248	S="."
249
250	set_xfrm_params
251}
252
253setup_ns_set_v4x() {
254	ns_set="a r1 s1 r2 s2 b"       # Network topology: x
255	imax=$(echo "$ns_set" | wc -w) # number of namespaces in this topology
256	prefix=${prefix4}
257	s="."
258	S="."
259	src="10.1.3.1"
260	dst="10.1.4.2"
261	src_net="10.1.1.0/24"
262	dst_net="10.1.5.0/24"
263	prefix_len=24
264
265	set_xfrm_params
266}
267
268setup_ns_set_v6() {
269	ns_set="a r1 s1 r2 s2 r3 b"    # Network topology default
270	imax=$(echo "$ns_set" | wc -w) # number of namespaces in this topology
271	prefix=${prefix6}
272	s=":"
273	S="::"
274	src="fc00:3::1"
275	dst="fc00:4::2"
276	src_net="fc00:1::0/64"
277	dst_net="fc00:6::0/64"
278	prefix_len=64
279
280	set_xfrm_params
281}
282
283setup_ns_set_v6x() {
284	ns_set="a r1 s1 r2 s2 b" # Network topology: x
285	imax=$(echo "$ns_set" | wc -w)
286	prefix=${prefix6}
287	s=":"
288	S="::"
289	src="fc00:3::1"
290	dst="fc00:4::2"
291	src_net="fc00:1::0/64"
292	dst_net="fc00:5::0/64"
293	prefix_len=64
294
295	set_xfrm_params
296}
297
298setup_network() {
299	# Create veths and add addresses
300	local -a ns_cmd
301	i=1
302	p=""
303	for ns in ${ns_active}; do
304		ns_cmd=(ip netns exec "${ns}")
305
306		if [ "${i}" -ne 1 ]; then
307			# Create veth between previous and current namespace
308			veth_add "${p}" "${ns}"
309			# Add addresses: previous gets .1 on eth0, current gets .2 on eth1
310			addr_add "${p}" "${prefix}${s}$((i-1))${S}1/${prefix_len}" eth0
311			addr_add "${ns}" "${prefix}${s}$((i-1))${S}2/${prefix_len}" eth1
312		fi
313
314		# Enable forwarding
315		run_cmd "${ns_cmd[@]}" sysctl -q net/ipv4/ip_forward=1
316		run_cmd "${ns_cmd[@]}" sysctl -q net/ipv6/conf/all/forwarding=1
317		run_cmd "${ns_cmd[@]}" sysctl -q net/ipv6/conf/default/accept_dad=0
318
319		p=${ns}
320		i=$((i + 1))
321	done
322
323	# Add routes (needs all addresses to exist first)
324	i=1
325	for ns in ${ns_active}; do
326		ns_cmd=(ip netns exec "${ns}")
327
328		# Forward routes to networks beyond this node
329		if [ "${i}" -ne "${imax}" ]; then
330			nhf="${prefix}${s}${i}${S}2" # nexthop forward
331			for j in $(seq $((i + 1)) "${imax}"); do
332				run_cmd "${ns_cmd[@]}" ip route replace \
333				       	"${prefix}${s}${j}${S}0/${prefix_len}" via "${nhf}"
334			done
335		fi
336
337		# Reverse routes to networks before this node
338		if [ "${i}" -gt 1 ]; then
339			nhr="${prefix}${s}$((i-1))${S}1" # nexthop reverse
340			for j in $(seq 1 $((i - 2))); do
341				run_cmd "${ns_cmd[@]}" ip route replace \
342					"${prefix}${s}${j}${S}0/${prefix_len}" via "${nhr}"
343			done
344		fi
345
346		i=$((i + 1))
347	done
348}
349
350setup_xfrm_mode() {
351	local MODE=${1:-tunnel}
352	if [ "${MODE}" != "tunnel" ] && [ "${MODE}" != "beet" ]; then
353		echo "xfrm mode ${MODE} not supported"
354		log_test_error
355		return 1
356	fi
357
358	run_cmd "${ns_s1[@]}" ip xfrm policy add src "${s1_src_net}" dst "${s1_dst_net}" dir out \
359		tmpl src "${s1_src}" dst "${s1_dst}" proto esp reqid 1 mode "${MODE}"
360
361	# no "input" policies. we are only doing forwarding so far
362
363	run_cmd "${ns_s1[@]}" ip xfrm policy add src "${s1_dst_net}" dst "${s1_src_net}" dir fwd \
364		flag icmp tmpl src "${s1_dst}" dst "${s1_src}" proto esp reqid 2 mode "${MODE}"
365
366	run_cmd "${ns_s1[@]}" ip xfrm state add src "${s1_src}" dst "${s1_dst}" proto esp spi 1 \
367		reqid 1 mode "${MODE}" aead 'rfc4106(gcm(aes))' \
368		0x1111111111111111111111111111111111111111 96 \
369		sel src "${s1_src_net}" dst "${s1_dst_net}" dir out
370
371	run_cmd "${ns_s1[@]}" ip xfrm state add src "${s1_dst}" dst "${s1_src}" proto esp spi 2 \
372		reqid 2 flag icmp replay-window 8 mode "${MODE}" aead 'rfc4106(gcm(aes))' \
373		0x2222222222222222222222222222222222222222 96 \
374		sel src "${s1_dst_net}" dst "${s1_src_net}" dir in
375
376	run_cmd "${ns_s2[@]}" ip xfrm policy add src "${s1_dst_net}" dst "${s1_src_net}" dir out \
377		flag icmp tmpl src "${s1_dst}" dst "${s1_src}" proto esp reqid 2 mode "${MODE}"
378
379	run_cmd "${ns_s2[@]}" ip xfrm policy add src "${s1_src_net}" dst "${s1_dst_net}" dir fwd \
380		tmpl src "${s1_src}" dst "${s1_dst}" proto esp reqid 1 mode "${MODE}"
381
382	run_cmd "${ns_s2[@]}" ip xfrm state add src "${s1_dst}" dst "${s1_src}" proto esp spi 2 \
383		reqid 2 mode "${MODE}" aead 'rfc4106(gcm(aes))' \
384		0x2222222222222222222222222222222222222222 96 \
385		sel src "${s1_dst_net}" dst "${s1_src_net}" dir out
386
387	run_cmd "${ns_s2[@]}" ip xfrm state add src "${s1_src}" dst "${s1_dst}" proto esp spi 1 \
388		reqid 1 flag icmp replay-window 8 mode "${MODE}" aead 'rfc4106(gcm(aes))' \
389		0x1111111111111111111111111111111111111111 96 \
390		sel src "${s1_src_net}" dst "${s1_dst_net}" dir in
391}
392
393setup_xfrm() {
394	setup_xfrm_mode tunnel
395}
396
397setup() {
398	[ "$(id -u)" -ne 0 ] && echo "  need to run as root" && return "$ksft_skip"
399
400	for arg; do
401		case "${arg}" in
402		ns_set_v4)     setup_ns_set_v4 ;;
403		ns_set_v4x)    setup_ns_set_v4x ;;
404		ns_set_v6)     setup_ns_set_v6 ;;
405		ns_set_v6x)    setup_ns_set_v6x ;;
406		namespaces)    setup_namespaces ;;
407		network)       setup_network ;;
408		xfrm)          setup_xfrm ;;
409		icmp_filter)   setup_icmp_filter ;;
410		icmpv6_filter) setup_icmpv6_filter ;;
411		*) echo "  ${arg} not supported"; return 1 ;;
412		esac || return 1
413	done
414}
415
416# shellcheck disable=SC2317 # called via trap
417pause() {
418	echo
419	echo "Pausing. Hit enter to continue"
420	read -r _
421}
422
423# shellcheck disable=SC2317 # called via trap
424log_test_error() {
425	# shellcheck disable=SC2031 # fail is set in subshell, read via trap
426	if [ "${fail}" = "yes" ] && [ -n "${desc}" ]; then
427		if [ "$VERBOSE" -gt 0 ]; then
428			show_icmp_filter
429		fi
430		printf "TEST: %-60s [ FAIL ]  %s\n" "${desc}" "${name}"
431		[ -n "${cmd}" ] && printf '%s\n\n' "${cmd}"
432		[ -n "${out}" ] && printf '%s\n\n' "${out}"
433	fi
434}
435
436# shellcheck disable=SC2317 # called via trap
437cleanup() {
438	# shellcheck disable=SC2031 # fail is set in subshell, read via trap
439	[[ "$PAUSE" = "always" || ( "$PAUSE" = "fail" && "$fail" = "yes" ) ]] && pause
440	cleanup_all_ns
441	# shellcheck disable=SC2031 # fail is set in subshell, read via trap
442	[ "${EXIT_ON_TEST_FAIL}" = "yes" ] && [ "${fail}" = "yes" ] && exit 1
443}
444
445test_unreachable_ipv6() {
446	setup ns_set_v6 namespaces network xfrm icmpv6_filter || return "$ksft_skip"
447	run_cmd "${ns_a[@]}" ping -W 5 -w 4 -c 1 fc00:6::2
448	run_cmd_err "${ns_a[@]}" ping -W 5 -w 4 -c 1 fc00:6::3
449	rc=0
450	echo -e "$out" | grep -q -E 'From fc00:5::2 icmp_seq.* Destination' || rc=1
451	return "${rc}"
452}
453
454test_unreachable_gw_ipv6() {
455	setup ns_set_v6x namespaces network xfrm icmpv6_filter || return "$ksft_skip"
456	run_cmd "${ns_a[@]}" ping -W 5 -w 4 -c 1 fc00:5::2
457	run_cmd_err "${ns_a[@]}" ping -W 5 -w 4 -c 1 fc00:5::3
458	rc=0
459	echo -e "$out" | grep -q -E 'From fc00:4::2 icmp_seq.* Destination' || rc=1
460	return "${rc}"
461}
462
463test_unreachable_ipv4() {
464	setup ns_set_v4 namespaces network icmp_filter xfrm || return "$ksft_skip"
465	run_cmd "${ns_a[@]}" ping -W 5 -w 4 -c 1 10.1.6.2
466	run_cmd_err "${ns_a[@]}" ping -W 5 -w 4 -c 1 10.1.6.3
467	rc=0
468	echo -e "$out" | grep -q -E 'From 10.1.5.2 icmp_seq.* Destination' || rc=1
469	return "${rc}"
470}
471
472test_unreachable_gw_ipv4() {
473	setup ns_set_v4x namespaces network icmp_filter xfrm || return "$ksft_skip"
474	run_cmd "${ns_a[@]}" ping -W 5 -w 4 -c 1 10.1.5.2
475	run_cmd_err "${ns_a[@]}" ping -W 5 -w 4 -c 1 10.1.5.3
476	rc=0
477	echo -e "$out" | grep -q -E 'From 10.1.4.2 icmp_seq.* Destination' || rc=1
478	return "${rc}"
479}
480
481test_mtu_ipv4_r2() {
482	setup ns_set_v4 namespaces network icmp_filter xfrm || return "$ksft_skip"
483	run_cmd "${ns_a[@]}" ping -W 5 -w 4 -c 1 10.1.6.2
484	run_cmd "${ns_r2[@]}" ip route replace 10.1.3.0/24 dev eth1 src 10.1.3.2 mtu 1300
485	run_cmd "${ns_r2[@]}" ip route replace 10.1.4.0/24 dev eth0 src 10.1.4.1 mtu 1300
486	# shellcheck disable=SC1010 # -M do: do = dont-fragment, not shell keyword
487	run_cmd "${ns_a[@]}" ping -M do -s 1300 -W 5 -w 4 -c 1 10.1.6.2 || true
488	rc=0
489	echo -e "$out" | grep -q -E "From 10.1.2.2 icmp_seq=.* Frag needed and DF set" || rc=1
490	return "${rc}"
491}
492
493test_mtu_ipv6_r2() {
494	setup ns_set_v6 namespaces network xfrm icmpv6_filter || return "$ksft_skip"
495	run_cmd "${ns_a[@]}" ping -W 5 -w 4 -c 1 fc00:6::2
496	run_cmd "${ns_r2[@]}" ip -6 route replace fc00:3::/64 \
497		dev eth1 metric 256 src fc00:3::2 mtu 1300
498	run_cmd "${ns_r2[@]}" ip -6 route replace fc00:4::/64 \
499		dev eth0 metric 256 src fc00:4::1 mtu 1300
500	# shellcheck disable=SC1010 # -M do: do = dont-fragment, not shell keyword
501	run_cmd "${ns_a[@]}" ping -M do -s 1300 -W 5 -w 4 -c 1 fc00:6::2 || true
502	rc=0
503	echo -e "$out" | grep -q -E "From fc00:2::2 icmp_seq=.* Packet too big: mtu=1230" || rc=1
504	return "${rc}"
505}
506
507test_mtu_ipv4_r3() {
508	setup ns_set_v4 namespaces network icmp_filter xfrm || return "$ksft_skip"
509	run_cmd "${ns_a[@]}" ping -W 5 -w 4 -c 1 10.1.6.2
510	run_cmd "${ns_r3[@]}" ip route replace 10.1.6.0/24 dev eth0 mtu 1300
511	# shellcheck disable=SC1010 # -M do: do = dont-fragment, not shell keyword
512	run_cmd "${ns_a[@]}" ping -M do -s 1350 -W 5 -w 4 -c 1 10.1.6.2 || true
513	rc=0
514	echo -e "$out" | grep -q -E "From 10.1.5.2 .* Frag needed and DF set \(mtu = 1300\)" || rc=1
515	return "${rc}"
516}
517
518test_mtu_ipv4_s2() {
519	setup ns_set_v4x namespaces network icmp_filter xfrm || return "$ksft_skip"
520	run_cmd "${ns_a[@]}" ping -W 5 -w 4 -c 1 10.1.5.2
521	run_cmd "${ns_s2[@]}" ip route replace 10.1.5.0/24 dev eth0 src 10.1.5.1 mtu 1300
522	# shellcheck disable=SC1010 # -M do: do = dont-fragment, not shell keyword
523	run_cmd "${ns_a[@]}" ping -M do -s 1350 -W 5 -w 4 -c 1 10.1.5.2 || true
524	rc=0
525	echo -e "$out" | grep -q -E "From 10.1.4.2.*Frag needed and DF set \(mtu = 1300\)" || rc=1
526	return "${rc}"
527}
528
529test_mtu_ipv6_s2() {
530	setup ns_set_v6x namespaces network xfrm icmpv6_filter || return "$ksft_skip"
531	run_cmd "${ns_a[@]}" ping -W 5 -w 4 -c 1 fc00:5::2
532	run_cmd "${ns_s2[@]}" ip -6 route replace fc00:5::/64 dev eth0 metric 256 mtu 1300
533	# shellcheck disable=SC1010 # -M do: do = dont-fragment, not shell keyword
534	run_cmd "${ns_a[@]}" ping -M do -s 1350 -W 5 -w 4 -c 1 fc00:5::2 || true
535	rc=0
536	echo -e "$out" | grep -q -E "From fc00:4::2.*Packet too big: mtu=1300" || rc=1
537	return "${rc}"
538}
539
540test_mtu_ipv6_r3() {
541	setup ns_set_v6 namespaces network xfrm icmpv6_filter || return "$ksft_skip"
542	run_cmd "${ns_a[@]}" ping -W 5 -w 4 -c 1 fc00:6::2
543	run_cmd "${ns_r3[@]}" ip -6 route replace fc00:6::/64 dev eth1 metric 256 mtu 1300
544	# shellcheck disable=SC1010 # -M do: do = dont-fragment, not shell keyword
545	run_cmd "${ns_a[@]}" ping -M do -s 1300 -W 5 -w 4 -c 1 fc00:6::2 || true
546	rc=0
547	echo -e "$out" | grep -q -E "From fc00:5::2 icmp_seq=.* Packet too big: mtu=1300" || rc=1
548	return "${rc}"
549}
550
551################################################################################
552#
553usage() {
554	echo
555	echo "$0 [OPTIONS] [TEST]..."
556	echo "If no TEST argument is given, all tests will be run."
557	echo
558	echo -e "\t-p Pause on fail. Namespaces are kept for diagnostics"
559	echo -e "\t-P Pause after the test. Namespaces are kept for diagnostics"
560	echo -e "\t-v Verbose output. Show commands; -vv Show output and nft rules also"
561	echo "Available tests${tests}"
562	exit 1
563}
564
565################################################################################
566#
567exitcode=0
568all_skipped=true
569out=
570cmd=
571
572while getopts :epPv o; do
573	case $o in
574	e) EXIT_ON_TEST_FAIL=yes ;;
575	P) PAUSE=always ;;
576	p) PAUSE=fail ;;
577	v) VERBOSE=$((VERBOSE + 1)) ;;
578	*) usage ;;
579	esac
580done
581shift $((OPTIND - 1))
582
583IFS=$'\t\n'
584
585for arg; do
586	# Check first that all requested tests are available before running any
587	command -v "test_${arg}" >/dev/null || {
588		echo "=== Test ${arg} not found"
589		usage
590	}
591done
592
593name=""
594desc=""
595fail="no"
596
597for t in ${tests}; do
598	[ "${name}" = "" ] && name="${t}" && continue
599	[ "${desc}" = "" ] && desc="${t}"
600
601	run_this=1
602	for arg; do
603		[ "${arg}" = "${name}" ] && run_this=1 && break
604		run_this=0
605	done
606	if [ $run_this -eq 1 ]; then
607		run_test
608	fi
609	name=""
610	desc=""
611done
612
613exit ${exitcode}
614