Back to home page

OSCL-LXR

 
 

    


0001 #!/bin/bash
0002 # SPDX-License-Identifier: GPL-2.0
0003 
0004 ##############################################################################
0005 # Defines
0006 
0007 # Kselftest framework requirement - SKIP code is 4.
0008 ksft_skip=4
0009 
0010 # Can be overridden by the configuration file.
0011 PING=${PING:=ping}
0012 PING6=${PING6:=ping6}
0013 MZ=${MZ:=mausezahn}
0014 ARPING=${ARPING:=arping}
0015 TEAMD=${TEAMD:=teamd}
0016 WAIT_TIME=${WAIT_TIME:=5}
0017 PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
0018 PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no}
0019 NETIF_TYPE=${NETIF_TYPE:=veth}
0020 NETIF_CREATE=${NETIF_CREATE:=yes}
0021 MCD=${MCD:=smcrouted}
0022 MC_CLI=${MC_CLI:=smcroutectl}
0023 PING_COUNT=${PING_COUNT:=10}
0024 PING_TIMEOUT=${PING_TIMEOUT:=5}
0025 WAIT_TIMEOUT=${WAIT_TIMEOUT:=20}
0026 INTERFACE_TIMEOUT=${INTERFACE_TIMEOUT:=600}
0027 LOW_AGEING_TIME=${LOW_AGEING_TIME:=1000}
0028 REQUIRE_JQ=${REQUIRE_JQ:=yes}
0029 REQUIRE_MZ=${REQUIRE_MZ:=yes}
0030 REQUIRE_MTOOLS=${REQUIRE_MTOOLS:=no}
0031 STABLE_MAC_ADDRS=${STABLE_MAC_ADDRS:=no}
0032 TCPDUMP_EXTRA_FLAGS=${TCPDUMP_EXTRA_FLAGS:=}
0033 
0034 relative_path="${BASH_SOURCE%/*}"
0035 if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then
0036         relative_path="."
0037 fi
0038 
0039 if [[ -f $relative_path/forwarding.config ]]; then
0040         source "$relative_path/forwarding.config"
0041 fi
0042 
0043 ##############################################################################
0044 # Sanity checks
0045 
0046 check_tc_version()
0047 {
0048         tc -j &> /dev/null
0049         if [[ $? -ne 0 ]]; then
0050                 echo "SKIP: iproute2 too old; tc is missing JSON support"
0051                 exit $ksft_skip
0052         fi
0053 }
0054 
0055 # Old versions of tc don't understand "mpls_uc"
0056 check_tc_mpls_support()
0057 {
0058         local dev=$1; shift
0059 
0060         tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \
0061                 matchall action pipe &> /dev/null
0062         if [[ $? -ne 0 ]]; then
0063                 echo "SKIP: iproute2 too old; tc is missing MPLS support"
0064                 return $ksft_skip
0065         fi
0066         tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \
0067                 matchall
0068 }
0069 
0070 # Old versions of tc produce invalid json output for mpls lse statistics
0071 check_tc_mpls_lse_stats()
0072 {
0073         local dev=$1; shift
0074         local ret;
0075 
0076         tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \
0077                 flower mpls lse depth 2                                 \
0078                 action continue &> /dev/null
0079 
0080         if [[ $? -ne 0 ]]; then
0081                 echo "SKIP: iproute2 too old; tc-flower is missing extended MPLS support"
0082                 return $ksft_skip
0083         fi
0084 
0085         tc -j filter show dev $dev ingress protocol mpls_uc | jq . &> /dev/null
0086         ret=$?
0087         tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \
0088                 flower
0089 
0090         if [[ $ret -ne 0 ]]; then
0091                 echo "SKIP: iproute2 too old; tc-flower produces invalid json output for extended MPLS filters"
0092                 return $ksft_skip
0093         fi
0094 }
0095 
0096 check_tc_shblock_support()
0097 {
0098         tc filter help 2>&1 | grep block &> /dev/null
0099         if [[ $? -ne 0 ]]; then
0100                 echo "SKIP: iproute2 too old; tc is missing shared block support"
0101                 exit $ksft_skip
0102         fi
0103 }
0104 
0105 check_tc_chain_support()
0106 {
0107         tc help 2>&1|grep chain &> /dev/null
0108         if [[ $? -ne 0 ]]; then
0109                 echo "SKIP: iproute2 too old; tc is missing chain support"
0110                 exit $ksft_skip
0111         fi
0112 }
0113 
0114 check_tc_action_hw_stats_support()
0115 {
0116         tc actions help 2>&1 | grep -q hw_stats
0117         if [[ $? -ne 0 ]]; then
0118                 echo "SKIP: iproute2 too old; tc is missing action hw_stats support"
0119                 exit $ksft_skip
0120         fi
0121 }
0122 
0123 check_ethtool_lanes_support()
0124 {
0125         ethtool --help 2>&1| grep lanes &> /dev/null
0126         if [[ $? -ne 0 ]]; then
0127                 echo "SKIP: ethtool too old; it is missing lanes support"
0128                 exit $ksft_skip
0129         fi
0130 }
0131 
0132 check_locked_port_support()
0133 {
0134         if ! bridge -d link show | grep -q " locked"; then
0135                 echo "SKIP: iproute2 too old; Locked port feature not supported."
0136                 return $ksft_skip
0137         fi
0138 }
0139 
0140 if [[ "$(id -u)" -ne 0 ]]; then
0141         echo "SKIP: need root privileges"
0142         exit $ksft_skip
0143 fi
0144 
0145 if [[ "$CHECK_TC" = "yes" ]]; then
0146         check_tc_version
0147 fi
0148 
0149 require_command()
0150 {
0151         local cmd=$1; shift
0152 
0153         if [[ ! -x "$(command -v "$cmd")" ]]; then
0154                 echo "SKIP: $cmd not installed"
0155                 exit $ksft_skip
0156         fi
0157 }
0158 
0159 if [[ "$REQUIRE_JQ" = "yes" ]]; then
0160         require_command jq
0161 fi
0162 if [[ "$REQUIRE_MZ" = "yes" ]]; then
0163         require_command $MZ
0164 fi
0165 if [[ "$REQUIRE_MTOOLS" = "yes" ]]; then
0166         # https://github.com/vladimiroltean/mtools/
0167         # patched for IPv6 support
0168         require_command msend
0169         require_command mreceive
0170 fi
0171 
0172 if [[ ! -v NUM_NETIFS ]]; then
0173         echo "SKIP: importer does not define \"NUM_NETIFS\""
0174         exit $ksft_skip
0175 fi
0176 
0177 ##############################################################################
0178 # Command line options handling
0179 
0180 count=0
0181 
0182 while [[ $# -gt 0 ]]; do
0183         if [[ "$count" -eq "0" ]]; then
0184                 unset NETIFS
0185                 declare -A NETIFS
0186         fi
0187         count=$((count + 1))
0188         NETIFS[p$count]="$1"
0189         shift
0190 done
0191 
0192 ##############################################################################
0193 # Network interfaces configuration
0194 
0195 create_netif_veth()
0196 {
0197         local i
0198 
0199         for ((i = 1; i <= NUM_NETIFS; ++i)); do
0200                 local j=$((i+1))
0201 
0202                 ip link show dev ${NETIFS[p$i]} &> /dev/null
0203                 if [[ $? -ne 0 ]]; then
0204                         ip link add ${NETIFS[p$i]} type veth \
0205                                 peer name ${NETIFS[p$j]}
0206                         if [[ $? -ne 0 ]]; then
0207                                 echo "Failed to create netif"
0208                                 exit 1
0209                         fi
0210                 fi
0211                 i=$j
0212         done
0213 }
0214 
0215 create_netif()
0216 {
0217         case "$NETIF_TYPE" in
0218         veth) create_netif_veth
0219               ;;
0220         *) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
0221            exit 1
0222            ;;
0223         esac
0224 }
0225 
0226 declare -A MAC_ADDR_ORIG
0227 mac_addr_prepare()
0228 {
0229         local new_addr=
0230         local dev=
0231 
0232         for ((i = 1; i <= NUM_NETIFS; ++i)); do
0233                 dev=${NETIFS[p$i]}
0234                 new_addr=$(printf "00:01:02:03:04:%02x" $i)
0235 
0236                 MAC_ADDR_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].address')
0237                 # Strip quotes
0238                 MAC_ADDR_ORIG["$dev"]=${MAC_ADDR_ORIG["$dev"]//\"/}
0239                 ip link set dev $dev address $new_addr
0240         done
0241 }
0242 
0243 mac_addr_restore()
0244 {
0245         local dev=
0246 
0247         for ((i = 1; i <= NUM_NETIFS; ++i)); do
0248                 dev=${NETIFS[p$i]}
0249                 ip link set dev $dev address ${MAC_ADDR_ORIG["$dev"]}
0250         done
0251 }
0252 
0253 if [[ "$NETIF_CREATE" = "yes" ]]; then
0254         create_netif
0255 fi
0256 
0257 if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then
0258         mac_addr_prepare
0259 fi
0260 
0261 for ((i = 1; i <= NUM_NETIFS; ++i)); do
0262         ip link show dev ${NETIFS[p$i]} &> /dev/null
0263         if [[ $? -ne 0 ]]; then
0264                 echo "SKIP: could not find all required interfaces"
0265                 exit $ksft_skip
0266         fi
0267 done
0268 
0269 ##############################################################################
0270 # Helpers
0271 
0272 # Exit status to return at the end. Set in case one of the tests fails.
0273 EXIT_STATUS=0
0274 # Per-test return value. Clear at the beginning of each test.
0275 RET=0
0276 
0277 check_err()
0278 {
0279         local err=$1
0280         local msg=$2
0281 
0282         if [[ $RET -eq 0 && $err -ne 0 ]]; then
0283                 RET=$err
0284                 retmsg=$msg
0285         fi
0286 }
0287 
0288 check_fail()
0289 {
0290         local err=$1
0291         local msg=$2
0292 
0293         if [[ $RET -eq 0 && $err -eq 0 ]]; then
0294                 RET=1
0295                 retmsg=$msg
0296         fi
0297 }
0298 
0299 check_err_fail()
0300 {
0301         local should_fail=$1; shift
0302         local err=$1; shift
0303         local what=$1; shift
0304 
0305         if ((should_fail)); then
0306                 check_fail $err "$what succeeded, but should have failed"
0307         else
0308                 check_err $err "$what failed"
0309         fi
0310 }
0311 
0312 log_test()
0313 {
0314         local test_name=$1
0315         local opt_str=$2
0316 
0317         if [[ $# -eq 2 ]]; then
0318                 opt_str="($opt_str)"
0319         fi
0320 
0321         if [[ $RET -ne 0 ]]; then
0322                 EXIT_STATUS=1
0323                 printf "TEST: %-60s  [FAIL]\n" "$test_name $opt_str"
0324                 if [[ ! -z "$retmsg" ]]; then
0325                         printf "\t%s\n" "$retmsg"
0326                 fi
0327                 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
0328                         echo "Hit enter to continue, 'q' to quit"
0329                         read a
0330                         [ "$a" = "q" ] && exit 1
0331                 fi
0332                 return 1
0333         fi
0334 
0335         printf "TEST: %-60s  [ OK ]\n" "$test_name $opt_str"
0336         return 0
0337 }
0338 
0339 log_test_skip()
0340 {
0341         local test_name=$1
0342         local opt_str=$2
0343 
0344         printf "TEST: %-60s  [SKIP]\n" "$test_name $opt_str"
0345         return 0
0346 }
0347 
0348 log_info()
0349 {
0350         local msg=$1
0351 
0352         echo "INFO: $msg"
0353 }
0354 
0355 busywait()
0356 {
0357         local timeout=$1; shift
0358 
0359         local start_time="$(date -u +%s%3N)"
0360         while true
0361         do
0362                 local out
0363                 out=$("$@")
0364                 local ret=$?
0365                 if ((!ret)); then
0366                         echo -n "$out"
0367                         return 0
0368                 fi
0369 
0370                 local current_time="$(date -u +%s%3N)"
0371                 if ((current_time - start_time > timeout)); then
0372                         echo -n "$out"
0373                         return 1
0374                 fi
0375         done
0376 }
0377 
0378 not()
0379 {
0380         "$@"
0381         [[ $? != 0 ]]
0382 }
0383 
0384 get_max()
0385 {
0386         local arr=("$@")
0387 
0388         max=${arr[0]}
0389         for cur in ${arr[@]}; do
0390                 if [[ $cur -gt $max ]]; then
0391                         max=$cur
0392                 fi
0393         done
0394 
0395         echo $max
0396 }
0397 
0398 grep_bridge_fdb()
0399 {
0400         local addr=$1; shift
0401         local word
0402         local flag
0403 
0404         if [ "$1" == "self" ] || [ "$1" == "master" ]; then
0405                 word=$1; shift
0406                 if [ "$1" == "-v" ]; then
0407                         flag=$1; shift
0408                 fi
0409         fi
0410 
0411         $@ | grep $addr | grep $flag "$word"
0412 }
0413 
0414 wait_for_port_up()
0415 {
0416         "$@" | grep -q "Link detected: yes"
0417 }
0418 
0419 wait_for_offload()
0420 {
0421         "$@" | grep -q offload
0422 }
0423 
0424 wait_for_trap()
0425 {
0426         "$@" | grep -q trap
0427 }
0428 
0429 until_counter_is()
0430 {
0431         local expr=$1; shift
0432         local current=$("$@")
0433 
0434         echo $((current))
0435         ((current $expr))
0436 }
0437 
0438 busywait_for_counter()
0439 {
0440         local timeout=$1; shift
0441         local delta=$1; shift
0442 
0443         local base=$("$@")
0444         busywait "$timeout" until_counter_is ">= $((base + delta))" "$@"
0445 }
0446 
0447 setup_wait_dev()
0448 {
0449         local dev=$1; shift
0450         local wait_time=${1:-$WAIT_TIME}; shift
0451 
0452         setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time
0453 
0454         if (($?)); then
0455                 check_err 1
0456                 log_test setup_wait_dev ": Interface $dev does not come up."
0457                 exit 1
0458         fi
0459 }
0460 
0461 setup_wait_dev_with_timeout()
0462 {
0463         local dev=$1; shift
0464         local max_iterations=${1:-$WAIT_TIMEOUT}; shift
0465         local wait_time=${1:-$WAIT_TIME}; shift
0466         local i
0467 
0468         for ((i = 1; i <= $max_iterations; ++i)); do
0469                 ip link show dev $dev up \
0470                         | grep 'state UP' &> /dev/null
0471                 if [[ $? -ne 0 ]]; then
0472                         sleep 1
0473                 else
0474                         sleep $wait_time
0475                         return 0
0476                 fi
0477         done
0478 
0479         return 1
0480 }
0481 
0482 setup_wait()
0483 {
0484         local num_netifs=${1:-$NUM_NETIFS}
0485         local i
0486 
0487         for ((i = 1; i <= num_netifs; ++i)); do
0488                 setup_wait_dev ${NETIFS[p$i]} 0
0489         done
0490 
0491         # Make sure links are ready.
0492         sleep $WAIT_TIME
0493 }
0494 
0495 cmd_jq()
0496 {
0497         local cmd=$1
0498         local jq_exp=$2
0499         local jq_opts=$3
0500         local ret
0501         local output
0502 
0503         output="$($cmd)"
0504         # it the command fails, return error right away
0505         ret=$?
0506         if [[ $ret -ne 0 ]]; then
0507                 return $ret
0508         fi
0509         output=$(echo $output | jq -r $jq_opts "$jq_exp")
0510         ret=$?
0511         if [[ $ret -ne 0 ]]; then
0512                 return $ret
0513         fi
0514         echo $output
0515         # return success only in case of non-empty output
0516         [ ! -z "$output" ]
0517 }
0518 
0519 lldpad_app_wait_set()
0520 {
0521         local dev=$1; shift
0522 
0523         while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do
0524                 echo "$dev: waiting for lldpad to push pending APP updates"
0525                 sleep 5
0526         done
0527 }
0528 
0529 lldpad_app_wait_del()
0530 {
0531         # Give lldpad a chance to push down the changes. If the device is downed
0532         # too soon, the updates will be left pending. However, they will have
0533         # been struck off the lldpad's DB already, so we won't be able to tell
0534         # they are pending. Then on next test iteration this would cause
0535         # weirdness as newly-added APP rules conflict with the old ones,
0536         # sometimes getting stuck in an "unknown" state.
0537         sleep 5
0538 }
0539 
0540 pre_cleanup()
0541 {
0542         if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
0543                 echo "Pausing before cleanup, hit any key to continue"
0544                 read
0545         fi
0546 
0547         if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then
0548                 mac_addr_restore
0549         fi
0550 }
0551 
0552 vrf_prepare()
0553 {
0554         ip -4 rule add pref 32765 table local
0555         ip -4 rule del pref 0
0556         ip -6 rule add pref 32765 table local
0557         ip -6 rule del pref 0
0558 }
0559 
0560 vrf_cleanup()
0561 {
0562         ip -6 rule add pref 0 table local
0563         ip -6 rule del pref 32765
0564         ip -4 rule add pref 0 table local
0565         ip -4 rule del pref 32765
0566 }
0567 
0568 __last_tb_id=0
0569 declare -A __TB_IDS
0570 
0571 __vrf_td_id_assign()
0572 {
0573         local vrf_name=$1
0574 
0575         __last_tb_id=$((__last_tb_id + 1))
0576         __TB_IDS[$vrf_name]=$__last_tb_id
0577         return $__last_tb_id
0578 }
0579 
0580 __vrf_td_id_lookup()
0581 {
0582         local vrf_name=$1
0583 
0584         return ${__TB_IDS[$vrf_name]}
0585 }
0586 
0587 vrf_create()
0588 {
0589         local vrf_name=$1
0590         local tb_id
0591 
0592         __vrf_td_id_assign $vrf_name
0593         tb_id=$?
0594 
0595         ip link add dev $vrf_name type vrf table $tb_id
0596         ip -4 route add table $tb_id unreachable default metric 4278198272
0597         ip -6 route add table $tb_id unreachable default metric 4278198272
0598 }
0599 
0600 vrf_destroy()
0601 {
0602         local vrf_name=$1
0603         local tb_id
0604 
0605         __vrf_td_id_lookup $vrf_name
0606         tb_id=$?
0607 
0608         ip -6 route del table $tb_id unreachable default metric 4278198272
0609         ip -4 route del table $tb_id unreachable default metric 4278198272
0610         ip link del dev $vrf_name
0611 }
0612 
0613 __addr_add_del()
0614 {
0615         local if_name=$1
0616         local add_del=$2
0617         local array
0618 
0619         shift
0620         shift
0621         array=("${@}")
0622 
0623         for addrstr in "${array[@]}"; do
0624                 ip address $add_del $addrstr dev $if_name
0625         done
0626 }
0627 
0628 __simple_if_init()
0629 {
0630         local if_name=$1; shift
0631         local vrf_name=$1; shift
0632         local addrs=("${@}")
0633 
0634         ip link set dev $if_name master $vrf_name
0635         ip link set dev $if_name up
0636 
0637         __addr_add_del $if_name add "${addrs[@]}"
0638 }
0639 
0640 __simple_if_fini()
0641 {
0642         local if_name=$1; shift
0643         local addrs=("${@}")
0644 
0645         __addr_add_del $if_name del "${addrs[@]}"
0646 
0647         ip link set dev $if_name down
0648         ip link set dev $if_name nomaster
0649 }
0650 
0651 simple_if_init()
0652 {
0653         local if_name=$1
0654         local vrf_name
0655         local array
0656 
0657         shift
0658         vrf_name=v$if_name
0659         array=("${@}")
0660 
0661         vrf_create $vrf_name
0662         ip link set dev $vrf_name up
0663         __simple_if_init $if_name $vrf_name "${array[@]}"
0664 }
0665 
0666 simple_if_fini()
0667 {
0668         local if_name=$1
0669         local vrf_name
0670         local array
0671 
0672         shift
0673         vrf_name=v$if_name
0674         array=("${@}")
0675 
0676         __simple_if_fini $if_name "${array[@]}"
0677         vrf_destroy $vrf_name
0678 }
0679 
0680 tunnel_create()
0681 {
0682         local name=$1; shift
0683         local type=$1; shift
0684         local local=$1; shift
0685         local remote=$1; shift
0686 
0687         ip link add name $name type $type \
0688            local $local remote $remote "$@"
0689         ip link set dev $name up
0690 }
0691 
0692 tunnel_destroy()
0693 {
0694         local name=$1; shift
0695 
0696         ip link del dev $name
0697 }
0698 
0699 vlan_create()
0700 {
0701         local if_name=$1; shift
0702         local vid=$1; shift
0703         local vrf=$1; shift
0704         local ips=("${@}")
0705         local name=$if_name.$vid
0706 
0707         ip link add name $name link $if_name type vlan id $vid
0708         if [ "$vrf" != "" ]; then
0709                 ip link set dev $name master $vrf
0710         fi
0711         ip link set dev $name up
0712         __addr_add_del $name add "${ips[@]}"
0713 }
0714 
0715 vlan_destroy()
0716 {
0717         local if_name=$1; shift
0718         local vid=$1; shift
0719         local name=$if_name.$vid
0720 
0721         ip link del dev $name
0722 }
0723 
0724 team_create()
0725 {
0726         local if_name=$1; shift
0727         local mode=$1; shift
0728 
0729         require_command $TEAMD
0730         $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
0731         for slave in "$@"; do
0732                 ip link set dev $slave down
0733                 ip link set dev $slave master $if_name
0734                 ip link set dev $slave up
0735         done
0736         ip link set dev $if_name up
0737 }
0738 
0739 team_destroy()
0740 {
0741         local if_name=$1; shift
0742 
0743         $TEAMD -t $if_name -k
0744 }
0745 
0746 master_name_get()
0747 {
0748         local if_name=$1
0749 
0750         ip -j link show dev $if_name | jq -r '.[]["master"]'
0751 }
0752 
0753 link_stats_get()
0754 {
0755         local if_name=$1; shift
0756         local dir=$1; shift
0757         local stat=$1; shift
0758 
0759         ip -j -s link show dev $if_name \
0760                 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]'
0761 }
0762 
0763 link_stats_tx_packets_get()
0764 {
0765         link_stats_get $1 tx packets
0766 }
0767 
0768 link_stats_rx_errors_get()
0769 {
0770         link_stats_get $1 rx errors
0771 }
0772 
0773 tc_rule_stats_get()
0774 {
0775         local dev=$1; shift
0776         local pref=$1; shift
0777         local dir=$1; shift
0778         local selector=${1:-.packets}; shift
0779 
0780         tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \
0781             | jq ".[1].options.actions[].stats$selector"
0782 }
0783 
0784 tc_rule_handle_stats_get()
0785 {
0786         local id=$1; shift
0787         local handle=$1; shift
0788         local selector=${1:-.packets}; shift
0789 
0790         tc -j -s filter show $id \
0791             | jq ".[] | select(.options.handle == $handle) | \
0792                   .options.actions[0].stats$selector"
0793 }
0794 
0795 ethtool_stats_get()
0796 {
0797         local dev=$1; shift
0798         local stat=$1; shift
0799 
0800         ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
0801 }
0802 
0803 qdisc_stats_get()
0804 {
0805         local dev=$1; shift
0806         local handle=$1; shift
0807         local selector=$1; shift
0808 
0809         tc -j -s qdisc show dev "$dev" \
0810             | jq '.[] | select(.handle == "'"$handle"'") | '"$selector"
0811 }
0812 
0813 qdisc_parent_stats_get()
0814 {
0815         local dev=$1; shift
0816         local parent=$1; shift
0817         local selector=$1; shift
0818 
0819         tc -j -s qdisc show dev "$dev" invisible \
0820             | jq '.[] | select(.parent == "'"$parent"'") | '"$selector"
0821 }
0822 
0823 ipv6_stats_get()
0824 {
0825         local dev=$1; shift
0826         local stat=$1; shift
0827 
0828         cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f2
0829 }
0830 
0831 hw_stats_get()
0832 {
0833         local suite=$1; shift
0834         local if_name=$1; shift
0835         local dir=$1; shift
0836         local stat=$1; shift
0837 
0838         ip -j stats show dev $if_name group offload subgroup $suite |
0839                 jq ".[0].stats64.$dir.$stat"
0840 }
0841 
0842 humanize()
0843 {
0844         local speed=$1; shift
0845 
0846         for unit in bps Kbps Mbps Gbps; do
0847                 if (($(echo "$speed < 1024" | bc))); then
0848                         break
0849                 fi
0850 
0851                 speed=$(echo "scale=1; $speed / 1024" | bc)
0852         done
0853 
0854         echo "$speed${unit}"
0855 }
0856 
0857 rate()
0858 {
0859         local t0=$1; shift
0860         local t1=$1; shift
0861         local interval=$1; shift
0862 
0863         echo $((8 * (t1 - t0) / interval))
0864 }
0865 
0866 packets_rate()
0867 {
0868         local t0=$1; shift
0869         local t1=$1; shift
0870         local interval=$1; shift
0871 
0872         echo $(((t1 - t0) / interval))
0873 }
0874 
0875 mac_get()
0876 {
0877         local if_name=$1
0878 
0879         ip -j link show dev $if_name | jq -r '.[]["address"]'
0880 }
0881 
0882 ipv6_lladdr_get()
0883 {
0884         local if_name=$1
0885 
0886         ip -j addr show dev $if_name | \
0887                 jq -r '.[]["addr_info"][] | select(.scope == "link").local' | \
0888                 head -1
0889 }
0890 
0891 bridge_ageing_time_get()
0892 {
0893         local bridge=$1
0894         local ageing_time
0895 
0896         # Need to divide by 100 to convert to seconds.
0897         ageing_time=$(ip -j -d link show dev $bridge \
0898                       | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
0899         echo $((ageing_time / 100))
0900 }
0901 
0902 declare -A SYSCTL_ORIG
0903 sysctl_set()
0904 {
0905         local key=$1; shift
0906         local value=$1; shift
0907 
0908         SYSCTL_ORIG[$key]=$(sysctl -n $key)
0909         sysctl -qw $key=$value
0910 }
0911 
0912 sysctl_restore()
0913 {
0914         local key=$1; shift
0915 
0916         sysctl -qw $key=${SYSCTL_ORIG["$key"]}
0917 }
0918 
0919 forwarding_enable()
0920 {
0921         sysctl_set net.ipv4.conf.all.forwarding 1
0922         sysctl_set net.ipv6.conf.all.forwarding 1
0923 }
0924 
0925 forwarding_restore()
0926 {
0927         sysctl_restore net.ipv6.conf.all.forwarding
0928         sysctl_restore net.ipv4.conf.all.forwarding
0929 }
0930 
0931 declare -A MTU_ORIG
0932 mtu_set()
0933 {
0934         local dev=$1; shift
0935         local mtu=$1; shift
0936 
0937         MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')
0938         ip link set dev $dev mtu $mtu
0939 }
0940 
0941 mtu_restore()
0942 {
0943         local dev=$1; shift
0944 
0945         ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
0946 }
0947 
0948 tc_offload_check()
0949 {
0950         local num_netifs=${1:-$NUM_NETIFS}
0951 
0952         for ((i = 1; i <= num_netifs; ++i)); do
0953                 ethtool -k ${NETIFS[p$i]} \
0954                         | grep "hw-tc-offload: on" &> /dev/null
0955                 if [[ $? -ne 0 ]]; then
0956                         return 1
0957                 fi
0958         done
0959 
0960         return 0
0961 }
0962 
0963 trap_install()
0964 {
0965         local dev=$1; shift
0966         local direction=$1; shift
0967 
0968         # Some devices may not support or need in-hardware trapping of traffic
0969         # (e.g. the veth pairs that this library creates for non-existent
0970         # loopbacks). Use continue instead, so that there is a filter in there
0971         # (some tests check counters), and so that other filters are still
0972         # processed.
0973         tc filter add dev $dev $direction pref 1 \
0974                 flower skip_sw action trap 2>/dev/null \
0975             || tc filter add dev $dev $direction pref 1 \
0976                        flower action continue
0977 }
0978 
0979 trap_uninstall()
0980 {
0981         local dev=$1; shift
0982         local direction=$1; shift
0983 
0984         tc filter del dev $dev $direction pref 1 flower
0985 }
0986 
0987 slow_path_trap_install()
0988 {
0989         # For slow-path testing, we need to install a trap to get to
0990         # slow path the packets that would otherwise be switched in HW.
0991         if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
0992                 trap_install "$@"
0993         fi
0994 }
0995 
0996 slow_path_trap_uninstall()
0997 {
0998         if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
0999                 trap_uninstall "$@"
1000         fi
1001 }
1002 
1003 __icmp_capture_add_del()
1004 {
1005         local add_del=$1; shift
1006         local pref=$1; shift
1007         local vsuf=$1; shift
1008         local tundev=$1; shift
1009         local filter=$1; shift
1010 
1011         tc filter $add_del dev "$tundev" ingress \
1012            proto ip$vsuf pref $pref \
1013            flower ip_proto icmp$vsuf $filter \
1014            action pass
1015 }
1016 
1017 icmp_capture_install()
1018 {
1019         __icmp_capture_add_del add 100 "" "$@"
1020 }
1021 
1022 icmp_capture_uninstall()
1023 {
1024         __icmp_capture_add_del del 100 "" "$@"
1025 }
1026 
1027 icmp6_capture_install()
1028 {
1029         __icmp_capture_add_del add 100 v6 "$@"
1030 }
1031 
1032 icmp6_capture_uninstall()
1033 {
1034         __icmp_capture_add_del del 100 v6 "$@"
1035 }
1036 
1037 __vlan_capture_add_del()
1038 {
1039         local add_del=$1; shift
1040         local pref=$1; shift
1041         local dev=$1; shift
1042         local filter=$1; shift
1043 
1044         tc filter $add_del dev "$dev" ingress \
1045            proto 802.1q pref $pref \
1046            flower $filter \
1047            action pass
1048 }
1049 
1050 vlan_capture_install()
1051 {
1052         __vlan_capture_add_del add 100 "$@"
1053 }
1054 
1055 vlan_capture_uninstall()
1056 {
1057         __vlan_capture_add_del del 100 "$@"
1058 }
1059 
1060 __dscp_capture_add_del()
1061 {
1062         local add_del=$1; shift
1063         local dev=$1; shift
1064         local base=$1; shift
1065         local dscp;
1066 
1067         for prio in {0..7}; do
1068                 dscp=$((base + prio))
1069                 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
1070                                        "skip_hw ip_tos $((dscp << 2))"
1071         done
1072 }
1073 
1074 dscp_capture_install()
1075 {
1076         local dev=$1; shift
1077         local base=$1; shift
1078 
1079         __dscp_capture_add_del add $dev $base
1080 }
1081 
1082 dscp_capture_uninstall()
1083 {
1084         local dev=$1; shift
1085         local base=$1; shift
1086 
1087         __dscp_capture_add_del del $dev $base
1088 }
1089 
1090 dscp_fetch_stats()
1091 {
1092         local dev=$1; shift
1093         local base=$1; shift
1094 
1095         for prio in {0..7}; do
1096                 local dscp=$((base + prio))
1097                 local t=$(tc_rule_stats_get $dev $((dscp + 100)))
1098                 echo "[$dscp]=$t "
1099         done
1100 }
1101 
1102 matchall_sink_create()
1103 {
1104         local dev=$1; shift
1105 
1106         tc qdisc add dev $dev clsact
1107         tc filter add dev $dev ingress \
1108            pref 10000 \
1109            matchall \
1110            action drop
1111 }
1112 
1113 tests_run()
1114 {
1115         local current_test
1116 
1117         for current_test in ${TESTS:-$ALL_TESTS}; do
1118                 $current_test
1119         done
1120 }
1121 
1122 multipath_eval()
1123 {
1124         local desc="$1"
1125         local weight_rp12=$2
1126         local weight_rp13=$3
1127         local packets_rp12=$4
1128         local packets_rp13=$5
1129         local weights_ratio packets_ratio diff
1130 
1131         RET=0
1132 
1133         if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1134                 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
1135                                 | bc -l)
1136         else
1137                 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
1138                                 | bc -l)
1139         fi
1140 
1141         if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
1142                check_err 1 "Packet difference is 0"
1143                log_test "Multipath"
1144                log_info "Expected ratio $weights_ratio"
1145                return
1146         fi
1147 
1148         if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1149                 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
1150                                 | bc -l)
1151         else
1152                 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
1153                                 | bc -l)
1154         fi
1155 
1156         diff=$(echo $weights_ratio - $packets_ratio | bc -l)
1157         diff=${diff#-}
1158 
1159         test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
1160         check_err $? "Too large discrepancy between expected and measured ratios"
1161         log_test "$desc"
1162         log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
1163 }
1164 
1165 in_ns()
1166 {
1167         local name=$1; shift
1168 
1169         ip netns exec $name bash <<-EOF
1170                 NUM_NETIFS=0
1171                 source lib.sh
1172                 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
1173         EOF
1174 }
1175 
1176 ##############################################################################
1177 # Tests
1178 
1179 ping_do()
1180 {
1181         local if_name=$1
1182         local dip=$2
1183         local args=$3
1184         local vrf_name
1185 
1186         vrf_name=$(master_name_get $if_name)
1187         ip vrf exec $vrf_name \
1188                 $PING $args $dip -c $PING_COUNT -i 0.1 \
1189                 -w $PING_TIMEOUT &> /dev/null
1190 }
1191 
1192 ping_test()
1193 {
1194         RET=0
1195 
1196         ping_do $1 $2
1197         check_err $?
1198         log_test "ping$3"
1199 }
1200 
1201 ping6_do()
1202 {
1203         local if_name=$1
1204         local dip=$2
1205         local args=$3
1206         local vrf_name
1207 
1208         vrf_name=$(master_name_get $if_name)
1209         ip vrf exec $vrf_name \
1210                 $PING6 $args $dip -c $PING_COUNT -i 0.1 \
1211                 -w $PING_TIMEOUT &> /dev/null
1212 }
1213 
1214 ping6_test()
1215 {
1216         RET=0
1217 
1218         ping6_do $1 $2
1219         check_err $?
1220         log_test "ping6$3"
1221 }
1222 
1223 learning_test()
1224 {
1225         local bridge=$1
1226         local br_port1=$2       # Connected to `host1_if`.
1227         local host1_if=$3
1228         local host2_if=$4
1229         local mac=de:ad:be:ef:13:37
1230         local ageing_time
1231 
1232         RET=0
1233 
1234         bridge -j fdb show br $bridge brport $br_port1 \
1235                 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1236         check_fail $? "Found FDB record when should not"
1237 
1238         # Disable unknown unicast flooding on `br_port1` to make sure
1239         # packets are only forwarded through the port after a matching
1240         # FDB entry was installed.
1241         bridge link set dev $br_port1 flood off
1242 
1243         ip link set $host1_if promisc on
1244         tc qdisc add dev $host1_if ingress
1245         tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
1246                 flower dst_mac $mac action drop
1247 
1248         $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1249         sleep 1
1250 
1251         tc -j -s filter show dev $host1_if ingress \
1252                 | jq -e ".[] | select(.options.handle == 101) \
1253                 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1254         check_fail $? "Packet reached first host when should not"
1255 
1256         $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1257         sleep 1
1258 
1259         bridge -j fdb show br $bridge brport $br_port1 \
1260                 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1261         check_err $? "Did not find FDB record when should"
1262 
1263         $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1264         sleep 1
1265 
1266         tc -j -s filter show dev $host1_if ingress \
1267                 | jq -e ".[] | select(.options.handle == 101) \
1268                 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1269         check_err $? "Packet did not reach second host when should"
1270 
1271         # Wait for 10 seconds after the ageing time to make sure FDB
1272         # record was aged-out.
1273         ageing_time=$(bridge_ageing_time_get $bridge)
1274         sleep $((ageing_time + 10))
1275 
1276         bridge -j fdb show br $bridge brport $br_port1 \
1277                 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1278         check_fail $? "Found FDB record when should not"
1279 
1280         bridge link set dev $br_port1 learning off
1281 
1282         $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1283         sleep 1
1284 
1285         bridge -j fdb show br $bridge brport $br_port1 \
1286                 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1287         check_fail $? "Found FDB record when should not"
1288 
1289         bridge link set dev $br_port1 learning on
1290 
1291         tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
1292         tc qdisc del dev $host1_if ingress
1293         ip link set $host1_if promisc off
1294 
1295         bridge link set dev $br_port1 flood on
1296 
1297         log_test "FDB learning"
1298 }
1299 
1300 flood_test_do()
1301 {
1302         local should_flood=$1
1303         local mac=$2
1304         local ip=$3
1305         local host1_if=$4
1306         local host2_if=$5
1307         local err=0
1308 
1309         # Add an ACL on `host2_if` which will tell us whether the packet
1310         # was flooded to it or not.
1311         ip link set $host2_if promisc on
1312         tc qdisc add dev $host2_if ingress
1313         tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
1314                 flower dst_mac $mac action drop
1315 
1316         $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
1317         sleep 1
1318 
1319         tc -j -s filter show dev $host2_if ingress \
1320                 | jq -e ".[] | select(.options.handle == 101) \
1321                 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1322         if [[ $? -ne 0 && $should_flood == "true" || \
1323               $? -eq 0 && $should_flood == "false" ]]; then
1324                 err=1
1325         fi
1326 
1327         tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
1328         tc qdisc del dev $host2_if ingress
1329         ip link set $host2_if promisc off
1330 
1331         return $err
1332 }
1333 
1334 flood_unicast_test()
1335 {
1336         local br_port=$1
1337         local host1_if=$2
1338         local host2_if=$3
1339         local mac=de:ad:be:ef:13:37
1340         local ip=192.0.2.100
1341 
1342         RET=0
1343 
1344         bridge link set dev $br_port flood off
1345 
1346         flood_test_do false $mac $ip $host1_if $host2_if
1347         check_err $? "Packet flooded when should not"
1348 
1349         bridge link set dev $br_port flood on
1350 
1351         flood_test_do true $mac $ip $host1_if $host2_if
1352         check_err $? "Packet was not flooded when should"
1353 
1354         log_test "Unknown unicast flood"
1355 }
1356 
1357 flood_multicast_test()
1358 {
1359         local br_port=$1
1360         local host1_if=$2
1361         local host2_if=$3
1362         local mac=01:00:5e:00:00:01
1363         local ip=239.0.0.1
1364 
1365         RET=0
1366 
1367         bridge link set dev $br_port mcast_flood off
1368 
1369         flood_test_do false $mac $ip $host1_if $host2_if
1370         check_err $? "Packet flooded when should not"
1371 
1372         bridge link set dev $br_port mcast_flood on
1373 
1374         flood_test_do true $mac $ip $host1_if $host2_if
1375         check_err $? "Packet was not flooded when should"
1376 
1377         log_test "Unregistered multicast flood"
1378 }
1379 
1380 flood_test()
1381 {
1382         # `br_port` is connected to `host2_if`
1383         local br_port=$1
1384         local host1_if=$2
1385         local host2_if=$3
1386 
1387         flood_unicast_test $br_port $host1_if $host2_if
1388         flood_multicast_test $br_port $host1_if $host2_if
1389 }
1390 
1391 __start_traffic()
1392 {
1393         local pktsize=$1; shift
1394         local proto=$1; shift
1395         local h_in=$1; shift    # Where the traffic egresses the host
1396         local sip=$1; shift
1397         local dip=$1; shift
1398         local dmac=$1; shift
1399 
1400         $MZ $h_in -p $pktsize -A $sip -B $dip -c 0 \
1401                 -a own -b $dmac -t "$proto" -q "$@" &
1402         sleep 1
1403 }
1404 
1405 start_traffic_pktsize()
1406 {
1407         local pktsize=$1; shift
1408 
1409         __start_traffic $pktsize udp "$@"
1410 }
1411 
1412 start_tcp_traffic_pktsize()
1413 {
1414         local pktsize=$1; shift
1415 
1416         __start_traffic $pktsize tcp "$@"
1417 }
1418 
1419 start_traffic()
1420 {
1421         start_traffic_pktsize 8000 "$@"
1422 }
1423 
1424 start_tcp_traffic()
1425 {
1426         start_tcp_traffic_pktsize 8000 "$@"
1427 }
1428 
1429 stop_traffic()
1430 {
1431         # Suppress noise from killing mausezahn.
1432         { kill %% && wait %%; } 2>/dev/null
1433 }
1434 
1435 declare -A cappid
1436 declare -A capfile
1437 declare -A capout
1438 
1439 tcpdump_start()
1440 {
1441         local if_name=$1; shift
1442         local ns=$1; shift
1443 
1444         capfile[$if_name]=$(mktemp)
1445         capout[$if_name]=$(mktemp)
1446 
1447         if [ -z $ns ]; then
1448                 ns_cmd=""
1449         else
1450                 ns_cmd="ip netns exec ${ns}"
1451         fi
1452 
1453         if [ -z $SUDO_USER ] ; then
1454                 capuser=""
1455         else
1456                 capuser="-Z $SUDO_USER"
1457         fi
1458 
1459         $ns_cmd tcpdump $TCPDUMP_EXTRA_FLAGS -e -n -Q in -i $if_name \
1460                 -s 65535 -B 32768 $capuser -w ${capfile[$if_name]} \
1461                 > "${capout[$if_name]}" 2>&1 &
1462         cappid[$if_name]=$!
1463 
1464         sleep 1
1465 }
1466 
1467 tcpdump_stop()
1468 {
1469         local if_name=$1
1470         local pid=${cappid[$if_name]}
1471 
1472         $ns_cmd kill "$pid" && wait "$pid"
1473         sleep 1
1474 }
1475 
1476 tcpdump_cleanup()
1477 {
1478         local if_name=$1
1479 
1480         rm ${capfile[$if_name]} ${capout[$if_name]}
1481 }
1482 
1483 tcpdump_show()
1484 {
1485         local if_name=$1
1486 
1487         tcpdump -e -n -r ${capfile[$if_name]} 2>&1
1488 }
1489 
1490 # return 0 if the packet wasn't seen on host2_if or 1 if it was
1491 mcast_packet_test()
1492 {
1493         local mac=$1
1494         local src_ip=$2
1495         local ip=$3
1496         local host1_if=$4
1497         local host2_if=$5
1498         local seen=0
1499         local tc_proto="ip"
1500         local mz_v6arg=""
1501 
1502         # basic check to see if we were passed an IPv4 address, if not assume IPv6
1503         if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
1504                 tc_proto="ipv6"
1505                 mz_v6arg="-6"
1506         fi
1507 
1508         # Add an ACL on `host2_if` which will tell us whether the packet
1509         # was received by it or not.
1510         tc qdisc add dev $host2_if ingress
1511         tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \
1512                 flower ip_proto udp dst_mac $mac action drop
1513 
1514         $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q
1515         sleep 1
1516 
1517         tc -j -s filter show dev $host2_if ingress \
1518                 | jq -e ".[] | select(.options.handle == 101) \
1519                 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1520         if [[ $? -eq 0 ]]; then
1521                 seen=1
1522         fi
1523 
1524         tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower
1525         tc qdisc del dev $host2_if ingress
1526 
1527         return $seen
1528 }
1529 
1530 brmcast_check_sg_entries()
1531 {
1532         local report=$1; shift
1533         local slist=("$@")
1534         local sarg=""
1535 
1536         for src in "${slist[@]}"; do
1537                 sarg="${sarg} and .source_list[].address == \"$src\""
1538         done
1539         bridge -j -d -s mdb show dev br0 \
1540                 | jq -e ".[].mdb[] | \
1541                          select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null
1542         check_err $? "Wrong *,G entry source list after $report report"
1543 
1544         for sgent in "${slist[@]}"; do
1545                 bridge -j -d -s mdb show dev br0 \
1546                         | jq -e ".[].mdb[] | \
1547                                  select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null
1548                 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)"
1549         done
1550 }
1551 
1552 brmcast_check_sg_fwding()
1553 {
1554         local should_fwd=$1; shift
1555         local sources=("$@")
1556 
1557         for src in "${sources[@]}"; do
1558                 local retval=0
1559 
1560                 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1
1561                 retval=$?
1562                 if [ $should_fwd -eq 1 ]; then
1563                         check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)"
1564                 else
1565                         check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)"
1566                 fi
1567         done
1568 }
1569 
1570 brmcast_check_sg_state()
1571 {
1572         local is_blocked=$1; shift
1573         local sources=("$@")
1574         local should_fail=1
1575 
1576         if [ $is_blocked -eq 1 ]; then
1577                 should_fail=0
1578         fi
1579 
1580         for src in "${sources[@]}"; do
1581                 bridge -j -d -s mdb show dev br0 \
1582                         | jq -e ".[].mdb[] | \
1583                                  select(.grp == \"$TEST_GROUP\" and .source_list != null) |
1584                                  .source_list[] |
1585                                  select(.address == \"$src\") |
1586                                  select(.timer == \"0.00\")" &>/dev/null
1587                 check_err_fail $should_fail $? "Entry $src has zero timer"
1588 
1589                 bridge -j -d -s mdb show dev br0 \
1590                         | jq -e ".[].mdb[] | \
1591                                  select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \
1592                                  .flags[] == \"blocked\")" &>/dev/null
1593                 check_err_fail $should_fail $? "Entry $src has blocked flag"
1594         done
1595 }
1596 
1597 mc_join()
1598 {
1599         local if_name=$1
1600         local group=$2
1601         local vrf_name=$(master_name_get $if_name)
1602 
1603         # We don't care about actual reception, just about joining the
1604         # IP multicast group and adding the L2 address to the device's
1605         # MAC filtering table
1606         ip vrf exec $vrf_name \
1607                 mreceive -g $group -I $if_name > /dev/null 2>&1 &
1608         mreceive_pid=$!
1609 
1610         sleep 1
1611 }
1612 
1613 mc_leave()
1614 {
1615         kill "$mreceive_pid" && wait "$mreceive_pid"
1616 }
1617 
1618 mc_send()
1619 {
1620         local if_name=$1
1621         local groups=$2
1622         local vrf_name=$(master_name_get $if_name)
1623 
1624         ip vrf exec $vrf_name \
1625                 msend -g $groups -I $if_name -c 1 > /dev/null 2>&1
1626 }
1627 
1628 start_ip_monitor()
1629 {
1630         local mtype=$1; shift
1631         local ip=${1-ip}; shift
1632 
1633         # start the monitor in the background
1634         tmpfile=`mktemp /var/run/nexthoptestXXX`
1635         mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null`
1636         sleep 0.2
1637         echo "$mpid $tmpfile"
1638 }
1639 
1640 stop_ip_monitor()
1641 {
1642         local mpid=$1; shift
1643         local tmpfile=$1; shift
1644         local el=$1; shift
1645         local what=$1; shift
1646 
1647         sleep 0.2
1648         kill $mpid
1649         local lines=`grep '^\w' $tmpfile | wc -l`
1650         test $lines -eq $el
1651         check_err $? "$what: $lines lines of events, expected $el"
1652         rm -rf $tmpfile
1653 }
1654 
1655 hw_stats_monitor_test()
1656 {
1657         local dev=$1; shift
1658         local type=$1; shift
1659         local make_suitable=$1; shift
1660         local make_unsuitable=$1; shift
1661         local ip=${1-ip}; shift
1662 
1663         RET=0
1664 
1665         # Expect a notification about enablement.
1666         local ipmout=$(start_ip_monitor stats "$ip")
1667         $ip stats set dev $dev ${type}_stats on
1668         stop_ip_monitor $ipmout 1 "${type}_stats enablement"
1669 
1670         # Expect a notification about offload.
1671         local ipmout=$(start_ip_monitor stats "$ip")
1672         $make_suitable
1673         stop_ip_monitor $ipmout 1 "${type}_stats installation"
1674 
1675         # Expect a notification about loss of offload.
1676         local ipmout=$(start_ip_monitor stats "$ip")
1677         $make_unsuitable
1678         stop_ip_monitor $ipmout 1 "${type}_stats deinstallation"
1679 
1680         # Expect a notification about disablement
1681         local ipmout=$(start_ip_monitor stats "$ip")
1682         $ip stats set dev $dev ${type}_stats off
1683         stop_ip_monitor $ipmout 1 "${type}_stats disablement"
1684 
1685         log_test "${type}_stats notifications"
1686 }