xref: /linux/tools/testing/selftests/net/forwarding/lib.sh (revision 860a9bed265146b10311bcadbbcef59c3af4454d)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4##############################################################################
5# Topology description. p1 looped back to p2, p3 to p4 and so on.
6
7declare -A NETIFS=(
8    [p1]=veth0
9    [p2]=veth1
10    [p3]=veth2
11    [p4]=veth3
12    [p5]=veth4
13    [p6]=veth5
14    [p7]=veth6
15    [p8]=veth7
16    [p9]=veth8
17    [p10]=veth9
18)
19
20# Port that does not have a cable connected.
21: "${NETIF_NO_CABLE:=eth8}"
22
23##############################################################################
24# Defines
25
26# Networking utilities.
27: "${PING:=ping}"
28: "${PING6:=ping6}"	# Some distros just use ping.
29: "${ARPING:=arping}"
30: "${TROUTE6:=traceroute6}"
31
32# Packet generator.
33: "${MZ:=mausezahn}"	# Some distributions use 'mz'.
34: "${MZ_DELAY:=0}"
35
36# Host configuration tools.
37: "${TEAMD:=teamd}"
38: "${MCD:=smcrouted}"
39: "${MC_CLI:=smcroutectl}"
40
41# Constants for netdevice bring-up:
42# Default time in seconds to wait for an interface to come up before giving up
43# and bailing out. Used during initial setup.
44: "${INTERFACE_TIMEOUT:=600}"
45# Like INTERFACE_TIMEOUT, but default for ad-hoc waiting in testing scripts.
46: "${WAIT_TIMEOUT:=20}"
47# Time to wait after interfaces participating in the test are all UP.
48: "${WAIT_TIME:=5}"
49
50# Whether to pause on, respectively, after a failure and before cleanup.
51: "${PAUSE_ON_FAIL:=no}"
52: "${PAUSE_ON_CLEANUP:=no}"
53
54# Whether to create virtual interfaces, and what netdevice type they should be.
55: "${NETIF_CREATE:=yes}"
56: "${NETIF_TYPE:=veth}"
57
58# Constants for ping tests:
59# How many packets should be sent.
60: "${PING_COUNT:=10}"
61# Timeout (in seconds) before ping exits regardless of how many packets have
62# been sent or received
63: "${PING_TIMEOUT:=5}"
64
65# Minimum ageing_time (in centiseconds) supported by hardware
66: "${LOW_AGEING_TIME:=1000}"
67
68# Whether to check for availability of certain tools.
69: "${REQUIRE_JQ:=yes}"
70: "${REQUIRE_MZ:=yes}"
71: "${REQUIRE_MTOOLS:=no}"
72
73# Whether to override MAC addresses on interfaces participating in the test.
74: "${STABLE_MAC_ADDRS:=no}"
75
76# Flags for tcpdump
77: "${TCPDUMP_EXTRA_FLAGS:=}"
78
79# Flags for TC filters.
80: "${TC_FLAG:=skip_hw}"
81
82# Whether the machine is "slow" -- i.e. might be incapable of running tests
83# involving heavy traffic. This might be the case on a debug kernel, a VM, or
84# e.g. a low-power board.
85: "${KSFT_MACHINE_SLOW:=no}"
86
87net_forwarding_dir=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
88
89if [[ -f $net_forwarding_dir/forwarding.config ]]; then
90	source "$net_forwarding_dir/forwarding.config"
91fi
92
93source "$net_forwarding_dir/../lib.sh"
94
95# timeout in seconds
96slowwait()
97{
98	local timeout_sec=$1; shift
99
100	loopy_wait "sleep 0.1" "$((timeout_sec * 1000))" "$@"
101}
102
103##############################################################################
104# Sanity checks
105
106check_tc_version()
107{
108	tc -j &> /dev/null
109	if [[ $? -ne 0 ]]; then
110		echo "SKIP: iproute2 too old; tc is missing JSON support"
111		exit $ksft_skip
112	fi
113}
114
115# Old versions of tc don't understand "mpls_uc"
116check_tc_mpls_support()
117{
118	local dev=$1; shift
119
120	tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \
121		matchall action pipe &> /dev/null
122	if [[ $? -ne 0 ]]; then
123		echo "SKIP: iproute2 too old; tc is missing MPLS support"
124		return $ksft_skip
125	fi
126	tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \
127		matchall
128}
129
130# Old versions of tc produce invalid json output for mpls lse statistics
131check_tc_mpls_lse_stats()
132{
133	local dev=$1; shift
134	local ret;
135
136	tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \
137		flower mpls lse depth 2                                 \
138		action continue &> /dev/null
139
140	if [[ $? -ne 0 ]]; then
141		echo "SKIP: iproute2 too old; tc-flower is missing extended MPLS support"
142		return $ksft_skip
143	fi
144
145	tc -j filter show dev $dev ingress protocol mpls_uc | jq . &> /dev/null
146	ret=$?
147	tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \
148		flower
149
150	if [[ $ret -ne 0 ]]; then
151		echo "SKIP: iproute2 too old; tc-flower produces invalid json output for extended MPLS filters"
152		return $ksft_skip
153	fi
154}
155
156check_tc_shblock_support()
157{
158	tc filter help 2>&1 | grep block &> /dev/null
159	if [[ $? -ne 0 ]]; then
160		echo "SKIP: iproute2 too old; tc is missing shared block support"
161		exit $ksft_skip
162	fi
163}
164
165check_tc_chain_support()
166{
167	tc help 2>&1|grep chain &> /dev/null
168	if [[ $? -ne 0 ]]; then
169		echo "SKIP: iproute2 too old; tc is missing chain support"
170		exit $ksft_skip
171	fi
172}
173
174check_tc_action_hw_stats_support()
175{
176	tc actions help 2>&1 | grep -q hw_stats
177	if [[ $? -ne 0 ]]; then
178		echo "SKIP: iproute2 too old; tc is missing action hw_stats support"
179		exit $ksft_skip
180	fi
181}
182
183check_tc_fp_support()
184{
185	tc qdisc add dev lo mqprio help 2>&1 | grep -q "fp "
186	if [[ $? -ne 0 ]]; then
187		echo "SKIP: iproute2 too old; tc is missing frame preemption support"
188		exit $ksft_skip
189	fi
190}
191
192check_ethtool_lanes_support()
193{
194	ethtool --help 2>&1| grep lanes &> /dev/null
195	if [[ $? -ne 0 ]]; then
196		echo "SKIP: ethtool too old; it is missing lanes support"
197		exit $ksft_skip
198	fi
199}
200
201check_ethtool_mm_support()
202{
203	ethtool --help 2>&1| grep -- '--show-mm' &> /dev/null
204	if [[ $? -ne 0 ]]; then
205		echo "SKIP: ethtool too old; it is missing MAC Merge layer support"
206		exit $ksft_skip
207	fi
208}
209
210check_ethtool_counter_group_support()
211{
212	ethtool --help 2>&1| grep -- '--all-groups' &> /dev/null
213	if [[ $? -ne 0 ]]; then
214		echo "SKIP: ethtool too old; it is missing standard counter group support"
215		exit $ksft_skip
216	fi
217}
218
219check_ethtool_pmac_std_stats_support()
220{
221	local dev=$1; shift
222	local grp=$1; shift
223
224	[ 0 -ne $(ethtool --json -S $dev --all-groups --src pmac 2>/dev/null \
225		| jq ".[].\"$grp\" | length") ]
226}
227
228check_locked_port_support()
229{
230	if ! bridge -d link show | grep -q " locked"; then
231		echo "SKIP: iproute2 too old; Locked port feature not supported."
232		return $ksft_skip
233	fi
234}
235
236check_port_mab_support()
237{
238	if ! bridge -d link show | grep -q "mab"; then
239		echo "SKIP: iproute2 too old; MacAuth feature not supported."
240		return $ksft_skip
241	fi
242}
243
244if [[ "$(id -u)" -ne 0 ]]; then
245	echo "SKIP: need root privileges"
246	exit $ksft_skip
247fi
248
249if [[ "$CHECK_TC" = "yes" ]]; then
250	check_tc_version
251fi
252
253require_command()
254{
255	local cmd=$1; shift
256
257	if [[ ! -x "$(command -v "$cmd")" ]]; then
258		echo "SKIP: $cmd not installed"
259		exit $ksft_skip
260	fi
261}
262
263if [[ "$REQUIRE_JQ" = "yes" ]]; then
264	require_command jq
265fi
266if [[ "$REQUIRE_MZ" = "yes" ]]; then
267	require_command $MZ
268fi
269if [[ "$REQUIRE_MTOOLS" = "yes" ]]; then
270	# https://github.com/vladimiroltean/mtools/
271	# patched for IPv6 support
272	require_command msend
273	require_command mreceive
274fi
275
276##############################################################################
277# Command line options handling
278
279count=0
280
281while [[ $# -gt 0 ]]; do
282	if [[ "$count" -eq "0" ]]; then
283		unset NETIFS
284		declare -A NETIFS
285	fi
286	count=$((count + 1))
287	NETIFS[p$count]="$1"
288	shift
289done
290
291##############################################################################
292# Network interfaces configuration
293
294if [[ ! -v NUM_NETIFS ]]; then
295	echo "SKIP: importer does not define \"NUM_NETIFS\""
296	exit $ksft_skip
297fi
298
299if (( NUM_NETIFS > ${#NETIFS[@]} )); then
300	echo "SKIP: Importer requires $NUM_NETIFS NETIFS, but only ${#NETIFS[@]} are defined (${NETIFS[@]})"
301	exit $ksft_skip
302fi
303
304for i in $(seq ${#NETIFS[@]}); do
305	if [[ ! ${NETIFS[p$i]} ]]; then
306		echo "SKIP: NETIFS[p$i] not given"
307		exit $ksft_skip
308	fi
309done
310
311create_netif_veth()
312{
313	local i
314
315	for ((i = 1; i <= NUM_NETIFS; ++i)); do
316		local j=$((i+1))
317
318		if [ -z ${NETIFS[p$i]} ]; then
319			echo "SKIP: Cannot create interface. Name not specified"
320			exit $ksft_skip
321		fi
322
323		ip link show dev ${NETIFS[p$i]} &> /dev/null
324		if [[ $? -ne 0 ]]; then
325			ip link add ${NETIFS[p$i]} type veth \
326				peer name ${NETIFS[p$j]}
327			if [[ $? -ne 0 ]]; then
328				echo "Failed to create netif"
329				exit 1
330			fi
331		fi
332		i=$j
333	done
334}
335
336create_netif()
337{
338	case "$NETIF_TYPE" in
339	veth) create_netif_veth
340	      ;;
341	*) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
342	   exit 1
343	   ;;
344	esac
345}
346
347declare -A MAC_ADDR_ORIG
348mac_addr_prepare()
349{
350	local new_addr=
351	local dev=
352
353	for ((i = 1; i <= NUM_NETIFS; ++i)); do
354		dev=${NETIFS[p$i]}
355		new_addr=$(printf "00:01:02:03:04:%02x" $i)
356
357		MAC_ADDR_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].address')
358		# Strip quotes
359		MAC_ADDR_ORIG["$dev"]=${MAC_ADDR_ORIG["$dev"]//\"/}
360		ip link set dev $dev address $new_addr
361	done
362}
363
364mac_addr_restore()
365{
366	local dev=
367
368	for ((i = 1; i <= NUM_NETIFS; ++i)); do
369		dev=${NETIFS[p$i]}
370		ip link set dev $dev address ${MAC_ADDR_ORIG["$dev"]}
371	done
372}
373
374if [[ "$NETIF_CREATE" = "yes" ]]; then
375	create_netif
376fi
377
378if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then
379	mac_addr_prepare
380fi
381
382for ((i = 1; i <= NUM_NETIFS; ++i)); do
383	ip link show dev ${NETIFS[p$i]} &> /dev/null
384	if [[ $? -ne 0 ]]; then
385		echo "SKIP: could not find all required interfaces"
386		exit $ksft_skip
387	fi
388done
389
390##############################################################################
391# Helpers
392
393# Exit status to return at the end. Set in case one of the tests fails.
394EXIT_STATUS=0
395# Per-test return value. Clear at the beginning of each test.
396RET=0
397
398ret_set_ksft_status()
399{
400	local ksft_status=$1; shift
401	local msg=$1; shift
402
403	RET=$(ksft_status_merge $RET $ksft_status)
404	if (( $? )); then
405		retmsg=$msg
406	fi
407}
408
409# Whether FAILs should be interpreted as XFAILs. Internal.
410FAIL_TO_XFAIL=
411
412check_err()
413{
414	local err=$1
415	local msg=$2
416
417	if ((err)); then
418		if [[ $FAIL_TO_XFAIL = yes ]]; then
419			ret_set_ksft_status $ksft_xfail "$msg"
420		else
421			ret_set_ksft_status $ksft_fail "$msg"
422		fi
423	fi
424}
425
426check_fail()
427{
428	local err=$1
429	local msg=$2
430
431	check_err $((!err)) "$msg"
432}
433
434check_err_fail()
435{
436	local should_fail=$1; shift
437	local err=$1; shift
438	local what=$1; shift
439
440	if ((should_fail)); then
441		check_fail $err "$what succeeded, but should have failed"
442	else
443		check_err $err "$what failed"
444	fi
445}
446
447xfail_on_slow()
448{
449	if [[ $KSFT_MACHINE_SLOW = yes ]]; then
450		FAIL_TO_XFAIL=yes "$@"
451	else
452		"$@"
453	fi
454}
455
456xfail_on_veth()
457{
458	local dev=$1; shift
459	local kind
460
461	kind=$(ip -j -d link show dev $dev |
462			jq -r '.[].linkinfo.info_kind')
463	if [[ $kind = veth ]]; then
464		FAIL_TO_XFAIL=yes "$@"
465	else
466		"$@"
467	fi
468}
469
470log_test_result()
471{
472	local test_name=$1; shift
473	local opt_str=$1; shift
474	local result=$1; shift
475	local retmsg=$1; shift
476
477	printf "TEST: %-60s  [%s]\n" "$test_name $opt_str" "$result"
478	if [[ $retmsg ]]; then
479		printf "\t%s\n" "$retmsg"
480	fi
481}
482
483pause_on_fail()
484{
485	if [[ $PAUSE_ON_FAIL == yes ]]; then
486		echo "Hit enter to continue, 'q' to quit"
487		read a
488		[[ $a == q ]] && exit 1
489	fi
490}
491
492handle_test_result_pass()
493{
494	local test_name=$1; shift
495	local opt_str=$1; shift
496
497	log_test_result "$test_name" "$opt_str" " OK "
498}
499
500handle_test_result_fail()
501{
502	local test_name=$1; shift
503	local opt_str=$1; shift
504
505	log_test_result "$test_name" "$opt_str" FAIL "$retmsg"
506	pause_on_fail
507}
508
509handle_test_result_xfail()
510{
511	local test_name=$1; shift
512	local opt_str=$1; shift
513
514	log_test_result "$test_name" "$opt_str" XFAIL "$retmsg"
515	pause_on_fail
516}
517
518handle_test_result_skip()
519{
520	local test_name=$1; shift
521	local opt_str=$1; shift
522
523	log_test_result "$test_name" "$opt_str" SKIP "$retmsg"
524}
525
526log_test()
527{
528	local test_name=$1
529	local opt_str=$2
530
531	if [[ $# -eq 2 ]]; then
532		opt_str="($opt_str)"
533	fi
534
535	if ((RET == ksft_pass)); then
536		handle_test_result_pass "$test_name" "$opt_str"
537	elif ((RET == ksft_xfail)); then
538		handle_test_result_xfail "$test_name" "$opt_str"
539	elif ((RET == ksft_skip)); then
540		handle_test_result_skip "$test_name" "$opt_str"
541	else
542		handle_test_result_fail "$test_name" "$opt_str"
543	fi
544
545	EXIT_STATUS=$(ksft_exit_status_merge $EXIT_STATUS $RET)
546	return $RET
547}
548
549log_test_skip()
550{
551	RET=$ksft_skip retmsg= log_test "$@"
552}
553
554log_test_xfail()
555{
556	RET=$ksft_xfail retmsg= log_test "$@"
557}
558
559log_info()
560{
561	local msg=$1
562
563	echo "INFO: $msg"
564}
565
566not()
567{
568	"$@"
569	[[ $? != 0 ]]
570}
571
572get_max()
573{
574	local arr=("$@")
575
576	max=${arr[0]}
577	for cur in ${arr[@]}; do
578		if [[ $cur -gt $max ]]; then
579			max=$cur
580		fi
581	done
582
583	echo $max
584}
585
586grep_bridge_fdb()
587{
588	local addr=$1; shift
589	local word
590	local flag
591
592	if [ "$1" == "self" ] || [ "$1" == "master" ]; then
593		word=$1; shift
594		if [ "$1" == "-v" ]; then
595			flag=$1; shift
596		fi
597	fi
598
599	$@ | grep $addr | grep $flag "$word"
600}
601
602wait_for_port_up()
603{
604	"$@" | grep -q "Link detected: yes"
605}
606
607wait_for_offload()
608{
609	"$@" | grep -q offload
610}
611
612wait_for_trap()
613{
614	"$@" | grep -q trap
615}
616
617until_counter_is()
618{
619	local expr=$1; shift
620	local current=$("$@")
621
622	echo $((current))
623	((current $expr))
624}
625
626busywait_for_counter()
627{
628	local timeout=$1; shift
629	local delta=$1; shift
630
631	local base=$("$@")
632	busywait "$timeout" until_counter_is ">= $((base + delta))" "$@"
633}
634
635slowwait_for_counter()
636{
637	local timeout=$1; shift
638	local delta=$1; shift
639
640	local base=$("$@")
641	slowwait "$timeout" until_counter_is ">= $((base + delta))" "$@"
642}
643
644setup_wait_dev()
645{
646	local dev=$1; shift
647	local wait_time=${1:-$WAIT_TIME}; shift
648
649	setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time
650
651	if (($?)); then
652		check_err 1
653		log_test setup_wait_dev ": Interface $dev does not come up."
654		exit 1
655	fi
656}
657
658setup_wait_dev_with_timeout()
659{
660	local dev=$1; shift
661	local max_iterations=${1:-$WAIT_TIMEOUT}; shift
662	local wait_time=${1:-$WAIT_TIME}; shift
663	local i
664
665	for ((i = 1; i <= $max_iterations; ++i)); do
666		ip link show dev $dev up \
667			| grep 'state UP' &> /dev/null
668		if [[ $? -ne 0 ]]; then
669			sleep 1
670		else
671			sleep $wait_time
672			return 0
673		fi
674	done
675
676	return 1
677}
678
679setup_wait()
680{
681	local num_netifs=${1:-$NUM_NETIFS}
682	local i
683
684	for ((i = 1; i <= num_netifs; ++i)); do
685		setup_wait_dev ${NETIFS[p$i]} 0
686	done
687
688	# Make sure links are ready.
689	sleep $WAIT_TIME
690}
691
692cmd_jq()
693{
694	local cmd=$1
695	local jq_exp=$2
696	local jq_opts=$3
697	local ret
698	local output
699
700	output="$($cmd)"
701	# it the command fails, return error right away
702	ret=$?
703	if [[ $ret -ne 0 ]]; then
704		return $ret
705	fi
706	output=$(echo $output | jq -r $jq_opts "$jq_exp")
707	ret=$?
708	if [[ $ret -ne 0 ]]; then
709		return $ret
710	fi
711	echo $output
712	# return success only in case of non-empty output
713	[ ! -z "$output" ]
714}
715
716pre_cleanup()
717{
718	if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
719		echo "Pausing before cleanup, hit any key to continue"
720		read
721	fi
722
723	if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then
724		mac_addr_restore
725	fi
726}
727
728vrf_prepare()
729{
730	ip -4 rule add pref 32765 table local
731	ip -4 rule del pref 0
732	ip -6 rule add pref 32765 table local
733	ip -6 rule del pref 0
734}
735
736vrf_cleanup()
737{
738	ip -6 rule add pref 0 table local
739	ip -6 rule del pref 32765
740	ip -4 rule add pref 0 table local
741	ip -4 rule del pref 32765
742}
743
744__last_tb_id=0
745declare -A __TB_IDS
746
747__vrf_td_id_assign()
748{
749	local vrf_name=$1
750
751	__last_tb_id=$((__last_tb_id + 1))
752	__TB_IDS[$vrf_name]=$__last_tb_id
753	return $__last_tb_id
754}
755
756__vrf_td_id_lookup()
757{
758	local vrf_name=$1
759
760	return ${__TB_IDS[$vrf_name]}
761}
762
763vrf_create()
764{
765	local vrf_name=$1
766	local tb_id
767
768	__vrf_td_id_assign $vrf_name
769	tb_id=$?
770
771	ip link add dev $vrf_name type vrf table $tb_id
772	ip -4 route add table $tb_id unreachable default metric 4278198272
773	ip -6 route add table $tb_id unreachable default metric 4278198272
774}
775
776vrf_destroy()
777{
778	local vrf_name=$1
779	local tb_id
780
781	__vrf_td_id_lookup $vrf_name
782	tb_id=$?
783
784	ip -6 route del table $tb_id unreachable default metric 4278198272
785	ip -4 route del table $tb_id unreachable default metric 4278198272
786	ip link del dev $vrf_name
787}
788
789__addr_add_del()
790{
791	local if_name=$1
792	local add_del=$2
793	local array
794
795	shift
796	shift
797	array=("${@}")
798
799	for addrstr in "${array[@]}"; do
800		ip address $add_del $addrstr dev $if_name
801	done
802}
803
804__simple_if_init()
805{
806	local if_name=$1; shift
807	local vrf_name=$1; shift
808	local addrs=("${@}")
809
810	ip link set dev $if_name master $vrf_name
811	ip link set dev $if_name up
812
813	__addr_add_del $if_name add "${addrs[@]}"
814}
815
816__simple_if_fini()
817{
818	local if_name=$1; shift
819	local addrs=("${@}")
820
821	__addr_add_del $if_name del "${addrs[@]}"
822
823	ip link set dev $if_name down
824	ip link set dev $if_name nomaster
825}
826
827simple_if_init()
828{
829	local if_name=$1
830	local vrf_name
831	local array
832
833	shift
834	vrf_name=v$if_name
835	array=("${@}")
836
837	vrf_create $vrf_name
838	ip link set dev $vrf_name up
839	__simple_if_init $if_name $vrf_name "${array[@]}"
840}
841
842simple_if_fini()
843{
844	local if_name=$1
845	local vrf_name
846	local array
847
848	shift
849	vrf_name=v$if_name
850	array=("${@}")
851
852	__simple_if_fini $if_name "${array[@]}"
853	vrf_destroy $vrf_name
854}
855
856tunnel_create()
857{
858	local name=$1; shift
859	local type=$1; shift
860	local local=$1; shift
861	local remote=$1; shift
862
863	ip link add name $name type $type \
864	   local $local remote $remote "$@"
865	ip link set dev $name up
866}
867
868tunnel_destroy()
869{
870	local name=$1; shift
871
872	ip link del dev $name
873}
874
875vlan_create()
876{
877	local if_name=$1; shift
878	local vid=$1; shift
879	local vrf=$1; shift
880	local ips=("${@}")
881	local name=$if_name.$vid
882
883	ip link add name $name link $if_name type vlan id $vid
884	if [ "$vrf" != "" ]; then
885		ip link set dev $name master $vrf
886	fi
887	ip link set dev $name up
888	__addr_add_del $name add "${ips[@]}"
889}
890
891vlan_destroy()
892{
893	local if_name=$1; shift
894	local vid=$1; shift
895	local name=$if_name.$vid
896
897	ip link del dev $name
898}
899
900team_create()
901{
902	local if_name=$1; shift
903	local mode=$1; shift
904
905	require_command $TEAMD
906	$TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
907	for slave in "$@"; do
908		ip link set dev $slave down
909		ip link set dev $slave master $if_name
910		ip link set dev $slave up
911	done
912	ip link set dev $if_name up
913}
914
915team_destroy()
916{
917	local if_name=$1; shift
918
919	$TEAMD -t $if_name -k
920}
921
922master_name_get()
923{
924	local if_name=$1
925
926	ip -j link show dev $if_name | jq -r '.[]["master"]'
927}
928
929link_stats_get()
930{
931	local if_name=$1; shift
932	local dir=$1; shift
933	local stat=$1; shift
934
935	ip -j -s link show dev $if_name \
936		| jq '.[]["stats64"]["'$dir'"]["'$stat'"]'
937}
938
939link_stats_tx_packets_get()
940{
941	link_stats_get $1 tx packets
942}
943
944link_stats_rx_errors_get()
945{
946	link_stats_get $1 rx errors
947}
948
949tc_rule_stats_get()
950{
951	local dev=$1; shift
952	local pref=$1; shift
953	local dir=$1; shift
954	local selector=${1:-.packets}; shift
955
956	tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \
957	    | jq ".[1].options.actions[].stats$selector"
958}
959
960tc_rule_handle_stats_get()
961{
962	local id=$1; shift
963	local handle=$1; shift
964	local selector=${1:-.packets}; shift
965	local netns=${1:-""}; shift
966
967	tc $netns -j -s filter show $id \
968	    | jq ".[] | select(.options.handle == $handle) | \
969		  .options.actions[0].stats$selector"
970}
971
972ethtool_stats_get()
973{
974	local dev=$1; shift
975	local stat=$1; shift
976
977	ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
978}
979
980ethtool_std_stats_get()
981{
982	local dev=$1; shift
983	local grp=$1; shift
984	local name=$1; shift
985	local src=$1; shift
986
987	ethtool --json -S $dev --groups $grp -- --src $src | \
988		jq '.[]."'"$grp"'"."'$name'"'
989}
990
991qdisc_stats_get()
992{
993	local dev=$1; shift
994	local handle=$1; shift
995	local selector=$1; shift
996
997	tc -j -s qdisc show dev "$dev" \
998	    | jq '.[] | select(.handle == "'"$handle"'") | '"$selector"
999}
1000
1001qdisc_parent_stats_get()
1002{
1003	local dev=$1; shift
1004	local parent=$1; shift
1005	local selector=$1; shift
1006
1007	tc -j -s qdisc show dev "$dev" invisible \
1008	    | jq '.[] | select(.parent == "'"$parent"'") | '"$selector"
1009}
1010
1011ipv6_stats_get()
1012{
1013	local dev=$1; shift
1014	local stat=$1; shift
1015
1016	cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f2
1017}
1018
1019hw_stats_get()
1020{
1021	local suite=$1; shift
1022	local if_name=$1; shift
1023	local dir=$1; shift
1024	local stat=$1; shift
1025
1026	ip -j stats show dev $if_name group offload subgroup $suite |
1027		jq ".[0].stats64.$dir.$stat"
1028}
1029
1030__nh_stats_get()
1031{
1032	local key=$1; shift
1033	local group_id=$1; shift
1034	local member_id=$1; shift
1035
1036	ip -j -s -s nexthop show id $group_id |
1037	    jq --argjson member_id "$member_id" --arg key "$key" \
1038	       '.[].group_stats[] | select(.id == $member_id) | .[$key]'
1039}
1040
1041nh_stats_get()
1042{
1043	local group_id=$1; shift
1044	local member_id=$1; shift
1045
1046	__nh_stats_get packets "$group_id" "$member_id"
1047}
1048
1049nh_stats_get_hw()
1050{
1051	local group_id=$1; shift
1052	local member_id=$1; shift
1053
1054	__nh_stats_get packets_hw "$group_id" "$member_id"
1055}
1056
1057humanize()
1058{
1059	local speed=$1; shift
1060
1061	for unit in bps Kbps Mbps Gbps; do
1062		if (($(echo "$speed < 1024" | bc))); then
1063			break
1064		fi
1065
1066		speed=$(echo "scale=1; $speed / 1024" | bc)
1067	done
1068
1069	echo "$speed${unit}"
1070}
1071
1072rate()
1073{
1074	local t0=$1; shift
1075	local t1=$1; shift
1076	local interval=$1; shift
1077
1078	echo $((8 * (t1 - t0) / interval))
1079}
1080
1081packets_rate()
1082{
1083	local t0=$1; shift
1084	local t1=$1; shift
1085	local interval=$1; shift
1086
1087	echo $(((t1 - t0) / interval))
1088}
1089
1090mac_get()
1091{
1092	local if_name=$1
1093
1094	ip -j link show dev $if_name | jq -r '.[]["address"]'
1095}
1096
1097ipv6_lladdr_get()
1098{
1099	local if_name=$1
1100
1101	ip -j addr show dev $if_name | \
1102		jq -r '.[]["addr_info"][] | select(.scope == "link").local' | \
1103		head -1
1104}
1105
1106bridge_ageing_time_get()
1107{
1108	local bridge=$1
1109	local ageing_time
1110
1111	# Need to divide by 100 to convert to seconds.
1112	ageing_time=$(ip -j -d link show dev $bridge \
1113		      | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
1114	echo $((ageing_time / 100))
1115}
1116
1117declare -A SYSCTL_ORIG
1118sysctl_set()
1119{
1120	local key=$1; shift
1121	local value=$1; shift
1122
1123	SYSCTL_ORIG[$key]=$(sysctl -n $key)
1124	sysctl -qw $key="$value"
1125}
1126
1127sysctl_restore()
1128{
1129	local key=$1; shift
1130
1131	sysctl -qw $key="${SYSCTL_ORIG[$key]}"
1132}
1133
1134forwarding_enable()
1135{
1136	sysctl_set net.ipv4.conf.all.forwarding 1
1137	sysctl_set net.ipv6.conf.all.forwarding 1
1138}
1139
1140forwarding_restore()
1141{
1142	sysctl_restore net.ipv6.conf.all.forwarding
1143	sysctl_restore net.ipv4.conf.all.forwarding
1144}
1145
1146declare -A MTU_ORIG
1147mtu_set()
1148{
1149	local dev=$1; shift
1150	local mtu=$1; shift
1151
1152	MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')
1153	ip link set dev $dev mtu $mtu
1154}
1155
1156mtu_restore()
1157{
1158	local dev=$1; shift
1159
1160	ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
1161}
1162
1163tc_offload_check()
1164{
1165	local num_netifs=${1:-$NUM_NETIFS}
1166
1167	for ((i = 1; i <= num_netifs; ++i)); do
1168		ethtool -k ${NETIFS[p$i]} \
1169			| grep "hw-tc-offload: on" &> /dev/null
1170		if [[ $? -ne 0 ]]; then
1171			return 1
1172		fi
1173	done
1174
1175	return 0
1176}
1177
1178trap_install()
1179{
1180	local dev=$1; shift
1181	local direction=$1; shift
1182
1183	# Some devices may not support or need in-hardware trapping of traffic
1184	# (e.g. the veth pairs that this library creates for non-existent
1185	# loopbacks). Use continue instead, so that there is a filter in there
1186	# (some tests check counters), and so that other filters are still
1187	# processed.
1188	tc filter add dev $dev $direction pref 1 \
1189		flower skip_sw action trap 2>/dev/null \
1190	    || tc filter add dev $dev $direction pref 1 \
1191		       flower action continue
1192}
1193
1194trap_uninstall()
1195{
1196	local dev=$1; shift
1197	local direction=$1; shift
1198
1199	tc filter del dev $dev $direction pref 1 flower
1200}
1201
1202slow_path_trap_install()
1203{
1204	# For slow-path testing, we need to install a trap to get to
1205	# slow path the packets that would otherwise be switched in HW.
1206	if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
1207		trap_install "$@"
1208	fi
1209}
1210
1211slow_path_trap_uninstall()
1212{
1213	if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
1214		trap_uninstall "$@"
1215	fi
1216}
1217
1218__icmp_capture_add_del()
1219{
1220	local add_del=$1; shift
1221	local pref=$1; shift
1222	local vsuf=$1; shift
1223	local tundev=$1; shift
1224	local filter=$1; shift
1225
1226	tc filter $add_del dev "$tundev" ingress \
1227	   proto ip$vsuf pref $pref \
1228	   flower ip_proto icmp$vsuf $filter \
1229	   action pass
1230}
1231
1232icmp_capture_install()
1233{
1234	__icmp_capture_add_del add 100 "" "$@"
1235}
1236
1237icmp_capture_uninstall()
1238{
1239	__icmp_capture_add_del del 100 "" "$@"
1240}
1241
1242icmp6_capture_install()
1243{
1244	__icmp_capture_add_del add 100 v6 "$@"
1245}
1246
1247icmp6_capture_uninstall()
1248{
1249	__icmp_capture_add_del del 100 v6 "$@"
1250}
1251
1252__vlan_capture_add_del()
1253{
1254	local add_del=$1; shift
1255	local pref=$1; shift
1256	local dev=$1; shift
1257	local filter=$1; shift
1258
1259	tc filter $add_del dev "$dev" ingress \
1260	   proto 802.1q pref $pref \
1261	   flower $filter \
1262	   action pass
1263}
1264
1265vlan_capture_install()
1266{
1267	__vlan_capture_add_del add 100 "$@"
1268}
1269
1270vlan_capture_uninstall()
1271{
1272	__vlan_capture_add_del del 100 "$@"
1273}
1274
1275__dscp_capture_add_del()
1276{
1277	local add_del=$1; shift
1278	local dev=$1; shift
1279	local base=$1; shift
1280	local dscp;
1281
1282	for prio in {0..7}; do
1283		dscp=$((base + prio))
1284		__icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
1285				       "skip_hw ip_tos $((dscp << 2))"
1286	done
1287}
1288
1289dscp_capture_install()
1290{
1291	local dev=$1; shift
1292	local base=$1; shift
1293
1294	__dscp_capture_add_del add $dev $base
1295}
1296
1297dscp_capture_uninstall()
1298{
1299	local dev=$1; shift
1300	local base=$1; shift
1301
1302	__dscp_capture_add_del del $dev $base
1303}
1304
1305dscp_fetch_stats()
1306{
1307	local dev=$1; shift
1308	local base=$1; shift
1309
1310	for prio in {0..7}; do
1311		local dscp=$((base + prio))
1312		local t=$(tc_rule_stats_get $dev $((dscp + 100)))
1313		echo "[$dscp]=$t "
1314	done
1315}
1316
1317matchall_sink_create()
1318{
1319	local dev=$1; shift
1320
1321	tc qdisc add dev $dev clsact
1322	tc filter add dev $dev ingress \
1323	   pref 10000 \
1324	   matchall \
1325	   action drop
1326}
1327
1328tests_run()
1329{
1330	local current_test
1331
1332	for current_test in ${TESTS:-$ALL_TESTS}; do
1333		$current_test
1334	done
1335}
1336
1337multipath_eval()
1338{
1339	local desc="$1"
1340	local weight_rp12=$2
1341	local weight_rp13=$3
1342	local packets_rp12=$4
1343	local packets_rp13=$5
1344	local weights_ratio packets_ratio diff
1345
1346	RET=0
1347
1348	if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1349		weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
1350				| bc -l)
1351	else
1352		weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
1353				| bc -l)
1354	fi
1355
1356	if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
1357	       check_err 1 "Packet difference is 0"
1358	       log_test "Multipath"
1359	       log_info "Expected ratio $weights_ratio"
1360	       return
1361	fi
1362
1363	if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1364		packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
1365				| bc -l)
1366	else
1367		packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
1368				| bc -l)
1369	fi
1370
1371	diff=$(echo $weights_ratio - $packets_ratio | bc -l)
1372	diff=${diff#-}
1373
1374	test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
1375	check_err $? "Too large discrepancy between expected and measured ratios"
1376	log_test "$desc"
1377	log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
1378}
1379
1380in_ns()
1381{
1382	local name=$1; shift
1383
1384	ip netns exec $name bash <<-EOF
1385		NUM_NETIFS=0
1386		source lib.sh
1387		$(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
1388	EOF
1389}
1390
1391##############################################################################
1392# Tests
1393
1394ping_do()
1395{
1396	local if_name=$1
1397	local dip=$2
1398	local args=$3
1399	local vrf_name
1400
1401	vrf_name=$(master_name_get $if_name)
1402	ip vrf exec $vrf_name \
1403		$PING $args $dip -c $PING_COUNT -i 0.1 \
1404		-w $PING_TIMEOUT &> /dev/null
1405}
1406
1407ping_test()
1408{
1409	RET=0
1410
1411	ping_do $1 $2
1412	check_err $?
1413	log_test "ping$3"
1414}
1415
1416ping_test_fails()
1417{
1418	RET=0
1419
1420	ping_do $1 $2
1421	check_fail $?
1422	log_test "ping fails$3"
1423}
1424
1425ping6_do()
1426{
1427	local if_name=$1
1428	local dip=$2
1429	local args=$3
1430	local vrf_name
1431
1432	vrf_name=$(master_name_get $if_name)
1433	ip vrf exec $vrf_name \
1434		$PING6 $args $dip -c $PING_COUNT -i 0.1 \
1435		-w $PING_TIMEOUT &> /dev/null
1436}
1437
1438ping6_test()
1439{
1440	RET=0
1441
1442	ping6_do $1 $2
1443	check_err $?
1444	log_test "ping6$3"
1445}
1446
1447ping6_test_fails()
1448{
1449	RET=0
1450
1451	ping6_do $1 $2
1452	check_fail $?
1453	log_test "ping6 fails$3"
1454}
1455
1456learning_test()
1457{
1458	local bridge=$1
1459	local br_port1=$2	# Connected to `host1_if`.
1460	local host1_if=$3
1461	local host2_if=$4
1462	local mac=de:ad:be:ef:13:37
1463	local ageing_time
1464
1465	RET=0
1466
1467	bridge -j fdb show br $bridge brport $br_port1 \
1468		| jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1469	check_fail $? "Found FDB record when should not"
1470
1471	# Disable unknown unicast flooding on `br_port1` to make sure
1472	# packets are only forwarded through the port after a matching
1473	# FDB entry was installed.
1474	bridge link set dev $br_port1 flood off
1475
1476	ip link set $host1_if promisc on
1477	tc qdisc add dev $host1_if ingress
1478	tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
1479		flower dst_mac $mac action drop
1480
1481	$MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1482	sleep 1
1483
1484	tc -j -s filter show dev $host1_if ingress \
1485		| jq -e ".[] | select(.options.handle == 101) \
1486		| select(.options.actions[0].stats.packets == 1)" &> /dev/null
1487	check_fail $? "Packet reached first host when should not"
1488
1489	$MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1490	sleep 1
1491
1492	bridge -j fdb show br $bridge brport $br_port1 \
1493		| jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1494	check_err $? "Did not find FDB record when should"
1495
1496	$MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1497	sleep 1
1498
1499	tc -j -s filter show dev $host1_if ingress \
1500		| jq -e ".[] | select(.options.handle == 101) \
1501		| select(.options.actions[0].stats.packets == 1)" &> /dev/null
1502	check_err $? "Packet did not reach second host when should"
1503
1504	# Wait for 10 seconds after the ageing time to make sure FDB
1505	# record was aged-out.
1506	ageing_time=$(bridge_ageing_time_get $bridge)
1507	sleep $((ageing_time + 10))
1508
1509	bridge -j fdb show br $bridge brport $br_port1 \
1510		| jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1511	check_fail $? "Found FDB record when should not"
1512
1513	bridge link set dev $br_port1 learning off
1514
1515	$MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1516	sleep 1
1517
1518	bridge -j fdb show br $bridge brport $br_port1 \
1519		| jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1520	check_fail $? "Found FDB record when should not"
1521
1522	bridge link set dev $br_port1 learning on
1523
1524	tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
1525	tc qdisc del dev $host1_if ingress
1526	ip link set $host1_if promisc off
1527
1528	bridge link set dev $br_port1 flood on
1529
1530	log_test "FDB learning"
1531}
1532
1533flood_test_do()
1534{
1535	local should_flood=$1
1536	local mac=$2
1537	local ip=$3
1538	local host1_if=$4
1539	local host2_if=$5
1540	local err=0
1541
1542	# Add an ACL on `host2_if` which will tell us whether the packet
1543	# was flooded to it or not.
1544	ip link set $host2_if promisc on
1545	tc qdisc add dev $host2_if ingress
1546	tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
1547		flower dst_mac $mac action drop
1548
1549	$MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
1550	sleep 1
1551
1552	tc -j -s filter show dev $host2_if ingress \
1553		| jq -e ".[] | select(.options.handle == 101) \
1554		| select(.options.actions[0].stats.packets == 1)" &> /dev/null
1555	if [[ $? -ne 0 && $should_flood == "true" || \
1556	      $? -eq 0 && $should_flood == "false" ]]; then
1557		err=1
1558	fi
1559
1560	tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
1561	tc qdisc del dev $host2_if ingress
1562	ip link set $host2_if promisc off
1563
1564	return $err
1565}
1566
1567flood_unicast_test()
1568{
1569	local br_port=$1
1570	local host1_if=$2
1571	local host2_if=$3
1572	local mac=de:ad:be:ef:13:37
1573	local ip=192.0.2.100
1574
1575	RET=0
1576
1577	bridge link set dev $br_port flood off
1578
1579	flood_test_do false $mac $ip $host1_if $host2_if
1580	check_err $? "Packet flooded when should not"
1581
1582	bridge link set dev $br_port flood on
1583
1584	flood_test_do true $mac $ip $host1_if $host2_if
1585	check_err $? "Packet was not flooded when should"
1586
1587	log_test "Unknown unicast flood"
1588}
1589
1590flood_multicast_test()
1591{
1592	local br_port=$1
1593	local host1_if=$2
1594	local host2_if=$3
1595	local mac=01:00:5e:00:00:01
1596	local ip=239.0.0.1
1597
1598	RET=0
1599
1600	bridge link set dev $br_port mcast_flood off
1601
1602	flood_test_do false $mac $ip $host1_if $host2_if
1603	check_err $? "Packet flooded when should not"
1604
1605	bridge link set dev $br_port mcast_flood on
1606
1607	flood_test_do true $mac $ip $host1_if $host2_if
1608	check_err $? "Packet was not flooded when should"
1609
1610	log_test "Unregistered multicast flood"
1611}
1612
1613flood_test()
1614{
1615	# `br_port` is connected to `host2_if`
1616	local br_port=$1
1617	local host1_if=$2
1618	local host2_if=$3
1619
1620	flood_unicast_test $br_port $host1_if $host2_if
1621	flood_multicast_test $br_port $host1_if $host2_if
1622}
1623
1624__start_traffic()
1625{
1626	local pktsize=$1; shift
1627	local proto=$1; shift
1628	local h_in=$1; shift    # Where the traffic egresses the host
1629	local sip=$1; shift
1630	local dip=$1; shift
1631	local dmac=$1; shift
1632
1633	$MZ $h_in -p $pktsize -A $sip -B $dip -c 0 \
1634		-a own -b $dmac -t "$proto" -q "$@" &
1635	sleep 1
1636}
1637
1638start_traffic_pktsize()
1639{
1640	local pktsize=$1; shift
1641
1642	__start_traffic $pktsize udp "$@"
1643}
1644
1645start_tcp_traffic_pktsize()
1646{
1647	local pktsize=$1; shift
1648
1649	__start_traffic $pktsize tcp "$@"
1650}
1651
1652start_traffic()
1653{
1654	start_traffic_pktsize 8000 "$@"
1655}
1656
1657start_tcp_traffic()
1658{
1659	start_tcp_traffic_pktsize 8000 "$@"
1660}
1661
1662stop_traffic()
1663{
1664	# Suppress noise from killing mausezahn.
1665	{ kill %% && wait %%; } 2>/dev/null
1666}
1667
1668declare -A cappid
1669declare -A capfile
1670declare -A capout
1671
1672tcpdump_start()
1673{
1674	local if_name=$1; shift
1675	local ns=$1; shift
1676
1677	capfile[$if_name]=$(mktemp)
1678	capout[$if_name]=$(mktemp)
1679
1680	if [ -z $ns ]; then
1681		ns_cmd=""
1682	else
1683		ns_cmd="ip netns exec ${ns}"
1684	fi
1685
1686	if [ -z $SUDO_USER ] ; then
1687		capuser=""
1688	else
1689		capuser="-Z $SUDO_USER"
1690	fi
1691
1692	$ns_cmd tcpdump $TCPDUMP_EXTRA_FLAGS -e -n -Q in -i $if_name \
1693		-s 65535 -B 32768 $capuser -w ${capfile[$if_name]} \
1694		> "${capout[$if_name]}" 2>&1 &
1695	cappid[$if_name]=$!
1696
1697	sleep 1
1698}
1699
1700tcpdump_stop()
1701{
1702	local if_name=$1
1703	local pid=${cappid[$if_name]}
1704
1705	$ns_cmd kill "$pid" && wait "$pid"
1706	sleep 1
1707}
1708
1709tcpdump_cleanup()
1710{
1711	local if_name=$1
1712
1713	rm ${capfile[$if_name]} ${capout[$if_name]}
1714}
1715
1716tcpdump_show()
1717{
1718	local if_name=$1
1719
1720	tcpdump -e -n -r ${capfile[$if_name]} 2>&1
1721}
1722
1723# return 0 if the packet wasn't seen on host2_if or 1 if it was
1724mcast_packet_test()
1725{
1726	local mac=$1
1727	local src_ip=$2
1728	local ip=$3
1729	local host1_if=$4
1730	local host2_if=$5
1731	local seen=0
1732	local tc_proto="ip"
1733	local mz_v6arg=""
1734
1735	# basic check to see if we were passed an IPv4 address, if not assume IPv6
1736	if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
1737		tc_proto="ipv6"
1738		mz_v6arg="-6"
1739	fi
1740
1741	# Add an ACL on `host2_if` which will tell us whether the packet
1742	# was received by it or not.
1743	tc qdisc add dev $host2_if ingress
1744	tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \
1745		flower ip_proto udp dst_mac $mac action drop
1746
1747	$MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q
1748	sleep 1
1749
1750	tc -j -s filter show dev $host2_if ingress \
1751		| jq -e ".[] | select(.options.handle == 101) \
1752		| select(.options.actions[0].stats.packets == 1)" &> /dev/null
1753	if [[ $? -eq 0 ]]; then
1754		seen=1
1755	fi
1756
1757	tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower
1758	tc qdisc del dev $host2_if ingress
1759
1760	return $seen
1761}
1762
1763brmcast_check_sg_entries()
1764{
1765	local report=$1; shift
1766	local slist=("$@")
1767	local sarg=""
1768
1769	for src in "${slist[@]}"; do
1770		sarg="${sarg} and .source_list[].address == \"$src\""
1771	done
1772	bridge -j -d -s mdb show dev br0 \
1773		| jq -e ".[].mdb[] | \
1774			 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null
1775	check_err $? "Wrong *,G entry source list after $report report"
1776
1777	for sgent in "${slist[@]}"; do
1778		bridge -j -d -s mdb show dev br0 \
1779			| jq -e ".[].mdb[] | \
1780				 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null
1781		check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)"
1782	done
1783}
1784
1785brmcast_check_sg_fwding()
1786{
1787	local should_fwd=$1; shift
1788	local sources=("$@")
1789
1790	for src in "${sources[@]}"; do
1791		local retval=0
1792
1793		mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1
1794		retval=$?
1795		if [ $should_fwd -eq 1 ]; then
1796			check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)"
1797		else
1798			check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)"
1799		fi
1800	done
1801}
1802
1803brmcast_check_sg_state()
1804{
1805	local is_blocked=$1; shift
1806	local sources=("$@")
1807	local should_fail=1
1808
1809	if [ $is_blocked -eq 1 ]; then
1810		should_fail=0
1811	fi
1812
1813	for src in "${sources[@]}"; do
1814		bridge -j -d -s mdb show dev br0 \
1815			| jq -e ".[].mdb[] | \
1816				 select(.grp == \"$TEST_GROUP\" and .source_list != null) |
1817				 .source_list[] |
1818				 select(.address == \"$src\") |
1819				 select(.timer == \"0.00\")" &>/dev/null
1820		check_err_fail $should_fail $? "Entry $src has zero timer"
1821
1822		bridge -j -d -s mdb show dev br0 \
1823			| jq -e ".[].mdb[] | \
1824				 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \
1825				 .flags[] == \"blocked\")" &>/dev/null
1826		check_err_fail $should_fail $? "Entry $src has blocked flag"
1827	done
1828}
1829
1830mc_join()
1831{
1832	local if_name=$1
1833	local group=$2
1834	local vrf_name=$(master_name_get $if_name)
1835
1836	# We don't care about actual reception, just about joining the
1837	# IP multicast group and adding the L2 address to the device's
1838	# MAC filtering table
1839	ip vrf exec $vrf_name \
1840		mreceive -g $group -I $if_name > /dev/null 2>&1 &
1841	mreceive_pid=$!
1842
1843	sleep 1
1844}
1845
1846mc_leave()
1847{
1848	kill "$mreceive_pid" && wait "$mreceive_pid"
1849}
1850
1851mc_send()
1852{
1853	local if_name=$1
1854	local groups=$2
1855	local vrf_name=$(master_name_get $if_name)
1856
1857	ip vrf exec $vrf_name \
1858		msend -g $groups -I $if_name -c 1 > /dev/null 2>&1
1859}
1860
1861start_ip_monitor()
1862{
1863	local mtype=$1; shift
1864	local ip=${1-ip}; shift
1865
1866	# start the monitor in the background
1867	tmpfile=`mktemp /var/run/nexthoptestXXX`
1868	mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null`
1869	sleep 0.2
1870	echo "$mpid $tmpfile"
1871}
1872
1873stop_ip_monitor()
1874{
1875	local mpid=$1; shift
1876	local tmpfile=$1; shift
1877	local el=$1; shift
1878	local what=$1; shift
1879
1880	sleep 0.2
1881	kill $mpid
1882	local lines=`grep '^\w' $tmpfile | wc -l`
1883	test $lines -eq $el
1884	check_err $? "$what: $lines lines of events, expected $el"
1885	rm -rf $tmpfile
1886}
1887
1888hw_stats_monitor_test()
1889{
1890	local dev=$1; shift
1891	local type=$1; shift
1892	local make_suitable=$1; shift
1893	local make_unsuitable=$1; shift
1894	local ip=${1-ip}; shift
1895
1896	RET=0
1897
1898	# Expect a notification about enablement.
1899	local ipmout=$(start_ip_monitor stats "$ip")
1900	$ip stats set dev $dev ${type}_stats on
1901	stop_ip_monitor $ipmout 1 "${type}_stats enablement"
1902
1903	# Expect a notification about offload.
1904	local ipmout=$(start_ip_monitor stats "$ip")
1905	$make_suitable
1906	stop_ip_monitor $ipmout 1 "${type}_stats installation"
1907
1908	# Expect a notification about loss of offload.
1909	local ipmout=$(start_ip_monitor stats "$ip")
1910	$make_unsuitable
1911	stop_ip_monitor $ipmout 1 "${type}_stats deinstallation"
1912
1913	# Expect a notification about disablement
1914	local ipmout=$(start_ip_monitor stats "$ip")
1915	$ip stats set dev $dev ${type}_stats off
1916	stop_ip_monitor $ipmout 1 "${type}_stats disablement"
1917
1918	log_test "${type}_stats notifications"
1919}
1920
1921ipv4_to_bytes()
1922{
1923	local IP=$1; shift
1924
1925	printf '%02x:' ${IP//./ } |
1926	    sed 's/:$//'
1927}
1928
1929# Convert a given IPv6 address, `IP' such that the :: token, if present, is
1930# expanded, and each 16-bit group is padded with zeroes to be 4 hexadecimal
1931# digits. An optional `BYTESEP' parameter can be given to further separate
1932# individual bytes of each 16-bit group.
1933expand_ipv6()
1934{
1935	local IP=$1; shift
1936	local bytesep=$1; shift
1937
1938	local cvt_ip=${IP/::/_}
1939	local colons=${cvt_ip//[^:]/}
1940	local allcol=:::::::
1941	# IP where :: -> the appropriate number of colons:
1942	local allcol_ip=${cvt_ip/_/${allcol:${#colons}}}
1943
1944	echo $allcol_ip | tr : '\n' |
1945	    sed s/^/0000/ |
1946	    sed 's/.*\(..\)\(..\)/\1'"$bytesep"'\2/' |
1947	    tr '\n' : |
1948	    sed 's/:$//'
1949}
1950
1951ipv6_to_bytes()
1952{
1953	local IP=$1; shift
1954
1955	expand_ipv6 "$IP" :
1956}
1957
1958u16_to_bytes()
1959{
1960	local u16=$1; shift
1961
1962	printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/'
1963}
1964
1965# Given a mausezahn-formatted payload (colon-separated bytes given as %02x),
1966# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be,
1967# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any)
1968# stands for 00:00.
1969payload_template_calc_checksum()
1970{
1971	local payload=$1; shift
1972
1973	(
1974	    # Set input radix.
1975	    echo "16i"
1976	    # Push zero for the initial checksum.
1977	    echo 0
1978
1979	    # Pad the payload with a terminating 00: in case we get an odd
1980	    # number of bytes.
1981	    echo "${payload%:}:00:" |
1982		sed 's/CHECKSUM/00:00/g' |
1983		tr '[:lower:]' '[:upper:]' |
1984		# Add the word to the checksum.
1985		sed 's/\(..\):\(..\):/\1\2+\n/g' |
1986		# Strip the extra odd byte we pushed if left unconverted.
1987		sed 's/\(..\):$//'
1988
1989	    echo "10000 ~ +"	# Calculate and add carry.
1990	    echo "FFFF r - p"	# Bit-flip and print.
1991	) |
1992	    dc |
1993	    tr '[:upper:]' '[:lower:]'
1994}
1995
1996payload_template_expand_checksum()
1997{
1998	local payload=$1; shift
1999	local checksum=$1; shift
2000
2001	local ckbytes=$(u16_to_bytes $checksum)
2002
2003	echo "$payload" | sed "s/CHECKSUM/$ckbytes/g"
2004}
2005
2006payload_template_nbytes()
2007{
2008	local payload=$1; shift
2009
2010	payload_template_expand_checksum "${payload%:}" 0 |
2011		sed 's/:/\n/g' | wc -l
2012}
2013
2014igmpv3_is_in_get()
2015{
2016	local GRP=$1; shift
2017	local sources=("$@")
2018
2019	local igmpv3
2020	local nsources=$(u16_to_bytes ${#sources[@]})
2021
2022	# IS_IN ( $sources )
2023	igmpv3=$(:
2024		)"22:"$(			: Type - Membership Report
2025		)"00:"$(			: Reserved
2026		)"CHECKSUM:"$(			: Checksum
2027		)"00:00:"$(			: Reserved
2028		)"00:01:"$(			: Number of Group Records
2029		)"01:"$(			: Record Type - IS_IN
2030		)"00:"$(			: Aux Data Len
2031		)"${nsources}:"$(		: Number of Sources
2032		)"$(ipv4_to_bytes $GRP):"$(	: Multicast Address
2033		)"$(for src in "${sources[@]}"; do
2034			ipv4_to_bytes $src
2035			echo -n :
2036		    done)"$(			: Source Addresses
2037		)
2038	local checksum=$(payload_template_calc_checksum "$igmpv3")
2039
2040	payload_template_expand_checksum "$igmpv3" $checksum
2041}
2042
2043igmpv2_leave_get()
2044{
2045	local GRP=$1; shift
2046
2047	local payload=$(:
2048		)"17:"$(			: Type - Leave Group
2049		)"00:"$(			: Max Resp Time - not meaningful
2050		)"CHECKSUM:"$(			: Checksum
2051		)"$(ipv4_to_bytes $GRP)"$(	: Group Address
2052		)
2053	local checksum=$(payload_template_calc_checksum "$payload")
2054
2055	payload_template_expand_checksum "$payload" $checksum
2056}
2057
2058mldv2_is_in_get()
2059{
2060	local SIP=$1; shift
2061	local GRP=$1; shift
2062	local sources=("$@")
2063
2064	local hbh
2065	local icmpv6
2066	local nsources=$(u16_to_bytes ${#sources[@]})
2067
2068	hbh=$(:
2069		)"3a:"$(			: Next Header - ICMPv6
2070		)"00:"$(			: Hdr Ext Len
2071		)"00:00:00:00:00:00:"$(		: Options and Padding
2072		)
2073
2074	icmpv6=$(:
2075		)"8f:"$(			: Type - MLDv2 Report
2076		)"00:"$(			: Code
2077		)"CHECKSUM:"$(			: Checksum
2078		)"00:00:"$(			: Reserved
2079		)"00:01:"$(			: Number of Group Records
2080		)"01:"$(			: Record Type - IS_IN
2081		)"00:"$(			: Aux Data Len
2082		)"${nsources}:"$(		: Number of Sources
2083		)"$(ipv6_to_bytes $GRP):"$(	: Multicast address
2084		)"$(for src in "${sources[@]}"; do
2085			ipv6_to_bytes $src
2086			echo -n :
2087		    done)"$(			: Source Addresses
2088		)
2089
2090	local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6))
2091	local sudohdr=$(:
2092		)"$(ipv6_to_bytes $SIP):"$(	: SIP
2093		)"$(ipv6_to_bytes $GRP):"$(	: DIP is multicast address
2094	        )"${len}:"$(			: Upper-layer length
2095	        )"00:3a:"$(			: Zero and next-header
2096	        )
2097	local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6})
2098
2099	payload_template_expand_checksum "$hbh$icmpv6" $checksum
2100}
2101
2102mldv1_done_get()
2103{
2104	local SIP=$1; shift
2105	local GRP=$1; shift
2106
2107	local hbh
2108	local icmpv6
2109
2110	hbh=$(:
2111		)"3a:"$(			: Next Header - ICMPv6
2112		)"00:"$(			: Hdr Ext Len
2113		)"00:00:00:00:00:00:"$(		: Options and Padding
2114		)
2115
2116	icmpv6=$(:
2117		)"84:"$(			: Type - MLDv1 Done
2118		)"00:"$(			: Code
2119		)"CHECKSUM:"$(			: Checksum
2120		)"00:00:"$(			: Max Resp Delay - not meaningful
2121		)"00:00:"$(			: Reserved
2122		)"$(ipv6_to_bytes $GRP):"$(	: Multicast address
2123		)
2124
2125	local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6))
2126	local sudohdr=$(:
2127		)"$(ipv6_to_bytes $SIP):"$(	: SIP
2128		)"$(ipv6_to_bytes $GRP):"$(	: DIP is multicast address
2129	        )"${len}:"$(			: Upper-layer length
2130	        )"00:3a:"$(			: Zero and next-header
2131	        )
2132	local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6})
2133
2134	payload_template_expand_checksum "$hbh$icmpv6" $checksum
2135}
2136
2137bail_on_lldpad()
2138{
2139	local reason1="$1"; shift
2140	local reason2="$1"; shift
2141	local caller=${FUNCNAME[1]}
2142	local src=${BASH_SOURCE[1]}
2143
2144	if systemctl is-active --quiet lldpad; then
2145
2146		cat >/dev/stderr <<-EOF
2147		WARNING: lldpad is running
2148
2149			lldpad will likely $reason1, and this test will
2150			$reason2. Both are not supported at the same time,
2151			one of them is arbitrarily going to overwrite the
2152			other. That will cause spurious failures (or, unlikely,
2153			passes) of this test.
2154		EOF
2155
2156		if [[ -z $ALLOW_LLDPAD ]]; then
2157			cat >/dev/stderr <<-EOF
2158
2159				If you want to run the test anyway, please set
2160				an environment variable ALLOW_LLDPAD to a
2161				non-empty string.
2162			EOF
2163			log_test_skip $src:$caller
2164			exit $EXIT_STATUS
2165		else
2166			return
2167		fi
2168	fi
2169}
2170
2171absval()
2172{
2173	local v=$1; shift
2174
2175	echo $((v > 0 ? v : -v))
2176}
2177