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 MC_CLI
=${MC_CLI:=smcroutectl}
20 PING_TIMEOUT
=${PING_TIMEOUT:=5}
22 relative_path
="${BASH_SOURCE%/*}"
23 if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then
27 if [[ -f $relative_path/forwarding.config
]]; then
28 source "$relative_path/forwarding.config"
31 ##############################################################################
37 if [[ $?
-ne 0 ]]; then
38 echo "SKIP: iproute2 too old; tc is missing JSON support"
43 check_tc_shblock_support
()
45 tc filter
help 2>&1 |
grep block
&> /dev
/null
46 if [[ $?
-ne 0 ]]; then
47 echo "SKIP: iproute2 too old; tc is missing shared block support"
52 check_tc_chain_support
()
54 tc
help 2>&1|
grep chain
&> /dev
/null
55 if [[ $?
-ne 0 ]]; then
56 echo "SKIP: iproute2 too old; tc is missing chain support"
61 if [[ "$(id -u)" -ne 0 ]]; then
62 echo "SKIP: need root privileges"
66 if [[ "$CHECK_TC" = "yes" ]]; then
74 if [[ ! -x "$(command -v "$cmd")" ]]; then
75 echo "SKIP: $cmd not installed"
83 if [[ ! -v NUM_NETIFS
]]; then
84 echo "SKIP: importer does not define \"NUM_NETIFS\""
88 ##############################################################################
89 # Command line options handling
93 while [[ $# -gt 0 ]]; do
94 if [[ "$count" -eq "0" ]]; then
103 ##############################################################################
104 # Network interfaces configuration
110 for ((i
= 1; i
<= NUM_NETIFS
; ++i
)); do
113 ip link show dev
${NETIFS[p$i]} &> /dev
/null
114 if [[ $?
-ne 0 ]]; then
115 ip link add
${NETIFS[p$i]} type veth \
116 peer name
${NETIFS[p$j]}
117 if [[ $?
-ne 0 ]]; then
118 echo "Failed to create netif"
128 case "$NETIF_TYPE" in
129 veth
) create_netif_veth
131 *) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
137 if [[ "$NETIF_CREATE" = "yes" ]]; then
141 for ((i
= 1; i
<= NUM_NETIFS
; ++i
)); do
142 ip link show dev
${NETIFS[p$i]} &> /dev
/null
143 if [[ $?
-ne 0 ]]; then
144 echo "SKIP: could not find all required interfaces"
149 ##############################################################################
152 # Exit status to return at the end. Set in case one of the tests fails.
154 # Per-test return value. Clear at the beginning of each test.
162 if [[ $RET -eq 0 && $err -ne 0 ]]; then
173 if [[ $RET -eq 0 && $err -eq 0 ]]; then
181 local should_fail
=$1; shift
185 if ((should_fail
)); then
186 check_fail
$err "$what succeeded, but should have failed"
188 check_err
$err "$what failed"
197 if [[ $# -eq 2 ]]; then
201 if [[ $RET -ne 0 ]]; then
203 printf "TEST: %-60s [FAIL]\n" "$test_name $opt_str"
204 if [[ ! -z "$retmsg" ]]; then
205 printf "\t%s\n" "$retmsg"
207 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
208 echo "Hit enter to continue, 'q' to quit"
210 [ "$a" = "q" ] && exit 1
215 printf "TEST: %-60s [ OK ]\n" "$test_name $opt_str"
231 ip link show dev
$dev up \
232 |
grep 'state UP' &> /dev
/null
233 if [[ $?
-ne 0 ]]; then
243 local num_netifs
=${1:-$NUM_NETIFS}
245 for ((i
= 1; i
<= num_netifs
; ++i
)); do
246 setup_wait_dev
${NETIFS[p$i]}
249 # Make sure links are ready.
261 # it the command fails, return error right away
263 if [[ $ret -ne 0 ]]; then
266 output
=$
(echo $output | jq
-r "$jq_exp")
268 # return success only in case of non-empty output
272 lldpad_app_wait_set
()
276 while lldptool
-t -i $dev -V APP
-c app |
grep -Eq "pending|unknown"; do
277 echo "$dev: waiting for lldpad to push pending APP updates"
282 lldpad_app_wait_del
()
284 # Give lldpad a chance to push down the changes. If the device is downed
285 # too soon, the updates will be left pending. However, they will have
286 # been struck off the lldpad's DB already, so we won't be able to tell
287 # they are pending. Then on next test iteration this would cause
288 # weirdness as newly-added APP rules conflict with the old ones,
289 # sometimes getting stuck in an "unknown" state.
295 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
296 echo "Pausing before cleanup, hit any key to continue"
303 ip
-4 rule add pref
32765 table
local
304 ip
-4 rule del pref
0
305 ip
-6 rule add pref
32765 table
local
306 ip
-6 rule del pref
0
311 ip
-6 rule add pref
0 table
local
312 ip
-6 rule del pref
32765
313 ip
-4 rule add pref
0 table
local
314 ip
-4 rule del pref
32765
324 __last_tb_id
=$
((__last_tb_id
+ 1))
325 __TB_IDS
[$vrf_name]=$__last_tb_id
333 return ${__TB_IDS[$vrf_name]}
341 __vrf_td_id_assign
$vrf_name
344 ip link add dev
$vrf_name type vrf table
$tb_id
345 ip
-4 route add table
$tb_id unreachable default metric
4278198272
346 ip
-6 route add table
$tb_id unreachable default metric
4278198272
354 __vrf_td_id_lookup
$vrf_name
357 ip
-6 route del table
$tb_id unreachable default metric
4278198272
358 ip
-4 route del table
$tb_id unreachable default metric
4278198272
359 ip link del dev
$vrf_name
372 for addrstr
in "${array[@]}"; do
373 ip address
$add_del $addrstr dev
$if_name
379 local if_name
=$1; shift
380 local vrf_name
=$1; shift
383 ip link
set dev
$if_name master
$vrf_name
384 ip link
set dev
$if_name up
386 __addr_add_del
$if_name add
"${addrs[@]}"
391 local if_name
=$1; shift
394 __addr_add_del
$if_name del
"${addrs[@]}"
396 ip link
set dev
$if_name down
397 ip link
set dev
$if_name nomaster
411 ip link
set dev
$vrf_name up
412 __simple_if_init
$if_name $vrf_name "${array[@]}"
425 __simple_if_fini
$if_name "${array[@]}"
426 vrf_destroy
$vrf_name
433 local local=$1; shift
434 local remote
=$1; shift
436 ip link add name
$name type $type \
437 local $local remote
$remote "$@"
438 ip link
set dev
$name up
445 ip link del dev
$name
450 local if_name
=$1; shift
454 local name
=$if_name.
$vid
456 ip link add name
$name link
$if_name type vlan id
$vid
457 if [ "$vrf" != "" ]; then
458 ip link
set dev
$name master
$vrf
460 ip link
set dev
$name up
461 __addr_add_del
$name add
"${ips[@]}"
466 local if_name
=$1; shift
468 local name
=$if_name.
$vid
470 ip link del dev
$name
475 local if_name
=$1; shift
478 require_command
$TEAMD
479 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
480 for slave
in "$@"; do
481 ip link
set dev
$slave down
482 ip link
set dev
$slave master
$if_name
483 ip link
set dev
$slave up
485 ip link
set dev
$if_name up
490 local if_name
=$1; shift
492 $TEAMD -t $if_name -k
499 ip
-j link show dev
$if_name | jq
-r '.[]["master"]'
504 local if_name
=$1; shift
508 ip
-j -s link show dev
$if_name \
509 | jq
'.[]["stats64"]["'$dir'"]["'$stat'"]'
512 link_stats_tx_packets_get
()
514 link_stats_get
$1 tx packets
517 link_stats_rx_errors_get
()
519 link_stats_get
$1 rx errors
528 tc
-j -s filter show dev
$dev ${dir:-ingress} pref
$pref \
529 | jq
'.[1].options.actions[].stats.packets'
537 ethtool
-S $dev |
grep "^ *$stat:" |
head -n 1 | cut
-d: -f2
544 ip
-j link show dev
$if_name | jq
-r '.[]["address"]'
547 bridge_ageing_time_get
()
552 # Need to divide by 100 to convert to seconds.
553 ageing_time
=$
(ip
-j -d link show dev
$bridge \
554 | jq
'.[]["linkinfo"]["info_data"]["ageing_time"]')
555 echo $
((ageing_time
/ 100))
558 declare -A SYSCTL_ORIG
562 local value
=$1; shift
564 SYSCTL_ORIG
[$key]=$
(sysctl
-n $key)
565 sysctl
-qw $key=$value
572 sysctl
-qw $key=${SYSCTL_ORIG["$key"]}
577 sysctl_set net.ipv4.conf.all.forwarding
1
578 sysctl_set net.ipv6.conf.all.forwarding
1
583 sysctl_restore net.ipv6.conf.all.forwarding
584 sysctl_restore net.ipv4.conf.all.forwarding
593 MTU_ORIG
["$dev"]=$
(ip
-j link show dev
$dev | jq
-e '.[].mtu')
594 ip link
set dev
$dev mtu
$mtu
601 ip link
set dev
$dev mtu
${MTU_ORIG["$dev"]}
606 local num_netifs
=${1:-$NUM_NETIFS}
608 for ((i
= 1; i
<= num_netifs
; ++i
)); do
609 ethtool
-k ${NETIFS[p$i]} \
610 |
grep "hw-tc-offload: on" &> /dev
/null
611 if [[ $?
-ne 0 ]]; then
622 local direction
=$1; shift
624 # Some devices may not support or need in-hardware trapping of traffic
625 # (e.g. the veth pairs that this library creates for non-existent
626 # loopbacks). Use continue instead, so that there is a filter in there
627 # (some tests check counters), and so that other filters are still
629 tc filter add dev
$dev $direction pref
1 \
630 flower skip_sw action
trap 2>/dev
/null \
631 || tc filter add dev
$dev $direction pref
1 \
632 flower action
continue
638 local direction
=$1; shift
640 tc filter del dev
$dev $direction pref
1 flower
643 slow_path_trap_install
()
645 # For slow-path testing, we need to install a trap to get to
646 # slow path the packets that would otherwise be switched in HW.
647 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
652 slow_path_trap_uninstall
()
654 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
659 __icmp_capture_add_del
()
661 local add_del
=$1; shift
664 local tundev
=$1; shift
665 local filter
=$1; shift
667 tc filter
$add_del dev
"$tundev" ingress \
668 proto ip
$vsuf pref
$pref \
669 flower ip_proto icmp
$vsuf $filter \
673 icmp_capture_install
()
675 __icmp_capture_add_del add
100 "" "$@"
678 icmp_capture_uninstall
()
680 __icmp_capture_add_del del
100 "" "$@"
683 icmp6_capture_install
()
685 __icmp_capture_add_del add
100 v6
"$@"
688 icmp6_capture_uninstall
()
690 __icmp_capture_add_del del
100 v6
"$@"
693 __vlan_capture_add_del
()
695 local add_del
=$1; shift
698 local filter
=$1; shift
700 tc filter
$add_del dev
"$dev" ingress \
701 proto
802.1q pref
$pref \
706 vlan_capture_install
()
708 __vlan_capture_add_del add
100 "$@"
711 vlan_capture_uninstall
()
713 __vlan_capture_add_del del
100 "$@"
716 __dscp_capture_add_del
()
718 local add_del
=$1; shift
723 for prio
in {0.
.7}; do
724 dscp
=$
((base
+ prio
))
725 __icmp_capture_add_del
$add_del $
((dscp
+ 100)) "" $dev \
726 "skip_hw ip_tos $((dscp << 2))"
730 dscp_capture_install
()
735 __dscp_capture_add_del add
$dev $base
738 dscp_capture_uninstall
()
743 __dscp_capture_add_del del
$dev $base
751 for prio
in {0.
.7}; do
752 local dscp
=$
((base
+ prio
))
753 local t
=$
(tc_rule_stats_get
$dev $
((dscp
+ 100)))
758 matchall_sink_create
()
762 tc qdisc add dev
$dev clsact
763 tc filter add dev
$dev ingress \
773 for current_test
in ${TESTS:-$ALL_TESTS}; do
783 local packets_rp12
=$4
784 local packets_rp13
=$5
785 local weights_ratio packets_ratio
diff
789 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
790 weights_ratio
=$
(echo "scale=2; $weight_rp12 / $weight_rp13" \
793 weights_ratio
=$
(echo "scale=2; $weight_rp13 / $weight_rp12" \
797 if [[ "$packets_rp12" -eq "0" ||
"$packets_rp13" -eq "0" ]]; then
798 check_err
1 "Packet difference is 0"
800 log_info
"Expected ratio $weights_ratio"
804 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
805 packets_ratio
=$
(echo "scale=2; $packets_rp12 / $packets_rp13" \
808 packets_ratio
=$
(echo "scale=2; $packets_rp13 / $packets_rp12" \
812 diff=$
(echo $weights_ratio - $packets_ratio |
bc -l)
815 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
816 check_err $?
"Too large discrepancy between expected and measured ratios"
818 log_info
"Expected ratio $weights_ratio Measured ratio $packets_ratio"
825 ip netns
exec $name bash
<<-EOF
828 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
832 ##############################################################################
842 vrf_name
=$
(master_name_get
$if_name)
843 ip vrf
exec $vrf_name \
844 $PING $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev
/null
863 vrf_name
=$
(master_name_get
$if_name)
864 ip vrf
exec $vrf_name \
865 $PING6 $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev
/null
880 local br_port1
=$2 # Connected to `host1_if`.
883 local mac
=de
:ad
:be
:ef
:13:37
888 bridge
-j fdb show br
$bridge brport
$br_port1 \
889 | jq
-e ".[] | select(.mac == \"$mac\")" &> /dev
/null
890 check_fail $?
"Found FDB record when should not"
892 # Disable unknown unicast flooding on `br_port1` to make sure
893 # packets are only forwarded through the port after a matching
894 # FDB entry was installed.
895 bridge link
set dev
$br_port1 flood off
897 tc qdisc add dev
$host1_if ingress
898 tc filter add dev
$host1_if ingress protocol ip pref
1 handle
101 \
899 flower dst_mac
$mac action drop
901 $MZ $host2_if -c 1 -p 64 -b $mac -t ip
-q
904 tc
-j -s filter show dev
$host1_if ingress \
905 | jq
-e ".[] | select(.options.handle == 101) \
906 | select(.options.actions[0].stats.packets == 1)" &> /dev
/null
907 check_fail $?
"Packet reached second host when should not"
909 $MZ $host1_if -c 1 -p 64 -a $mac -t ip
-q
912 bridge
-j fdb show br
$bridge brport
$br_port1 \
913 | jq
-e ".[] | select(.mac == \"$mac\")" &> /dev
/null
914 check_err $?
"Did not find FDB record when should"
916 $MZ $host2_if -c 1 -p 64 -b $mac -t ip
-q
919 tc
-j -s filter show dev
$host1_if ingress \
920 | jq
-e ".[] | select(.options.handle == 101) \
921 | select(.options.actions[0].stats.packets == 1)" &> /dev
/null
922 check_err $?
"Packet did not reach second host when should"
924 # Wait for 10 seconds after the ageing time to make sure FDB
925 # record was aged-out.
926 ageing_time
=$
(bridge_ageing_time_get
$bridge)
927 sleep $
((ageing_time
+ 10))
929 bridge
-j fdb show br
$bridge brport
$br_port1 \
930 | jq
-e ".[] | select(.mac == \"$mac\")" &> /dev
/null
931 check_fail $?
"Found FDB record when should not"
933 bridge link
set dev
$br_port1 learning off
935 $MZ $host1_if -c 1 -p 64 -a $mac -t ip
-q
938 bridge
-j fdb show br
$bridge brport
$br_port1 \
939 | jq
-e ".[] | select(.mac == \"$mac\")" &> /dev
/null
940 check_fail $?
"Found FDB record when should not"
942 bridge link
set dev
$br_port1 learning on
944 tc filter del dev
$host1_if ingress protocol ip pref
1 handle
101 flower
945 tc qdisc del dev
$host1_if ingress
947 bridge link
set dev
$br_port1 flood on
949 log_test
"FDB learning"
954 local should_flood
=$1
961 # Add an ACL on `host2_if` which will tell us whether the packet
962 # was flooded to it or not.
963 tc qdisc add dev
$host2_if ingress
964 tc filter add dev
$host2_if ingress protocol ip pref
1 handle
101 \
965 flower dst_mac
$mac action drop
967 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip
-q
970 tc
-j -s filter show dev
$host2_if ingress \
971 | jq
-e ".[] | select(.options.handle == 101) \
972 | select(.options.actions[0].stats.packets == 1)" &> /dev
/null
973 if [[ $?
-ne 0 && $should_flood == "true" || \
974 $?
-eq 0 && $should_flood == "false" ]]; then
978 tc filter del dev
$host2_if ingress protocol ip pref
1 handle
101 flower
979 tc qdisc del dev
$host2_if ingress
989 local mac
=de
:ad
:be
:ef
:13:37
994 bridge link
set dev
$br_port flood off
996 flood_test_do false
$mac $ip $host1_if $host2_if
997 check_err $?
"Packet flooded when should not"
999 bridge link
set dev
$br_port flood on
1001 flood_test_do true
$mac $ip $host1_if $host2_if
1002 check_err $?
"Packet was not flooded when should"
1004 log_test
"Unknown unicast flood"
1007 flood_multicast_test
()
1012 local mac
=01:00:5e
:00:00:01
1017 bridge link
set dev
$br_port mcast_flood off
1019 flood_test_do false
$mac $ip $host1_if $host2_if
1020 check_err $?
"Packet flooded when should not"
1022 bridge link
set dev
$br_port mcast_flood on
1024 flood_test_do true
$mac $ip $host1_if $host2_if
1025 check_err $?
"Packet was not flooded when should"
1027 log_test
"Unregistered multicast flood"
1032 # `br_port` is connected to `host2_if`
1037 flood_unicast_test
$br_port $host1_if $host2_if
1038 flood_multicast_test
$br_port $host1_if $host2_if