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