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