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