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