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