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