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