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