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