0001
0002
0003
0004
0005
0006
0007
0008 ksft_skip=4
0009
0010
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
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
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
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
0167
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
0179
0180 count=0
0181
0182 while [[ $
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
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
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
0271
0272
0273 EXIT_STATUS=0
0274
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 [[ $
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
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
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
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
0532
0533
0534
0535
0536
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
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
0969
0970
0971
0972
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
0990
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
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
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
1239
1240
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
1272
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
1310
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
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
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
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
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
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
1509
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
1604
1605
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
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
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
1671 local ipmout=$(start_ip_monitor stats "$ip")
1672 $make_suitable
1673 stop_ip_monitor $ipmout 1 "${type}_stats installation"
1674
1675
1676 local ipmout=$(start_ip_monitor stats "$ip")
1677 $make_unsuitable
1678 stop_ip_monitor $ipmout 1 "${type}_stats deinstallation"
1679
1680
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 }