2 # SPDX-License-Identifier: GPL-2.0
4 ##############################################################################
7 # Can be overridden by the configuration file.
11 ARPING=${ARPING:=arping}
13 WAIT_TIME=${WAIT_TIME:=5}
14 PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
15 PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no}
16 NETIF_TYPE=${NETIF_TYPE:=veth}
17 NETIF_CREATE=${NETIF_CREATE:=yes}
19 relative_path="${BASH_SOURCE%/*}"
20 if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then
24 if [[ -f $relative_path/forwarding.config ]]; then
25 source "$relative_path/forwarding.config"
28 ##############################################################################
34 if [[ $? -ne 0 ]]; then
35 echo "SKIP: iproute2 too old; tc is missing JSON support"
40 check_tc_shblock_support()
42 tc filter help 2>&1 | grep block &> /dev/null
43 if [[ $? -ne 0 ]]; then
44 echo "SKIP: iproute2 too old; tc is missing shared block support"
49 check_tc_chain_support()
51 tc help 2>&1|grep chain &> /dev/null
52 if [[ $? -ne 0 ]]; then
53 echo "SKIP: iproute2 too old; tc is missing chain support"
58 if [[ "$(id -u)" -ne 0 ]]; then
59 echo "SKIP: need root privileges"
63 if [[ "$CHECK_TC" = "yes" ]]; then
71 if [[ ! -x "$(command -v "$cmd")" ]]; then
72 echo "SKIP: $cmd not installed"
80 if [[ ! -v NUM_NETIFS ]]; then
81 echo "SKIP: importer does not define \"NUM_NETIFS\""
85 ##############################################################################
86 # Command line options handling
90 while [[ $# -gt 0 ]]; do
91 if [[ "$count" -eq "0" ]]; then
100 ##############################################################################
101 # Network interfaces configuration
107 for i in $(eval echo {1..$NUM_NETIFS}); do
110 ip link show dev ${NETIFS[p$i]} &> /dev/null
111 if [[ $? -ne 0 ]]; then
112 ip link add ${NETIFS[p$i]} type veth \
113 peer name ${NETIFS[p$j]}
114 if [[ $? -ne 0 ]]; then
115 echo "Failed to create netif"
125 case "$NETIF_TYPE" in
126 veth) create_netif_veth
128 *) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
134 if [[ "$NETIF_CREATE" = "yes" ]]; then
138 for i in $(eval echo {1..$NUM_NETIFS}); do
139 ip link show dev ${NETIFS[p$i]} &> /dev/null
140 if [[ $? -ne 0 ]]; then
141 echo "SKIP: could not find all required interfaces"
146 ##############################################################################
149 # Exit status to return at the end. Set in case one of the tests fails.
151 # Per-test return value. Clear at the beginning of each test.
159 if [[ $RET -eq 0 && $err -ne 0 ]]; then
170 if [[ $RET -eq 0 && $err -eq 0 ]]; then
178 local should_fail=$1; shift
182 if ((should_fail)); then
183 check_fail $err "$what succeeded, but should have failed"
185 check_err $err "$what failed"
194 if [[ $# -eq 2 ]]; then
198 if [[ $RET -ne 0 ]]; then
200 printf "TEST: %-60s [FAIL]\n" "$test_name $opt_str"
201 if [[ ! -z "$retmsg" ]]; then
202 printf "\t%s\n" "$retmsg"
204 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
205 echo "Hit enter to continue, 'q' to quit"
207 [ "$a" = "q" ] && exit 1
212 printf "TEST: %-60s [PASS]\n" "$test_name $opt_str"
228 ip link show dev $dev up \
229 | grep 'state UP' &> /dev/null
230 if [[ $? -ne 0 ]]; then
240 local num_netifs=${1:-$NUM_NETIFS}
242 for ((i = 1; i <= num_netifs; ++i)); do
243 setup_wait_dev ${NETIFS[p$i]}
246 # Make sure links are ready.
252 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
253 echo "Pausing before cleanup, hit any key to continue"
260 ip -4 rule add pref 32765 table local
261 ip -4 rule del pref 0
262 ip -6 rule add pref 32765 table local
263 ip -6 rule del pref 0
268 ip -6 rule add pref 0 table local
269 ip -6 rule del pref 32765
270 ip -4 rule add pref 0 table local
271 ip -4 rule del pref 32765
281 __last_tb_id=$((__last_tb_id + 1))
282 __TB_IDS[$vrf_name]=$__last_tb_id
290 return ${__TB_IDS[$vrf_name]}
298 __vrf_td_id_assign $vrf_name
301 ip link add dev $vrf_name type vrf table $tb_id
302 ip -4 route add table $tb_id unreachable default metric 4278198272
303 ip -6 route add table $tb_id unreachable default metric 4278198272
311 __vrf_td_id_lookup $vrf_name
314 ip -6 route del table $tb_id unreachable default metric 4278198272
315 ip -4 route del table $tb_id unreachable default metric 4278198272
316 ip link del dev $vrf_name
329 for addrstr in "${array[@]}"; do
330 ip address $add_del $addrstr dev $if_name
336 local if_name=$1; shift
337 local vrf_name=$1; shift
340 ip link set dev $if_name master $vrf_name
341 ip link set dev $if_name up
343 __addr_add_del $if_name add "${addrs[@]}"
348 local if_name=$1; shift
351 __addr_add_del $if_name del "${addrs[@]}"
353 ip link set dev $if_name down
354 ip link set dev $if_name nomaster
368 ip link set dev $vrf_name up
369 __simple_if_init $if_name $vrf_name "${array[@]}"
382 __simple_if_fini $if_name "${array[@]}"
383 vrf_destroy $vrf_name
390 local local=$1; shift
391 local remote=$1; shift
393 ip link add name $name type $type \
394 local $local remote $remote "$@"
395 ip link set dev $name up
402 ip link del dev $name
407 local if_name=$1; shift
411 local name=$if_name.$vid
413 ip link add name $name link $if_name type vlan id $vid
414 if [ "$vrf" != "" ]; then
415 ip link set dev $name master $vrf
417 ip link set dev $name up
418 __addr_add_del $name add "${ips[@]}"
423 local if_name=$1; shift
425 local name=$if_name.$vid
427 ip link del dev $name
432 local if_name=$1; shift
435 require_command $TEAMD
436 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
437 for slave in "$@"; do
438 ip link set dev $slave down
439 ip link set dev $slave master $if_name
440 ip link set dev $slave up
442 ip link set dev $if_name up
447 local if_name=$1; shift
449 $TEAMD -t $if_name -k
456 ip -j link show dev $if_name | jq -r '.[]["master"]'
459 link_stats_tx_packets_get()
463 ip -j -s link show dev $if_name | jq '.[]["stats64"]["tx"]["packets"]'
472 tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \
473 | jq '.[1].options.actions[].stats.packets'
480 ip -j link show dev $if_name | jq -r '.[]["address"]'
483 bridge_ageing_time_get()
488 # Need to divide by 100 to convert to seconds.
489 ageing_time=$(ip -j -d link show dev $bridge \
490 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
491 echo $((ageing_time / 100))
494 declare -A SYSCTL_ORIG
498 local value=$1; shift
500 SYSCTL_ORIG[$key]=$(sysctl -n $key)
501 sysctl -qw $key=$value
508 sysctl -qw $key=${SYSCTL_ORIG["$key"]}
513 sysctl_set net.ipv4.conf.all.forwarding 1
514 sysctl_set net.ipv6.conf.all.forwarding 1
519 sysctl_restore net.ipv6.conf.all.forwarding
520 sysctl_restore net.ipv4.conf.all.forwarding
525 local num_netifs=${1:-$NUM_NETIFS}
527 for ((i = 1; i <= num_netifs; ++i)); do
528 ethtool -k ${NETIFS[p$i]} \
529 | grep "hw-tc-offload: on" &> /dev/null
530 if [[ $? -ne 0 ]]; then
541 local direction=$1; shift
543 # Some devices may not support or need in-hardware trapping of traffic
544 # (e.g. the veth pairs that this library creates for non-existent
545 # loopbacks). Use continue instead, so that there is a filter in there
546 # (some tests check counters), and so that other filters are still
548 tc filter add dev $dev $direction pref 1 \
549 flower skip_sw action trap 2>/dev/null \
550 || tc filter add dev $dev $direction pref 1 \
551 flower action continue
557 local direction=$1; shift
559 tc filter del dev $dev $direction pref 1 flower
562 slow_path_trap_install()
564 # For slow-path testing, we need to install a trap to get to
565 # slow path the packets that would otherwise be switched in HW.
566 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
571 slow_path_trap_uninstall()
573 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
578 __icmp_capture_add_del()
580 local add_del=$1; shift
583 local tundev=$1; shift
584 local filter=$1; shift
586 tc filter $add_del dev "$tundev" ingress \
587 proto ip$vsuf pref $pref \
588 flower ip_proto icmp$vsuf $filter \
592 icmp_capture_install()
594 __icmp_capture_add_del add 100 "" "$@"
597 icmp_capture_uninstall()
599 __icmp_capture_add_del del 100 "" "$@"
602 icmp6_capture_install()
604 __icmp_capture_add_del add 100 v6 "$@"
607 icmp6_capture_uninstall()
609 __icmp_capture_add_del del 100 v6 "$@"
612 __vlan_capture_add_del()
614 local add_del=$1; shift
617 local filter=$1; shift
619 tc filter $add_del dev "$dev" ingress \
620 proto 802.1q pref $pref \
625 vlan_capture_install()
627 __vlan_capture_add_del add 100 "$@"
630 vlan_capture_uninstall()
632 __vlan_capture_add_del del 100 "$@"
635 matchall_sink_create()
639 tc qdisc add dev $dev clsact
640 tc filter add dev $dev ingress \
650 for current_test in ${TESTS:-$ALL_TESTS}; do
660 local packets_rp12=$4
661 local packets_rp13=$5
662 local weights_ratio packets_ratio diff
666 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
667 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
670 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
674 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
675 check_err 1 "Packet difference is 0"
677 log_info "Expected ratio $weights_ratio"
681 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
682 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
685 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
689 diff=$(echo $weights_ratio - $packets_ratio | bc -l)
692 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
693 check_err $? "Too large discrepancy between expected and measured ratios"
695 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
698 ##############################################################################
707 vrf_name=$(master_name_get $if_name)
708 ip vrf exec $vrf_name $PING $dip -c 10 -i 0.1 -w 2 &> /dev/null
726 vrf_name=$(master_name_get $if_name)
727 ip vrf exec $vrf_name $PING6 $dip -c 10 -i 0.1 -w 2 &> /dev/null
742 local br_port1=$2 # Connected to `host1_if`.
745 local mac=de:ad:be:ef:13:37
750 bridge -j fdb show br $bridge brport $br_port1 \
751 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
752 check_fail $? "Found FDB record when should not"
754 # Disable unknown unicast flooding on `br_port1` to make sure
755 # packets are only forwarded through the port after a matching
756 # FDB entry was installed.
757 bridge link set dev $br_port1 flood off
759 tc qdisc add dev $host1_if ingress
760 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
761 flower dst_mac $mac action drop
763 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
766 tc -j -s filter show dev $host1_if ingress \
767 | jq -e ".[] | select(.options.handle == 101) \
768 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
769 check_fail $? "Packet reached second host when should not"
771 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
774 bridge -j fdb show br $bridge brport $br_port1 \
775 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
776 check_err $? "Did not find FDB record when should"
778 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
781 tc -j -s filter show dev $host1_if ingress \
782 | jq -e ".[] | select(.options.handle == 101) \
783 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
784 check_err $? "Packet did not reach second host when should"
786 # Wait for 10 seconds after the ageing time to make sure FDB
787 # record was aged-out.
788 ageing_time=$(bridge_ageing_time_get $bridge)
789 sleep $((ageing_time + 10))
791 bridge -j fdb show br $bridge brport $br_port1 \
792 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
793 check_fail $? "Found FDB record when should not"
795 bridge link set dev $br_port1 learning off
797 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
800 bridge -j fdb show br $bridge brport $br_port1 \
801 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
802 check_fail $? "Found FDB record when should not"
804 bridge link set dev $br_port1 learning on
806 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
807 tc qdisc del dev $host1_if ingress
809 bridge link set dev $br_port1 flood on
811 log_test "FDB learning"
816 local should_flood=$1
823 # Add an ACL on `host2_if` which will tell us whether the packet
824 # was flooded to it or not.
825 tc qdisc add dev $host2_if ingress
826 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
827 flower dst_mac $mac action drop
829 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
832 tc -j -s filter show dev $host2_if ingress \
833 | jq -e ".[] | select(.options.handle == 101) \
834 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
835 if [[ $? -ne 0 && $should_flood == "true" || \
836 $? -eq 0 && $should_flood == "false" ]]; then
840 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
841 tc qdisc del dev $host2_if ingress
851 local mac=de:ad:be:ef:13:37
856 bridge link set dev $br_port flood off
858 flood_test_do false $mac $ip $host1_if $host2_if
859 check_err $? "Packet flooded when should not"
861 bridge link set dev $br_port flood on
863 flood_test_do true $mac $ip $host1_if $host2_if
864 check_err $? "Packet was not flooded when should"
866 log_test "Unknown unicast flood"
869 flood_multicast_test()
874 local mac=01:00:5e:00:00:01
879 bridge link set dev $br_port mcast_flood off
881 flood_test_do false $mac $ip $host1_if $host2_if
882 check_err $? "Packet flooded when should not"
884 bridge link set dev $br_port mcast_flood on
886 flood_test_do true $mac $ip $host1_if $host2_if
887 check_err $? "Packet was not flooded when should"
889 log_test "Unregistered multicast flood"
894 # `br_port` is connected to `host2_if`
899 flood_unicast_test $br_port $host1_if $host2_if
900 flood_multicast_test $br_port $host1_if $host2_if