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