brintos

brintos / linux-shallow public Read only

0
0
Text · 44.6 KiB · c992e38 Raw
2296 lines · bash
1#!/bin/bash2# SPDX-License-Identifier: GPL-2.03 4##############################################################################5# Topology description. p1 looped back to p2, p3 to p4 and so on.6 7declare -A NETIFS=(8    [p1]=veth09    [p2]=veth110    [p3]=veth211    [p4]=veth312    [p5]=veth413    [p6]=veth514    [p7]=veth615    [p8]=veth716    [p9]=veth817    [p10]=veth918)19 20# Port that does not have a cable connected.21: "${NETIF_NO_CABLE:=eth8}"22 23##############################################################################24# Defines25 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 up43# 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_FAIL:=no}"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 have62# been sent or received63: "${PING_TIMEOUT:=5}"64 65# Minimum ageing_time (in centiseconds) supported by hardware66: "${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 73# Whether to override MAC addresses on interfaces participating in the test.74: "${STABLE_MAC_ADDRS:=no}"75 76# Flags for tcpdump77: "${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 tests83# involving heavy traffic. This might be the case on a debug kernel, a VM, or84# e.g. a low-power board.85: "${KSFT_MACHINE_SLOW:=no}"86 87##############################################################################88# Find netifs by test-specified driver name89 90driver_name_get()91{92	local dev=$1; shift93	local driver_path="/sys/class/net/$dev/device/driver"94 95	if [[ -L $driver_path ]]; then96		basename `realpath $driver_path`97	fi98}99 100netif_find_driver()101{102	local ifnames=`ip -j link show | jq -r ".[].ifname"`103	local count=0104 105	for ifname in $ifnames106	do107		local driver_name=`driver_name_get $ifname`108		if [[ ! -z $driver_name && $driver_name == $NETIF_FIND_DRIVER ]]; then109			count=$((count + 1))110			NETIFS[p$count]="$ifname"111		fi112	done113}114 115# Whether to find netdevice according to the driver speficied by the importer116: "${NETIF_FIND_DRIVER:=}"117 118if [[ $NETIF_FIND_DRIVER ]]; then119	unset NETIFS120	declare -A NETIFS121	netif_find_driver122fi123 124net_forwarding_dir=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")125 126if [[ -f $net_forwarding_dir/forwarding.config ]]; then127	source "$net_forwarding_dir/forwarding.config"128fi129 130source "$net_forwarding_dir/../lib.sh"131 132##############################################################################133# Sanity checks134 135check_tc_version()136{137	tc -j &> /dev/null138	if [[ $? -ne 0 ]]; then139		echo "SKIP: iproute2 too old; tc is missing JSON support"140		exit $ksft_skip141	fi142}143 144# Old versions of tc don't understand "mpls_uc"145check_tc_mpls_support()146{147	local dev=$1; shift148 149	tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \150		matchall action pipe &> /dev/null151	if [[ $? -ne 0 ]]; then152		echo "SKIP: iproute2 too old; tc is missing MPLS support"153		return $ksft_skip154	fi155	tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \156		matchall157}158 159# Old versions of tc produce invalid json output for mpls lse statistics160check_tc_mpls_lse_stats()161{162	local dev=$1; shift163	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/null168 169	if [[ $? -ne 0 ]]; then170		echo "SKIP: iproute2 too old; tc-flower is missing extended MPLS support"171		return $ksft_skip172	fi173 174	tc -j filter show dev $dev ingress protocol mpls_uc | jq . &> /dev/null175	ret=$?176	tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \177		flower178 179	if [[ $ret -ne 0 ]]; then180		echo "SKIP: iproute2 too old; tc-flower produces invalid json output for extended MPLS filters"181		return $ksft_skip182	fi183}184 185check_tc_shblock_support()186{187	tc filter help 2>&1 | grep block &> /dev/null188	if [[ $? -ne 0 ]]; then189		echo "SKIP: iproute2 too old; tc is missing shared block support"190		exit $ksft_skip191	fi192}193 194check_tc_chain_support()195{196	tc help 2>&1|grep chain &> /dev/null197	if [[ $? -ne 0 ]]; then198		echo "SKIP: iproute2 too old; tc is missing chain support"199		exit $ksft_skip200	fi201}202 203check_tc_action_hw_stats_support()204{205	tc actions help 2>&1 | grep -q hw_stats206	if [[ $? -ne 0 ]]; then207		echo "SKIP: iproute2 too old; tc is missing action hw_stats support"208		exit $ksft_skip209	fi210}211 212check_tc_fp_support()213{214	tc qdisc add dev lo mqprio help 2>&1 | grep -q "fp "215	if [[ $? -ne 0 ]]; then216		echo "SKIP: iproute2 too old; tc is missing frame preemption support"217		exit $ksft_skip218	fi219}220 221check_ethtool_lanes_support()222{223	ethtool --help 2>&1| grep lanes &> /dev/null224	if [[ $? -ne 0 ]]; then225		echo "SKIP: ethtool too old; it is missing lanes support"226		exit $ksft_skip227	fi228}229 230check_ethtool_mm_support()231{232	ethtool --help 2>&1| grep -- '--show-mm' &> /dev/null233	if [[ $? -ne 0 ]]; then234		echo "SKIP: ethtool too old; it is missing MAC Merge layer support"235		exit $ksft_skip236	fi237}238 239check_ethtool_counter_group_support()240{241	ethtool --help 2>&1| grep -- '--all-groups' &> /dev/null242	if [[ $? -ne 0 ]]; then243		echo "SKIP: ethtool too old; it is missing standard counter group support"244		exit $ksft_skip245	fi246}247 248check_ethtool_pmac_std_stats_support()249{250	local dev=$1; shift251	local grp=$1; shift252 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"; then260		echo "SKIP: iproute2 too old; Locked port feature not supported."261		return $ksft_skip262	fi263}264 265check_port_mab_support()266{267	if ! bridge -d link show | grep -q "mab"; then268		echo "SKIP: iproute2 too old; MacAuth feature not supported."269		return $ksft_skip270	fi271}272 273if [[ "$(id -u)" -ne 0 ]]; then274	echo "SKIP: need root privileges"275	exit $ksft_skip276fi277 278check_driver()279{280	local dev=$1; shift281	local expected=$1; shift282	local driver_name=`driver_name_get $dev`283 284	if [[ $driver_name != $expected ]]; then285		echo "SKIP: expected driver $expected for $dev, got $driver_name instead"286		exit $ksft_skip287	fi288}289 290if [[ "$CHECK_TC" = "yes" ]]; then291	check_tc_version292fi293 294require_command()295{296	local cmd=$1; shift297 298	if [[ ! -x "$(command -v "$cmd")" ]]; then299		echo "SKIP: $cmd not installed"300		exit $ksft_skip301	fi302}303 304# IPv6 support was added in v3.0305check_mtools_version()306{307	local version="$(msend -v)"308	local major309 310	version=${version##msend version }311	major=$(echo $version | cut -d. -f1)312 313	if [ $major -lt 3 ]; then314		echo "SKIP: expected mtools version 3.0, got $version"315		exit $ksft_skip316	fi317}318 319if [[ "$REQUIRE_JQ" = "yes" ]]; then320	require_command jq321fi322if [[ "$REQUIRE_MZ" = "yes" ]]; then323	require_command $MZ324fi325if [[ "$REQUIRE_MTOOLS" = "yes" ]]; then326	# https://github.com/troglobit/mtools327	require_command msend328	require_command mreceive329	check_mtools_version330fi331 332##############################################################################333# Command line options handling334 335count=0336 337while [[ $# -gt 0 ]]; do338	if [[ "$count" -eq "0" ]]; then339		unset NETIFS340		declare -A NETIFS341	fi342	count=$((count + 1))343	NETIFS[p$count]="$1"344	shift345done346 347##############################################################################348# Network interfaces configuration349 350if [[ ! -v NUM_NETIFS ]]; then351	echo "SKIP: importer does not define \"NUM_NETIFS\""352	exit $ksft_skip353fi354 355if (( NUM_NETIFS > ${#NETIFS[@]} )); then356	echo "SKIP: Importer requires $NUM_NETIFS NETIFS, but only ${#NETIFS[@]} are defined (${NETIFS[@]})"357	exit $ksft_skip358fi359 360for i in $(seq ${#NETIFS[@]}); do361	if [[ ! ${NETIFS[p$i]} ]]; then362		echo "SKIP: NETIFS[p$i] not given"363		exit $ksft_skip364	fi365done366 367create_netif_veth()368{369	local i370 371	for ((i = 1; i <= NUM_NETIFS; ++i)); do372		local j=$((i+1))373 374		if [ -z ${NETIFS[p$i]} ]; then375			echo "SKIP: Cannot create interface. Name not specified"376			exit $ksft_skip377		fi378 379		ip link show dev ${NETIFS[p$i]} &> /dev/null380		if [[ $? -ne 0 ]]; then381			ip link add ${NETIFS[p$i]} type veth \382				peer name ${NETIFS[p$j]}383			if [[ $? -ne 0 ]]; then384				echo "Failed to create netif"385				exit 1386			fi387		fi388		i=$j389	done390}391 392create_netif()393{394	case "$NETIF_TYPE" in395	veth) create_netif_veth396	      ;;397	*) echo "Can not create interfaces of type \'$NETIF_TYPE\'"398	   exit 1399	   ;;400	esac401}402 403declare -A MAC_ADDR_ORIG404mac_addr_prepare()405{406	local new_addr=407	local dev=408 409	for ((i = 1; i <= NUM_NETIFS; ++i)); do410		dev=${NETIFS[p$i]}411		new_addr=$(printf "00:01:02:03:04:%02x" $i)412 413		MAC_ADDR_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].address')414		# Strip quotes415		MAC_ADDR_ORIG["$dev"]=${MAC_ADDR_ORIG["$dev"]//\"/}416		ip link set dev $dev address $new_addr417	done418}419 420mac_addr_restore()421{422	local dev=423 424	for ((i = 1; i <= NUM_NETIFS; ++i)); do425		dev=${NETIFS[p$i]}426		ip link set dev $dev address ${MAC_ADDR_ORIG["$dev"]}427	done428}429 430if [[ "$NETIF_CREATE" = "yes" ]]; then431	create_netif432fi433 434if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then435	mac_addr_prepare436fi437 438for ((i = 1; i <= NUM_NETIFS; ++i)); do439	ip link show dev ${NETIFS[p$i]} &> /dev/null440	if [[ $? -ne 0 ]]; then441		echo "SKIP: could not find all required interfaces"442		exit $ksft_skip443	fi444done445 446##############################################################################447# Helpers448 449# Exit status to return at the end. Set in case one of the tests fails.450EXIT_STATUS=0451# Per-test return value. Clear at the beginning of each test.452RET=0453 454ret_set_ksft_status()455{456	local ksft_status=$1; shift457	local msg=$1; shift458 459	RET=$(ksft_status_merge $RET $ksft_status)460	if (( $? )); then461		retmsg=$msg462	fi463}464 465# Whether FAILs should be interpreted as XFAILs. Internal.466FAIL_TO_XFAIL=467 468check_err()469{470	local err=$1471	local msg=$2472 473	if ((err)); then474		if [[ $FAIL_TO_XFAIL = yes ]]; then475			ret_set_ksft_status $ksft_xfail "$msg"476		else477			ret_set_ksft_status $ksft_fail "$msg"478		fi479	fi480}481 482check_fail()483{484	local err=$1485	local msg=$2486 487	check_err $((!err)) "$msg"488}489 490check_err_fail()491{492	local should_fail=$1; shift493	local err=$1; shift494	local what=$1; shift495 496	if ((should_fail)); then497		check_fail $err "$what succeeded, but should have failed"498	else499		check_err $err "$what failed"500	fi501}502 503xfail()504{505	FAIL_TO_XFAIL=yes "$@"506}507 508xfail_on_slow()509{510	if [[ $KSFT_MACHINE_SLOW = yes ]]; then511		FAIL_TO_XFAIL=yes "$@"512	else513		"$@"514	fi515}516 517omit_on_slow()518{519	if [[ $KSFT_MACHINE_SLOW != yes ]]; then520		"$@"521	fi522}523 524xfail_on_veth()525{526	local dev=$1; shift527	local kind528 529	kind=$(ip -j -d link show dev $dev |530			jq -r '.[].linkinfo.info_kind')531	if [[ $kind = veth ]]; then532		FAIL_TO_XFAIL=yes "$@"533	else534		"$@"535	fi536}537 538log_test_result()539{540	local test_name=$1; shift541	local opt_str=$1; shift542	local result=$1; shift543	local retmsg=$1; shift544 545	printf "TEST: %-60s  [%s]\n" "$test_name $opt_str" "$result"546	if [[ $retmsg ]]; then547		printf "\t%s\n" "$retmsg"548	fi549}550 551pause_on_fail()552{553	if [[ $PAUSE_ON_FAIL == yes ]]; then554		echo "Hit enter to continue, 'q' to quit"555		read a556		[[ $a == q ]] && exit 1557	fi558}559 560handle_test_result_pass()561{562	local test_name=$1; shift563	local opt_str=$1; shift564 565	log_test_result "$test_name" "$opt_str" " OK "566}567 568handle_test_result_fail()569{570	local test_name=$1; shift571	local opt_str=$1; shift572 573	log_test_result "$test_name" "$opt_str" FAIL "$retmsg"574	pause_on_fail575}576 577handle_test_result_xfail()578{579	local test_name=$1; shift580	local opt_str=$1; shift581 582	log_test_result "$test_name" "$opt_str" XFAIL "$retmsg"583	pause_on_fail584}585 586handle_test_result_skip()587{588	local test_name=$1; shift589	local opt_str=$1; shift590 591	log_test_result "$test_name" "$opt_str" SKIP "$retmsg"592}593 594log_test()595{596	local test_name=$1597	local opt_str=$2598 599	if [[ $# -eq 2 ]]; then600		opt_str="($opt_str)"601	fi602 603	if ((RET == ksft_pass)); then604		handle_test_result_pass "$test_name" "$opt_str"605	elif ((RET == ksft_xfail)); then606		handle_test_result_xfail "$test_name" "$opt_str"607	elif ((RET == ksft_skip)); then608		handle_test_result_skip "$test_name" "$opt_str"609	else610		handle_test_result_fail "$test_name" "$opt_str"611	fi612 613	EXIT_STATUS=$(ksft_exit_status_merge $EXIT_STATUS $RET)614	return $RET615}616 617log_test_skip()618{619	RET=$ksft_skip retmsg= log_test "$@"620}621 622log_test_xfail()623{624	RET=$ksft_xfail retmsg= log_test "$@"625}626 627log_info()628{629	local msg=$1630 631	echo "INFO: $msg"632}633 634not()635{636	"$@"637	[[ $? != 0 ]]638}639 640get_max()641{642	local arr=("$@")643 644	max=${arr[0]}645	for cur in ${arr[@]}; do646		if [[ $cur -gt $max ]]; then647			max=$cur648		fi649	done650 651	echo $max652}653 654grep_bridge_fdb()655{656	local addr=$1; shift657	local word658	local flag659 660	if [ "$1" == "self" ] || [ "$1" == "master" ]; then661		word=$1; shift662		if [ "$1" == "-v" ]; then663			flag=$1; shift664		fi665	fi666 667	$@ | grep $addr | grep $flag "$word"668}669 670wait_for_port_up()671{672	"$@" | grep -q "Link detected: yes"673}674 675wait_for_offload()676{677	"$@" | grep -q offload678}679 680wait_for_trap()681{682	"$@" | grep -q trap683}684 685setup_wait_dev()686{687	local dev=$1; shift688	local wait_time=${1:-$WAIT_TIME}; shift689 690	setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time691 692	if (($?)); then693		check_err 1694		log_test setup_wait_dev ": Interface $dev does not come up."695		exit 1696	fi697}698 699setup_wait_dev_with_timeout()700{701	local dev=$1; shift702	local max_iterations=${1:-$WAIT_TIMEOUT}; shift703	local wait_time=${1:-$WAIT_TIME}; shift704	local i705 706	for ((i = 1; i <= $max_iterations; ++i)); do707		ip link show dev $dev up \708			| grep 'state UP' &> /dev/null709		if [[ $? -ne 0 ]]; then710			sleep 1711		else712			sleep $wait_time713			return 0714		fi715	done716 717	return 1718}719 720setup_wait()721{722	local num_netifs=${1:-$NUM_NETIFS}723	local i724 725	for ((i = 1; i <= num_netifs; ++i)); do726		setup_wait_dev ${NETIFS[p$i]} 0727	done728 729	# Make sure links are ready.730	sleep $WAIT_TIME731}732 733wait_for_dev()734{735        local dev=$1; shift736        local timeout=${1:-$WAIT_TIMEOUT}; shift737 738        slowwait $timeout ip link show dev $dev &> /dev/null739        if (( $? )); then740                check_err 1741                log_test wait_for_dev "Interface $dev did not appear."742                exit $EXIT_STATUS743        fi744}745 746cmd_jq()747{748	local cmd=$1749	local jq_exp=$2750	local jq_opts=$3751	local ret752	local output753 754	output="$($cmd)"755	# it the command fails, return error right away756	ret=$?757	if [[ $ret -ne 0 ]]; then758		return $ret759	fi760	output=$(echo $output | jq -r $jq_opts "$jq_exp")761	ret=$?762	if [[ $ret -ne 0 ]]; then763		return $ret764	fi765	echo $output766	# return success only in case of non-empty output767	[ ! -z "$output" ]768}769 770pre_cleanup()771{772	if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then773		echo "Pausing before cleanup, hit any key to continue"774		read775	fi776 777	if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then778		mac_addr_restore779	fi780}781 782vrf_prepare()783{784	ip -4 rule add pref 32765 table local785	ip -4 rule del pref 0786	ip -6 rule add pref 32765 table local787	ip -6 rule del pref 0788}789 790vrf_cleanup()791{792	ip -6 rule add pref 0 table local793	ip -6 rule del pref 32765794	ip -4 rule add pref 0 table local795	ip -4 rule del pref 32765796}797 798__last_tb_id=0799declare -A __TB_IDS800 801__vrf_td_id_assign()802{803	local vrf_name=$1804 805	__last_tb_id=$((__last_tb_id + 1))806	__TB_IDS[$vrf_name]=$__last_tb_id807	return $__last_tb_id808}809 810__vrf_td_id_lookup()811{812	local vrf_name=$1813 814	return ${__TB_IDS[$vrf_name]}815}816 817vrf_create()818{819	local vrf_name=$1820	local tb_id821 822	__vrf_td_id_assign $vrf_name823	tb_id=$?824 825	ip link add dev $vrf_name type vrf table $tb_id826	ip -4 route add table $tb_id unreachable default metric 4278198272827	ip -6 route add table $tb_id unreachable default metric 4278198272828}829 830vrf_destroy()831{832	local vrf_name=$1833	local tb_id834 835	__vrf_td_id_lookup $vrf_name836	tb_id=$?837 838	ip -6 route del table $tb_id unreachable default metric 4278198272839	ip -4 route del table $tb_id unreachable default metric 4278198272840	ip link del dev $vrf_name841}842 843__addr_add_del()844{845	local if_name=$1846	local add_del=$2847	local array848 849	shift850	shift851	array=("${@}")852 853	for addrstr in "${array[@]}"; do854		ip address $add_del $addrstr dev $if_name855	done856}857 858__simple_if_init()859{860	local if_name=$1; shift861	local vrf_name=$1; shift862	local addrs=("${@}")863 864	ip link set dev $if_name master $vrf_name865	ip link set dev $if_name up866 867	__addr_add_del $if_name add "${addrs[@]}"868}869 870__simple_if_fini()871{872	local if_name=$1; shift873	local addrs=("${@}")874 875	__addr_add_del $if_name del "${addrs[@]}"876 877	ip link set dev $if_name down878	ip link set dev $if_name nomaster879}880 881simple_if_init()882{883	local if_name=$1884	local vrf_name885	local array886 887	shift888	vrf_name=v$if_name889	array=("${@}")890 891	vrf_create $vrf_name892	ip link set dev $vrf_name up893	__simple_if_init $if_name $vrf_name "${array[@]}"894}895 896simple_if_fini()897{898	local if_name=$1899	local vrf_name900	local array901 902	shift903	vrf_name=v$if_name904	array=("${@}")905 906	__simple_if_fini $if_name "${array[@]}"907	vrf_destroy $vrf_name908}909 910tunnel_create()911{912	local name=$1; shift913	local type=$1; shift914	local local=$1; shift915	local remote=$1; shift916 917	ip link add name $name type $type \918	   local $local remote $remote "$@"919	ip link set dev $name up920}921 922tunnel_destroy()923{924	local name=$1; shift925 926	ip link del dev $name927}928 929vlan_create()930{931	local if_name=$1; shift932	local vid=$1; shift933	local vrf=$1; shift934	local ips=("${@}")935	local name=$if_name.$vid936 937	ip link add name $name link $if_name type vlan id $vid938	if [ "$vrf" != "" ]; then939		ip link set dev $name master $vrf940	fi941	ip link set dev $name up942	__addr_add_del $name add "${ips[@]}"943}944 945vlan_destroy()946{947	local if_name=$1; shift948	local vid=$1; shift949	local name=$if_name.$vid950 951	ip link del dev $name952}953 954team_create()955{956	local if_name=$1; shift957	local mode=$1; shift958 959	require_command $TEAMD960	$TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'961	for slave in "$@"; do962		ip link set dev $slave down963		ip link set dev $slave master $if_name964		ip link set dev $slave up965	done966	ip link set dev $if_name up967}968 969team_destroy()970{971	local if_name=$1; shift972 973	$TEAMD -t $if_name -k974}975 976master_name_get()977{978	local if_name=$1979 980	ip -j link show dev $if_name | jq -r '.[]["master"]'981}982 983link_stats_get()984{985	local if_name=$1; shift986	local dir=$1; shift987	local stat=$1; shift988 989	ip -j -s link show dev $if_name \990		| jq '.[]["stats64"]["'$dir'"]["'$stat'"]'991}992 993link_stats_tx_packets_get()994{995	link_stats_get $1 tx packets996}997 998link_stats_rx_errors_get()999{1000	link_stats_get $1 rx errors1001}1002 1003ethtool_stats_get()1004{1005	local dev=$1; shift1006	local stat=$1; shift1007 1008	ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f21009}1010 1011ethtool_std_stats_get()1012{1013	local dev=$1; shift1014	local grp=$1; shift1015	local name=$1; shift1016	local src=$1; shift1017 1018	ethtool --json -S $dev --groups $grp -- --src $src | \1019		jq '.[]."'"$grp"'"."'$name'"'1020}1021 1022qdisc_stats_get()1023{1024	local dev=$1; shift1025	local handle=$1; shift1026	local selector=$1; shift1027 1028	tc -j -s qdisc show dev "$dev" \1029	    | jq '.[] | select(.handle == "'"$handle"'") | '"$selector"1030}1031 1032qdisc_parent_stats_get()1033{1034	local dev=$1; shift1035	local parent=$1; shift1036	local selector=$1; shift1037 1038	tc -j -s qdisc show dev "$dev" invisible \1039	    | jq '.[] | select(.parent == "'"$parent"'") | '"$selector"1040}1041 1042ipv6_stats_get()1043{1044	local dev=$1; shift1045	local stat=$1; shift1046 1047	cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f21048}1049 1050hw_stats_get()1051{1052	local suite=$1; shift1053	local if_name=$1; shift1054	local dir=$1; shift1055	local stat=$1; shift1056 1057	ip -j stats show dev $if_name group offload subgroup $suite |1058		jq ".[0].stats64.$dir.$stat"1059}1060 1061__nh_stats_get()1062{1063	local key=$1; shift1064	local group_id=$1; shift1065	local member_id=$1; shift1066 1067	ip -j -s -s nexthop show id $group_id |1068	    jq --argjson member_id "$member_id" --arg key "$key" \1069	       '.[].group_stats[] | select(.id == $member_id) | .[$key]'1070}1071 1072nh_stats_get()1073{1074	local group_id=$1; shift1075	local member_id=$1; shift1076 1077	__nh_stats_get packets "$group_id" "$member_id"1078}1079 1080nh_stats_get_hw()1081{1082	local group_id=$1; shift1083	local member_id=$1; shift1084 1085	__nh_stats_get packets_hw "$group_id" "$member_id"1086}1087 1088humanize()1089{1090	local speed=$1; shift1091 1092	for unit in bps Kbps Mbps Gbps; do1093		if (($(echo "$speed < 1024" | bc))); then1094			break1095		fi1096 1097		speed=$(echo "scale=1; $speed / 1024" | bc)1098	done1099 1100	echo "$speed${unit}"1101}1102 1103rate()1104{1105	local t0=$1; shift1106	local t1=$1; shift1107	local interval=$1; shift1108 1109	echo $((8 * (t1 - t0) / interval))1110}1111 1112packets_rate()1113{1114	local t0=$1; shift1115	local t1=$1; shift1116	local interval=$1; shift1117 1118	echo $(((t1 - t0) / interval))1119}1120 1121mac_get()1122{1123	local if_name=$11124 1125	ip -j link show dev $if_name | jq -r '.[]["address"]'1126}1127 1128ether_addr_to_u64()1129{1130	local addr="$1"1131	local order="$((1 << 40))"1132	local val=01133	local byte1134 1135	addr="${addr//:/ }"1136 1137	for byte in $addr; do1138		byte="0x$byte"1139		val=$((val + order * byte))1140		order=$((order >> 8))1141	done1142 1143	printf "0x%x" $val1144}1145 1146u64_to_ether_addr()1147{1148	local val=$11149	local byte1150	local i1151 1152	for ((i = 40; i >= 0; i -= 8)); do1153		byte=$(((val & (0xff << i)) >> i))1154		printf "%02x" $byte1155		if [ $i -ne 0 ]; then1156			printf ":"1157		fi1158	done1159}1160 1161ipv6_lladdr_get()1162{1163	local if_name=$11164 1165	ip -j addr show dev $if_name | \1166		jq -r '.[]["addr_info"][] | select(.scope == "link").local' | \1167		head -11168}1169 1170bridge_ageing_time_get()1171{1172	local bridge=$11173	local ageing_time1174 1175	# Need to divide by 100 to convert to seconds.1176	ageing_time=$(ip -j -d link show dev $bridge \1177		      | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')1178	echo $((ageing_time / 100))1179}1180 1181declare -A SYSCTL_ORIG1182sysctl_save()1183{1184	local key=$1; shift1185 1186	SYSCTL_ORIG[$key]=$(sysctl -n $key)1187}1188 1189sysctl_set()1190{1191	local key=$1; shift1192	local value=$1; shift1193 1194	sysctl_save "$key"1195	sysctl -qw $key="$value"1196}1197 1198sysctl_restore()1199{1200	local key=$1; shift1201 1202	sysctl -qw $key="${SYSCTL_ORIG[$key]}"1203}1204 1205forwarding_enable()1206{1207	sysctl_set net.ipv4.conf.all.forwarding 11208	sysctl_set net.ipv6.conf.all.forwarding 11209}1210 1211forwarding_restore()1212{1213	sysctl_restore net.ipv6.conf.all.forwarding1214	sysctl_restore net.ipv4.conf.all.forwarding1215}1216 1217declare -A MTU_ORIG1218mtu_set()1219{1220	local dev=$1; shift1221	local mtu=$1; shift1222 1223	MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')1224	ip link set dev $dev mtu $mtu1225}1226 1227mtu_restore()1228{1229	local dev=$1; shift1230 1231	ip link set dev $dev mtu ${MTU_ORIG["$dev"]}1232}1233 1234tc_offload_check()1235{1236	local num_netifs=${1:-$NUM_NETIFS}1237 1238	for ((i = 1; i <= num_netifs; ++i)); do1239		ethtool -k ${NETIFS[p$i]} \1240			| grep "hw-tc-offload: on" &> /dev/null1241		if [[ $? -ne 0 ]]; then1242			return 11243		fi1244	done1245 1246	return 01247}1248 1249trap_install()1250{1251	local dev=$1; shift1252	local direction=$1; shift1253 1254	# Some devices may not support or need in-hardware trapping of traffic1255	# (e.g. the veth pairs that this library creates for non-existent1256	# loopbacks). Use continue instead, so that there is a filter in there1257	# (some tests check counters), and so that other filters are still1258	# processed.1259	tc filter add dev $dev $direction pref 1 \1260		flower skip_sw action trap 2>/dev/null \1261	    || tc filter add dev $dev $direction pref 1 \1262		       flower action continue1263}1264 1265trap_uninstall()1266{1267	local dev=$1; shift1268	local direction=$1; shift1269 1270	tc filter del dev $dev $direction pref 1 flower1271}1272 1273__icmp_capture_add_del()1274{1275	local add_del=$1; shift1276	local pref=$1; shift1277	local vsuf=$1; shift1278	local tundev=$1; shift1279	local filter=$1; shift1280 1281	tc filter $add_del dev "$tundev" ingress \1282	   proto ip$vsuf pref $pref \1283	   flower ip_proto icmp$vsuf $filter \1284	   action pass1285}1286 1287icmp_capture_install()1288{1289	local tundev=$1; shift1290	local filter=$1; shift1291 1292	__icmp_capture_add_del add 100 "" "$tundev" "$filter"1293}1294 1295icmp_capture_uninstall()1296{1297	local tundev=$1; shift1298	local filter=$1; shift1299 1300	__icmp_capture_add_del del 100 "" "$tundev" "$filter"1301}1302 1303icmp6_capture_install()1304{1305	local tundev=$1; shift1306	local filter=$1; shift1307 1308	__icmp_capture_add_del add 100 v6 "$tundev" "$filter"1309}1310 1311icmp6_capture_uninstall()1312{1313	local tundev=$1; shift1314	local filter=$1; shift1315 1316	__icmp_capture_add_del del 100 v6 "$tundev" "$filter"1317}1318 1319__vlan_capture_add_del()1320{1321	local add_del=$1; shift1322	local pref=$1; shift1323	local dev=$1; shift1324	local filter=$1; shift1325 1326	tc filter $add_del dev "$dev" ingress \1327	   proto 802.1q pref $pref \1328	   flower $filter \1329	   action pass1330}1331 1332vlan_capture_install()1333{1334	local dev=$1; shift1335	local filter=$1; shift1336 1337	__vlan_capture_add_del add 100 "$dev" "$filter"1338}1339 1340vlan_capture_uninstall()1341{1342	local dev=$1; shift1343	local filter=$1; shift1344 1345	__vlan_capture_add_del del 100 "$dev" "$filter"1346}1347 1348__dscp_capture_add_del()1349{1350	local add_del=$1; shift1351	local dev=$1; shift1352	local base=$1; shift1353	local dscp;1354 1355	for prio in {0..7}; do1356		dscp=$((base + prio))1357		__icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \1358				       "skip_hw ip_tos $((dscp << 2))"1359	done1360}1361 1362dscp_capture_install()1363{1364	local dev=$1; shift1365	local base=$1; shift1366 1367	__dscp_capture_add_del add $dev $base1368}1369 1370dscp_capture_uninstall()1371{1372	local dev=$1; shift1373	local base=$1; shift1374 1375	__dscp_capture_add_del del $dev $base1376}1377 1378dscp_fetch_stats()1379{1380	local dev=$1; shift1381	local base=$1; shift1382 1383	for prio in {0..7}; do1384		local dscp=$((base + prio))1385		local t=$(tc_rule_stats_get $dev $((dscp + 100)))1386		echo "[$dscp]=$t "1387	done1388}1389 1390matchall_sink_create()1391{1392	local dev=$1; shift1393 1394	tc qdisc add dev $dev clsact1395	tc filter add dev $dev ingress \1396	   pref 10000 \1397	   matchall \1398	   action drop1399}1400 1401tests_run()1402{1403	local current_test1404 1405	for current_test in ${TESTS:-$ALL_TESTS}; do1406		$current_test1407	done1408}1409 1410multipath_eval()1411{1412	local desc="$1"1413	local weight_rp12=$21414	local weight_rp13=$31415	local packets_rp12=$41416	local packets_rp13=$51417	local weights_ratio packets_ratio diff1418 1419	RET=01420 1421	if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then1422		weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \1423				| bc -l)1424	else1425		weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \1426				| bc -l)1427	fi1428 1429	if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then1430	       check_err 1 "Packet difference is 0"1431	       log_test "Multipath"1432	       log_info "Expected ratio $weights_ratio"1433	       return1434	fi1435 1436	if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then1437		packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \1438				| bc -l)1439	else1440		packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \1441				| bc -l)1442	fi1443 1444	diff=$(echo $weights_ratio - $packets_ratio | bc -l)1445	diff=${diff#-}1446 1447	test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 01448	check_err $? "Too large discrepancy between expected and measured ratios"1449	log_test "$desc"1450	log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"1451}1452 1453in_ns()1454{1455	local name=$1; shift1456 1457	ip netns exec $name bash <<-EOF1458		NUM_NETIFS=01459		source lib.sh1460		$(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)1461	EOF1462}1463 1464##############################################################################1465# Tests1466 1467ping_do()1468{1469	local if_name=$11470	local dip=$21471	local args=$31472	local vrf_name1473 1474	vrf_name=$(master_name_get $if_name)1475	ip vrf exec $vrf_name \1476		$PING $args $dip -c $PING_COUNT -i 0.1 \1477		-w $PING_TIMEOUT &> /dev/null1478}1479 1480ping_test()1481{1482	RET=01483 1484	ping_do $1 $21485	check_err $?1486	log_test "ping$3"1487}1488 1489ping_test_fails()1490{1491	RET=01492 1493	ping_do $1 $21494	check_fail $?1495	log_test "ping fails$3"1496}1497 1498ping6_do()1499{1500	local if_name=$11501	local dip=$21502	local args=$31503	local vrf_name1504 1505	vrf_name=$(master_name_get $if_name)1506	ip vrf exec $vrf_name \1507		$PING6 $args $dip -c $PING_COUNT -i 0.1 \1508		-w $PING_TIMEOUT &> /dev/null1509}1510 1511ping6_test()1512{1513	RET=01514 1515	ping6_do $1 $21516	check_err $?1517	log_test "ping6$3"1518}1519 1520ping6_test_fails()1521{1522	RET=01523 1524	ping6_do $1 $21525	check_fail $?1526	log_test "ping6 fails$3"1527}1528 1529learning_test()1530{1531	local bridge=$11532	local br_port1=$2	# Connected to `host1_if`.1533	local host1_if=$31534	local host2_if=$41535	local mac=de:ad:be:ef:13:371536	local ageing_time1537 1538	RET=01539 1540	bridge -j fdb show br $bridge brport $br_port1 \1541		| jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null1542	check_fail $? "Found FDB record when should not"1543 1544	# Disable unknown unicast flooding on `br_port1` to make sure1545	# packets are only forwarded through the port after a matching1546	# FDB entry was installed.1547	bridge link set dev $br_port1 flood off1548 1549	ip link set $host1_if promisc on1550	tc qdisc add dev $host1_if ingress1551	tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \1552		flower dst_mac $mac action drop1553 1554	$MZ $host2_if -c 1 -p 64 -b $mac -t ip -q1555	sleep 11556 1557	tc -j -s filter show dev $host1_if ingress \1558		| jq -e ".[] | select(.options.handle == 101) \1559		| select(.options.actions[0].stats.packets == 1)" &> /dev/null1560	check_fail $? "Packet reached first host when should not"1561 1562	$MZ $host1_if -c 1 -p 64 -a $mac -t ip -q1563	sleep 11564 1565	bridge -j fdb show br $bridge brport $br_port1 \1566		| jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null1567	check_err $? "Did not find FDB record when should"1568 1569	$MZ $host2_if -c 1 -p 64 -b $mac -t ip -q1570	sleep 11571 1572	tc -j -s filter show dev $host1_if ingress \1573		| jq -e ".[] | select(.options.handle == 101) \1574		| select(.options.actions[0].stats.packets == 1)" &> /dev/null1575	check_err $? "Packet did not reach second host when should"1576 1577	# Wait for 10 seconds after the ageing time to make sure FDB1578	# record was aged-out.1579	ageing_time=$(bridge_ageing_time_get $bridge)1580	sleep $((ageing_time + 10))1581 1582	bridge -j fdb show br $bridge brport $br_port1 \1583		| jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null1584	check_fail $? "Found FDB record when should not"1585 1586	bridge link set dev $br_port1 learning off1587 1588	$MZ $host1_if -c 1 -p 64 -a $mac -t ip -q1589	sleep 11590 1591	bridge -j fdb show br $bridge brport $br_port1 \1592		| jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null1593	check_fail $? "Found FDB record when should not"1594 1595	bridge link set dev $br_port1 learning on1596 1597	tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower1598	tc qdisc del dev $host1_if ingress1599	ip link set $host1_if promisc off1600 1601	bridge link set dev $br_port1 flood on1602 1603	log_test "FDB learning"1604}1605 1606flood_test_do()1607{1608	local should_flood=$11609	local mac=$21610	local ip=$31611	local host1_if=$41612	local host2_if=$51613	local err=01614 1615	# Add an ACL on `host2_if` which will tell us whether the packet1616	# was flooded to it or not.1617	ip link set $host2_if promisc on1618	tc qdisc add dev $host2_if ingress1619	tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \1620		flower dst_mac $mac action drop1621 1622	$MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q1623	sleep 11624 1625	tc -j -s filter show dev $host2_if ingress \1626		| jq -e ".[] | select(.options.handle == 101) \1627		| select(.options.actions[0].stats.packets == 1)" &> /dev/null1628	if [[ $? -ne 0 && $should_flood == "true" || \1629	      $? -eq 0 && $should_flood == "false" ]]; then1630		err=11631	fi1632 1633	tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower1634	tc qdisc del dev $host2_if ingress1635	ip link set $host2_if promisc off1636 1637	return $err1638}1639 1640flood_unicast_test()1641{1642	local br_port=$11643	local host1_if=$21644	local host2_if=$31645	local mac=de:ad:be:ef:13:371646	local ip=192.0.2.1001647 1648	RET=01649 1650	bridge link set dev $br_port flood off1651 1652	flood_test_do false $mac $ip $host1_if $host2_if1653	check_err $? "Packet flooded when should not"1654 1655	bridge link set dev $br_port flood on1656 1657	flood_test_do true $mac $ip $host1_if $host2_if1658	check_err $? "Packet was not flooded when should"1659 1660	log_test "Unknown unicast flood"1661}1662 1663flood_multicast_test()1664{1665	local br_port=$11666	local host1_if=$21667	local host2_if=$31668	local mac=01:00:5e:00:00:011669	local ip=239.0.0.11670 1671	RET=01672 1673	bridge link set dev $br_port mcast_flood off1674 1675	flood_test_do false $mac $ip $host1_if $host2_if1676	check_err $? "Packet flooded when should not"1677 1678	bridge link set dev $br_port mcast_flood on1679 1680	flood_test_do true $mac $ip $host1_if $host2_if1681	check_err $? "Packet was not flooded when should"1682 1683	log_test "Unregistered multicast flood"1684}1685 1686flood_test()1687{1688	# `br_port` is connected to `host2_if`1689	local br_port=$11690	local host1_if=$21691	local host2_if=$31692 1693	flood_unicast_test $br_port $host1_if $host2_if1694	flood_multicast_test $br_port $host1_if $host2_if1695}1696 1697__start_traffic()1698{1699	local pktsize=$1; shift1700	local proto=$1; shift1701	local h_in=$1; shift    # Where the traffic egresses the host1702	local sip=$1; shift1703	local dip=$1; shift1704	local dmac=$1; shift1705	local -a mz_args=("$@")1706 1707	$MZ $h_in -p $pktsize -A $sip -B $dip -c 0 \1708		-a own -b $dmac -t "$proto" -q "${mz_args[@]}" &1709	sleep 11710}1711 1712start_traffic_pktsize()1713{1714	local pktsize=$1; shift1715	local h_in=$1; shift1716	local sip=$1; shift1717	local dip=$1; shift1718	local dmac=$1; shift1719	local -a mz_args=("$@")1720 1721	__start_traffic $pktsize udp "$h_in" "$sip" "$dip" "$dmac" \1722			"${mz_args[@]}"1723}1724 1725start_tcp_traffic_pktsize()1726{1727	local pktsize=$1; shift1728	local h_in=$1; shift1729	local sip=$1; shift1730	local dip=$1; shift1731	local dmac=$1; shift1732	local -a mz_args=("$@")1733 1734	__start_traffic $pktsize tcp "$h_in" "$sip" "$dip" "$dmac" \1735			"${mz_args[@]}"1736}1737 1738start_traffic()1739{1740	local h_in=$1; shift1741	local sip=$1; shift1742	local dip=$1; shift1743	local dmac=$1; shift1744	local -a mz_args=("$@")1745 1746	start_traffic_pktsize 8000 "$h_in" "$sip" "$dip" "$dmac" \1747			      "${mz_args[@]}"1748}1749 1750start_tcp_traffic()1751{1752	local h_in=$1; shift1753	local sip=$1; shift1754	local dip=$1; shift1755	local dmac=$1; shift1756	local -a mz_args=("$@")1757 1758	start_tcp_traffic_pktsize 8000 "$h_in" "$sip" "$dip" "$dmac" \1759				  "${mz_args[@]}"1760}1761 1762stop_traffic()1763{1764	# Suppress noise from killing mausezahn.1765	{ kill %% && wait %%; } 2>/dev/null1766}1767 1768declare -A cappid1769declare -A capfile1770declare -A capout1771 1772tcpdump_start()1773{1774	local if_name=$1; shift1775	local ns=$1; shift1776 1777	capfile[$if_name]=$(mktemp)1778	capout[$if_name]=$(mktemp)1779 1780	if [ -z $ns ]; then1781		ns_cmd=""1782	else1783		ns_cmd="ip netns exec ${ns}"1784	fi1785 1786	if [ -z $SUDO_USER ] ; then1787		capuser=""1788	else1789		capuser="-Z $SUDO_USER"1790	fi1791 1792	$ns_cmd tcpdump $TCPDUMP_EXTRA_FLAGS -e -n -Q in -i $if_name \1793		-s 65535 -B 32768 $capuser -w ${capfile[$if_name]} \1794		> "${capout[$if_name]}" 2>&1 &1795	cappid[$if_name]=$!1796 1797	sleep 11798}1799 1800tcpdump_stop()1801{1802	local if_name=$11803	local pid=${cappid[$if_name]}1804 1805	$ns_cmd kill "$pid" && wait "$pid"1806	sleep 11807}1808 1809tcpdump_cleanup()1810{1811	local if_name=$11812 1813	rm ${capfile[$if_name]} ${capout[$if_name]}1814}1815 1816tcpdump_show()1817{1818	local if_name=$11819 1820	tcpdump -e -n -r ${capfile[$if_name]} 2>&11821}1822 1823# return 0 if the packet wasn't seen on host2_if or 1 if it was1824mcast_packet_test()1825{1826	local mac=$11827	local src_ip=$21828	local ip=$31829	local host1_if=$41830	local host2_if=$51831	local seen=01832	local tc_proto="ip"1833	local mz_v6arg=""1834 1835	# basic check to see if we were passed an IPv4 address, if not assume IPv61836	if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then1837		tc_proto="ipv6"1838		mz_v6arg="-6"1839	fi1840 1841	# Add an ACL on `host2_if` which will tell us whether the packet1842	# was received by it or not.1843	tc qdisc add dev $host2_if ingress1844	tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \1845		flower ip_proto udp dst_mac $mac action drop1846 1847	$MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q1848	sleep 11849 1850	tc -j -s filter show dev $host2_if ingress \1851		| jq -e ".[] | select(.options.handle == 101) \1852		| select(.options.actions[0].stats.packets == 1)" &> /dev/null1853	if [[ $? -eq 0 ]]; then1854		seen=11855	fi1856 1857	tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower1858	tc qdisc del dev $host2_if ingress1859 1860	return $seen1861}1862 1863brmcast_check_sg_entries()1864{1865	local report=$1; shift1866	local slist=("$@")1867	local sarg=""1868 1869	for src in "${slist[@]}"; do1870		sarg="${sarg} and .source_list[].address == \"$src\""1871	done1872	bridge -j -d -s mdb show dev br0 \1873		| jq -e ".[].mdb[] | \1874			 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null1875	check_err $? "Wrong *,G entry source list after $report report"1876 1877	for sgent in "${slist[@]}"; do1878		bridge -j -d -s mdb show dev br0 \1879			| jq -e ".[].mdb[] | \1880				 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null1881		check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)"1882	done1883}1884 1885brmcast_check_sg_fwding()1886{1887	local should_fwd=$1; shift1888	local sources=("$@")1889 1890	for src in "${sources[@]}"; do1891		local retval=01892 1893		mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h11894		retval=$?1895		if [ $should_fwd -eq 1 ]; then1896			check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)"1897		else1898			check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)"1899		fi1900	done1901}1902 1903brmcast_check_sg_state()1904{1905	local is_blocked=$1; shift1906	local sources=("$@")1907	local should_fail=11908 1909	if [ $is_blocked -eq 1 ]; then1910		should_fail=01911	fi1912 1913	for src in "${sources[@]}"; do1914		bridge -j -d -s mdb show dev br0 \1915			| jq -e ".[].mdb[] | \1916				 select(.grp == \"$TEST_GROUP\" and .source_list != null) |1917				 .source_list[] |1918				 select(.address == \"$src\") |1919				 select(.timer == \"0.00\")" &>/dev/null1920		check_err_fail $should_fail $? "Entry $src has zero timer"1921 1922		bridge -j -d -s mdb show dev br0 \1923			| jq -e ".[].mdb[] | \1924				 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \1925				 .flags[] == \"blocked\")" &>/dev/null1926		check_err_fail $should_fail $? "Entry $src has blocked flag"1927	done1928}1929 1930mc_join()1931{1932	local if_name=$11933	local group=$21934	local vrf_name=$(master_name_get $if_name)1935 1936	# We don't care about actual reception, just about joining the1937	# IP multicast group and adding the L2 address to the device's1938	# MAC filtering table1939	ip vrf exec $vrf_name \1940		mreceive -g $group -I $if_name > /dev/null 2>&1 &1941	mreceive_pid=$!1942 1943	sleep 11944}1945 1946mc_leave()1947{1948	kill "$mreceive_pid" && wait "$mreceive_pid"1949}1950 1951mc_send()1952{1953	local if_name=$11954	local groups=$21955	local vrf_name=$(master_name_get $if_name)1956 1957	ip vrf exec $vrf_name \1958		msend -g $groups -I $if_name -c 1 > /dev/null 2>&11959}1960 1961start_ip_monitor()1962{1963	local mtype=$1; shift1964	local ip=${1-ip}; shift1965 1966	# start the monitor in the background1967	tmpfile=`mktemp /var/run/nexthoptestXXX`1968	mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null`1969	sleep 0.21970	echo "$mpid $tmpfile"1971}1972 1973stop_ip_monitor()1974{1975	local mpid=$1; shift1976	local tmpfile=$1; shift1977	local el=$1; shift1978	local what=$1; shift1979 1980	sleep 0.21981	kill $mpid1982	local lines=`grep '^\w' $tmpfile | wc -l`1983	test $lines -eq $el1984	check_err $? "$what: $lines lines of events, expected $el"1985	rm -rf $tmpfile1986}1987 1988hw_stats_monitor_test()1989{1990	local dev=$1; shift1991	local type=$1; shift1992	local make_suitable=$1; shift1993	local make_unsuitable=$1; shift1994	local ip=${1-ip}; shift1995 1996	RET=01997 1998	# Expect a notification about enablement.1999	local ipmout=$(start_ip_monitor stats "$ip")2000	$ip stats set dev $dev ${type}_stats on2001	stop_ip_monitor $ipmout 1 "${type}_stats enablement"2002 2003	# Expect a notification about offload.2004	local ipmout=$(start_ip_monitor stats "$ip")2005	$make_suitable2006	stop_ip_monitor $ipmout 1 "${type}_stats installation"2007 2008	# Expect a notification about loss of offload.2009	local ipmout=$(start_ip_monitor stats "$ip")2010	$make_unsuitable2011	stop_ip_monitor $ipmout 1 "${type}_stats deinstallation"2012 2013	# Expect a notification about disablement2014	local ipmout=$(start_ip_monitor stats "$ip")2015	$ip stats set dev $dev ${type}_stats off2016	stop_ip_monitor $ipmout 1 "${type}_stats disablement"2017 2018	log_test "${type}_stats notifications"2019}2020 2021ipv4_to_bytes()2022{2023	local IP=$1; shift2024 2025	printf '%02x:' ${IP//./ } |2026	    sed 's/:$//'2027}2028 2029# Convert a given IPv6 address, `IP' such that the :: token, if present, is2030# expanded, and each 16-bit group is padded with zeroes to be 4 hexadecimal2031# digits. An optional `BYTESEP' parameter can be given to further separate2032# individual bytes of each 16-bit group.2033expand_ipv6()2034{2035	local IP=$1; shift2036	local bytesep=$1; shift2037 2038	local cvt_ip=${IP/::/_}2039	local colons=${cvt_ip//[^:]/}2040	local allcol=:::::::2041	# IP where :: -> the appropriate number of colons:2042	local allcol_ip=${cvt_ip/_/${allcol:${#colons}}}2043 2044	echo $allcol_ip | tr : '\n' |2045	    sed s/^/0000/ |2046	    sed 's/.*\(..\)\(..\)/\1'"$bytesep"'\2/' |2047	    tr '\n' : |2048	    sed 's/:$//'2049}2050 2051ipv6_to_bytes()2052{2053	local IP=$1; shift2054 2055	expand_ipv6 "$IP" :2056}2057 2058u16_to_bytes()2059{2060	local u16=$1; shift2061 2062	printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/'2063}2064 2065# Given a mausezahn-formatted payload (colon-separated bytes given as %02x),2066# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be,2067# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any)2068# stands for 00:00.2069payload_template_calc_checksum()2070{2071	local payload=$1; shift2072 2073	(2074	    # Set input radix.2075	    echo "16i"2076	    # Push zero for the initial checksum.2077	    echo 02078 2079	    # Pad the payload with a terminating 00: in case we get an odd2080	    # number of bytes.2081	    echo "${payload%:}:00:" |2082		sed 's/CHECKSUM/00:00/g' |2083		tr '[:lower:]' '[:upper:]' |2084		# Add the word to the checksum.2085		sed 's/\(..\):\(..\):/\1\2+\n/g' |2086		# Strip the extra odd byte we pushed if left unconverted.2087		sed 's/\(..\):$//'2088 2089	    echo "10000 ~ +"	# Calculate and add carry.2090	    echo "FFFF r - p"	# Bit-flip and print.2091	) |2092	    dc |2093	    tr '[:upper:]' '[:lower:]'2094}2095 2096payload_template_expand_checksum()2097{2098	local payload=$1; shift2099	local checksum=$1; shift2100 2101	local ckbytes=$(u16_to_bytes $checksum)2102 2103	echo "$payload" | sed "s/CHECKSUM/$ckbytes/g"2104}2105 2106payload_template_nbytes()2107{2108	local payload=$1; shift2109 2110	payload_template_expand_checksum "${payload%:}" 0 |2111		sed 's/:/\n/g' | wc -l2112}2113 2114igmpv3_is_in_get()2115{2116	local GRP=$1; shift2117	local sources=("$@")2118 2119	local igmpv32120	local nsources=$(u16_to_bytes ${#sources[@]})2121 2122	# IS_IN ( $sources )2123	igmpv3=$(:2124		)"22:"$(			: Type - Membership Report2125		)"00:"$(			: Reserved2126		)"CHECKSUM:"$(			: Checksum2127		)"00:00:"$(			: Reserved2128		)"00:01:"$(			: Number of Group Records2129		)"01:"$(			: Record Type - IS_IN2130		)"00:"$(			: Aux Data Len2131		)"${nsources}:"$(		: Number of Sources2132		)"$(ipv4_to_bytes $GRP):"$(	: Multicast Address2133		)"$(for src in "${sources[@]}"; do2134			ipv4_to_bytes $src2135			echo -n :2136		    done)"$(			: Source Addresses2137		)2138	local checksum=$(payload_template_calc_checksum "$igmpv3")2139 2140	payload_template_expand_checksum "$igmpv3" $checksum2141}2142 2143igmpv2_leave_get()2144{2145	local GRP=$1; shift2146 2147	local payload=$(:2148		)"17:"$(			: Type - Leave Group2149		)"00:"$(			: Max Resp Time - not meaningful2150		)"CHECKSUM:"$(			: Checksum2151		)"$(ipv4_to_bytes $GRP)"$(	: Group Address2152		)2153	local checksum=$(payload_template_calc_checksum "$payload")2154 2155	payload_template_expand_checksum "$payload" $checksum2156}2157 2158mldv2_is_in_get()2159{2160	local SIP=$1; shift2161	local GRP=$1; shift2162	local sources=("$@")2163 2164	local hbh2165	local icmpv62166	local nsources=$(u16_to_bytes ${#sources[@]})2167 2168	hbh=$(:2169		)"3a:"$(			: Next Header - ICMPv62170		)"00:"$(			: Hdr Ext Len2171		)"00:00:00:00:00:00:"$(		: Options and Padding2172		)2173 2174	icmpv6=$(:2175		)"8f:"$(			: Type - MLDv2 Report2176		)"00:"$(			: Code2177		)"CHECKSUM:"$(			: Checksum2178		)"00:00:"$(			: Reserved2179		)"00:01:"$(			: Number of Group Records2180		)"01:"$(			: Record Type - IS_IN2181		)"00:"$(			: Aux Data Len2182		)"${nsources}:"$(		: Number of Sources2183		)"$(ipv6_to_bytes $GRP):"$(	: Multicast address2184		)"$(for src in "${sources[@]}"; do2185			ipv6_to_bytes $src2186			echo -n :2187		    done)"$(			: Source Addresses2188		)2189 2190	local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6))2191	local sudohdr=$(:2192		)"$(ipv6_to_bytes $SIP):"$(	: SIP2193		)"$(ipv6_to_bytes $GRP):"$(	: DIP is multicast address2194	        )"${len}:"$(			: Upper-layer length2195	        )"00:3a:"$(			: Zero and next-header2196	        )2197	local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6})2198 2199	payload_template_expand_checksum "$hbh$icmpv6" $checksum2200}2201 2202mldv1_done_get()2203{2204	local SIP=$1; shift2205	local GRP=$1; shift2206 2207	local hbh2208	local icmpv62209 2210	hbh=$(:2211		)"3a:"$(			: Next Header - ICMPv62212		)"00:"$(			: Hdr Ext Len2213		)"00:00:00:00:00:00:"$(		: Options and Padding2214		)2215 2216	icmpv6=$(:2217		)"84:"$(			: Type - MLDv1 Done2218		)"00:"$(			: Code2219		)"CHECKSUM:"$(			: Checksum2220		)"00:00:"$(			: Max Resp Delay - not meaningful2221		)"00:00:"$(			: Reserved2222		)"$(ipv6_to_bytes $GRP):"$(	: Multicast address2223		)2224 2225	local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6))2226	local sudohdr=$(:2227		)"$(ipv6_to_bytes $SIP):"$(	: SIP2228		)"$(ipv6_to_bytes $GRP):"$(	: DIP is multicast address2229	        )"${len}:"$(			: Upper-layer length2230	        )"00:3a:"$(			: Zero and next-header2231	        )2232	local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6})2233 2234	payload_template_expand_checksum "$hbh$icmpv6" $checksum2235}2236 2237bail_on_lldpad()2238{2239	local reason1="$1"; shift2240	local reason2="$1"; shift2241	local caller=${FUNCNAME[1]}2242	local src=${BASH_SOURCE[1]}2243 2244	if systemctl is-active --quiet lldpad; then2245 2246		cat >/dev/stderr <<-EOF2247		WARNING: lldpad is running2248 2249			lldpad will likely $reason1, and this test will2250			$reason2. Both are not supported at the same time,2251			one of them is arbitrarily going to overwrite the2252			other. That will cause spurious failures (or, unlikely,2253			passes) of this test.2254		EOF2255 2256		if [[ -z $ALLOW_LLDPAD ]]; then2257			cat >/dev/stderr <<-EOF2258 2259				If you want to run the test anyway, please set2260				an environment variable ALLOW_LLDPAD to a2261				non-empty string.2262			EOF2263			log_test_skip $src:$caller2264			exit $EXIT_STATUS2265		else2266			return2267		fi2268	fi2269}2270 2271absval()2272{2273	local v=$1; shift2274 2275	echo $((v > 0 ? v : -v))2276}2277 2278has_unicast_flt()2279{2280	local dev=$1; shift2281	local mac_addr=$(mac_get $dev)2282	local tmp=$(ether_addr_to_u64 $mac_addr)2283	local promisc2284 2285	ip link set $dev up2286	ip link add link $dev name macvlan-tmp type macvlan mode private2287	ip link set macvlan-tmp address $(u64_to_ether_addr $((tmp + 1)))2288	ip link set macvlan-tmp up2289 2290	promisc=$(ip -j -d link show dev $dev | jq -r '.[].promiscuity')2291 2292	ip link del macvlan-tmp2293 2294	[[ $promisc == 1 ]] && echo "no" || echo "yes"2295}2296