xref: /linux/tools/testing/selftests/net/vrf_route_leaking.sh (revision 955abe0a1b41de5ba61fe4cd614ebc123084d499)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# Copyright (c) 2019 David Ahern <dsahern@gmail.com>. All rights reserved.
5# Copyright (c) 2020 Michael Jeanson <mjeanson@efficios.com>. All rights reserved.
6#
7# Requires CONFIG_NET_VRF, CONFIG_VETH, CONFIG_BRIDGE and CONFIG_NET_NS.
8#
9#
10# Symmetric routing topology
11#
12#                     blue         red
13# +----+              .253 +----+ .253              +----+
14# | h1 |-------------------| r1 |-------------------| h2 |
15# +----+ .1                +----+                .2 +----+
16#         172.16.1/24                  172.16.2/24
17#    2001:db8:16:1/64                  2001:db8:16:2/64
18#
19#
20# Route from h1 to h2 and back goes through r1, incoming vrf blue has a route
21# to the outgoing vrf red for the n2 network and red has a route back to n1.
22# The red VRF interface has a MTU of 1400.
23#
24# The first test sends a ping with a ttl of 1 from h1 to h2 and parses the
25# output of the command to check that a ttl expired error is received.
26#
27# The second test runs traceroute from h1 to h2 and parses the output to check
28# for a hop on r1.
29#
30# The third test sends a ping with a packet size of 1450 from h1 to h2 and
31# parses the output of the command to check that a fragmentation error is
32# received.
33#
34#
35# Asymmetric routing topology
36#
37# This topology represents a customer setup where the issue with icmp errors
38# and VRF route leaking was initialy reported. The MTU test isn't done here
39# because of the lack of a return route in the red VRF.
40#
41#                     blue         red
42#                     .253 +----+ .253
43#                     +----| r1 |----+
44#                     |    +----+    |
45# +----+              |              |              +----+
46# | h1 |--------------+              +--------------| h2 |
47# +----+ .1           |              |           .2 +----+
48#         172.16.1/24 |    +----+    | 172.16.2/24
49#    2001:db8:16:1/64 +----| r2 |----+ 2001:db8:16:2/64
50#                     .254 +----+ .254
51#
52#
53# Route from h1 to h2 goes through r1, incoming vrf blue has a route to the
54# outgoing vrf red for the n2 network but red doesn't have a route back to n1.
55# Route from h2 to h1 goes through r2.
56#
57# The objective is to check that the incoming vrf routing table is selected
58# to send an ICMP error back to the source when the ttl of a packet reaches 1
59# while it is forwarded between different vrfs.
60
61source lib.sh
62PATH=$PWD:$PWD/tools/testing/selftests/net:$PATH
63VERBOSE=0
64PAUSE_ON_FAIL=no
65DEFAULT_TTYPE=sym
66
67H1_N1=172.16.1.0/24
68H1_N1_6=2001:db8:16:1::/64
69
70H1_N1_IP=172.16.1.1
71R1_N1_IP=172.16.1.253
72R2_N1_IP=172.16.1.254
73
74H1_N1_IP6=2001:db8:16:1::1
75R1_N1_IP6=2001:db8:16:1::253
76R2_N1_IP6=2001:db8:16:1::254
77
78H2_N2=172.16.2.0/24
79H2_N2_6=2001:db8:16:2::/64
80
81H2_N2_IP=172.16.2.2
82R1_N2_IP=172.16.2.253
83R2_N2_IP=172.16.2.254
84
85H2_N2_IP6=2001:db8:16:2::2
86R1_N2_IP6=2001:db8:16:2::253
87R2_N2_IP6=2001:db8:16:2::254
88
89################################################################################
90# helpers
91
92log_section()
93{
94	echo
95	echo "###########################################################################"
96	echo "$*"
97	echo "###########################################################################"
98	echo
99}
100
101log_test()
102{
103	local rc=$1
104	local expected=$2
105	local msg="$3"
106
107	if [ "${rc}" -eq "${expected}" ]; then
108		printf "TEST: %-60s  [ OK ]\n" "${msg}"
109		nsuccess=$((nsuccess+1))
110	else
111		ret=1
112		nfail=$((nfail+1))
113		printf "TEST: %-60s  [FAIL]\n" "${msg}"
114		if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
115			echo
116			echo "hit enter to continue, 'q' to quit"
117			read -r a
118			[ "$a" = "q" ] && exit 1
119		fi
120	fi
121}
122
123run_cmd()
124{
125	local cmd="$*"
126	local out
127	local rc
128
129	if [ "$VERBOSE" = "1" ]; then
130		echo "COMMAND: $cmd"
131	fi
132
133	# shellcheck disable=SC2086
134	out=$(eval $cmd 2>&1)
135	rc=$?
136	if [ "$VERBOSE" = "1" ] && [ -n "$out" ]; then
137		echo "$out"
138	fi
139
140	[ "$VERBOSE" = "1" ] && echo
141
142	return $rc
143}
144
145run_cmd_grep()
146{
147	local grep_pattern="$1"
148	shift
149	local cmd="$*"
150	local out
151	local rc
152
153	if [ "$VERBOSE" = "1" ]; then
154		echo "COMMAND: $cmd"
155	fi
156
157	# shellcheck disable=SC2086
158	out=$(eval $cmd 2>&1)
159	if [ "$VERBOSE" = "1" ] && [ -n "$out" ]; then
160		echo "$out"
161	fi
162
163	echo "$out" | grep -q "$grep_pattern"
164	rc=$?
165
166	[ "$VERBOSE" = "1" ] && echo
167
168	return $rc
169}
170
171################################################################################
172# setup and teardown
173
174cleanup()
175{
176	cleanup_ns $h1 $h2 $r1 $r2
177}
178
179setup_vrf()
180{
181	local ns=$1
182
183	ip -netns "${ns}" rule del pref 0
184	ip -netns "${ns}" rule add pref 32765 from all lookup local
185	ip -netns "${ns}" -6 rule del pref 0
186	ip -netns "${ns}" -6 rule add pref 32765 from all lookup local
187}
188
189create_vrf()
190{
191	local ns=$1
192	local vrf=$2
193	local table=$3
194
195	ip -netns "${ns}" link add "${vrf}" type vrf table "${table}"
196	ip -netns "${ns}" link set "${vrf}" up
197	ip -netns "${ns}" route add vrf "${vrf}" unreachable default metric 8192
198	ip -netns "${ns}" -6 route add vrf "${vrf}" unreachable default metric 8192
199
200	ip -netns "${ns}" addr add 127.0.0.1/8 dev "${vrf}"
201	ip -netns "${ns}" -6 addr add ::1 dev "${vrf}" nodad
202}
203
204setup_sym()
205{
206	local ns
207
208	# make sure we are starting with a clean slate
209	cleanup
210
211	#
212	# create nodes as namespaces
213	setup_ns h1 h2 r1
214	for ns in $h1 $h2 $r1; do
215		if echo $ns | grep -q h[12]-; then
216			ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=0
217			ip netns exec $ns sysctl -q -w net.ipv6.conf.all.keep_addr_on_down=1
218		else
219			ip netns exec $ns sysctl -q -w net.ipv4.ip_forward=1
220			ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=1
221		fi
222	done
223
224	#
225	# create interconnects
226	#
227	ip -netns $h1 link add eth0 type veth peer name r1h1
228	ip -netns $h1 link set r1h1 netns $r1 name eth0 up
229
230	ip -netns $h2 link add eth0 type veth peer name r1h2
231	ip -netns $h2 link set r1h2 netns $r1 name eth1 up
232
233	#
234	# h1
235	#
236	ip -netns $h1 addr add dev eth0 ${H1_N1_IP}/24
237	ip -netns $h1 -6 addr add dev eth0 ${H1_N1_IP6}/64 nodad
238	ip -netns $h1 link set eth0 up
239
240	# h1 to h2 via r1
241	ip -netns $h1    route add ${H2_N2} via ${R1_N1_IP} dev eth0
242	ip -netns $h1 -6 route add ${H2_N2_6} via "${R1_N1_IP6}" dev eth0
243
244	#
245	# h2
246	#
247	ip -netns $h2 addr add dev eth0 ${H2_N2_IP}/24
248	ip -netns $h2 -6 addr add dev eth0 ${H2_N2_IP6}/64 nodad
249	ip -netns $h2 link set eth0 up
250
251	# h2 to h1 via r1
252	ip -netns $h2 route add default via ${R1_N2_IP} dev eth0
253	ip -netns $h2 -6 route add default via ${R1_N2_IP6} dev eth0
254
255	#
256	# r1
257	#
258	setup_vrf $r1
259	create_vrf $r1 blue 1101
260	create_vrf $r1 red 1102
261	ip -netns $r1 link set mtu 1400 dev eth1
262	ip -netns $r1 link set eth0 vrf blue up
263	ip -netns $r1 link set eth1 vrf red up
264	ip -netns $r1 addr add dev eth0 ${R1_N1_IP}/24
265	ip -netns $r1 -6 addr add dev eth0 ${R1_N1_IP6}/64 nodad
266	ip -netns $r1 addr add dev eth1 ${R1_N2_IP}/24
267	ip -netns $r1 -6 addr add dev eth1 ${R1_N2_IP6}/64 nodad
268
269	# Route leak from blue to red
270	ip -netns $r1 route add vrf blue ${H2_N2} dev red
271	ip -netns $r1 -6 route add vrf blue ${H2_N2_6} dev red
272
273	# Route leak from red to blue
274	ip -netns $r1 route add vrf red ${H1_N1} dev blue
275	ip -netns $r1 -6 route add vrf red ${H1_N1_6} dev blue
276
277
278	# Wait for ip config to settle
279	sleep 2
280}
281
282setup_asym()
283{
284	local ns
285
286	# make sure we are starting with a clean slate
287	cleanup
288
289	#
290	# create nodes as namespaces
291	setup_ns h1 h2 r1 r2
292	for ns in $h1 $h2 $r1 $r2; do
293		if echo $ns | grep -q h[12]-; then
294			ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=0
295			ip netns exec $ns sysctl -q -w net.ipv6.conf.all.keep_addr_on_down=1
296		else
297			ip netns exec $ns sysctl -q -w net.ipv4.ip_forward=1
298			ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=1
299		fi
300	done
301
302	#
303	# create interconnects
304	#
305	ip -netns $h1 link add eth0 type veth peer name r1h1
306	ip -netns $h1 link set r1h1 netns $r1 name eth0 up
307
308	ip -netns $h1 link add eth1 type veth peer name r2h1
309	ip -netns $h1 link set r2h1 netns $r2 name eth0 up
310
311	ip -netns $h2 link add eth0 type veth peer name r1h2
312	ip -netns $h2 link set r1h2 netns $r1 name eth1 up
313
314	ip -netns $h2 link add eth1 type veth peer name r2h2
315	ip -netns $h2 link set r2h2 netns $r2 name eth1 up
316
317	#
318	# h1
319	#
320	ip -netns $h1 link add br0 type bridge
321	ip -netns $h1 link set br0 up
322	ip -netns $h1 addr add dev br0 ${H1_N1_IP}/24
323	ip -netns $h1 -6 addr add dev br0 ${H1_N1_IP6}/64 nodad
324	ip -netns $h1 link set eth0 master br0 up
325	ip -netns $h1 link set eth1 master br0 up
326
327	# h1 to h2 via r1
328	ip -netns $h1    route add ${H2_N2} via ${R1_N1_IP} dev br0
329	ip -netns $h1 -6 route add ${H2_N2_6} via "${R1_N1_IP6}" dev br0
330
331	#
332	# h2
333	#
334	ip -netns $h2 link add br0 type bridge
335	ip -netns $h2 link set br0 up
336	ip -netns $h2 addr add dev br0 ${H2_N2_IP}/24
337	ip -netns $h2 -6 addr add dev br0 ${H2_N2_IP6}/64 nodad
338	ip -netns $h2 link set eth0 master br0 up
339	ip -netns $h2 link set eth1 master br0 up
340
341	# h2 to h1 via r2
342	ip -netns $h2 route add default via ${R2_N2_IP} dev br0
343	ip -netns $h2 -6 route add default via ${R2_N2_IP6} dev br0
344
345	#
346	# r1
347	#
348	setup_vrf $r1
349	create_vrf $r1 blue 1101
350	create_vrf $r1 red 1102
351	ip -netns $r1 link set mtu 1400 dev eth1
352	ip -netns $r1 link set eth0 vrf blue up
353	ip -netns $r1 link set eth1 vrf red up
354	ip -netns $r1 addr add dev eth0 ${R1_N1_IP}/24
355	ip -netns $r1 -6 addr add dev eth0 ${R1_N1_IP6}/64 nodad
356	ip -netns $r1 addr add dev eth1 ${R1_N2_IP}/24
357	ip -netns $r1 -6 addr add dev eth1 ${R1_N2_IP6}/64 nodad
358
359	# Route leak from blue to red
360	ip -netns $r1 route add vrf blue ${H2_N2} dev red
361	ip -netns $r1 -6 route add vrf blue ${H2_N2_6} dev red
362
363	# No route leak from red to blue
364
365	#
366	# r2
367	#
368	ip -netns $r2 addr add dev eth0 ${R2_N1_IP}/24
369	ip -netns $r2 -6 addr add dev eth0 ${R2_N1_IP6}/64 nodad
370	ip -netns $r2 addr add dev eth1 ${R2_N2_IP}/24
371	ip -netns $r2 -6 addr add dev eth1 ${R2_N2_IP6}/64 nodad
372
373	# Wait for ip config to settle
374	sleep 2
375}
376
377check_connectivity()
378{
379	ip netns exec $h1 ping -c1 -w1 ${H2_N2_IP} >/dev/null 2>&1
380	log_test $? 0 "Basic IPv4 connectivity"
381	return $?
382}
383
384check_connectivity6()
385{
386	ip netns exec $h1 "${ping6}" -c1 -w1 ${H2_N2_IP6} >/dev/null 2>&1
387	log_test $? 0 "Basic IPv6 connectivity"
388	return $?
389}
390
391check_traceroute()
392{
393	if [ ! -x "$(command -v traceroute)" ]; then
394		echo "SKIP: Could not run IPV4 test without traceroute"
395		return 1
396	fi
397}
398
399check_traceroute6()
400{
401	if [ ! -x "$(command -v traceroute6)" ]; then
402		echo "SKIP: Could not run IPV6 test without traceroute6"
403		return 1
404	fi
405}
406
407ipv4_traceroute()
408{
409	local ttype="$1"
410
411	[ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE"
412
413	log_section "IPv4 ($ttype route): VRF ICMP error route lookup traceroute"
414
415	check_traceroute || return
416
417	setup_"$ttype"
418
419	check_connectivity || return
420
421	run_cmd_grep "${R1_N1_IP}" ip netns exec $h1 traceroute ${H2_N2_IP}
422	log_test $? 0 "Traceroute reports a hop on r1"
423}
424
425ipv4_traceroute_asym()
426{
427	ipv4_traceroute asym
428}
429
430ipv6_traceroute()
431{
432	local ttype="$1"
433
434	[ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE"
435
436	log_section "IPv6 ($ttype route): VRF ICMP error route lookup traceroute"
437
438	check_traceroute6 || return
439
440	setup_"$ttype"
441
442	check_connectivity6 || return
443
444	run_cmd_grep "${R1_N1_IP6}" ip netns exec $h1 traceroute6 ${H2_N2_IP6}
445	log_test $? 0 "Traceroute6 reports a hop on r1"
446}
447
448ipv6_traceroute_asym()
449{
450	ipv6_traceroute asym
451}
452
453ipv4_ping_ttl()
454{
455	local ttype="$1"
456
457	[ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE"
458
459	log_section "IPv4 ($ttype route): VRF ICMP ttl error route lookup ping"
460
461	setup_"$ttype"
462
463	check_connectivity || return
464
465	run_cmd_grep "Time to live exceeded" ip netns exec $h1 ping -t1 -c1 -W2 ${H2_N2_IP}
466	log_test $? 0 "Ping received ICMP ttl exceeded"
467}
468
469ipv4_ping_ttl_asym()
470{
471	ipv4_ping_ttl asym
472}
473
474ipv4_ping_frag()
475{
476	local ttype="$1"
477
478	[ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE"
479
480	log_section "IPv4 ($ttype route): VRF ICMP fragmentation error route lookup ping"
481
482	setup_"$ttype"
483
484	check_connectivity || return
485
486	run_cmd_grep "Frag needed" ip netns exec $h1 ping -s 1450 -Mdo -c1 -W2 ${H2_N2_IP}
487	log_test $? 0 "Ping received ICMP Frag needed"
488}
489
490ipv4_ping_frag_asym()
491{
492	ipv4_ping_frag asym
493}
494
495ipv6_ping_ttl()
496{
497	local ttype="$1"
498
499	[ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE"
500
501	log_section "IPv6 ($ttype route): VRF ICMP ttl error route lookup ping"
502
503	setup_"$ttype"
504
505	check_connectivity6 || return
506
507	run_cmd_grep "Time exceeded: Hop limit" ip netns exec $h1 "${ping6}" -t1 -c1 -W2 ${H2_N2_IP6}
508	log_test $? 0 "Ping received ICMP Hop limit"
509}
510
511ipv6_ping_ttl_asym()
512{
513	ipv6_ping_ttl asym
514}
515
516ipv6_ping_frag()
517{
518	local ttype="$1"
519
520	[ "x$ttype" = "x" ] && ttype="$DEFAULT_TTYPE"
521
522	log_section "IPv6 ($ttype route): VRF ICMP fragmentation error route lookup ping"
523
524	setup_"$ttype"
525
526	check_connectivity6 || return
527
528	run_cmd_grep "Packet too big" ip netns exec $h1 "${ping6}" -s 1450 -Mdo -c1 -W2 ${H2_N2_IP6}
529	log_test $? 0 "Ping received ICMP Packet too big"
530}
531
532ipv6_ping_frag_asym()
533{
534	ipv6_ping_frag asym
535}
536
537ipv4_ping_local()
538{
539	log_section "IPv4 (sym route): VRF ICMP local error route lookup ping"
540
541	setup_sym
542
543	check_connectivity || return
544
545	run_cmd ip netns exec $r1 ip vrf exec blue ping -c1 -w1 ${H2_N2_IP}
546	log_test $? 0 "VRF ICMP local IPv4"
547}
548
549ipv4_tcp_local()
550{
551	log_section "IPv4 (sym route): VRF tcp local connection"
552
553	setup_sym
554
555	check_connectivity || return
556
557	run_cmd nettest -s -O "$h2" -l ${H2_N2_IP} -I eth0 -3 eth0 &
558	sleep 1
559	run_cmd nettest -N "$r1" -d blue -r ${H2_N2_IP}
560	log_test $? 0 "VRF tcp local connection IPv4"
561}
562
563ipv4_udp_local()
564{
565	log_section "IPv4 (sym route): VRF udp local connection"
566
567	setup_sym
568
569	check_connectivity || return
570
571	run_cmd nettest -s -D -O "$h2" -l ${H2_N2_IP} -I eth0 -3 eth0 &
572	sleep 1
573	run_cmd nettest -D -N "$r1" -d blue -r ${H2_N2_IP}
574	log_test $? 0 "VRF udp local connection IPv4"
575}
576
577ipv6_ping_local()
578{
579	log_section "IPv6 (sym route): VRF ICMP local error route lookup ping"
580
581	setup_sym
582
583	check_connectivity6 || return
584
585	run_cmd ip netns exec $r1 ip vrf exec blue ${ping6} -c1 -w1 ${H2_N2_IP6}
586	log_test $? 0 "VRF ICMP local IPv6"
587}
588
589ipv6_tcp_local()
590{
591	log_section "IPv6 (sym route): VRF tcp local connection"
592
593	setup_sym
594
595	check_connectivity6 || return
596
597	run_cmd nettest -s -6 -O "$h2" -l ${H2_N2_IP6} -I eth0 -3 eth0 &
598	sleep 1
599	run_cmd nettest -6 -N "$r1" -d blue -r ${H2_N2_IP6}
600	log_test $? 0 "VRF tcp local connection IPv6"
601}
602
603ipv6_udp_local()
604{
605	log_section "IPv6 (sym route): VRF udp local connection"
606
607	setup_sym
608
609	check_connectivity6 || return
610
611	run_cmd nettest -s -6 -D -O "$h2" -l ${H2_N2_IP6} -I eth0 -3 eth0 &
612	sleep 1
613	run_cmd nettest -6 -D -N "$r1" -d blue -r ${H2_N2_IP6}
614	log_test $? 0 "VRF udp local connection IPv6"
615}
616
617################################################################################
618# usage
619
620usage()
621{
622        cat <<EOF
623usage: ${0##*/} OPTS
624
625	-4          Run IPv4 tests only
626	-6          Run IPv6 tests only
627        -t TEST     Run only TEST
628	-p          Pause on fail
629	-v          verbose mode (show commands and output)
630EOF
631}
632
633################################################################################
634# main
635
636# Some systems don't have a ping6 binary anymore
637command -v ping6 > /dev/null 2>&1 && ping6=$(command -v ping6) || ping6=$(command -v ping)
638
639TESTS_IPV4="ipv4_ping_ttl ipv4_traceroute ipv4_ping_frag ipv4_ping_local ipv4_tcp_local
640ipv4_udp_local ipv4_ping_ttl_asym ipv4_traceroute_asym"
641TESTS_IPV6="ipv6_ping_ttl ipv6_traceroute ipv6_ping_local ipv6_tcp_local ipv6_udp_local
642ipv6_ping_ttl_asym ipv6_traceroute_asym"
643
644ret=0
645nsuccess=0
646nfail=0
647
648while getopts :46t:pvh o
649do
650	case $o in
651		4) TESTS=ipv4;;
652		6) TESTS=ipv6;;
653		t) TESTS=$OPTARG;;
654		p) PAUSE_ON_FAIL=yes;;
655		v) VERBOSE=1;;
656		h) usage; exit 0;;
657		*) usage; exit 1;;
658	esac
659done
660
661#
662# show user test config
663#
664if [ -z "$TESTS" ]; then
665        TESTS="$TESTS_IPV4 $TESTS_IPV6"
666elif [ "$TESTS" = "ipv4" ]; then
667        TESTS="$TESTS_IPV4"
668elif [ "$TESTS" = "ipv6" ]; then
669        TESTS="$TESTS_IPV6"
670fi
671
672for t in $TESTS
673do
674	case $t in
675	ipv4_ping_ttl|ping)              ipv4_ping_ttl;;&
676	ipv4_ping_ttl_asym|ping)         ipv4_ping_ttl_asym;;&
677	ipv4_traceroute|traceroute)      ipv4_traceroute;;&
678	ipv4_traceroute_asym|traceroute) ipv4_traceroute_asym;;&
679	ipv4_ping_frag|ping)             ipv4_ping_frag;;&
680	ipv4_ping_local|ping)            ipv4_ping_local;;&
681	ipv4_tcp_local)                  ipv4_tcp_local;;&
682	ipv4_udp_local)                  ipv4_udp_local;;&
683
684	ipv6_ping_ttl|ping)              ipv6_ping_ttl;;&
685	ipv6_ping_ttl_asym|ping)         ipv6_ping_ttl_asym;;&
686	ipv6_traceroute|traceroute)      ipv6_traceroute;;&
687	ipv6_traceroute_asym|traceroute) ipv6_traceroute_asym;;&
688	ipv6_ping_frag|ping)             ipv6_ping_frag;;&
689	ipv6_ping_local|ping)            ipv6_ping_local;;&
690	ipv6_tcp_local)                  ipv6_tcp_local;;&
691	ipv6_udp_local)                  ipv6_udp_local;;&
692
693	# setup namespaces and config, but do not run any tests
694	setup_sym|setup)                 setup_sym; exit 0;;
695	setup_asym)                      setup_asym; exit 0;;
696
697	help)                       echo "Test names: $TESTS"; exit 0;;
698	esac
699done
700
701cleanup
702
703printf "\nTests passed: %3d\n" ${nsuccess}
704printf "Tests failed: %3d\n"   ${nfail}
705
706exit $ret
707