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