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