1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3 4############################################################################## 5# Defines 6 7# Kselftest framework requirement - SKIP code is 4. 8ksft_skip=4 9 10# Can be overridden by the configuration file. 11PING=${PING:=ping} 12PING6=${PING6:=ping6} 13MZ=${MZ:=mausezahn} 14ARPING=${ARPING:=arping} 15TEAMD=${TEAMD:=teamd} 16WAIT_TIME=${WAIT_TIME:=5} 17PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} 18PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no} 19NETIF_TYPE=${NETIF_TYPE:=veth} 20NETIF_CREATE=${NETIF_CREATE:=yes} 21MCD=${MCD:=smcrouted} 22MC_CLI=${MC_CLI:=smcroutectl} 23PING_TIMEOUT=${PING_TIMEOUT:=5} 24WAIT_TIMEOUT=${WAIT_TIMEOUT:=20} 25INTERFACE_TIMEOUT=${INTERFACE_TIMEOUT:=600} 26REQUIRE_JQ=${REQUIRE_JQ:=yes} 27REQUIRE_MZ=${REQUIRE_MZ:=yes} 28 29relative_path="${BASH_SOURCE%/*}" 30if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then 31 relative_path="." 32fi 33 34if [[ -f $relative_path/forwarding.config ]]; then 35 source "$relative_path/forwarding.config" 36fi 37 38############################################################################## 39# Sanity checks 40 41check_tc_version() 42{ 43 tc -j &> /dev/null 44 if [[ $? -ne 0 ]]; then 45 echo "SKIP: iproute2 too old; tc is missing JSON support" 46 exit $ksft_skip 47 fi 48} 49 50# Old versions of tc don't understand "mpls_uc" 51check_tc_mpls_support() 52{ 53 local dev=$1; shift 54 55 tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \ 56 matchall action pipe &> /dev/null 57 if [[ $? -ne 0 ]]; then 58 echo "SKIP: iproute2 too old; tc is missing MPLS support" 59 return $ksft_skip 60 fi 61 tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \ 62 matchall 63} 64 65# Old versions of tc produce invalid json output for mpls lse statistics 66check_tc_mpls_lse_stats() 67{ 68 local dev=$1; shift 69 local ret; 70 71 tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \ 72 flower mpls lse depth 2 \ 73 action continue &> /dev/null 74 75 if [[ $? -ne 0 ]]; then 76 echo "SKIP: iproute2 too old; tc-flower is missing extended MPLS support" 77 return $ksft_skip 78 fi 79 80 tc -j filter show dev $dev ingress protocol mpls_uc | jq . &> /dev/null 81 ret=$? 82 tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \ 83 flower 84 85 if [[ $ret -ne 0 ]]; then 86 echo "SKIP: iproute2 too old; tc-flower produces invalid json output for extended MPLS filters" 87 return $ksft_skip 88 fi 89} 90 91check_tc_shblock_support() 92{ 93 tc filter help 2>&1 | grep block &> /dev/null 94 if [[ $? -ne 0 ]]; then 95 echo "SKIP: iproute2 too old; tc is missing shared block support" 96 exit $ksft_skip 97 fi 98} 99 100check_tc_chain_support() 101{ 102 tc help 2>&1|grep chain &> /dev/null 103 if [[ $? -ne 0 ]]; then 104 echo "SKIP: iproute2 too old; tc is missing chain support" 105 exit $ksft_skip 106 fi 107} 108 109check_tc_action_hw_stats_support() 110{ 111 tc actions help 2>&1 | grep -q hw_stats 112 if [[ $? -ne 0 ]]; then 113 echo "SKIP: iproute2 too old; tc is missing action hw_stats support" 114 exit $ksft_skip 115 fi 116} 117 118check_ethtool_lanes_support() 119{ 120 ethtool --help 2>&1| grep lanes &> /dev/null 121 if [[ $? -ne 0 ]]; then 122 echo "SKIP: ethtool too old; it is missing lanes support" 123 exit $ksft_skip 124 fi 125} 126 127if [[ "$(id -u)" -ne 0 ]]; then 128 echo "SKIP: need root privileges" 129 exit $ksft_skip 130fi 131 132if [[ "$CHECK_TC" = "yes" ]]; then 133 check_tc_version 134fi 135 136require_command() 137{ 138 local cmd=$1; shift 139 140 if [[ ! -x "$(command -v "$cmd")" ]]; then 141 echo "SKIP: $cmd not installed" 142 exit $ksft_skip 143 fi 144} 145 146if [[ "$REQUIRE_JQ" = "yes" ]]; then 147 require_command jq 148fi 149if [[ "$REQUIRE_MZ" = "yes" ]]; then 150 require_command $MZ 151fi 152 153if [[ ! -v NUM_NETIFS ]]; then 154 echo "SKIP: importer does not define \"NUM_NETIFS\"" 155 exit $ksft_skip 156fi 157 158############################################################################## 159# Command line options handling 160 161count=0 162 163while [[ $# -gt 0 ]]; do 164 if [[ "$count" -eq "0" ]]; then 165 unset NETIFS 166 declare -A NETIFS 167 fi 168 count=$((count + 1)) 169 NETIFS[p$count]="$1" 170 shift 171done 172 173############################################################################## 174# Network interfaces configuration 175 176create_netif_veth() 177{ 178 local i 179 180 for ((i = 1; i <= NUM_NETIFS; ++i)); do 181 local j=$((i+1)) 182 183 ip link show dev ${NETIFS[p$i]} &> /dev/null 184 if [[ $? -ne 0 ]]; then 185 ip link add ${NETIFS[p$i]} type veth \ 186 peer name ${NETIFS[p$j]} 187 if [[ $? -ne 0 ]]; then 188 echo "Failed to create netif" 189 exit 1 190 fi 191 fi 192 i=$j 193 done 194} 195 196create_netif() 197{ 198 case "$NETIF_TYPE" in 199 veth) create_netif_veth 200 ;; 201 *) echo "Can not create interfaces of type \'$NETIF_TYPE\'" 202 exit 1 203 ;; 204 esac 205} 206 207if [[ "$NETIF_CREATE" = "yes" ]]; then 208 create_netif 209fi 210 211for ((i = 1; i <= NUM_NETIFS; ++i)); do 212 ip link show dev ${NETIFS[p$i]} &> /dev/null 213 if [[ $? -ne 0 ]]; then 214 echo "SKIP: could not find all required interfaces" 215 exit $ksft_skip 216 fi 217done 218 219############################################################################## 220# Helpers 221 222# Exit status to return at the end. Set in case one of the tests fails. 223EXIT_STATUS=0 224# Per-test return value. Clear at the beginning of each test. 225RET=0 226 227check_err() 228{ 229 local err=$1 230 local msg=$2 231 232 if [[ $RET -eq 0 && $err -ne 0 ]]; then 233 RET=$err 234 retmsg=$msg 235 fi 236} 237 238check_fail() 239{ 240 local err=$1 241 local msg=$2 242 243 if [[ $RET -eq 0 && $err -eq 0 ]]; then 244 RET=1 245 retmsg=$msg 246 fi 247} 248 249check_err_fail() 250{ 251 local should_fail=$1; shift 252 local err=$1; shift 253 local what=$1; shift 254 255 if ((should_fail)); then 256 check_fail $err "$what succeeded, but should have failed" 257 else 258 check_err $err "$what failed" 259 fi 260} 261 262log_test() 263{ 264 local test_name=$1 265 local opt_str=$2 266 267 if [[ $# -eq 2 ]]; then 268 opt_str="($opt_str)" 269 fi 270 271 if [[ $RET -ne 0 ]]; then 272 EXIT_STATUS=1 273 printf "TEST: %-60s [FAIL]\n" "$test_name $opt_str" 274 if [[ ! -z "$retmsg" ]]; then 275 printf "\t%s\n" "$retmsg" 276 fi 277 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then 278 echo "Hit enter to continue, 'q' to quit" 279 read a 280 [ "$a" = "q" ] && exit 1 281 fi 282 return 1 283 fi 284 285 printf "TEST: %-60s [ OK ]\n" "$test_name $opt_str" 286 return 0 287} 288 289log_info() 290{ 291 local msg=$1 292 293 echo "INFO: $msg" 294} 295 296busywait() 297{ 298 local timeout=$1; shift 299 300 local start_time="$(date -u +%s%3N)" 301 while true 302 do 303 local out 304 out=$("$@") 305 local ret=$? 306 if ((!ret)); then 307 echo -n "$out" 308 return 0 309 fi 310 311 local current_time="$(date -u +%s%3N)" 312 if ((current_time - start_time > timeout)); then 313 echo -n "$out" 314 return 1 315 fi 316 done 317} 318 319not() 320{ 321 "$@" 322 [[ $? != 0 ]] 323} 324 325get_max() 326{ 327 local arr=("$@") 328 329 max=${arr[0]} 330 for cur in ${arr[@]}; do 331 if [[ $cur -gt $max ]]; then 332 max=$cur 333 fi 334 done 335 336 echo $max 337} 338 339grep_bridge_fdb() 340{ 341 local addr=$1; shift 342 local word 343 local flag 344 345 if [ "$1" == "self" ] || [ "$1" == "master" ]; then 346 word=$1; shift 347 if [ "$1" == "-v" ]; then 348 flag=$1; shift 349 fi 350 fi 351 352 $@ | grep $addr | grep $flag "$word" 353} 354 355wait_for_port_up() 356{ 357 "$@" | grep -q "Link detected: yes" 358} 359 360wait_for_offload() 361{ 362 "$@" | grep -q offload 363} 364 365wait_for_trap() 366{ 367 "$@" | grep -q trap 368} 369 370until_counter_is() 371{ 372 local expr=$1; shift 373 local current=$("$@") 374 375 echo $((current)) 376 ((current $expr)) 377} 378 379busywait_for_counter() 380{ 381 local timeout=$1; shift 382 local delta=$1; shift 383 384 local base=$("$@") 385 busywait "$timeout" until_counter_is ">= $((base + delta))" "$@" 386} 387 388setup_wait_dev() 389{ 390 local dev=$1; shift 391 local wait_time=${1:-$WAIT_TIME}; shift 392 393 setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time 394 395 if (($?)); then 396 check_err 1 397 log_test setup_wait_dev ": Interface $dev does not come up." 398 exit 1 399 fi 400} 401 402setup_wait_dev_with_timeout() 403{ 404 local dev=$1; shift 405 local max_iterations=${1:-$WAIT_TIMEOUT}; shift 406 local wait_time=${1:-$WAIT_TIME}; shift 407 local i 408 409 for ((i = 1; i <= $max_iterations; ++i)); do 410 ip link show dev $dev up \ 411 | grep 'state UP' &> /dev/null 412 if [[ $? -ne 0 ]]; then 413 sleep 1 414 else 415 sleep $wait_time 416 return 0 417 fi 418 done 419 420 return 1 421} 422 423setup_wait() 424{ 425 local num_netifs=${1:-$NUM_NETIFS} 426 local i 427 428 for ((i = 1; i <= num_netifs; ++i)); do 429 setup_wait_dev ${NETIFS[p$i]} 0 430 done 431 432 # Make sure links are ready. 433 sleep $WAIT_TIME 434} 435 436cmd_jq() 437{ 438 local cmd=$1 439 local jq_exp=$2 440 local jq_opts=$3 441 local ret 442 local output 443 444 output="$($cmd)" 445 # it the command fails, return error right away 446 ret=$? 447 if [[ $ret -ne 0 ]]; then 448 return $ret 449 fi 450 output=$(echo $output | jq -r $jq_opts "$jq_exp") 451 ret=$? 452 if [[ $ret -ne 0 ]]; then 453 return $ret 454 fi 455 echo $output 456 # return success only in case of non-empty output 457 [ ! -z "$output" ] 458} 459 460lldpad_app_wait_set() 461{ 462 local dev=$1; shift 463 464 while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do 465 echo "$dev: waiting for lldpad to push pending APP updates" 466 sleep 5 467 done 468} 469 470lldpad_app_wait_del() 471{ 472 # Give lldpad a chance to push down the changes. If the device is downed 473 # too soon, the updates will be left pending. However, they will have 474 # been struck off the lldpad's DB already, so we won't be able to tell 475 # they are pending. Then on next test iteration this would cause 476 # weirdness as newly-added APP rules conflict with the old ones, 477 # sometimes getting stuck in an "unknown" state. 478 sleep 5 479} 480 481pre_cleanup() 482{ 483 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then 484 echo "Pausing before cleanup, hit any key to continue" 485 read 486 fi 487} 488 489vrf_prepare() 490{ 491 ip -4 rule add pref 32765 table local 492 ip -4 rule del pref 0 493 ip -6 rule add pref 32765 table local 494 ip -6 rule del pref 0 495} 496 497vrf_cleanup() 498{ 499 ip -6 rule add pref 0 table local 500 ip -6 rule del pref 32765 501 ip -4 rule add pref 0 table local 502 ip -4 rule del pref 32765 503} 504 505__last_tb_id=0 506declare -A __TB_IDS 507 508__vrf_td_id_assign() 509{ 510 local vrf_name=$1 511 512 __last_tb_id=$((__last_tb_id + 1)) 513 __TB_IDS[$vrf_name]=$__last_tb_id 514 return $__last_tb_id 515} 516 517__vrf_td_id_lookup() 518{ 519 local vrf_name=$1 520 521 return ${__TB_IDS[$vrf_name]} 522} 523 524vrf_create() 525{ 526 local vrf_name=$1 527 local tb_id 528 529 __vrf_td_id_assign $vrf_name 530 tb_id=$? 531 532 ip link add dev $vrf_name type vrf table $tb_id 533 ip -4 route add table $tb_id unreachable default metric 4278198272 534 ip -6 route add table $tb_id unreachable default metric 4278198272 535} 536 537vrf_destroy() 538{ 539 local vrf_name=$1 540 local tb_id 541 542 __vrf_td_id_lookup $vrf_name 543 tb_id=$? 544 545 ip -6 route del table $tb_id unreachable default metric 4278198272 546 ip -4 route del table $tb_id unreachable default metric 4278198272 547 ip link del dev $vrf_name 548} 549 550__addr_add_del() 551{ 552 local if_name=$1 553 local add_del=$2 554 local array 555 556 shift 557 shift 558 array=("${@}") 559 560 for addrstr in "${array[@]}"; do 561 ip address $add_del $addrstr dev $if_name 562 done 563} 564 565__simple_if_init() 566{ 567 local if_name=$1; shift 568 local vrf_name=$1; shift 569 local addrs=("${@}") 570 571 ip link set dev $if_name master $vrf_name 572 ip link set dev $if_name up 573 574 __addr_add_del $if_name add "${addrs[@]}" 575} 576 577__simple_if_fini() 578{ 579 local if_name=$1; shift 580 local addrs=("${@}") 581 582 __addr_add_del $if_name del "${addrs[@]}" 583 584 ip link set dev $if_name down 585 ip link set dev $if_name nomaster 586} 587 588simple_if_init() 589{ 590 local if_name=$1 591 local vrf_name 592 local array 593 594 shift 595 vrf_name=v$if_name 596 array=("${@}") 597 598 vrf_create $vrf_name 599 ip link set dev $vrf_name up 600 __simple_if_init $if_name $vrf_name "${array[@]}" 601} 602 603simple_if_fini() 604{ 605 local if_name=$1 606 local vrf_name 607 local array 608 609 shift 610 vrf_name=v$if_name 611 array=("${@}") 612 613 __simple_if_fini $if_name "${array[@]}" 614 vrf_destroy $vrf_name 615} 616 617tunnel_create() 618{ 619 local name=$1; shift 620 local type=$1; shift 621 local local=$1; shift 622 local remote=$1; shift 623 624 ip link add name $name type $type \ 625 local $local remote $remote "$@" 626 ip link set dev $name up 627} 628 629tunnel_destroy() 630{ 631 local name=$1; shift 632 633 ip link del dev $name 634} 635 636vlan_create() 637{ 638 local if_name=$1; shift 639 local vid=$1; shift 640 local vrf=$1; shift 641 local ips=("${@}") 642 local name=$if_name.$vid 643 644 ip link add name $name link $if_name type vlan id $vid 645 if [ "$vrf" != "" ]; then 646 ip link set dev $name master $vrf 647 fi 648 ip link set dev $name up 649 __addr_add_del $name add "${ips[@]}" 650} 651 652vlan_destroy() 653{ 654 local if_name=$1; shift 655 local vid=$1; shift 656 local name=$if_name.$vid 657 658 ip link del dev $name 659} 660 661team_create() 662{ 663 local if_name=$1; shift 664 local mode=$1; shift 665 666 require_command $TEAMD 667 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}' 668 for slave in "$@"; do 669 ip link set dev $slave down 670 ip link set dev $slave master $if_name 671 ip link set dev $slave up 672 done 673 ip link set dev $if_name up 674} 675 676team_destroy() 677{ 678 local if_name=$1; shift 679 680 $TEAMD -t $if_name -k 681} 682 683master_name_get() 684{ 685 local if_name=$1 686 687 ip -j link show dev $if_name | jq -r '.[]["master"]' 688} 689 690link_stats_get() 691{ 692 local if_name=$1; shift 693 local dir=$1; shift 694 local stat=$1; shift 695 696 ip -j -s link show dev $if_name \ 697 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]' 698} 699 700link_stats_tx_packets_get() 701{ 702 link_stats_get $1 tx packets 703} 704 705link_stats_rx_errors_get() 706{ 707 link_stats_get $1 rx errors 708} 709 710tc_rule_stats_get() 711{ 712 local dev=$1; shift 713 local pref=$1; shift 714 local dir=$1; shift 715 local selector=${1:-.packets}; shift 716 717 tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \ 718 | jq ".[1].options.actions[].stats$selector" 719} 720 721tc_rule_handle_stats_get() 722{ 723 local id=$1; shift 724 local handle=$1; shift 725 local selector=${1:-.packets}; shift 726 727 tc -j -s filter show $id \ 728 | jq ".[] | select(.options.handle == $handle) | \ 729 .options.actions[0].stats$selector" 730} 731 732ethtool_stats_get() 733{ 734 local dev=$1; shift 735 local stat=$1; shift 736 737 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2 738} 739 740qdisc_stats_get() 741{ 742 local dev=$1; shift 743 local handle=$1; shift 744 local selector=$1; shift 745 746 tc -j -s qdisc show dev "$dev" \ 747 | jq '.[] | select(.handle == "'"$handle"'") | '"$selector" 748} 749 750qdisc_parent_stats_get() 751{ 752 local dev=$1; shift 753 local parent=$1; shift 754 local selector=$1; shift 755 756 tc -j -s qdisc show dev "$dev" invisible \ 757 | jq '.[] | select(.parent == "'"$parent"'") | '"$selector" 758} 759 760ipv6_stats_get() 761{ 762 local dev=$1; shift 763 local stat=$1; shift 764 765 cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f2 766} 767 768humanize() 769{ 770 local speed=$1; shift 771 772 for unit in bps Kbps Mbps Gbps; do 773 if (($(echo "$speed < 1024" | bc))); then 774 break 775 fi 776 777 speed=$(echo "scale=1; $speed / 1024" | bc) 778 done 779 780 echo "$speed${unit}" 781} 782 783rate() 784{ 785 local t0=$1; shift 786 local t1=$1; shift 787 local interval=$1; shift 788 789 echo $((8 * (t1 - t0) / interval)) 790} 791 792packets_rate() 793{ 794 local t0=$1; shift 795 local t1=$1; shift 796 local interval=$1; shift 797 798 echo $(((t1 - t0) / interval)) 799} 800 801mac_get() 802{ 803 local if_name=$1 804 805 ip -j link show dev $if_name | jq -r '.[]["address"]' 806} 807 808bridge_ageing_time_get() 809{ 810 local bridge=$1 811 local ageing_time 812 813 # Need to divide by 100 to convert to seconds. 814 ageing_time=$(ip -j -d link show dev $bridge \ 815 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]') 816 echo $((ageing_time / 100)) 817} 818 819declare -A SYSCTL_ORIG 820sysctl_set() 821{ 822 local key=$1; shift 823 local value=$1; shift 824 825 SYSCTL_ORIG[$key]=$(sysctl -n $key) 826 sysctl -qw $key=$value 827} 828 829sysctl_restore() 830{ 831 local key=$1; shift 832 833 sysctl -qw $key=${SYSCTL_ORIG["$key"]} 834} 835 836forwarding_enable() 837{ 838 sysctl_set net.ipv4.conf.all.forwarding 1 839 sysctl_set net.ipv6.conf.all.forwarding 1 840} 841 842forwarding_restore() 843{ 844 sysctl_restore net.ipv6.conf.all.forwarding 845 sysctl_restore net.ipv4.conf.all.forwarding 846} 847 848declare -A MTU_ORIG 849mtu_set() 850{ 851 local dev=$1; shift 852 local mtu=$1; shift 853 854 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu') 855 ip link set dev $dev mtu $mtu 856} 857 858mtu_restore() 859{ 860 local dev=$1; shift 861 862 ip link set dev $dev mtu ${MTU_ORIG["$dev"]} 863} 864 865tc_offload_check() 866{ 867 local num_netifs=${1:-$NUM_NETIFS} 868 869 for ((i = 1; i <= num_netifs; ++i)); do 870 ethtool -k ${NETIFS[p$i]} \ 871 | grep "hw-tc-offload: on" &> /dev/null 872 if [[ $? -ne 0 ]]; then 873 return 1 874 fi 875 done 876 877 return 0 878} 879 880trap_install() 881{ 882 local dev=$1; shift 883 local direction=$1; shift 884 885 # Some devices may not support or need in-hardware trapping of traffic 886 # (e.g. the veth pairs that this library creates for non-existent 887 # loopbacks). Use continue instead, so that there is a filter in there 888 # (some tests check counters), and so that other filters are still 889 # processed. 890 tc filter add dev $dev $direction pref 1 \ 891 flower skip_sw action trap 2>/dev/null \ 892 || tc filter add dev $dev $direction pref 1 \ 893 flower action continue 894} 895 896trap_uninstall() 897{ 898 local dev=$1; shift 899 local direction=$1; shift 900 901 tc filter del dev $dev $direction pref 1 flower 902} 903 904slow_path_trap_install() 905{ 906 # For slow-path testing, we need to install a trap to get to 907 # slow path the packets that would otherwise be switched in HW. 908 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 909 trap_install "$@" 910 fi 911} 912 913slow_path_trap_uninstall() 914{ 915 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 916 trap_uninstall "$@" 917 fi 918} 919 920__icmp_capture_add_del() 921{ 922 local add_del=$1; shift 923 local pref=$1; shift 924 local vsuf=$1; shift 925 local tundev=$1; shift 926 local filter=$1; shift 927 928 tc filter $add_del dev "$tundev" ingress \ 929 proto ip$vsuf pref $pref \ 930 flower ip_proto icmp$vsuf $filter \ 931 action pass 932} 933 934icmp_capture_install() 935{ 936 __icmp_capture_add_del add 100 "" "$@" 937} 938 939icmp_capture_uninstall() 940{ 941 __icmp_capture_add_del del 100 "" "$@" 942} 943 944icmp6_capture_install() 945{ 946 __icmp_capture_add_del add 100 v6 "$@" 947} 948 949icmp6_capture_uninstall() 950{ 951 __icmp_capture_add_del del 100 v6 "$@" 952} 953 954__vlan_capture_add_del() 955{ 956 local add_del=$1; shift 957 local pref=$1; shift 958 local dev=$1; shift 959 local filter=$1; shift 960 961 tc filter $add_del dev "$dev" ingress \ 962 proto 802.1q pref $pref \ 963 flower $filter \ 964 action pass 965} 966 967vlan_capture_install() 968{ 969 __vlan_capture_add_del add 100 "$@" 970} 971 972vlan_capture_uninstall() 973{ 974 __vlan_capture_add_del del 100 "$@" 975} 976 977__dscp_capture_add_del() 978{ 979 local add_del=$1; shift 980 local dev=$1; shift 981 local base=$1; shift 982 local dscp; 983 984 for prio in {0..7}; do 985 dscp=$((base + prio)) 986 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \ 987 "skip_hw ip_tos $((dscp << 2))" 988 done 989} 990 991dscp_capture_install() 992{ 993 local dev=$1; shift 994 local base=$1; shift 995 996 __dscp_capture_add_del add $dev $base 997} 998 999dscp_capture_uninstall() 1000{ 1001 local dev=$1; shift 1002 local base=$1; shift 1003 1004 __dscp_capture_add_del del $dev $base 1005} 1006 1007dscp_fetch_stats() 1008{ 1009 local dev=$1; shift 1010 local base=$1; shift 1011 1012 for prio in {0..7}; do 1013 local dscp=$((base + prio)) 1014 local t=$(tc_rule_stats_get $dev $((dscp + 100))) 1015 echo "[$dscp]=$t " 1016 done 1017} 1018 1019matchall_sink_create() 1020{ 1021 local dev=$1; shift 1022 1023 tc qdisc add dev $dev clsact 1024 tc filter add dev $dev ingress \ 1025 pref 10000 \ 1026 matchall \ 1027 action drop 1028} 1029 1030tests_run() 1031{ 1032 local current_test 1033 1034 for current_test in ${TESTS:-$ALL_TESTS}; do 1035 $current_test 1036 done 1037} 1038 1039multipath_eval() 1040{ 1041 local desc="$1" 1042 local weight_rp12=$2 1043 local weight_rp13=$3 1044 local packets_rp12=$4 1045 local packets_rp13=$5 1046 local weights_ratio packets_ratio diff 1047 1048 RET=0 1049 1050 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 1051 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ 1052 | bc -l) 1053 else 1054 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \ 1055 | bc -l) 1056 fi 1057 1058 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then 1059 check_err 1 "Packet difference is 0" 1060 log_test "Multipath" 1061 log_info "Expected ratio $weights_ratio" 1062 return 1063 fi 1064 1065 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 1066 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ 1067 | bc -l) 1068 else 1069 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \ 1070 | bc -l) 1071 fi 1072 1073 diff=$(echo $weights_ratio - $packets_ratio | bc -l) 1074 diff=${diff#-} 1075 1076 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 1077 check_err $? "Too large discrepancy between expected and measured ratios" 1078 log_test "$desc" 1079 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" 1080} 1081 1082in_ns() 1083{ 1084 local name=$1; shift 1085 1086 ip netns exec $name bash <<-EOF 1087 NUM_NETIFS=0 1088 source lib.sh 1089 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done) 1090 EOF 1091} 1092 1093############################################################################## 1094# Tests 1095 1096ping_do() 1097{ 1098 local if_name=$1 1099 local dip=$2 1100 local args=$3 1101 local vrf_name 1102 1103 vrf_name=$(master_name_get $if_name) 1104 ip vrf exec $vrf_name \ 1105 $PING $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null 1106} 1107 1108ping_test() 1109{ 1110 RET=0 1111 1112 ping_do $1 $2 1113 check_err $? 1114 log_test "ping$3" 1115} 1116 1117ping6_do() 1118{ 1119 local if_name=$1 1120 local dip=$2 1121 local args=$3 1122 local vrf_name 1123 1124 vrf_name=$(master_name_get $if_name) 1125 ip vrf exec $vrf_name \ 1126 $PING6 $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null 1127} 1128 1129ping6_test() 1130{ 1131 RET=0 1132 1133 ping6_do $1 $2 1134 check_err $? 1135 log_test "ping6$3" 1136} 1137 1138learning_test() 1139{ 1140 local bridge=$1 1141 local br_port1=$2 # Connected to `host1_if`. 1142 local host1_if=$3 1143 local host2_if=$4 1144 local mac=de:ad:be:ef:13:37 1145 local ageing_time 1146 1147 RET=0 1148 1149 bridge -j fdb show br $bridge brport $br_port1 \ 1150 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1151 check_fail $? "Found FDB record when should not" 1152 1153 # Disable unknown unicast flooding on `br_port1` to make sure 1154 # packets are only forwarded through the port after a matching 1155 # FDB entry was installed. 1156 bridge link set dev $br_port1 flood off 1157 1158 tc qdisc add dev $host1_if ingress 1159 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \ 1160 flower dst_mac $mac action drop 1161 1162 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1163 sleep 1 1164 1165 tc -j -s filter show dev $host1_if ingress \ 1166 | jq -e ".[] | select(.options.handle == 101) \ 1167 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1168 check_fail $? "Packet reached second host when should not" 1169 1170 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1171 sleep 1 1172 1173 bridge -j fdb show br $bridge brport $br_port1 \ 1174 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1175 check_err $? "Did not find FDB record when should" 1176 1177 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1178 sleep 1 1179 1180 tc -j -s filter show dev $host1_if ingress \ 1181 | jq -e ".[] | select(.options.handle == 101) \ 1182 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1183 check_err $? "Packet did not reach second host when should" 1184 1185 # Wait for 10 seconds after the ageing time to make sure FDB 1186 # record was aged-out. 1187 ageing_time=$(bridge_ageing_time_get $bridge) 1188 sleep $((ageing_time + 10)) 1189 1190 bridge -j fdb show br $bridge brport $br_port1 \ 1191 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1192 check_fail $? "Found FDB record when should not" 1193 1194 bridge link set dev $br_port1 learning off 1195 1196 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1197 sleep 1 1198 1199 bridge -j fdb show br $bridge brport $br_port1 \ 1200 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1201 check_fail $? "Found FDB record when should not" 1202 1203 bridge link set dev $br_port1 learning on 1204 1205 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower 1206 tc qdisc del dev $host1_if ingress 1207 1208 bridge link set dev $br_port1 flood on 1209 1210 log_test "FDB learning" 1211} 1212 1213flood_test_do() 1214{ 1215 local should_flood=$1 1216 local mac=$2 1217 local ip=$3 1218 local host1_if=$4 1219 local host2_if=$5 1220 local err=0 1221 1222 # Add an ACL on `host2_if` which will tell us whether the packet 1223 # was flooded to it or not. 1224 tc qdisc add dev $host2_if ingress 1225 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \ 1226 flower dst_mac $mac action drop 1227 1228 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q 1229 sleep 1 1230 1231 tc -j -s filter show dev $host2_if ingress \ 1232 | jq -e ".[] | select(.options.handle == 101) \ 1233 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1234 if [[ $? -ne 0 && $should_flood == "true" || \ 1235 $? -eq 0 && $should_flood == "false" ]]; then 1236 err=1 1237 fi 1238 1239 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower 1240 tc qdisc del dev $host2_if ingress 1241 1242 return $err 1243} 1244 1245flood_unicast_test() 1246{ 1247 local br_port=$1 1248 local host1_if=$2 1249 local host2_if=$3 1250 local mac=de:ad:be:ef:13:37 1251 local ip=192.0.2.100 1252 1253 RET=0 1254 1255 bridge link set dev $br_port flood off 1256 1257 flood_test_do false $mac $ip $host1_if $host2_if 1258 check_err $? "Packet flooded when should not" 1259 1260 bridge link set dev $br_port flood on 1261 1262 flood_test_do true $mac $ip $host1_if $host2_if 1263 check_err $? "Packet was not flooded when should" 1264 1265 log_test "Unknown unicast flood" 1266} 1267 1268flood_multicast_test() 1269{ 1270 local br_port=$1 1271 local host1_if=$2 1272 local host2_if=$3 1273 local mac=01:00:5e:00:00:01 1274 local ip=239.0.0.1 1275 1276 RET=0 1277 1278 bridge link set dev $br_port mcast_flood off 1279 1280 flood_test_do false $mac $ip $host1_if $host2_if 1281 check_err $? "Packet flooded when should not" 1282 1283 bridge link set dev $br_port mcast_flood on 1284 1285 flood_test_do true $mac $ip $host1_if $host2_if 1286 check_err $? "Packet was not flooded when should" 1287 1288 log_test "Unregistered multicast flood" 1289} 1290 1291flood_test() 1292{ 1293 # `br_port` is connected to `host2_if` 1294 local br_port=$1 1295 local host1_if=$2 1296 local host2_if=$3 1297 1298 flood_unicast_test $br_port $host1_if $host2_if 1299 flood_multicast_test $br_port $host1_if $host2_if 1300} 1301 1302__start_traffic() 1303{ 1304 local proto=$1; shift 1305 local h_in=$1; shift # Where the traffic egresses the host 1306 local sip=$1; shift 1307 local dip=$1; shift 1308 local dmac=$1; shift 1309 1310 $MZ $h_in -p 8000 -A $sip -B $dip -c 0 \ 1311 -a own -b $dmac -t "$proto" -q "$@" & 1312 sleep 1 1313} 1314 1315start_traffic() 1316{ 1317 __start_traffic udp "$@" 1318} 1319 1320start_tcp_traffic() 1321{ 1322 __start_traffic tcp "$@" 1323} 1324 1325stop_traffic() 1326{ 1327 # Suppress noise from killing mausezahn. 1328 { kill %% && wait %%; } 2>/dev/null 1329} 1330 1331tcpdump_start() 1332{ 1333 local if_name=$1; shift 1334 local ns=$1; shift 1335 1336 capfile=$(mktemp) 1337 capout=$(mktemp) 1338 1339 if [ -z $ns ]; then 1340 ns_cmd="" 1341 else 1342 ns_cmd="ip netns exec ${ns}" 1343 fi 1344 1345 if [ -z $SUDO_USER ] ; then 1346 capuser="" 1347 else 1348 capuser="-Z $SUDO_USER" 1349 fi 1350 1351 $ns_cmd tcpdump -e -n -Q in -i $if_name \ 1352 -s 65535 -B 32768 $capuser -w $capfile > "$capout" 2>&1 & 1353 cappid=$! 1354 1355 sleep 1 1356} 1357 1358tcpdump_stop() 1359{ 1360 $ns_cmd kill $cappid 1361 sleep 1 1362} 1363 1364tcpdump_cleanup() 1365{ 1366 rm $capfile $capout 1367} 1368 1369tcpdump_show() 1370{ 1371 tcpdump -e -n -r $capfile 2>&1 1372} 1373 1374# return 0 if the packet wasn't seen on host2_if or 1 if it was 1375mcast_packet_test() 1376{ 1377 local mac=$1 1378 local src_ip=$2 1379 local ip=$3 1380 local host1_if=$4 1381 local host2_if=$5 1382 local seen=0 1383 local tc_proto="ip" 1384 local mz_v6arg="" 1385 1386 # basic check to see if we were passed an IPv4 address, if not assume IPv6 1387 if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then 1388 tc_proto="ipv6" 1389 mz_v6arg="-6" 1390 fi 1391 1392 # Add an ACL on `host2_if` which will tell us whether the packet 1393 # was received by it or not. 1394 tc qdisc add dev $host2_if ingress 1395 tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \ 1396 flower ip_proto udp dst_mac $mac action drop 1397 1398 $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q 1399 sleep 1 1400 1401 tc -j -s filter show dev $host2_if ingress \ 1402 | jq -e ".[] | select(.options.handle == 101) \ 1403 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1404 if [[ $? -eq 0 ]]; then 1405 seen=1 1406 fi 1407 1408 tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower 1409 tc qdisc del dev $host2_if ingress 1410 1411 return $seen 1412} 1413 1414brmcast_check_sg_entries() 1415{ 1416 local report=$1; shift 1417 local slist=("$@") 1418 local sarg="" 1419 1420 for src in "${slist[@]}"; do 1421 sarg="${sarg} and .source_list[].address == \"$src\"" 1422 done 1423 bridge -j -d -s mdb show dev br0 \ 1424 | jq -e ".[].mdb[] | \ 1425 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null 1426 check_err $? "Wrong *,G entry source list after $report report" 1427 1428 for sgent in "${slist[@]}"; do 1429 bridge -j -d -s mdb show dev br0 \ 1430 | jq -e ".[].mdb[] | \ 1431 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null 1432 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)" 1433 done 1434} 1435 1436brmcast_check_sg_fwding() 1437{ 1438 local should_fwd=$1; shift 1439 local sources=("$@") 1440 1441 for src in "${sources[@]}"; do 1442 local retval=0 1443 1444 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1 1445 retval=$? 1446 if [ $should_fwd -eq 1 ]; then 1447 check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)" 1448 else 1449 check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)" 1450 fi 1451 done 1452} 1453 1454brmcast_check_sg_state() 1455{ 1456 local is_blocked=$1; shift 1457 local sources=("$@") 1458 local should_fail=1 1459 1460 if [ $is_blocked -eq 1 ]; then 1461 should_fail=0 1462 fi 1463 1464 for src in "${sources[@]}"; do 1465 bridge -j -d -s mdb show dev br0 \ 1466 | jq -e ".[].mdb[] | \ 1467 select(.grp == \"$TEST_GROUP\" and .source_list != null) | 1468 .source_list[] | 1469 select(.address == \"$src\") | 1470 select(.timer == \"0.00\")" &>/dev/null 1471 check_err_fail $should_fail $? "Entry $src has zero timer" 1472 1473 bridge -j -d -s mdb show dev br0 \ 1474 | jq -e ".[].mdb[] | \ 1475 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \ 1476 .flags[] == \"blocked\")" &>/dev/null 1477 check_err_fail $should_fail $? "Entry $src has blocked flag" 1478 done 1479} 1480