xref: /linux/tools/testing/selftests/net/traceroute.sh (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# Run traceroute/traceroute6 tests
5#
6
7source lib.sh
8VERBOSE=0
9PAUSE_ON_FAIL=no
10
11################################################################################
12#
13run_cmd()
14{
15	local ns
16	local cmd
17	local out
18	local rc
19
20	ns="$1"
21	shift
22	cmd="$*"
23
24	if [ "$VERBOSE" = "1" ]; then
25		printf "    COMMAND: $cmd\n"
26	fi
27
28	out=$(eval ip netns exec ${ns} ${cmd} 2>&1)
29	rc=$?
30	if [ "$VERBOSE" = "1" -a -n "$out" ]; then
31		echo "    $out"
32	fi
33
34	[ "$VERBOSE" = "1" ] && echo
35
36	return $rc
37}
38
39################################################################################
40# create namespaces and interconnects
41
42create_ns()
43{
44	local ns=$1
45	local addr=$2
46	local addr6=$3
47
48	[ -z "${addr}" ] && addr="-"
49	[ -z "${addr6}" ] && addr6="-"
50
51	if [ "${addr}" != "-" ]; then
52		ip netns exec ${ns} ip addr add dev lo ${addr}
53	fi
54	if [ "${addr6}" != "-" ]; then
55		ip netns exec ${ns} ip -6 addr add dev lo ${addr6}
56	fi
57
58	ip netns exec ${ns} ip ro add unreachable default metric 8192
59	ip netns exec ${ns} ip -6 ro add unreachable default metric 8192
60
61	ip netns exec ${ns} sysctl -qw net.ipv4.ip_forward=1
62	ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1
63	ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.forwarding=1
64	ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.forwarding=1
65	ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.accept_dad=0
66}
67
68# create veth pair to connect namespaces and apply addresses.
69connect_ns()
70{
71	local ns1=$1
72	local ns1_dev=$2
73	local ns1_addr=$3
74	local ns1_addr6=$4
75	local ns2=$5
76	local ns2_dev=$6
77	local ns2_addr=$7
78	local ns2_addr6=$8
79
80	ip netns exec ${ns1} ip li add ${ns1_dev} type veth peer name tmp
81	ip netns exec ${ns1} ip li set ${ns1_dev} up
82	ip netns exec ${ns1} ip li set tmp netns ${ns2} name ${ns2_dev}
83	ip netns exec ${ns2} ip li set ${ns2_dev} up
84
85	if [ "${ns1_addr}" != "-" ]; then
86		ip netns exec ${ns1} ip addr add dev ${ns1_dev} ${ns1_addr}
87	fi
88
89	if [ "${ns2_addr}" != "-" ]; then
90		ip netns exec ${ns2} ip addr add dev ${ns2_dev} ${ns2_addr}
91	fi
92
93	if [ "${ns1_addr6}" != "-" ]; then
94		ip netns exec ${ns1} ip addr add dev ${ns1_dev} ${ns1_addr6}
95	fi
96
97	if [ "${ns2_addr6}" != "-" ]; then
98		ip netns exec ${ns2} ip addr add dev ${ns2_dev} ${ns2_addr6}
99	fi
100}
101
102################################################################################
103# traceroute6 test
104#
105# Verify that in this scenario
106#
107#        ------------------------ N2
108#         |                    |
109#       ------              ------  N3  ----
110#       | R1 |              | R2 |------|H2|
111#       ------              ------      ----
112#         |                    |
113#        ------------------------ N1
114#                  |
115#                 ----
116#                 |H1|
117#                 ----
118#
119# where H1's default route goes through R1 and R1's default route goes
120# through R2 over N2, traceroute6 from H1 to H2 reports R2's address
121# on N2 and not N1.
122#
123# Addresses are assigned as follows:
124#
125# N1: 2000:101::/64
126# N2: 2000:102::/64
127# N3: 2000:103::/64
128#
129# R1's host part of address: 1
130# R2's host part of address: 2
131# H1's host part of address: 3
132# H2's host part of address: 4
133#
134# For example:
135# the IPv6 address of R1's interface on N2 is 2000:102::1/64
136
137cleanup_traceroute6()
138{
139	cleanup_ns $h1 $h2 $r1 $r2
140}
141
142setup_traceroute6()
143{
144	brdev=br0
145
146	# start clean
147	cleanup_traceroute6
148
149	set -e
150	setup_ns h1 h2 r1 r2
151	create_ns $h1
152	create_ns $h2
153	create_ns $r1
154	create_ns $r2
155
156	# Setup N3
157	connect_ns $r2 eth3 - 2000:103::2/64 $h2 eth3 - 2000:103::4/64
158	ip netns exec $h2 ip route add default via 2000:103::2
159
160	# Setup N2
161	connect_ns $r1 eth2 - 2000:102::1/64 $r2 eth2 - 2000:102::2/64
162	ip netns exec $r1 ip route add default via 2000:102::2
163
164	# Setup N1. host-1 and router-2 connect to a bridge in router-1.
165	ip netns exec $r1 ip link add name ${brdev} type bridge
166	ip netns exec $r1 ip link set ${brdev} up
167	ip netns exec $r1 ip addr add 2000:101::1/64 dev ${brdev}
168
169	connect_ns $h1 eth0 - 2000:101::3/64 $r1 eth0 - -
170	ip netns exec $r1 ip link set dev eth0 master ${brdev}
171	ip netns exec $h1 ip route add default via 2000:101::1
172
173	connect_ns $r2 eth1 - 2000:101::2/64 $r1 eth1 - -
174	ip netns exec $r1 ip link set dev eth1 master ${brdev}
175
176	# Prime the network
177	ip netns exec $h1 ping6 -c5 2000:103::4 >/dev/null 2>&1
178
179	set +e
180}
181
182run_traceroute6()
183{
184	setup_traceroute6
185
186	RET=0
187
188	# traceroute6 host-2 from host-1 (expects 2000:102::2)
189	run_cmd $h1 "traceroute6 2000:103::4 | grep -q 2000:102::2"
190	check_err $? "traceroute6 did not return 2000:102::2"
191	log_test "IPv6 traceroute"
192
193	cleanup_traceroute6
194}
195
196################################################################################
197# traceroute6 with VRF test
198#
199# Verify that in this scenario
200#
201#        ------------------------ N2
202#         |                    |
203#       ------              ------  N3  ----
204#       | R1 |              | R2 |------|H2|
205#       ------              ------      ----
206#         |                    |
207#        ------------------------ N1
208#                  |
209#                 ----
210#                 |H1|
211#                 ----
212#
213# Where H1's default route goes through R1 and R1's default route goes through
214# R2 over N2, traceroute6 from H1 to H2 reports R2's address on N2 and not N1.
215# The interfaces connecting R2 to the different subnets are membmer in a VRF
216# and the intention is to check that traceroute6 does not report the VRF's
217# address.
218#
219# Addresses are assigned as follows:
220#
221# N1: 2000:101::/64
222# N2: 2000:102::/64
223# N3: 2000:103::/64
224#
225# R1's host part of address: 1
226# R2's host part of address: 2
227# H1's host part of address: 3
228# H2's host part of address: 4
229#
230# For example:
231# the IPv6 address of R1's interface on N2 is 2000:102::1/64
232
233cleanup_traceroute6_vrf()
234{
235	cleanup_all_ns
236}
237
238setup_traceroute6_vrf()
239{
240	# Start clean
241	cleanup_traceroute6_vrf
242
243	setup_ns h1 h2 r1 r2
244	create_ns "$h1"
245	create_ns "$h2"
246	create_ns "$r1"
247	create_ns "$r2"
248
249	ip -n "$r2" link add name vrf100 up type vrf table 100
250	ip -n "$r2" addr add 2001:db8:100::1/64 dev vrf100
251
252	# Setup N3
253	connect_ns "$r2" eth3 - 2000:103::2/64 "$h2" eth3 - 2000:103::4/64
254
255	ip -n "$r2" link set dev eth3 master vrf100
256
257	ip -n "$h2" route add default via 2000:103::2
258
259	# Setup N2
260	connect_ns "$r1" eth2 - 2000:102::1/64 "$r2" eth2 - 2000:102::2/64
261
262	ip -n "$r1" route add default via 2000:102::2
263
264	ip -n "$r2" link set dev eth2 master vrf100
265
266	# Setup N1. host-1 and router-2 connect to a bridge in router-1.
267	ip -n "$r1" link add name br100 up type bridge
268	ip -n "$r1" addr add 2000:101::1/64 dev br100
269
270	connect_ns "$h1" eth0 - 2000:101::3/64 "$r1" eth0 - -
271
272	ip -n "$h1" route add default via 2000:101::1
273
274	ip -n "$r1" link set dev eth0 master br100
275
276	connect_ns "$r2" eth1 - 2000:101::2/64 "$r1" eth1 - -
277
278	ip -n "$r2" link set dev eth1 master vrf100
279
280	ip -n "$r1" link set dev eth1 master br100
281
282	# Prime the network
283	ip netns exec "$h1" ping6 -c5 2000:103::4 >/dev/null 2>&1
284}
285
286run_traceroute6_vrf()
287{
288	setup_traceroute6_vrf
289
290	RET=0
291
292	# traceroute6 host-2 from host-1 (expects 2000:102::2)
293	run_cmd "$h1" "traceroute6 2000:103::4 | grep 2000:102::2"
294	check_err $? "traceroute6 did not return 2000:102::2"
295	log_test "IPv6 traceroute with VRF"
296
297	cleanup_traceroute6_vrf
298}
299
300################################################################################
301# traceroute test
302#
303# Verify that traceroute from H1 to H2 shows 1.0.3.1 and 1.0.1.1 when
304# traceroute uses 1.0.3.3 and 1.0.1.3 as the source IP, respectively.
305#
306#      1.0.3.3/24    1.0.3.1/24
307# ---- 1.0.1.3/24    1.0.1.1/24 ---- 1.0.2.1/24    1.0.2.4/24 ----
308# |H1|--------------------------|R1|--------------------------|H2|
309# ----            N1            ----            N2            ----
310#
311# where net.ipv4.icmp_errors_use_inbound_ifaddr is set on R1 and 1.0.3.1/24 and
312# 1.0.1.1/24 are R1's primary addresses on N1. The kernel is expected to prefer
313# a source address that is on the same subnet as the destination IP of the ICMP
314# error message.
315
316cleanup_traceroute()
317{
318	cleanup_ns $h1 $h2 $router
319}
320
321setup_traceroute()
322{
323	# start clean
324	cleanup_traceroute
325
326	set -e
327	setup_ns h1 h2 router
328	create_ns $h1
329	create_ns $h2
330	create_ns $router
331
332	connect_ns $h1 eth0 1.0.1.3/24 - \
333	           $router eth1 1.0.3.1/24 -
334	ip -n "$h1" addr add 1.0.3.3/24 dev eth0
335	ip netns exec $h1 ip route add default via 1.0.1.1
336
337	ip netns exec $router ip addr add 1.0.1.1/24 dev eth1
338	ip netns exec $router sysctl -qw \
339				net.ipv4.icmp_errors_use_inbound_ifaddr=1
340
341	connect_ns $h2 eth0 1.0.2.4/24 - \
342	           $router eth2 1.0.2.1/24 -
343	ip netns exec $h2 ip route add default via 1.0.2.1
344
345	# Prime the network
346	ip netns exec $h1 ping -c5 1.0.2.4 >/dev/null 2>&1
347
348	set +e
349}
350
351run_traceroute()
352{
353	setup_traceroute
354
355	RET=0
356
357	# traceroute host-2 from host-1. Expect a source IP that is on the same
358	# subnet as destination IP of the ICMP error message.
359	run_cmd "$h1" "traceroute -s 1.0.1.3 1.0.2.4 | grep -q 1.0.1.1"
360	check_err $? "traceroute did not return 1.0.1.1"
361	run_cmd "$h1" "traceroute -s 1.0.3.3 1.0.2.4 | grep -q 1.0.3.1"
362	check_err $? "traceroute did not return 1.0.3.1"
363	log_test "IPv4 traceroute"
364
365	cleanup_traceroute
366}
367
368################################################################################
369# traceroute with VRF test
370#
371# Verify that traceroute from H1 to H2 shows 1.0.3.1 and 1.0.1.1 when
372# traceroute uses 1.0.3.3 and 1.0.1.3 as the source IP, respectively. The
373# intention is to check that the kernel does not choose an IP assigned to the
374# VRF device, but rather an address from the VRF port (eth1) that received the
375# packet that generates the ICMP error message.
376#
377#                          1.0.4.1/24 (vrf100)
378#      1.0.3.3/24    1.0.3.1/24
379# ---- 1.0.1.3/24    1.0.1.1/24 ---- 1.0.2.1/24    1.0.2.4/24 ----
380# |H1|--------------------------|R1|--------------------------|H2|
381# ----            N1            ----            N2            ----
382
383cleanup_traceroute_vrf()
384{
385	cleanup_all_ns
386}
387
388setup_traceroute_vrf()
389{
390	# Start clean
391	cleanup_traceroute_vrf
392
393	setup_ns h1 h2 router
394	create_ns "$h1"
395	create_ns "$h2"
396	create_ns "$router"
397
398	ip -n "$router" link add name vrf100 up type vrf table 100
399	ip -n "$router" addr add 1.0.4.1/24 dev vrf100
400
401	connect_ns "$h1" eth0 1.0.1.3/24 - \
402	           "$router" eth1 1.0.1.1/24 -
403
404	ip -n "$h1" addr add 1.0.3.3/24 dev eth0
405	ip -n "$h1" route add default via 1.0.1.1
406
407	ip -n "$router" link set dev eth1 master vrf100
408	ip -n "$router" addr add 1.0.3.1/24 dev eth1
409	ip netns exec "$router" sysctl -qw \
410		net.ipv4.icmp_errors_use_inbound_ifaddr=1
411
412	connect_ns "$h2" eth0 1.0.2.4/24 - \
413	           "$router" eth2 1.0.2.1/24 -
414
415	ip -n "$h2" route add default via 1.0.2.1
416
417	ip -n "$router" link set dev eth2 master vrf100
418
419	# Prime the network
420	ip netns exec "$h1" ping -c5 1.0.2.4 >/dev/null 2>&1
421}
422
423run_traceroute_vrf()
424{
425	setup_traceroute_vrf
426
427	RET=0
428
429	# traceroute host-2 from host-1. Expect a source IP that is on the same
430	# subnet as destination IP of the ICMP error message.
431	run_cmd "$h1" "traceroute -s 1.0.1.3 1.0.2.4 | grep 1.0.1.1"
432	check_err $? "traceroute did not return 1.0.1.1"
433	run_cmd "$h1" "traceroute -s 1.0.3.3 1.0.2.4 | grep 1.0.3.1"
434	check_err $? "traceroute did not return 1.0.3.1"
435	log_test "IPv4 traceroute with VRF"
436
437	cleanup_traceroute_vrf
438}
439
440################################################################################
441# Run tests
442
443run_tests()
444{
445	run_traceroute6
446	run_traceroute6_vrf
447	run_traceroute
448	run_traceroute_vrf
449}
450
451################################################################################
452# main
453
454while getopts :pv o
455do
456	case $o in
457		p) PAUSE_ON_FAIL=yes;;
458		v) VERBOSE=$(($VERBOSE + 1));;
459		*) exit 1;;
460	esac
461done
462
463require_command traceroute6
464require_command traceroute
465
466run_tests
467
468exit "${EXIT_STATUS}"
469