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
45# Old versions of tc don't understand "mpls_uc"
46check_tc_mpls_support()
47{
48 local dev=$1; shift
49
50 tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \
51 matchall action pipe &> /dev/null
52 if [[ $? -ne 0 ]]; then
53 echo "SKIP: iproute2 too old; tc is missing MPLS support"
54 return 1
55 fi
56 tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \
57 matchall
58}
59
60# Old versions of tc produce invalid json output for mpls lse statistics
61check_tc_mpls_lse_stats()
62{
63 local dev=$1; shift
64 local ret;
65
66 tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \
67 flower mpls lse depth 2 \
68 action continue &> /dev/null
69
70 if [[ $? -ne 0 ]]; then
71 echo "SKIP: iproute2 too old; tc-flower is missing extended MPLS support"
72 return 1
73 fi
74
75 tc -j filter show dev $dev ingress protocol mpls_uc | jq . &> /dev/null
76 ret=$?
77 tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \
78 flower
79
80 if [[ $ret -ne 0 ]]; then
81 echo "SKIP: iproute2 too old; tc-flower produces invalid json output for extended MPLS filters"
82 return 1
83 fi
84}
85
86check_tc_shblock_support()
87{
88 tc filter help 2>&1 | grep block &> /dev/null
89 if [[ $? -ne 0 ]]; then
90 echo "SKIP: iproute2 too old; tc is missing shared block support"
91 exit 1
92 fi
93}
94
95check_tc_chain_support()
96{
97 tc help 2>&1|grep chain &> /dev/null
98 if [[ $? -ne 0 ]]; then
99 echo "SKIP: iproute2 too old; tc is missing chain support"
100 exit 1
101 fi
102}
103
104check_tc_action_hw_stats_support()
105{
106 tc actions help 2>&1 | grep -q hw_stats
107 if [[ $? -ne 0 ]]; then
108 echo "SKIP: iproute2 too old; tc is missing action hw_stats support"
109 exit 1
110 fi
111}
112
113check_ethtool_lanes_support()
114{
115 ethtool --help 2>&1| grep lanes &> /dev/null
116 if [[ $? -ne 0 ]]; then
117 echo "SKIP: ethtool too old; it is missing lanes support"
118 exit 1
119 fi
120}
121
122if [[ "$(id -u)" -ne 0 ]]; then
123 echo "SKIP: need root privileges"
124 exit 0
125fi
126
127if [[ "$CHECK_TC" = "yes" ]]; then
128 check_tc_version
129fi
130
131require_command()
132{
133 local cmd=$1; shift
134
135 if [[ ! -x "$(command -v "$cmd")" ]]; then
136 echo "SKIP: $cmd not installed"
137 exit 1
138 fi
139}
140
141require_command jq
142require_command $MZ
143
144if [[ ! -v NUM_NETIFS ]]; then
145 echo "SKIP: importer does not define \"NUM_NETIFS\""
146 exit 1
147fi
148
149##############################################################################
150# Command line options handling
151
152count=0
153
154while [[ $# -gt 0 ]]; do
155 if [[ "$count" -eq "0" ]]; then
156 unset NETIFS
157 declare -A NETIFS
158 fi
159 count=$((count + 1))
160 NETIFS[p$count]="$1"
161 shift
162done
163
164##############################################################################
165# Network interfaces configuration
166
167create_netif_veth()
168{
169 local i
170
171 for ((i = 1; i <= NUM_NETIFS; ++i)); do
172 local j=$((i+1))
173
174 ip link show dev ${NETIFS[p$i]} &> /dev/null
175 if [[ $? -ne 0 ]]; then
176 ip link add ${NETIFS[p$i]} type veth \
177 peer name ${NETIFS[p$j]}
178 if [[ $? -ne 0 ]]; then
179 echo "Failed to create netif"
180 exit 1
181 fi
182 fi
183 i=$j
184 done
185}
186
187create_netif()
188{
189 case "$NETIF_TYPE" in
190 veth) create_netif_veth
191 ;;
192 *) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
193 exit 1
194 ;;
195 esac
196}
197
198if [[ "$NETIF_CREATE" = "yes" ]]; then
199 create_netif
200fi
201
202for ((i = 1; i <= NUM_NETIFS; ++i)); do
203 ip link show dev ${NETIFS[p$i]} &> /dev/null
204 if [[ $? -ne 0 ]]; then
205 echo "SKIP: could not find all required interfaces"
206 exit 1
207 fi
208done
209
210##############################################################################
211# Helpers
212
213# Exit status to return at the end. Set in case one of the tests fails.
214EXIT_STATUS=0
215# Per-test return value. Clear at the beginning of each test.
216RET=0
217
218check_err()
219{
220 local err=$1
221 local msg=$2
222
223 if [[ $RET -eq 0 && $err -ne 0 ]]; then
224 RET=$err
225 retmsg=$msg
226 fi
227}
228
229check_fail()
230{
231 local err=$1
232 local msg=$2
233
234 if [[ $RET -eq 0 && $err -eq 0 ]]; then
235 RET=1
236 retmsg=$msg
237 fi
238}
239
240check_err_fail()
241{
242 local should_fail=$1; shift
243 local err=$1; shift
244 local what=$1; shift
245
246 if ((should_fail)); then
247 check_fail $err "$what succeeded, but should have failed"
248 else
249 check_err $err "$what failed"
250 fi
251}
252
253log_test()
254{
255 local test_name=$1
256 local opt_str=$2
257
258 if [[ $# -eq 2 ]]; then
259 opt_str="($opt_str)"
260 fi
261
262 if [[ $RET -ne 0 ]]; then
263 EXIT_STATUS=1
264 printf "TEST: %-60s [FAIL]\n" "$test_name $opt_str"
265 if [[ ! -z "$retmsg" ]]; then
266 printf "\t%s\n" "$retmsg"
267 fi
268 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
269 echo "Hit enter to continue, 'q' to quit"
270 read a
271 [ "$a" = "q" ] && exit 1
272 fi
273 return 1
274 fi
275
276 printf "TEST: %-60s [ OK ]\n" "$test_name $opt_str"
277 return 0
278}
279
280log_info()
281{
282 local msg=$1
283
284 echo "INFO: $msg"
285}
286
287busywait()
288{
289 local timeout=$1; shift
290
291 local start_time="$(date -u +%s%3N)"
292 while true
293 do
294 local out
295 out=$("$@")
296 local ret=$?
297 if ((!ret)); then
298 echo -n "$out"
299 return 0
300 fi
301
302 local current_time="$(date -u +%s%3N)"
303 if ((current_time - start_time > timeout)); then
304 echo -n "$out"
305 return 1
306 fi
307 done
308}
309
310not()
311{
312 "$@"
313 [[ $? != 0 ]]
314}
315
316get_max()
317{
318 local arr=("$@")
319
320 max=${arr[0]}
321 for cur in ${arr[@]}; do
322 if [[ $cur -gt $max ]]; then
323 max=$cur
324 fi
325 done
326
327 echo $max
328}
329
330grep_bridge_fdb()
331{
332 local addr=$1; shift
333 local word
334 local flag
335
336 if [ "$1" == "self" ] || [ "$1" == "master" ]; then
337 word=$1; shift
338 if [ "$1" == "-v" ]; then
339 flag=$1; shift
340 fi
341 fi
342
343 $@ | grep $addr | grep $flag "$word"
344}
345
346wait_for_port_up()
347{
348 "$@" | grep -q "Link detected: yes"
349}
350
351wait_for_offload()
352{
353 "$@" | grep -q offload
354}
355
356until_counter_is()
357{
358 local expr=$1; shift
359 local current=$("$@")
360
361 echo $((current))
362 ((current $expr))
363}
364
365busywait_for_counter()
366{
367 local timeout=$1; shift
368 local delta=$1; shift
369
370 local base=$("$@")
371 busywait "$timeout" until_counter_is ">= $((base + delta))" "$@"
372}
373
374setup_wait_dev()
375{
376 local dev=$1; shift
377 local wait_time=${1:-$WAIT_TIME}; shift
378
379 setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time
380
381 if (($?)); then
382 check_err 1
383 log_test setup_wait_dev ": Interface $dev does not come up."
384 exit 1
385 fi
386}
387
388setup_wait_dev_with_timeout()
389{
390 local dev=$1; shift
391 local max_iterations=${1:-$WAIT_TIMEOUT}; shift
392 local wait_time=${1:-$WAIT_TIME}; shift
393 local i
394
395 for ((i = 1; i <= $max_iterations; ++i)); do
396 ip link show dev $dev up \
397 | grep 'state UP' &> /dev/null
398 if [[ $? -ne 0 ]]; then
399 sleep 1
400 else
401 sleep $wait_time
402 return 0
403 fi
404 done
405
406 return 1
407}
408
409setup_wait()
410{
411 local num_netifs=${1:-$NUM_NETIFS}
412 local i
413
414 for ((i = 1; i <= num_netifs; ++i)); do
415 setup_wait_dev ${NETIFS[p$i]} 0
416 done
417
418 # Make sure links are ready.
419 sleep $WAIT_TIME
420}
421
422cmd_jq()
423{
424 local cmd=$1
425 local jq_exp=$2
426 local jq_opts=$3
427 local ret
428 local output
429
430 output="$($cmd)"
431 # it the command fails, return error right away
432 ret=$?
433 if [[ $ret -ne 0 ]]; then
434 return $ret
435 fi
436 output=$(echo $output | jq -r $jq_opts "$jq_exp")
437 ret=$?
438 if [[ $ret -ne 0 ]]; then
439 return $ret
440 fi
441 echo $output
442 # return success only in case of non-empty output
443 [ ! -z "$output" ]
444}
445
446lldpad_app_wait_set()
447{
448 local dev=$1; shift
449
450 while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do
451 echo "$dev: waiting for lldpad to push pending APP updates"
452 sleep 5
453 done
454}
455
456lldpad_app_wait_del()
457{
458 # Give lldpad a chance to push down the changes. If the device is downed
459 # too soon, the updates will be left pending. However, they will have
460 # been struck off the lldpad's DB already, so we won't be able to tell
461 # they are pending. Then on next test iteration this would cause
462 # weirdness as newly-added APP rules conflict with the old ones,
463 # sometimes getting stuck in an "unknown" state.
464 sleep 5
465}
466
467pre_cleanup()
468{
469 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
470 echo "Pausing before cleanup, hit any key to continue"
471 read
472 fi
473}
474
475vrf_prepare()
476{
477 ip -4 rule add pref 32765 table local
478 ip -4 rule del pref 0
479 ip -6 rule add pref 32765 table local
480 ip -6 rule del pref 0
481}
482
483vrf_cleanup()
484{
485 ip -6 rule add pref 0 table local
486 ip -6 rule del pref 32765
487 ip -4 rule add pref 0 table local
488 ip -4 rule del pref 32765
489}
490
491__last_tb_id=0
492declare -A __TB_IDS
493
494__vrf_td_id_assign()
495{
496 local vrf_name=$1
497
498 __last_tb_id=$((__last_tb_id + 1))
499 __TB_IDS[$vrf_name]=$__last_tb_id
500 return $__last_tb_id
501}
502
503__vrf_td_id_lookup()
504{
505 local vrf_name=$1
506
507 return ${__TB_IDS[$vrf_name]}
508}
509
510vrf_create()
511{
512 local vrf_name=$1
513 local tb_id
514
515 __vrf_td_id_assign $vrf_name
516 tb_id=$?
517
518 ip link add dev $vrf_name type vrf table $tb_id
519 ip -4 route add table $tb_id unreachable default metric 4278198272
520 ip -6 route add table $tb_id unreachable default metric 4278198272
521}
522
523vrf_destroy()
524{
525 local vrf_name=$1
526 local tb_id
527
528 __vrf_td_id_lookup $vrf_name
529 tb_id=$?
530
531 ip -6 route del table $tb_id unreachable default metric 4278198272
532 ip -4 route del table $tb_id unreachable default metric 4278198272
533 ip link del dev $vrf_name
534}
535
536__addr_add_del()
537{
538 local if_name=$1
539 local add_del=$2
540 local array
541
542 shift
543 shift
544 array=("${@}")
545
546 for addrstr in "${array[@]}"; do
547 ip address $add_del $addrstr dev $if_name
548 done
549}
550
551__simple_if_init()
552{
553 local if_name=$1; shift
554 local vrf_name=$1; shift
555 local addrs=("${@}")
556
557 ip link set dev $if_name master $vrf_name
558 ip link set dev $if_name up
559
560 __addr_add_del $if_name add "${addrs[@]}"
561}
562
563__simple_if_fini()
564{
565 local if_name=$1; shift
566 local addrs=("${@}")
567
568 __addr_add_del $if_name del "${addrs[@]}"
569
570 ip link set dev $if_name down
571 ip link set dev $if_name nomaster
572}
573
574simple_if_init()
575{
576 local if_name=$1
577 local vrf_name
578 local array
579
580 shift
581 vrf_name=v$if_name
582 array=("${@}")
583
584 vrf_create $vrf_name
585 ip link set dev $vrf_name up
586 __simple_if_init $if_name $vrf_name "${array[@]}"
587}
588
589simple_if_fini()
590{
591 local if_name=$1
592 local vrf_name
593 local array
594
595 shift
596 vrf_name=v$if_name
597 array=("${@}")
598
599 __simple_if_fini $if_name "${array[@]}"
600 vrf_destroy $vrf_name
601}
602
603tunnel_create()
604{
605 local name=$1; shift
606 local type=$1; shift
607 local local=$1; shift
608 local remote=$1; shift
609
610 ip link add name $name type $type \
611 local $local remote $remote "$@"
612 ip link set dev $name up
613}
614
615tunnel_destroy()
616{
617 local name=$1; shift
618
619 ip link del dev $name
620}
621
622vlan_create()
623{
624 local if_name=$1; shift
625 local vid=$1; shift
626 local vrf=$1; shift
627 local ips=("${@}")
628 local name=$if_name.$vid
629
630 ip link add name $name link $if_name type vlan id $vid
631 if [ "$vrf" != "" ]; then
632 ip link set dev $name master $vrf
633 fi
634 ip link set dev $name up
635 __addr_add_del $name add "${ips[@]}"
636}
637
638vlan_destroy()
639{
640 local if_name=$1; shift
641 local vid=$1; shift
642 local name=$if_name.$vid
643
644 ip link del dev $name
645}
646
647team_create()
648{
649 local if_name=$1; shift
650 local mode=$1; shift
651
652 require_command $TEAMD
653 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
654 for slave in "$@"; do
655 ip link set dev $slave down
656 ip link set dev $slave master $if_name
657 ip link set dev $slave up
658 done
659 ip link set dev $if_name up
660}
661
662team_destroy()
663{
664 local if_name=$1; shift
665
666 $TEAMD -t $if_name -k
667}
668
669master_name_get()
670{
671 local if_name=$1
672
673 ip -j link show dev $if_name | jq -r '.[]["master"]'
674}
675
676link_stats_get()
677{
678 local if_name=$1; shift
679 local dir=$1; shift
680 local stat=$1; shift
681
682 ip -j -s link show dev $if_name \
683 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]'
684}
685
686link_stats_tx_packets_get()
687{
688 link_stats_get $1 tx packets
689}
690
691link_stats_rx_errors_get()
692{
693 link_stats_get $1 rx errors
694}
695
696tc_rule_stats_get()
697{
698 local dev=$1; shift
699 local pref=$1; shift
700 local dir=$1; shift
701 local selector=${1:-.packets}; shift
702
703 tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \
704 | jq ".[1].options.actions[].stats$selector"
705}
706
707tc_rule_handle_stats_get()
708{
709 local id=$1; shift
710 local handle=$1; shift
711 local selector=${1:-.packets}; shift
712
713 tc -j -s filter show $id \
714 | jq ".[] | select(.options.handle == $handle) | \
715 .options.actions[0].stats$selector"
716}
717
718ethtool_stats_get()
719{
720 local dev=$1; shift
721 local stat=$1; shift
722
723 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
724}
725
726qdisc_stats_get()
727{
728 local dev=$1; shift
729 local handle=$1; shift
730 local selector=$1; shift
731
732 tc -j -s qdisc show dev "$dev" \
733 | jq '.[] | select(.handle == "'"$handle"'") | '"$selector"
734}
735
736qdisc_parent_stats_get()
737{
738 local dev=$1; shift
739 local parent=$1; shift
740 local selector=$1; shift
741
742 tc -j -s qdisc show dev "$dev" invisible \
743 | jq '.[] | select(.parent == "'"$parent"'") | '"$selector"
744}
745
746humanize()
747{
748 local speed=$1; shift
749
750 for unit in bps Kbps Mbps Gbps; do
751 if (($(echo "$speed < 1024" | bc))); then
752 break
753 fi
754
755 speed=$(echo "scale=1; $speed / 1024" | bc)
756 done
757
758 echo "$speed${unit}"
759}
760
761rate()
762{
763 local t0=$1; shift
764 local t1=$1; shift
765 local interval=$1; shift
766
767 echo $((8 * (t1 - t0) / interval))
768}
769
770mac_get()
771{
772 local if_name=$1
773
774 ip -j link show dev $if_name | jq -r '.[]["address"]'
775}
776
777bridge_ageing_time_get()
778{
779 local bridge=$1
780 local ageing_time
781
782 # Need to divide by 100 to convert to seconds.
783 ageing_time=$(ip -j -d link show dev $bridge \
784 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
785 echo $((ageing_time / 100))
786}
787
788declare -A SYSCTL_ORIG
789sysctl_set()
790{
791 local key=$1; shift
792 local value=$1; shift
793
794 SYSCTL_ORIG[$key]=$(sysctl -n $key)
795 sysctl -qw $key=$value
796}
797
798sysctl_restore()
799{
800 local key=$1; shift
801
802 sysctl -qw $key=${SYSCTL_ORIG["$key"]}
803}
804
805forwarding_enable()
806{
807 sysctl_set net.ipv4.conf.all.forwarding 1
808 sysctl_set net.ipv6.conf.all.forwarding 1
809}
810
811forwarding_restore()
812{
813 sysctl_restore net.ipv6.conf.all.forwarding
814 sysctl_restore net.ipv4.conf.all.forwarding
815}
816
817declare -A MTU_ORIG
818mtu_set()
819{
820 local dev=$1; shift
821 local mtu=$1; shift
822
823 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')
824 ip link set dev $dev mtu $mtu
825}
826
827mtu_restore()
828{
829 local dev=$1; shift
830
831 ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
832}
833
834tc_offload_check()
835{
836 local num_netifs=${1:-$NUM_NETIFS}
837
838 for ((i = 1; i <= num_netifs; ++i)); do
839 ethtool -k ${NETIFS[p$i]} \
840 | grep "hw-tc-offload: on" &> /dev/null
841 if [[ $? -ne 0 ]]; then
842 return 1
843 fi
844 done
845
846 return 0
847}
848
849trap_install()
850{
851 local dev=$1; shift
852 local direction=$1; shift
853
854 # Some devices may not support or need in-hardware trapping of traffic
855 # (e.g. the veth pairs that this library creates for non-existent
856 # loopbacks). Use continue instead, so that there is a filter in there
857 # (some tests check counters), and so that other filters are still
858 # processed.
859 tc filter add dev $dev $direction pref 1 \
860 flower skip_sw action trap 2>/dev/null \
861 || tc filter add dev $dev $direction pref 1 \
862 flower action continue
863}
864
865trap_uninstall()
866{
867 local dev=$1; shift
868 local direction=$1; shift
869
870 tc filter del dev $dev $direction pref 1 flower
871}
872
873slow_path_trap_install()
874{
875 # For slow-path testing, we need to install a trap to get to
876 # slow path the packets that would otherwise be switched in HW.
877 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
878 trap_install "$@"
879 fi
880}
881
882slow_path_trap_uninstall()
883{
884 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
885 trap_uninstall "$@"
886 fi
887}
888
889__icmp_capture_add_del()
890{
891 local add_del=$1; shift
892 local pref=$1; shift
893 local vsuf=$1; shift
894 local tundev=$1; shift
895 local filter=$1; shift
896
897 tc filter $add_del dev "$tundev" ingress \
898 proto ip$vsuf pref $pref \
899 flower ip_proto icmp$vsuf $filter \
900 action pass
901}
902
903icmp_capture_install()
904{
905 __icmp_capture_add_del add 100 "" "$@"
906}
907
908icmp_capture_uninstall()
909{
910 __icmp_capture_add_del del 100 "" "$@"
911}
912
913icmp6_capture_install()
914{
915 __icmp_capture_add_del add 100 v6 "$@"
916}
917
918icmp6_capture_uninstall()
919{
920 __icmp_capture_add_del del 100 v6 "$@"
921}
922
923__vlan_capture_add_del()
924{
925 local add_del=$1; shift
926 local pref=$1; shift
927 local dev=$1; shift
928 local filter=$1; shift
929
930 tc filter $add_del dev "$dev" ingress \
931 proto 802.1q pref $pref \
932 flower $filter \
933 action pass
934}
935
936vlan_capture_install()
937{
938 __vlan_capture_add_del add 100 "$@"
939}
940
941vlan_capture_uninstall()
942{
943 __vlan_capture_add_del del 100 "$@"
944}
945
946__dscp_capture_add_del()
947{
948 local add_del=$1; shift
949 local dev=$1; shift
950 local base=$1; shift
951 local dscp;
952
953 for prio in {0..7}; do
954 dscp=$((base + prio))
955 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
956 "skip_hw ip_tos $((dscp << 2))"
957 done
958}
959
960dscp_capture_install()
961{
962 local dev=$1; shift
963 local base=$1; shift
964
965 __dscp_capture_add_del add $dev $base
966}
967
968dscp_capture_uninstall()
969{
970 local dev=$1; shift
971 local base=$1; shift
972
973 __dscp_capture_add_del del $dev $base
974}
975
976dscp_fetch_stats()
977{
978 local dev=$1; shift
979 local base=$1; shift
980
981 for prio in {0..7}; do
982 local dscp=$((base + prio))
983 local t=$(tc_rule_stats_get $dev $((dscp + 100)))
984 echo "[$dscp]=$t "
985 done
986}
987
988matchall_sink_create()
989{
990 local dev=$1; shift
991
992 tc qdisc add dev $dev clsact
993 tc filter add dev $dev ingress \
994 pref 10000 \
995 matchall \
996 action drop
997}
998
999tests_run()
1000{
1001 local current_test
1002
1003 for current_test in ${TESTS:-$ALL_TESTS}; do
1004 $current_test
1005 done
1006}
1007
1008multipath_eval()
1009{
1010 local desc="$1"
1011 local weight_rp12=$2
1012 local weight_rp13=$3
1013 local packets_rp12=$4
1014 local packets_rp13=$5
1015 local weights_ratio packets_ratio diff
1016
1017 RET=0
1018
1019 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1020 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
1021 | bc -l)
1022 else
1023 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
1024 | bc -l)
1025 fi
1026
1027 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
1028 check_err 1 "Packet difference is 0"
1029 log_test "Multipath"
1030 log_info "Expected ratio $weights_ratio"
1031 return
1032 fi
1033
1034 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1035 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
1036 | bc -l)
1037 else
1038 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
1039 | bc -l)
1040 fi
1041
1042 diff=$(echo $weights_ratio - $packets_ratio | bc -l)
1043 diff=${diff#-}
1044
1045 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
1046 check_err $? "Too large discrepancy between expected and measured ratios"
1047 log_test "$desc"
1048 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
1049}
1050
1051in_ns()
1052{
1053 local name=$1; shift
1054
1055 ip netns exec $name bash <<-EOF
1056 NUM_NETIFS=0
1057 source lib.sh
1058 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
1059 EOF
1060}
1061
1062##############################################################################
1063# Tests
1064
1065ping_do()
1066{
1067 local if_name=$1
1068 local dip=$2
1069 local args=$3
1070 local vrf_name
1071
1072 vrf_name=$(master_name_get $if_name)
1073 ip vrf exec $vrf_name \
1074 $PING $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null
1075}
1076
1077ping_test()
1078{
1079 RET=0
1080
1081 ping_do $1 $2
1082 check_err $?
1083 log_test "ping$3"
1084}
1085
1086ping6_do()
1087{
1088 local if_name=$1
1089 local dip=$2
1090 local args=$3
1091 local vrf_name
1092
1093 vrf_name=$(master_name_get $if_name)
1094 ip vrf exec $vrf_name \
1095 $PING6 $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null
1096}
1097
1098ping6_test()
1099{
1100 RET=0
1101
1102 ping6_do $1 $2
1103 check_err $?
1104 log_test "ping6$3"
1105}
1106
1107learning_test()
1108{
1109 local bridge=$1
1110 local br_port1=$2 # Connected to `host1_if`.
1111 local host1_if=$3
1112 local host2_if=$4
1113 local mac=de:ad:be:ef:13:37
1114 local ageing_time
1115
1116 RET=0
1117
1118 bridge -j fdb show br $bridge brport $br_port1 \
1119 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1120 check_fail $? "Found FDB record when should not"
1121
1122 # Disable unknown unicast flooding on `br_port1` to make sure
1123 # packets are only forwarded through the port after a matching
1124 # FDB entry was installed.
1125 bridge link set dev $br_port1 flood off
1126
1127 tc qdisc add dev $host1_if ingress
1128 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
1129 flower dst_mac $mac action drop
1130
1131 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1132 sleep 1
1133
1134 tc -j -s filter show dev $host1_if ingress \
1135 | jq -e ".[] | select(.options.handle == 101) \
1136 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1137 check_fail $? "Packet reached second host when should not"
1138
1139 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1140 sleep 1
1141
1142 bridge -j fdb show br $bridge brport $br_port1 \
1143 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1144 check_err $? "Did not find FDB record when should"
1145
1146 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1147 sleep 1
1148
1149 tc -j -s filter show dev $host1_if ingress \
1150 | jq -e ".[] | select(.options.handle == 101) \
1151 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1152 check_err $? "Packet did not reach second host when should"
1153
1154 # Wait for 10 seconds after the ageing time to make sure FDB
1155 # record was aged-out.
1156 ageing_time=$(bridge_ageing_time_get $bridge)
1157 sleep $((ageing_time + 10))
1158
1159 bridge -j fdb show br $bridge brport $br_port1 \
1160 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1161 check_fail $? "Found FDB record when should not"
1162
1163 bridge link set dev $br_port1 learning off
1164
1165 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1166 sleep 1
1167
1168 bridge -j fdb show br $bridge brport $br_port1 \
1169 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1170 check_fail $? "Found FDB record when should not"
1171
1172 bridge link set dev $br_port1 learning on
1173
1174 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
1175 tc qdisc del dev $host1_if ingress
1176
1177 bridge link set dev $br_port1 flood on
1178
1179 log_test "FDB learning"
1180}
1181
1182flood_test_do()
1183{
1184 local should_flood=$1
1185 local mac=$2
1186 local ip=$3
1187 local host1_if=$4
1188 local host2_if=$5
1189 local err=0
1190
1191 # Add an ACL on `host2_if` which will tell us whether the packet
1192 # was flooded to it or not.
1193 tc qdisc add dev $host2_if ingress
1194 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
1195 flower dst_mac $mac action drop
1196
1197 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
1198 sleep 1
1199
1200 tc -j -s filter show dev $host2_if ingress \
1201 | jq -e ".[] | select(.options.handle == 101) \
1202 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1203 if [[ $? -ne 0 && $should_flood == "true" || \
1204 $? -eq 0 && $should_flood == "false" ]]; then
1205 err=1
1206 fi
1207
1208 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
1209 tc qdisc del dev $host2_if ingress
1210
1211 return $err
1212}
1213
1214flood_unicast_test()
1215{
1216 local br_port=$1
1217 local host1_if=$2
1218 local host2_if=$3
1219 local mac=de:ad:be:ef:13:37
1220 local ip=192.0.2.100
1221
1222 RET=0
1223
1224 bridge link set dev $br_port flood off
1225
1226 flood_test_do false $mac $ip $host1_if $host2_if
1227 check_err $? "Packet flooded when should not"
1228
1229 bridge link set dev $br_port flood on
1230
1231 flood_test_do true $mac $ip $host1_if $host2_if
1232 check_err $? "Packet was not flooded when should"
1233
1234 log_test "Unknown unicast flood"
1235}
1236
1237flood_multicast_test()
1238{
1239 local br_port=$1
1240 local host1_if=$2
1241 local host2_if=$3
1242 local mac=01:00:5e:00:00:01
1243 local ip=239.0.0.1
1244
1245 RET=0
1246
1247 bridge link set dev $br_port mcast_flood off
1248
1249 flood_test_do false $mac $ip $host1_if $host2_if
1250 check_err $? "Packet flooded when should not"
1251
1252 bridge link set dev $br_port mcast_flood on
1253
1254 flood_test_do true $mac $ip $host1_if $host2_if
1255 check_err $? "Packet was not flooded when should"
1256
1257 log_test "Unregistered multicast flood"
1258}
1259
1260flood_test()
1261{
1262 # `br_port` is connected to `host2_if`
1263 local br_port=$1
1264 local host1_if=$2
1265 local host2_if=$3
1266
1267 flood_unicast_test $br_port $host1_if $host2_if
1268 flood_multicast_test $br_port $host1_if $host2_if
1269}
1270
1271__start_traffic()
1272{
1273 local proto=$1; shift
1274 local h_in=$1; shift # Where the traffic egresses the host
1275 local sip=$1; shift
1276 local dip=$1; shift
1277 local dmac=$1; shift
1278
1279 $MZ $h_in -p 8000 -A $sip -B $dip -c 0 \
1280 -a own -b $dmac -t "$proto" -q "$@" &
1281 sleep 1
1282}
1283
1284start_traffic()
1285{
1286 __start_traffic udp "$@"
1287}
1288
1289start_tcp_traffic()
1290{
1291 __start_traffic tcp "$@"
1292}
1293
1294stop_traffic()
1295{
1296 # Suppress noise from killing mausezahn.
1297 { kill %% && wait %%; } 2>/dev/null
1298}
1299
1300tcpdump_start()
1301{
1302 local if_name=$1; shift
1303 local ns=$1; shift
1304
1305 capfile=$(mktemp)
1306 capout=$(mktemp)
1307
1308 if [ -z $ns ]; then
1309 ns_cmd=""
1310 else
1311 ns_cmd="ip netns exec ${ns}"
1312 fi
1313
1314 if [ -z $SUDO_USER ] ; then
1315 capuser=""
1316 else
1317 capuser="-Z $SUDO_USER"
1318 fi
1319
1320 $ns_cmd tcpdump -e -n -Q in -i $if_name \
1321 -s 65535 -B 32768 $capuser -w $capfile > "$capout" 2>&1 &
1322 cappid=$!
1323
1324 sleep 1
1325}
1326
1327tcpdump_stop()
1328{
1329 $ns_cmd kill $cappid
1330 sleep 1
1331}
1332
1333tcpdump_cleanup()
1334{
1335 rm $capfile $capout
1336}
1337
1338tcpdump_show()
1339{
1340 tcpdump -e -n -r $capfile 2>&1
1341}
1342
1343# return 0 if the packet wasn't seen on host2_if or 1 if it was
1344mcast_packet_test()
1345{
1346 local mac=$1
1347 local src_ip=$2
1348 local ip=$3
1349 local host1_if=$4
1350 local host2_if=$5
1351 local seen=0
1352 local tc_proto="ip"
1353 local mz_v6arg=""
1354
1355 # basic check to see if we were passed an IPv4 address, if not assume IPv6
1356 if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
1357 tc_proto="ipv6"
1358 mz_v6arg="-6"
1359 fi
1360
1361 # Add an ACL on `host2_if` which will tell us whether the packet
1362 # was received by it or not.
1363 tc qdisc add dev $host2_if ingress
1364 tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \
1365 flower ip_proto udp dst_mac $mac action drop
1366
1367 $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q
1368 sleep 1
1369
1370 tc -j -s filter show dev $host2_if ingress \
1371 | jq -e ".[] | select(.options.handle == 101) \
1372 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1373 if [[ $? -eq 0 ]]; then
1374 seen=1
1375 fi
1376
1377 tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower
1378 tc qdisc del dev $host2_if ingress
1379
1380 return $seen
1381}
1382
1383brmcast_check_sg_entries()
1384{
1385 local report=$1; shift
1386 local slist=("$@")
1387 local sarg=""
1388
1389 for src in "${slist[@]}"; do
1390 sarg="${sarg} and .source_list[].address == \"$src\""
1391 done
1392 bridge -j -d -s mdb show dev br0 \
1393 | jq -e ".[].mdb[] | \
1394 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null
1395 check_err $? "Wrong *,G entry source list after $report report"
1396
1397 for sgent in "${slist[@]}"; do
1398 bridge -j -d -s mdb show dev br0 \
1399 | jq -e ".[].mdb[] | \
1400 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null
1401 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)"
1402 done
1403}
1404
1405brmcast_check_sg_fwding()
1406{
1407 local should_fwd=$1; shift
1408 local sources=("$@")
1409
1410 for src in "${sources[@]}"; do
1411 local retval=0
1412
1413 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1
1414 retval=$?
1415 if [ $should_fwd -eq 1 ]; then
1416 check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)"
1417 else
1418 check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)"
1419 fi
1420 done
1421}
1422
1423brmcast_check_sg_state()
1424{
1425 local is_blocked=$1; shift
1426 local sources=("$@")
1427 local should_fail=1
1428
1429 if [ $is_blocked -eq 1 ]; then
1430 should_fail=0
1431 fi
1432
1433 for src in "${sources[@]}"; do
1434 bridge -j -d -s mdb show dev br0 \
1435 | jq -e ".[].mdb[] | \
1436 select(.grp == \"$TEST_GROUP\" and .source_list != null) |
1437 .source_list[] |
1438 select(.address == \"$src\") |
1439 select(.timer == \"0.00\")" &>/dev/null
1440 check_err_fail $should_fail $? "Entry $src has zero timer"
1441
1442 bridge -j -d -s mdb show dev br0 \
1443 | jq -e ".[].mdb[] | \
1444 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \
1445 .flags[] == \"blocked\")" &>/dev/null
1446 check_err_fail $should_fail $? "Entry $src has blocked flag"
1447 done
1448}