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