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