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