Linux 4.19.133
[linux/fpc-iii.git] / tools / testing / selftests / net / forwarding / lib.sh
blob08bac6cf1bb3ab8898b22c28ec13c89083b3f69c
1 #!/bin/bash
2 # SPDX-License-Identifier: GPL-2.0
4 ##############################################################################
5 # Defines
7 # Can be overridden by the configuration file.
8 PING=${PING:=ping}
9 PING6=${PING6:=ping6}
10 MZ=${MZ:=mausezahn}
11 ARPING=${ARPING:=arping}
12 TEAMD=${TEAMD:=teamd}
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
21 relative_path="."
24 if [[ -f $relative_path/forwarding.config ]]; then
25 source "$relative_path/forwarding.config"
28 ##############################################################################
29 # Sanity checks
31 check_tc_version()
33 tc -j &> /dev/null
34 if [[ $? -ne 0 ]]; then
35 echo "SKIP: iproute2 too old; tc is missing JSON support"
36 exit 1
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"
45 exit 1
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"
54 exit 1
58 if [[ "$(id -u)" -ne 0 ]]; then
59 echo "SKIP: need root privileges"
60 exit 0
63 if [[ "$CHECK_TC" = "yes" ]]; then
64 check_tc_version
67 require_command()
69 local cmd=$1; shift
71 if [[ ! -x "$(command -v "$cmd")" ]]; then
72 echo "SKIP: $cmd not installed"
73 exit 1
77 require_command jq
78 require_command $MZ
80 if [[ ! -v NUM_NETIFS ]]; then
81 echo "SKIP: importer does not define \"NUM_NETIFS\""
82 exit 1
85 ##############################################################################
86 # Command line options handling
88 count=0
90 while [[ $# -gt 0 ]]; do
91 if [[ "$count" -eq "0" ]]; then
92 unset NETIFS
93 declare -A NETIFS
95 count=$((count + 1))
96 NETIFS[p$count]="$1"
97 shift
98 done
100 ##############################################################################
101 # Network interfaces configuration
103 create_netif_veth()
105 local i
107 for i in $(eval echo {1..$NUM_NETIFS}); do
108 local j=$((i+1))
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"
116 exit 1
119 i=$j
120 done
123 create_netif()
125 case "$NETIF_TYPE" in
126 veth) create_netif_veth
128 *) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
129 exit 1
131 esac
134 if [[ "$NETIF_CREATE" = "yes" ]]; then
135 create_netif
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"
142 exit 1
144 done
146 ##############################################################################
147 # Helpers
149 # Exit status to return at the end. Set in case one of the tests fails.
150 EXIT_STATUS=0
151 # Per-test return value. Clear at the beginning of each test.
152 RET=0
154 check_err()
156 local err=$1
157 local msg=$2
159 if [[ $RET -eq 0 && $err -ne 0 ]]; then
160 RET=$err
161 retmsg=$msg
165 check_fail()
167 local err=$1
168 local msg=$2
170 if [[ $RET -eq 0 && $err -eq 0 ]]; then
171 RET=1
172 retmsg=$msg
176 check_err_fail()
178 local should_fail=$1; shift
179 local err=$1; shift
180 local what=$1; shift
182 if ((should_fail)); then
183 check_fail $err "$what succeeded, but should have failed"
184 else
185 check_err $err "$what failed"
189 log_test()
191 local test_name=$1
192 local opt_str=$2
194 if [[ $# -eq 2 ]]; then
195 opt_str="($opt_str)"
198 if [[ $RET -ne 0 ]]; then
199 EXIT_STATUS=1
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"
206 read a
207 [ "$a" = "q" ] && exit 1
209 return 1
212 printf "TEST: %-60s [PASS]\n" "$test_name $opt_str"
213 return 0
216 log_info()
218 local msg=$1
220 echo "INFO: $msg"
223 setup_wait_dev()
225 local dev=$1; shift
227 while true; do
228 ip link show dev $dev up \
229 | grep 'state UP' &> /dev/null
230 if [[ $? -ne 0 ]]; then
231 sleep 1
232 else
233 break
235 done
238 setup_wait()
240 local num_netifs=${1:-$NUM_NETIFS}
242 for ((i = 1; i <= num_netifs; ++i)); do
243 setup_wait_dev ${NETIFS[p$i]}
244 done
246 # Make sure links are ready.
247 sleep $WAIT_TIME
250 lldpad_app_wait_set()
252 local dev=$1; shift
254 while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do
255 echo "$dev: waiting for lldpad to push pending APP updates"
256 sleep 5
257 done
260 lldpad_app_wait_del()
262 # Give lldpad a chance to push down the changes. If the device is downed
263 # too soon, the updates will be left pending. However, they will have
264 # been struck off the lldpad's DB already, so we won't be able to tell
265 # they are pending. Then on next test iteration this would cause
266 # weirdness as newly-added APP rules conflict with the old ones,
267 # sometimes getting stuck in an "unknown" state.
268 sleep 5
271 pre_cleanup()
273 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
274 echo "Pausing before cleanup, hit any key to continue"
275 read
279 vrf_prepare()
281 ip -4 rule add pref 32765 table local
282 ip -4 rule del pref 0
283 ip -6 rule add pref 32765 table local
284 ip -6 rule del pref 0
287 vrf_cleanup()
289 ip -6 rule add pref 0 table local
290 ip -6 rule del pref 32765
291 ip -4 rule add pref 0 table local
292 ip -4 rule del pref 32765
295 __last_tb_id=0
296 declare -A __TB_IDS
298 __vrf_td_id_assign()
300 local vrf_name=$1
302 __last_tb_id=$((__last_tb_id + 1))
303 __TB_IDS[$vrf_name]=$__last_tb_id
304 return $__last_tb_id
307 __vrf_td_id_lookup()
309 local vrf_name=$1
311 return ${__TB_IDS[$vrf_name]}
314 vrf_create()
316 local vrf_name=$1
317 local tb_id
319 __vrf_td_id_assign $vrf_name
320 tb_id=$?
322 ip link add dev $vrf_name type vrf table $tb_id
323 ip -4 route add table $tb_id unreachable default metric 4278198272
324 ip -6 route add table $tb_id unreachable default metric 4278198272
327 vrf_destroy()
329 local vrf_name=$1
330 local tb_id
332 __vrf_td_id_lookup $vrf_name
333 tb_id=$?
335 ip -6 route del table $tb_id unreachable default metric 4278198272
336 ip -4 route del table $tb_id unreachable default metric 4278198272
337 ip link del dev $vrf_name
340 __addr_add_del()
342 local if_name=$1
343 local add_del=$2
344 local array
346 shift
347 shift
348 array=("${@}")
350 for addrstr in "${array[@]}"; do
351 ip address $add_del $addrstr dev $if_name
352 done
355 __simple_if_init()
357 local if_name=$1; shift
358 local vrf_name=$1; shift
359 local addrs=("${@}")
361 ip link set dev $if_name master $vrf_name
362 ip link set dev $if_name up
364 __addr_add_del $if_name add "${addrs[@]}"
367 __simple_if_fini()
369 local if_name=$1; shift
370 local addrs=("${@}")
372 __addr_add_del $if_name del "${addrs[@]}"
374 ip link set dev $if_name down
375 ip link set dev $if_name nomaster
378 simple_if_init()
380 local if_name=$1
381 local vrf_name
382 local array
384 shift
385 vrf_name=v$if_name
386 array=("${@}")
388 vrf_create $vrf_name
389 ip link set dev $vrf_name up
390 __simple_if_init $if_name $vrf_name "${array[@]}"
393 simple_if_fini()
395 local if_name=$1
396 local vrf_name
397 local array
399 shift
400 vrf_name=v$if_name
401 array=("${@}")
403 __simple_if_fini $if_name "${array[@]}"
404 vrf_destroy $vrf_name
407 tunnel_create()
409 local name=$1; shift
410 local type=$1; shift
411 local local=$1; shift
412 local remote=$1; shift
414 ip link add name $name type $type \
415 local $local remote $remote "$@"
416 ip link set dev $name up
419 tunnel_destroy()
421 local name=$1; shift
423 ip link del dev $name
426 vlan_create()
428 local if_name=$1; shift
429 local vid=$1; shift
430 local vrf=$1; shift
431 local ips=("${@}")
432 local name=$if_name.$vid
434 ip link add name $name link $if_name type vlan id $vid
435 if [ "$vrf" != "" ]; then
436 ip link set dev $name master $vrf
438 ip link set dev $name up
439 __addr_add_del $name add "${ips[@]}"
442 vlan_destroy()
444 local if_name=$1; shift
445 local vid=$1; shift
446 local name=$if_name.$vid
448 ip link del dev $name
451 team_create()
453 local if_name=$1; shift
454 local mode=$1; shift
456 require_command $TEAMD
457 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
458 for slave in "$@"; do
459 ip link set dev $slave down
460 ip link set dev $slave master $if_name
461 ip link set dev $slave up
462 done
463 ip link set dev $if_name up
466 team_destroy()
468 local if_name=$1; shift
470 $TEAMD -t $if_name -k
473 master_name_get()
475 local if_name=$1
477 ip -j link show dev $if_name | jq -r '.[]["master"]'
480 link_stats_tx_packets_get()
482 local if_name=$1
484 ip -j -s link show dev $if_name | jq '.[]["stats64"]["tx"]["packets"]'
487 tc_rule_stats_get()
489 local dev=$1; shift
490 local pref=$1; shift
491 local dir=$1; shift
493 tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \
494 | jq '.[1].options.actions[].stats.packets'
497 mac_get()
499 local if_name=$1
501 ip -j link show dev $if_name | jq -r '.[]["address"]'
504 bridge_ageing_time_get()
506 local bridge=$1
507 local ageing_time
509 # Need to divide by 100 to convert to seconds.
510 ageing_time=$(ip -j -d link show dev $bridge \
511 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
512 echo $((ageing_time / 100))
515 declare -A SYSCTL_ORIG
516 sysctl_set()
518 local key=$1; shift
519 local value=$1; shift
521 SYSCTL_ORIG[$key]=$(sysctl -n $key)
522 sysctl -qw $key=$value
525 sysctl_restore()
527 local key=$1; shift
529 sysctl -qw $key=${SYSCTL_ORIG["$key"]}
532 forwarding_enable()
534 sysctl_set net.ipv4.conf.all.forwarding 1
535 sysctl_set net.ipv6.conf.all.forwarding 1
538 forwarding_restore()
540 sysctl_restore net.ipv6.conf.all.forwarding
541 sysctl_restore net.ipv4.conf.all.forwarding
544 tc_offload_check()
546 local num_netifs=${1:-$NUM_NETIFS}
548 for ((i = 1; i <= num_netifs; ++i)); do
549 ethtool -k ${NETIFS[p$i]} \
550 | grep "hw-tc-offload: on" &> /dev/null
551 if [[ $? -ne 0 ]]; then
552 return 1
554 done
556 return 0
559 trap_install()
561 local dev=$1; shift
562 local direction=$1; shift
564 # Some devices may not support or need in-hardware trapping of traffic
565 # (e.g. the veth pairs that this library creates for non-existent
566 # loopbacks). Use continue instead, so that there is a filter in there
567 # (some tests check counters), and so that other filters are still
568 # processed.
569 tc filter add dev $dev $direction pref 1 \
570 flower skip_sw action trap 2>/dev/null \
571 || tc filter add dev $dev $direction pref 1 \
572 flower action continue
575 trap_uninstall()
577 local dev=$1; shift
578 local direction=$1; shift
580 tc filter del dev $dev $direction pref 1 flower
583 slow_path_trap_install()
585 # For slow-path testing, we need to install a trap to get to
586 # slow path the packets that would otherwise be switched in HW.
587 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
588 trap_install "$@"
592 slow_path_trap_uninstall()
594 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
595 trap_uninstall "$@"
599 __icmp_capture_add_del()
601 local add_del=$1; shift
602 local pref=$1; shift
603 local vsuf=$1; shift
604 local tundev=$1; shift
605 local filter=$1; shift
607 tc filter $add_del dev "$tundev" ingress \
608 proto ip$vsuf pref $pref \
609 flower ip_proto icmp$vsuf $filter \
610 action pass
613 icmp_capture_install()
615 __icmp_capture_add_del add 100 "" "$@"
618 icmp_capture_uninstall()
620 __icmp_capture_add_del del 100 "" "$@"
623 icmp6_capture_install()
625 __icmp_capture_add_del add 100 v6 "$@"
628 icmp6_capture_uninstall()
630 __icmp_capture_add_del del 100 v6 "$@"
633 __vlan_capture_add_del()
635 local add_del=$1; shift
636 local pref=$1; shift
637 local dev=$1; shift
638 local filter=$1; shift
640 tc filter $add_del dev "$dev" ingress \
641 proto 802.1q pref $pref \
642 flower $filter \
643 action pass
646 vlan_capture_install()
648 __vlan_capture_add_del add 100 "$@"
651 vlan_capture_uninstall()
653 __vlan_capture_add_del del 100 "$@"
656 __dscp_capture_add_del()
658 local add_del=$1; shift
659 local dev=$1; shift
660 local base=$1; shift
661 local dscp;
663 for prio in {0..7}; do
664 dscp=$((base + prio))
665 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
666 "skip_hw ip_tos $((dscp << 2))"
667 done
670 dscp_capture_install()
672 local dev=$1; shift
673 local base=$1; shift
675 __dscp_capture_add_del add $dev $base
678 dscp_capture_uninstall()
680 local dev=$1; shift
681 local base=$1; shift
683 __dscp_capture_add_del del $dev $base
686 dscp_fetch_stats()
688 local dev=$1; shift
689 local base=$1; shift
691 for prio in {0..7}; do
692 local dscp=$((base + prio))
693 local t=$(tc_rule_stats_get $dev $((dscp + 100)))
694 echo "[$dscp]=$t "
695 done
698 matchall_sink_create()
700 local dev=$1; shift
702 tc qdisc add dev $dev clsact
703 tc filter add dev $dev ingress \
704 pref 10000 \
705 matchall \
706 action drop
709 tests_run()
711 local current_test
713 for current_test in ${TESTS:-$ALL_TESTS}; do
714 $current_test
715 done
718 multipath_eval()
720 local desc="$1"
721 local weight_rp12=$2
722 local weight_rp13=$3
723 local packets_rp12=$4
724 local packets_rp13=$5
725 local weights_ratio packets_ratio diff
727 RET=0
729 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
730 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
731 | bc -l)
732 else
733 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
734 | bc -l)
737 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
738 check_err 1 "Packet difference is 0"
739 log_test "Multipath"
740 log_info "Expected ratio $weights_ratio"
741 return
744 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
745 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
746 | bc -l)
747 else
748 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
749 | bc -l)
752 diff=$(echo $weights_ratio - $packets_ratio | bc -l)
753 diff=${diff#-}
755 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
756 check_err $? "Too large discrepancy between expected and measured ratios"
757 log_test "$desc"
758 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
761 ##############################################################################
762 # Tests
764 ping_do()
766 local if_name=$1
767 local dip=$2
768 local vrf_name
770 vrf_name=$(master_name_get $if_name)
771 ip vrf exec $vrf_name $PING $dip -c 10 -i 0.1 -w 2 &> /dev/null
774 ping_test()
776 RET=0
778 ping_do $1 $2
779 check_err $?
780 log_test "ping"
783 ping6_do()
785 local if_name=$1
786 local dip=$2
787 local vrf_name
789 vrf_name=$(master_name_get $if_name)
790 ip vrf exec $vrf_name $PING6 $dip -c 10 -i 0.1 -w 2 &> /dev/null
793 ping6_test()
795 RET=0
797 ping6_do $1 $2
798 check_err $?
799 log_test "ping6"
802 learning_test()
804 local bridge=$1
805 local br_port1=$2 # Connected to `host1_if`.
806 local host1_if=$3
807 local host2_if=$4
808 local mac=de:ad:be:ef:13:37
809 local ageing_time
811 RET=0
813 bridge -j fdb show br $bridge brport $br_port1 \
814 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
815 check_fail $? "Found FDB record when should not"
817 # Disable unknown unicast flooding on `br_port1` to make sure
818 # packets are only forwarded through the port after a matching
819 # FDB entry was installed.
820 bridge link set dev $br_port1 flood off
822 tc qdisc add dev $host1_if ingress
823 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
824 flower dst_mac $mac action drop
826 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
827 sleep 1
829 tc -j -s filter show dev $host1_if ingress \
830 | jq -e ".[] | select(.options.handle == 101) \
831 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
832 check_fail $? "Packet reached second host when should not"
834 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
835 sleep 1
837 bridge -j fdb show br $bridge brport $br_port1 \
838 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
839 check_err $? "Did not find FDB record when should"
841 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
842 sleep 1
844 tc -j -s filter show dev $host1_if ingress \
845 | jq -e ".[] | select(.options.handle == 101) \
846 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
847 check_err $? "Packet did not reach second host when should"
849 # Wait for 10 seconds after the ageing time to make sure FDB
850 # record was aged-out.
851 ageing_time=$(bridge_ageing_time_get $bridge)
852 sleep $((ageing_time + 10))
854 bridge -j fdb show br $bridge brport $br_port1 \
855 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
856 check_fail $? "Found FDB record when should not"
858 bridge link set dev $br_port1 learning off
860 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
861 sleep 1
863 bridge -j fdb show br $bridge brport $br_port1 \
864 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
865 check_fail $? "Found FDB record when should not"
867 bridge link set dev $br_port1 learning on
869 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
870 tc qdisc del dev $host1_if ingress
872 bridge link set dev $br_port1 flood on
874 log_test "FDB learning"
877 flood_test_do()
879 local should_flood=$1
880 local mac=$2
881 local ip=$3
882 local host1_if=$4
883 local host2_if=$5
884 local err=0
886 # Add an ACL on `host2_if` which will tell us whether the packet
887 # was flooded to it or not.
888 tc qdisc add dev $host2_if ingress
889 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
890 flower dst_mac $mac action drop
892 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
893 sleep 1
895 tc -j -s filter show dev $host2_if ingress \
896 | jq -e ".[] | select(.options.handle == 101) \
897 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
898 if [[ $? -ne 0 && $should_flood == "true" || \
899 $? -eq 0 && $should_flood == "false" ]]; then
900 err=1
903 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
904 tc qdisc del dev $host2_if ingress
906 return $err
909 flood_unicast_test()
911 local br_port=$1
912 local host1_if=$2
913 local host2_if=$3
914 local mac=de:ad:be:ef:13:37
915 local ip=192.0.2.100
917 RET=0
919 bridge link set dev $br_port flood off
921 flood_test_do false $mac $ip $host1_if $host2_if
922 check_err $? "Packet flooded when should not"
924 bridge link set dev $br_port flood on
926 flood_test_do true $mac $ip $host1_if $host2_if
927 check_err $? "Packet was not flooded when should"
929 log_test "Unknown unicast flood"
932 flood_multicast_test()
934 local br_port=$1
935 local host1_if=$2
936 local host2_if=$3
937 local mac=01:00:5e:00:00:01
938 local ip=239.0.0.1
940 RET=0
942 bridge link set dev $br_port mcast_flood off
944 flood_test_do false $mac $ip $host1_if $host2_if
945 check_err $? "Packet flooded when should not"
947 bridge link set dev $br_port mcast_flood on
949 flood_test_do true $mac $ip $host1_if $host2_if
950 check_err $? "Packet was not flooded when should"
952 log_test "Unregistered multicast flood"
955 flood_test()
957 # `br_port` is connected to `host2_if`
958 local br_port=$1
959 local host1_if=$2
960 local host2_if=$3
962 flood_unicast_test $br_port $host1_if $host2_if
963 flood_multicast_test $br_port $host1_if $host2_if