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