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