Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
228setup_wait_dev()
229{
230 local dev=$1; shift
231 local wait_time=${1:-$WAIT_TIME}; shift
232
233 setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time
234
235 if (($?)); then
236 check_err 1
237 log_test setup_wait_dev ": Interface $dev does not come up."
238 exit 1
239 fi
240}
241
242setup_wait_dev_with_timeout()
243{
244 local dev=$1; shift
245 local max_iterations=${1:-$WAIT_TIMEOUT}; shift
246 local wait_time=${1:-$WAIT_TIME}; shift
247 local i
248
249 for ((i = 1; i <= $max_iterations; ++i)); do
250 ip link show dev $dev up \
251 | grep 'state UP' &> /dev/null
252 if [[ $? -ne 0 ]]; then
253 sleep 1
254 else
255 sleep $wait_time
256 return 0
257 fi
258 done
259
260 return 1
261}
262
263setup_wait()
264{
265 local num_netifs=${1:-$NUM_NETIFS}
266 local i
267
268 for ((i = 1; i <= num_netifs; ++i)); do
269 setup_wait_dev ${NETIFS[p$i]} 0
270 done
271
272 # Make sure links are ready.
273 sleep $WAIT_TIME
274}
275
276cmd_jq()
277{
278 local cmd=$1
279 local jq_exp=$2
280 local jq_opts=$3
281 local ret
282 local output
283
284 output="$($cmd)"
285 # it the command fails, return error right away
286 ret=$?
287 if [[ $ret -ne 0 ]]; then
288 return $ret
289 fi
290 output=$(echo $output | jq -r $jq_opts "$jq_exp")
291 ret=$?
292 if [[ $ret -ne 0 ]]; then
293 return $ret
294 fi
295 echo $output
296 # return success only in case of non-empty output
297 [ ! -z "$output" ]
298}
299
300lldpad_app_wait_set()
301{
302 local dev=$1; shift
303
304 while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do
305 echo "$dev: waiting for lldpad to push pending APP updates"
306 sleep 5
307 done
308}
309
310lldpad_app_wait_del()
311{
312 # Give lldpad a chance to push down the changes. If the device is downed
313 # too soon, the updates will be left pending. However, they will have
314 # been struck off the lldpad's DB already, so we won't be able to tell
315 # they are pending. Then on next test iteration this would cause
316 # weirdness as newly-added APP rules conflict with the old ones,
317 # sometimes getting stuck in an "unknown" state.
318 sleep 5
319}
320
321pre_cleanup()
322{
323 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
324 echo "Pausing before cleanup, hit any key to continue"
325 read
326 fi
327}
328
329vrf_prepare()
330{
331 ip -4 rule add pref 32765 table local
332 ip -4 rule del pref 0
333 ip -6 rule add pref 32765 table local
334 ip -6 rule del pref 0
335}
336
337vrf_cleanup()
338{
339 ip -6 rule add pref 0 table local
340 ip -6 rule del pref 32765
341 ip -4 rule add pref 0 table local
342 ip -4 rule del pref 32765
343}
344
345__last_tb_id=0
346declare -A __TB_IDS
347
348__vrf_td_id_assign()
349{
350 local vrf_name=$1
351
352 __last_tb_id=$((__last_tb_id + 1))
353 __TB_IDS[$vrf_name]=$__last_tb_id
354 return $__last_tb_id
355}
356
357__vrf_td_id_lookup()
358{
359 local vrf_name=$1
360
361 return ${__TB_IDS[$vrf_name]}
362}
363
364vrf_create()
365{
366 local vrf_name=$1
367 local tb_id
368
369 __vrf_td_id_assign $vrf_name
370 tb_id=$?
371
372 ip link add dev $vrf_name type vrf table $tb_id
373 ip -4 route add table $tb_id unreachable default metric 4278198272
374 ip -6 route add table $tb_id unreachable default metric 4278198272
375}
376
377vrf_destroy()
378{
379 local vrf_name=$1
380 local tb_id
381
382 __vrf_td_id_lookup $vrf_name
383 tb_id=$?
384
385 ip -6 route del table $tb_id unreachable default metric 4278198272
386 ip -4 route del table $tb_id unreachable default metric 4278198272
387 ip link del dev $vrf_name
388}
389
390__addr_add_del()
391{
392 local if_name=$1
393 local add_del=$2
394 local array
395
396 shift
397 shift
398 array=("${@}")
399
400 for addrstr in "${array[@]}"; do
401 ip address $add_del $addrstr dev $if_name
402 done
403}
404
405__simple_if_init()
406{
407 local if_name=$1; shift
408 local vrf_name=$1; shift
409 local addrs=("${@}")
410
411 ip link set dev $if_name master $vrf_name
412 ip link set dev $if_name up
413
414 __addr_add_del $if_name add "${addrs[@]}"
415}
416
417__simple_if_fini()
418{
419 local if_name=$1; shift
420 local addrs=("${@}")
421
422 __addr_add_del $if_name del "${addrs[@]}"
423
424 ip link set dev $if_name down
425 ip link set dev $if_name nomaster
426}
427
428simple_if_init()
429{
430 local if_name=$1
431 local vrf_name
432 local array
433
434 shift
435 vrf_name=v$if_name
436 array=("${@}")
437
438 vrf_create $vrf_name
439 ip link set dev $vrf_name up
440 __simple_if_init $if_name $vrf_name "${array[@]}"
441}
442
443simple_if_fini()
444{
445 local if_name=$1
446 local vrf_name
447 local array
448
449 shift
450 vrf_name=v$if_name
451 array=("${@}")
452
453 __simple_if_fini $if_name "${array[@]}"
454 vrf_destroy $vrf_name
455}
456
457tunnel_create()
458{
459 local name=$1; shift
460 local type=$1; shift
461 local local=$1; shift
462 local remote=$1; shift
463
464 ip link add name $name type $type \
465 local $local remote $remote "$@"
466 ip link set dev $name up
467}
468
469tunnel_destroy()
470{
471 local name=$1; shift
472
473 ip link del dev $name
474}
475
476vlan_create()
477{
478 local if_name=$1; shift
479 local vid=$1; shift
480 local vrf=$1; shift
481 local ips=("${@}")
482 local name=$if_name.$vid
483
484 ip link add name $name link $if_name type vlan id $vid
485 if [ "$vrf" != "" ]; then
486 ip link set dev $name master $vrf
487 fi
488 ip link set dev $name up
489 __addr_add_del $name add "${ips[@]}"
490}
491
492vlan_destroy()
493{
494 local if_name=$1; shift
495 local vid=$1; shift
496 local name=$if_name.$vid
497
498 ip link del dev $name
499}
500
501team_create()
502{
503 local if_name=$1; shift
504 local mode=$1; shift
505
506 require_command $TEAMD
507 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
508 for slave in "$@"; do
509 ip link set dev $slave down
510 ip link set dev $slave master $if_name
511 ip link set dev $slave up
512 done
513 ip link set dev $if_name up
514}
515
516team_destroy()
517{
518 local if_name=$1; shift
519
520 $TEAMD -t $if_name -k
521}
522
523master_name_get()
524{
525 local if_name=$1
526
527 ip -j link show dev $if_name | jq -r '.[]["master"]'
528}
529
530link_stats_get()
531{
532 local if_name=$1; shift
533 local dir=$1; shift
534 local stat=$1; shift
535
536 ip -j -s link show dev $if_name \
537 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]'
538}
539
540link_stats_tx_packets_get()
541{
542 link_stats_get $1 tx packets
543}
544
545link_stats_rx_errors_get()
546{
547 link_stats_get $1 rx errors
548}
549
550tc_rule_stats_get()
551{
552 local dev=$1; shift
553 local pref=$1; shift
554 local dir=$1; shift
555
556 tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \
557 | jq '.[1].options.actions[].stats.packets'
558}
559
560ethtool_stats_get()
561{
562 local dev=$1; shift
563 local stat=$1; shift
564
565 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
566}
567
568mac_get()
569{
570 local if_name=$1
571
572 ip -j link show dev $if_name | jq -r '.[]["address"]'
573}
574
575bridge_ageing_time_get()
576{
577 local bridge=$1
578 local ageing_time
579
580 # Need to divide by 100 to convert to seconds.
581 ageing_time=$(ip -j -d link show dev $bridge \
582 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
583 echo $((ageing_time / 100))
584}
585
586declare -A SYSCTL_ORIG
587sysctl_set()
588{
589 local key=$1; shift
590 local value=$1; shift
591
592 SYSCTL_ORIG[$key]=$(sysctl -n $key)
593 sysctl -qw $key=$value
594}
595
596sysctl_restore()
597{
598 local key=$1; shift
599
600 sysctl -qw $key=${SYSCTL_ORIG["$key"]}
601}
602
603forwarding_enable()
604{
605 sysctl_set net.ipv4.conf.all.forwarding 1
606 sysctl_set net.ipv6.conf.all.forwarding 1
607}
608
609forwarding_restore()
610{
611 sysctl_restore net.ipv6.conf.all.forwarding
612 sysctl_restore net.ipv4.conf.all.forwarding
613}
614
615declare -A MTU_ORIG
616mtu_set()
617{
618 local dev=$1; shift
619 local mtu=$1; shift
620
621 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')
622 ip link set dev $dev mtu $mtu
623}
624
625mtu_restore()
626{
627 local dev=$1; shift
628
629 ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
630}
631
632tc_offload_check()
633{
634 local num_netifs=${1:-$NUM_NETIFS}
635
636 for ((i = 1; i <= num_netifs; ++i)); do
637 ethtool -k ${NETIFS[p$i]} \
638 | grep "hw-tc-offload: on" &> /dev/null
639 if [[ $? -ne 0 ]]; then
640 return 1
641 fi
642 done
643
644 return 0
645}
646
647trap_install()
648{
649 local dev=$1; shift
650 local direction=$1; shift
651
652 # Some devices may not support or need in-hardware trapping of traffic
653 # (e.g. the veth pairs that this library creates for non-existent
654 # loopbacks). Use continue instead, so that there is a filter in there
655 # (some tests check counters), and so that other filters are still
656 # processed.
657 tc filter add dev $dev $direction pref 1 \
658 flower skip_sw action trap 2>/dev/null \
659 || tc filter add dev $dev $direction pref 1 \
660 flower action continue
661}
662
663trap_uninstall()
664{
665 local dev=$1; shift
666 local direction=$1; shift
667
668 tc filter del dev $dev $direction pref 1 flower
669}
670
671slow_path_trap_install()
672{
673 # For slow-path testing, we need to install a trap to get to
674 # slow path the packets that would otherwise be switched in HW.
675 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
676 trap_install "$@"
677 fi
678}
679
680slow_path_trap_uninstall()
681{
682 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
683 trap_uninstall "$@"
684 fi
685}
686
687__icmp_capture_add_del()
688{
689 local add_del=$1; shift
690 local pref=$1; shift
691 local vsuf=$1; shift
692 local tundev=$1; shift
693 local filter=$1; shift
694
695 tc filter $add_del dev "$tundev" ingress \
696 proto ip$vsuf pref $pref \
697 flower ip_proto icmp$vsuf $filter \
698 action pass
699}
700
701icmp_capture_install()
702{
703 __icmp_capture_add_del add 100 "" "$@"
704}
705
706icmp_capture_uninstall()
707{
708 __icmp_capture_add_del del 100 "" "$@"
709}
710
711icmp6_capture_install()
712{
713 __icmp_capture_add_del add 100 v6 "$@"
714}
715
716icmp6_capture_uninstall()
717{
718 __icmp_capture_add_del del 100 v6 "$@"
719}
720
721__vlan_capture_add_del()
722{
723 local add_del=$1; shift
724 local pref=$1; shift
725 local dev=$1; shift
726 local filter=$1; shift
727
728 tc filter $add_del dev "$dev" ingress \
729 proto 802.1q pref $pref \
730 flower $filter \
731 action pass
732}
733
734vlan_capture_install()
735{
736 __vlan_capture_add_del add 100 "$@"
737}
738
739vlan_capture_uninstall()
740{
741 __vlan_capture_add_del del 100 "$@"
742}
743
744__dscp_capture_add_del()
745{
746 local add_del=$1; shift
747 local dev=$1; shift
748 local base=$1; shift
749 local dscp;
750
751 for prio in {0..7}; do
752 dscp=$((base + prio))
753 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
754 "skip_hw ip_tos $((dscp << 2))"
755 done
756}
757
758dscp_capture_install()
759{
760 local dev=$1; shift
761 local base=$1; shift
762
763 __dscp_capture_add_del add $dev $base
764}
765
766dscp_capture_uninstall()
767{
768 local dev=$1; shift
769 local base=$1; shift
770
771 __dscp_capture_add_del del $dev $base
772}
773
774dscp_fetch_stats()
775{
776 local dev=$1; shift
777 local base=$1; shift
778
779 for prio in {0..7}; do
780 local dscp=$((base + prio))
781 local t=$(tc_rule_stats_get $dev $((dscp + 100)))
782 echo "[$dscp]=$t "
783 done
784}
785
786matchall_sink_create()
787{
788 local dev=$1; shift
789
790 tc qdisc add dev $dev clsact
791 tc filter add dev $dev ingress \
792 pref 10000 \
793 matchall \
794 action drop
795}
796
797tests_run()
798{
799 local current_test
800
801 for current_test in ${TESTS:-$ALL_TESTS}; do
802 $current_test
803 done
804}
805
806multipath_eval()
807{
808 local desc="$1"
809 local weight_rp12=$2
810 local weight_rp13=$3
811 local packets_rp12=$4
812 local packets_rp13=$5
813 local weights_ratio packets_ratio diff
814
815 RET=0
816
817 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
818 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
819 | bc -l)
820 else
821 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
822 | bc -l)
823 fi
824
825 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
826 check_err 1 "Packet difference is 0"
827 log_test "Multipath"
828 log_info "Expected ratio $weights_ratio"
829 return
830 fi
831
832 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
833 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
834 | bc -l)
835 else
836 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
837 | bc -l)
838 fi
839
840 diff=$(echo $weights_ratio - $packets_ratio | bc -l)
841 diff=${diff#-}
842
843 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
844 check_err $? "Too large discrepancy between expected and measured ratios"
845 log_test "$desc"
846 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
847}
848
849in_ns()
850{
851 local name=$1; shift
852
853 ip netns exec $name bash <<-EOF
854 NUM_NETIFS=0
855 source lib.sh
856 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
857 EOF
858}
859
860##############################################################################
861# Tests
862
863ping_do()
864{
865 local if_name=$1
866 local dip=$2
867 local args=$3
868 local vrf_name
869
870 vrf_name=$(master_name_get $if_name)
871 ip vrf exec $vrf_name \
872 $PING $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null
873}
874
875ping_test()
876{
877 RET=0
878
879 ping_do $1 $2
880 check_err $?
881 log_test "ping$3"
882}
883
884ping6_do()
885{
886 local if_name=$1
887 local dip=$2
888 local args=$3
889 local vrf_name
890
891 vrf_name=$(master_name_get $if_name)
892 ip vrf exec $vrf_name \
893 $PING6 $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null
894}
895
896ping6_test()
897{
898 RET=0
899
900 ping6_do $1 $2
901 check_err $?
902 log_test "ping6$3"
903}
904
905learning_test()
906{
907 local bridge=$1
908 local br_port1=$2 # Connected to `host1_if`.
909 local host1_if=$3
910 local host2_if=$4
911 local mac=de:ad:be:ef:13:37
912 local ageing_time
913
914 RET=0
915
916 bridge -j fdb show br $bridge brport $br_port1 \
917 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
918 check_fail $? "Found FDB record when should not"
919
920 # Disable unknown unicast flooding on `br_port1` to make sure
921 # packets are only forwarded through the port after a matching
922 # FDB entry was installed.
923 bridge link set dev $br_port1 flood off
924
925 tc qdisc add dev $host1_if ingress
926 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
927 flower dst_mac $mac action drop
928
929 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
930 sleep 1
931
932 tc -j -s filter show dev $host1_if ingress \
933 | jq -e ".[] | select(.options.handle == 101) \
934 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
935 check_fail $? "Packet reached second host when should not"
936
937 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
938 sleep 1
939
940 bridge -j fdb show br $bridge brport $br_port1 \
941 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
942 check_err $? "Did not find FDB record when should"
943
944 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
945 sleep 1
946
947 tc -j -s filter show dev $host1_if ingress \
948 | jq -e ".[] | select(.options.handle == 101) \
949 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
950 check_err $? "Packet did not reach second host when should"
951
952 # Wait for 10 seconds after the ageing time to make sure FDB
953 # record was aged-out.
954 ageing_time=$(bridge_ageing_time_get $bridge)
955 sleep $((ageing_time + 10))
956
957 bridge -j fdb show br $bridge brport $br_port1 \
958 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
959 check_fail $? "Found FDB record when should not"
960
961 bridge link set dev $br_port1 learning off
962
963 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
964 sleep 1
965
966 bridge -j fdb show br $bridge brport $br_port1 \
967 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
968 check_fail $? "Found FDB record when should not"
969
970 bridge link set dev $br_port1 learning on
971
972 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
973 tc qdisc del dev $host1_if ingress
974
975 bridge link set dev $br_port1 flood on
976
977 log_test "FDB learning"
978}
979
980flood_test_do()
981{
982 local should_flood=$1
983 local mac=$2
984 local ip=$3
985 local host1_if=$4
986 local host2_if=$5
987 local err=0
988
989 # Add an ACL on `host2_if` which will tell us whether the packet
990 # was flooded to it or not.
991 tc qdisc add dev $host2_if ingress
992 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
993 flower dst_mac $mac action drop
994
995 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
996 sleep 1
997
998 tc -j -s filter show dev $host2_if ingress \
999 | jq -e ".[] | select(.options.handle == 101) \
1000 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1001 if [[ $? -ne 0 && $should_flood == "true" || \
1002 $? -eq 0 && $should_flood == "false" ]]; then
1003 err=1
1004 fi
1005
1006 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
1007 tc qdisc del dev $host2_if ingress
1008
1009 return $err
1010}
1011
1012flood_unicast_test()
1013{
1014 local br_port=$1
1015 local host1_if=$2
1016 local host2_if=$3
1017 local mac=de:ad:be:ef:13:37
1018 local ip=192.0.2.100
1019
1020 RET=0
1021
1022 bridge link set dev $br_port flood off
1023
1024 flood_test_do false $mac $ip $host1_if $host2_if
1025 check_err $? "Packet flooded when should not"
1026
1027 bridge link set dev $br_port flood on
1028
1029 flood_test_do true $mac $ip $host1_if $host2_if
1030 check_err $? "Packet was not flooded when should"
1031
1032 log_test "Unknown unicast flood"
1033}
1034
1035flood_multicast_test()
1036{
1037 local br_port=$1
1038 local host1_if=$2
1039 local host2_if=$3
1040 local mac=01:00:5e:00:00:01
1041 local ip=239.0.0.1
1042
1043 RET=0
1044
1045 bridge link set dev $br_port mcast_flood off
1046
1047 flood_test_do false $mac $ip $host1_if $host2_if
1048 check_err $? "Packet flooded when should not"
1049
1050 bridge link set dev $br_port mcast_flood on
1051
1052 flood_test_do true $mac $ip $host1_if $host2_if
1053 check_err $? "Packet was not flooded when should"
1054
1055 log_test "Unregistered multicast flood"
1056}
1057
1058flood_test()
1059{
1060 # `br_port` is connected to `host2_if`
1061 local br_port=$1
1062 local host1_if=$2
1063 local host2_if=$3
1064
1065 flood_unicast_test $br_port $host1_if $host2_if
1066 flood_multicast_test $br_port $host1_if $host2_if
1067}