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