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