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