1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# ns: me               | ns: peer              | ns: remote
5#   2001:db8:91::1     |       2001:db8:91::2  |
6#   172.16.1.1         |       172.16.1.2      |
7#            veth1 <---|---> veth2             |
8#                      |              veth5 <--|--> veth6  172.16.101.1
9#            veth3 <---|---> veth4             |           2001:db8:101::1
10#   172.16.2.1         |       172.16.2.2      |
11#   2001:db8:92::1     |       2001:db8:92::2  |
12#
13# This test is for checking IPv4 and IPv6 FIB behavior with nexthop
14# objects. Device reference counts and network namespace cleanup tested
15# by use of network namespace for peer.
16
17ret=0
18# Kselftest framework requirement - SKIP code is 4.
19ksft_skip=4
20
21# all tests in this script. Can be overridden with -t option
22IPV4_TESTS="
23	ipv4_fcnal
24	ipv4_grp_fcnal
25	ipv4_res_grp_fcnal
26	ipv4_withv6_fcnal
27	ipv4_fcnal_runtime
28	ipv4_large_grp
29	ipv4_large_res_grp
30	ipv4_compat_mode
31	ipv4_fdb_grp_fcnal
32	ipv4_torture
33	ipv4_res_torture
34"
35
36IPV6_TESTS="
37	ipv6_fcnal
38	ipv6_grp_fcnal
39	ipv6_res_grp_fcnal
40	ipv6_fcnal_runtime
41	ipv6_large_grp
42	ipv6_large_res_grp
43	ipv6_compat_mode
44	ipv6_fdb_grp_fcnal
45	ipv6_torture
46	ipv6_res_torture
47"
48
49ALL_TESTS="
50	basic
51	basic_res
52	${IPV4_TESTS}
53	${IPV6_TESTS}
54"
55TESTS="${ALL_TESTS}"
56VERBOSE=0
57PAUSE_ON_FAIL=no
58PAUSE=no
59
60nsid=100
61
62################################################################################
63# utilities
64
65log_test()
66{
67	local rc=$1
68	local expected=$2
69	local msg="$3"
70
71	if [ ${rc} -eq ${expected} ]; then
72		printf "TEST: %-60s  [ OK ]\n" "${msg}"
73		nsuccess=$((nsuccess+1))
74	else
75		ret=1
76		nfail=$((nfail+1))
77		printf "TEST: %-60s  [FAIL]\n" "${msg}"
78		if [ "$VERBOSE" = "1" ]; then
79			echo "    rc=$rc, expected $expected"
80		fi
81
82		if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
83		echo
84			echo "hit enter to continue, 'q' to quit"
85			read a
86			[ "$a" = "q" ] && exit 1
87		fi
88	fi
89
90	if [ "${PAUSE}" = "yes" ]; then
91		echo
92		echo "hit enter to continue, 'q' to quit"
93		read a
94		[ "$a" = "q" ] && exit 1
95	fi
96
97	[ "$VERBOSE" = "1" ] && echo
98}
99
100run_cmd()
101{
102	local cmd="$1"
103	local out
104	local stderr="2>/dev/null"
105
106	if [ "$VERBOSE" = "1" ]; then
107		printf "COMMAND: $cmd\n"
108		stderr=
109	fi
110
111	out=$(eval $cmd $stderr)
112	rc=$?
113	if [ "$VERBOSE" = "1" -a -n "$out" ]; then
114		echo "    $out"
115	fi
116
117	return $rc
118}
119
120get_linklocal()
121{
122	local dev=$1
123	local ns
124	local addr
125
126	[ -n "$2" ] && ns="-netns $2"
127	addr=$(ip $ns -6 -br addr show dev ${dev} | \
128	awk '{
129		for (i = 3; i <= NF; ++i) {
130			if ($i ~ /^fe80/)
131				print $i
132		}
133	}'
134	)
135	addr=${addr/\/*}
136
137	[ -z "$addr" ] && return 1
138
139	echo $addr
140
141	return 0
142}
143
144create_ns()
145{
146	local n=${1}
147
148	ip netns del ${n} 2>/dev/null
149
150	set -e
151	ip netns add ${n}
152	ip netns set ${n} $((nsid++))
153	ip -netns ${n} addr add 127.0.0.1/8 dev lo
154	ip -netns ${n} link set lo up
155
156	ip netns exec ${n} sysctl -qw net.ipv4.ip_forward=1
157	ip netns exec ${n} sysctl -qw net.ipv4.fib_multipath_use_neigh=1
158	ip netns exec ${n} sysctl -qw net.ipv4.conf.default.ignore_routes_with_linkdown=1
159	ip netns exec ${n} sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1
160	ip netns exec ${n} sysctl -qw net.ipv6.conf.all.forwarding=1
161	ip netns exec ${n} sysctl -qw net.ipv6.conf.default.forwarding=1
162	ip netns exec ${n} sysctl -qw net.ipv6.conf.default.ignore_routes_with_linkdown=1
163	ip netns exec ${n} sysctl -qw net.ipv6.conf.all.accept_dad=0
164	ip netns exec ${n} sysctl -qw net.ipv6.conf.default.accept_dad=0
165
166	set +e
167}
168
169setup()
170{
171	cleanup
172
173	create_ns me
174	create_ns peer
175	create_ns remote
176
177	IP="ip -netns me"
178	BRIDGE="bridge -netns me"
179	set -e
180	$IP li add veth1 type veth peer name veth2
181	$IP li set veth1 up
182	$IP addr add 172.16.1.1/24 dev veth1
183	$IP -6 addr add 2001:db8:91::1/64 dev veth1 nodad
184
185	$IP li add veth3 type veth peer name veth4
186	$IP li set veth3 up
187	$IP addr add 172.16.2.1/24 dev veth3
188	$IP -6 addr add 2001:db8:92::1/64 dev veth3 nodad
189
190	$IP li set veth2 netns peer up
191	ip -netns peer addr add 172.16.1.2/24 dev veth2
192	ip -netns peer -6 addr add 2001:db8:91::2/64 dev veth2 nodad
193
194	$IP li set veth4 netns peer up
195	ip -netns peer addr add 172.16.2.2/24 dev veth4
196	ip -netns peer -6 addr add 2001:db8:92::2/64 dev veth4 nodad
197
198	ip -netns remote li add veth5 type veth peer name veth6
199	ip -netns remote li set veth5 up
200	ip -netns remote addr add dev veth5 172.16.101.1/24
201	ip -netns remote -6 addr add dev veth5 2001:db8:101::1/64 nodad
202	ip -netns remote ro add 172.16.0.0/22 via 172.16.101.2
203	ip -netns remote -6 ro add 2001:db8:90::/40 via 2001:db8:101::2
204
205	ip -netns remote li set veth6 netns peer up
206	ip -netns peer addr add dev veth6 172.16.101.2/24
207	ip -netns peer -6 addr add dev veth6 2001:db8:101::2/64 nodad
208	set +e
209}
210
211cleanup()
212{
213	local ns
214
215	for ns in me peer remote; do
216		ip netns del ${ns} 2>/dev/null
217	done
218}
219
220check_output()
221{
222	local out="$1"
223	local expected="$2"
224	local rc=0
225
226	[ "${out}" = "${expected}" ] && return 0
227
228	if [ -z "${out}" ]; then
229		if [ "$VERBOSE" = "1" ]; then
230			printf "\nNo entry found\n"
231			printf "Expected:\n"
232			printf "    ${expected}\n"
233		fi
234		return 1
235	fi
236
237	out=$(echo ${out})
238	if [ "${out}" != "${expected}" ]; then
239		rc=1
240		if [ "${VERBOSE}" = "1" ]; then
241			printf "    Unexpected entry. Have:\n"
242			printf "        ${out}\n"
243			printf "    Expected:\n"
244			printf "        ${expected}\n\n"
245		else
246			echo "      WARNING: Unexpected route entry"
247		fi
248	fi
249
250	return $rc
251}
252
253check_nexthop()
254{
255	local nharg="$1"
256	local expected="$2"
257	local out
258
259	out=$($IP nexthop ls ${nharg} 2>/dev/null)
260
261	check_output "${out}" "${expected}"
262}
263
264check_nexthop_bucket()
265{
266	local nharg="$1"
267	local expected="$2"
268	local out
269
270	# remove the idle time since we cannot match it
271	out=$($IP nexthop bucket ${nharg} \
272		| sed s/idle_time\ [0-9.]*\ // 2>/dev/null)
273
274	check_output "${out}" "${expected}"
275}
276
277check_route()
278{
279	local pfx="$1"
280	local expected="$2"
281	local out
282
283	out=$($IP route ls match ${pfx} 2>/dev/null)
284
285	check_output "${out}" "${expected}"
286}
287
288check_route6()
289{
290	local pfx="$1"
291	local expected="$2"
292	local out
293
294	out=$($IP -6 route ls match ${pfx} 2>/dev/null | sed -e 's/pref medium//')
295
296	check_output "${out}" "${expected}"
297}
298
299check_large_grp()
300{
301	local ipv=$1
302	local ecmp=$2
303	local grpnum=100
304	local nhidstart=100
305	local grpidstart=1000
306	local iter=0
307	local nhidstr=""
308	local grpidstr=""
309	local grpstr=""
310	local ipstr=""
311
312	if [ $ipv -eq 4 ]; then
313		ipstr="172.16.1."
314	else
315		ipstr="2001:db8:91::"
316	fi
317
318	#
319	# Create $grpnum groups with specified $ecmp and dump them
320	#
321
322	# create nexthops with different gateways
323	iter=2
324	while [ $iter -le $(($ecmp + 1)) ]
325	do
326		nhidstr="$(($nhidstart + $iter))"
327		run_cmd "$IP nexthop add id $nhidstr via $ipstr$iter dev veth1"
328		check_nexthop "id $nhidstr" "id $nhidstr via $ipstr$iter dev veth1 scope link"
329
330		if [ $iter -le $ecmp ]; then
331			grpstr+="$nhidstr/"
332		else
333			grpstr+="$nhidstr"
334		fi
335		((iter++))
336	done
337
338	# create duplicate large ecmp groups
339	iter=0
340	while [ $iter -le $grpnum ]
341	do
342		grpidstr="$(($grpidstart + $iter))"
343		run_cmd "$IP nexthop add id $grpidstr group $grpstr"
344		check_nexthop "id $grpidstr" "id $grpidstr group $grpstr"
345		((iter++))
346	done
347
348	# dump large groups
349	run_cmd "$IP nexthop list"
350	log_test $? 0 "Dump large (x$ecmp) ecmp groups"
351}
352
353check_large_res_grp()
354{
355	local ipv=$1
356	local buckets=$2
357	local ipstr=""
358
359	if [ $ipv -eq 4 ]; then
360		ipstr="172.16.1.2"
361	else
362		ipstr="2001:db8:91::2"
363	fi
364
365	# create a resilient group with $buckets buckets and dump them
366	run_cmd "$IP nexthop add id 100 via $ipstr dev veth1"
367	run_cmd "$IP nexthop add id 1000 group 100 type resilient buckets $buckets"
368	run_cmd "$IP nexthop bucket list"
369	log_test $? 0 "Dump large (x$buckets) nexthop buckets"
370}
371
372start_ip_monitor()
373{
374	local mtype=$1
375
376	# start the monitor in the background
377	tmpfile=`mktemp /var/run/nexthoptestXXX`
378	mpid=`($IP monitor $mtype > $tmpfile & echo $!) 2>/dev/null`
379	sleep 0.2
380	echo "$mpid $tmpfile"
381}
382
383stop_ip_monitor()
384{
385	local mpid=$1
386	local tmpfile=$2
387	local el=$3
388
389	# check the monitor results
390	kill $mpid
391	lines=`wc -l $tmpfile | cut "-d " -f1`
392	test $lines -eq $el
393	rc=$?
394	rm -rf $tmpfile
395
396	return $rc
397}
398
399check_nexthop_fdb_support()
400{
401	$IP nexthop help 2>&1 | grep -q fdb
402	if [ $? -ne 0 ]; then
403		echo "SKIP: iproute2 too old, missing fdb nexthop support"
404		return $ksft_skip
405	fi
406}
407
408check_nexthop_res_support()
409{
410	$IP nexthop help 2>&1 | grep -q resilient
411	if [ $? -ne 0 ]; then
412		echo "SKIP: iproute2 too old, missing resilient nexthop group support"
413		return $ksft_skip
414	fi
415}
416
417ipv6_fdb_grp_fcnal()
418{
419	local rc
420
421	echo
422	echo "IPv6 fdb groups functional"
423	echo "--------------------------"
424
425	check_nexthop_fdb_support
426	if [ $? -eq $ksft_skip ]; then
427		return $ksft_skip
428	fi
429
430	# create group with multiple nexthops
431	run_cmd "$IP nexthop add id 61 via 2001:db8:91::2 fdb"
432	run_cmd "$IP nexthop add id 62 via 2001:db8:91::3 fdb"
433	run_cmd "$IP nexthop add id 102 group 61/62 fdb"
434	check_nexthop "id 102" "id 102 group 61/62 fdb"
435	log_test $? 0 "Fdb Nexthop group with multiple nexthops"
436
437	## get nexthop group
438	run_cmd "$IP nexthop get id 102"
439	check_nexthop "id 102" "id 102 group 61/62 fdb"
440	log_test $? 0 "Get Fdb nexthop group by id"
441
442	# fdb nexthop group can only contain fdb nexthops
443	run_cmd "$IP nexthop add id 63 via 2001:db8:91::4"
444	run_cmd "$IP nexthop add id 64 via 2001:db8:91::5"
445	run_cmd "$IP nexthop add id 103 group 63/64 fdb"
446	log_test $? 2 "Fdb Nexthop group with non-fdb nexthops"
447
448	# Non fdb nexthop group can not contain fdb nexthops
449	run_cmd "$IP nexthop add id 65 via 2001:db8:91::5 fdb"
450	run_cmd "$IP nexthop add id 66 via 2001:db8:91::6 fdb"
451	run_cmd "$IP nexthop add id 104 group 65/66"
452	log_test $? 2 "Non-Fdb Nexthop group with fdb nexthops"
453
454	# fdb nexthop cannot have blackhole
455	run_cmd "$IP nexthop add id 67 blackhole fdb"
456	log_test $? 2 "Fdb Nexthop with blackhole"
457
458	# fdb nexthop with oif
459	run_cmd "$IP nexthop add id 68 via 2001:db8:91::7 dev veth1 fdb"
460	log_test $? 2 "Fdb Nexthop with oif"
461
462	# fdb nexthop with onlink
463	run_cmd "$IP nexthop add id 68 via 2001:db8:91::7 onlink fdb"
464	log_test $? 2 "Fdb Nexthop with onlink"
465
466	# fdb nexthop with encap
467	run_cmd "$IP nexthop add id 69 encap mpls 101 via 2001:db8:91::8 dev veth1 fdb"
468	log_test $? 2 "Fdb Nexthop with encap"
469
470	run_cmd "$IP link add name vx10 type vxlan id 1010 local 2001:db8:91::9 remote 2001:db8:91::10 dstport 4789 nolearning noudpcsum tos inherit ttl 100"
471	run_cmd "$BRIDGE fdb add 02:02:00:00:00:13 dev vx10 nhid 102 self"
472	log_test $? 0 "Fdb mac add with nexthop group"
473
474	## fdb nexthops can only reference nexthop groups and not nexthops
475	run_cmd "$BRIDGE fdb add 02:02:00:00:00:14 dev vx10 nhid 61 self"
476	log_test $? 255 "Fdb mac add with nexthop"
477
478	run_cmd "$IP -6 ro add 2001:db8:101::1/128 nhid 66"
479	log_test $? 2 "Route add with fdb nexthop"
480
481	run_cmd "$IP -6 ro add 2001:db8:101::1/128 nhid 103"
482	log_test $? 2 "Route add with fdb nexthop group"
483
484	run_cmd "$IP nexthop del id 61"
485	run_cmd "$BRIDGE fdb get to 02:02:00:00:00:13 dev vx10 self"
486	log_test $? 0 "Fdb entry after deleting a single nexthop"
487
488	run_cmd "$IP nexthop del id 102"
489	log_test $? 0 "Fdb nexthop delete"
490
491	run_cmd "$BRIDGE fdb get to 02:02:00:00:00:13 dev vx10 self"
492	log_test $? 254 "Fdb entry after deleting a nexthop group"
493
494	$IP link del dev vx10
495}
496
497ipv4_fdb_grp_fcnal()
498{
499	local rc
500
501	echo
502	echo "IPv4 fdb groups functional"
503	echo "--------------------------"
504
505	check_nexthop_fdb_support
506	if [ $? -eq $ksft_skip ]; then
507		return $ksft_skip
508	fi
509
510	# create group with multiple nexthops
511	run_cmd "$IP nexthop add id 12 via 172.16.1.2 fdb"
512	run_cmd "$IP nexthop add id 13 via 172.16.1.3 fdb"
513	run_cmd "$IP nexthop add id 102 group 12/13 fdb"
514	check_nexthop "id 102" "id 102 group 12/13 fdb"
515	log_test $? 0 "Fdb Nexthop group with multiple nexthops"
516
517	# get nexthop group
518	run_cmd "$IP nexthop get id 102"
519	check_nexthop "id 102" "id 102 group 12/13 fdb"
520	log_test $? 0 "Get Fdb nexthop group by id"
521
522	# fdb nexthop group can only contain fdb nexthops
523	run_cmd "$IP nexthop add id 14 via 172.16.1.2"
524	run_cmd "$IP nexthop add id 15 via 172.16.1.3"
525	run_cmd "$IP nexthop add id 103 group 14/15 fdb"
526	log_test $? 2 "Fdb Nexthop group with non-fdb nexthops"
527
528	# Non fdb nexthop group can not contain fdb nexthops
529	run_cmd "$IP nexthop add id 16 via 172.16.1.2 fdb"
530	run_cmd "$IP nexthop add id 17 via 172.16.1.3 fdb"
531	run_cmd "$IP nexthop add id 104 group 14/15"
532	log_test $? 2 "Non-Fdb Nexthop group with fdb nexthops"
533
534	# fdb nexthop cannot have blackhole
535	run_cmd "$IP nexthop add id 18 blackhole fdb"
536	log_test $? 2 "Fdb Nexthop with blackhole"
537
538	# fdb nexthop with oif
539	run_cmd "$IP nexthop add id 16 via 172.16.1.2 dev veth1 fdb"
540	log_test $? 2 "Fdb Nexthop with oif"
541
542	# fdb nexthop with onlink
543	run_cmd "$IP nexthop add id 16 via 172.16.1.2 onlink fdb"
544	log_test $? 2 "Fdb Nexthop with onlink"
545
546	# fdb nexthop with encap
547	run_cmd "$IP nexthop add id 17 encap mpls 101 via 172.16.1.2 dev veth1 fdb"
548	log_test $? 2 "Fdb Nexthop with encap"
549
550	run_cmd "$IP link add name vx10 type vxlan id 1010 local 10.0.0.1 remote 10.0.0.2 dstport 4789 nolearning noudpcsum tos inherit ttl 100"
551	run_cmd "$BRIDGE fdb add 02:02:00:00:00:13 dev vx10 nhid 102 self"
552	log_test $? 0 "Fdb mac add with nexthop group"
553
554	# fdb nexthops can only reference nexthop groups and not nexthops
555	run_cmd "$BRIDGE fdb add 02:02:00:00:00:14 dev vx10 nhid 12 self"
556	log_test $? 255 "Fdb mac add with nexthop"
557
558	run_cmd "$IP ro add 172.16.0.0/22 nhid 15"
559	log_test $? 2 "Route add with fdb nexthop"
560
561	run_cmd "$IP ro add 172.16.0.0/22 nhid 103"
562	log_test $? 2 "Route add with fdb nexthop group"
563
564	run_cmd "$IP nexthop del id 12"
565	run_cmd "$BRIDGE fdb get to 02:02:00:00:00:13 dev vx10 self"
566	log_test $? 0 "Fdb entry after deleting a single nexthop"
567
568	run_cmd "$IP nexthop del id 102"
569	log_test $? 0 "Fdb nexthop delete"
570
571	run_cmd "$BRIDGE fdb get to 02:02:00:00:00:13 dev vx10 self"
572	log_test $? 254 "Fdb entry after deleting a nexthop group"
573
574	$IP link del dev vx10
575}
576
577################################################################################
578# basic operations (add, delete, replace) on nexthops and nexthop groups
579#
580# IPv6
581
582ipv6_fcnal()
583{
584	local rc
585
586	echo
587	echo "IPv6"
588	echo "----------------------"
589
590	run_cmd "$IP nexthop add id 52 via 2001:db8:91::2 dev veth1"
591	rc=$?
592	log_test $rc 0 "Create nexthop with id, gw, dev"
593	if [ $rc -ne 0 ]; then
594		echo "Basic IPv6 create fails; can not continue"
595		return 1
596	fi
597
598	run_cmd "$IP nexthop get id 52"
599	log_test $? 0 "Get nexthop by id"
600	check_nexthop "id 52" "id 52 via 2001:db8:91::2 dev veth1 scope link"
601
602	run_cmd "$IP nexthop del id 52"
603	log_test $? 0 "Delete nexthop by id"
604	check_nexthop "id 52" ""
605
606	#
607	# gw, device spec
608	#
609	# gw validation, no device - fails since dev required
610	run_cmd "$IP nexthop add id 52 via 2001:db8:92::3"
611	log_test $? 2 "Create nexthop - gw only"
612
613	# gw is not reachable throught given dev
614	run_cmd "$IP nexthop add id 53 via 2001:db8:3::3 dev veth1"
615	log_test $? 2 "Create nexthop - invalid gw+dev combination"
616
617	# onlink arg overrides gw+dev lookup
618	run_cmd "$IP nexthop add id 53 via 2001:db8:3::3 dev veth1 onlink"
619	log_test $? 0 "Create nexthop - gw+dev and onlink"
620
621	# admin down should delete nexthops
622	set -e
623	run_cmd "$IP -6 nexthop add id 55 via 2001:db8:91::3 dev veth1"
624	run_cmd "$IP nexthop add id 56 via 2001:db8:91::4 dev veth1"
625	run_cmd "$IP nexthop add id 57 via 2001:db8:91::5 dev veth1"
626	run_cmd "$IP li set dev veth1 down"
627	set +e
628	check_nexthop "dev veth1" ""
629	log_test $? 0 "Nexthops removed on admin down"
630}
631
632ipv6_grp_refs()
633{
634	if [ ! -x "$(command -v mausezahn)" ]; then
635		echo "SKIP: Could not run test; need mausezahn tool"
636		return
637	fi
638
639	run_cmd "$IP link set dev veth1 up"
640	run_cmd "$IP link add veth1.10 link veth1 up type vlan id 10"
641	run_cmd "$IP link add veth1.20 link veth1 up type vlan id 20"
642	run_cmd "$IP -6 addr add 2001:db8:91::1/64 dev veth1.10"
643	run_cmd "$IP -6 addr add 2001:db8:92::1/64 dev veth1.20"
644	run_cmd "$IP -6 neigh add 2001:db8:91::2 lladdr 00:11:22:33:44:55 dev veth1.10"
645	run_cmd "$IP -6 neigh add 2001:db8:92::2 lladdr 00:11:22:33:44:55 dev veth1.20"
646	run_cmd "$IP nexthop add id 100 via 2001:db8:91::2 dev veth1.10"
647	run_cmd "$IP nexthop add id 101 via 2001:db8:92::2 dev veth1.20"
648	run_cmd "$IP nexthop add id 102 group 100"
649	run_cmd "$IP route add 2001:db8:101::1/128 nhid 102"
650
651	# create per-cpu dsts through nh 100
652	run_cmd "ip netns exec me mausezahn -6 veth1.10 -B 2001:db8:101::1 -A 2001:db8:91::1 -c 5 -t tcp "dp=1-1023, flags=syn" >/dev/null 2>&1"
653
654	# remove nh 100 from the group to delete the route potentially leaving
655	# a stale per-cpu dst which holds a reference to the nexthop's net
656	# device and to the IPv6 route
657	run_cmd "$IP nexthop replace id 102 group 101"
658	run_cmd "$IP route del 2001:db8:101::1/128"
659
660	# add both nexthops to the group so a reference is taken on them
661	run_cmd "$IP nexthop replace id 102 group 100/101"
662
663	# if the bug described in commit "net: nexthop: release IPv6 per-cpu
664	# dsts when replacing a nexthop group" exists at this point we have
665	# an unlinked IPv6 route (but not freed due to stale dst) with a
666	# reference over the group so we delete the group which will again
667	# only unlink it due to the route reference
668	run_cmd "$IP nexthop del id 102"
669
670	# delete the nexthop with stale dst, since we have an unlinked
671	# group with a ref to it and an unlinked IPv6 route with ref to the
672	# group, the nh will only be unlinked and not freed so the stale dst
673	# remains forever and we get a net device refcount imbalance
674	run_cmd "$IP nexthop del id 100"
675
676	# if a reference was lost this command will hang because the net device
677	# cannot be removed
678	timeout -s KILL 5 ip netns exec me ip link del veth1.10 >/dev/null 2>&1
679
680	# we can't cleanup if the command is hung trying to delete the netdev
681	if [ $? -eq 137 ]; then
682		return 1
683	fi
684
685	# cleanup
686	run_cmd "$IP link del veth1.20"
687	run_cmd "$IP nexthop flush"
688
689	return 0
690}
691
692ipv6_grp_fcnal()
693{
694	local rc
695
696	echo
697	echo "IPv6 groups functional"
698	echo "----------------------"
699
700	# basic functionality: create a nexthop group, default weight
701	run_cmd "$IP nexthop add id 61 via 2001:db8:91::2 dev veth1"
702	run_cmd "$IP nexthop add id 101 group 61"
703	log_test $? 0 "Create nexthop group with single nexthop"
704
705	# get nexthop group
706	run_cmd "$IP nexthop get id 101"
707	log_test $? 0 "Get nexthop group by id"
708	check_nexthop "id 101" "id 101 group 61"
709
710	# delete nexthop group
711	run_cmd "$IP nexthop del id 101"
712	log_test $? 0 "Delete nexthop group by id"
713	check_nexthop "id 101" ""
714
715	$IP nexthop flush >/dev/null 2>&1
716	check_nexthop "id 101" ""
717
718	#
719	# create group with multiple nexthops - mix of gw and dev only
720	#
721	run_cmd "$IP nexthop add id 62 via 2001:db8:91::2 dev veth1"
722	run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
723	run_cmd "$IP nexthop add id 64 via 2001:db8:91::4 dev veth1"
724	run_cmd "$IP nexthop add id 65 dev veth1"
725	run_cmd "$IP nexthop add id 102 group 62/63/64/65"
726	log_test $? 0 "Nexthop group with multiple nexthops"
727	check_nexthop "id 102" "id 102 group 62/63/64/65"
728
729	# Delete nexthop in a group and group is updated
730	run_cmd "$IP nexthop del id 63"
731	check_nexthop "id 102" "id 102 group 62/64/65"
732	log_test $? 0 "Nexthop group updated when entry is deleted"
733
734	# create group with multiple weighted nexthops
735	run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
736	run_cmd "$IP nexthop add id 103 group 62/63,2/64,3/65,4"
737	log_test $? 0 "Nexthop group with weighted nexthops"
738	check_nexthop "id 103" "id 103 group 62/63,2/64,3/65,4"
739
740	# Delete nexthop in a weighted group and group is updated
741	run_cmd "$IP nexthop del id 63"
742	check_nexthop "id 103" "id 103 group 62/64,3/65,4"
743	log_test $? 0 "Weighted nexthop group updated when entry is deleted"
744
745	# admin down - nexthop is removed from group
746	run_cmd "$IP li set dev veth1 down"
747	check_nexthop "dev veth1" ""
748	log_test $? 0 "Nexthops in groups removed on admin down"
749
750	# expect groups to have been deleted as well
751	check_nexthop "" ""
752
753	run_cmd "$IP li set dev veth1 up"
754
755	$IP nexthop flush >/dev/null 2>&1
756
757	# group with nexthops using different devices
758	set -e
759	run_cmd "$IP nexthop add id 62 via 2001:db8:91::2 dev veth1"
760	run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
761	run_cmd "$IP nexthop add id 64 via 2001:db8:91::4 dev veth1"
762	run_cmd "$IP nexthop add id 65 via 2001:db8:91::5 dev veth1"
763
764	run_cmd "$IP nexthop add id 72 via 2001:db8:92::2 dev veth3"
765	run_cmd "$IP nexthop add id 73 via 2001:db8:92::3 dev veth3"
766	run_cmd "$IP nexthop add id 74 via 2001:db8:92::4 dev veth3"
767	run_cmd "$IP nexthop add id 75 via 2001:db8:92::5 dev veth3"
768	set +e
769
770	# multiple groups with same nexthop
771	run_cmd "$IP nexthop add id 104 group 62"
772	run_cmd "$IP nexthop add id 105 group 62"
773	check_nexthop "group" "id 104 group 62 id 105 group 62"
774	log_test $? 0 "Multiple groups with same nexthop"
775
776	run_cmd "$IP nexthop flush groups"
777	[ $? -ne 0 ] && return 1
778
779	# on admin down of veth1, it should be removed from the group
780	run_cmd "$IP nexthop add id 105 group 62/63/72/73/64"
781	run_cmd "$IP li set veth1 down"
782	check_nexthop "id 105" "id 105 group 72/73"
783	log_test $? 0 "Nexthops in group removed on admin down - mixed group"
784
785	run_cmd "$IP nexthop add id 106 group 105/74"
786	log_test $? 2 "Nexthop group can not have a group as an entry"
787
788	# a group can have a blackhole entry only if it is the only
789	# nexthop in the group. Needed for atomic replace with an
790	# actual nexthop group
791	run_cmd "$IP -6 nexthop add id 31 blackhole"
792	run_cmd "$IP nexthop add id 107 group 31"
793	log_test $? 0 "Nexthop group with a blackhole entry"
794
795	run_cmd "$IP nexthop add id 108 group 31/24"
796	log_test $? 2 "Nexthop group can not have a blackhole and another nexthop"
797
798	ipv6_grp_refs
799	log_test $? 0 "Nexthop group replace refcounts"
800}
801
802ipv6_res_grp_fcnal()
803{
804	local rc
805
806	echo
807	echo "IPv6 resilient groups functional"
808	echo "--------------------------------"
809
810	check_nexthop_res_support
811	if [ $? -eq $ksft_skip ]; then
812		return $ksft_skip
813	fi
814
815	#
816	# migration of nexthop buckets - equal weights
817	#
818	run_cmd "$IP nexthop add id 62 via 2001:db8:91::2 dev veth1"
819	run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
820	run_cmd "$IP nexthop add id 102 group 62/63 type resilient buckets 2 idle_timer 0"
821
822	run_cmd "$IP nexthop del id 63"
823	check_nexthop "id 102" \
824		"id 102 group 62 type resilient buckets 2 idle_timer 0 unbalanced_timer 0 unbalanced_time 0"
825	log_test $? 0 "Nexthop group updated when entry is deleted"
826	check_nexthop_bucket "list id 102" \
827		"id 102 index 0 nhid 62 id 102 index 1 nhid 62"
828	log_test $? 0 "Nexthop buckets updated when entry is deleted"
829
830	run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
831	run_cmd "$IP nexthop replace id 102 group 62/63 type resilient buckets 2 idle_timer 0"
832	check_nexthop "id 102" \
833		"id 102 group 62/63 type resilient buckets 2 idle_timer 0 unbalanced_timer 0 unbalanced_time 0"
834	log_test $? 0 "Nexthop group updated after replace"
835	check_nexthop_bucket "list id 102" \
836		"id 102 index 0 nhid 63 id 102 index 1 nhid 62"
837	log_test $? 0 "Nexthop buckets updated after replace"
838
839	$IP nexthop flush >/dev/null 2>&1
840
841	#
842	# migration of nexthop buckets - unequal weights
843	#
844	run_cmd "$IP nexthop add id 62 via 2001:db8:91::2 dev veth1"
845	run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
846	run_cmd "$IP nexthop add id 102 group 62,3/63,1 type resilient buckets 4 idle_timer 0"
847
848	run_cmd "$IP nexthop del id 63"
849	check_nexthop "id 102" \
850		"id 102 group 62,3 type resilient buckets 4 idle_timer 0 unbalanced_timer 0 unbalanced_time 0"
851	log_test $? 0 "Nexthop group updated when entry is deleted - nECMP"
852	check_nexthop_bucket "list id 102" \
853		"id 102 index 0 nhid 62 id 102 index 1 nhid 62 id 102 index 2 nhid 62 id 102 index 3 nhid 62"
854	log_test $? 0 "Nexthop buckets updated when entry is deleted - nECMP"
855
856	run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
857	run_cmd "$IP nexthop replace id 102 group 62,3/63,1 type resilient buckets 4 idle_timer 0"
858	check_nexthop "id 102" \
859		"id 102 group 62,3/63 type resilient buckets 4 idle_timer 0 unbalanced_timer 0 unbalanced_time 0"
860	log_test $? 0 "Nexthop group updated after replace - nECMP"
861	check_nexthop_bucket "list id 102" \
862		"id 102 index 0 nhid 63 id 102 index 1 nhid 62 id 102 index 2 nhid 62 id 102 index 3 nhid 62"
863	log_test $? 0 "Nexthop buckets updated after replace - nECMP"
864}
865
866ipv6_fcnal_runtime()
867{
868	local rc
869
870	echo
871	echo "IPv6 functional runtime"
872	echo "-----------------------"
873
874	#
875	# IPv6 - the basics
876	#
877	run_cmd "$IP nexthop add id 81 via 2001:db8:91::2 dev veth1"
878	run_cmd "$IP ro add 2001:db8:101::1/128 nhid 81"
879	log_test $? 0 "Route add"
880
881	run_cmd "$IP ro delete 2001:db8:101::1/128 nhid 81"
882	log_test $? 0 "Route delete"
883
884	run_cmd "$IP ro add 2001:db8:101::1/128 nhid 81"
885	run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
886	log_test $? 0 "Ping with nexthop"
887
888	run_cmd "$IP nexthop add id 82 via 2001:db8:92::2 dev veth3"
889	run_cmd "$IP nexthop add id 122 group 81/82"
890	run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 122"
891	run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
892	log_test $? 0 "Ping - multipath"
893
894	#
895	# IPv6 with blackhole nexthops
896	#
897	run_cmd "$IP -6 nexthop add id 83 blackhole"
898	run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 83"
899	run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
900	log_test $? 2 "Ping - blackhole"
901
902	run_cmd "$IP nexthop replace id 83 via 2001:db8:91::2 dev veth1"
903	run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
904	log_test $? 0 "Ping - blackhole replaced with gateway"
905
906	run_cmd "$IP -6 nexthop replace id 83 blackhole"
907	run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
908	log_test $? 2 "Ping - gateway replaced by blackhole"
909
910	run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 122"
911	run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
912	if [ $? -eq 0 ]; then
913		run_cmd "$IP nexthop replace id 122 group 83"
914		run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
915		log_test $? 2 "Ping - group with blackhole"
916
917		run_cmd "$IP nexthop replace id 122 group 81/82"
918		run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
919		log_test $? 0 "Ping - group blackhole replaced with gateways"
920	else
921		log_test 2 0 "Ping - multipath failed"
922	fi
923
924	#
925	# device only and gw + dev only mix
926	#
927	run_cmd "$IP -6 nexthop add id 85 dev veth1"
928	run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 85"
929	log_test $? 0 "IPv6 route with device only nexthop"
930	check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 85 dev veth1 metric 1024"
931
932	run_cmd "$IP nexthop add id 123 group 81/85"
933	run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 123"
934	log_test $? 0 "IPv6 multipath route with nexthop mix - dev only + gw"
935	check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 123 metric 1024 nexthop via 2001:db8:91::2 dev veth1 weight 1 nexthop dev veth1 weight 1"
936
937	#
938	# IPv6 route with v4 nexthop - not allowed
939	#
940	run_cmd "$IP ro delete 2001:db8:101::1/128"
941	run_cmd "$IP nexthop add id 84 via 172.16.1.1 dev veth1"
942	run_cmd "$IP ro add 2001:db8:101::1/128 nhid 84"
943	log_test $? 2 "IPv6 route can not have a v4 gateway"
944
945	run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 81"
946	run_cmd "$IP nexthop replace id 81 via 172.16.1.1 dev veth1"
947	log_test $? 2 "Nexthop replace - v6 route, v4 nexthop"
948
949	run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 122"
950	run_cmd "$IP nexthop replace id 81 via 172.16.1.1 dev veth1"
951	log_test $? 2 "Nexthop replace of group entry - v6 route, v4 nexthop"
952
953	run_cmd "$IP nexthop add id 86 via 2001:db8:92::2 dev veth3"
954	run_cmd "$IP nexthop add id 87 via 172.16.1.1 dev veth1"
955	run_cmd "$IP nexthop add id 88 via 172.16.1.1 dev veth1"
956	run_cmd "$IP nexthop add id 124 group 86/87/88"
957	run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 124"
958	log_test $? 2 "IPv6 route can not have a group with v4 and v6 gateways"
959
960	run_cmd "$IP nexthop del id 88"
961	run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 124"
962	log_test $? 2 "IPv6 route can not have a group with v4 and v6 gateways"
963
964	run_cmd "$IP nexthop del id 87"
965	run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 124"
966	log_test $? 0 "IPv6 route using a group after removing v4 gateways"
967
968	run_cmd "$IP ro delete 2001:db8:101::1/128"
969	run_cmd "$IP nexthop add id 87 via 172.16.1.1 dev veth1"
970	run_cmd "$IP nexthop add id 88 via 172.16.1.1 dev veth1"
971	run_cmd "$IP nexthop replace id 124 group 86/87/88"
972	run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 124"
973	log_test $? 2 "IPv6 route can not have a group with v4 and v6 gateways"
974
975	run_cmd "$IP nexthop replace id 88 via 2001:db8:92::2 dev veth3"
976	run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 124"
977	log_test $? 2 "IPv6 route can not have a group with v4 and v6 gateways"
978
979	run_cmd "$IP nexthop replace id 87 via 2001:db8:92::2 dev veth3"
980	run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 124"
981	log_test $? 0 "IPv6 route using a group after replacing v4 gateways"
982
983	$IP nexthop flush >/dev/null 2>&1
984
985	#
986	# weird IPv6 cases
987	#
988	run_cmd "$IP nexthop add id 86 via 2001:db8:91::2 dev veth1"
989	run_cmd "$IP ro add 2001:db8:101::1/128 nhid 81"
990
991	# route can not use prefsrc with nexthops
992	run_cmd "$IP ro add 2001:db8:101::2/128 nhid 86 from 2001:db8:91::1"
993	log_test $? 2 "IPv6 route can not use src routing with external nexthop"
994
995	# check cleanup path on invalid metric
996	run_cmd "$IP ro add 2001:db8:101::2/128 nhid 86 congctl lock foo"
997	log_test $? 2 "IPv6 route with invalid metric"
998
999	# rpfilter and default route
1000	$IP nexthop flush >/dev/null 2>&1
1001	run_cmd "ip netns exec me ip6tables -t mangle -I PREROUTING 1 -m rpfilter --invert -j DROP"
1002	run_cmd "$IP nexthop add id 91 via 2001:db8:91::2 dev veth1"
1003	run_cmd "$IP nexthop add id 92 via 2001:db8:92::2 dev veth3"
1004	run_cmd "$IP nexthop add id 93 group 91/92"
1005	run_cmd "$IP -6 ro add default nhid 91"
1006	run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
1007	log_test $? 0 "Nexthop with default route and rpfilter"
1008	run_cmd "$IP -6 ro replace default nhid 93"
1009	run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
1010	log_test $? 0 "Nexthop with multipath default route and rpfilter"
1011
1012	# TO-DO:
1013	# existing route with old nexthop; append route with new nexthop
1014	# existing route with old nexthop; replace route with new
1015	# existing route with new nexthop; replace route with old
1016	# route with src address and using nexthop - not allowed
1017}
1018
1019ipv6_large_grp()
1020{
1021	local ecmp=32
1022
1023	echo
1024	echo "IPv6 large groups (x$ecmp)"
1025	echo "---------------------"
1026
1027	check_large_grp 6 $ecmp
1028
1029	$IP nexthop flush >/dev/null 2>&1
1030}
1031
1032ipv6_large_res_grp()
1033{
1034	echo
1035	echo "IPv6 large resilient group (128k buckets)"
1036	echo "-----------------------------------------"
1037
1038	check_nexthop_res_support
1039	if [ $? -eq $ksft_skip ]; then
1040		return $ksft_skip
1041	fi
1042
1043	check_large_res_grp 6 $((128 * 1024))
1044
1045	$IP nexthop flush >/dev/null 2>&1
1046}
1047
1048ipv6_del_add_loop1()
1049{
1050	while :; do
1051		$IP nexthop del id 100
1052		$IP nexthop add id 100 via 2001:db8:91::2 dev veth1
1053	done >/dev/null 2>&1
1054}
1055
1056ipv6_grp_replace_loop()
1057{
1058	while :; do
1059		$IP nexthop replace id 102 group 100/101
1060	done >/dev/null 2>&1
1061}
1062
1063ipv6_torture()
1064{
1065	local pid1
1066	local pid2
1067	local pid3
1068	local pid4
1069	local pid5
1070
1071	echo
1072	echo "IPv6 runtime torture"
1073	echo "--------------------"
1074	if [ ! -x "$(command -v mausezahn)" ]; then
1075		echo "SKIP: Could not run test; need mausezahn tool"
1076		return
1077	fi
1078
1079	run_cmd "$IP nexthop add id 100 via 2001:db8:91::2 dev veth1"
1080	run_cmd "$IP nexthop add id 101 via 2001:db8:92::2 dev veth3"
1081	run_cmd "$IP nexthop add id 102 group 100/101"
1082	run_cmd "$IP route add 2001:db8:101::1 nhid 102"
1083	run_cmd "$IP route add 2001:db8:101::2 nhid 102"
1084
1085	ipv6_del_add_loop1 &
1086	pid1=$!
1087	ipv6_grp_replace_loop &
1088	pid2=$!
1089	ip netns exec me ping -f 2001:db8:101::1 >/dev/null 2>&1 &
1090	pid3=$!
1091	ip netns exec me ping -f 2001:db8:101::2 >/dev/null 2>&1 &
1092	pid4=$!
1093	ip netns exec me mausezahn -6 veth1 -B 2001:db8:101::2 -A 2001:db8:91::1 -c 0 -t tcp "dp=1-1023, flags=syn" >/dev/null 2>&1 &
1094	pid5=$!
1095
1096	sleep 300
1097	kill -9 $pid1 $pid2 $pid3 $pid4 $pid5
1098	wait $pid1 $pid2 $pid3 $pid4 $pid5 2>/dev/null
1099
1100	# if we did not crash, success
1101	log_test 0 0 "IPv6 torture test"
1102}
1103
1104ipv6_res_grp_replace_loop()
1105{
1106	while :; do
1107		$IP nexthop replace id 102 group 100/101 type resilient
1108	done >/dev/null 2>&1
1109}
1110
1111ipv6_res_torture()
1112{
1113	local pid1
1114	local pid2
1115	local pid3
1116	local pid4
1117	local pid5
1118
1119	echo
1120	echo "IPv6 runtime resilient nexthop group torture"
1121	echo "--------------------------------------------"
1122
1123	check_nexthop_res_support
1124	if [ $? -eq $ksft_skip ]; then
1125		return $ksft_skip
1126	fi
1127
1128	if [ ! -x "$(command -v mausezahn)" ]; then
1129		echo "SKIP: Could not run test; need mausezahn tool"
1130		return
1131	fi
1132
1133	run_cmd "$IP nexthop add id 100 via 2001:db8:91::2 dev veth1"
1134	run_cmd "$IP nexthop add id 101 via 2001:db8:92::2 dev veth3"
1135	run_cmd "$IP nexthop add id 102 group 100/101 type resilient buckets 512 idle_timer 0"
1136	run_cmd "$IP route add 2001:db8:101::1 nhid 102"
1137	run_cmd "$IP route add 2001:db8:101::2 nhid 102"
1138
1139	ipv6_del_add_loop1 &
1140	pid1=$!
1141	ipv6_res_grp_replace_loop &
1142	pid2=$!
1143	ip netns exec me ping -f 2001:db8:101::1 >/dev/null 2>&1 &
1144	pid3=$!
1145	ip netns exec me ping -f 2001:db8:101::2 >/dev/null 2>&1 &
1146	pid4=$!
1147	ip netns exec me mausezahn -6 veth1 \
1148			    -B 2001:db8:101::2 -A 2001:db8:91::1 -c 0 \
1149			    -t tcp "dp=1-1023, flags=syn" >/dev/null 2>&1 &
1150	pid5=$!
1151
1152	sleep 300
1153	kill -9 $pid1 $pid2 $pid3 $pid4 $pid5
1154	wait $pid1 $pid2 $pid3 $pid4 $pid5 2>/dev/null
1155
1156	# if we did not crash, success
1157	log_test 0 0 "IPv6 resilient nexthop group torture test"
1158}
1159
1160ipv4_fcnal()
1161{
1162	local rc
1163
1164	echo
1165	echo "IPv4 functional"
1166	echo "----------------------"
1167
1168	#
1169	# basic IPv4 ops - add, get, delete
1170	#
1171	run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
1172	rc=$?
1173	log_test $rc 0 "Create nexthop with id, gw, dev"
1174	if [ $rc -ne 0 ]; then
1175		echo "Basic IPv4 create fails; can not continue"
1176		return 1
1177	fi
1178
1179	run_cmd "$IP nexthop get id 12"
1180	log_test $? 0 "Get nexthop by id"
1181	check_nexthop "id 12" "id 12 via 172.16.1.2 dev veth1 scope link"
1182
1183	run_cmd "$IP nexthop del id 12"
1184	log_test $? 0 "Delete nexthop by id"
1185	check_nexthop "id 52" ""
1186
1187	#
1188	# gw, device spec
1189	#
1190	# gw validation, no device - fails since dev is required
1191	run_cmd "$IP nexthop add id 12 via 172.16.2.3"
1192	log_test $? 2 "Create nexthop - gw only"
1193
1194	# gw not reachable through given dev
1195	run_cmd "$IP nexthop add id 13 via 172.16.3.2 dev veth1"
1196	log_test $? 2 "Create nexthop - invalid gw+dev combination"
1197
1198	# onlink flag overrides gw+dev lookup
1199	run_cmd "$IP nexthop add id 13 via 172.16.3.2 dev veth1 onlink"
1200	log_test $? 0 "Create nexthop - gw+dev and onlink"
1201
1202	# admin down should delete nexthops
1203	set -e
1204	run_cmd "$IP nexthop add id 15 via 172.16.1.3 dev veth1"
1205	run_cmd "$IP nexthop add id 16 via 172.16.1.4 dev veth1"
1206	run_cmd "$IP nexthop add id 17 via 172.16.1.5 dev veth1"
1207	run_cmd "$IP li set dev veth1 down"
1208	set +e
1209	check_nexthop "dev veth1" ""
1210	log_test $? 0 "Nexthops removed on admin down"
1211}
1212
1213ipv4_grp_fcnal()
1214{
1215	local rc
1216
1217	echo
1218	echo "IPv4 groups functional"
1219	echo "----------------------"
1220
1221	# basic functionality: create a nexthop group, default weight
1222	run_cmd "$IP nexthop add id 11 via 172.16.1.2 dev veth1"
1223	run_cmd "$IP nexthop add id 101 group 11"
1224	log_test $? 0 "Create nexthop group with single nexthop"
1225
1226	# get nexthop group
1227	run_cmd "$IP nexthop get id 101"
1228	log_test $? 0 "Get nexthop group by id"
1229	check_nexthop "id 101" "id 101 group 11"
1230
1231	# delete nexthop group
1232	run_cmd "$IP nexthop del id 101"
1233	log_test $? 0 "Delete nexthop group by id"
1234	check_nexthop "id 101" ""
1235
1236	$IP nexthop flush >/dev/null 2>&1
1237
1238	#
1239	# create group with multiple nexthops
1240	run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
1241	run_cmd "$IP nexthop add id 13 via 172.16.1.3 dev veth1"
1242	run_cmd "$IP nexthop add id 14 via 172.16.1.4 dev veth1"
1243	run_cmd "$IP nexthop add id 15 via 172.16.1.5 dev veth1"
1244	run_cmd "$IP nexthop add id 102 group 12/13/14/15"
1245	log_test $? 0 "Nexthop group with multiple nexthops"
1246	check_nexthop "id 102" "id 102 group 12/13/14/15"
1247
1248	# Delete nexthop in a group and group is updated
1249	run_cmd "$IP nexthop del id 13"
1250	check_nexthop "id 102" "id 102 group 12/14/15"
1251	log_test $? 0 "Nexthop group updated when entry is deleted"
1252
1253	# create group with multiple weighted nexthops
1254	run_cmd "$IP nexthop add id 13 via 172.16.1.3 dev veth1"
1255	run_cmd "$IP nexthop add id 103 group 12/13,2/14,3/15,4"
1256	log_test $? 0 "Nexthop group with weighted nexthops"
1257	check_nexthop "id 103" "id 103 group 12/13,2/14,3/15,4"
1258
1259	# Delete nexthop in a weighted group and group is updated
1260	run_cmd "$IP nexthop del id 13"
1261	check_nexthop "id 103" "id 103 group 12/14,3/15,4"
1262	log_test $? 0 "Weighted nexthop group updated when entry is deleted"
1263
1264	# admin down - nexthop is removed from group
1265	run_cmd "$IP li set dev veth1 down"
1266	check_nexthop "dev veth1" ""
1267	log_test $? 0 "Nexthops in groups removed on admin down"
1268
1269	# expect groups to have been deleted as well
1270	check_nexthop "" ""
1271
1272	run_cmd "$IP li set dev veth1 up"
1273
1274	$IP nexthop flush >/dev/null 2>&1
1275
1276	# group with nexthops using different devices
1277	set -e
1278	run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
1279	run_cmd "$IP nexthop add id 13 via 172.16.1.3 dev veth1"
1280	run_cmd "$IP nexthop add id 14 via 172.16.1.4 dev veth1"
1281	run_cmd "$IP nexthop add id 15 via 172.16.1.5 dev veth1"
1282
1283	run_cmd "$IP nexthop add id 22 via 172.16.2.2 dev veth3"
1284	run_cmd "$IP nexthop add id 23 via 172.16.2.3 dev veth3"
1285	run_cmd "$IP nexthop add id 24 via 172.16.2.4 dev veth3"
1286	run_cmd "$IP nexthop add id 25 via 172.16.2.5 dev veth3"
1287	set +e
1288
1289	# multiple groups with same nexthop
1290	run_cmd "$IP nexthop add id 104 group 12"
1291	run_cmd "$IP nexthop add id 105 group 12"
1292	check_nexthop "group" "id 104 group 12 id 105 group 12"
1293	log_test $? 0 "Multiple groups with same nexthop"
1294
1295	run_cmd "$IP nexthop flush groups"
1296	[ $? -ne 0 ] && return 1
1297
1298	# on admin down of veth1, it should be removed from the group
1299	run_cmd "$IP nexthop add id 105 group 12/13/22/23/14"
1300	run_cmd "$IP li set veth1 down"
1301	check_nexthop "id 105" "id 105 group 22/23"
1302	log_test $? 0 "Nexthops in group removed on admin down - mixed group"
1303
1304	run_cmd "$IP nexthop add id 106 group 105/24"
1305	log_test $? 2 "Nexthop group can not have a group as an entry"
1306
1307	# a group can have a blackhole entry only if it is the only
1308	# nexthop in the group. Needed for atomic replace with an
1309	# actual nexthop group
1310	run_cmd "$IP nexthop add id 31 blackhole"
1311	run_cmd "$IP nexthop add id 107 group 31"
1312	log_test $? 0 "Nexthop group with a blackhole entry"
1313
1314	run_cmd "$IP nexthop add id 108 group 31/24"
1315	log_test $? 2 "Nexthop group can not have a blackhole and another nexthop"
1316}
1317
1318ipv4_res_grp_fcnal()
1319{
1320	local rc
1321
1322	echo
1323	echo "IPv4 resilient groups functional"
1324	echo "--------------------------------"
1325
1326	check_nexthop_res_support
1327	if [ $? -eq $ksft_skip ]; then
1328		return $ksft_skip
1329	fi
1330
1331	#
1332	# migration of nexthop buckets - equal weights
1333	#
1334	run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
1335	run_cmd "$IP nexthop add id 13 via 172.16.1.3 dev veth1"
1336	run_cmd "$IP nexthop add id 102 group 12/13 type resilient buckets 2 idle_timer 0"
1337
1338	run_cmd "$IP nexthop del id 13"
1339	check_nexthop "id 102" \
1340		"id 102 group 12 type resilient buckets 2 idle_timer 0 unbalanced_timer 0 unbalanced_time 0"
1341	log_test $? 0 "Nexthop group updated when entry is deleted"
1342	check_nexthop_bucket "list id 102" \
1343		"id 102 index 0 nhid 12 id 102 index 1 nhid 12"
1344	log_test $? 0 "Nexthop buckets updated when entry is deleted"
1345
1346	run_cmd "$IP nexthop add id 13 via 172.16.1.3 dev veth1"
1347	run_cmd "$IP nexthop replace id 102 group 12/13 type resilient buckets 2 idle_timer 0"
1348	check_nexthop "id 102" \
1349		"id 102 group 12/13 type resilient buckets 2 idle_timer 0 unbalanced_timer 0 unbalanced_time 0"
1350	log_test $? 0 "Nexthop group updated after replace"
1351	check_nexthop_bucket "list id 102" \
1352		"id 102 index 0 nhid 13 id 102 index 1 nhid 12"
1353	log_test $? 0 "Nexthop buckets updated after replace"
1354
1355	$IP nexthop flush >/dev/null 2>&1
1356
1357	#
1358	# migration of nexthop buckets - unequal weights
1359	#
1360	run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
1361	run_cmd "$IP nexthop add id 13 via 172.16.1.3 dev veth1"
1362	run_cmd "$IP nexthop add id 102 group 12,3/13,1 type resilient buckets 4 idle_timer 0"
1363
1364	run_cmd "$IP nexthop del id 13"
1365	check_nexthop "id 102" \
1366		"id 102 group 12,3 type resilient buckets 4 idle_timer 0 unbalanced_timer 0 unbalanced_time 0"
1367	log_test $? 0 "Nexthop group updated when entry is deleted - nECMP"
1368	check_nexthop_bucket "list id 102" \
1369		"id 102 index 0 nhid 12 id 102 index 1 nhid 12 id 102 index 2 nhid 12 id 102 index 3 nhid 12"
1370	log_test $? 0 "Nexthop buckets updated when entry is deleted - nECMP"
1371
1372	run_cmd "$IP nexthop add id 13 via 172.16.1.3 dev veth1"
1373	run_cmd "$IP nexthop replace id 102 group 12,3/13,1 type resilient buckets 4 idle_timer 0"
1374	check_nexthop "id 102" \
1375		"id 102 group 12,3/13 type resilient buckets 4 idle_timer 0 unbalanced_timer 0 unbalanced_time 0"
1376	log_test $? 0 "Nexthop group updated after replace - nECMP"
1377	check_nexthop_bucket "list id 102" \
1378		"id 102 index 0 nhid 13 id 102 index 1 nhid 12 id 102 index 2 nhid 12 id 102 index 3 nhid 12"
1379	log_test $? 0 "Nexthop buckets updated after replace - nECMP"
1380}
1381
1382ipv4_withv6_fcnal()
1383{
1384	local lladdr
1385
1386	set -e
1387	lladdr=$(get_linklocal veth2 peer)
1388	run_cmd "$IP nexthop add id 11 via ${lladdr} dev veth1"
1389	set +e
1390	run_cmd "$IP ro add 172.16.101.1/32 nhid 11"
1391	log_test $? 0 "IPv6 nexthop with IPv4 route"
1392	check_route "172.16.101.1" "172.16.101.1 nhid 11 via inet6 ${lladdr} dev veth1"
1393
1394	set -e
1395	run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
1396	run_cmd "$IP nexthop add id 101 group 11/12"
1397	set +e
1398	run_cmd "$IP ro replace 172.16.101.1/32 nhid 101"
1399	log_test $? 0 "IPv6 nexthop with IPv4 route"
1400
1401	check_route "172.16.101.1" "172.16.101.1 nhid 101 nexthop via inet6 ${lladdr} dev veth1 weight 1 nexthop via 172.16.1.2 dev veth1 weight 1"
1402
1403	run_cmd "$IP ro replace 172.16.101.1/32 via inet6 ${lladdr} dev veth1"
1404	log_test $? 0 "IPv4 route with IPv6 gateway"
1405	check_route "172.16.101.1" "172.16.101.1 via inet6 ${lladdr} dev veth1"
1406
1407	run_cmd "$IP ro replace 172.16.101.1/32 via inet6 2001:db8:50::1 dev veth1"
1408	log_test $? 2 "IPv4 route with invalid IPv6 gateway"
1409}
1410
1411ipv4_fcnal_runtime()
1412{
1413	local lladdr
1414	local rc
1415
1416	echo
1417	echo "IPv4 functional runtime"
1418	echo "-----------------------"
1419
1420	run_cmd "$IP nexthop add id 21 via 172.16.1.2 dev veth1"
1421	run_cmd "$IP ro add 172.16.101.1/32 nhid 21"
1422	log_test $? 0 "Route add"
1423	check_route "172.16.101.1" "172.16.101.1 nhid 21 via 172.16.1.2 dev veth1"
1424
1425	run_cmd "$IP ro delete 172.16.101.1/32 nhid 21"
1426	log_test $? 0 "Route delete"
1427
1428	#
1429	# scope mismatch
1430	#
1431	run_cmd "$IP nexthop add id 22 via 172.16.1.2 dev veth1"
1432	run_cmd "$IP ro add 172.16.101.1/32 nhid 22 scope host"
1433	log_test $? 2 "Route add - scope conflict with nexthop"
1434
1435	run_cmd "$IP nexthop replace id 22 dev veth3"
1436	run_cmd "$IP ro add 172.16.101.1/32 nhid 22 scope host"
1437	run_cmd "$IP nexthop replace id 22 via 172.16.2.2 dev veth3"
1438	log_test $? 2 "Nexthop replace with invalid scope for existing route"
1439
1440	# check cleanup path on invalid metric
1441	run_cmd "$IP ro add 172.16.101.2/32 nhid 22 congctl lock foo"
1442	log_test $? 2 "IPv4 route with invalid metric"
1443
1444	#
1445	# add route with nexthop and check traffic
1446	#
1447	run_cmd "$IP nexthop replace id 21 via 172.16.1.2 dev veth1"
1448	run_cmd "$IP ro replace 172.16.101.1/32 nhid 21"
1449	run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
1450	log_test $? 0 "Basic ping"
1451
1452	run_cmd "$IP nexthop replace id 22 via 172.16.2.2 dev veth3"
1453	run_cmd "$IP nexthop add id 122 group 21/22"
1454	run_cmd "$IP ro replace 172.16.101.1/32 nhid 122"
1455	run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
1456	log_test $? 0 "Ping - multipath"
1457
1458	run_cmd "$IP ro delete 172.16.101.1/32 nhid 122"
1459
1460	#
1461	# multiple default routes
1462	# - tests fib_select_default
1463	run_cmd "$IP nexthop add id 501 via 172.16.1.2 dev veth1"
1464	run_cmd "$IP ro add default nhid 501"
1465	run_cmd "$IP ro add default via 172.16.1.3 dev veth1 metric 20"
1466	run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
1467	log_test $? 0 "Ping - multiple default routes, nh first"
1468
1469	# flip the order
1470	run_cmd "$IP ro del default nhid 501"
1471	run_cmd "$IP ro del default via 172.16.1.3 dev veth1 metric 20"
1472	run_cmd "$IP ro add default via 172.16.1.2 dev veth1 metric 20"
1473	run_cmd "$IP nexthop replace id 501 via 172.16.1.3 dev veth1"
1474	run_cmd "$IP ro add default nhid 501 metric 20"
1475	run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
1476	log_test $? 0 "Ping - multiple default routes, nh second"
1477
1478	run_cmd "$IP nexthop delete nhid 501"
1479	run_cmd "$IP ro del default"
1480
1481	#
1482	# IPv4 with blackhole nexthops
1483	#
1484	run_cmd "$IP nexthop add id 23 blackhole"
1485	run_cmd "$IP ro replace 172.16.101.1/32 nhid 23"
1486	run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
1487	log_test $? 2 "Ping - blackhole"
1488
1489	run_cmd "$IP nexthop replace id 23 via 172.16.1.2 dev veth1"
1490	run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
1491	log_test $? 0 "Ping - blackhole replaced with gateway"
1492
1493	run_cmd "$IP nexthop replace id 23 blackhole"
1494	run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
1495	log_test $? 2 "Ping - gateway replaced by blackhole"
1496
1497	run_cmd "$IP ro replace 172.16.101.1/32 nhid 122"
1498	run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
1499	if [ $? -eq 0 ]; then
1500		run_cmd "$IP nexthop replace id 122 group 23"
1501		run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
1502		log_test $? 2 "Ping - group with blackhole"
1503
1504		run_cmd "$IP nexthop replace id 122 group 21/22"
1505		run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
1506		log_test $? 0 "Ping - group blackhole replaced with gateways"
1507	else
1508		log_test 2 0 "Ping - multipath failed"
1509	fi
1510
1511	#
1512	# device only and gw + dev only mix
1513	#
1514	run_cmd "$IP nexthop add id 85 dev veth1"
1515	run_cmd "$IP ro replace 172.16.101.1/32 nhid 85"
1516	log_test $? 0 "IPv4 route with device only nexthop"
1517	check_route "172.16.101.1" "172.16.101.1 nhid 85 dev veth1"
1518
1519	run_cmd "$IP nexthop add id 123 group 21/85"
1520	run_cmd "$IP ro replace 172.16.101.1/32 nhid 123"
1521	log_test $? 0 "IPv4 multipath route with nexthop mix - dev only + gw"
1522	check_route "172.16.101.1" "172.16.101.1 nhid 123 nexthop via 172.16.1.2 dev veth1 weight 1 nexthop dev veth1 weight 1"
1523
1524	#
1525	# IPv4 with IPv6
1526	#
1527	set -e
1528	lladdr=$(get_linklocal veth2 peer)
1529	run_cmd "$IP nexthop add id 24 via ${lladdr} dev veth1"
1530	set +e
1531	run_cmd "$IP ro replace 172.16.101.1/32 nhid 24"
1532	run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
1533	log_test $? 0 "IPv6 nexthop with IPv4 route"
1534
1535	$IP neigh sh | grep -q "${lladdr} dev veth1"
1536	if [ $? -eq 1 ]; then
1537		echo "    WARNING: Neigh entry missing for ${lladdr}"
1538		$IP neigh sh | grep 'dev veth1'
1539	fi
1540
1541	$IP neigh sh | grep -q "172.16.101.1 dev eth1"
1542	if [ $? -eq 0 ]; then
1543		echo "    WARNING: Neigh entry exists for 172.16.101.1"
1544		$IP neigh sh | grep 'dev veth1'
1545	fi
1546
1547	set -e
1548	run_cmd "$IP nexthop add id 25 via 172.16.1.2 dev veth1"
1549	run_cmd "$IP nexthop add id 101 group 24/25"
1550	set +e
1551	run_cmd "$IP ro replace 172.16.101.1/32 nhid 101"
1552	log_test $? 0 "IPv4 route with mixed v4-v6 multipath route"
1553
1554	check_route "172.16.101.1" "172.16.101.1 nhid 101 nexthop via inet6 ${lladdr} dev veth1 weight 1 nexthop via 172.16.1.2 dev veth1 weight 1"
1555
1556	run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
1557	log_test $? 0 "IPv6 nexthop with IPv4 route"
1558
1559	run_cmd "$IP ro replace 172.16.101.1/32 via inet6 ${lladdr} dev veth1"
1560	run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
1561	log_test $? 0 "IPv4 route with IPv6 gateway"
1562
1563	$IP neigh sh | grep -q "${lladdr} dev veth1"
1564	if [ $? -eq 1 ]; then
1565		echo "    WARNING: Neigh entry missing for ${lladdr}"
1566		$IP neigh sh | grep 'dev veth1'
1567	fi
1568
1569	$IP neigh sh | grep -q "172.16.101.1 dev eth1"
1570	if [ $? -eq 0 ]; then
1571		echo "    WARNING: Neigh entry exists for 172.16.101.1"
1572		$IP neigh sh | grep 'dev veth1'
1573	fi
1574
1575	run_cmd "$IP ro del 172.16.101.1/32 via inet6 ${lladdr} dev veth1"
1576	run_cmd "$IP -4 ro add default via inet6 ${lladdr} dev veth1"
1577	run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
1578	log_test $? 0 "IPv4 default route with IPv6 gateway"
1579
1580	#
1581	# MPLS as an example of LWT encap
1582	#
1583	run_cmd "$IP nexthop add id 51 encap mpls 101 via 172.16.1.2 dev veth1"
1584	log_test $? 0 "IPv4 route with MPLS encap"
1585	check_nexthop "id 51" "id 51 encap mpls 101 via 172.16.1.2 dev veth1 scope link"
1586	log_test $? 0 "IPv4 route with MPLS encap - check"
1587
1588	run_cmd "$IP nexthop add id 52 encap mpls 102 via inet6 2001:db8:91::2 dev veth1"
1589	log_test $? 0 "IPv4 route with MPLS encap and v6 gateway"
1590	check_nexthop "id 52" "id 52 encap mpls 102 via 2001:db8:91::2 dev veth1 scope link"
1591	log_test $? 0 "IPv4 route with MPLS encap, v6 gw - check"
1592}
1593
1594ipv4_large_grp()
1595{
1596	local ecmp=32
1597
1598	echo
1599	echo "IPv4 large groups (x$ecmp)"
1600	echo "---------------------"
1601
1602	check_large_grp 4 $ecmp
1603
1604	$IP nexthop flush >/dev/null 2>&1
1605}
1606
1607ipv4_large_res_grp()
1608{
1609	echo
1610	echo "IPv4 large resilient group (128k buckets)"
1611	echo "-----------------------------------------"
1612
1613	check_nexthop_res_support
1614	if [ $? -eq $ksft_skip ]; then
1615		return $ksft_skip
1616	fi
1617
1618	check_large_res_grp 4 $((128 * 1024))
1619
1620	$IP nexthop flush >/dev/null 2>&1
1621}
1622
1623sysctl_nexthop_compat_mode_check()
1624{
1625	local sysctlname="net.ipv4.nexthop_compat_mode"
1626	local lprefix=$1
1627
1628	IPE="ip netns exec me"
1629
1630	$IPE sysctl -q $sysctlname 2>&1 >/dev/null
1631	if [ $? -ne 0 ]; then
1632		echo "SKIP: kernel lacks nexthop compat mode sysctl control"
1633		return $ksft_skip
1634	fi
1635
1636	out=$($IPE sysctl $sysctlname 2>/dev/null)
1637	log_test $? 0 "$lprefix default nexthop compat mode check"
1638	check_output "${out}" "$sysctlname = 1"
1639}
1640
1641sysctl_nexthop_compat_mode_set()
1642{
1643	local sysctlname="net.ipv4.nexthop_compat_mode"
1644	local mode=$1
1645	local lprefix=$2
1646
1647	IPE="ip netns exec me"
1648
1649	out=$($IPE sysctl -w $sysctlname=$mode)
1650	log_test $? 0 "$lprefix set compat mode - $mode"
1651	check_output "${out}" "net.ipv4.nexthop_compat_mode = $mode"
1652}
1653
1654ipv6_compat_mode()
1655{
1656	local rc
1657
1658	echo
1659	echo "IPv6 nexthop api compat mode test"
1660	echo "--------------------------------"
1661
1662	sysctl_nexthop_compat_mode_check "IPv6"
1663	if [ $? -eq $ksft_skip ]; then
1664		return $ksft_skip
1665	fi
1666
1667	run_cmd "$IP nexthop add id 62 via 2001:db8:91::2 dev veth1"
1668	run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
1669	run_cmd "$IP nexthop add id 122 group 62/63"
1670	ipmout=$(start_ip_monitor route)
1671
1672	run_cmd "$IP -6 ro add 2001:db8:101::1/128 nhid 122"
1673	# route add notification should contain expanded nexthops
1674	stop_ip_monitor $ipmout 3
1675	log_test $? 0 "IPv6 compat mode on - route add notification"
1676
1677	# route dump should contain expanded nexthops
1678	check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 122 metric 1024 nexthop via 2001:db8:91::2 dev veth1 weight 1 nexthop via 2001:db8:91::3 dev veth1 weight 1"
1679	log_test $? 0 "IPv6 compat mode on - route dump"
1680
1681	# change in nexthop group should generate route notification
1682	run_cmd "$IP nexthop add id 64 via 2001:db8:91::4 dev veth1"
1683	ipmout=$(start_ip_monitor route)
1684	run_cmd "$IP nexthop replace id 122 group 62/64"
1685	stop_ip_monitor $ipmout 3
1686
1687	log_test $? 0 "IPv6 compat mode on - nexthop change"
1688
1689	# set compat mode off
1690	sysctl_nexthop_compat_mode_set 0 "IPv6"
1691
1692	run_cmd "$IP -6 ro del 2001:db8:101::1/128 nhid 122"
1693
1694	run_cmd "$IP nexthop add id 62 via 2001:db8:91::2 dev veth1"
1695	run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
1696	run_cmd "$IP nexthop add id 122 group 62/63"
1697	ipmout=$(start_ip_monitor route)
1698
1699	run_cmd "$IP -6 ro add 2001:db8:101::1/128 nhid 122"
1700	# route add notification should not contain expanded nexthops
1701	stop_ip_monitor $ipmout 1
1702	log_test $? 0 "IPv6 compat mode off - route add notification"
1703
1704	# route dump should not contain expanded nexthops
1705	check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 122 metric 1024"
1706	log_test $? 0 "IPv6 compat mode off - route dump"
1707
1708	# change in nexthop group should not generate route notification
1709	run_cmd "$IP nexthop add id 64 via 2001:db8:91::4 dev veth1"
1710	ipmout=$(start_ip_monitor route)
1711	run_cmd "$IP nexthop replace id 122 group 62/64"
1712	stop_ip_monitor $ipmout 0
1713	log_test $? 0 "IPv6 compat mode off - nexthop change"
1714
1715	# nexthop delete should not generate route notification
1716	ipmout=$(start_ip_monitor route)
1717	run_cmd "$IP nexthop del id 122"
1718	stop_ip_monitor $ipmout 0
1719	log_test $? 0 "IPv6 compat mode off - nexthop delete"
1720
1721	# set compat mode back on
1722	sysctl_nexthop_compat_mode_set 1 "IPv6"
1723}
1724
1725ipv4_compat_mode()
1726{
1727	local rc
1728
1729	echo
1730	echo "IPv4 nexthop api compat mode"
1731	echo "----------------------------"
1732
1733	sysctl_nexthop_compat_mode_check "IPv4"
1734	if [ $? -eq $ksft_skip ]; then
1735		return $ksft_skip
1736	fi
1737
1738	run_cmd "$IP nexthop add id 21 via 172.16.1.2 dev veth1"
1739	run_cmd "$IP nexthop add id 22 via 172.16.1.2 dev veth1"
1740	run_cmd "$IP nexthop add id 122 group 21/22"
1741	ipmout=$(start_ip_monitor route)
1742
1743	run_cmd "$IP ro add 172.16.101.1/32 nhid 122"
1744	stop_ip_monitor $ipmout 3
1745
1746	# route add notification should contain expanded nexthops
1747	log_test $? 0 "IPv4 compat mode on - route add notification"
1748
1749	# route dump should contain expanded nexthops
1750	check_route "172.16.101.1" "172.16.101.1 nhid 122 nexthop via 172.16.1.2 dev veth1 weight 1 nexthop via 172.16.1.2 dev veth1 weight 1"
1751	log_test $? 0 "IPv4 compat mode on - route dump"
1752
1753	# change in nexthop group should generate route notification
1754	run_cmd "$IP nexthop add id 23 via 172.16.1.3 dev veth1"
1755	ipmout=$(start_ip_monitor route)
1756	run_cmd "$IP nexthop replace id 122 group 21/23"
1757	stop_ip_monitor $ipmout 3
1758	log_test $? 0 "IPv4 compat mode on - nexthop change"
1759
1760	sysctl_nexthop_compat_mode_set 0 "IPv4"
1761
1762	# cleanup
1763	run_cmd "$IP ro del 172.16.101.1/32 nhid 122"
1764
1765	ipmout=$(start_ip_monitor route)
1766	run_cmd "$IP ro add 172.16.101.1/32 nhid 122"
1767	stop_ip_monitor $ipmout 1
1768	# route add notification should not contain expanded nexthops
1769	log_test $? 0 "IPv4 compat mode off - route add notification"
1770
1771	# route dump should not contain expanded nexthops
1772	check_route "172.16.101.1" "172.16.101.1 nhid 122"
1773	log_test $? 0 "IPv4 compat mode off - route dump"
1774
1775	# change in nexthop group should not generate route notification
1776	ipmout=$(start_ip_monitor route)
1777	run_cmd "$IP nexthop replace id 122 group 21/22"
1778	stop_ip_monitor $ipmout 0
1779	log_test $? 0 "IPv4 compat mode off - nexthop change"
1780
1781	# nexthop delete should not generate route notification
1782	ipmout=$(start_ip_monitor route)
1783	run_cmd "$IP nexthop del id 122"
1784	stop_ip_monitor $ipmout 0
1785	log_test $? 0 "IPv4 compat mode off - nexthop delete"
1786
1787	sysctl_nexthop_compat_mode_set 1 "IPv4"
1788}
1789
1790ipv4_del_add_loop1()
1791{
1792	while :; do
1793		$IP nexthop del id 100
1794		$IP nexthop add id 100 via 172.16.1.2 dev veth1
1795	done >/dev/null 2>&1
1796}
1797
1798ipv4_grp_replace_loop()
1799{
1800	while :; do
1801		$IP nexthop replace id 102 group 100/101
1802	done >/dev/null 2>&1
1803}
1804
1805ipv4_torture()
1806{
1807	local pid1
1808	local pid2
1809	local pid3
1810	local pid4
1811	local pid5
1812
1813	echo
1814	echo "IPv4 runtime torture"
1815	echo "--------------------"
1816	if [ ! -x "$(command -v mausezahn)" ]; then
1817		echo "SKIP: Could not run test; need mausezahn tool"
1818		return
1819	fi
1820
1821	run_cmd "$IP nexthop add id 100 via 172.16.1.2 dev veth1"
1822	run_cmd "$IP nexthop add id 101 via 172.16.2.2 dev veth3"
1823	run_cmd "$IP nexthop add id 102 group 100/101"
1824	run_cmd "$IP route add 172.16.101.1 nhid 102"
1825	run_cmd "$IP route add 172.16.101.2 nhid 102"
1826
1827	ipv4_del_add_loop1 &
1828	pid1=$!
1829	ipv4_grp_replace_loop &
1830	pid2=$!
1831	ip netns exec me ping -f 172.16.101.1 >/dev/null 2>&1 &
1832	pid3=$!
1833	ip netns exec me ping -f 172.16.101.2 >/dev/null 2>&1 &
1834	pid4=$!
1835	ip netns exec me mausezahn veth1 -B 172.16.101.2 -A 172.16.1.1 -c 0 -t tcp "dp=1-1023, flags=syn" >/dev/null 2>&1 &
1836	pid5=$!
1837
1838	sleep 300
1839	kill -9 $pid1 $pid2 $pid3 $pid4 $pid5
1840	wait $pid1 $pid2 $pid3 $pid4 $pid5 2>/dev/null
1841
1842	# if we did not crash, success
1843	log_test 0 0 "IPv4 torture test"
1844}
1845
1846ipv4_res_grp_replace_loop()
1847{
1848	while :; do
1849		$IP nexthop replace id 102 group 100/101 type resilient
1850	done >/dev/null 2>&1
1851}
1852
1853ipv4_res_torture()
1854{
1855	local pid1
1856	local pid2
1857	local pid3
1858	local pid4
1859	local pid5
1860
1861	echo
1862	echo "IPv4 runtime resilient nexthop group torture"
1863	echo "--------------------------------------------"
1864
1865	check_nexthop_res_support
1866	if [ $? -eq $ksft_skip ]; then
1867		return $ksft_skip
1868	fi
1869
1870	if [ ! -x "$(command -v mausezahn)" ]; then
1871		echo "SKIP: Could not run test; need mausezahn tool"
1872		return
1873	fi
1874
1875	run_cmd "$IP nexthop add id 100 via 172.16.1.2 dev veth1"
1876	run_cmd "$IP nexthop add id 101 via 172.16.2.2 dev veth3"
1877	run_cmd "$IP nexthop add id 102 group 100/101 type resilient buckets 512 idle_timer 0"
1878	run_cmd "$IP route add 172.16.101.1 nhid 102"
1879	run_cmd "$IP route add 172.16.101.2 nhid 102"
1880
1881	ipv4_del_add_loop1 &
1882	pid1=$!
1883	ipv4_res_grp_replace_loop &
1884	pid2=$!
1885	ip netns exec me ping -f 172.16.101.1 >/dev/null 2>&1 &
1886	pid3=$!
1887	ip netns exec me ping -f 172.16.101.2 >/dev/null 2>&1 &
1888	pid4=$!
1889	ip netns exec me mausezahn veth1 \
1890				-B 172.16.101.2 -A 172.16.1.1 -c 0 \
1891				-t tcp "dp=1-1023, flags=syn" >/dev/null 2>&1 &
1892	pid5=$!
1893
1894	sleep 300
1895	kill -9 $pid1 $pid2 $pid3 $pid4 $pid5
1896	wait $pid1 $pid2 $pid3 $pid4 $pid5 2>/dev/null
1897
1898	# if we did not crash, success
1899	log_test 0 0 "IPv4 resilient nexthop group torture test"
1900}
1901
1902basic()
1903{
1904	echo
1905	echo "Basic functional tests"
1906	echo "----------------------"
1907	run_cmd "$IP nexthop ls"
1908	log_test $? 0 "List with nothing defined"
1909
1910	run_cmd "$IP nexthop get id 1"
1911	log_test $? 2 "Nexthop get on non-existent id"
1912
1913	# attempt to create nh without a device or gw - fails
1914	run_cmd "$IP nexthop add id 1"
1915	log_test $? 2 "Nexthop with no device or gateway"
1916
1917	# attempt to create nh with down device - fails
1918	$IP li set veth1 down
1919	run_cmd "$IP nexthop add id 1 dev veth1"
1920	log_test $? 2 "Nexthop with down device"
1921
1922	# create nh with linkdown device - fails
1923	$IP li set veth1 up
1924	ip -netns peer li set veth2 down
1925	run_cmd "$IP nexthop add id 1 dev veth1"
1926	log_test $? 2 "Nexthop with device that is linkdown"
1927	ip -netns peer li set veth2 up
1928
1929	# device only
1930	run_cmd "$IP nexthop add id 1 dev veth1"
1931	log_test $? 0 "Nexthop with device only"
1932
1933	# create nh with duplicate id
1934	run_cmd "$IP nexthop add id 1 dev veth3"
1935	log_test $? 2 "Nexthop with duplicate id"
1936
1937	# blackhole nexthop
1938	run_cmd "$IP nexthop add id 2 blackhole"
1939	log_test $? 0 "Blackhole nexthop"
1940
1941	# blackhole nexthop can not have other specs
1942	run_cmd "$IP nexthop replace id 2 blackhole dev veth1"
1943	log_test $? 2 "Blackhole nexthop with other attributes"
1944
1945	# blackhole nexthop should not be affected by the state of the loopback
1946	# device
1947	run_cmd "$IP link set dev lo down"
1948	check_nexthop "id 2" "id 2 blackhole"
1949	log_test $? 0 "Blackhole nexthop with loopback device down"
1950
1951	run_cmd "$IP link set dev lo up"
1952
1953	#
1954	# groups
1955	#
1956
1957	run_cmd "$IP nexthop add id 101 group 1"
1958	log_test $? 0 "Create group"
1959
1960	run_cmd "$IP nexthop add id 102 group 2"
1961	log_test $? 0 "Create group with blackhole nexthop"
1962
1963	# multipath group can not have a blackhole as 1 path
1964	run_cmd "$IP nexthop add id 103 group 1/2"
1965	log_test $? 2 "Create multipath group where 1 path is a blackhole"
1966
1967	# multipath group can not have a member replaced by a blackhole
1968	run_cmd "$IP nexthop replace id 2 dev veth3"
1969	run_cmd "$IP nexthop replace id 102 group 1/2"
1970	run_cmd "$IP nexthop replace id 2 blackhole"
1971	log_test $? 2 "Multipath group can not have a member replaced by blackhole"
1972
1973	# attempt to create group with non-existent nexthop
1974	run_cmd "$IP nexthop add id 103 group 12"
1975	log_test $? 2 "Create group with non-existent nexthop"
1976
1977	# attempt to create group with same nexthop
1978	run_cmd "$IP nexthop add id 103 group 1/1"
1979	log_test $? 2 "Create group with same nexthop multiple times"
1980
1981	# replace nexthop with a group - fails
1982	run_cmd "$IP nexthop replace id 2 group 1"
1983	log_test $? 2 "Replace nexthop with nexthop group"
1984
1985	# replace nexthop group with a nexthop - fails
1986	run_cmd "$IP nexthop replace id 101 dev veth1"
1987	log_test $? 2 "Replace nexthop group with nexthop"
1988
1989	# nexthop group with other attributes fail
1990	run_cmd "$IP nexthop add id 104 group 1 dev veth1"
1991	log_test $? 2 "Nexthop group and device"
1992
1993	# Tests to ensure that flushing works as expected.
1994	run_cmd "$IP nexthop add id 105 blackhole proto 99"
1995	run_cmd "$IP nexthop add id 106 blackhole proto 100"
1996	run_cmd "$IP nexthop add id 107 blackhole proto 99"
1997	run_cmd "$IP nexthop flush proto 99"
1998	check_nexthop "id 105" ""
1999	check_nexthop "id 106" "id 106 blackhole proto 100"
2000	check_nexthop "id 107" ""
2001	run_cmd "$IP nexthop flush proto 100"
2002	check_nexthop "id 106" ""
2003
2004	run_cmd "$IP nexthop flush proto 100"
2005	log_test $? 0 "Test proto flush"
2006
2007	run_cmd "$IP nexthop add id 104 group 1 blackhole"
2008	log_test $? 2 "Nexthop group and blackhole"
2009
2010	$IP nexthop flush >/dev/null 2>&1
2011
2012	# Test to ensure that flushing with a multi-part nexthop dump works as
2013	# expected.
2014	local batch_file=$(mktemp)
2015
2016	for i in $(seq 1 $((64 * 1024))); do
2017		echo "nexthop add id $i blackhole" >> $batch_file
2018	done
2019
2020	$IP -b $batch_file
2021	$IP nexthop flush >/dev/null 2>&1
2022	[[ $($IP nexthop | wc -l) -eq 0 ]]
2023	log_test $? 0 "Large scale nexthop flushing"
2024
2025	rm $batch_file
2026}
2027
2028check_nexthop_buckets_balance()
2029{
2030	local nharg=$1; shift
2031	local ret
2032
2033	while (($# > 0)); do
2034		local selector=$1; shift
2035		local condition=$1; shift
2036		local count
2037
2038		count=$($IP -j nexthop bucket ${nharg} ${selector} | jq length)
2039		(( $count $condition ))
2040		ret=$?
2041		if ((ret != 0)); then
2042			return $ret
2043		fi
2044	done
2045
2046	return 0
2047}
2048
2049basic_res()
2050{
2051	echo
2052	echo "Basic resilient nexthop group functional tests"
2053	echo "----------------------------------------------"
2054
2055	check_nexthop_res_support
2056	if [ $? -eq $ksft_skip ]; then
2057		return $ksft_skip
2058	fi
2059
2060	run_cmd "$IP nexthop add id 1 dev veth1"
2061
2062	#
2063	# resilient nexthop group addition
2064	#
2065
2066	run_cmd "$IP nexthop add id 101 group 1 type resilient buckets 8"
2067	log_test $? 0 "Add a nexthop group with default parameters"
2068
2069	run_cmd "$IP nexthop get id 101"
2070	check_nexthop "id 101" \
2071		"id 101 group 1 type resilient buckets 8 idle_timer 120 unbalanced_timer 0 unbalanced_time 0"
2072	log_test $? 0 "Get a nexthop group with default parameters"
2073
2074	run_cmd "$IP nexthop add id 102 group 1 type resilient
2075			buckets 4 idle_timer 100 unbalanced_timer 5"
2076	run_cmd "$IP nexthop get id 102"
2077	check_nexthop "id 102" \
2078		"id 102 group 1 type resilient buckets 4 idle_timer 100 unbalanced_timer 5 unbalanced_time 0"
2079	log_test $? 0 "Get a nexthop group with non-default parameters"
2080
2081	run_cmd "$IP nexthop add id 103 group 1 type resilient buckets 0"
2082	log_test $? 2 "Add a nexthop group with 0 buckets"
2083
2084	#
2085	# resilient nexthop group replacement
2086	#
2087
2088	run_cmd "$IP nexthop replace id 101 group 1 type resilient
2089			buckets 8 idle_timer 240 unbalanced_timer 80"
2090	log_test $? 0 "Replace nexthop group parameters"
2091	check_nexthop "id 101" \
2092		"id 101 group 1 type resilient buckets 8 idle_timer 240 unbalanced_timer 80 unbalanced_time 0"
2093	log_test $? 0 "Get a nexthop group after replacing parameters"
2094
2095	run_cmd "$IP nexthop replace id 101 group 1 type resilient idle_timer 512"
2096	log_test $? 0 "Replace idle timer"
2097	check_nexthop "id 101" \
2098		"id 101 group 1 type resilient buckets 8 idle_timer 512 unbalanced_timer 80 unbalanced_time 0"
2099	log_test $? 0 "Get a nexthop group after replacing idle timer"
2100
2101	run_cmd "$IP nexthop replace id 101 group 1 type resilient unbalanced_timer 256"
2102	log_test $? 0 "Replace unbalanced timer"
2103	check_nexthop "id 101" \
2104		"id 101 group 1 type resilient buckets 8 idle_timer 512 unbalanced_timer 256 unbalanced_time 0"
2105	log_test $? 0 "Get a nexthop group after replacing unbalanced timer"
2106
2107	run_cmd "$IP nexthop replace id 101 group 1 type resilient"
2108	log_test $? 0 "Replace with no parameters"
2109	check_nexthop "id 101" \
2110		"id 101 group 1 type resilient buckets 8 idle_timer 512 unbalanced_timer 256 unbalanced_time 0"
2111	log_test $? 0 "Get a nexthop group after replacing no parameters"
2112
2113	run_cmd "$IP nexthop replace id 101 group 1"
2114	log_test $? 2 "Replace nexthop group type - implicit"
2115
2116	run_cmd "$IP nexthop replace id 101 group 1 type mpath"
2117	log_test $? 2 "Replace nexthop group type - explicit"
2118
2119	run_cmd "$IP nexthop replace id 101 group 1 type resilient buckets 1024"
2120	log_test $? 2 "Replace number of nexthop buckets"
2121
2122	check_nexthop "id 101" \
2123		"id 101 group 1 type resilient buckets 8 idle_timer 512 unbalanced_timer 256 unbalanced_time 0"
2124	log_test $? 0 "Get a nexthop group after replacing with invalid parameters"
2125
2126	#
2127	# resilient nexthop buckets dump
2128	#
2129
2130	$IP nexthop flush >/dev/null 2>&1
2131	run_cmd "$IP nexthop add id 1 dev veth1"
2132	run_cmd "$IP nexthop add id 2 dev veth3"
2133	run_cmd "$IP nexthop add id 101 group 1/2 type resilient buckets 4"
2134	run_cmd "$IP nexthop add id 201 group 1/2"
2135
2136	check_nexthop_bucket "" \
2137		"id 101 index 0 nhid 2 id 101 index 1 nhid 2 id 101 index 2 nhid 1 id 101 index 3 nhid 1"
2138	log_test $? 0 "Dump all nexthop buckets"
2139
2140	check_nexthop_bucket "list id 101" \
2141		"id 101 index 0 nhid 2 id 101 index 1 nhid 2 id 101 index 2 nhid 1 id 101 index 3 nhid 1"
2142	log_test $? 0 "Dump all nexthop buckets in a group"
2143
2144	sleep 0.1
2145	(( $($IP -j nexthop bucket list id 101 |
2146	     jq '[.[] | select(.bucket.idle_time > 0 and
2147	                       .bucket.idle_time < 2)] | length') == 4 ))
2148	log_test $? 0 "All nexthop buckets report a positive near-zero idle time"
2149
2150	check_nexthop_bucket "list dev veth1" \
2151		"id 101 index 2 nhid 1 id 101 index 3 nhid 1"
2152	log_test $? 0 "Dump all nexthop buckets with a specific nexthop device"
2153
2154	check_nexthop_bucket "list nhid 2" \
2155		"id 101 index 0 nhid 2 id 101 index 1 nhid 2"
2156	log_test $? 0 "Dump all nexthop buckets with a specific nexthop identifier"
2157
2158	run_cmd "$IP nexthop bucket list id 111"
2159	log_test $? 2 "Dump all nexthop buckets in a non-existent group"
2160
2161	run_cmd "$IP nexthop bucket list id 201"
2162	log_test $? 2 "Dump all nexthop buckets in a non-resilient group"
2163
2164	run_cmd "$IP nexthop bucket list dev bla"
2165	log_test $? 255 "Dump all nexthop buckets using a non-existent device"
2166
2167	run_cmd "$IP nexthop bucket list groups"
2168	log_test $? 255 "Dump all nexthop buckets with invalid 'groups' keyword"
2169
2170	run_cmd "$IP nexthop bucket list fdb"
2171	log_test $? 255 "Dump all nexthop buckets with invalid 'fdb' keyword"
2172
2173	#
2174	# resilient nexthop buckets get requests
2175	#
2176
2177	check_nexthop_bucket "get id 101 index 0" "id 101 index 0 nhid 2"
2178	log_test $? 0 "Get a valid nexthop bucket"
2179
2180	run_cmd "$IP nexthop bucket get id 101 index 999"
2181	log_test $? 2 "Get a nexthop bucket with valid group, but invalid index"
2182
2183	run_cmd "$IP nexthop bucket get id 201 index 0"
2184	log_test $? 2 "Get a nexthop bucket from a non-resilient group"
2185
2186	run_cmd "$IP nexthop bucket get id 999 index 0"
2187	log_test $? 2 "Get a nexthop bucket from a non-existent group"
2188
2189	#
2190	# tests for bucket migration
2191	#
2192
2193	$IP nexthop flush >/dev/null 2>&1
2194
2195	run_cmd "$IP nexthop add id 1 dev veth1"
2196	run_cmd "$IP nexthop add id 2 dev veth3"
2197	run_cmd "$IP nexthop add id 101
2198			group 1/2 type resilient buckets 10
2199			idle_timer 1 unbalanced_timer 20"
2200
2201	check_nexthop_buckets_balance "list id 101" \
2202				      "nhid 1" "== 5" \
2203				      "nhid 2" "== 5"
2204	log_test $? 0 "Initial bucket allocation"
2205
2206	run_cmd "$IP nexthop replace id 101
2207			group 1,2/2,3 type resilient"
2208	check_nexthop_buckets_balance "list id 101" \
2209				      "nhid 1" "== 4" \
2210				      "nhid 2" "== 6"
2211	log_test $? 0 "Bucket allocation after replace"
2212
2213	# Check that increase in idle timer does not make buckets appear busy.
2214	run_cmd "$IP nexthop replace id 101
2215			group 1,2/2,3 type resilient
2216			idle_timer 10"
2217	run_cmd "$IP nexthop replace id 101
2218			group 1/2 type resilient"
2219	check_nexthop_buckets_balance "list id 101" \
2220				      "nhid 1" "== 5" \
2221				      "nhid 2" "== 5"
2222	log_test $? 0 "Buckets migrated after idle timer change"
2223
2224	$IP nexthop flush >/dev/null 2>&1
2225}
2226
2227################################################################################
2228# usage
2229
2230usage()
2231{
2232	cat <<EOF
2233usage: ${0##*/} OPTS
2234
2235        -t <test>   Test(s) to run (default: all)
2236                    (options: $ALL_TESTS)
2237        -4          IPv4 tests only
2238        -6          IPv6 tests only
2239        -p          Pause on fail
2240        -P          Pause after each test before cleanup
2241        -v          verbose mode (show commands and output)
2242
2243    Runtime test
2244	-n num	    Number of nexthops to target
2245	-N    	    Use new style to install routes in DUT
2246
2247done
2248EOF
2249}
2250
2251################################################################################
2252# main
2253
2254while getopts :t:pP46hv o
2255do
2256	case $o in
2257		t) TESTS=$OPTARG;;
2258		4) TESTS=${IPV4_TESTS};;
2259		6) TESTS=${IPV6_TESTS};;
2260		p) PAUSE_ON_FAIL=yes;;
2261		P) PAUSE=yes;;
2262		v) VERBOSE=$(($VERBOSE + 1));;
2263		h) usage; exit 0;;
2264		*) usage; exit 1;;
2265	esac
2266done
2267
2268# make sure we don't pause twice
2269[ "${PAUSE}" = "yes" ] && PAUSE_ON_FAIL=no
2270
2271if [ "$(id -u)" -ne 0 ];then
2272	echo "SKIP: Need root privileges"
2273	exit $ksft_skip;
2274fi
2275
2276if [ ! -x "$(command -v ip)" ]; then
2277	echo "SKIP: Could not run test without ip tool"
2278	exit $ksft_skip
2279fi
2280
2281ip help 2>&1 | grep -q nexthop
2282if [ $? -ne 0 ]; then
2283	echo "SKIP: iproute2 too old, missing nexthop command"
2284	exit $ksft_skip
2285fi
2286
2287out=$(ip nexthop ls 2>&1 | grep -q "Operation not supported")
2288if [ $? -eq 0 ]; then
2289	echo "SKIP: kernel lacks nexthop support"
2290	exit $ksft_skip
2291fi
2292
2293for t in $TESTS
2294do
2295	case $t in
2296	none) IP="ip -netns peer"; setup; exit 0;;
2297	*) setup; $t; cleanup;;
2298	esac
2299done
2300
2301if [ "$TESTS" != "none" ]; then
2302	printf "\nTests passed: %3d\n" ${nsuccess}
2303	printf "Tests failed: %3d\n"   ${nfail}
2304fi
2305
2306exit $ret
2307