File Index
Files and directories
Sign in
FILE

lib.sh

File preview with syntax highlighting.

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