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 ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric ipv6_route_metrics ipv4_route_metrics ipv4_route_v6_gw rp_filter"
13
14VERBOSE=0
15PAUSE_ON_FAIL=no
16PAUSE=no
17IP="ip -netns ns1"
18NS_EXEC="ip netns exec ns1"
19
20log_test()
21{
22	local rc=$1
23	local expected=$2
24	local msg="$3"
25
26	if [ ${rc} -eq ${expected} ]; then
27		printf "    TEST: %-60s  [ OK ]\n" "${msg}"
28		nsuccess=$((nsuccess+1))
29	else
30		ret=1
31		nfail=$((nfail+1))
32		printf "    TEST: %-60s  [FAIL]\n" "${msg}"
33		if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
34		echo
35			echo "hit enter to continue, 'q' to quit"
36			read a
37			[ "$a" = "q" ] && exit 1
38		fi
39	fi
40
41	if [ "${PAUSE}" = "yes" ]; then
42		echo
43		echo "hit enter to continue, 'q' to quit"
44		read a
45		[ "$a" = "q" ] && exit 1
46	fi
47}
48
49setup()
50{
51	set -e
52	ip netns add ns1
53	ip netns set ns1 auto
54	$IP link set dev lo up
55	ip netns exec ns1 sysctl -qw net.ipv4.ip_forward=1
56	ip netns exec ns1 sysctl -qw net.ipv6.conf.all.forwarding=1
57
58	$IP link add dummy0 type dummy
59	$IP link set dev dummy0 up
60	$IP address add 198.51.100.1/24 dev dummy0
61	$IP -6 address add 2001:db8:1::1/64 dev dummy0
62	set +e
63
64}
65
66cleanup()
67{
68	$IP link del dev dummy0 &> /dev/null
69	ip netns del ns1
70	ip netns del ns2 &> /dev/null
71}
72
73get_linklocal()
74{
75	local dev=$1
76	local addr
77
78	addr=$($IP -6 -br addr show dev ${dev} | \
79	awk '{
80		for (i = 3; i <= NF; ++i) {
81			if ($i ~ /^fe80/)
82				print $i
83		}
84	}'
85	)
86	addr=${addr/\/*}
87
88	[ -z "$addr" ] && return 1
89
90	echo $addr
91
92	return 0
93}
94
95fib_unreg_unicast_test()
96{
97	echo
98	echo "Single path route test"
99
100	setup
101
102	echo "    Start point"
103	$IP route get fibmatch 198.51.100.2 &> /dev/null
104	log_test $? 0 "IPv4 fibmatch"
105	$IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
106	log_test $? 0 "IPv6 fibmatch"
107
108	set -e
109	$IP link del dev dummy0
110	set +e
111
112	echo "    Nexthop device deleted"
113	$IP route get fibmatch 198.51.100.2 &> /dev/null
114	log_test $? 2 "IPv4 fibmatch - no route"
115	$IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
116	log_test $? 2 "IPv6 fibmatch - no route"
117
118	cleanup
119}
120
121fib_unreg_multipath_test()
122{
123
124	echo
125	echo "Multipath route test"
126
127	setup
128
129	set -e
130	$IP link add dummy1 type dummy
131	$IP link set dev dummy1 up
132	$IP address add 192.0.2.1/24 dev dummy1
133	$IP -6 address add 2001:db8:2::1/64 dev dummy1
134
135	$IP route add 203.0.113.0/24 \
136		nexthop via 198.51.100.2 dev dummy0 \
137		nexthop via 192.0.2.2 dev dummy1
138	$IP -6 route add 2001:db8:3::/64 \
139		nexthop via 2001:db8:1::2 dev dummy0 \
140		nexthop via 2001:db8:2::2 dev dummy1
141	set +e
142
143	echo "    Start point"
144	$IP route get fibmatch 203.0.113.1 &> /dev/null
145	log_test $? 0 "IPv4 fibmatch"
146	$IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
147	log_test $? 0 "IPv6 fibmatch"
148
149	set -e
150	$IP link del dev dummy0
151	set +e
152
153	echo "    One nexthop device deleted"
154	$IP route get fibmatch 203.0.113.1 &> /dev/null
155	log_test $? 2 "IPv4 - multipath route removed on delete"
156
157	$IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
158	# In IPv6 we do not flush the entire multipath route.
159	log_test $? 0 "IPv6 - multipath down to single path"
160
161	set -e
162	$IP link del dev dummy1
163	set +e
164
165	echo "    Second nexthop device deleted"
166	$IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
167	log_test $? 2 "IPv6 - no route"
168
169	cleanup
170}
171
172fib_unreg_test()
173{
174	fib_unreg_unicast_test
175	fib_unreg_multipath_test
176}
177
178fib_down_unicast_test()
179{
180	echo
181	echo "Single path, admin down"
182
183	setup
184
185	echo "    Start point"
186	$IP route get fibmatch 198.51.100.2 &> /dev/null
187	log_test $? 0 "IPv4 fibmatch"
188	$IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
189	log_test $? 0 "IPv6 fibmatch"
190
191	set -e
192	$IP link set dev dummy0 down
193	set +e
194
195	echo "    Route deleted on down"
196	$IP route get fibmatch 198.51.100.2 &> /dev/null
197	log_test $? 2 "IPv4 fibmatch"
198	$IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
199	log_test $? 2 "IPv6 fibmatch"
200
201	cleanup
202}
203
204fib_down_multipath_test_do()
205{
206	local down_dev=$1
207	local up_dev=$2
208
209	$IP route get fibmatch 203.0.113.1 \
210		oif $down_dev &> /dev/null
211	log_test $? 2 "IPv4 fibmatch on down device"
212	$IP -6 route get fibmatch 2001:db8:3::1 \
213		oif $down_dev &> /dev/null
214	log_test $? 2 "IPv6 fibmatch on down device"
215
216	$IP route get fibmatch 203.0.113.1 \
217		oif $up_dev &> /dev/null
218	log_test $? 0 "IPv4 fibmatch on up device"
219	$IP -6 route get fibmatch 2001:db8:3::1 \
220		oif $up_dev &> /dev/null
221	log_test $? 0 "IPv6 fibmatch on up device"
222
223	$IP route get fibmatch 203.0.113.1 | \
224		grep $down_dev | grep -q "dead linkdown"
225	log_test $? 0 "IPv4 flags on down device"
226	$IP -6 route get fibmatch 2001:db8:3::1 | \
227		grep $down_dev | grep -q "dead linkdown"
228	log_test $? 0 "IPv6 flags on down device"
229
230	$IP route get fibmatch 203.0.113.1 | \
231		grep $up_dev | grep -q "dead linkdown"
232	log_test $? 1 "IPv4 flags on up device"
233	$IP -6 route get fibmatch 2001:db8:3::1 | \
234		grep $up_dev | grep -q "dead linkdown"
235	log_test $? 1 "IPv6 flags on up device"
236}
237
238fib_down_multipath_test()
239{
240	echo
241	echo "Admin down multipath"
242
243	setup
244
245	set -e
246	$IP link add dummy1 type dummy
247	$IP link set dev dummy1 up
248
249	$IP address add 192.0.2.1/24 dev dummy1
250	$IP -6 address add 2001:db8:2::1/64 dev dummy1
251
252	$IP route add 203.0.113.0/24 \
253		nexthop via 198.51.100.2 dev dummy0 \
254		nexthop via 192.0.2.2 dev dummy1
255	$IP -6 route add 2001:db8:3::/64 \
256		nexthop via 2001:db8:1::2 dev dummy0 \
257		nexthop via 2001:db8:2::2 dev dummy1
258	set +e
259
260	echo "    Verify start point"
261	$IP route get fibmatch 203.0.113.1 &> /dev/null
262	log_test $? 0 "IPv4 fibmatch"
263
264	$IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
265	log_test $? 0 "IPv6 fibmatch"
266
267	set -e
268	$IP link set dev dummy0 down
269	set +e
270
271	echo "    One device down, one up"
272	fib_down_multipath_test_do "dummy0" "dummy1"
273
274	set -e
275	$IP link set dev dummy0 up
276	$IP link set dev dummy1 down
277	set +e
278
279	echo "    Other device down and up"
280	fib_down_multipath_test_do "dummy1" "dummy0"
281
282	set -e
283	$IP link set dev dummy0 down
284	set +e
285
286	echo "    Both devices down"
287	$IP route get fibmatch 203.0.113.1 &> /dev/null
288	log_test $? 2 "IPv4 fibmatch"
289	$IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
290	log_test $? 2 "IPv6 fibmatch"
291
292	$IP link del dev dummy1
293	cleanup
294}
295
296fib_down_test()
297{
298	fib_down_unicast_test
299	fib_down_multipath_test
300}
301
302# Local routes should not be affected when carrier changes.
303fib_carrier_local_test()
304{
305	echo
306	echo "Local carrier tests - single path"
307
308	setup
309
310	set -e
311	$IP link set dev dummy0 carrier on
312	set +e
313
314	echo "    Start point"
315	$IP route get fibmatch 198.51.100.1 &> /dev/null
316	log_test $? 0 "IPv4 fibmatch"
317	$IP -6 route get fibmatch 2001:db8:1::1 &> /dev/null
318	log_test $? 0 "IPv6 fibmatch"
319
320	$IP route get fibmatch 198.51.100.1 | \
321		grep -q "linkdown"
322	log_test $? 1 "IPv4 - no linkdown flag"
323	$IP -6 route get fibmatch 2001:db8:1::1 | \
324		grep -q "linkdown"
325	log_test $? 1 "IPv6 - no linkdown flag"
326
327	set -e
328	$IP link set dev dummy0 carrier off
329	sleep 1
330	set +e
331
332	echo "    Carrier off on nexthop"
333	$IP route get fibmatch 198.51.100.1 &> /dev/null
334	log_test $? 0 "IPv4 fibmatch"
335	$IP -6 route get fibmatch 2001:db8:1::1 &> /dev/null
336	log_test $? 0 "IPv6 fibmatch"
337
338	$IP route get fibmatch 198.51.100.1 | \
339		grep -q "linkdown"
340	log_test $? 1 "IPv4 - linkdown flag set"
341	$IP -6 route get fibmatch 2001:db8:1::1 | \
342		grep -q "linkdown"
343	log_test $? 1 "IPv6 - linkdown flag set"
344
345	set -e
346	$IP address add 192.0.2.1/24 dev dummy0
347	$IP -6 address add 2001:db8:2::1/64 dev dummy0
348	set +e
349
350	echo "    Route to local address with carrier down"
351	$IP route get fibmatch 192.0.2.1 &> /dev/null
352	log_test $? 0 "IPv4 fibmatch"
353	$IP -6 route get fibmatch 2001:db8:2::1 &> /dev/null
354	log_test $? 0 "IPv6 fibmatch"
355
356	$IP route get fibmatch 192.0.2.1 | \
357		grep -q "linkdown"
358	log_test $? 1 "IPv4 linkdown flag set"
359	$IP -6 route get fibmatch 2001:db8:2::1 | \
360		grep -q "linkdown"
361	log_test $? 1 "IPv6 linkdown flag set"
362
363	cleanup
364}
365
366fib_carrier_unicast_test()
367{
368	ret=0
369
370	echo
371	echo "Single path route carrier test"
372
373	setup
374
375	set -e
376	$IP link set dev dummy0 carrier on
377	set +e
378
379	echo "    Start point"
380	$IP route get fibmatch 198.51.100.2 &> /dev/null
381	log_test $? 0 "IPv4 fibmatch"
382	$IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
383	log_test $? 0 "IPv6 fibmatch"
384
385	$IP route get fibmatch 198.51.100.2 | \
386		grep -q "linkdown"
387	log_test $? 1 "IPv4 no linkdown flag"
388	$IP -6 route get fibmatch 2001:db8:1::2 | \
389		grep -q "linkdown"
390	log_test $? 1 "IPv6 no linkdown flag"
391
392	set -e
393	$IP link set dev dummy0 carrier off
394	sleep 1
395	set +e
396
397	echo "    Carrier down"
398	$IP route get fibmatch 198.51.100.2 &> /dev/null
399	log_test $? 0 "IPv4 fibmatch"
400	$IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
401	log_test $? 0 "IPv6 fibmatch"
402
403	$IP route get fibmatch 198.51.100.2 | \
404		grep -q "linkdown"
405	log_test $? 0 "IPv4 linkdown flag set"
406	$IP -6 route get fibmatch 2001:db8:1::2 | \
407		grep -q "linkdown"
408	log_test $? 0 "IPv6 linkdown flag set"
409
410	set -e
411	$IP address add 192.0.2.1/24 dev dummy0
412	$IP -6 address add 2001:db8:2::1/64 dev dummy0
413	set +e
414
415	echo "    Second address added with carrier down"
416	$IP route get fibmatch 192.0.2.2 &> /dev/null
417	log_test $? 0 "IPv4 fibmatch"
418	$IP -6 route get fibmatch 2001:db8:2::2 &> /dev/null
419	log_test $? 0 "IPv6 fibmatch"
420
421	$IP route get fibmatch 192.0.2.2 | \
422		grep -q "linkdown"
423	log_test $? 0 "IPv4 linkdown flag set"
424	$IP -6 route get fibmatch 2001:db8:2::2 | \
425		grep -q "linkdown"
426	log_test $? 0 "IPv6 linkdown flag set"
427
428	cleanup
429}
430
431fib_carrier_test()
432{
433	fib_carrier_local_test
434	fib_carrier_unicast_test
435}
436
437fib_rp_filter_test()
438{
439	echo
440	echo "IPv4 rp_filter tests"
441
442	setup
443
444	set -e
445	$IP link set dev lo address 52:54:00:6a:c7:5e
446	$IP link set dummy0 address 52:54:00:6a:c7:5e
447	$IP link add dummy1 type dummy
448	$IP link set dummy1 address 52:54:00:6a:c7:5e
449	$IP link set dev dummy1 up
450	$NS_EXEC sysctl -qw net.ipv4.conf.all.rp_filter=1
451	$NS_EXEC sysctl -qw net.ipv4.conf.all.accept_local=1
452	$NS_EXEC sysctl -qw net.ipv4.conf.all.route_localnet=1
453
454	$NS_EXEC tc qd add dev dummy1 parent root handle 1: fq_codel
455	$NS_EXEC tc filter add dev dummy1 parent 1: protocol arp basic action mirred egress redirect dev lo
456	$NS_EXEC tc filter add dev dummy1 parent 1: protocol ip basic action mirred egress redirect dev lo
457	set +e
458
459	run_cmd "ip netns exec ns1 ping -I dummy1 -w1 -c1 198.51.100.1"
460	log_test $? 0 "rp_filter passes local packets"
461
462	run_cmd "ip netns exec ns1 ping -I dummy1 -w1 -c1 127.0.0.1"
463	log_test $? 0 "rp_filter passes loopback packets"
464
465	cleanup
466}
467
468################################################################################
469# Tests on nexthop spec
470
471# run 'ip route add' with given spec
472add_rt()
473{
474	local desc="$1"
475	local erc=$2
476	local vrf=$3
477	local pfx=$4
478	local gw=$5
479	local dev=$6
480	local cmd out rc
481
482	[ "$vrf" = "-" ] && vrf="default"
483	[ -n "$gw" ] && gw="via $gw"
484	[ -n "$dev" ] && dev="dev $dev"
485
486	cmd="$IP route add vrf $vrf $pfx $gw $dev"
487	if [ "$VERBOSE" = "1" ]; then
488		printf "\n    COMMAND: $cmd\n"
489	fi
490
491	out=$(eval $cmd 2>&1)
492	rc=$?
493	if [ "$VERBOSE" = "1" -a -n "$out" ]; then
494		echo "    $out"
495	fi
496	log_test $rc $erc "$desc"
497}
498
499fib4_nexthop()
500{
501	echo
502	echo "IPv4 nexthop tests"
503
504	echo "<<< write me >>>"
505}
506
507fib6_nexthop()
508{
509	local lldummy=$(get_linklocal dummy0)
510	local llv1=$(get_linklocal dummy0)
511
512	if [ -z "$lldummy" ]; then
513		echo "Failed to get linklocal address for dummy0"
514		return 1
515	fi
516	if [ -z "$llv1" ]; then
517		echo "Failed to get linklocal address for veth1"
518		return 1
519	fi
520
521	echo
522	echo "IPv6 nexthop tests"
523
524	add_rt "Directly connected nexthop, unicast address" 0 \
525		- 2001:db8:101::/64 2001:db8:1::2
526	add_rt "Directly connected nexthop, unicast address with device" 0 \
527		- 2001:db8:102::/64 2001:db8:1::2 "dummy0"
528	add_rt "Gateway is linklocal address" 0 \
529		- 2001:db8:103::1/64 $llv1 "veth0"
530
531	# fails because LL address requires a device
532	add_rt "Gateway is linklocal address, no device" 2 \
533		- 2001:db8:104::1/64 $llv1
534
535	# local address can not be a gateway
536	add_rt "Gateway can not be local unicast address" 2 \
537		- 2001:db8:105::/64 2001:db8:1::1
538	add_rt "Gateway can not be local unicast address, with device" 2 \
539		- 2001:db8:106::/64 2001:db8:1::1 "dummy0"
540	add_rt "Gateway can not be a local linklocal address" 2 \
541		- 2001:db8:107::1/64 $lldummy "dummy0"
542
543	# VRF tests
544	add_rt "Gateway can be local address in a VRF" 0 \
545		- 2001:db8:108::/64 2001:db8:51::2
546	add_rt "Gateway can be local address in a VRF, with device" 0 \
547		- 2001:db8:109::/64 2001:db8:51::2 "veth0"
548	add_rt "Gateway can be local linklocal address in a VRF" 0 \
549		- 2001:db8:110::1/64 $llv1 "veth0"
550
551	add_rt "Redirect to VRF lookup" 0 \
552		- 2001:db8:111::/64 "" "red"
553
554	add_rt "VRF route, gateway can be local address in default VRF" 0 \
555		red 2001:db8:112::/64 2001:db8:51::1
556
557	# local address in same VRF fails
558	add_rt "VRF route, gateway can not be a local address" 2 \
559		red 2001:db8:113::1/64 2001:db8:2::1
560	add_rt "VRF route, gateway can not be a local addr with device" 2 \
561		red 2001:db8:114::1/64 2001:db8:2::1 "dummy1"
562}
563
564# Default VRF:
565#   dummy0 - 198.51.100.1/24 2001:db8:1::1/64
566#   veth0  - 192.0.2.1/24    2001:db8:51::1/64
567#
568# VRF red:
569#   dummy1 - 192.168.2.1/24 2001:db8:2::1/64
570#   veth1  - 192.0.2.2/24   2001:db8:51::2/64
571#
572#  [ dummy0   veth0 ]--[ veth1   dummy1 ]
573
574fib_nexthop_test()
575{
576	setup
577
578	set -e
579
580	$IP -4 rule add pref 32765 table local
581	$IP -4 rule del pref 0
582	$IP -6 rule add pref 32765 table local
583	$IP -6 rule del pref 0
584
585	$IP link add red type vrf table 1
586	$IP link set red up
587	$IP -4 route add vrf red unreachable default metric 4278198272
588	$IP -6 route add vrf red unreachable default metric 4278198272
589
590	$IP link add veth0 type veth peer name veth1
591	$IP link set dev veth0 up
592	$IP address add 192.0.2.1/24 dev veth0
593	$IP -6 address add 2001:db8:51::1/64 dev veth0
594
595	$IP link set dev veth1 vrf red up
596	$IP address add 192.0.2.2/24 dev veth1
597	$IP -6 address add 2001:db8:51::2/64 dev veth1
598
599	$IP link add dummy1 type dummy
600	$IP link set dev dummy1 vrf red up
601	$IP address add 192.168.2.1/24 dev dummy1
602	$IP -6 address add 2001:db8:2::1/64 dev dummy1
603	set +e
604
605	sleep 1
606	fib4_nexthop
607	fib6_nexthop
608
609	(
610	$IP link del dev dummy1
611	$IP link del veth0
612	$IP link del red
613	) 2>/dev/null
614	cleanup
615}
616
617################################################################################
618# Tests on route add and replace
619
620run_cmd()
621{
622	local cmd="$1"
623	local out
624	local stderr="2>/dev/null"
625
626	if [ "$VERBOSE" = "1" ]; then
627		printf "    COMMAND: $cmd\n"
628		stderr=
629	fi
630
631	out=$(eval $cmd $stderr)
632	rc=$?
633	if [ "$VERBOSE" = "1" -a -n "$out" ]; then
634		echo "    $out"
635	fi
636
637	[ "$VERBOSE" = "1" ] && echo
638
639	return $rc
640}
641
642check_expected()
643{
644	local out="$1"
645	local expected="$2"
646	local rc=0
647
648	[ "${out}" = "${expected}" ] && return 0
649
650	if [ -z "${out}" ]; then
651		if [ "$VERBOSE" = "1" ]; then
652			printf "\nNo route entry found\n"
653			printf "Expected:\n"
654			printf "    ${expected}\n"
655		fi
656		return 1
657	fi
658
659	# tricky way to convert output to 1-line without ip's
660	# messy '\'; this drops all extra white space
661	out=$(echo ${out})
662	if [ "${out}" != "${expected}" ]; then
663		rc=1
664		if [ "${VERBOSE}" = "1" ]; then
665			printf "    Unexpected route entry. Have:\n"
666			printf "        ${out}\n"
667			printf "    Expected:\n"
668			printf "        ${expected}\n\n"
669		fi
670	fi
671
672	return $rc
673}
674
675# add route for a prefix, flushing any existing routes first
676# expected to be the first step of a test
677add_route6()
678{
679	local pfx="$1"
680	local nh="$2"
681	local out
682
683	if [ "$VERBOSE" = "1" ]; then
684		echo
685		echo "    ##################################################"
686		echo
687	fi
688
689	run_cmd "$IP -6 ro flush ${pfx}"
690	[ $? -ne 0 ] && exit 1
691
692	out=$($IP -6 ro ls match ${pfx})
693	if [ -n "$out" ]; then
694		echo "Failed to flush routes for prefix used for tests."
695		exit 1
696	fi
697
698	run_cmd "$IP -6 ro add ${pfx} ${nh}"
699	if [ $? -ne 0 ]; then
700		echo "Failed to add initial route for test."
701		exit 1
702	fi
703}
704
705# add initial route - used in replace route tests
706add_initial_route6()
707{
708	add_route6 "2001:db8:104::/64" "$1"
709}
710
711check_route6()
712{
713	local pfx
714	local expected="$1"
715	local out
716	local rc=0
717
718	set -- $expected
719	pfx=$1
720
721	out=$($IP -6 ro ls match ${pfx} | sed -e 's/ pref medium//')
722	check_expected "${out}" "${expected}"
723}
724
725route_cleanup()
726{
727	$IP li del red 2>/dev/null
728	$IP li del dummy1 2>/dev/null
729	$IP li del veth1 2>/dev/null
730	$IP li del veth3 2>/dev/null
731
732	cleanup &> /dev/null
733}
734
735route_setup()
736{
737	route_cleanup
738	setup
739
740	[ "${VERBOSE}" = "1" ] && set -x
741	set -e
742
743	ip netns add ns2
744	ip netns set ns2 auto
745	ip -netns ns2 link set dev lo up
746	ip netns exec ns2 sysctl -qw net.ipv4.ip_forward=1
747	ip netns exec ns2 sysctl -qw net.ipv6.conf.all.forwarding=1
748
749	$IP li add veth1 type veth peer name veth2
750	$IP li add veth3 type veth peer name veth4
751
752	$IP li set veth1 up
753	$IP li set veth3 up
754	$IP li set veth2 netns ns2 up
755	$IP li set veth4 netns ns2 up
756	ip -netns ns2 li add dummy1 type dummy
757	ip -netns ns2 li set dummy1 up
758
759	$IP -6 addr add 2001:db8:101::1/64 dev veth1 nodad
760	$IP -6 addr add 2001:db8:103::1/64 dev veth3 nodad
761	$IP addr add 172.16.101.1/24 dev veth1
762	$IP addr add 172.16.103.1/24 dev veth3
763
764	ip -netns ns2 -6 addr add 2001:db8:101::2/64 dev veth2 nodad
765	ip -netns ns2 -6 addr add 2001:db8:103::2/64 dev veth4 nodad
766	ip -netns ns2 -6 addr add 2001:db8:104::1/64 dev dummy1 nodad
767
768	ip -netns ns2 addr add 172.16.101.2/24 dev veth2
769	ip -netns ns2 addr add 172.16.103.2/24 dev veth4
770	ip -netns ns2 addr add 172.16.104.1/24 dev dummy1
771
772	set +e
773}
774
775# assumption is that basic add of a single path route works
776# otherwise just adding an address on an interface is broken
777ipv6_rt_add()
778{
779	local rc
780
781	echo
782	echo "IPv6 route add / append tests"
783
784	# route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
785	add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
786	run_cmd "$IP -6 ro add 2001:db8:104::/64 via 2001:db8:103::2"
787	log_test $? 2 "Attempt to add duplicate route - gw"
788
789	# route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
790	add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
791	run_cmd "$IP -6 ro add 2001:db8:104::/64 dev veth3"
792	log_test $? 2 "Attempt to add duplicate route - dev only"
793
794	# route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
795	add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
796	run_cmd "$IP -6 ro add unreachable 2001:db8:104::/64"
797	log_test $? 2 "Attempt to add duplicate route - reject route"
798
799	# route append with same prefix adds a new route
800	# - iproute2 sets NLM_F_CREATE | NLM_F_APPEND
801	add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
802	run_cmd "$IP -6 ro append 2001:db8:104::/64 via 2001:db8:103::2"
803	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"
804	log_test $? 0 "Append nexthop to existing route - gw"
805
806	# insert mpath directly
807	add_route6 "2001:db8:104::/64" "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
808	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"
809	log_test $? 0 "Add multipath route"
810
811	add_route6 "2001:db8:104::/64" "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
812	run_cmd "$IP -6 ro add 2001:db8:104::/64 nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
813	log_test $? 2 "Attempt to add duplicate multipath route"
814
815	# insert of a second route without append but different metric
816	add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
817	run_cmd "$IP -6 ro add 2001:db8:104::/64 via 2001:db8:103::2 metric 512"
818	rc=$?
819	if [ $rc -eq 0 ]; then
820		run_cmd "$IP -6 ro add 2001:db8:104::/64 via 2001:db8:103::3 metric 256"
821		rc=$?
822	fi
823	log_test $rc 0 "Route add with different metrics"
824
825	run_cmd "$IP -6 ro del 2001:db8:104::/64 metric 512"
826	rc=$?
827	if [ $rc -eq 0 ]; then
828		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"
829		rc=$?
830	fi
831	log_test $rc 0 "Route delete with metric"
832}
833
834ipv6_rt_replace_single()
835{
836	# single path with single path
837	#
838	add_initial_route6 "via 2001:db8:101::2"
839	run_cmd "$IP -6 ro replace 2001:db8:104::/64 via 2001:db8:103::2"
840	check_route6 "2001:db8:104::/64 via 2001:db8:103::2 dev veth3 metric 1024"
841	log_test $? 0 "Single path with single path"
842
843	# single path with multipath
844	#
845	add_initial_route6 "nexthop via 2001:db8:101::2"
846	run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:103::2"
847	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"
848	log_test $? 0 "Single path with multipath"
849
850	# single path with single path using MULTIPATH attribute
851	#
852	add_initial_route6 "via 2001:db8:101::2"
853	run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:103::2"
854	check_route6 "2001:db8:104::/64 via 2001:db8:103::2 dev veth3 metric 1024"
855	log_test $? 0 "Single path with single path via multipath attribute"
856
857	# route replace fails - invalid nexthop
858	add_initial_route6 "via 2001:db8:101::2"
859	run_cmd "$IP -6 ro replace 2001:db8:104::/64 via 2001:db8:104::2"
860	if [ $? -eq 0 ]; then
861		# previous command is expected to fail so if it returns 0
862		# that means the test failed.
863		log_test 0 1 "Invalid nexthop"
864	else
865		check_route6 "2001:db8:104::/64 via 2001:db8:101::2 dev veth1 metric 1024"
866		log_test $? 0 "Invalid nexthop"
867	fi
868
869	# replace non-existent route
870	# - note use of change versus replace since ip adds NLM_F_CREATE
871	#   for replace
872	add_initial_route6 "via 2001:db8:101::2"
873	run_cmd "$IP -6 ro change 2001:db8:105::/64 via 2001:db8:101::2"
874	log_test $? 2 "Single path - replace of non-existent route"
875}
876
877ipv6_rt_replace_mpath()
878{
879	# multipath with multipath
880	add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
881	run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:103::3"
882	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"
883	log_test $? 0 "Multipath with multipath"
884
885	# multipath with single
886	add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
887	run_cmd "$IP -6 ro replace 2001:db8:104::/64 via 2001:db8:101::3"
888	check_route6  "2001:db8:104::/64 via 2001:db8:101::3 dev veth1 metric 1024"
889	log_test $? 0 "Multipath with single path"
890
891	# multipath with single
892	add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
893	run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3"
894	check_route6 "2001:db8:104::/64 via 2001:db8:101::3 dev veth1 metric 1024"
895	log_test $? 0 "Multipath with single path via multipath attribute"
896
897	# route replace fails - invalid nexthop 1
898	add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
899	run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:111::3 nexthop via 2001:db8:103::3"
900	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"
901	log_test $? 0 "Multipath - invalid first nexthop"
902
903	# route replace fails - invalid nexthop 2
904	add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
905	run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:113::3"
906	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"
907	log_test $? 0 "Multipath - invalid second nexthop"
908
909	# multipath non-existent route
910	add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
911	run_cmd "$IP -6 ro change 2001:db8:105::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:103::3"
912	log_test $? 2 "Multipath - replace of non-existent route"
913}
914
915ipv6_rt_replace()
916{
917	echo
918	echo "IPv6 route replace tests"
919
920	ipv6_rt_replace_single
921	ipv6_rt_replace_mpath
922}
923
924ipv6_route_test()
925{
926	route_setup
927
928	ipv6_rt_add
929	ipv6_rt_replace
930
931	route_cleanup
932}
933
934ip_addr_metric_check()
935{
936	ip addr help 2>&1 | grep -q metric
937	if [ $? -ne 0 ]; then
938		echo "iproute2 command does not support metric for addresses. Skipping test"
939		return 1
940	fi
941
942	return 0
943}
944
945ipv6_addr_metric_test()
946{
947	local rc
948
949	echo
950	echo "IPv6 prefix route tests"
951
952	ip_addr_metric_check || return 1
953
954	setup
955
956	set -e
957	$IP li add dummy1 type dummy
958	$IP li add dummy2 type dummy
959	$IP li set dummy1 up
960	$IP li set dummy2 up
961
962	# default entry is metric 256
963	run_cmd "$IP -6 addr add dev dummy1 2001:db8:104::1/64"
964	run_cmd "$IP -6 addr add dev dummy2 2001:db8:104::2/64"
965	set +e
966
967	check_route6 "2001:db8:104::/64 dev dummy1 proto kernel metric 256 2001:db8:104::/64 dev dummy2 proto kernel metric 256"
968	log_test $? 0 "Default metric"
969
970	set -e
971	run_cmd "$IP -6 addr flush dev dummy1"
972	run_cmd "$IP -6 addr add dev dummy1 2001:db8:104::1/64 metric 257"
973	set +e
974
975	check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 256 2001:db8:104::/64 dev dummy1 proto kernel metric 257"
976	log_test $? 0 "User specified metric on first device"
977
978	set -e
979	run_cmd "$IP -6 addr flush dev dummy2"
980	run_cmd "$IP -6 addr add dev dummy2 2001:db8:104::2/64 metric 258"
981	set +e
982
983	check_route6 "2001:db8:104::/64 dev dummy1 proto kernel metric 257 2001:db8:104::/64 dev dummy2 proto kernel metric 258"
984	log_test $? 0 "User specified metric on second device"
985
986	run_cmd "$IP -6 addr del dev dummy1 2001:db8:104::1/64 metric 257"
987	rc=$?
988	if [ $rc -eq 0 ]; then
989		check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 258"
990		rc=$?
991	fi
992	log_test $rc 0 "Delete of address on first device"
993
994	run_cmd "$IP -6 addr change dev dummy2 2001:db8:104::2/64 metric 259"
995	rc=$?
996	if [ $rc -eq 0 ]; then
997		check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 259"
998		rc=$?
999	fi
1000	log_test $rc 0 "Modify metric of address"
1001
1002	# verify prefix route removed on down
1003	run_cmd "ip netns exec ns1 sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1"
1004	run_cmd "$IP li set dev dummy2 down"
1005	rc=$?
1006	if [ $rc -eq 0 ]; then
1007		out=$($IP -6 ro ls match 2001:db8:104::/64)
1008		check_expected "${out}" ""
1009		rc=$?
1010	fi
1011	log_test $rc 0 "Prefix route removed on link down"
1012
1013	# verify prefix route re-inserted with assigned metric
1014	run_cmd "$IP li set dev dummy2 up"
1015	rc=$?
1016	if [ $rc -eq 0 ]; then
1017		check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 259"
1018		rc=$?
1019	fi
1020	log_test $rc 0 "Prefix route with metric on link up"
1021
1022	$IP li del dummy1
1023	$IP li del dummy2
1024	cleanup
1025}
1026
1027ipv6_route_metrics_test()
1028{
1029	local rc
1030
1031	echo
1032	echo "IPv6 routes with metrics"
1033
1034	route_setup
1035
1036	#
1037	# single path with metrics
1038	#
1039	run_cmd "$IP -6 ro add 2001:db8:111::/64 via 2001:db8:101::2 mtu 1400"
1040	rc=$?
1041	if [ $rc -eq 0 ]; then
1042		check_route6  "2001:db8:111::/64 via 2001:db8:101::2 dev veth1 metric 1024 mtu 1400"
1043		rc=$?
1044	fi
1045	log_test $rc 0 "Single path route with mtu metric"
1046
1047
1048	#
1049	# multipath via separate routes with metrics
1050	#
1051	run_cmd "$IP -6 ro add 2001:db8:112::/64 via 2001:db8:101::2 mtu 1400"
1052	run_cmd "$IP -6 ro append 2001:db8:112::/64 via 2001:db8:103::2"
1053	rc=$?
1054	if [ $rc -eq 0 ]; then
1055		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"
1056		rc=$?
1057	fi
1058	log_test $rc 0 "Multipath route via 2 single routes with mtu metric on first"
1059
1060	# second route is coalesced to first to make a multipath route.
1061	# MTU of the second path is hidden from display!
1062	run_cmd "$IP -6 ro add 2001:db8:113::/64 via 2001:db8:101::2"
1063	run_cmd "$IP -6 ro append 2001:db8:113::/64 via 2001:db8:103::2 mtu 1400"
1064	rc=$?
1065	if [ $rc -eq 0 ]; then
1066		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"
1067		rc=$?
1068	fi
1069	log_test $rc 0 "Multipath route via 2 single routes with mtu metric on 2nd"
1070
1071	run_cmd "$IP -6 ro del 2001:db8:113::/64 via 2001:db8:101::2"
1072	if [ $? -eq 0 ]; then
1073		check_route6 "2001:db8:113::/64 via 2001:db8:103::2 dev veth3 metric 1024 mtu 1400"
1074		log_test $? 0 "    MTU of second leg"
1075	fi
1076
1077	#
1078	# multipath with metrics
1079	#
1080	run_cmd "$IP -6 ro add 2001:db8:115::/64 mtu 1400 nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
1081	rc=$?
1082	if [ $rc -eq 0 ]; then
1083		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"
1084		rc=$?
1085	fi
1086	log_test $rc 0 "Multipath route with mtu metric"
1087
1088	$IP -6 ro add 2001:db8:104::/64 via 2001:db8:101::2 mtu 1300
1089	run_cmd "ip netns exec ns1 ping6 -w1 -c1 -s 1500 2001:db8:104::1"
1090	log_test $? 0 "Using route with mtu metric"
1091
1092	run_cmd "$IP -6 ro add 2001:db8:114::/64 via  2001:db8:101::2  congctl lock foo"
1093	log_test $? 2 "Invalid metric (fails metric_convert)"
1094
1095	route_cleanup
1096}
1097
1098# add route for a prefix, flushing any existing routes first
1099# expected to be the first step of a test
1100add_route()
1101{
1102	local pfx="$1"
1103	local nh="$2"
1104	local out
1105
1106	if [ "$VERBOSE" = "1" ]; then
1107		echo
1108		echo "    ##################################################"
1109		echo
1110	fi
1111
1112	run_cmd "$IP ro flush ${pfx}"
1113	[ $? -ne 0 ] && exit 1
1114
1115	out=$($IP ro ls match ${pfx})
1116	if [ -n "$out" ]; then
1117		echo "Failed to flush routes for prefix used for tests."
1118		exit 1
1119	fi
1120
1121	run_cmd "$IP ro add ${pfx} ${nh}"
1122	if [ $? -ne 0 ]; then
1123		echo "Failed to add initial route for test."
1124		exit 1
1125	fi
1126}
1127
1128# add initial route - used in replace route tests
1129add_initial_route()
1130{
1131	add_route "172.16.104.0/24" "$1"
1132}
1133
1134check_route()
1135{
1136	local pfx
1137	local expected="$1"
1138	local out
1139
1140	set -- $expected
1141	pfx=$1
1142	[ "${pfx}" = "unreachable" ] && pfx=$2
1143
1144	out=$($IP ro ls match ${pfx})
1145	check_expected "${out}" "${expected}"
1146}
1147
1148# assumption is that basic add of a single path route works
1149# otherwise just adding an address on an interface is broken
1150ipv4_rt_add()
1151{
1152	local rc
1153
1154	echo
1155	echo "IPv4 route add / append tests"
1156
1157	# route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
1158	add_route "172.16.104.0/24" "via 172.16.101.2"
1159	run_cmd "$IP ro add 172.16.104.0/24 via 172.16.103.2"
1160	log_test $? 2 "Attempt to add duplicate route - gw"
1161
1162	# route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
1163	add_route "172.16.104.0/24" "via 172.16.101.2"
1164	run_cmd "$IP ro add 172.16.104.0/24 dev veth3"
1165	log_test $? 2 "Attempt to add duplicate route - dev only"
1166
1167	# route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
1168	add_route "172.16.104.0/24" "via 172.16.101.2"
1169	run_cmd "$IP ro add unreachable 172.16.104.0/24"
1170	log_test $? 2 "Attempt to add duplicate route - reject route"
1171
1172	# iproute2 prepend only sets NLM_F_CREATE
1173	# - adds a new route; does NOT convert existing route to ECMP
1174	add_route "172.16.104.0/24" "via 172.16.101.2"
1175	run_cmd "$IP ro prepend 172.16.104.0/24 via 172.16.103.2"
1176	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"
1177	log_test $? 0 "Add new nexthop for existing prefix"
1178
1179	# route append with same prefix adds a new route
1180	# - iproute2 sets NLM_F_CREATE | NLM_F_APPEND
1181	add_route "172.16.104.0/24" "via 172.16.101.2"
1182	run_cmd "$IP ro append 172.16.104.0/24 via 172.16.103.2"
1183	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"
1184	log_test $? 0 "Append nexthop to existing route - gw"
1185
1186	add_route "172.16.104.0/24" "via 172.16.101.2"
1187	run_cmd "$IP ro append 172.16.104.0/24 dev veth3"
1188	check_route "172.16.104.0/24 via 172.16.101.2 dev veth1 172.16.104.0/24 dev veth3 scope link"
1189	log_test $? 0 "Append nexthop to existing route - dev only"
1190
1191	add_route "172.16.104.0/24" "via 172.16.101.2"
1192	run_cmd "$IP ro append unreachable 172.16.104.0/24"
1193	check_route "172.16.104.0/24 via 172.16.101.2 dev veth1 unreachable 172.16.104.0/24"
1194	log_test $? 0 "Append nexthop to existing route - reject route"
1195
1196	run_cmd "$IP ro flush 172.16.104.0/24"
1197	run_cmd "$IP ro add unreachable 172.16.104.0/24"
1198	run_cmd "$IP ro append 172.16.104.0/24 via 172.16.103.2"
1199	check_route "unreachable 172.16.104.0/24 172.16.104.0/24 via 172.16.103.2 dev veth3"
1200	log_test $? 0 "Append nexthop to existing reject route - gw"
1201
1202	run_cmd "$IP ro flush 172.16.104.0/24"
1203	run_cmd "$IP ro add unreachable 172.16.104.0/24"
1204	run_cmd "$IP ro append 172.16.104.0/24 dev veth3"
1205	check_route "unreachable 172.16.104.0/24 172.16.104.0/24 dev veth3 scope link"
1206	log_test $? 0 "Append nexthop to existing reject route - dev only"
1207
1208	# insert mpath directly
1209	add_route "172.16.104.0/24" "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1210	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"
1211	log_test $? 0 "add multipath route"
1212
1213	add_route "172.16.104.0/24" "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1214	run_cmd "$IP ro add 172.16.104.0/24 nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1215	log_test $? 2 "Attempt to add duplicate multipath route"
1216
1217	# insert of a second route without append but different metric
1218	add_route "172.16.104.0/24" "via 172.16.101.2"
1219	run_cmd "$IP ro add 172.16.104.0/24 via 172.16.103.2 metric 512"
1220	rc=$?
1221	if [ $rc -eq 0 ]; then
1222		run_cmd "$IP ro add 172.16.104.0/24 via 172.16.103.3 metric 256"
1223		rc=$?
1224	fi
1225	log_test $rc 0 "Route add with different metrics"
1226
1227	run_cmd "$IP ro del 172.16.104.0/24 metric 512"
1228	rc=$?
1229	if [ $rc -eq 0 ]; then
1230		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"
1231		rc=$?
1232	fi
1233	log_test $rc 0 "Route delete with metric"
1234}
1235
1236ipv4_rt_replace_single()
1237{
1238	# single path with single path
1239	#
1240	add_initial_route "via 172.16.101.2"
1241	run_cmd "$IP ro replace 172.16.104.0/24 via 172.16.103.2"
1242	check_route "172.16.104.0/24 via 172.16.103.2 dev veth3"
1243	log_test $? 0 "Single path with single path"
1244
1245	# single path with multipath
1246	#
1247	add_initial_route "nexthop via 172.16.101.2"
1248	run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3 nexthop via 172.16.103.2"
1249	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"
1250	log_test $? 0 "Single path with multipath"
1251
1252	# single path with reject
1253	#
1254	add_initial_route "nexthop via 172.16.101.2"
1255	run_cmd "$IP ro replace unreachable 172.16.104.0/24"
1256	check_route "unreachable 172.16.104.0/24"
1257	log_test $? 0 "Single path with reject route"
1258
1259	# single path with single path using MULTIPATH attribute
1260	#
1261	add_initial_route "via 172.16.101.2"
1262	run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.103.2"
1263	check_route "172.16.104.0/24 via 172.16.103.2 dev veth3"
1264	log_test $? 0 "Single path with single path via multipath attribute"
1265
1266	# route replace fails - invalid nexthop
1267	add_initial_route "via 172.16.101.2"
1268	run_cmd "$IP ro replace 172.16.104.0/24 via 2001:db8:104::2"
1269	if [ $? -eq 0 ]; then
1270		# previous command is expected to fail so if it returns 0
1271		# that means the test failed.
1272		log_test 0 1 "Invalid nexthop"
1273	else
1274		check_route "172.16.104.0/24 via 172.16.101.2 dev veth1"
1275		log_test $? 0 "Invalid nexthop"
1276	fi
1277
1278	# replace non-existent route
1279	# - note use of change versus replace since ip adds NLM_F_CREATE
1280	#   for replace
1281	add_initial_route "via 172.16.101.2"
1282	run_cmd "$IP ro change 172.16.105.0/24 via 172.16.101.2"
1283	log_test $? 2 "Single path - replace of non-existent route"
1284}
1285
1286ipv4_rt_replace_mpath()
1287{
1288	# multipath with multipath
1289	add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1290	run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3 nexthop via 172.16.103.3"
1291	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"
1292	log_test $? 0 "Multipath with multipath"
1293
1294	# multipath with single
1295	add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1296	run_cmd "$IP ro replace 172.16.104.0/24 via 172.16.101.3"
1297	check_route  "172.16.104.0/24 via 172.16.101.3 dev veth1"
1298	log_test $? 0 "Multipath with single path"
1299
1300	# multipath with single
1301	add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1302	run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3"
1303	check_route "172.16.104.0/24 via 172.16.101.3 dev veth1"
1304	log_test $? 0 "Multipath with single path via multipath attribute"
1305
1306	# multipath with reject
1307	add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1308	run_cmd "$IP ro replace unreachable 172.16.104.0/24"
1309	check_route "unreachable 172.16.104.0/24"
1310	log_test $? 0 "Multipath with reject route"
1311
1312	# route replace fails - invalid nexthop 1
1313	add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1314	run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.111.3 nexthop via 172.16.103.3"
1315	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"
1316	log_test $? 0 "Multipath - invalid first nexthop"
1317
1318	# route replace fails - invalid nexthop 2
1319	add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1320	run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3 nexthop via 172.16.113.3"
1321	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"
1322	log_test $? 0 "Multipath - invalid second nexthop"
1323
1324	# multipath non-existent route
1325	add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1326	run_cmd "$IP ro change 172.16.105.0/24 nexthop via 172.16.101.3 nexthop via 172.16.103.3"
1327	log_test $? 2 "Multipath - replace of non-existent route"
1328}
1329
1330ipv4_rt_replace()
1331{
1332	echo
1333	echo "IPv4 route replace tests"
1334
1335	ipv4_rt_replace_single
1336	ipv4_rt_replace_mpath
1337}
1338
1339ipv4_route_test()
1340{
1341	route_setup
1342
1343	ipv4_rt_add
1344	ipv4_rt_replace
1345
1346	route_cleanup
1347}
1348
1349ipv4_addr_metric_test()
1350{
1351	local rc
1352
1353	echo
1354	echo "IPv4 prefix route tests"
1355
1356	ip_addr_metric_check || return 1
1357
1358	setup
1359
1360	set -e
1361	$IP li add dummy1 type dummy
1362	$IP li add dummy2 type dummy
1363	$IP li set dummy1 up
1364	$IP li set dummy2 up
1365
1366	# default entry is metric 256
1367	run_cmd "$IP addr add dev dummy1 172.16.104.1/24"
1368	run_cmd "$IP addr add dev dummy2 172.16.104.2/24"
1369	set +e
1370
1371	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"
1372	log_test $? 0 "Default metric"
1373
1374	set -e
1375	run_cmd "$IP addr flush dev dummy1"
1376	run_cmd "$IP addr add dev dummy1 172.16.104.1/24 metric 257"
1377	set +e
1378
1379	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"
1380	log_test $? 0 "User specified metric on first device"
1381
1382	set -e
1383	run_cmd "$IP addr flush dev dummy2"
1384	run_cmd "$IP addr add dev dummy2 172.16.104.2/24 metric 258"
1385	set +e
1386
1387	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"
1388	log_test $? 0 "User specified metric on second device"
1389
1390	run_cmd "$IP addr del dev dummy1 172.16.104.1/24 metric 257"
1391	rc=$?
1392	if [ $rc -eq 0 ]; then
1393		check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 258"
1394		rc=$?
1395	fi
1396	log_test $rc 0 "Delete of address on first device"
1397
1398	run_cmd "$IP addr change dev dummy2 172.16.104.2/24 metric 259"
1399	rc=$?
1400	if [ $rc -eq 0 ]; then
1401		check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 259"
1402		rc=$?
1403	fi
1404	log_test $rc 0 "Modify metric of address"
1405
1406	# verify prefix route removed on down
1407	run_cmd "$IP li set dev dummy2 down"
1408	rc=$?
1409	if [ $rc -eq 0 ]; then
1410		out=$($IP ro ls match 172.16.104.0/24)
1411		check_expected "${out}" ""
1412		rc=$?
1413	fi
1414	log_test $rc 0 "Prefix route removed on link down"
1415
1416	# verify prefix route re-inserted with assigned metric
1417	run_cmd "$IP li set dev dummy2 up"
1418	rc=$?
1419	if [ $rc -eq 0 ]; then
1420		check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 259"
1421		rc=$?
1422	fi
1423	log_test $rc 0 "Prefix route with metric on link up"
1424
1425	$IP li del dummy1
1426	$IP li del dummy2
1427	cleanup
1428}
1429
1430ipv4_route_metrics_test()
1431{
1432	local rc
1433
1434	echo
1435	echo "IPv4 route add / append tests"
1436
1437	route_setup
1438
1439	run_cmd "$IP ro add 172.16.111.0/24 via 172.16.101.2 mtu 1400"
1440	rc=$?
1441	if [ $rc -eq 0 ]; then
1442		check_route "172.16.111.0/24 via 172.16.101.2 dev veth1 mtu 1400"
1443		rc=$?
1444	fi
1445	log_test $rc 0 "Single path route with mtu metric"
1446
1447
1448	run_cmd "$IP ro add 172.16.112.0/24 mtu 1400 nexthop via 172.16.101.2 nexthop via 172.16.103.2"
1449	rc=$?
1450	if [ $rc -eq 0 ]; then
1451		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"
1452		rc=$?
1453	fi
1454	log_test $rc 0 "Multipath route with mtu metric"
1455
1456	$IP ro add 172.16.104.0/24 via 172.16.101.2 mtu 1300
1457	run_cmd "ip netns exec ns1 ping -w1 -c1 -s 1500 172.16.104.1"
1458	log_test $? 0 "Using route with mtu metric"
1459
1460	run_cmd "$IP ro add 172.16.111.0/24 via 172.16.101.2 congctl lock foo"
1461	log_test $? 2 "Invalid metric (fails metric_convert)"
1462
1463	route_cleanup
1464}
1465
1466ipv4_route_v6_gw_test()
1467{
1468	local rc
1469
1470	echo
1471	echo "IPv4 route with IPv6 gateway tests"
1472
1473	route_setup
1474	sleep 2
1475
1476	#
1477	# single path route
1478	#
1479	run_cmd "$IP ro add 172.16.104.0/24 via inet6 2001:db8:101::2"
1480	rc=$?
1481	log_test $rc 0 "Single path route with IPv6 gateway"
1482	if [ $rc -eq 0 ]; then
1483		check_route "172.16.104.0/24 via inet6 2001:db8:101::2 dev veth1"
1484	fi
1485
1486	run_cmd "ip netns exec ns1 ping -w1 -c1 172.16.104.1"
1487	log_test $rc 0 "Single path route with IPv6 gateway - ping"
1488
1489	run_cmd "$IP ro del 172.16.104.0/24 via inet6 2001:db8:101::2"
1490	rc=$?
1491	log_test $rc 0 "Single path route delete"
1492	if [ $rc -eq 0 ]; then
1493		check_route "172.16.112.0/24"
1494	fi
1495
1496	#
1497	# multipath - v6 then v4
1498	#
1499	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"
1500	rc=$?
1501	log_test $rc 0 "Multipath route add - v6 nexthop then v4"
1502	if [ $rc -eq 0 ]; then
1503		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"
1504	fi
1505
1506	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"
1507	log_test $? 2 "    Multipath route delete - nexthops in wrong order"
1508
1509	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"
1510	log_test $? 0 "    Multipath route delete exact match"
1511
1512	#
1513	# multipath - v4 then v6
1514	#
1515	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"
1516	rc=$?
1517	log_test $rc 0 "Multipath route add - v4 nexthop then v6"
1518	if [ $rc -eq 0 ]; then
1519		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"
1520	fi
1521
1522	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"
1523	log_test $? 2 "    Multipath route delete - nexthops in wrong order"
1524
1525	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"
1526	log_test $? 0 "    Multipath route delete exact match"
1527
1528	route_cleanup
1529}
1530
1531################################################################################
1532# usage
1533
1534usage()
1535{
1536	cat <<EOF
1537usage: ${0##*/} OPTS
1538
1539        -t <test>   Test(s) to run (default: all)
1540                    (options: $TESTS)
1541        -p          Pause on fail
1542        -P          Pause after each test before cleanup
1543        -v          verbose mode (show commands and output)
1544EOF
1545}
1546
1547################################################################################
1548# main
1549
1550while getopts :t:pPhv o
1551do
1552	case $o in
1553		t) TESTS=$OPTARG;;
1554		p) PAUSE_ON_FAIL=yes;;
1555		P) PAUSE=yes;;
1556		v) VERBOSE=$(($VERBOSE + 1));;
1557		h) usage; exit 0;;
1558		*) usage; exit 1;;
1559	esac
1560done
1561
1562PEER_CMD="ip netns exec ${PEER_NS}"
1563
1564# make sure we don't pause twice
1565[ "${PAUSE}" = "yes" ] && PAUSE_ON_FAIL=no
1566
1567if [ "$(id -u)" -ne 0 ];then
1568	echo "SKIP: Need root privileges"
1569	exit $ksft_skip;
1570fi
1571
1572if [ ! -x "$(command -v ip)" ]; then
1573	echo "SKIP: Could not run test without ip tool"
1574	exit $ksft_skip
1575fi
1576
1577ip route help 2>&1 | grep -q fibmatch
1578if [ $? -ne 0 ]; then
1579	echo "SKIP: iproute2 too old, missing fibmatch"
1580	exit $ksft_skip
1581fi
1582
1583# start clean
1584cleanup &> /dev/null
1585
1586for t in $TESTS
1587do
1588	case $t in
1589	fib_unreg_test|unregister)	fib_unreg_test;;
1590	fib_down_test|down)		fib_down_test;;
1591	fib_carrier_test|carrier)	fib_carrier_test;;
1592	fib_rp_filter_test|rp_filter)	fib_rp_filter_test;;
1593	fib_nexthop_test|nexthop)	fib_nexthop_test;;
1594	ipv6_route_test|ipv6_rt)	ipv6_route_test;;
1595	ipv4_route_test|ipv4_rt)	ipv4_route_test;;
1596	ipv6_addr_metric)		ipv6_addr_metric_test;;
1597	ipv4_addr_metric)		ipv4_addr_metric_test;;
1598	ipv6_route_metrics)		ipv6_route_metrics_test;;
1599	ipv4_route_metrics)		ipv4_route_metrics_test;;
1600	ipv4_route_v6_gw)		ipv4_route_v6_gw_test;;
1601
1602	help) echo "Test names: $TESTS"; exit 0;;
1603	esac
1604done
1605
1606if [ "$TESTS" != "none" ]; then
1607	printf "\nTests passed: %3d\n" ${nsuccess}
1608	printf "Tests failed: %3d\n"   ${nfail}
1609fi
1610
1611exit $ret
1612