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