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