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