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