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