1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# This test is for checking IPv4 and IPv6 FIB behavior in response to
5# different events.
6
7ret=0
8# Kselftest framework requirement - SKIP code is 4.
9ksft_skip=4
10
11# all tests in this script. Can be overridden with -t option
12TESTS="unregister down carrier nexthop suppress ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric ipv6_route_metrics ipv4_route_metrics ipv4_route_v6_gw rp_filter ipv4_del_addr ipv4_mangle ipv6_mangle"
13
14VERBOSE=0
15PAUSE_ON_FAIL=no
16PAUSE=no
17IP="ip -netns ns1"
18NS_EXEC="ip netns exec ns1"
19
20which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
21
22log_test()
23{
24	local rc=$1
25	local expected=$2
26	local msg="$3"
27
28	if [ ${rc} -eq ${expected} ]; then
29		printf "    TEST: %-60s  [ OK ]\n" "${msg}"
30		nsuccess=$((nsuccess+1))
31	else
32		ret=1
33		nfail=$((nfail+1))
34		printf "    TEST: %-60s  [FAIL]\n" "${msg}"
35		if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
36		echo
37			echo "hit enter to continue, 'q' to quit"
38			read a
39			[ "$a" = "q" ] && exit 1
40		fi
41	fi
42
43	if [ "${PAUSE}" = "yes" ]; then
44		echo
45		echo "hit enter to continue, 'q' to quit"
46		read a
47		[ "$a" = "q" ] && exit 1
48	fi
49}
50
51setup()
52{
53	set -e
54	ip netns add ns1
55	ip netns set ns1 auto
56	$IP link set dev lo up
57	ip netns exec ns1 sysctl -qw net.ipv4.ip_forward=1
58	ip netns exec ns1 sysctl -qw net.ipv6.conf.all.forwarding=1
59
60	$IP link add dummy0 type dummy
61	$IP link set dev dummy0 up
62	$IP address add 198.51.100.1/24 dev dummy0
63	$IP -6 address add 2001:db8:1::1/64 dev dummy0
64	set +e
65
66}
67
68cleanup()
69{
70	$IP link del dev dummy0 &> /dev/null
71	ip netns del ns1
72	ip netns del ns2 &> /dev/null
73}
74
75get_linklocal()
76{
77	local dev=$1
78	local addr
79
80	addr=$($IP -6 -br addr show dev ${dev} | \
81	awk '{
82		for (i = 3; i <= NF; ++i) {
83			if ($i ~ /^fe80/)
84				print $i
85		}
86	}'
87	)
88	addr=${addr/\/*}
89
90	[ -z "$addr" ] && return 1
91
92	echo $addr
93
94	return 0
95}
96
97fib_unreg_unicast_test()
98{
99	echo
100	echo "Single path route test"
101
102	setup
103
104	echo "    Start point"
105	$IP route get fibmatch 198.51.100.2 &> /dev/null
106	log_test $? 0 "IPv4 fibmatch"
107	$IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
108	log_test $? 0 "IPv6 fibmatch"
109
110	set -e
111	$IP link del dev dummy0
112	set +e
113
114	echo "    Nexthop device deleted"
115	$IP route get fibmatch 198.51.100.2 &> /dev/null
116	log_test $? 2 "IPv4 fibmatch - no route"
117	$IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
118	log_test $? 2 "IPv6 fibmatch - no route"
119
120	cleanup
121}
122
123fib_unreg_multipath_test()
124{
125
126	echo
127	echo "Multipath route test"
128
129	setup
130
131	set -e
132	$IP link add dummy1 type dummy
133	$IP link set dev dummy1 up
134	$IP address add 192.0.2.1/24 dev dummy1
135	$IP -6 address add 2001:db8:2::1/64 dev dummy1
136
137	$IP route add 203.0.113.0/24 \
138		nexthop via 198.51.100.2 dev dummy0 \
139		nexthop via 192.0.2.2 dev dummy1
140	$IP -6 route add 2001:db8:3::/64 \
141		nexthop via 2001:db8:1::2 dev dummy0 \
142		nexthop via 2001:db8:2::2 dev dummy1
143	set +e
144
145	echo "    Start point"
146	$IP route get fibmatch 203.0.113.1 &> /dev/null
147	log_test $? 0 "IPv4 fibmatch"
148	$IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
149	log_test $? 0 "IPv6 fibmatch"
150
151	set -e
152	$IP link del dev dummy0
153	set +e
154
155	echo "    One nexthop device deleted"
156	$IP route get fibmatch 203.0.113.1 &> /dev/null
157	log_test $? 2 "IPv4 - multipath route removed on delete"
158
159	$IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
160	# In IPv6 we do not flush the entire multipath route.
161	log_test $? 0 "IPv6 - multipath down to single path"
162
163	set -e
164	$IP link del dev dummy1
165	set +e
166
167	echo "    Second nexthop device deleted"
168	$IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
169	log_test $? 2 "IPv6 - no route"
170
171	cleanup
172}
173
174fib_unreg_test()
175{
176	fib_unreg_unicast_test
177	fib_unreg_multipath_test
178}
179
180fib_down_unicast_test()
181{
182	echo
183	echo "Single path, admin down"
184
185	setup
186
187	echo "    Start point"
188	$IP route get fibmatch 198.51.100.2 &> /dev/null
189	log_test $? 0 "IPv4 fibmatch"
190	$IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
191	log_test $? 0 "IPv6 fibmatch"
192
193	set -e
194	$IP link set dev dummy0 down
195	set +e
196
197	echo "    Route deleted on down"
198	$IP route get fibmatch 198.51.100.2 &> /dev/null
199	log_test $? 2 "IPv4 fibmatch"
200	$IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
201	log_test $? 2 "IPv6 fibmatch"
202
203	cleanup
204}
205
206fib_down_multipath_test_do()
207{
208	local down_dev=$1
209	local up_dev=$2
210
211	$IP route get fibmatch 203.0.113.1 \
212		oif $down_dev &> /dev/null
213	log_test $? 2 "IPv4 fibmatch on down device"
214	$IP -6 route get fibmatch 2001:db8:3::1 \
215		oif $down_dev &> /dev/null
216	log_test $? 2 "IPv6 fibmatch on down device"
217
218	$IP route get fibmatch 203.0.113.1 \
219		oif $up_dev &> /dev/null
220	log_test $? 0 "IPv4 fibmatch on up device"
221	$IP -6 route get fibmatch 2001:db8:3::1 \
222		oif $up_dev &> /dev/null
223	log_test $? 0 "IPv6 fibmatch on up device"
224
225	$IP route get fibmatch 203.0.113.1 | \
226		grep $down_dev | grep -q "dead linkdown"
227	log_test $? 0 "IPv4 flags on down device"
228	$IP -6 route get fibmatch 2001:db8:3::1 | \
229		grep $down_dev | grep -q "dead linkdown"
230	log_test $? 0 "IPv6 flags on down device"
231
232	$IP route get fibmatch 203.0.113.1 | \
233		grep $up_dev | grep -q "dead linkdown"
234	log_test $? 1 "IPv4 flags on up device"
235	$IP -6 route get fibmatch 2001:db8:3::1 | \
236		grep $up_dev | grep -q "dead linkdown"
237	log_test $? 1 "IPv6 flags on up device"
238}
239
240fib_down_multipath_test()
241{
242	echo
243	echo "Admin down multipath"
244
245	setup
246
247	set -e
248	$IP link add dummy1 type dummy
249	$IP link set dev dummy1 up
250
251	$IP address add 192.0.2.1/24 dev dummy1
252	$IP -6 address add 2001:db8:2::1/64 dev dummy1
253
254	$IP route add 203.0.113.0/24 \
255		nexthop via 198.51.100.2 dev dummy0 \
256		nexthop via 192.0.2.2 dev dummy1
257	$IP -6 route add 2001:db8:3::/64 \
258		nexthop via 2001:db8:1::2 dev dummy0 \
259		nexthop via 2001:db8:2::2 dev dummy1
260	set +e
261
262	echo "    Verify start point"
263	$IP route get fibmatch 203.0.113.1 &> /dev/null
264	log_test $? 0 "IPv4 fibmatch"
265
266	$IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
267	log_test $? 0 "IPv6 fibmatch"
268
269	set -e
270	$IP link set dev dummy0 down
271	set +e
272
273	echo "    One device down, one up"
274	fib_down_multipath_test_do "dummy0" "dummy1"
275
276	set -e
277	$IP link set dev dummy0 up
278	$IP link set dev dummy1 down
279	set +e
280
281	echo "    Other device down and up"
282	fib_down_multipath_test_do "dummy1" "dummy0"
283
284	set -e
285	$IP link set dev dummy0 down
286	set +e
287
288	echo "    Both devices down"
289	$IP route get fibmatch 203.0.113.1 &> /dev/null
290	log_test $? 2 "IPv4 fibmatch"
291	$IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
292	log_test $? 2 "IPv6 fibmatch"
293
294	$IP link del dev dummy1
295	cleanup
296}
297
298fib_down_test()
299{
300	fib_down_unicast_test
301	fib_down_multipath_test
302}
303
304# Local routes should not be affected when carrier changes.
305fib_carrier_local_test()
306{
307	echo
308	echo "Local carrier tests - single path"
309
310	setup
311
312	set -e
313	$IP link set dev dummy0 carrier on
314	set +e
315
316	echo "    Start point"
317	$IP route get fibmatch 198.51.100.1 &> /dev/null
318	log_test $? 0 "IPv4 fibmatch"
319	$IP -6 route get fibmatch 2001:db8:1::1 &> /dev/null
320	log_test $? 0 "IPv6 fibmatch"
321
322	$IP route get fibmatch 198.51.100.1 | \
323		grep -q "linkdown"
324	log_test $? 1 "IPv4 - no linkdown flag"
325	$IP -6 route get fibmatch 2001:db8:1::1 | \
326		grep -q "linkdown"
327	log_test $? 1 "IPv6 - no linkdown flag"
328
329	set -e
330	$IP link set dev dummy0 carrier off
331	sleep 1
332	set +e
333
334	echo "    Carrier off on nexthop"
335	$IP route get fibmatch 198.51.100.1 &> /dev/null
336	log_test $? 0 "IPv4 fibmatch"
337	$IP -6 route get fibmatch 2001:db8:1::1 &> /dev/null
338	log_test $? 0 "IPv6 fibmatch"
339
340	$IP route get fibmatch 198.51.100.1 | \
341		grep -q "linkdown"
342	log_test $? 1 "IPv4 - linkdown flag set"
343	$IP -6 route get fibmatch 2001:db8:1::1 | \
344		grep -q "linkdown"
345	log_test $? 1 "IPv6 - linkdown flag set"
346
347	set -e
348	$IP address add 192.0.2.1/24 dev dummy0
349	$IP -6 address add 2001:db8:2::1/64 dev dummy0
350	set +e
351
352	echo "    Route to local address with carrier down"
353	$IP route get fibmatch 192.0.2.1 &> /dev/null
354	log_test $? 0 "IPv4 fibmatch"
355	$IP -6 route get fibmatch 2001:db8:2::1 &> /dev/null
356	log_test $? 0 "IPv6 fibmatch"
357
358	$IP route get fibmatch 192.0.2.1 | \
359		grep -q "linkdown"
360	log_test $? 1 "IPv4 linkdown flag set"
361	$IP -6 route get fibmatch 2001:db8:2::1 | \
362		grep -q "linkdown"
363	log_test $? 1 "IPv6 linkdown flag set"
364
365	cleanup
366}
367
368fib_carrier_unicast_test()
369{
370	ret=0
371
372	echo
373	echo "Single path route carrier test"
374
375	setup
376
377	set -e
378	$IP link set dev dummy0 carrier on
379	set +e
380
381	echo "    Start point"
382	$IP route get fibmatch 198.51.100.2 &> /dev/null
383	log_test $? 0 "IPv4 fibmatch"
384	$IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
385	log_test $? 0 "IPv6 fibmatch"
386
387	$IP route get fibmatch 198.51.100.2 | \
388		grep -q "linkdown"
389	log_test $? 1 "IPv4 no linkdown flag"
390	$IP -6 route get fibmatch 2001:db8:1::2 | \
391		grep -q "linkdown"
392	log_test $? 1 "IPv6 no linkdown flag"
393
394	set -e
395	$IP link set dev dummy0 carrier off
396	sleep 1
397	set +e
398
399	echo "    Carrier down"
400	$IP route get fibmatch 198.51.100.2 &> /dev/null
401	log_test $? 0 "IPv4 fibmatch"
402	$IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
403	log_test $? 0 "IPv6 fibmatch"
404
405	$IP route get fibmatch 198.51.100.2 | \
406		grep -q "linkdown"
407	log_test $? 0 "IPv4 linkdown flag set"
408	$IP -6 route get fibmatch 2001:db8:1::2 | \
409		grep -q "linkdown"
410	log_test $? 0 "IPv6 linkdown flag set"
411
412	set -e
413	$IP address add 192.0.2.1/24 dev dummy0
414	$IP -6 address add 2001:db8:2::1/64 dev dummy0
415	set +e
416
417	echo "    Second address added with carrier down"
418	$IP route get fibmatch 192.0.2.2 &> /dev/null
419	log_test $? 0 "IPv4 fibmatch"
420	$IP -6 route get fibmatch 2001:db8:2::2 &> /dev/null
421	log_test $? 0 "IPv6 fibmatch"
422
423	$IP route get fibmatch 192.0.2.2 | \
424		grep -q "linkdown"
425	log_test $? 0 "IPv4 linkdown flag set"
426	$IP -6 route get fibmatch 2001:db8:2::2 | \
427		grep -q "linkdown"
428	log_test $? 0 "IPv6 linkdown flag set"
429
430	cleanup
431}
432
433fib_carrier_test()
434{
435	fib_carrier_local_test
436	fib_carrier_unicast_test
437}
438
439fib_rp_filter_test()
440{
441	echo
442	echo "IPv4 rp_filter tests"
443
444	setup
445
446	set -e
447	ip netns add ns2
448	ip netns set ns2 auto
449
450	ip -netns ns2 link set dev lo up
451
452	$IP link add name veth1 type veth peer name veth2
453	$IP link set dev veth2 netns ns2
454	$IP address add 192.0.2.1/24 dev veth1
455	ip -netns ns2 address add 192.0.2.1/24 dev veth2
456	$IP link set dev veth1 up
457	ip -netns ns2 link set dev veth2 up
458
459	$IP link set dev lo address 52:54:00:6a:c7:5e
460	$IP link set dev veth1 address 52:54:00:6a:c7:5e
461	ip -netns ns2 link set dev lo address 52:54:00:6a:c7:5e
462	ip -netns ns2 link set dev veth2 address 52:54:00:6a:c7:5e
463
464	# 1. (ns2) redirect lo's egress to veth2's egress
465	ip netns exec ns2 tc qdisc add dev lo parent root handle 1: fq_codel
466	ip netns exec ns2 tc filter add dev lo parent 1: protocol arp basic \
467		action mirred egress redirect dev veth2
468	ip netns exec ns2 tc filter add dev lo parent 1: protocol ip basic \
469		action mirred egress redirect dev veth2
470
471	# 2. (ns1) redirect veth1's ingress to lo's ingress
472	$NS_EXEC tc qdisc add dev veth1 ingress
473	$NS_EXEC tc filter add dev veth1 ingress protocol arp basic \
474		action mirred ingress redirect dev lo
475	$NS_EXEC tc filter add dev veth1 ingress protocol ip basic \
476		action mirred ingress redirect dev lo
477
478	# 3. (ns1) redirect lo's egress to veth1's egress
479	$NS_EXEC tc qdisc add dev lo parent root handle 1: fq_codel
480	$NS_EXEC tc filter add dev lo parent 1: protocol arp basic \
481		action mirred egress redirect dev veth1
482	$NS_EXEC tc filter add dev lo parent 1: protocol ip basic \
483		action mirred egress redirect dev veth1
484
485	# 4. (ns2) redirect veth2's ingress to lo's ingress
486	ip netns exec ns2 tc qdisc add dev veth2 ingress
487	ip netns exec ns2 tc filter add dev veth2 ingress protocol arp basic \
488		action mirred ingress redirect dev lo
489	ip netns exec ns2 tc filter add dev veth2 ingress protocol ip basic \
490		action mirred ingress redirect dev lo
491
492	$NS_EXEC sysctl -qw net.ipv4.conf.all.rp_filter=1
493	$NS_EXEC sysctl -qw net.ipv4.conf.all.accept_local=1
494	$NS_EXEC sysctl -qw net.ipv4.conf.all.route_localnet=1
495	ip netns exec ns2 sysctl -qw net.ipv4.conf.all.rp_filter=1
496	ip netns exec ns2 sysctl -qw net.ipv4.conf.all.accept_local=1
497	ip netns exec ns2 sysctl -qw net.ipv4.conf.all.route_localnet=1
498	set +e
499
500	run_cmd "ip netns exec ns2 ping -w1 -c1 192.0.2.1"
501	log_test $? 0 "rp_filter passes local packets"
502
503	run_cmd "ip netns exec ns2 ping -w1 -c1 127.0.0.1"
504	log_test $? 0 "rp_filter passes loopback packets"
505
506	cleanup
507}
508
509################################################################################
510# Tests on nexthop spec
511
512# run 'ip route add' with given spec
513add_rt()
514{
515	local desc="$1"
516	local erc=$2
517	local vrf=$3
518	local pfx=$4
519	local gw=$5
520	local dev=$6
521	local cmd out rc
522
523	[ "$vrf" = "-" ] && vrf="default"
524	[ -n "$gw" ] && gw="via $gw"
525	[ -n "$dev" ] && dev="dev $dev"
526
527	cmd="$IP route add vrf $vrf $pfx $gw $dev"
528	if [ "$VERBOSE" = "1" ]; then
529		printf "\n    COMMAND: $cmd\n"
530	fi
531
532	out=$(eval $cmd 2>&1)
533	rc=$?
534	if [ "$VERBOSE" = "1" -a -n "$out" ]; then
535		echo "    $out"
536	fi
537	log_test $rc $erc "$desc"
538}
539
540fib4_nexthop()
541{
542	echo
543	echo "IPv4 nexthop tests"
544
545	echo "<<< write me >>>"
546}
547
548fib6_nexthop()
549{
550	local lldummy=$(get_linklocal dummy0)
551	local llv1=$(get_linklocal dummy0)
552
553	if [ -z "$lldummy" ]; then
554		echo "Failed to get linklocal address for dummy0"
555		return 1
556	fi
557	if [ -z "$llv1" ]; then
558		echo "Failed to get linklocal address for veth1"
559		return 1
560	fi
561
562	echo
563	echo "IPv6 nexthop tests"
564
565	add_rt "Directly connected nexthop, unicast address" 0 \
566		- 2001:db8:101::/64 2001:db8:1::2
567	add_rt "Directly connected nexthop, unicast address with device" 0 \
568		- 2001:db8:102::/64 2001:db8:1::2 "dummy0"
569	add_rt "Gateway is linklocal address" 0 \
570		- 2001:db8:103::1/64 $llv1 "veth0"
571
572	# fails because LL address requires a device
573	add_rt "Gateway is linklocal address, no device" 2 \
574		- 2001:db8:104::1/64 $llv1
575
576	# local address can not be a gateway
577	add_rt "Gateway can not be local unicast address" 2 \
578		- 2001:db8:105::/64 2001:db8:1::1
579	add_rt "Gateway can not be local unicast address, with device" 2 \
580		- 2001:db8:106::/64 2001:db8:1::1 "dummy0"
581	add_rt "Gateway can not be a local linklocal address" 2 \
582		- 2001:db8:107::1/64 $lldummy "dummy0"
583
584	# VRF tests
585	add_rt "Gateway can be local address in a VRF" 0 \
586		- 2001:db8:108::/64 2001:db8:51::2
587	add_rt "Gateway can be local address in a VRF, with device" 0 \
588		- 2001:db8:109::/64 2001:db8:51::2 "veth0"
589	add_rt "Gateway can be local linklocal address in a VRF" 0 \
590		- 2001:db8:110::1/64 $llv1 "veth0"
591
592	add_rt "Redirect to VRF lookup" 0 \
593		- 2001:db8:111::/64 "" "red"
594
595	add_rt "VRF route, gateway can be local address in default VRF" 0 \
596		red 2001:db8:112::/64 2001:db8:51::1
597
598	# local address in same VRF fails
599	add_rt "VRF route, gateway can not be a local address" 2 \
600		red 2001:db8:113::1/64 2001:db8:2::1
601	add_rt "VRF route, gateway can not be a local addr with device" 2 \
602		red 2001:db8:114::1/64 2001:db8:2::1 "dummy1"
603}
604
605# Default VRF:
606#   dummy0 - 198.51.100.1/24 2001:db8:1::1/64
607#   veth0  - 192.0.2.1/24    2001:db8:51::1/64
608#
609# VRF red:
610#   dummy1 - 192.168.2.1/24 2001:db8:2::1/64
611#   veth1  - 192.0.2.2/24   2001:db8:51::2/64
612#
613#  [ dummy0   veth0 ]--[ veth1   dummy1 ]
614
615fib_nexthop_test()
616{
617	setup
618
619	set -e
620
621	$IP -4 rule add pref 32765 table local
622	$IP -4 rule del pref 0
623	$IP -6 rule add pref 32765 table local
624	$IP -6 rule del pref 0
625
626	$IP link add red type vrf table 1
627	$IP link set red up
628	$IP -4 route add vrf red unreachable default metric 4278198272
629	$IP -6 route add vrf red unreachable default metric 4278198272
630
631	$IP link add veth0 type veth peer name veth1
632	$IP link set dev veth0 up
633	$IP address add 192.0.2.1/24 dev veth0
634	$IP -6 address add 2001:db8:51::1/64 dev veth0
635
636	$IP link set dev veth1 vrf red up
637	$IP address add 192.0.2.2/24 dev veth1
638	$IP -6 address add 2001:db8:51::2/64 dev veth1
639
640	$IP link add dummy1 type dummy
641	$IP link set dev dummy1 vrf red up
642	$IP address add 192.168.2.1/24 dev dummy1
643	$IP -6 address add 2001:db8:2::1/64 dev dummy1
644	set +e
645
646	sleep 1
647	fib4_nexthop
648	fib6_nexthop
649
650	(
651	$IP link del dev dummy1
652	$IP link del veth0
653	$IP link del red
654	) 2>/dev/null
655	cleanup
656}
657
658fib_suppress_test()
659{
660	echo
661	echo "FIB rule with suppress_prefixlength"
662	setup
663
664	$IP link add dummy1 type dummy
665	$IP link set dummy1 up
666	$IP -6 route add default dev dummy1
667	$IP -6 rule add table main suppress_prefixlength 0
668	ping -f -c 1000 -W 1 1234::1 >/dev/null 2>&1
669	$IP -6 rule del table main suppress_prefixlength 0
670	$IP link del dummy1
671
672	# If we got here without crashing, we're good.
673	log_test 0 0 "FIB rule suppress test"
674
675	cleanup
676}
677
678################################################################################
679# Tests on route add and replace
680
681run_cmd()
682{
683	local cmd="$1"
684	local out
685	local stderr="2>/dev/null"
686
687	if [ "$VERBOSE" = "1" ]; then
688		printf "    COMMAND: $cmd\n"
689		stderr=
690	fi
691
692	out=$(eval $cmd $stderr)
693	rc=$?
694	if [ "$VERBOSE" = "1" -a -n "$out" ]; then
695		echo "    $out"
696	fi
697
698	[ "$VERBOSE" = "1" ] && echo
699
700	return $rc
701}
702
703check_expected()
704{
705	local out="$1"
706	local expected="$2"
707	local rc=0
708
709	[ "${out}" = "${expected}" ] && return 0
710
711	if [ -z "${out}" ]; then
712		if [ "$VERBOSE" = "1" ]; then
713			printf "\nNo route entry found\n"
714			printf "Expected:\n"
715			printf "    ${expected}\n"
716		fi
717		return 1
718	fi
719
720	# tricky way to convert output to 1-line without ip's
721	# messy '\'; this drops all extra white space
722	out=$(echo ${out})
723	if [ "${out}" != "${expected}" ]; then
724		rc=1
725		if [ "${VERBOSE}" = "1" ]; then
726			printf "    Unexpected route entry. Have:\n"
727			printf "        ${out}\n"
728			printf "    Expected:\n"
729			printf "        ${expected}\n\n"
730		fi
731	fi
732
733	return $rc
734}
735
736# add route for a prefix, flushing any existing routes first
737# expected to be the first step of a test
738add_route6()
739{
740	local pfx="$1"
741	local nh="$2"
742	local out
743
744	if [ "$VERBOSE" = "1" ]; then
745		echo
746		echo "    ##################################################"
747		echo
748	fi
749
750	run_cmd "$IP -6 ro flush ${pfx}"
751	[ $? -ne 0 ] && exit 1
752
753	out=$($IP -6 ro ls match ${pfx})
754	if [ -n "$out" ]; then
755		echo "Failed to flush routes for prefix used for tests."
756		exit 1
757	fi
758
759	run_cmd "$IP -6 ro add ${pfx} ${nh}"
760	if [ $? -ne 0 ]; then
761		echo "Failed to add initial route for test."
762		exit 1
763	fi
764}
765
766# add initial route - used in replace route tests
767add_initial_route6()
768{
769	add_route6 "2001:db8:104::/64" "$1"
770}
771
772check_route6()
773{
774	local pfx
775	local expected="$1"
776	local out
777	local rc=0
778
779	set -- $expected
780	pfx=$1
781
782	out=$($IP -6 ro ls match ${pfx} | sed -e 's/ pref medium//')
783	check_expected "${out}" "${expected}"
784}
785
786route_cleanup()
787{
788	$IP li del red 2>/dev/null
789	$IP li del dummy1 2>/dev/null
790	$IP li del veth1 2>/dev/null
791	$IP li del veth3 2>/dev/null
792
793	cleanup &> /dev/null
794}
795
796route_setup()
797{
798	route_cleanup
799	setup
800
801	[ "${VERBOSE}" = "1" ] && set -x
802	set -e
803
804	ip netns add ns2
805	ip netns set ns2 auto
806	ip -netns ns2 link set dev lo up
807	ip netns exec ns2 sysctl -qw net.ipv4.ip_forward=1
808	ip netns exec ns2 sysctl -qw net.ipv6.conf.all.forwarding=1
809
810	$IP li add veth1 type veth peer name veth2
811	$IP li add veth3 type veth peer name veth4
812
813	$IP li set veth1 up
814	$IP li set veth3 up
815	$IP li set veth2 netns ns2 up
816	$IP li set veth4 netns ns2 up
817	ip -netns ns2 li add dummy1 type dummy
818	ip -netns ns2 li set dummy1 up
819
820	$IP -6 addr add 2001:db8:101::1/64 dev veth1 nodad
821	$IP -6 addr add 2001:db8:103::1/64 dev veth3 nodad
822	$IP addr add 172.16.101.1/24 dev veth1
823	$IP addr add 172.16.103.1/24 dev veth3
824
825	ip -netns ns2 -6 addr add 2001:db8:101::2/64 dev veth2 nodad
826	ip -netns ns2 -6 addr add 2001:db8:103::2/64 dev veth4 nodad
827	ip -netns ns2 -6 addr add 2001:db8:104::1/64 dev dummy1 nodad
828
829	ip -netns ns2 addr add 172.16.101.2/24 dev veth2
830	ip -netns ns2 addr add 172.16.103.2/24 dev veth4
831	ip -netns ns2 addr add 172.16.104.1/24 dev dummy1
832
833	set +e
834}
835
836# assumption is that basic add of a single path route works
837# otherwise just adding an address on an interface is broken
838ipv6_rt_add()
839{
840	local rc
841
842	echo
843	echo "IPv6 route add / append tests"
844
845	# route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
846	add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
847	run_cmd "$IP -6 ro add 2001:db8:104::/64 via 2001:db8:103::2"
848	log_test $? 2 "Attempt to add duplicate route - gw"
849
850	# route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
851	add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
852	run_cmd "$IP -6 ro add 2001:db8:104::/64 dev veth3"
853	log_test $? 2 "Attempt to add duplicate route - dev only"
854
855	# route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
856	add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
857	run_cmd "$IP -6 ro add unreachable 2001:db8:104::/64"
858	log_test $? 2 "Attempt to add duplicate route - reject route"
859
860	# route append with same prefix adds a new route
861	# - iproute2 sets NLM_F_CREATE | NLM_F_APPEND
862	add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
863	run_cmd "$IP -6 ro append 2001:db8:104::/64 via 2001:db8:103::2"
864	check_route6 "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
865	log_test $? 0 "Append nexthop to existing route - gw"
866
867	# insert mpath directly
868	add_route6 "2001:db8:104::/64" "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
869	check_route6  "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
870	log_test $? 0 "Add multipath route"
871
872	add_route6 "2001:db8:104::/64" "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
873	run_cmd "$IP -6 ro add 2001:db8:104::/64 nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
874	log_test $? 2 "Attempt to add duplicate multipath route"
875
876	# insert of a second route without append but different metric
877	add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
878	run_cmd "$IP -6 ro add 2001:db8:104::/64 via 2001:db8:103::2 metric 512"
879	rc=$?
880	if [ $rc -eq 0 ]; then
881		run_cmd "$IP -6 ro add 2001:db8:104::/64 via 2001:db8:103::3 metric 256"
882		rc=$?
883	fi
884	log_test $rc 0 "Route add with different metrics"
885
886	run_cmd "$IP -6 ro del 2001:db8:104::/64 metric 512"
887	rc=$?
888	if [ $rc -eq 0 ]; then
889		check_route6 "2001:db8:104::/64 via 2001:db8:103::3 dev veth3 metric 256 2001:db8:104::/64 via 2001:db8:101::2 dev veth1 metric 1024"
890		rc=$?
891	fi
892	log_test $rc 0 "Route delete with metric"
893}
894
895ipv6_rt_replace_single()
896{
897	# single path with single path
898	#
899	add_initial_route6 "via 2001:db8:101::2"
900	run_cmd "$IP -6 ro replace 2001:db8:104::/64 via 2001:db8:103::2"
901	check_route6 "2001:db8:104::/64 via 2001:db8:103::2 dev veth3 metric 1024"
902	log_test $? 0 "Single path with single path"
903
904	# single path with multipath
905	#
906	add_initial_route6 "nexthop via 2001:db8:101::2"
907	run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:103::2"
908	check_route6 "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::3 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
909	log_test $? 0 "Single path with multipath"
910
911	# single path with single path using MULTIPATH attribute
912	#
913	add_initial_route6 "via 2001:db8:101::2"
914	run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:103::2"
915	check_route6 "2001:db8:104::/64 via 2001:db8:103::2 dev veth3 metric 1024"
916	log_test $? 0 "Single path with single path via multipath attribute"
917
918	# route replace fails - invalid nexthop
919	add_initial_route6 "via 2001:db8:101::2"
920	run_cmd "$IP -6 ro replace 2001:db8:104::/64 via 2001:db8:104::2"
921	if [ $? -eq 0 ]; then
922		# previous command is expected to fail so if it returns 0
923		# that means the test failed.
924		log_test 0 1 "Invalid nexthop"
925	else
926		check_route6 "2001:db8:104::/64 via 2001:db8:101::2 dev veth1 metric 1024"
927		log_test $? 0 "Invalid nexthop"
928	fi
929
930	# replace non-existent route
931	# - note use of change versus replace since ip adds NLM_F_CREATE
932	#   for replace
933	add_initial_route6 "via 2001:db8:101::2"
934	run_cmd "$IP -6 ro change 2001:db8:105::/64 via 2001:db8:101::2"
935	log_test $? 2 "Single path - replace of non-existent route"
936}
937
938ipv6_rt_replace_mpath()
939{
940	# multipath with multipath
941	add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
942	run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:103::3"
943	check_route6  "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::3 dev veth1 weight 1 nexthop via 2001:db8:103::3 dev veth3 weight 1"
944	log_test $? 0 "Multipath with multipath"
945
946	# multipath with single
947	add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
948	run_cmd "$IP -6 ro replace 2001:db8:104::/64 via 2001:db8:101::3"
949	check_route6  "2001:db8:104::/64 via 2001:db8:101::3 dev veth1 metric 1024"
950	log_test $? 0 "Multipath with single path"
951
952	# multipath with single
953	add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
954	run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3"
955	check_route6 "2001:db8:104::/64 via 2001:db8:101::3 dev veth1 metric 1024"
956	log_test $? 0 "Multipath with single path via multipath attribute"
957
958	# multipath with dev-only
959	add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
960	run_cmd "$IP -6 ro replace 2001:db8:104::/64 dev veth1"
961	check_route6 "2001:db8:104::/64 dev veth1 metric 1024"
962	log_test $? 0 "Multipath with dev-only"
963
964	# route replace fails - invalid nexthop 1
965	add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
966	run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:111::3 nexthop via 2001:db8:103::3"
967	check_route6  "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
968	log_test $? 0 "Multipath - invalid first nexthop"
969
970	# route replace fails - invalid nexthop 2
971	add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
972	run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:113::3"
973	check_route6  "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
974	log_test $? 0 "Multipath - invalid second nexthop"
975
976	# multipath non-existent route
977	add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
978	run_cmd "$IP -6 ro change 2001:db8:105::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:103::3"
979	log_test $? 2 "Multipath - replace of non-existent route"
980}
981
982ipv6_rt_replace()
983{
984	echo
985	echo "IPv6 route replace tests"
986
987	ipv6_rt_replace_single
988	ipv6_rt_replace_mpath
989}
990
991ipv6_route_test()
992{
993	route_setup
994
995	ipv6_rt_add
996	ipv6_rt_replace
997
998	route_cleanup
999}
1000
1001ip_addr_metric_check()
1002{
1003	ip addr help 2>&1 | grep -q metric
1004	if [ $? -ne 0 ]; then
1005		echo "iproute2 command does not support metric for addresses. Skipping test"
1006		return 1
1007	fi
1008
1009	return 0
1010}
1011
1012ipv6_addr_metric_test()
1013{
1014	local rc
1015
1016	echo
1017	echo "IPv6 prefix route tests"
1018
1019	ip_addr_metric_check || return 1
1020
1021	setup
1022
1023	set -e
1024	$IP li add dummy1 type dummy
1025	$IP li add dummy2 type dummy
1026	$IP li set dummy1 up
1027	$IP li set dummy2 up
1028
1029	# default entry is metric 256
1030	run_cmd "$IP -6 addr add dev dummy1 2001:db8:104::1/64"
1031	run_cmd "$IP -6 addr add dev dummy2 2001:db8:104::2/64"
1032	set +e
1033
1034	check_route6 "2001:db8:104::/64 dev dummy1 proto kernel metric 256 2001:db8:104::/64 dev dummy2 proto kernel metric 256"
1035	log_test $? 0 "Default metric"
1036
1037	set -e
1038	run_cmd "$IP -6 addr flush dev dummy1"
1039	run_cmd "$IP -6 addr add dev dummy1 2001:db8:104::1/64 metric 257"
1040	set +e
1041
1042	check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 256 2001:db8:104::/64 dev dummy1 proto kernel metric 257"
1043	log_test $? 0 "User specified metric on first device"
1044
1045	set -e
1046	run_cmd "$IP -6 addr flush dev dummy2"
1047	run_cmd "$IP -6 addr add dev dummy2 2001:db8:104::2/64 metric 258"
1048	set +e
1049
1050	check_route6 "2001:db8:104::/64 dev dummy1 proto kernel metric 257 2001:db8:104::/64 dev dummy2 proto kernel metric 258"
1051	log_test $? 0 "User specified metric on second device"
1052
1053	run_cmd "$IP -6 addr del dev dummy1 2001:db8:104::1/64 metric 257"
1054	rc=$?
1055	if [ $rc -eq 0 ]; then
1056		check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 258"
1057		rc=$?
1058	fi
1059	log_test $rc 0 "Delete of address on first device"
1060
1061	run_cmd "$IP -6 addr change dev dummy2 2001:db8:104::2/64 metric 259"
1062	rc=$?
1063	if [ $rc -eq 0 ]; then
1064		check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 259"
1065		rc=$?
1066	fi
1067	log_test $rc 0 "Modify metric of address"
1068
1069	# verify prefix route removed on down
1070	run_cmd "ip netns exec ns1 sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1"
1071	run_cmd "$IP li set dev dummy2 down"
1072	rc=$?
1073	if [ $rc -eq 0 ]; then
1074		out=$($IP -6 ro ls match 2001:db8:104::/64)
1075		check_expected "${out}" ""
1076		rc=$?
1077	fi
1078	log_test $rc 0 "Prefix route removed on link down"
1079
1080	# verify prefix route re-inserted with assigned metric
1081	run_cmd "$IP li set dev dummy2 up"
1082	rc=$?
1083	if [ $rc -eq 0 ]; then
1084		check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 259"
1085		rc=$?
1086	fi
1087	log_test $rc 0 "Prefix route with metric on link up"
1088
1089	# verify peer metric added correctly
1090	set -e
1091	run_cmd "$IP -6 addr flush dev dummy2"
1092	run_cmd "$IP -6 addr add dev dummy2 2001:db8:104::1 peer 2001:db8:104::2 metric 260"
1093	set +e
1094
1095	check_route6 "2001:db8:104::1 dev dummy2 proto kernel metric 260"
1096	log_test $? 0 "Set metric with peer route on local side"
1097	check_route6 "2001:db8:104::2 dev dummy2 proto kernel metric 260"
1098	log_test $? 0 "Set metric with peer route on peer side"
1099
1100	set -e
1101	run_cmd "$IP -6 addr change dev dummy2 2001:db8:104::1 peer 2001:db8:104::3 metric 261"
1102	set +e
1103
1104	check_route6 "2001:db8:104::1 dev dummy2 proto kernel metric 261"
1105	log_test $? 0 "Modify metric and peer address on local side"
1106	check_route6 "2001:db8:104::3 dev dummy2 proto kernel metric 261"
1107	log_test $? 0 "Modify metric and peer address on peer side"
1108
1109	$IP li del dummy1
1110	$IP li del dummy2
1111	cleanup
1112}
1113
1114ipv6_route_metrics_test()
1115{
1116	local rc
1117
1118	echo
1119	echo "IPv6 routes with metrics"
1120
1121	route_setup
1122
1123	#
1124	# single path with metrics
1125	#
1126	run_cmd "$IP -6 ro add 2001:db8:111::/64 via 2001:db8:101::2 mtu 1400"
1127	rc=$?
1128	if [ $rc -eq 0 ]; then
1129		check_route6  "2001:db8:111::/64 via 2001:db8:101::2 dev veth1 metric 1024 mtu 1400"
1130		rc=$?
1131	fi
1132	log_test $rc 0 "Single path route with mtu metric"
1133
1134
1135	#
1136	# multipath via separate routes with metrics
1137	#
1138	run_cmd "$IP -6 ro add 2001:db8:112::/64 via 2001:db8:101::2 mtu 1400"
1139	run_cmd "$IP -6 ro append 2001:db8:112::/64 via 2001:db8:103::2"
1140	rc=$?
1141	if [ $rc -eq 0 ]; then
1142		check_route6 "2001:db8:112::/64 metric 1024 mtu 1400 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
1143		rc=$?
1144	fi
1145	log_test $rc 0 "Multipath route via 2 single routes with mtu metric on first"
1146
1147	# second route is coalesced to first to make a multipath route.
1148	# MTU of the second path is hidden from display!
1149	run_cmd "$IP -6 ro add 2001:db8:113::/64 via 2001:db8:101::2"
1150	run_cmd "$IP -6 ro append 2001:db8:113::/64 via 2001:db8:103::2 mtu 1400"
1151	rc=$?
1152	if [ $rc -eq 0 ]; then
1153		check_route6 "2001:db8:113::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
1154		rc=$?
1155	fi
1156	log_test $rc 0 "Multipath route via 2 single routes with mtu metric on 2nd"
1157
1158	run_cmd "$IP -6 ro del 2001:db8:113::/64 via 2001:db8:101::2"
1159	if [ $? -eq 0 ]; then
1160		check_route6 "2001:db8:113::/64 via 2001:db8:103::2 dev veth3 metric 1024 mtu 1400"
1161		log_test $? 0 "    MTU of second leg"
1162	fi
1163
1164	#
1165	# multipath with metrics
1166	#
1167	run_cmd "$IP -6 ro add 2001:db8:115::/64 mtu 1400 nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
1168	rc=$?
1169	if [ $rc -eq 0 ]; then
1170		check_route6  "2001:db8:115::/64 metric 1024 mtu 1400 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
1171		rc=$?
1172	fi
1173	log_test $rc 0 "Multipath route with mtu metric"
1174
1175	$IP -6 ro add 2001:db8:104::/64 via 2001:db8:101::2 mtu 1300
1176	run_cmd "ip netns exec ns1 ${ping6} -w1 -c1 -s 1500 2001:db8:104::1"
1177	log_test $? 0 "Using route with mtu metric"
1178
1179	run_cmd "$IP -6 ro add 2001:db8:114::/64 via  2001:db8:101::2  congctl lock foo"
1180	log_test $? 2 "Invalid metric (fails metric_convert)"
1181
1182	route_cleanup
1183}
1184
1185# add route for a prefix, flushing any existing routes first
1186# expected to be the first step of a test
1187add_route()
1188{
1189	local pfx="$1"
1190	local nh="$2"
1191	local out
1192
1193	if [ "$VERBOSE" = "1" ]; then
1194		echo
1195		echo "    ##################################################"
1196		echo
1197	fi
1198
1199	run_cmd "$IP ro flush ${pfx}"
1200	[ $? -ne 0 ] && exit 1
1201
1202	out=$($IP ro ls match ${pfx})
1203	if [ -n "$out" ]; then
1204		echo "Failed to flush routes for prefix used for tests."
1205		exit 1
1206	fi
1207
1208	run_cmd "$IP ro add ${pfx} ${nh}"
1209	if [ $? -ne 0 ]; then
1210		echo "Failed to add initial route for test."
1211		exit 1
1212	fi
1213}
1214
1215# add initial route - used in replace route tests
1216add_initial_route()
1217{
1218	add_route "172.16.104.0/24" "$1"
1219}
1220
1221check_route()
1222{
1223	local pfx
1224	local expected="$1"
1225	local out
1226
1227	set -- $expected
1228	pfx=$1
1229	[ "${pfx}" = "unreachable" ] && pfx=$2
1230
1231	out=$($IP ro ls match ${pfx})
1232	check_expected "${out}" "${expected}"
1233}
1234
1235# assumption is that basic add of a single path route works
1236# otherwise just adding an address on an interface is broken
1237ipv4_rt_add()
1238{
1239	local rc
1240
1241	echo
1242	echo "IPv4 route add / append tests"
1243
1244	# route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
1245	add_route "172.16.104.0/24" "via 172.16.101.2"
1246	run_cmd "$IP ro add 172.16.104.0/24 via 172.16.103.2"
1247	log_test $? 2 "Attempt to add duplicate route - gw"
1248
1249	# route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
1250	add_route "172.16.104.0/24" "via 172.16.101.2"
1251	run_cmd "$IP ro add 172.16.104.0/24 dev veth3"
1252	log_test $? 2 "Attempt to add duplicate route - dev only"
1253
1254	# route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
1255	add_route "172.16.104.0/24" "via 172.16.101.2"
1256	run_cmd "$IP ro add unreachable 172.16.104.0/24"
1257	log_test $? 2 "Attempt to add duplicate route - reject route"
1258
1259	# iproute2 prepend only sets NLM_F_CREATE
1260	# - adds a new route; does NOT convert existing route to ECMP
1261	add_route "172.16.104.0/24" "via 172.16.101.2"
1262	run_cmd "$IP ro prepend 172.16.104.0/24 via 172.16.103.2"
1263	check_route "172.16.104.0/24 via 172.16.103.2 dev veth3 172.16.104.0/24 via 172.16.101.2 dev veth1"
1264	log_test $? 0 "Add new nexthop for existing prefix"
1265
1266	# route append with same prefix adds a new route
1267	# - iproute2 sets NLM_F_CREATE | NLM_F_APPEND
1268	add_route "172.16.104.0/24" "via 172.16.101.2"
1269	run_cmd "$IP ro append 172.16.104.0/24 via 172.16.103.2"
1270	check_route "172.16.104.0/24 via 172.16.101.2 dev veth1 172.16.104.0/24 via 172.16.103.2 dev veth3"
1271	log_test $? 0 "Append nexthop to existing route - gw"
1272
1273	add_route "172.16.104.0/24" "via 172.16.101.2"
1274	run_cmd "$IP ro append 172.16.104.0/24 dev veth3"
1275	check_route "172.16.104.0/24 via 172.16.101.2 dev veth1 172.16.104.0/24 dev veth3 scope link"
1276	log_test $? 0 "Append nexthop to existing route - dev only"
1277
1278	add_route "172.16.104.0/24" "via 172.16.101.2"
1279	run_cmd "$IP ro append unreachable 172.16.104.0/24"
1280	check_route "172.16.104.0/24 via 172.16.101.2 dev veth1 unreachable 172.16.104.0/24"
1281	log_test $? 0 "Append nexthop to existing route - reject route"
1282
1283	run_cmd "$IP ro flush 172.16.104.0/24"
1284	run_cmd "$IP ro add unreachable 172.16.104.0/24"
1285	run_cmd "$IP ro append 172.16.104.0/24 via 172.16.103.2"
1286	check_route "unreachable 172.16.104.0/24 172.16.104.0/24 via 172.16.103.2 dev veth3"
1287	log_test $? 0 "Append nexthop to existing reject route - gw"
1288
1289	run_cmd "$IP ro flush 172.16.104.0/24"
1290	run_cmd "$IP ro add unreachable 172.16.104.0/24"
1291	run_cmd "$IP ro append 172.16.104.0/24 dev veth3"
1292	check_route "unreachable 172.16.104.0/24 172.16.104.0/24 dev veth3 scope link"
1293	log_test $? 0 "Append nexthop to existing reject route - dev only"
1294
1295	# insert mpath directly
1296	add_route "172.16.104.0/24" "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1297	check_route  "172.16.104.0/24 nexthop via 172.16.101.2 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
1298	log_test $? 0 "add multipath route"
1299
1300	add_route "172.16.104.0/24" "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1301	run_cmd "$IP ro add 172.16.104.0/24 nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1302	log_test $? 2 "Attempt to add duplicate multipath route"
1303
1304	# insert of a second route without append but different metric
1305	add_route "172.16.104.0/24" "via 172.16.101.2"
1306	run_cmd "$IP ro add 172.16.104.0/24 via 172.16.103.2 metric 512"
1307	rc=$?
1308	if [ $rc -eq 0 ]; then
1309		run_cmd "$IP ro add 172.16.104.0/24 via 172.16.103.3 metric 256"
1310		rc=$?
1311	fi
1312	log_test $rc 0 "Route add with different metrics"
1313
1314	run_cmd "$IP ro del 172.16.104.0/24 metric 512"
1315	rc=$?
1316	if [ $rc -eq 0 ]; then
1317		check_route "172.16.104.0/24 via 172.16.101.2 dev veth1 172.16.104.0/24 via 172.16.103.3 dev veth3 metric 256"
1318		rc=$?
1319	fi
1320	log_test $rc 0 "Route delete with metric"
1321}
1322
1323ipv4_rt_replace_single()
1324{
1325	# single path with single path
1326	#
1327	add_initial_route "via 172.16.101.2"
1328	run_cmd "$IP ro replace 172.16.104.0/24 via 172.16.103.2"
1329	check_route "172.16.104.0/24 via 172.16.103.2 dev veth3"
1330	log_test $? 0 "Single path with single path"
1331
1332	# single path with multipath
1333	#
1334	add_initial_route "nexthop via 172.16.101.2"
1335	run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3 nexthop via 172.16.103.2"
1336	check_route "172.16.104.0/24 nexthop via 172.16.101.3 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
1337	log_test $? 0 "Single path with multipath"
1338
1339	# single path with reject
1340	#
1341	add_initial_route "nexthop via 172.16.101.2"
1342	run_cmd "$IP ro replace unreachable 172.16.104.0/24"
1343	check_route "unreachable 172.16.104.0/24"
1344	log_test $? 0 "Single path with reject route"
1345
1346	# single path with single path using MULTIPATH attribute
1347	#
1348	add_initial_route "via 172.16.101.2"
1349	run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.103.2"
1350	check_route "172.16.104.0/24 via 172.16.103.2 dev veth3"
1351	log_test $? 0 "Single path with single path via multipath attribute"
1352
1353	# route replace fails - invalid nexthop
1354	add_initial_route "via 172.16.101.2"
1355	run_cmd "$IP ro replace 172.16.104.0/24 via 2001:db8:104::2"
1356	if [ $? -eq 0 ]; then
1357		# previous command is expected to fail so if it returns 0
1358		# that means the test failed.
1359		log_test 0 1 "Invalid nexthop"
1360	else
1361		check_route "172.16.104.0/24 via 172.16.101.2 dev veth1"
1362		log_test $? 0 "Invalid nexthop"
1363	fi
1364
1365	# replace non-existent route
1366	# - note use of change versus replace since ip adds NLM_F_CREATE
1367	#   for replace
1368	add_initial_route "via 172.16.101.2"
1369	run_cmd "$IP ro change 172.16.105.0/24 via 172.16.101.2"
1370	log_test $? 2 "Single path - replace of non-existent route"
1371}
1372
1373ipv4_rt_replace_mpath()
1374{
1375	# multipath with multipath
1376	add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1377	run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3 nexthop via 172.16.103.3"
1378	check_route  "172.16.104.0/24 nexthop via 172.16.101.3 dev veth1 weight 1 nexthop via 172.16.103.3 dev veth3 weight 1"
1379	log_test $? 0 "Multipath with multipath"
1380
1381	# multipath with single
1382	add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1383	run_cmd "$IP ro replace 172.16.104.0/24 via 172.16.101.3"
1384	check_route  "172.16.104.0/24 via 172.16.101.3 dev veth1"
1385	log_test $? 0 "Multipath with single path"
1386
1387	# multipath with single
1388	add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1389	run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3"
1390	check_route "172.16.104.0/24 via 172.16.101.3 dev veth1"
1391	log_test $? 0 "Multipath with single path via multipath attribute"
1392
1393	# multipath with reject
1394	add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1395	run_cmd "$IP ro replace unreachable 172.16.104.0/24"
1396	check_route "unreachable 172.16.104.0/24"
1397	log_test $? 0 "Multipath with reject route"
1398
1399	# route replace fails - invalid nexthop 1
1400	add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1401	run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.111.3 nexthop via 172.16.103.3"
1402	check_route  "172.16.104.0/24 nexthop via 172.16.101.2 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
1403	log_test $? 0 "Multipath - invalid first nexthop"
1404
1405	# route replace fails - invalid nexthop 2
1406	add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1407	run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3 nexthop via 172.16.113.3"
1408	check_route  "172.16.104.0/24 nexthop via 172.16.101.2 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
1409	log_test $? 0 "Multipath - invalid second nexthop"
1410
1411	# multipath non-existent route
1412	add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1413	run_cmd "$IP ro change 172.16.105.0/24 nexthop via 172.16.101.3 nexthop via 172.16.103.3"
1414	log_test $? 2 "Multipath - replace of non-existent route"
1415}
1416
1417ipv4_rt_replace()
1418{
1419	echo
1420	echo "IPv4 route replace tests"
1421
1422	ipv4_rt_replace_single
1423	ipv4_rt_replace_mpath
1424}
1425
1426# checks that cached input route on VRF port is deleted
1427# when VRF is deleted
1428ipv4_local_rt_cache()
1429{
1430	run_cmd "ip addr add 10.0.0.1/32 dev lo"
1431	run_cmd "ip netns add test-ns"
1432	run_cmd "ip link add veth-outside type veth peer name veth-inside"
1433	run_cmd "ip link add vrf-100 type vrf table 1100"
1434	run_cmd "ip link set veth-outside master vrf-100"
1435	run_cmd "ip link set veth-inside netns test-ns"
1436	run_cmd "ip link set veth-outside up"
1437	run_cmd "ip link set vrf-100 up"
1438	run_cmd "ip route add 10.1.1.1/32 dev veth-outside table 1100"
1439	run_cmd "ip netns exec test-ns ip link set veth-inside up"
1440	run_cmd "ip netns exec test-ns ip addr add 10.1.1.1/32 dev veth-inside"
1441	run_cmd "ip netns exec test-ns ip route add 10.0.0.1/32 dev veth-inside"
1442	run_cmd "ip netns exec test-ns ip route add default via 10.0.0.1"
1443	run_cmd "ip netns exec test-ns ping 10.0.0.1 -c 1 -i 1"
1444	run_cmd "ip link delete vrf-100"
1445
1446	# if we do not hang test is a success
1447	log_test $? 0 "Cached route removed from VRF port device"
1448}
1449
1450ipv4_rt_dsfield()
1451{
1452	echo
1453	echo "IPv4 route with dsfield tests"
1454
1455	run_cmd "$IP route flush 172.16.102.0/24"
1456
1457	# New routes should reject dsfield options that interfere with ECN
1458	run_cmd "$IP route add 172.16.102.0/24 dsfield 0x01 via 172.16.101.2"
1459	log_test $? 2 "Reject route with dsfield 0x01"
1460
1461	run_cmd "$IP route add 172.16.102.0/24 dsfield 0x02 via 172.16.101.2"
1462	log_test $? 2 "Reject route with dsfield 0x02"
1463
1464	run_cmd "$IP route add 172.16.102.0/24 dsfield 0x03 via 172.16.101.2"
1465	log_test $? 2 "Reject route with dsfield 0x03"
1466
1467	# A generic route that doesn't take DSCP into account
1468	run_cmd "$IP route add 172.16.102.0/24 via 172.16.101.2"
1469
1470	# A more specific route for DSCP 0x10
1471	run_cmd "$IP route add 172.16.102.0/24 dsfield 0x10 via 172.16.103.2"
1472
1473	# DSCP 0x10 should match the specific route, no matter the ECN bits
1474	$IP route get fibmatch 172.16.102.1 dsfield 0x10 | \
1475		grep -q "via 172.16.103.2"
1476	log_test $? 0 "IPv4 route with DSCP and ECN:Not-ECT"
1477
1478	$IP route get fibmatch 172.16.102.1 dsfield 0x11 | \
1479		grep -q "via 172.16.103.2"
1480	log_test $? 0 "IPv4 route with DSCP and ECN:ECT(1)"
1481
1482	$IP route get fibmatch 172.16.102.1 dsfield 0x12 | \
1483		grep -q "via 172.16.103.2"
1484	log_test $? 0 "IPv4 route with DSCP and ECN:ECT(0)"
1485
1486	$IP route get fibmatch 172.16.102.1 dsfield 0x13 | \
1487		grep -q "via 172.16.103.2"
1488	log_test $? 0 "IPv4 route with DSCP and ECN:CE"
1489
1490	# Unknown DSCP should match the generic route, no matter the ECN bits
1491	$IP route get fibmatch 172.16.102.1 dsfield 0x14 | \
1492		grep -q "via 172.16.101.2"
1493	log_test $? 0 "IPv4 route with unknown DSCP and ECN:Not-ECT"
1494
1495	$IP route get fibmatch 172.16.102.1 dsfield 0x15 | \
1496		grep -q "via 172.16.101.2"
1497	log_test $? 0 "IPv4 route with unknown DSCP and ECN:ECT(1)"
1498
1499	$IP route get fibmatch 172.16.102.1 dsfield 0x16 | \
1500		grep -q "via 172.16.101.2"
1501	log_test $? 0 "IPv4 route with unknown DSCP and ECN:ECT(0)"
1502
1503	$IP route get fibmatch 172.16.102.1 dsfield 0x17 | \
1504		grep -q "via 172.16.101.2"
1505	log_test $? 0 "IPv4 route with unknown DSCP and ECN:CE"
1506
1507	# Null DSCP should match the generic route, no matter the ECN bits
1508	$IP route get fibmatch 172.16.102.1 dsfield 0x00 | \
1509		grep -q "via 172.16.101.2"
1510	log_test $? 0 "IPv4 route with no DSCP and ECN:Not-ECT"
1511
1512	$IP route get fibmatch 172.16.102.1 dsfield 0x01 | \
1513		grep -q "via 172.16.101.2"
1514	log_test $? 0 "IPv4 route with no DSCP and ECN:ECT(1)"
1515
1516	$IP route get fibmatch 172.16.102.1 dsfield 0x02 | \
1517		grep -q "via 172.16.101.2"
1518	log_test $? 0 "IPv4 route with no DSCP and ECN:ECT(0)"
1519
1520	$IP route get fibmatch 172.16.102.1 dsfield 0x03 | \
1521		grep -q "via 172.16.101.2"
1522	log_test $? 0 "IPv4 route with no DSCP and ECN:CE"
1523}
1524
1525ipv4_route_test()
1526{
1527	route_setup
1528
1529	ipv4_rt_add
1530	ipv4_rt_replace
1531	ipv4_local_rt_cache
1532	ipv4_rt_dsfield
1533
1534	route_cleanup
1535}
1536
1537ipv4_addr_metric_test()
1538{
1539	local rc
1540
1541	echo
1542	echo "IPv4 prefix route tests"
1543
1544	ip_addr_metric_check || return 1
1545
1546	setup
1547
1548	set -e
1549	$IP li add dummy1 type dummy
1550	$IP li add dummy2 type dummy
1551	$IP li set dummy1 up
1552	$IP li set dummy2 up
1553
1554	# default entry is metric 256
1555	run_cmd "$IP addr add dev dummy1 172.16.104.1/24"
1556	run_cmd "$IP addr add dev dummy2 172.16.104.2/24"
1557	set +e
1558
1559	check_route "172.16.104.0/24 dev dummy1 proto kernel scope link src 172.16.104.1 172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2"
1560	log_test $? 0 "Default metric"
1561
1562	set -e
1563	run_cmd "$IP addr flush dev dummy1"
1564	run_cmd "$IP addr add dev dummy1 172.16.104.1/24 metric 257"
1565	set +e
1566
1567	check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 172.16.104.0/24 dev dummy1 proto kernel scope link src 172.16.104.1 metric 257"
1568	log_test $? 0 "User specified metric on first device"
1569
1570	set -e
1571	run_cmd "$IP addr flush dev dummy2"
1572	run_cmd "$IP addr add dev dummy2 172.16.104.2/24 metric 258"
1573	set +e
1574
1575	check_route "172.16.104.0/24 dev dummy1 proto kernel scope link src 172.16.104.1 metric 257 172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 258"
1576	log_test $? 0 "User specified metric on second device"
1577
1578	run_cmd "$IP addr del dev dummy1 172.16.104.1/24 metric 257"
1579	rc=$?
1580	if [ $rc -eq 0 ]; then
1581		check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 258"
1582		rc=$?
1583	fi
1584	log_test $rc 0 "Delete of address on first device"
1585
1586	run_cmd "$IP addr change dev dummy2 172.16.104.2/24 metric 259"
1587	rc=$?
1588	if [ $rc -eq 0 ]; then
1589		check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 259"
1590		rc=$?
1591	fi
1592	log_test $rc 0 "Modify metric of address"
1593
1594	# verify prefix route removed on down
1595	run_cmd "$IP li set dev dummy2 down"
1596	rc=$?
1597	if [ $rc -eq 0 ]; then
1598		out=$($IP ro ls match 172.16.104.0/24)
1599		check_expected "${out}" ""
1600		rc=$?
1601	fi
1602	log_test $rc 0 "Prefix route removed on link down"
1603
1604	# verify prefix route re-inserted with assigned metric
1605	run_cmd "$IP li set dev dummy2 up"
1606	rc=$?
1607	if [ $rc -eq 0 ]; then
1608		check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 259"
1609		rc=$?
1610	fi
1611	log_test $rc 0 "Prefix route with metric on link up"
1612
1613	# explicitly check for metric changes on edge scenarios
1614	run_cmd "$IP addr flush dev dummy2"
1615	run_cmd "$IP addr add dev dummy2 172.16.104.0/24 metric 259"
1616	run_cmd "$IP addr change dev dummy2 172.16.104.0/24 metric 260"
1617	rc=$?
1618	if [ $rc -eq 0 ]; then
1619		check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.0 metric 260"
1620		rc=$?
1621	fi
1622	log_test $rc 0 "Modify metric of .0/24 address"
1623
1624	run_cmd "$IP addr flush dev dummy2"
1625	run_cmd "$IP addr add dev dummy2 172.16.104.1/32 peer 172.16.104.2 metric 260"
1626	rc=$?
1627	if [ $rc -eq 0 ]; then
1628		check_route "172.16.104.2 dev dummy2 proto kernel scope link src 172.16.104.1 metric 260"
1629		rc=$?
1630	fi
1631	log_test $rc 0 "Set metric of address with peer route"
1632
1633	run_cmd "$IP addr change dev dummy2 172.16.104.1/32 peer 172.16.104.3 metric 261"
1634	rc=$?
1635	if [ $rc -eq 0 ]; then
1636		check_route "172.16.104.3 dev dummy2 proto kernel scope link src 172.16.104.1 metric 261"
1637		rc=$?
1638	fi
1639	log_test $rc 0 "Modify metric and peer address for peer route"
1640
1641	$IP li del dummy1
1642	$IP li del dummy2
1643	cleanup
1644}
1645
1646ipv4_route_metrics_test()
1647{
1648	local rc
1649
1650	echo
1651	echo "IPv4 route add / append tests"
1652
1653	route_setup
1654
1655	run_cmd "$IP ro add 172.16.111.0/24 via 172.16.101.2 mtu 1400"
1656	rc=$?
1657	if [ $rc -eq 0 ]; then
1658		check_route "172.16.111.0/24 via 172.16.101.2 dev veth1 mtu 1400"
1659		rc=$?
1660	fi
1661	log_test $rc 0 "Single path route with mtu metric"
1662
1663
1664	run_cmd "$IP ro add 172.16.112.0/24 mtu 1400 nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1665	rc=$?
1666	if [ $rc -eq 0 ]; then
1667		check_route "172.16.112.0/24 mtu 1400 nexthop via 172.16.101.2 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
1668		rc=$?
1669	fi
1670	log_test $rc 0 "Multipath route with mtu metric"
1671
1672	$IP ro add 172.16.104.0/24 via 172.16.101.2 mtu 1300
1673	run_cmd "ip netns exec ns1 ping -w1 -c1 -s 1500 172.16.104.1"
1674	log_test $? 0 "Using route with mtu metric"
1675
1676	run_cmd "$IP ro add 172.16.111.0/24 via 172.16.101.2 congctl lock foo"
1677	log_test $? 2 "Invalid metric (fails metric_convert)"
1678
1679	route_cleanup
1680}
1681
1682ipv4_del_addr_test()
1683{
1684	echo
1685	echo "IPv4 delete address route tests"
1686
1687	setup
1688
1689	set -e
1690	$IP li add dummy1 type dummy
1691	$IP li set dummy1 up
1692	$IP li add dummy2 type dummy
1693	$IP li set dummy2 up
1694	$IP li add red type vrf table 1111
1695	$IP li set red up
1696	$IP ro add vrf red unreachable default
1697	$IP li set dummy2 vrf red
1698
1699	$IP addr add dev dummy1 172.16.104.1/24
1700	$IP addr add dev dummy1 172.16.104.11/24
1701	$IP addr add dev dummy2 172.16.104.1/24
1702	$IP addr add dev dummy2 172.16.104.11/24
1703	$IP route add 172.16.105.0/24 via 172.16.104.2 src 172.16.104.11
1704	$IP route add vrf red 172.16.105.0/24 via 172.16.104.2 src 172.16.104.11
1705	set +e
1706
1707	# removing address from device in vrf should only remove route from vrf table
1708	$IP addr del dev dummy2 172.16.104.11/24
1709	$IP ro ls vrf red | grep -q 172.16.105.0/24
1710	log_test $? 1 "Route removed from VRF when source address deleted"
1711
1712	$IP ro ls | grep -q 172.16.105.0/24
1713	log_test $? 0 "Route in default VRF not removed"
1714
1715	$IP addr add dev dummy2 172.16.104.11/24
1716	$IP route add vrf red 172.16.105.0/24 via 172.16.104.2 src 172.16.104.11
1717
1718	$IP addr del dev dummy1 172.16.104.11/24
1719	$IP ro ls | grep -q 172.16.105.0/24
1720	log_test $? 1 "Route removed in default VRF when source address deleted"
1721
1722	$IP ro ls vrf red | grep -q 172.16.105.0/24
1723	log_test $? 0 "Route in VRF is not removed by address delete"
1724
1725	$IP li del dummy1
1726	$IP li del dummy2
1727	cleanup
1728}
1729
1730
1731ipv4_route_v6_gw_test()
1732{
1733	local rc
1734
1735	echo
1736	echo "IPv4 route with IPv6 gateway tests"
1737
1738	route_setup
1739	sleep 2
1740
1741	#
1742	# single path route
1743	#
1744	run_cmd "$IP ro add 172.16.104.0/24 via inet6 2001:db8:101::2"
1745	rc=$?
1746	log_test $rc 0 "Single path route with IPv6 gateway"
1747	if [ $rc -eq 0 ]; then
1748		check_route "172.16.104.0/24 via inet6 2001:db8:101::2 dev veth1"
1749	fi
1750
1751	run_cmd "ip netns exec ns1 ping -w1 -c1 172.16.104.1"
1752	log_test $rc 0 "Single path route with IPv6 gateway - ping"
1753
1754	run_cmd "$IP ro del 172.16.104.0/24 via inet6 2001:db8:101::2"
1755	rc=$?
1756	log_test $rc 0 "Single path route delete"
1757	if [ $rc -eq 0 ]; then
1758		check_route "172.16.112.0/24"
1759	fi
1760
1761	#
1762	# multipath - v6 then v4
1763	#
1764	run_cmd "$IP ro add 172.16.104.0/24 nexthop via inet6 2001:db8:101::2 dev veth1 nexthop via 172.16.103.2 dev veth3"
1765	rc=$?
1766	log_test $rc 0 "Multipath route add - v6 nexthop then v4"
1767	if [ $rc -eq 0 ]; then
1768		check_route "172.16.104.0/24 nexthop via inet6 2001:db8:101::2 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
1769	fi
1770
1771	run_cmd "$IP ro del 172.16.104.0/24 nexthop via 172.16.103.2 dev veth3 nexthop via inet6 2001:db8:101::2 dev veth1"
1772	log_test $? 2 "    Multipath route delete - nexthops in wrong order"
1773
1774	run_cmd "$IP ro del 172.16.104.0/24 nexthop via inet6 2001:db8:101::2 dev veth1 nexthop via 172.16.103.2 dev veth3"
1775	log_test $? 0 "    Multipath route delete exact match"
1776
1777	#
1778	# multipath - v4 then v6
1779	#
1780	run_cmd "$IP ro add 172.16.104.0/24 nexthop via 172.16.103.2 dev veth3 nexthop via inet6 2001:db8:101::2 dev veth1"
1781	rc=$?
1782	log_test $rc 0 "Multipath route add - v4 nexthop then v6"
1783	if [ $rc -eq 0 ]; then
1784		check_route "172.16.104.0/24 nexthop via 172.16.103.2 dev veth3 weight 1 nexthop via inet6 2001:db8:101::2 dev veth1 weight 1"
1785	fi
1786
1787	run_cmd "$IP ro del 172.16.104.0/24 nexthop via inet6 2001:db8:101::2 dev veth1 nexthop via 172.16.103.2 dev veth3"
1788	log_test $? 2 "    Multipath route delete - nexthops in wrong order"
1789
1790	run_cmd "$IP ro del 172.16.104.0/24 nexthop via 172.16.103.2 dev veth3 nexthop via inet6 2001:db8:101::2 dev veth1"
1791	log_test $? 0 "    Multipath route delete exact match"
1792
1793	route_cleanup
1794}
1795
1796socat_check()
1797{
1798	if [ ! -x "$(command -v socat)" ]; then
1799		echo "socat command not found. Skipping test"
1800		return 1
1801	fi
1802
1803	return 0
1804}
1805
1806iptables_check()
1807{
1808	iptables -t mangle -L OUTPUT &> /dev/null
1809	if [ $? -ne 0 ]; then
1810		echo "iptables configuration not supported. Skipping test"
1811		return 1
1812	fi
1813
1814	return 0
1815}
1816
1817ip6tables_check()
1818{
1819	ip6tables -t mangle -L OUTPUT &> /dev/null
1820	if [ $? -ne 0 ]; then
1821		echo "ip6tables configuration not supported. Skipping test"
1822		return 1
1823	fi
1824
1825	return 0
1826}
1827
1828ipv4_mangle_test()
1829{
1830	local rc
1831
1832	echo
1833	echo "IPv4 mangling tests"
1834
1835	socat_check || return 1
1836	iptables_check || return 1
1837
1838	route_setup
1839	sleep 2
1840
1841	local tmp_file=$(mktemp)
1842	ip netns exec ns2 socat UDP4-LISTEN:54321,fork $tmp_file &
1843
1844	# Add a FIB rule and a route that will direct our connection to the
1845	# listening server.
1846	$IP rule add pref 100 ipproto udp sport 12345 dport 54321 table 123
1847	$IP route add table 123 172.16.101.0/24 dev veth1
1848
1849	# Add an unreachable route to the main table that will block our
1850	# connection in case the FIB rule is not hit.
1851	$IP route add unreachable 172.16.101.2/32
1852
1853	run_cmd "echo a | $NS_EXEC socat STDIN UDP4:172.16.101.2:54321,sourceport=12345"
1854	log_test $? 0 "    Connection with correct parameters"
1855
1856	run_cmd "echo a | $NS_EXEC socat STDIN UDP4:172.16.101.2:54321,sourceport=11111"
1857	log_test $? 1 "    Connection with incorrect parameters"
1858
1859	# Add a mangling rule and make sure connection is still successful.
1860	$NS_EXEC iptables -t mangle -A OUTPUT -j MARK --set-mark 1
1861
1862	run_cmd "echo a | $NS_EXEC socat STDIN UDP4:172.16.101.2:54321,sourceport=12345"
1863	log_test $? 0 "    Connection with correct parameters - mangling"
1864
1865	# Delete the mangling rule and make sure connection is still
1866	# successful.
1867	$NS_EXEC iptables -t mangle -D OUTPUT -j MARK --set-mark 1
1868
1869	run_cmd "echo a | $NS_EXEC socat STDIN UDP4:172.16.101.2:54321,sourceport=12345"
1870	log_test $? 0 "    Connection with correct parameters - no mangling"
1871
1872	# Verify connections were indeed successful on server side.
1873	[[ $(cat $tmp_file | wc -l) -eq 3 ]]
1874	log_test $? 0 "    Connection check - server side"
1875
1876	$IP route del unreachable 172.16.101.2/32
1877	$IP route del table 123 172.16.101.0/24 dev veth1
1878	$IP rule del pref 100
1879
1880	{ kill %% && wait %%; } 2>/dev/null
1881	rm $tmp_file
1882
1883	route_cleanup
1884}
1885
1886ipv6_mangle_test()
1887{
1888	local rc
1889
1890	echo
1891	echo "IPv6 mangling tests"
1892
1893	socat_check || return 1
1894	ip6tables_check || return 1
1895
1896	route_setup
1897	sleep 2
1898
1899	local tmp_file=$(mktemp)
1900	ip netns exec ns2 socat UDP6-LISTEN:54321,fork $tmp_file &
1901
1902	# Add a FIB rule and a route that will direct our connection to the
1903	# listening server.
1904	$IP -6 rule add pref 100 ipproto udp sport 12345 dport 54321 table 123
1905	$IP -6 route add table 123 2001:db8:101::/64 dev veth1
1906
1907	# Add an unreachable route to the main table that will block our
1908	# connection in case the FIB rule is not hit.
1909	$IP -6 route add unreachable 2001:db8:101::2/128
1910
1911	run_cmd "echo a | $NS_EXEC socat STDIN UDP6:[2001:db8:101::2]:54321,sourceport=12345"
1912	log_test $? 0 "    Connection with correct parameters"
1913
1914	run_cmd "echo a | $NS_EXEC socat STDIN UDP6:[2001:db8:101::2]:54321,sourceport=11111"
1915	log_test $? 1 "    Connection with incorrect parameters"
1916
1917	# Add a mangling rule and make sure connection is still successful.
1918	$NS_EXEC ip6tables -t mangle -A OUTPUT -j MARK --set-mark 1
1919
1920	run_cmd "echo a | $NS_EXEC socat STDIN UDP6:[2001:db8:101::2]:54321,sourceport=12345"
1921	log_test $? 0 "    Connection with correct parameters - mangling"
1922
1923	# Delete the mangling rule and make sure connection is still
1924	# successful.
1925	$NS_EXEC ip6tables -t mangle -D OUTPUT -j MARK --set-mark 1
1926
1927	run_cmd "echo a | $NS_EXEC socat STDIN UDP6:[2001:db8:101::2]:54321,sourceport=12345"
1928	log_test $? 0 "    Connection with correct parameters - no mangling"
1929
1930	# Verify connections were indeed successful on server side.
1931	[[ $(cat $tmp_file | wc -l) -eq 3 ]]
1932	log_test $? 0 "    Connection check - server side"
1933
1934	$IP -6 route del unreachable 2001:db8:101::2/128
1935	$IP -6 route del table 123 2001:db8:101::/64 dev veth1
1936	$IP -6 rule del pref 100
1937
1938	{ kill %% && wait %%; } 2>/dev/null
1939	rm $tmp_file
1940
1941	route_cleanup
1942}
1943
1944################################################################################
1945# usage
1946
1947usage()
1948{
1949	cat <<EOF
1950usage: ${0##*/} OPTS
1951
1952        -t <test>   Test(s) to run (default: all)
1953                    (options: $TESTS)
1954        -p          Pause on fail
1955        -P          Pause after each test before cleanup
1956        -v          verbose mode (show commands and output)
1957EOF
1958}
1959
1960################################################################################
1961# main
1962
1963while getopts :t:pPhv o
1964do
1965	case $o in
1966		t) TESTS=$OPTARG;;
1967		p) PAUSE_ON_FAIL=yes;;
1968		P) PAUSE=yes;;
1969		v) VERBOSE=$(($VERBOSE + 1));;
1970		h) usage; exit 0;;
1971		*) usage; exit 1;;
1972	esac
1973done
1974
1975PEER_CMD="ip netns exec ${PEER_NS}"
1976
1977# make sure we don't pause twice
1978[ "${PAUSE}" = "yes" ] && PAUSE_ON_FAIL=no
1979
1980if [ "$(id -u)" -ne 0 ];then
1981	echo "SKIP: Need root privileges"
1982	exit $ksft_skip;
1983fi
1984
1985if [ ! -x "$(command -v ip)" ]; then
1986	echo "SKIP: Could not run test without ip tool"
1987	exit $ksft_skip
1988fi
1989
1990ip route help 2>&1 | grep -q fibmatch
1991if [ $? -ne 0 ]; then
1992	echo "SKIP: iproute2 too old, missing fibmatch"
1993	exit $ksft_skip
1994fi
1995
1996# start clean
1997cleanup &> /dev/null
1998
1999for t in $TESTS
2000do
2001	case $t in
2002	fib_unreg_test|unregister)	fib_unreg_test;;
2003	fib_down_test|down)		fib_down_test;;
2004	fib_carrier_test|carrier)	fib_carrier_test;;
2005	fib_rp_filter_test|rp_filter)	fib_rp_filter_test;;
2006	fib_nexthop_test|nexthop)	fib_nexthop_test;;
2007	fib_suppress_test|suppress)	fib_suppress_test;;
2008	ipv6_route_test|ipv6_rt)	ipv6_route_test;;
2009	ipv4_route_test|ipv4_rt)	ipv4_route_test;;
2010	ipv6_addr_metric)		ipv6_addr_metric_test;;
2011	ipv4_addr_metric)		ipv4_addr_metric_test;;
2012	ipv4_del_addr)			ipv4_del_addr_test;;
2013	ipv6_route_metrics)		ipv6_route_metrics_test;;
2014	ipv4_route_metrics)		ipv4_route_metrics_test;;
2015	ipv4_route_v6_gw)		ipv4_route_v6_gw_test;;
2016	ipv4_mangle)			ipv4_mangle_test;;
2017	ipv6_mangle)			ipv6_mangle_test;;
2018
2019	help) echo "Test names: $TESTS"; exit 0;;
2020	esac
2021done
2022
2023if [ "$TESTS" != "none" ]; then
2024	printf "\nTests passed: %3d\n" ${nsuccess}
2025	printf "Tests failed: %3d\n"   ${nfail}
2026fi
2027
2028exit $ret
2029