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