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