selftests: net: push jq workaround into separate helper
[linux-2.6-block.git] / tools / testing / selftests / net / forwarding / lib.sh
1 #!/bin/bash
2 # SPDX-License-Identifier: GPL-2.0
3
4 ##############################################################################
5 # Defines
6
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}
18 MCD=${MCD:=smcrouted}
19 MC_CLI=${MC_CLI:=smcroutectl}
20 PING_TIMEOUT=${PING_TIMEOUT:=5}
21
22 relative_path="${BASH_SOURCE%/*}"
23 if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then
24         relative_path="."
25 fi
26
27 if [[ -f $relative_path/forwarding.config ]]; then
28         source "$relative_path/forwarding.config"
29 fi
30
31 ##############################################################################
32 # Sanity checks
33
34 check_tc_version()
35 {
36         tc -j &> /dev/null
37         if [[ $? -ne 0 ]]; then
38                 echo "SKIP: iproute2 too old; tc is missing JSON support"
39                 exit 1
40         fi
41 }
42
43 check_tc_shblock_support()
44 {
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"
48                 exit 1
49         fi
50 }
51
52 check_tc_chain_support()
53 {
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"
57                 exit 1
58         fi
59 }
60
61 if [[ "$(id -u)" -ne 0 ]]; then
62         echo "SKIP: need root privileges"
63         exit 0
64 fi
65
66 if [[ "$CHECK_TC" = "yes" ]]; then
67         check_tc_version
68 fi
69
70 require_command()
71 {
72         local cmd=$1; shift
73
74         if [[ ! -x "$(command -v "$cmd")" ]]; then
75                 echo "SKIP: $cmd not installed"
76                 exit 1
77         fi
78 }
79
80 require_command jq
81 require_command $MZ
82
83 if [[ ! -v NUM_NETIFS ]]; then
84         echo "SKIP: importer does not define \"NUM_NETIFS\""
85         exit 1
86 fi
87
88 ##############################################################################
89 # Command line options handling
90
91 count=0
92
93 while [[ $# -gt 0 ]]; do
94         if [[ "$count" -eq "0" ]]; then
95                 unset NETIFS
96                 declare -A NETIFS
97         fi
98         count=$((count + 1))
99         NETIFS[p$count]="$1"
100         shift
101 done
102
103 ##############################################################################
104 # Network interfaces configuration
105
106 create_netif_veth()
107 {
108         local i
109
110         for ((i = 1; i <= NUM_NETIFS; ++i)); do
111                 local j=$((i+1))
112
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"
119                                 exit 1
120                         fi
121                 fi
122                 i=$j
123         done
124 }
125
126 create_netif()
127 {
128         case "$NETIF_TYPE" in
129         veth) create_netif_veth
130               ;;
131         *) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
132            exit 1
133            ;;
134         esac
135 }
136
137 if [[ "$NETIF_CREATE" = "yes" ]]; then
138         create_netif
139 fi
140
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"
145                 exit 1
146         fi
147 done
148
149 ##############################################################################
150 # Helpers
151
152 # Exit status to return at the end. Set in case one of the tests fails.
153 EXIT_STATUS=0
154 # Per-test return value. Clear at the beginning of each test.
155 RET=0
156
157 check_err()
158 {
159         local err=$1
160         local msg=$2
161
162         if [[ $RET -eq 0 && $err -ne 0 ]]; then
163                 RET=$err
164                 retmsg=$msg
165         fi
166 }
167
168 check_fail()
169 {
170         local err=$1
171         local msg=$2
172
173         if [[ $RET -eq 0 && $err -eq 0 ]]; then
174                 RET=1
175                 retmsg=$msg
176         fi
177 }
178
179 check_err_fail()
180 {
181         local should_fail=$1; shift
182         local err=$1; shift
183         local what=$1; shift
184
185         if ((should_fail)); then
186                 check_fail $err "$what succeeded, but should have failed"
187         else
188                 check_err $err "$what failed"
189         fi
190 }
191
192 log_test()
193 {
194         local test_name=$1
195         local opt_str=$2
196
197         if [[ $# -eq 2 ]]; then
198                 opt_str="($opt_str)"
199         fi
200
201         if [[ $RET -ne 0 ]]; then
202                 EXIT_STATUS=1
203                 printf "TEST: %-60s  [FAIL]\n" "$test_name $opt_str"
204                 if [[ ! -z "$retmsg" ]]; then
205                         printf "\t%s\n" "$retmsg"
206                 fi
207                 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
208                         echo "Hit enter to continue, 'q' to quit"
209                         read a
210                         [ "$a" = "q" ] && exit 1
211                 fi
212                 return 1
213         fi
214
215         printf "TEST: %-60s  [ OK ]\n" "$test_name $opt_str"
216         return 0
217 }
218
219 log_info()
220 {
221         local msg=$1
222
223         echo "INFO: $msg"
224 }
225
226 setup_wait_dev()
227 {
228         local dev=$1; shift
229
230         while true; do
231                 ip link show dev $dev up \
232                         | grep 'state UP' &> /dev/null
233                 if [[ $? -ne 0 ]]; then
234                         sleep 1
235                 else
236                         break
237                 fi
238         done
239 }
240
241 setup_wait()
242 {
243         local num_netifs=${1:-$NUM_NETIFS}
244
245         for ((i = 1; i <= num_netifs; ++i)); do
246                 setup_wait_dev ${NETIFS[p$i]}
247         done
248
249         # Make sure links are ready.
250         sleep $WAIT_TIME
251 }
252
253 cmd_jq()
254 {
255         local cmd=$1
256         local jq_exp=$2
257         local ret
258         local output
259
260         output="$($cmd)"
261         # it the command fails, return error right away
262         ret=$?
263         if [[ $ret -ne 0 ]]; then
264                 return $ret
265         fi
266         output=$(echo $output | jq -r "$jq_exp")
267         echo $output
268         # return success only in case of non-empty output
269         [ ! -z "$output" ]
270 }
271
272 lldpad_app_wait_set()
273 {
274         local dev=$1; shift
275
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"
278                 sleep 5
279         done
280 }
281
282 lldpad_app_wait_del()
283 {
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.
290         sleep 5
291 }
292
293 pre_cleanup()
294 {
295         if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
296                 echo "Pausing before cleanup, hit any key to continue"
297                 read
298         fi
299 }
300
301 vrf_prepare()
302 {
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
307 }
308
309 vrf_cleanup()
310 {
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
315 }
316
317 __last_tb_id=0
318 declare -A __TB_IDS
319
320 __vrf_td_id_assign()
321 {
322         local vrf_name=$1
323
324         __last_tb_id=$((__last_tb_id + 1))
325         __TB_IDS[$vrf_name]=$__last_tb_id
326         return $__last_tb_id
327 }
328
329 __vrf_td_id_lookup()
330 {
331         local vrf_name=$1
332
333         return ${__TB_IDS[$vrf_name]}
334 }
335
336 vrf_create()
337 {
338         local vrf_name=$1
339         local tb_id
340
341         __vrf_td_id_assign $vrf_name
342         tb_id=$?
343
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
347 }
348
349 vrf_destroy()
350 {
351         local vrf_name=$1
352         local tb_id
353
354         __vrf_td_id_lookup $vrf_name
355         tb_id=$?
356
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
360 }
361
362 __addr_add_del()
363 {
364         local if_name=$1
365         local add_del=$2
366         local array
367
368         shift
369         shift
370         array=("${@}")
371
372         for addrstr in "${array[@]}"; do
373                 ip address $add_del $addrstr dev $if_name
374         done
375 }
376
377 __simple_if_init()
378 {
379         local if_name=$1; shift
380         local vrf_name=$1; shift
381         local addrs=("${@}")
382
383         ip link set dev $if_name master $vrf_name
384         ip link set dev $if_name up
385
386         __addr_add_del $if_name add "${addrs[@]}"
387 }
388
389 __simple_if_fini()
390 {
391         local if_name=$1; shift
392         local addrs=("${@}")
393
394         __addr_add_del $if_name del "${addrs[@]}"
395
396         ip link set dev $if_name down
397         ip link set dev $if_name nomaster
398 }
399
400 simple_if_init()
401 {
402         local if_name=$1
403         local vrf_name
404         local array
405
406         shift
407         vrf_name=v$if_name
408         array=("${@}")
409
410         vrf_create $vrf_name
411         ip link set dev $vrf_name up
412         __simple_if_init $if_name $vrf_name "${array[@]}"
413 }
414
415 simple_if_fini()
416 {
417         local if_name=$1
418         local vrf_name
419         local array
420
421         shift
422         vrf_name=v$if_name
423         array=("${@}")
424
425         __simple_if_fini $if_name "${array[@]}"
426         vrf_destroy $vrf_name
427 }
428
429 tunnel_create()
430 {
431         local name=$1; shift
432         local type=$1; shift
433         local local=$1; shift
434         local remote=$1; shift
435
436         ip link add name $name type $type \
437            local $local remote $remote "$@"
438         ip link set dev $name up
439 }
440
441 tunnel_destroy()
442 {
443         local name=$1; shift
444
445         ip link del dev $name
446 }
447
448 vlan_create()
449 {
450         local if_name=$1; shift
451         local vid=$1; shift
452         local vrf=$1; shift
453         local ips=("${@}")
454         local name=$if_name.$vid
455
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
459         fi
460         ip link set dev $name up
461         __addr_add_del $name add "${ips[@]}"
462 }
463
464 vlan_destroy()
465 {
466         local if_name=$1; shift
467         local vid=$1; shift
468         local name=$if_name.$vid
469
470         ip link del dev $name
471 }
472
473 team_create()
474 {
475         local if_name=$1; shift
476         local mode=$1; shift
477
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
484         done
485         ip link set dev $if_name up
486 }
487
488 team_destroy()
489 {
490         local if_name=$1; shift
491
492         $TEAMD -t $if_name -k
493 }
494
495 master_name_get()
496 {
497         local if_name=$1
498
499         ip -j link show dev $if_name | jq -r '.[]["master"]'
500 }
501
502 link_stats_get()
503 {
504         local if_name=$1; shift
505         local dir=$1; shift
506         local stat=$1; shift
507
508         ip -j -s link show dev $if_name \
509                 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]'
510 }
511
512 link_stats_tx_packets_get()
513 {
514         link_stats_get $1 tx packets
515 }
516
517 link_stats_rx_errors_get()
518 {
519         link_stats_get $1 rx errors
520 }
521
522 tc_rule_stats_get()
523 {
524         local dev=$1; shift
525         local pref=$1; shift
526         local dir=$1; shift
527
528         tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \
529             | jq '.[1].options.actions[].stats.packets'
530 }
531
532 ethtool_stats_get()
533 {
534         local dev=$1; shift
535         local stat=$1; shift
536
537         ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
538 }
539
540 mac_get()
541 {
542         local if_name=$1
543
544         ip -j link show dev $if_name | jq -r '.[]["address"]'
545 }
546
547 bridge_ageing_time_get()
548 {
549         local bridge=$1
550         local ageing_time
551
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))
556 }
557
558 declare -A SYSCTL_ORIG
559 sysctl_set()
560 {
561         local key=$1; shift
562         local value=$1; shift
563
564         SYSCTL_ORIG[$key]=$(sysctl -n $key)
565         sysctl -qw $key=$value
566 }
567
568 sysctl_restore()
569 {
570         local key=$1; shift
571
572         sysctl -qw $key=${SYSCTL_ORIG["$key"]}
573 }
574
575 forwarding_enable()
576 {
577         sysctl_set net.ipv4.conf.all.forwarding 1
578         sysctl_set net.ipv6.conf.all.forwarding 1
579 }
580
581 forwarding_restore()
582 {
583         sysctl_restore net.ipv6.conf.all.forwarding
584         sysctl_restore net.ipv4.conf.all.forwarding
585 }
586
587 declare -A MTU_ORIG
588 mtu_set()
589 {
590         local dev=$1; shift
591         local mtu=$1; shift
592
593         MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')
594         ip link set dev $dev mtu $mtu
595 }
596
597 mtu_restore()
598 {
599         local dev=$1; shift
600
601         ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
602 }
603
604 tc_offload_check()
605 {
606         local num_netifs=${1:-$NUM_NETIFS}
607
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
612                         return 1
613                 fi
614         done
615
616         return 0
617 }
618
619 trap_install()
620 {
621         local dev=$1; shift
622         local direction=$1; shift
623
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
628         # processed.
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
633 }
634
635 trap_uninstall()
636 {
637         local dev=$1; shift
638         local direction=$1; shift
639
640         tc filter del dev $dev $direction pref 1 flower
641 }
642
643 slow_path_trap_install()
644 {
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
648                 trap_install "$@"
649         fi
650 }
651
652 slow_path_trap_uninstall()
653 {
654         if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
655                 trap_uninstall "$@"
656         fi
657 }
658
659 __icmp_capture_add_del()
660 {
661         local add_del=$1; shift
662         local pref=$1; shift
663         local vsuf=$1; shift
664         local tundev=$1; shift
665         local filter=$1; shift
666
667         tc filter $add_del dev "$tundev" ingress \
668            proto ip$vsuf pref $pref \
669            flower ip_proto icmp$vsuf $filter \
670            action pass
671 }
672
673 icmp_capture_install()
674 {
675         __icmp_capture_add_del add 100 "" "$@"
676 }
677
678 icmp_capture_uninstall()
679 {
680         __icmp_capture_add_del del 100 "" "$@"
681 }
682
683 icmp6_capture_install()
684 {
685         __icmp_capture_add_del add 100 v6 "$@"
686 }
687
688 icmp6_capture_uninstall()
689 {
690         __icmp_capture_add_del del 100 v6 "$@"
691 }
692
693 __vlan_capture_add_del()
694 {
695         local add_del=$1; shift
696         local pref=$1; shift
697         local dev=$1; shift
698         local filter=$1; shift
699
700         tc filter $add_del dev "$dev" ingress \
701            proto 802.1q pref $pref \
702            flower $filter \
703            action pass
704 }
705
706 vlan_capture_install()
707 {
708         __vlan_capture_add_del add 100 "$@"
709 }
710
711 vlan_capture_uninstall()
712 {
713         __vlan_capture_add_del del 100 "$@"
714 }
715
716 __dscp_capture_add_del()
717 {
718         local add_del=$1; shift
719         local dev=$1; shift
720         local base=$1; shift
721         local dscp;
722
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))"
727         done
728 }
729
730 dscp_capture_install()
731 {
732         local dev=$1; shift
733         local base=$1; shift
734
735         __dscp_capture_add_del add $dev $base
736 }
737
738 dscp_capture_uninstall()
739 {
740         local dev=$1; shift
741         local base=$1; shift
742
743         __dscp_capture_add_del del $dev $base
744 }
745
746 dscp_fetch_stats()
747 {
748         local dev=$1; shift
749         local base=$1; shift
750
751         for prio in {0..7}; do
752                 local dscp=$((base + prio))
753                 local t=$(tc_rule_stats_get $dev $((dscp + 100)))
754                 echo "[$dscp]=$t "
755         done
756 }
757
758 matchall_sink_create()
759 {
760         local dev=$1; shift
761
762         tc qdisc add dev $dev clsact
763         tc filter add dev $dev ingress \
764            pref 10000 \
765            matchall \
766            action drop
767 }
768
769 tests_run()
770 {
771         local current_test
772
773         for current_test in ${TESTS:-$ALL_TESTS}; do
774                 $current_test
775         done
776 }
777
778 multipath_eval()
779 {
780         local desc="$1"
781         local weight_rp12=$2
782         local weight_rp13=$3
783         local packets_rp12=$4
784         local packets_rp13=$5
785         local weights_ratio packets_ratio diff
786
787         RET=0
788
789         if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
790                 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
791                                 | bc -l)
792         else
793                 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
794                                 | bc -l)
795         fi
796
797         if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
798                check_err 1 "Packet difference is 0"
799                log_test "Multipath"
800                log_info "Expected ratio $weights_ratio"
801                return
802         fi
803
804         if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
805                 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
806                                 | bc -l)
807         else
808                 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
809                                 | bc -l)
810         fi
811
812         diff=$(echo $weights_ratio - $packets_ratio | bc -l)
813         diff=${diff#-}
814
815         test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
816         check_err $? "Too large discrepancy between expected and measured ratios"
817         log_test "$desc"
818         log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
819 }
820
821 in_ns()
822 {
823         local name=$1; shift
824
825         ip netns exec $name bash <<-EOF
826                 NUM_NETIFS=0
827                 source lib.sh
828                 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
829         EOF
830 }
831
832 ##############################################################################
833 # Tests
834
835 ping_do()
836 {
837         local if_name=$1
838         local dip=$2
839         local args=$3
840         local vrf_name
841
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
845 }
846
847 ping_test()
848 {
849         RET=0
850
851         ping_do $1 $2
852         check_err $?
853         log_test "ping$3"
854 }
855
856 ping6_do()
857 {
858         local if_name=$1
859         local dip=$2
860         local args=$3
861         local vrf_name
862
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
866 }
867
868 ping6_test()
869 {
870         RET=0
871
872         ping6_do $1 $2
873         check_err $?
874         log_test "ping6$3"
875 }
876
877 learning_test()
878 {
879         local bridge=$1
880         local br_port1=$2       # Connected to `host1_if`.
881         local host1_if=$3
882         local host2_if=$4
883         local mac=de:ad:be:ef:13:37
884         local ageing_time
885
886         RET=0
887
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"
891
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
896
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
900
901         $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
902         sleep 1
903
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"
908
909         $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
910         sleep 1
911
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"
915
916         $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
917         sleep 1
918
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"
923
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))
928
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"
932
933         bridge link set dev $br_port1 learning off
934
935         $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
936         sleep 1
937
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"
941
942         bridge link set dev $br_port1 learning on
943
944         tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
945         tc qdisc del dev $host1_if ingress
946
947         bridge link set dev $br_port1 flood on
948
949         log_test "FDB learning"
950 }
951
952 flood_test_do()
953 {
954         local should_flood=$1
955         local mac=$2
956         local ip=$3
957         local host1_if=$4
958         local host2_if=$5
959         local err=0
960
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
966
967         $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
968         sleep 1
969
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
975                 err=1
976         fi
977
978         tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
979         tc qdisc del dev $host2_if ingress
980
981         return $err
982 }
983
984 flood_unicast_test()
985 {
986         local br_port=$1
987         local host1_if=$2
988         local host2_if=$3
989         local mac=de:ad:be:ef:13:37
990         local ip=192.0.2.100
991
992         RET=0
993
994         bridge link set dev $br_port flood off
995
996         flood_test_do false $mac $ip $host1_if $host2_if
997         check_err $? "Packet flooded when should not"
998
999         bridge link set dev $br_port flood on
1000
1001         flood_test_do true $mac $ip $host1_if $host2_if
1002         check_err $? "Packet was not flooded when should"
1003
1004         log_test "Unknown unicast flood"
1005 }
1006
1007 flood_multicast_test()
1008 {
1009         local br_port=$1
1010         local host1_if=$2
1011         local host2_if=$3
1012         local mac=01:00:5e:00:00:01
1013         local ip=239.0.0.1
1014
1015         RET=0
1016
1017         bridge link set dev $br_port mcast_flood off
1018
1019         flood_test_do false $mac $ip $host1_if $host2_if
1020         check_err $? "Packet flooded when should not"
1021
1022         bridge link set dev $br_port mcast_flood on
1023
1024         flood_test_do true $mac $ip $host1_if $host2_if
1025         check_err $? "Packet was not flooded when should"
1026
1027         log_test "Unregistered multicast flood"
1028 }
1029
1030 flood_test()
1031 {
1032         # `br_port` is connected to `host2_if`
1033         local br_port=$1
1034         local host1_if=$2
1035         local host2_if=$3
1036
1037         flood_unicast_test $br_port $host1_if $host2_if
1038         flood_multicast_test $br_port $host1_if $host2_if
1039 }