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