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