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