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