xref: /linux/tools/testing/selftests/net/traceroute.sh (revision 6dfafbd0299a60bfb5d5e277fdf100037c7ded07)
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__check_traceroute_version()
40{
41	local cmd=$1; shift
42	local req_ver=$1; shift
43	local ver
44
45	req_ver=$(echo "$req_ver" | sed 's/\.//g')
46	ver=$($cmd -V 2>&1 | grep -Eo '[0-9]+.[0-9]+.[0-9]+' | sed 's/\.//g')
47	if [[ $ver -lt $req_ver ]]; then
48		return 1
49	else
50		return 0
51	fi
52}
53
54check_traceroute6_version()
55{
56	local req_ver=$1; shift
57
58	__check_traceroute_version traceroute6 "$req_ver"
59}
60
61check_traceroute_version()
62{
63	local req_ver=$1; shift
64
65	__check_traceroute_version traceroute "$req_ver"
66}
67
68################################################################################
69# create namespaces and interconnects
70
71create_ns()
72{
73	local ns=$1
74	local addr=$2
75	local addr6=$3
76
77	[ -z "${addr}" ] && addr="-"
78	[ -z "${addr6}" ] && addr6="-"
79
80	if [ "${addr}" != "-" ]; then
81		ip netns exec ${ns} ip addr add dev lo ${addr}
82	fi
83	if [ "${addr6}" != "-" ]; then
84		ip netns exec ${ns} ip -6 addr add dev lo ${addr6}
85	fi
86
87	ip netns exec ${ns} ip ro add unreachable default metric 8192
88	ip netns exec ${ns} ip -6 ro add unreachable default metric 8192
89
90	ip netns exec ${ns} sysctl -qw net.ipv4.ip_forward=1
91	ip netns exec ${ns} sysctl -qw net.ipv4.icmp_ratelimit=0
92	ip netns exec ${ns} sysctl -qw net.ipv6.icmp.ratelimit=0
93	ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1
94	ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.forwarding=1
95	ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.forwarding=1
96	ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.accept_dad=0
97}
98
99# create veth pair to connect namespaces and apply addresses.
100connect_ns()
101{
102	local ns1=$1
103	local ns1_dev=$2
104	local ns1_addr=$3
105	local ns1_addr6=$4
106	local ns2=$5
107	local ns2_dev=$6
108	local ns2_addr=$7
109	local ns2_addr6=$8
110
111	ip netns exec ${ns1} ip li add ${ns1_dev} type veth peer name tmp
112	ip netns exec ${ns1} ip li set ${ns1_dev} up
113	ip netns exec ${ns1} ip li set tmp netns ${ns2} name ${ns2_dev}
114	ip netns exec ${ns2} ip li set ${ns2_dev} up
115
116	if [ "${ns1_addr}" != "-" ]; then
117		ip netns exec ${ns1} ip addr add dev ${ns1_dev} ${ns1_addr}
118	fi
119
120	if [ "${ns2_addr}" != "-" ]; then
121		ip netns exec ${ns2} ip addr add dev ${ns2_dev} ${ns2_addr}
122	fi
123
124	if [ "${ns1_addr6}" != "-" ]; then
125		ip netns exec ${ns1} ip addr add dev ${ns1_dev} ${ns1_addr6}
126	fi
127
128	if [ "${ns2_addr6}" != "-" ]; then
129		ip netns exec ${ns2} ip addr add dev ${ns2_dev} ${ns2_addr6}
130	fi
131}
132
133################################################################################
134# traceroute6 test
135#
136# Verify that in this scenario
137#
138#        ------------------------ N2
139#         |                    |
140#       ------              ------  N3  ----
141#       | R1 |              | R2 |------|H2|
142#       ------              ------      ----
143#         |                    |
144#        ------------------------ N1
145#                  |
146#                 ----
147#                 |H1|
148#                 ----
149#
150# where H1's default route goes through R1 and R1's default route goes
151# through R2 over N2, traceroute6 from H1 to H2 reports R2's address
152# on N2 and not N1.
153#
154# Addresses are assigned as follows:
155#
156# N1: 2000:101::/64
157# N2: 2000:102::/64
158# N3: 2000:103::/64
159#
160# R1's host part of address: 1
161# R2's host part of address: 2
162# H1's host part of address: 3
163# H2's host part of address: 4
164#
165# For example:
166# the IPv6 address of R1's interface on N2 is 2000:102::1/64
167
168cleanup_traceroute6()
169{
170	cleanup_ns $h1 $h2 $r1 $r2
171}
172
173setup_traceroute6()
174{
175	brdev=br0
176
177	# start clean
178	cleanup_traceroute6
179
180	set -e
181	setup_ns h1 h2 r1 r2
182	create_ns $h1
183	create_ns $h2
184	create_ns $r1
185	create_ns $r2
186
187	# Setup N3
188	connect_ns $r2 eth3 - 2000:103::2/64 $h2 eth3 - 2000:103::4/64
189	ip netns exec $h2 ip route add default via 2000:103::2
190
191	# Setup N2
192	connect_ns $r1 eth2 - 2000:102::1/64 $r2 eth2 - 2000:102::2/64
193	ip netns exec $r1 ip route add default via 2000:102::2
194
195	# Setup N1. host-1 and router-2 connect to a bridge in router-1.
196	ip netns exec $r1 ip link add name ${brdev} type bridge
197	ip netns exec $r1 ip link set ${brdev} up
198	ip netns exec $r1 ip addr add 2000:101::1/64 dev ${brdev}
199
200	connect_ns $h1 eth0 - 2000:101::3/64 $r1 eth0 - -
201	ip netns exec $r1 ip link set dev eth0 master ${brdev}
202	ip netns exec $h1 ip route add default via 2000:101::1
203
204	connect_ns $r2 eth1 - 2000:101::2/64 $r1 eth1 - -
205	ip netns exec $r1 ip link set dev eth1 master ${brdev}
206
207	# Prime the network
208	ip netns exec $h1 ping6 -c5 2000:103::4 >/dev/null 2>&1
209
210	set +e
211}
212
213run_traceroute6()
214{
215	setup_traceroute6
216
217	RET=0
218
219	# traceroute6 host-2 from host-1 (expects 2000:102::2)
220	run_cmd $h1 "traceroute6 2000:103::4 | grep -q 2000:102::2"
221	check_err $? "traceroute6 did not return 2000:102::2"
222	log_test "IPv6 traceroute"
223
224	cleanup_traceroute6
225}
226
227################################################################################
228# traceroute6 with VRF test
229#
230# Verify that in this scenario
231#
232#        ------------------------ N2
233#         |                    |
234#       ------              ------  N3  ----
235#       | R1 |              | R2 |------|H2|
236#       ------              ------      ----
237#         |                    |
238#        ------------------------ N1
239#                  |
240#                 ----
241#                 |H1|
242#                 ----
243#
244# Where H1's default route goes through R1 and R1's default route goes through
245# R2 over N2, traceroute6 from H1 to H2 reports R2's address on N2 and not N1.
246# The interfaces connecting R2 to the different subnets are membmer in a VRF
247# and the intention is to check that traceroute6 does not report the VRF's
248# address.
249#
250# Addresses are assigned as follows:
251#
252# N1: 2000:101::/64
253# N2: 2000:102::/64
254# N3: 2000:103::/64
255#
256# R1's host part of address: 1
257# R2's host part of address: 2
258# H1's host part of address: 3
259# H2's host part of address: 4
260#
261# For example:
262# the IPv6 address of R1's interface on N2 is 2000:102::1/64
263
264cleanup_traceroute6_vrf()
265{
266	cleanup_all_ns
267}
268
269setup_traceroute6_vrf()
270{
271	# Start clean
272	cleanup_traceroute6_vrf
273
274	setup_ns h1 h2 r1 r2
275	create_ns "$h1"
276	create_ns "$h2"
277	create_ns "$r1"
278	create_ns "$r2"
279
280	ip -n "$r2" link add name vrf100 up type vrf table 100
281	ip -n "$r2" addr add 2001:db8:100::1/64 dev vrf100
282
283	# Setup N3
284	connect_ns "$r2" eth3 - 2000:103::2/64 "$h2" eth3 - 2000:103::4/64
285
286	ip -n "$r2" link set dev eth3 master vrf100
287
288	ip -n "$h2" route add default via 2000:103::2
289
290	# Setup N2
291	connect_ns "$r1" eth2 - 2000:102::1/64 "$r2" eth2 - 2000:102::2/64
292
293	ip -n "$r1" route add default via 2000:102::2
294
295	ip -n "$r2" link set dev eth2 master vrf100
296
297	# Setup N1. host-1 and router-2 connect to a bridge in router-1.
298	ip -n "$r1" link add name br100 up type bridge
299	ip -n "$r1" addr add 2000:101::1/64 dev br100
300
301	connect_ns "$h1" eth0 - 2000:101::3/64 "$r1" eth0 - -
302
303	ip -n "$h1" route add default via 2000:101::1
304
305	ip -n "$r1" link set dev eth0 master br100
306
307	connect_ns "$r2" eth1 - 2000:101::2/64 "$r1" eth1 - -
308
309	ip -n "$r2" link set dev eth1 master vrf100
310
311	ip -n "$r1" link set dev eth1 master br100
312
313	# Prime the network
314	ip netns exec "$h1" ping6 -c5 2000:103::4 >/dev/null 2>&1
315}
316
317run_traceroute6_vrf()
318{
319	setup_traceroute6_vrf
320
321	RET=0
322
323	# traceroute6 host-2 from host-1 (expects 2000:102::2)
324	run_cmd "$h1" "traceroute6 2000:103::4 | grep 2000:102::2"
325	check_err $? "traceroute6 did not return 2000:102::2"
326	log_test "IPv6 traceroute with VRF"
327
328	cleanup_traceroute6_vrf
329}
330
331################################################################################
332# traceroute6 with ICMP extensions test
333#
334# Verify that in this scenario
335#
336# ----                          ----                          ----
337# |H1|--------------------------|R1|--------------------------|H2|
338# ----            N1            ----            N2            ----
339#
340# ICMP extensions are correctly reported. The loopback interfaces on all the
341# nodes are assigned global addresses and the interfaces connecting the nodes
342# are assigned IPv6 link-local addresses.
343
344cleanup_traceroute6_ext()
345{
346	cleanup_all_ns
347}
348
349setup_traceroute6_ext()
350{
351	# Start clean
352	cleanup_traceroute6_ext
353
354	setup_ns h1 r1 h2
355	create_ns "$h1"
356	create_ns "$r1"
357	create_ns "$h2"
358
359	# Setup N1
360	connect_ns "$h1" eth1 - fe80::1/64 "$r1" eth1 - fe80::2/64
361	# Setup N2
362	connect_ns "$r1" eth2 - fe80::3/64 "$h2" eth2 - fe80::4/64
363
364	# Setup H1
365	ip -n "$h1" address add 2001:db8:1::1/128 dev lo
366	ip -n "$h1" route add ::/0 nexthop via fe80::2 dev eth1
367
368	# Setup R1
369	ip -n "$r1" address add 2001:db8:1::2/128 dev lo
370	ip -n "$r1" route add 2001:db8:1::1/128 nexthop via fe80::1 dev eth1
371	ip -n "$r1" route add 2001:db8:1::3/128 nexthop via fe80::4 dev eth2
372
373	# Setup H2
374	ip -n "$h2" address add 2001:db8:1::3/128 dev lo
375	ip -n "$h2" route add ::/0 nexthop via fe80::3 dev eth2
376
377	# Prime the network
378	ip netns exec "$h1" ping6 -c5 2001:db8:1::3 >/dev/null 2>&1
379}
380
381traceroute6_ext_iio_iif_test()
382{
383	local r1_ifindex h2_ifindex
384	local pkt_len=$1; shift
385
386	# Test that incoming interface info is not appended by default.
387	run_cmd "$h1" "traceroute6 -e 2001:db8:1::3 $pkt_len | grep INC"
388	check_fail $? "Incoming interface info appended by default when should not"
389
390	# Test that the extension is appended when enabled.
391	run_cmd "$r1" "bash -c \"echo 0x01 > /proc/sys/net/ipv6/icmp/errors_extension_mask\""
392	check_err $? "Failed to enable incoming interface info extension on R1"
393
394	run_cmd "$h1" "traceroute6 -e 2001:db8:1::3 $pkt_len | grep INC"
395	check_err $? "Incoming interface info not appended after enable"
396
397	# Test that the extension is not appended when disabled.
398	run_cmd "$r1" "bash -c \"echo 0x00 > /proc/sys/net/ipv6/icmp/errors_extension_mask\""
399	check_err $? "Failed to disable incoming interface info extension on R1"
400
401	run_cmd "$h1" "traceroute6 -e 2001:db8:1::3 $pkt_len | grep INC"
402	check_fail $? "Incoming interface info appended after disable"
403
404	# Test that the extension is sent correctly from both R1 and H2.
405	run_cmd "$r1" "sysctl -w net.ipv6.icmp.errors_extension_mask=0x01"
406	r1_ifindex=$(ip -n "$r1" -j link show dev eth1 | jq '.[]["ifindex"]')
407	run_cmd "$h1" "traceroute6 -e 2001:db8:1::3 $pkt_len | grep '<INC:$r1_ifindex,\"eth1\",mtu=1500>'"
408	check_err $? "Wrong incoming interface info reported from R1"
409
410	run_cmd "$h2" "sysctl -w net.ipv6.icmp.errors_extension_mask=0x01"
411	h2_ifindex=$(ip -n "$h2" -j link show dev eth2 | jq '.[]["ifindex"]')
412	run_cmd "$h1" "traceroute6 -e 2001:db8:1::3 $pkt_len | grep '<INC:$h2_ifindex,\"eth2\",mtu=1500>'"
413	check_err $? "Wrong incoming interface info reported from H2"
414
415	# Add a global address on the incoming interface of R1 and check that
416	# it is reported.
417	run_cmd "$r1" "ip address add 2001:db8:100::1/64 dev eth1 nodad"
418	run_cmd "$h1" "traceroute6 -e 2001:db8:1::3 $pkt_len | grep '<INC:$r1_ifindex,2001:db8:100::1,\"eth1\",mtu=1500>'"
419	check_err $? "Wrong incoming interface info reported from R1 after address addition"
420	run_cmd "$r1" "ip address del 2001:db8:100::1/64 dev eth1"
421
422	# Change name and MTU and make sure the result is still correct.
423	run_cmd "$r1" "ip link set dev eth1 name eth1tag mtu 1501"
424	run_cmd "$h1" "traceroute6 -e 2001:db8:1::3 $pkt_len | grep '<INC:$r1_ifindex,\"eth1tag\",mtu=1501>'"
425	check_err $? "Wrong incoming interface info reported from R1 after name and MTU change"
426	run_cmd "$r1" "ip link set dev eth1tag name eth1 mtu 1500"
427
428	run_cmd "$r1" "sysctl -w net.ipv6.icmp.errors_extension_mask=0x00"
429	run_cmd "$h2" "sysctl -w net.ipv6.icmp.errors_extension_mask=0x00"
430}
431
432run_traceroute6_ext()
433{
434	# Need at least version 2.1.5 for RFC 5837 support.
435	if ! check_traceroute6_version 2.1.5; then
436		log_test_skip "traceroute6 too old, missing ICMP extensions support"
437		return
438	fi
439
440	setup_traceroute6_ext
441
442	RET=0
443
444	## General ICMP extensions tests
445
446	# Test that ICMP extensions are disabled by default.
447	run_cmd "$h1" "sysctl net.ipv6.icmp.errors_extension_mask | grep \"= 0$\""
448	check_err $? "ICMP extensions are not disabled by default"
449
450	# Test that unsupported values are rejected. Do not use "sysctl" as
451	# older versions do not return an error code upon failure.
452	run_cmd "$h1" "bash -c \"echo 0x80 > /proc/sys/net/ipv6/icmp/errors_extension_mask\""
453	check_fail $? "Unsupported sysctl value was not rejected"
454
455	## Extension-specific tests
456
457	# Incoming interface info test. Test with various packet sizes,
458	# including the default one.
459	traceroute6_ext_iio_iif_test
460	traceroute6_ext_iio_iif_test 127
461	traceroute6_ext_iio_iif_test 128
462	traceroute6_ext_iio_iif_test 129
463
464	log_test "IPv6 traceroute with ICMP extensions"
465
466	cleanup_traceroute6_ext
467}
468
469################################################################################
470# traceroute test
471#
472# Verify that traceroute from H1 to H2 shows 1.0.3.1 and 1.0.1.1 when
473# traceroute uses 1.0.3.3 and 1.0.1.3 as the source IP, respectively.
474#
475#      1.0.3.3/24    1.0.3.1/24
476# ---- 1.0.1.3/24    1.0.1.1/24 ---- 1.0.2.1/24    1.0.2.4/24 ----
477# |H1|--------------------------|R1|--------------------------|H2|
478# ----            N1            ----            N2            ----
479#
480# where net.ipv4.icmp_errors_use_inbound_ifaddr is set on R1 and 1.0.3.1/24 and
481# 1.0.1.1/24 are R1's primary addresses on N1. The kernel is expected to prefer
482# a source address that is on the same subnet as the destination IP of the ICMP
483# error message.
484
485cleanup_traceroute()
486{
487	cleanup_ns $h1 $h2 $router
488}
489
490setup_traceroute()
491{
492	# start clean
493	cleanup_traceroute
494
495	set -e
496	setup_ns h1 h2 router
497	create_ns $h1
498	create_ns $h2
499	create_ns $router
500
501	connect_ns $h1 eth0 1.0.1.3/24 - \
502	           $router eth1 1.0.3.1/24 -
503	ip -n "$h1" addr add 1.0.3.3/24 dev eth0
504	ip netns exec $h1 ip route add default via 1.0.1.1
505
506	ip netns exec $router ip addr add 1.0.1.1/24 dev eth1
507	ip netns exec $router sysctl -qw \
508				net.ipv4.icmp_errors_use_inbound_ifaddr=1
509
510	connect_ns $h2 eth0 1.0.2.4/24 - \
511	           $router eth2 1.0.2.1/24 -
512	ip netns exec $h2 ip route add default via 1.0.2.1
513
514	# Prime the network
515	ip netns exec $h1 ping -c5 1.0.2.4 >/dev/null 2>&1
516
517	set +e
518}
519
520run_traceroute()
521{
522	setup_traceroute
523
524	RET=0
525
526	# traceroute host-2 from host-1. Expect a source IP that is on the same
527	# subnet as destination IP of the ICMP error message.
528	run_cmd "$h1" "traceroute -s 1.0.1.3 1.0.2.4 | grep -q 1.0.1.1"
529	check_err $? "traceroute did not return 1.0.1.1"
530	run_cmd "$h1" "traceroute -s 1.0.3.3 1.0.2.4 | grep -q 1.0.3.1"
531	check_err $? "traceroute did not return 1.0.3.1"
532	log_test "IPv4 traceroute"
533
534	cleanup_traceroute
535}
536
537################################################################################
538# traceroute with VRF test
539#
540# Verify that traceroute from H1 to H2 shows 1.0.3.1 and 1.0.1.1 when
541# traceroute uses 1.0.3.3 and 1.0.1.3 as the source IP, respectively. The
542# intention is to check that the kernel does not choose an IP assigned to the
543# VRF device, but rather an address from the VRF port (eth1) that received the
544# packet that generates the ICMP error message.
545#
546#                          1.0.4.1/24 (vrf100)
547#      1.0.3.3/24    1.0.3.1/24
548# ---- 1.0.1.3/24    1.0.1.1/24 ---- 1.0.2.1/24    1.0.2.4/24 ----
549# |H1|--------------------------|R1|--------------------------|H2|
550# ----            N1            ----            N2            ----
551
552cleanup_traceroute_vrf()
553{
554	cleanup_all_ns
555}
556
557setup_traceroute_vrf()
558{
559	# Start clean
560	cleanup_traceroute_vrf
561
562	setup_ns h1 h2 router
563	create_ns "$h1"
564	create_ns "$h2"
565	create_ns "$router"
566
567	ip -n "$router" link add name vrf100 up type vrf table 100
568	ip -n "$router" addr add 1.0.4.1/24 dev vrf100
569
570	connect_ns "$h1" eth0 1.0.1.3/24 - \
571	           "$router" eth1 1.0.1.1/24 -
572
573	ip -n "$h1" addr add 1.0.3.3/24 dev eth0
574	ip -n "$h1" route add default via 1.0.1.1
575
576	ip -n "$router" link set dev eth1 master vrf100
577	ip -n "$router" addr add 1.0.3.1/24 dev eth1
578	ip netns exec "$router" sysctl -qw \
579		net.ipv4.icmp_errors_use_inbound_ifaddr=1
580
581	connect_ns "$h2" eth0 1.0.2.4/24 - \
582	           "$router" eth2 1.0.2.1/24 -
583
584	ip -n "$h2" route add default via 1.0.2.1
585
586	ip -n "$router" link set dev eth2 master vrf100
587
588	# Prime the network
589	ip netns exec "$h1" ping -c5 1.0.2.4 >/dev/null 2>&1
590}
591
592run_traceroute_vrf()
593{
594	setup_traceroute_vrf
595
596	RET=0
597
598	# traceroute host-2 from host-1. Expect a source IP that is on the same
599	# subnet as destination IP of the ICMP error message.
600	run_cmd "$h1" "traceroute -s 1.0.1.3 1.0.2.4 | grep 1.0.1.1"
601	check_err $? "traceroute did not return 1.0.1.1"
602	run_cmd "$h1" "traceroute -s 1.0.3.3 1.0.2.4 | grep 1.0.3.1"
603	check_err $? "traceroute did not return 1.0.3.1"
604	log_test "IPv4 traceroute with VRF"
605
606	cleanup_traceroute_vrf
607}
608
609################################################################################
610# traceroute with ICMP extensions test
611#
612# Verify that in this scenario
613#
614# ----                          ----                          ----
615# |H1|--------------------------|R1|--------------------------|H2|
616# ----            N1            ----            N2            ----
617#
618# ICMP extensions are correctly reported. The loopback interfaces on all the
619# nodes are assigned global addresses and the interfaces connecting the nodes
620# are assigned IPv6 link-local addresses.
621
622cleanup_traceroute_ext()
623{
624	cleanup_all_ns
625}
626
627setup_traceroute_ext()
628{
629	# Start clean
630	cleanup_traceroute_ext
631
632	setup_ns h1 r1 h2
633	create_ns "$h1"
634	create_ns "$r1"
635	create_ns "$h2"
636
637	# Setup N1
638	connect_ns "$h1" eth1 - fe80::1/64 "$r1" eth1 - fe80::2/64
639	# Setup N2
640	connect_ns "$r1" eth2 - fe80::3/64 "$h2" eth2 - fe80::4/64
641
642	# Setup H1
643	ip -n "$h1" address add 192.0.2.1/32 dev lo
644	ip -n "$h1" route add 0.0.0.0/0 nexthop via inet6 fe80::2 dev eth1
645
646	# Setup R1
647	ip -n "$r1" address add 192.0.2.2/32 dev lo
648	ip -n "$r1" route add 192.0.2.1/32 nexthop via inet6 fe80::1 dev eth1
649	ip -n "$r1" route add 192.0.2.3/32 nexthop via inet6 fe80::4 dev eth2
650
651	# Setup H2
652	ip -n "$h2" address add 192.0.2.3/32 dev lo
653	ip -n "$h2" route add 0.0.0.0/0 nexthop via inet6 fe80::3 dev eth2
654
655	# Prime the network
656	ip netns exec "$h1" ping -c5 192.0.2.3 >/dev/null 2>&1
657}
658
659traceroute_ext_iio_iif_test()
660{
661	local r1_ifindex h2_ifindex
662	local pkt_len=$1; shift
663
664	# Test that incoming interface info is not appended by default.
665	run_cmd "$h1" "traceroute -e 192.0.2.3 $pkt_len | grep INC"
666	check_fail $? "Incoming interface info appended by default when should not"
667
668	# Test that the extension is appended when enabled.
669	run_cmd "$r1" "bash -c \"echo 0x01 > /proc/sys/net/ipv4/icmp_errors_extension_mask\""
670	check_err $? "Failed to enable incoming interface info extension on R1"
671
672	run_cmd "$h1" "traceroute -e 192.0.2.3 $pkt_len | grep INC"
673	check_err $? "Incoming interface info not appended after enable"
674
675	# Test that the extension is not appended when disabled.
676	run_cmd "$r1" "bash -c \"echo 0x00 > /proc/sys/net/ipv4/icmp_errors_extension_mask\""
677	check_err $? "Failed to disable incoming interface info extension on R1"
678
679	run_cmd "$h1" "traceroute -e 192.0.2.3 $pkt_len | grep INC"
680	check_fail $? "Incoming interface info appended after disable"
681
682	# Test that the extension is sent correctly from both R1 and H2.
683	run_cmd "$r1" "sysctl -w net.ipv4.icmp_errors_extension_mask=0x01"
684	r1_ifindex=$(ip -n "$r1" -j link show dev eth1 | jq '.[]["ifindex"]')
685	run_cmd "$h1" "traceroute -e 192.0.2.3 $pkt_len | grep '<INC:$r1_ifindex,\"eth1\",mtu=1500>'"
686	check_err $? "Wrong incoming interface info reported from R1"
687
688	run_cmd "$h2" "sysctl -w net.ipv4.icmp_errors_extension_mask=0x01"
689	h2_ifindex=$(ip -n "$h2" -j link show dev eth2 | jq '.[]["ifindex"]')
690	run_cmd "$h1" "traceroute -e 192.0.2.3 $pkt_len | grep '<INC:$h2_ifindex,\"eth2\",mtu=1500>'"
691	check_err $? "Wrong incoming interface info reported from H2"
692
693	# Add a global address on the incoming interface of R1 and check that
694	# it is reported.
695	run_cmd "$r1" "ip address add 198.51.100.1/24 dev eth1"
696	run_cmd "$h1" "traceroute -e 192.0.2.3 $pkt_len | grep '<INC:$r1_ifindex,198.51.100.1,\"eth1\",mtu=1500>'"
697	check_err $? "Wrong incoming interface info reported from R1 after address addition"
698	run_cmd "$r1" "ip address del 198.51.100.1/24 dev eth1"
699
700	# Change name and MTU and make sure the result is still correct.
701	# Re-add the route towards H1 since it was deleted when we removed the
702	# last IPv4 address from eth1 on R1.
703	run_cmd "$r1" "ip route add 192.0.2.1/32 nexthop via inet6 fe80::1 dev eth1"
704	run_cmd "$r1" "ip link set dev eth1 name eth1tag mtu 1501"
705	run_cmd "$h1" "traceroute -e 192.0.2.3 $pkt_len | grep '<INC:$r1_ifindex,\"eth1tag\",mtu=1501>'"
706	check_err $? "Wrong incoming interface info reported from R1 after name and MTU change"
707	run_cmd "$r1" "ip link set dev eth1tag name eth1 mtu 1500"
708
709	run_cmd "$r1" "sysctl -w net.ipv4.icmp_errors_extension_mask=0x00"
710	run_cmd "$h2" "sysctl -w net.ipv4.icmp_errors_extension_mask=0x00"
711}
712
713run_traceroute_ext()
714{
715	# Need at least version 2.1.5 for RFC 5837 support.
716	if ! check_traceroute_version 2.1.5; then
717		log_test_skip "traceroute too old, missing ICMP extensions support"
718		return
719	fi
720
721	setup_traceroute_ext
722
723	RET=0
724
725	## General ICMP extensions tests
726
727	# Test that ICMP extensions are disabled by default.
728	run_cmd "$h1" "sysctl net.ipv4.icmp_errors_extension_mask | grep \"= 0$\""
729	check_err $? "ICMP extensions are not disabled by default"
730
731	# Test that unsupported values are rejected. Do not use "sysctl" as
732	# older versions do not return an error code upon failure.
733	run_cmd "$h1" "bash -c \"echo 0x80 > /proc/sys/net/ipv4/icmp_errors_extension_mask\""
734	check_fail $? "Unsupported sysctl value was not rejected"
735
736	## Extension-specific tests
737
738	# Incoming interface info test. Test with various packet sizes,
739	# including the default one.
740	traceroute_ext_iio_iif_test
741	traceroute_ext_iio_iif_test 127
742	traceroute_ext_iio_iif_test 128
743	traceroute_ext_iio_iif_test 129
744
745	log_test "IPv4 traceroute with ICMP extensions"
746
747	cleanup_traceroute_ext
748}
749
750################################################################################
751# Run tests
752
753run_tests()
754{
755	run_traceroute6
756	run_traceroute6_vrf
757	run_traceroute6_ext
758	run_traceroute
759	run_traceroute_vrf
760	run_traceroute_ext
761}
762
763################################################################################
764# main
765
766while getopts :pv o
767do
768	case $o in
769		p) PAUSE_ON_FAIL=yes;;
770		v) VERBOSE=$(($VERBOSE + 1));;
771		*) exit 1;;
772	esac
773done
774
775require_command traceroute6
776require_command traceroute
777require_command jq
778
779run_tests
780
781exit "${EXIT_STATUS}"
782