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_set() 1138{ 1139 local key=$1; shift 1140 local value=$1; shift 1141 1142 SYSCTL_ORIG[$key]=$(sysctl -n $key) 1143 sysctl -qw $key="$value" 1144} 1145 1146sysctl_restore() 1147{ 1148 local key=$1; shift 1149 1150 sysctl -qw $key="${SYSCTL_ORIG[$key]}" 1151} 1152 1153forwarding_enable() 1154{ 1155 sysctl_set net.ipv4.conf.all.forwarding 1 1156 sysctl_set net.ipv6.conf.all.forwarding 1 1157} 1158 1159forwarding_restore() 1160{ 1161 sysctl_restore net.ipv6.conf.all.forwarding 1162 sysctl_restore net.ipv4.conf.all.forwarding 1163} 1164 1165declare -A MTU_ORIG 1166mtu_set() 1167{ 1168 local dev=$1; shift 1169 local mtu=$1; shift 1170 1171 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu') 1172 ip link set dev $dev mtu $mtu 1173} 1174 1175mtu_restore() 1176{ 1177 local dev=$1; shift 1178 1179 ip link set dev $dev mtu ${MTU_ORIG["$dev"]} 1180} 1181 1182tc_offload_check() 1183{ 1184 local num_netifs=${1:-$NUM_NETIFS} 1185 1186 for ((i = 1; i <= num_netifs; ++i)); do 1187 ethtool -k ${NETIFS[p$i]} \ 1188 | grep "hw-tc-offload: on" &> /dev/null 1189 if [[ $? -ne 0 ]]; then 1190 return 1 1191 fi 1192 done 1193 1194 return 0 1195} 1196 1197trap_install() 1198{ 1199 local dev=$1; shift 1200 local direction=$1; shift 1201 1202 # Some devices may not support or need in-hardware trapping of traffic 1203 # (e.g. the veth pairs that this library creates for non-existent 1204 # loopbacks). Use continue instead, so that there is a filter in there 1205 # (some tests check counters), and so that other filters are still 1206 # processed. 1207 tc filter add dev $dev $direction pref 1 \ 1208 flower skip_sw action trap 2>/dev/null \ 1209 || tc filter add dev $dev $direction pref 1 \ 1210 flower action continue 1211} 1212 1213trap_uninstall() 1214{ 1215 local dev=$1; shift 1216 local direction=$1; shift 1217 1218 tc filter del dev $dev $direction pref 1 flower 1219} 1220 1221slow_path_trap_install() 1222{ 1223 # For slow-path testing, we need to install a trap to get to 1224 # slow path the packets that would otherwise be switched in HW. 1225 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 1226 trap_install "$@" 1227 fi 1228} 1229 1230slow_path_trap_uninstall() 1231{ 1232 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 1233 trap_uninstall "$@" 1234 fi 1235} 1236 1237__icmp_capture_add_del() 1238{ 1239 local add_del=$1; shift 1240 local pref=$1; shift 1241 local vsuf=$1; shift 1242 local tundev=$1; shift 1243 local filter=$1; shift 1244 1245 tc filter $add_del dev "$tundev" ingress \ 1246 proto ip$vsuf pref $pref \ 1247 flower ip_proto icmp$vsuf $filter \ 1248 action pass 1249} 1250 1251icmp_capture_install() 1252{ 1253 __icmp_capture_add_del add 100 "" "$@" 1254} 1255 1256icmp_capture_uninstall() 1257{ 1258 __icmp_capture_add_del del 100 "" "$@" 1259} 1260 1261icmp6_capture_install() 1262{ 1263 __icmp_capture_add_del add 100 v6 "$@" 1264} 1265 1266icmp6_capture_uninstall() 1267{ 1268 __icmp_capture_add_del del 100 v6 "$@" 1269} 1270 1271__vlan_capture_add_del() 1272{ 1273 local add_del=$1; shift 1274 local pref=$1; shift 1275 local dev=$1; shift 1276 local filter=$1; shift 1277 1278 tc filter $add_del dev "$dev" ingress \ 1279 proto 802.1q pref $pref \ 1280 flower $filter \ 1281 action pass 1282} 1283 1284vlan_capture_install() 1285{ 1286 __vlan_capture_add_del add 100 "$@" 1287} 1288 1289vlan_capture_uninstall() 1290{ 1291 __vlan_capture_add_del del 100 "$@" 1292} 1293 1294__dscp_capture_add_del() 1295{ 1296 local add_del=$1; shift 1297 local dev=$1; shift 1298 local base=$1; shift 1299 local dscp; 1300 1301 for prio in {0..7}; do 1302 dscp=$((base + prio)) 1303 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \ 1304 "skip_hw ip_tos $((dscp << 2))" 1305 done 1306} 1307 1308dscp_capture_install() 1309{ 1310 local dev=$1; shift 1311 local base=$1; shift 1312 1313 __dscp_capture_add_del add $dev $base 1314} 1315 1316dscp_capture_uninstall() 1317{ 1318 local dev=$1; shift 1319 local base=$1; shift 1320 1321 __dscp_capture_add_del del $dev $base 1322} 1323 1324dscp_fetch_stats() 1325{ 1326 local dev=$1; shift 1327 local base=$1; shift 1328 1329 for prio in {0..7}; do 1330 local dscp=$((base + prio)) 1331 local t=$(tc_rule_stats_get $dev $((dscp + 100))) 1332 echo "[$dscp]=$t " 1333 done 1334} 1335 1336matchall_sink_create() 1337{ 1338 local dev=$1; shift 1339 1340 tc qdisc add dev $dev clsact 1341 tc filter add dev $dev ingress \ 1342 pref 10000 \ 1343 matchall \ 1344 action drop 1345} 1346 1347tests_run() 1348{ 1349 local current_test 1350 1351 for current_test in ${TESTS:-$ALL_TESTS}; do 1352 $current_test 1353 done 1354} 1355 1356multipath_eval() 1357{ 1358 local desc="$1" 1359 local weight_rp12=$2 1360 local weight_rp13=$3 1361 local packets_rp12=$4 1362 local packets_rp13=$5 1363 local weights_ratio packets_ratio diff 1364 1365 RET=0 1366 1367 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 1368 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ 1369 | bc -l) 1370 else 1371 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \ 1372 | bc -l) 1373 fi 1374 1375 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then 1376 check_err 1 "Packet difference is 0" 1377 log_test "Multipath" 1378 log_info "Expected ratio $weights_ratio" 1379 return 1380 fi 1381 1382 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 1383 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ 1384 | bc -l) 1385 else 1386 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \ 1387 | bc -l) 1388 fi 1389 1390 diff=$(echo $weights_ratio - $packets_ratio | bc -l) 1391 diff=${diff#-} 1392 1393 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 1394 check_err $? "Too large discrepancy between expected and measured ratios" 1395 log_test "$desc" 1396 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" 1397} 1398 1399in_ns() 1400{ 1401 local name=$1; shift 1402 1403 ip netns exec $name bash <<-EOF 1404 NUM_NETIFS=0 1405 source lib.sh 1406 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done) 1407 EOF 1408} 1409 1410############################################################################## 1411# Tests 1412 1413ping_do() 1414{ 1415 local if_name=$1 1416 local dip=$2 1417 local args=$3 1418 local vrf_name 1419 1420 vrf_name=$(master_name_get $if_name) 1421 ip vrf exec $vrf_name \ 1422 $PING $args $dip -c $PING_COUNT -i 0.1 \ 1423 -w $PING_TIMEOUT &> /dev/null 1424} 1425 1426ping_test() 1427{ 1428 RET=0 1429 1430 ping_do $1 $2 1431 check_err $? 1432 log_test "ping$3" 1433} 1434 1435ping_test_fails() 1436{ 1437 RET=0 1438 1439 ping_do $1 $2 1440 check_fail $? 1441 log_test "ping fails$3" 1442} 1443 1444ping6_do() 1445{ 1446 local if_name=$1 1447 local dip=$2 1448 local args=$3 1449 local vrf_name 1450 1451 vrf_name=$(master_name_get $if_name) 1452 ip vrf exec $vrf_name \ 1453 $PING6 $args $dip -c $PING_COUNT -i 0.1 \ 1454 -w $PING_TIMEOUT &> /dev/null 1455} 1456 1457ping6_test() 1458{ 1459 RET=0 1460 1461 ping6_do $1 $2 1462 check_err $? 1463 log_test "ping6$3" 1464} 1465 1466ping6_test_fails() 1467{ 1468 RET=0 1469 1470 ping6_do $1 $2 1471 check_fail $? 1472 log_test "ping6 fails$3" 1473} 1474 1475learning_test() 1476{ 1477 local bridge=$1 1478 local br_port1=$2 # Connected to `host1_if`. 1479 local host1_if=$3 1480 local host2_if=$4 1481 local mac=de:ad:be:ef:13:37 1482 local ageing_time 1483 1484 RET=0 1485 1486 bridge -j fdb show br $bridge brport $br_port1 \ 1487 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1488 check_fail $? "Found FDB record when should not" 1489 1490 # Disable unknown unicast flooding on `br_port1` to make sure 1491 # packets are only forwarded through the port after a matching 1492 # FDB entry was installed. 1493 bridge link set dev $br_port1 flood off 1494 1495 ip link set $host1_if promisc on 1496 tc qdisc add dev $host1_if ingress 1497 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \ 1498 flower dst_mac $mac action drop 1499 1500 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1501 sleep 1 1502 1503 tc -j -s filter show dev $host1_if ingress \ 1504 | jq -e ".[] | select(.options.handle == 101) \ 1505 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1506 check_fail $? "Packet reached first host when should not" 1507 1508 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1509 sleep 1 1510 1511 bridge -j fdb show br $bridge brport $br_port1 \ 1512 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1513 check_err $? "Did not find FDB record when should" 1514 1515 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1516 sleep 1 1517 1518 tc -j -s filter show dev $host1_if ingress \ 1519 | jq -e ".[] | select(.options.handle == 101) \ 1520 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1521 check_err $? "Packet did not reach second host when should" 1522 1523 # Wait for 10 seconds after the ageing time to make sure FDB 1524 # record was aged-out. 1525 ageing_time=$(bridge_ageing_time_get $bridge) 1526 sleep $((ageing_time + 10)) 1527 1528 bridge -j fdb show br $bridge brport $br_port1 \ 1529 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1530 check_fail $? "Found FDB record when should not" 1531 1532 bridge link set dev $br_port1 learning off 1533 1534 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1535 sleep 1 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 on 1542 1543 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower 1544 tc qdisc del dev $host1_if ingress 1545 ip link set $host1_if promisc off 1546 1547 bridge link set dev $br_port1 flood on 1548 1549 log_test "FDB learning" 1550} 1551 1552flood_test_do() 1553{ 1554 local should_flood=$1 1555 local mac=$2 1556 local ip=$3 1557 local host1_if=$4 1558 local host2_if=$5 1559 local err=0 1560 1561 # Add an ACL on `host2_if` which will tell us whether the packet 1562 # was flooded to it or not. 1563 ip link set $host2_if promisc on 1564 tc qdisc add dev $host2_if ingress 1565 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \ 1566 flower dst_mac $mac action drop 1567 1568 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q 1569 sleep 1 1570 1571 tc -j -s filter show dev $host2_if ingress \ 1572 | jq -e ".[] | select(.options.handle == 101) \ 1573 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1574 if [[ $? -ne 0 && $should_flood == "true" || \ 1575 $? -eq 0 && $should_flood == "false" ]]; then 1576 err=1 1577 fi 1578 1579 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower 1580 tc qdisc del dev $host2_if ingress 1581 ip link set $host2_if promisc off 1582 1583 return $err 1584} 1585 1586flood_unicast_test() 1587{ 1588 local br_port=$1 1589 local host1_if=$2 1590 local host2_if=$3 1591 local mac=de:ad:be:ef:13:37 1592 local ip=192.0.2.100 1593 1594 RET=0 1595 1596 bridge link set dev $br_port flood off 1597 1598 flood_test_do false $mac $ip $host1_if $host2_if 1599 check_err $? "Packet flooded when should not" 1600 1601 bridge link set dev $br_port flood on 1602 1603 flood_test_do true $mac $ip $host1_if $host2_if 1604 check_err $? "Packet was not flooded when should" 1605 1606 log_test "Unknown unicast flood" 1607} 1608 1609flood_multicast_test() 1610{ 1611 local br_port=$1 1612 local host1_if=$2 1613 local host2_if=$3 1614 local mac=01:00:5e:00:00:01 1615 local ip=239.0.0.1 1616 1617 RET=0 1618 1619 bridge link set dev $br_port mcast_flood off 1620 1621 flood_test_do false $mac $ip $host1_if $host2_if 1622 check_err $? "Packet flooded when should not" 1623 1624 bridge link set dev $br_port mcast_flood on 1625 1626 flood_test_do true $mac $ip $host1_if $host2_if 1627 check_err $? "Packet was not flooded when should" 1628 1629 log_test "Unregistered multicast flood" 1630} 1631 1632flood_test() 1633{ 1634 # `br_port` is connected to `host2_if` 1635 local br_port=$1 1636 local host1_if=$2 1637 local host2_if=$3 1638 1639 flood_unicast_test $br_port $host1_if $host2_if 1640 flood_multicast_test $br_port $host1_if $host2_if 1641} 1642 1643__start_traffic() 1644{ 1645 local pktsize=$1; shift 1646 local proto=$1; shift 1647 local h_in=$1; shift # Where the traffic egresses the host 1648 local sip=$1; shift 1649 local dip=$1; shift 1650 local dmac=$1; shift 1651 1652 $MZ $h_in -p $pktsize -A $sip -B $dip -c 0 \ 1653 -a own -b $dmac -t "$proto" -q "$@" & 1654 sleep 1 1655} 1656 1657start_traffic_pktsize() 1658{ 1659 local pktsize=$1; shift 1660 1661 __start_traffic $pktsize udp "$@" 1662} 1663 1664start_tcp_traffic_pktsize() 1665{ 1666 local pktsize=$1; shift 1667 1668 __start_traffic $pktsize tcp "$@" 1669} 1670 1671start_traffic() 1672{ 1673 start_traffic_pktsize 8000 "$@" 1674} 1675 1676start_tcp_traffic() 1677{ 1678 start_tcp_traffic_pktsize 8000 "$@" 1679} 1680 1681stop_traffic() 1682{ 1683 # Suppress noise from killing mausezahn. 1684 { kill %% && wait %%; } 2>/dev/null 1685} 1686 1687declare -A cappid 1688declare -A capfile 1689declare -A capout 1690 1691tcpdump_start() 1692{ 1693 local if_name=$1; shift 1694 local ns=$1; shift 1695 1696 capfile[$if_name]=$(mktemp) 1697 capout[$if_name]=$(mktemp) 1698 1699 if [ -z $ns ]; then 1700 ns_cmd="" 1701 else 1702 ns_cmd="ip netns exec ${ns}" 1703 fi 1704 1705 if [ -z $SUDO_USER ] ; then 1706 capuser="" 1707 else 1708 capuser="-Z $SUDO_USER" 1709 fi 1710 1711 $ns_cmd tcpdump $TCPDUMP_EXTRA_FLAGS -e -n -Q in -i $if_name \ 1712 -s 65535 -B 32768 $capuser -w ${capfile[$if_name]} \ 1713 > "${capout[$if_name]}" 2>&1 & 1714 cappid[$if_name]=$! 1715 1716 sleep 1 1717} 1718 1719tcpdump_stop() 1720{ 1721 local if_name=$1 1722 local pid=${cappid[$if_name]} 1723 1724 $ns_cmd kill "$pid" && wait "$pid" 1725 sleep 1 1726} 1727 1728tcpdump_cleanup() 1729{ 1730 local if_name=$1 1731 1732 rm ${capfile[$if_name]} ${capout[$if_name]} 1733} 1734 1735tcpdump_show() 1736{ 1737 local if_name=$1 1738 1739 tcpdump -e -n -r ${capfile[$if_name]} 2>&1 1740} 1741 1742# return 0 if the packet wasn't seen on host2_if or 1 if it was 1743mcast_packet_test() 1744{ 1745 local mac=$1 1746 local src_ip=$2 1747 local ip=$3 1748 local host1_if=$4 1749 local host2_if=$5 1750 local seen=0 1751 local tc_proto="ip" 1752 local mz_v6arg="" 1753 1754 # basic check to see if we were passed an IPv4 address, if not assume IPv6 1755 if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then 1756 tc_proto="ipv6" 1757 mz_v6arg="-6" 1758 fi 1759 1760 # Add an ACL on `host2_if` which will tell us whether the packet 1761 # was received by it or not. 1762 tc qdisc add dev $host2_if ingress 1763 tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \ 1764 flower ip_proto udp dst_mac $mac action drop 1765 1766 $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q 1767 sleep 1 1768 1769 tc -j -s filter show dev $host2_if ingress \ 1770 | jq -e ".[] | select(.options.handle == 101) \ 1771 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1772 if [[ $? -eq 0 ]]; then 1773 seen=1 1774 fi 1775 1776 tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower 1777 tc qdisc del dev $host2_if ingress 1778 1779 return $seen 1780} 1781 1782brmcast_check_sg_entries() 1783{ 1784 local report=$1; shift 1785 local slist=("$@") 1786 local sarg="" 1787 1788 for src in "${slist[@]}"; do 1789 sarg="${sarg} and .source_list[].address == \"$src\"" 1790 done 1791 bridge -j -d -s mdb show dev br0 \ 1792 | jq -e ".[].mdb[] | \ 1793 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null 1794 check_err $? "Wrong *,G entry source list after $report report" 1795 1796 for sgent in "${slist[@]}"; do 1797 bridge -j -d -s mdb show dev br0 \ 1798 | jq -e ".[].mdb[] | \ 1799 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null 1800 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)" 1801 done 1802} 1803 1804brmcast_check_sg_fwding() 1805{ 1806 local should_fwd=$1; shift 1807 local sources=("$@") 1808 1809 for src in "${sources[@]}"; do 1810 local retval=0 1811 1812 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1 1813 retval=$? 1814 if [ $should_fwd -eq 1 ]; then 1815 check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)" 1816 else 1817 check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)" 1818 fi 1819 done 1820} 1821 1822brmcast_check_sg_state() 1823{ 1824 local is_blocked=$1; shift 1825 local sources=("$@") 1826 local should_fail=1 1827 1828 if [ $is_blocked -eq 1 ]; then 1829 should_fail=0 1830 fi 1831 1832 for src in "${sources[@]}"; do 1833 bridge -j -d -s mdb show dev br0 \ 1834 | jq -e ".[].mdb[] | \ 1835 select(.grp == \"$TEST_GROUP\" and .source_list != null) | 1836 .source_list[] | 1837 select(.address == \"$src\") | 1838 select(.timer == \"0.00\")" &>/dev/null 1839 check_err_fail $should_fail $? "Entry $src has zero timer" 1840 1841 bridge -j -d -s mdb show dev br0 \ 1842 | jq -e ".[].mdb[] | \ 1843 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \ 1844 .flags[] == \"blocked\")" &>/dev/null 1845 check_err_fail $should_fail $? "Entry $src has blocked flag" 1846 done 1847} 1848 1849mc_join() 1850{ 1851 local if_name=$1 1852 local group=$2 1853 local vrf_name=$(master_name_get $if_name) 1854 1855 # We don't care about actual reception, just about joining the 1856 # IP multicast group and adding the L2 address to the device's 1857 # MAC filtering table 1858 ip vrf exec $vrf_name \ 1859 mreceive -g $group -I $if_name > /dev/null 2>&1 & 1860 mreceive_pid=$! 1861 1862 sleep 1 1863} 1864 1865mc_leave() 1866{ 1867 kill "$mreceive_pid" && wait "$mreceive_pid" 1868} 1869 1870mc_send() 1871{ 1872 local if_name=$1 1873 local groups=$2 1874 local vrf_name=$(master_name_get $if_name) 1875 1876 ip vrf exec $vrf_name \ 1877 msend -g $groups -I $if_name -c 1 > /dev/null 2>&1 1878} 1879 1880start_ip_monitor() 1881{ 1882 local mtype=$1; shift 1883 local ip=${1-ip}; shift 1884 1885 # start the monitor in the background 1886 tmpfile=`mktemp /var/run/nexthoptestXXX` 1887 mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null` 1888 sleep 0.2 1889 echo "$mpid $tmpfile" 1890} 1891 1892stop_ip_monitor() 1893{ 1894 local mpid=$1; shift 1895 local tmpfile=$1; shift 1896 local el=$1; shift 1897 local what=$1; shift 1898 1899 sleep 0.2 1900 kill $mpid 1901 local lines=`grep '^\w' $tmpfile | wc -l` 1902 test $lines -eq $el 1903 check_err $? "$what: $lines lines of events, expected $el" 1904 rm -rf $tmpfile 1905} 1906 1907hw_stats_monitor_test() 1908{ 1909 local dev=$1; shift 1910 local type=$1; shift 1911 local make_suitable=$1; shift 1912 local make_unsuitable=$1; shift 1913 local ip=${1-ip}; shift 1914 1915 RET=0 1916 1917 # Expect a notification about enablement. 1918 local ipmout=$(start_ip_monitor stats "$ip") 1919 $ip stats set dev $dev ${type}_stats on 1920 stop_ip_monitor $ipmout 1 "${type}_stats enablement" 1921 1922 # Expect a notification about offload. 1923 local ipmout=$(start_ip_monitor stats "$ip") 1924 $make_suitable 1925 stop_ip_monitor $ipmout 1 "${type}_stats installation" 1926 1927 # Expect a notification about loss of offload. 1928 local ipmout=$(start_ip_monitor stats "$ip") 1929 $make_unsuitable 1930 stop_ip_monitor $ipmout 1 "${type}_stats deinstallation" 1931 1932 # Expect a notification about disablement 1933 local ipmout=$(start_ip_monitor stats "$ip") 1934 $ip stats set dev $dev ${type}_stats off 1935 stop_ip_monitor $ipmout 1 "${type}_stats disablement" 1936 1937 log_test "${type}_stats notifications" 1938} 1939 1940ipv4_to_bytes() 1941{ 1942 local IP=$1; shift 1943 1944 printf '%02x:' ${IP//./ } | 1945 sed 's/:$//' 1946} 1947 1948# Convert a given IPv6 address, `IP' such that the :: token, if present, is 1949# expanded, and each 16-bit group is padded with zeroes to be 4 hexadecimal 1950# digits. An optional `BYTESEP' parameter can be given to further separate 1951# individual bytes of each 16-bit group. 1952expand_ipv6() 1953{ 1954 local IP=$1; shift 1955 local bytesep=$1; shift 1956 1957 local cvt_ip=${IP/::/_} 1958 local colons=${cvt_ip//[^:]/} 1959 local allcol=::::::: 1960 # IP where :: -> the appropriate number of colons: 1961 local allcol_ip=${cvt_ip/_/${allcol:${#colons}}} 1962 1963 echo $allcol_ip | tr : '\n' | 1964 sed s/^/0000/ | 1965 sed 's/.*\(..\)\(..\)/\1'"$bytesep"'\2/' | 1966 tr '\n' : | 1967 sed 's/:$//' 1968} 1969 1970ipv6_to_bytes() 1971{ 1972 local IP=$1; shift 1973 1974 expand_ipv6 "$IP" : 1975} 1976 1977u16_to_bytes() 1978{ 1979 local u16=$1; shift 1980 1981 printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/' 1982} 1983 1984# Given a mausezahn-formatted payload (colon-separated bytes given as %02x), 1985# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be, 1986# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any) 1987# stands for 00:00. 1988payload_template_calc_checksum() 1989{ 1990 local payload=$1; shift 1991 1992 ( 1993 # Set input radix. 1994 echo "16i" 1995 # Push zero for the initial checksum. 1996 echo 0 1997 1998 # Pad the payload with a terminating 00: in case we get an odd 1999 # number of bytes. 2000 echo "${payload%:}:00:" | 2001 sed 's/CHECKSUM/00:00/g' | 2002 tr '[:lower:]' '[:upper:]' | 2003 # Add the word to the checksum. 2004 sed 's/\(..\):\(..\):/\1\2+\n/g' | 2005 # Strip the extra odd byte we pushed if left unconverted. 2006 sed 's/\(..\):$//' 2007 2008 echo "10000 ~ +" # Calculate and add carry. 2009 echo "FFFF r - p" # Bit-flip and print. 2010 ) | 2011 dc | 2012 tr '[:upper:]' '[:lower:]' 2013} 2014 2015payload_template_expand_checksum() 2016{ 2017 local payload=$1; shift 2018 local checksum=$1; shift 2019 2020 local ckbytes=$(u16_to_bytes $checksum) 2021 2022 echo "$payload" | sed "s/CHECKSUM/$ckbytes/g" 2023} 2024 2025payload_template_nbytes() 2026{ 2027 local payload=$1; shift 2028 2029 payload_template_expand_checksum "${payload%:}" 0 | 2030 sed 's/:/\n/g' | wc -l 2031} 2032 2033igmpv3_is_in_get() 2034{ 2035 local GRP=$1; shift 2036 local sources=("$@") 2037 2038 local igmpv3 2039 local nsources=$(u16_to_bytes ${#sources[@]}) 2040 2041 # IS_IN ( $sources ) 2042 igmpv3=$(: 2043 )"22:"$( : Type - Membership Report 2044 )"00:"$( : Reserved 2045 )"CHECKSUM:"$( : Checksum 2046 )"00:00:"$( : Reserved 2047 )"00:01:"$( : Number of Group Records 2048 )"01:"$( : Record Type - IS_IN 2049 )"00:"$( : Aux Data Len 2050 )"${nsources}:"$( : Number of Sources 2051 )"$(ipv4_to_bytes $GRP):"$( : Multicast Address 2052 )"$(for src in "${sources[@]}"; do 2053 ipv4_to_bytes $src 2054 echo -n : 2055 done)"$( : Source Addresses 2056 ) 2057 local checksum=$(payload_template_calc_checksum "$igmpv3") 2058 2059 payload_template_expand_checksum "$igmpv3" $checksum 2060} 2061 2062igmpv2_leave_get() 2063{ 2064 local GRP=$1; shift 2065 2066 local payload=$(: 2067 )"17:"$( : Type - Leave Group 2068 )"00:"$( : Max Resp Time - not meaningful 2069 )"CHECKSUM:"$( : Checksum 2070 )"$(ipv4_to_bytes $GRP)"$( : Group Address 2071 ) 2072 local checksum=$(payload_template_calc_checksum "$payload") 2073 2074 payload_template_expand_checksum "$payload" $checksum 2075} 2076 2077mldv2_is_in_get() 2078{ 2079 local SIP=$1; shift 2080 local GRP=$1; shift 2081 local sources=("$@") 2082 2083 local hbh 2084 local icmpv6 2085 local nsources=$(u16_to_bytes ${#sources[@]}) 2086 2087 hbh=$(: 2088 )"3a:"$( : Next Header - ICMPv6 2089 )"00:"$( : Hdr Ext Len 2090 )"00:00:00:00:00:00:"$( : Options and Padding 2091 ) 2092 2093 icmpv6=$(: 2094 )"8f:"$( : Type - MLDv2 Report 2095 )"00:"$( : Code 2096 )"CHECKSUM:"$( : Checksum 2097 )"00:00:"$( : Reserved 2098 )"00:01:"$( : Number of Group Records 2099 )"01:"$( : Record Type - IS_IN 2100 )"00:"$( : Aux Data Len 2101 )"${nsources}:"$( : Number of Sources 2102 )"$(ipv6_to_bytes $GRP):"$( : Multicast address 2103 )"$(for src in "${sources[@]}"; do 2104 ipv6_to_bytes $src 2105 echo -n : 2106 done)"$( : Source Addresses 2107 ) 2108 2109 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6)) 2110 local sudohdr=$(: 2111 )"$(ipv6_to_bytes $SIP):"$( : SIP 2112 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address 2113 )"${len}:"$( : Upper-layer length 2114 )"00:3a:"$( : Zero and next-header 2115 ) 2116 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6}) 2117 2118 payload_template_expand_checksum "$hbh$icmpv6" $checksum 2119} 2120 2121mldv1_done_get() 2122{ 2123 local SIP=$1; shift 2124 local GRP=$1; shift 2125 2126 local hbh 2127 local icmpv6 2128 2129 hbh=$(: 2130 )"3a:"$( : Next Header - ICMPv6 2131 )"00:"$( : Hdr Ext Len 2132 )"00:00:00:00:00:00:"$( : Options and Padding 2133 ) 2134 2135 icmpv6=$(: 2136 )"84:"$( : Type - MLDv1 Done 2137 )"00:"$( : Code 2138 )"CHECKSUM:"$( : Checksum 2139 )"00:00:"$( : Max Resp Delay - not meaningful 2140 )"00:00:"$( : Reserved 2141 )"$(ipv6_to_bytes $GRP):"$( : Multicast address 2142 ) 2143 2144 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6)) 2145 local sudohdr=$(: 2146 )"$(ipv6_to_bytes $SIP):"$( : SIP 2147 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address 2148 )"${len}:"$( : Upper-layer length 2149 )"00:3a:"$( : Zero and next-header 2150 ) 2151 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6}) 2152 2153 payload_template_expand_checksum "$hbh$icmpv6" $checksum 2154} 2155 2156bail_on_lldpad() 2157{ 2158 local reason1="$1"; shift 2159 local reason2="$1"; shift 2160 local caller=${FUNCNAME[1]} 2161 local src=${BASH_SOURCE[1]} 2162 2163 if systemctl is-active --quiet lldpad; then 2164 2165 cat >/dev/stderr <<-EOF 2166 WARNING: lldpad is running 2167 2168 lldpad will likely $reason1, and this test will 2169 $reason2. Both are not supported at the same time, 2170 one of them is arbitrarily going to overwrite the 2171 other. That will cause spurious failures (or, unlikely, 2172 passes) of this test. 2173 EOF 2174 2175 if [[ -z $ALLOW_LLDPAD ]]; then 2176 cat >/dev/stderr <<-EOF 2177 2178 If you want to run the test anyway, please set 2179 an environment variable ALLOW_LLDPAD to a 2180 non-empty string. 2181 EOF 2182 log_test_skip $src:$caller 2183 exit $EXIT_STATUS 2184 else 2185 return 2186 fi 2187 fi 2188} 2189 2190absval() 2191{ 2192 local v=$1; shift 2193 2194 echo $((v > 0 ? v : -v)) 2195} 2196