xref: /linux/tools/testing/selftests/net/icmp_redirect.sh (revision 1b98f357dadd6ea613a435fbaef1a5dd7b35fd21)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# redirect test
5#
6#                     .253 +----+
7#                     +----| r1 |
8#                     |    +----+
9# +----+              |       |.1
10# | h1 |--------------+       |   10.1.1.0/30 2001:db8:1::0/126
11# +----+ .1           |       |.2
12#         172.16.1/24 |    +----+                   +----+
13#    2001:db8:16:1/64 +----| r2 |-------------------| h2 |
14#                     .254 +----+ .254           .2 +----+
15#                                    172.16.2/24
16#                                  2001:db8:16:2/64
17#
18# Route from h1 to h2 goes through r1, eth1 - connection between r1 and r2.
19# Route on r1 changed to go to r2 via eth0. This causes a redirect to be sent
20# from r1 to h1 telling h1 to use r2 when talking to h2.
21
22source lib.sh
23VERBOSE=0
24PAUSE_ON_FAIL=no
25
26H1_N1_IP=172.16.1.1
27R1_N1_IP=172.16.1.253
28R2_N1_IP=172.16.1.254
29
30H1_N1_IP6=2001:db8:16:1::1
31R1_N1_IP6=2001:db8:16:1::253
32R2_N1_IP6=2001:db8:16:1::254
33
34R1_R2_N1_IP=10.1.1.1
35R2_R1_N1_IP=10.1.1.2
36
37R1_R2_N1_IP6=2001:db8:1::1
38R2_R1_N1_IP6=2001:db8:1::2
39
40H2_N2=172.16.2.0/24
41H2_N2_6=2001:db8:16:2::/64
42H2_N2_IP=172.16.2.2
43R2_N2_IP=172.16.2.254
44H2_N2_IP6=2001:db8:16:2::2
45R2_N2_IP6=2001:db8:16:2::254
46
47VRF=red
48VRF_TABLE=1111
49
50################################################################################
51# helpers
52
53log_section()
54{
55	echo
56	echo "###########################################################################"
57	echo "$*"
58	echo "###########################################################################"
59	echo
60}
61
62log_test()
63{
64	local rc=$1
65	local expected=$2
66	local msg="$3"
67	local xfail=$4
68
69	if [ ${rc} -eq ${expected} ]; then
70		printf "TEST: %-60s  [ OK ]\n" "${msg}"
71		nsuccess=$((nsuccess+1))
72	elif [ ${rc} -eq ${xfail} ]; then
73		printf "TEST: %-60s  [XFAIL]\n" "${msg}"
74		nxfail=$((nxfail+1))
75	else
76		ret=1
77		nfail=$((nfail+1))
78		printf "TEST: %-60s  [FAIL]\n" "${msg}"
79		if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
80			echo
81			echo "hit enter to continue, 'q' to quit"
82			read a
83			[ "$a" = "q" ] && exit 1
84		fi
85	fi
86}
87
88log_debug()
89{
90	if [ "$VERBOSE" = "1" ]; then
91		echo "$*"
92	fi
93}
94
95run_cmd()
96{
97	local cmd="$*"
98	local out
99	local rc
100
101	if [ "$VERBOSE" = "1" ]; then
102		echo "COMMAND: $cmd"
103	fi
104
105	out=$(eval $cmd 2>&1)
106	rc=$?
107	if [ "$VERBOSE" = "1" -a -n "$out" ]; then
108		echo "$out"
109	fi
110
111	[ "$VERBOSE" = "1" ] && echo
112
113	return $rc
114}
115
116get_linklocal()
117{
118	local ns=$1
119	local dev=$2
120	local addr
121
122	addr=$(ip -netns $ns -6 -br addr show dev ${dev} | \
123	awk '{
124		for (i = 3; i <= NF; ++i) {
125			if ($i ~ /^fe80/)
126				print $i
127		}
128	}'
129	)
130	addr=${addr/\/*}
131
132	[ -z "$addr" ] && return 1
133
134	echo $addr
135
136	return 0
137}
138
139################################################################################
140# setup and teardown
141
142cleanup()
143{
144	cleanup_ns $h1 $h2 $r1 $r2
145}
146
147create_vrf()
148{
149	local ns=$1
150
151	ip -netns ${ns} link add ${VRF} type vrf table ${VRF_TABLE}
152	ip -netns ${ns} link set ${VRF} up
153	ip -netns ${ns} route add vrf ${VRF} unreachable default metric 8192
154	ip -netns ${ns} -6 route add vrf ${VRF} unreachable default metric 8192
155
156	ip -netns ${ns} addr add 127.0.0.1/8 dev ${VRF}
157	ip -netns ${ns} -6 addr add ::1 dev ${VRF} nodad
158
159	ip -netns ${ns} ru del pref 0
160	ip -netns ${ns} ru add pref 32765 from all lookup local
161	ip -netns ${ns} -6 ru del pref 0
162	ip -netns ${ns} -6 ru add pref 32765 from all lookup local
163}
164
165setup()
166{
167	local ns
168
169	#
170	# create nodes as namespaces
171	setup_ns h1 h2 r1 r2
172	for ns in $h1 $h2 $r1 $r2; do
173		if echo $ns | grep -q h[12]-; then
174			ip netns exec $ns sysctl -q -w net.ipv4.conf.all.accept_redirects=1
175			ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=0
176			ip netns exec $ns sysctl -q -w net.ipv6.conf.all.accept_redirects=1
177			ip netns exec $ns sysctl -q -w net.ipv6.conf.all.keep_addr_on_down=1
178		else
179			ip netns exec $ns sysctl -q -w net.ipv4.ip_forward=1
180			ip netns exec $ns sysctl -q -w net.ipv4.conf.all.send_redirects=1
181
182			ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=1
183			ip netns exec $ns sysctl -q -w net.ipv6.route.mtu_expires=10
184		fi
185	done
186
187	#
188	# create interconnects
189	#
190	ip -netns $h1 li add eth0 type veth peer name r1h1
191	ip -netns $h1 li set r1h1 netns $r1 name eth0 up
192
193	ip -netns $h1 li add eth1 type veth peer name r2h1
194	ip -netns $h1 li set r2h1 netns $r2 name eth0 up
195
196	ip -netns $h2 li add eth0 type veth peer name r2h2
197	ip -netns $h2 li set eth0 up
198	ip -netns $h2 li set r2h2 netns $r2 name eth2 up
199
200	ip -netns $r1 li add eth1 type veth peer name r2r1
201	ip -netns $r1 li set eth1 up
202	ip -netns $r1 li set r2r1 netns $r2 name eth1 up
203
204	#
205	# h1
206	#
207	if [ "${WITH_VRF}" = "yes" ]; then
208		create_vrf "$h1"
209		H1_VRF_ARG="vrf ${VRF}"
210		H1_PING_ARG="-I ${VRF}"
211	else
212		H1_VRF_ARG=
213		H1_PING_ARG=
214	fi
215	ip -netns $h1 li add br0 type bridge
216	if [ "${WITH_VRF}" = "yes" ]; then
217		ip -netns $h1 li set br0 vrf ${VRF} up
218	else
219		ip -netns $h1 li set br0 up
220	fi
221	ip -netns $h1 addr add dev br0 ${H1_N1_IP}/24
222	ip -netns $h1 -6 addr add dev br0 ${H1_N1_IP6}/64 nodad
223	ip -netns $h1 li set eth0 master br0 up
224	ip -netns $h1 li set eth1 master br0 up
225
226	#
227	# h2
228	#
229	ip -netns $h2 addr add dev eth0 ${H2_N2_IP}/24
230	ip -netns $h2 ro add default via ${R2_N2_IP} dev eth0
231	ip -netns $h2 -6 addr add dev eth0 ${H2_N2_IP6}/64 nodad
232	ip -netns $h2 -6 ro add default via ${R2_N2_IP6} dev eth0
233
234	#
235	# r1
236	#
237	ip -netns $r1 addr add dev eth0 ${R1_N1_IP}/24
238	ip -netns $r1 -6 addr add dev eth0 ${R1_N1_IP6}/64 nodad
239	ip -netns $r1 addr add dev eth1 ${R1_R2_N1_IP}/30
240	ip -netns $r1 -6 addr add dev eth1 ${R1_R2_N1_IP6}/126 nodad
241
242	#
243	# r2
244	#
245	ip -netns $r2 addr add dev eth0 ${R2_N1_IP}/24
246	ip -netns $r2 -6 addr add dev eth0 ${R2_N1_IP6}/64 nodad
247	ip -netns $r2 addr add dev eth1 ${R2_R1_N1_IP}/30
248	ip -netns $r2 -6 addr add dev eth1 ${R2_R1_N1_IP6}/126 nodad
249	ip -netns $r2 addr add dev eth2 ${R2_N2_IP}/24
250	ip -netns $r2 -6 addr add dev eth2 ${R2_N2_IP6}/64 nodad
251
252	sleep 2
253
254	R1_LLADDR=$(get_linklocal $r1 eth0)
255	if [ $? -ne 0 ]; then
256		echo "Error: Failed to get link-local address of r1's eth0"
257		exit 1
258	fi
259	log_debug "initial gateway is R1's lladdr = ${R1_LLADDR}"
260
261	R2_LLADDR=$(get_linklocal $r2 eth0)
262	if [ $? -ne 0 ]; then
263		echo "Error: Failed to get link-local address of r2's eth0"
264		exit 1
265	fi
266	log_debug "initial gateway is R2's lladdr = ${R2_LLADDR}"
267}
268
269change_h2_mtu()
270{
271	local mtu=$1
272
273	run_cmd ip -netns $h2 li set eth0 mtu ${mtu}
274	run_cmd ip -netns $r2 li set eth2 mtu ${mtu}
275}
276
277check_exception()
278{
279	local mtu="$1"
280	local with_redirect="$2"
281	local desc="$3"
282
283	# From 172.16.1.101: icmp_seq=1 Redirect Host(New nexthop: 172.16.1.102)
284	if [ "$VERBOSE" = "1" ]; then
285		echo "Commands to check for exception:"
286		run_cmd ip -netns $h1 ro get ${H1_VRF_ARG} ${H2_N2_IP}
287		run_cmd ip -netns $h1 -6 ro get ${H1_VRF_ARG} ${H2_N2_IP6}
288	fi
289
290	if [ -n "${mtu}" ]; then
291		mtu=" mtu ${mtu}"
292	fi
293	if [ "$with_redirect" = "yes" ]; then
294		ip -netns $h1 ro get ${H1_VRF_ARG} ${H2_N2_IP} | \
295		grep -q "cache <redirected> expires [0-9]*sec${mtu}"
296	elif [ -n "${mtu}" ]; then
297		ip -netns $h1 ro get ${H1_VRF_ARG} ${H2_N2_IP} | \
298		grep -q "cache expires [0-9]*sec${mtu}"
299	else
300		# want to verify that neither mtu nor redirected appears in
301		# the route get output. The -v will wipe out the cache line
302		# if either are set so the last grep -q will not find a match
303		ip -netns $h1 ro get ${H1_VRF_ARG} ${H2_N2_IP} | \
304		grep -E -v 'mtu|redirected' | grep -q "cache"
305	fi
306	log_test $? 0 "IPv4: ${desc}" 0
307
308	# No PMTU info for test "redirect" and "mtu exception plus redirect"
309	if [ "$with_redirect" = "yes" ] && [ "$desc" != "redirect exception plus mtu" ]; then
310		ip -netns $h1 -6 ro get ${H1_VRF_ARG} ${H2_N2_IP6} | \
311		grep -v "mtu" | grep -q "${H2_N2_IP6} .*via ${R2_LLADDR} dev br0"
312	elif [ -n "${mtu}" ]; then
313		ip -netns $h1 -6 ro get ${H1_VRF_ARG} ${H2_N2_IP6} | \
314		grep -q "${mtu}"
315	else
316		# IPv6 is a bit harder. First strip out the match if it
317		# contains an mtu exception and then look for the first
318		# gateway - R1's lladdr
319		ip -netns $h1 -6 ro get ${H1_VRF_ARG} ${H2_N2_IP6} | \
320		grep -v "mtu" | grep -q "${R1_LLADDR}"
321	fi
322	log_test $? 0 "IPv6: ${desc}" 1
323}
324
325run_ping()
326{
327	local sz=$1
328
329	run_cmd ip netns exec $h1 ping -q -M want -i 0.5 -c 10 -w 2 -s ${sz} ${H1_PING_ARG} ${H2_N2_IP}
330	run_cmd ip netns exec $h1 ${ping6} -q -M want -i 0.5 -c 10 -w 2 -s ${sz} ${H1_PING_ARG} ${H2_N2_IP6}
331}
332
333replace_route_new()
334{
335	# r1 to h2 via r2 and eth0
336	run_cmd ip -netns $r1 nexthop replace id 1 via ${R2_N1_IP} dev eth0
337	run_cmd ip -netns $r1 nexthop replace id 2 via ${R2_LLADDR} dev eth0
338}
339
340reset_route_new()
341{
342	run_cmd ip -netns $r1 nexthop flush
343	run_cmd ip -netns $h1 nexthop flush
344
345	initial_route_new
346}
347
348initial_route_new()
349{
350	# r1 to h2 via r2 and eth1
351	run_cmd ip -netns $r1 nexthop add id 1 via ${R2_R1_N1_IP} dev eth1
352	run_cmd ip -netns $r1 ro add ${H2_N2} nhid 1
353
354	run_cmd ip -netns $r1 nexthop add id 2 via ${R2_R1_N1_IP6} dev eth1
355	run_cmd ip -netns $r1 -6 ro add ${H2_N2_6} nhid 2
356
357	# h1 to h2 via r1
358	run_cmd ip -netns $h1 nexthop add id 1 via ${R1_N1_IP} dev br0
359	run_cmd ip -netns $h1 ro add ${H1_VRF_ARG} ${H2_N2} nhid 1
360
361	run_cmd ip -netns $h1 nexthop add id 2 via ${R1_LLADDR} dev br0
362	run_cmd ip -netns $h1 -6 ro add ${H1_VRF_ARG} ${H2_N2_6} nhid 2
363}
364
365replace_route_legacy()
366{
367	# r1 to h2 via r2 and eth0
368	run_cmd ip -netns $r1    ro replace ${H2_N2}   via ${R2_N1_IP}  dev eth0
369	run_cmd ip -netns $r1 -6 ro replace ${H2_N2_6} via ${R2_LLADDR} dev eth0
370}
371
372reset_route_legacy()
373{
374	run_cmd ip -netns $r1    ro del ${H2_N2}
375	run_cmd ip -netns $r1 -6 ro del ${H2_N2_6}
376
377	run_cmd ip -netns $h1    ro del ${H1_VRF_ARG} ${H2_N2}
378	run_cmd ip -netns $h1 -6 ro del ${H1_VRF_ARG} ${H2_N2_6}
379
380	initial_route_legacy
381}
382
383initial_route_legacy()
384{
385	# r1 to h2 via r2 and eth1
386	run_cmd ip -netns $r1    ro add ${H2_N2}   via ${R2_R1_N1_IP}  dev eth1
387	run_cmd ip -netns $r1 -6 ro add ${H2_N2_6} via ${R2_R1_N1_IP6} dev eth1
388
389	# h1 to h2 via r1
390	# - IPv6 redirect only works if gateway is the LLA
391	run_cmd ip -netns $h1    ro add ${H1_VRF_ARG} ${H2_N2} via ${R1_N1_IP} dev br0
392	run_cmd ip -netns $h1 -6 ro add ${H1_VRF_ARG} ${H2_N2_6} via ${R1_LLADDR} dev br0
393}
394
395check_connectivity()
396{
397	local rc
398
399	run_cmd ip netns exec $h1 ping -c1 -w1 ${H1_PING_ARG} ${H2_N2_IP}
400	rc=$?
401	run_cmd ip netns exec $h1 ${ping6} -c1 -w1 ${H1_PING_ARG} ${H2_N2_IP6}
402	[ $? -ne 0 ] && rc=$?
403
404	return $rc
405}
406
407do_test()
408{
409	local ttype="$1"
410
411	eval initial_route_${ttype}
412
413	# verify connectivity
414	check_connectivity
415	if [ $? -ne 0 ]; then
416		echo "Error: Basic connectivity is broken"
417		ret=1
418		return
419	fi
420
421	# redirect exception followed by mtu
422	eval replace_route_${ttype}
423	run_ping 64
424	check_exception "" "yes" "redirect exception"
425
426	check_connectivity
427	if [ $? -ne 0 ]; then
428		echo "Error: Basic connectivity is broken after redirect"
429		ret=1
430		return
431	fi
432
433	change_h2_mtu 1300
434	run_ping 1350
435	check_exception "1300" "yes" "redirect exception plus mtu"
436
437	# remove exceptions and restore routing
438	change_h2_mtu 1500
439	eval reset_route_${ttype}
440
441	check_connectivity
442	if [ $? -ne 0 ]; then
443		echo "Error: Basic connectivity is broken after reset"
444		ret=1
445		return
446	fi
447	check_exception "" "no" "routing reset"
448
449	# MTU exception followed by redirect
450	change_h2_mtu 1300
451	run_ping 1350
452	check_exception "1300" "no" "mtu exception"
453
454	eval replace_route_${ttype}
455	run_ping 64
456	check_exception "1300" "yes" "mtu exception plus redirect"
457
458	check_connectivity
459	if [ $? -ne 0 ]; then
460		echo "Error: Basic connectivity is broken after redirect"
461		ret=1
462		return
463	fi
464}
465
466################################################################################
467# usage
468
469usage()
470{
471        cat <<EOF
472usage: ${0##*/} OPTS
473
474	-p          Pause on fail
475	-v          verbose mode (show commands and output)
476EOF
477}
478
479################################################################################
480# main
481
482# Some systems don't have a ping6 binary anymore
483which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
484
485ret=0
486nsuccess=0
487nfail=0
488nxfail=0
489
490while getopts :pv o
491do
492	case $o in
493                p) PAUSE_ON_FAIL=yes;;
494                v) VERBOSE=$(($VERBOSE + 1));;
495                *) usage; exit 1;;
496	esac
497done
498
499trap cleanup EXIT
500
501cleanup
502WITH_VRF=no
503setup
504
505log_section "Legacy routing"
506do_test "legacy"
507
508cleanup
509log_section "Legacy routing with VRF"
510WITH_VRF=yes
511setup
512do_test "legacy"
513
514cleanup
515log_section "Routing with nexthop objects"
516ip nexthop ls >/dev/null 2>&1
517if [ $? -eq 0 ]; then
518	WITH_VRF=no
519	setup
520	do_test "new"
521
522	cleanup
523	log_section "Routing with nexthop objects and VRF"
524	WITH_VRF=yes
525	setup
526	do_test "new"
527else
528	echo "Nexthop objects not supported; skipping tests"
529fi
530
531printf "\nTests passed: %3d\n" ${nsuccess}
532printf "Tests failed: %3d\n"   ${nfail}
533printf "Tests xfailed: %3d\n"  ${nxfail}
534
535exit $ret
536