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# Topology description. p1 looped back to p2, p3 to p4 and so on.
6
7declare -A NETIFS=(
8 [p1]=veth0
9 [p2]=veth1
10 [p3]=veth2
11 [p4]=veth3
12 [p5]=veth4
13 [p6]=veth5
14 [p7]=veth6
15 [p8]=veth7
16 [p9]=veth8
17 [p10]=veth9
18)
19
20# Port that does not have a cable connected.
21: "${NETIF_NO_CABLE:=eth8}"
22
23##############################################################################
24# Defines
25
26# Networking utilities.
27: "${PING:=ping}"
28: "${PING6:=ping6}" # Some distros just use ping.
29: "${ARPING:=arping}"
30: "${TROUTE6:=traceroute6}"
31
32# Packet generator.
33: "${MZ:=mausezahn}" # Some distributions use 'mz'.
34: "${MZ_DELAY:=0}"
35
36# Host configuration tools.
37: "${TEAMD:=teamd}"
38: "${MCD:=smcrouted}"
39: "${MC_CLI:=smcroutectl}"
40: "${MCD_TABLE_NAME:=selftests}"
41
42# Constants for netdevice bring-up:
43# Default time in seconds to wait for an interface to come up before giving up
44# and bailing out. Used during initial setup.
45: "${INTERFACE_TIMEOUT:=600}"
46# Like INTERFACE_TIMEOUT, but default for ad-hoc waiting in testing scripts.
47: "${WAIT_TIMEOUT:=20}"
48# Time to wait after interfaces participating in the test are all UP.
49: "${WAIT_TIME:=5}"
50
51# Whether to pause on, respectively, after a failure and before cleanup.
52: "${PAUSE_ON_CLEANUP:=no}"
53
54# Whether to create virtual interfaces, and what netdevice type they should be.
55: "${NETIF_CREATE:=yes}"
56: "${NETIF_TYPE:=veth}"
57
58# Constants for ping tests:
59# How many packets should be sent.
60: "${PING_COUNT:=10}"
61# Timeout (in seconds) before ping exits regardless of how many packets have
62# been sent or received
63: "${PING_TIMEOUT:=5}"
64
65# Minimum ageing_time (in centiseconds) supported by hardware
66: "${LOW_AGEING_TIME:=1000}"
67
68# Whether to check for availability of certain tools.
69: "${REQUIRE_JQ:=yes}"
70: "${REQUIRE_MZ:=yes}"
71: "${REQUIRE_MTOOLS:=no}"
72: "${REQUIRE_TEAMD:=no}"
73
74# Whether to override MAC addresses on interfaces participating in the test.
75: "${STABLE_MAC_ADDRS:=no}"
76
77# Flags for tcpdump
78: "${TCPDUMP_EXTRA_FLAGS:=}"
79
80# Flags for TC filters.
81: "${TC_FLAG:=skip_hw}"
82
83# Whether the machine is "slow" -- i.e. might be incapable of running tests
84# involving heavy traffic. This might be the case on a debug kernel, a VM, or
85# e.g. a low-power board.
86: "${KSFT_MACHINE_SLOW:=no}"
87
88##############################################################################
89# Find netifs by test-specified driver name
90
91driver_name_get()
92{
93 local dev=$1; shift
94 local driver_path="/sys/class/net/$dev/device/driver"
95
96 if [[ -L $driver_path ]]; then
97 basename `realpath $driver_path`
98 fi
99}
100
101netif_find_driver()
102{
103 local ifnames=`ip -j link show | jq -r ".[].ifname"`
104 local count=0
105
106 for ifname in $ifnames
107 do
108 local driver_name=`driver_name_get $ifname`
109 if [[ ! -z $driver_name && $driver_name == $NETIF_FIND_DRIVER ]]; then
110 count=$((count + 1))
111 NETIFS[p$count]="$ifname"
112 fi
113 done
114}
115
116# Whether to find netdevice according to the driver speficied by the importer
117: "${NETIF_FIND_DRIVER:=}"
118
119if [[ $NETIF_FIND_DRIVER ]]; then
120 unset NETIFS
121 declare -A NETIFS
122 netif_find_driver
123fi
124
125net_forwarding_dir=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
126
127if [[ -f $net_forwarding_dir/forwarding.config ]]; then
128 source "$net_forwarding_dir/forwarding.config"
129fi
130
131source "$net_forwarding_dir/../lib.sh"
132
133##############################################################################
134# Sanity checks
135
136check_tc_version()
137{
138 tc -j &> /dev/null
139 if [[ $? -ne 0 ]]; then
140 echo "SKIP: iproute2 too old; tc is missing JSON support"
141 exit $ksft_skip
142 fi
143}
144
145check_tc_erspan_support()
146{
147 local dev=$1; shift
148
149 tc filter add dev $dev ingress pref 1 handle 1 flower \
150 erspan_opts 1:0:0:0 &> /dev/null
151 if [[ $? -ne 0 ]]; then
152 echo "SKIP: iproute2 too old; tc is missing erspan support"
153 return $ksft_skip
154 fi
155 tc filter del dev $dev ingress pref 1 handle 1 flower \
156 erspan_opts 1:0:0:0 &> /dev/null
157}
158
159# Old versions of tc don't understand "mpls_uc"
160check_tc_mpls_support()
161{
162 local dev=$1; shift
163
164 tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \
165 matchall action pipe &> /dev/null
166 if [[ $? -ne 0 ]]; then
167 echo "SKIP: iproute2 too old; tc is missing MPLS support"
168 return $ksft_skip
169 fi
170 tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \
171 matchall
172}
173
174# Old versions of tc produce invalid json output for mpls lse statistics
175check_tc_mpls_lse_stats()
176{
177 local dev=$1; shift
178 local ret;
179
180 tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \
181 flower mpls lse depth 2 \
182 action continue &> /dev/null
183
184 if [[ $? -ne 0 ]]; then
185 echo "SKIP: iproute2 too old; tc-flower is missing extended MPLS support"
186 return $ksft_skip
187 fi
188
189 tc -j filter show dev $dev ingress protocol mpls_uc | jq . &> /dev/null
190 ret=$?
191 tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \
192 flower
193
194 if [[ $ret -ne 0 ]]; then
195 echo "SKIP: iproute2 too old; tc-flower produces invalid json output for extended MPLS filters"
196 return $ksft_skip
197 fi
198}
199
200check_tc_shblock_support()
201{
202 tc filter help 2>&1 | grep block &> /dev/null
203 if [[ $? -ne 0 ]]; then
204 echo "SKIP: iproute2 too old; tc is missing shared block support"
205 exit $ksft_skip
206 fi
207}
208
209check_tc_chain_support()
210{
211 tc help 2>&1|grep chain &> /dev/null
212 if [[ $? -ne 0 ]]; then
213 echo "SKIP: iproute2 too old; tc is missing chain support"
214 exit $ksft_skip
215 fi
216}
217
218check_tc_action_hw_stats_support()
219{
220 tc actions help 2>&1 | grep -q hw_stats
221 if [[ $? -ne 0 ]]; then
222 echo "SKIP: iproute2 too old; tc is missing action hw_stats support"
223 exit $ksft_skip
224 fi
225}
226
227check_tc_fp_support()
228{
229 tc qdisc add dev lo mqprio help 2>&1 | grep -q "fp "
230 if [[ $? -ne 0 ]]; then
231 echo "SKIP: iproute2 too old; tc is missing frame preemption support"
232 exit $ksft_skip
233 fi
234}
235
236check_ethtool_lanes_support()
237{
238 ethtool --help 2>&1| grep lanes &> /dev/null
239 if [[ $? -ne 0 ]]; then
240 echo "SKIP: ethtool too old; it is missing lanes support"
241 exit $ksft_skip
242 fi
243}
244
245check_ethtool_mm_support()
246{
247 ethtool --help 2>&1| grep -- '--show-mm' &> /dev/null
248 if [[ $? -ne 0 ]]; then
249 echo "SKIP: ethtool too old; it is missing MAC Merge layer support"
250 exit $ksft_skip
251 fi
252}
253
254check_ethtool_counter_group_support()
255{
256 ethtool --help 2>&1| grep -- '--all-groups' &> /dev/null
257 if [[ $? -ne 0 ]]; then
258 echo "SKIP: ethtool too old; it is missing standard counter group support"
259 exit $ksft_skip
260 fi
261}
262
263check_ethtool_pmac_std_stats_support()
264{
265 local dev=$1; shift
266 local grp=$1; shift
267
268 [ 0 -ne $(ethtool --json -S $dev --all-groups --src pmac 2>/dev/null \
269 | jq ".[].\"$grp\" | length") ]
270}
271
272check_locked_port_support()
273{
274 if ! bridge -d link show | grep -q " locked"; then
275 echo "SKIP: iproute2 too old; Locked port feature not supported."
276 return $ksft_skip
277 fi
278}
279
280check_port_mab_support()
281{
282 if ! bridge -d link show | grep -q "mab"; then
283 echo "SKIP: iproute2 too old; MacAuth feature not supported."
284 return $ksft_skip
285 fi
286}
287
288if [[ "$(id -u)" -ne 0 ]]; then
289 echo "SKIP: need root privileges"
290 exit $ksft_skip
291fi
292
293check_driver()
294{
295 local dev=$1; shift
296 local expected=$1; shift
297 local driver_name=`driver_name_get $dev`
298
299 if [[ $driver_name != $expected ]]; then
300 echo "SKIP: expected driver $expected for $dev, got $driver_name instead"
301 exit $ksft_skip
302 fi
303}
304
305if [[ "$CHECK_TC" = "yes" ]]; then
306 check_tc_version
307fi
308
309# IPv6 support was added in v3.0
310check_mtools_version()
311{
312 local version="$(msend -v)"
313 local major
314
315 version=${version##msend version }
316 major=$(echo $version | cut -d. -f1)
317
318 if [ $major -lt 3 ]; then
319 echo "SKIP: expected mtools version 3.0, got $version"
320 exit $ksft_skip
321 fi
322}
323
324if [[ "$REQUIRE_JQ" = "yes" ]]; then
325 require_command jq
326fi
327if [[ "$REQUIRE_MZ" = "yes" ]]; then
328 require_command $MZ
329fi
330if [[ "$REQUIRE_TEAMD" = "yes" ]]; then
331 require_command $TEAMD
332fi
333if [[ "$REQUIRE_MTOOLS" = "yes" ]]; then
334 # https://github.com/troglobit/mtools
335 require_command msend
336 require_command mreceive
337 check_mtools_version
338fi
339
340##############################################################################
341# Command line options handling
342
343count=0
344
345while [[ $# -gt 0 ]]; do
346 if [[ "$count" -eq "0" ]]; then
347 unset NETIFS
348 declare -A NETIFS
349 fi
350 count=$((count + 1))
351 NETIFS[p$count]="$1"
352 shift
353done
354
355##############################################################################
356# Network interfaces configuration
357
358if [[ ! -v NUM_NETIFS ]]; then
359 echo "SKIP: importer does not define \"NUM_NETIFS\""
360 exit $ksft_skip
361fi
362
363if (( NUM_NETIFS > ${#NETIFS[@]} )); then
364 echo "SKIP: Importer requires $NUM_NETIFS NETIFS, but only ${#NETIFS[@]} are defined (${NETIFS[@]})"
365 exit $ksft_skip
366fi
367
368for i in $(seq ${#NETIFS[@]}); do
369 if [[ ! ${NETIFS[p$i]} ]]; then
370 echo "SKIP: NETIFS[p$i] not given"
371 exit $ksft_skip
372 fi
373done
374
375create_netif_veth()
376{
377 local i
378
379 for ((i = 1; i <= NUM_NETIFS; ++i)); do
380 local j=$((i+1))
381
382 if [ -z ${NETIFS[p$i]} ]; then
383 echo "SKIP: Cannot create interface. Name not specified"
384 exit $ksft_skip
385 fi
386
387 ip link show dev ${NETIFS[p$i]} &> /dev/null
388 if [[ $? -ne 0 ]]; then
389 ip link add ${NETIFS[p$i]} type veth \
390 peer name ${NETIFS[p$j]}
391 if [[ $? -ne 0 ]]; then
392 echo "Failed to create netif"
393 exit 1
394 fi
395 fi
396 i=$j
397 done
398}
399
400create_netif()
401{
402 case "$NETIF_TYPE" in
403 veth) create_netif_veth
404 ;;
405 *) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
406 exit 1
407 ;;
408 esac
409}
410
411declare -A MAC_ADDR_ORIG
412mac_addr_prepare()
413{
414 local new_addr=
415 local dev=
416
417 for ((i = 1; i <= NUM_NETIFS; ++i)); do
418 dev=${NETIFS[p$i]}
419 new_addr=$(printf "00:01:02:03:04:%02x" $i)
420
421 MAC_ADDR_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].address')
422 # Strip quotes
423 MAC_ADDR_ORIG["$dev"]=${MAC_ADDR_ORIG["$dev"]//\"/}
424 ip link set dev $dev address $new_addr
425 done
426}
427
428mac_addr_restore()
429{
430 local dev=
431
432 for ((i = 1; i <= NUM_NETIFS; ++i)); do
433 dev=${NETIFS[p$i]}
434 ip link set dev $dev address ${MAC_ADDR_ORIG["$dev"]}
435 done
436}
437
438if [[ "$NETIF_CREATE" = "yes" ]]; then
439 create_netif
440fi
441
442if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then
443 mac_addr_prepare
444fi
445
446for ((i = 1; i <= NUM_NETIFS; ++i)); do
447 ip link show dev ${NETIFS[p$i]} &> /dev/null
448 if [[ $? -ne 0 ]]; then
449 echo "SKIP: could not find all required interfaces"
450 exit $ksft_skip
451 fi
452done
453
454##############################################################################
455# Helpers
456
457not()
458{
459 "$@"
460 [[ $? != 0 ]]
461}
462
463get_max()
464{
465 local arr=("$@")
466
467 max=${arr[0]}
468 for cur in ${arr[@]}; do
469 if [[ $cur -gt $max ]]; then
470 max=$cur
471 fi
472 done
473
474 echo $max
475}
476
477grep_bridge_fdb()
478{
479 local addr=$1; shift
480 local word
481 local flag
482
483 if [ "$1" == "self" ] || [ "$1" == "master" ]; then
484 word=$1; shift
485 if [ "$1" == "-v" ]; then
486 flag=$1; shift
487 fi
488 fi
489
490 $@ | grep $addr | grep $flag "$word"
491}
492
493wait_for_port_up()
494{
495 "$@" | grep -q "Link detected: yes"
496}
497
498wait_for_offload()
499{
500 "$@" | grep -q offload
501}
502
503wait_for_trap()
504{
505 "$@" | grep -q trap
506}
507
508setup_wait_dev()
509{
510 local dev=$1; shift
511 local wait_time=${1:-$WAIT_TIME}; shift
512
513 setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time
514
515 if (($?)); then
516 check_err 1
517 log_test setup_wait_dev ": Interface $dev does not come up."
518 exit 1
519 fi
520}
521
522setup_wait_dev_with_timeout()
523{
524 local dev=$1; shift
525 local max_iterations=${1:-$WAIT_TIMEOUT}; shift
526 local wait_time=${1:-$WAIT_TIME}; shift
527 local i
528
529 for ((i = 1; i <= $max_iterations; ++i)); do
530 ip link show dev $dev up \
531 | grep 'state UP' &> /dev/null
532 if [[ $? -ne 0 ]]; then
533 sleep 1
534 else
535 sleep $wait_time
536 return 0
537 fi
538 done
539
540 return 1
541}
542
543setup_wait_n()
544{
545 local num_netifs=$1; shift
546 local i
547
548 for ((i = 1; i <= num_netifs; ++i)); do
549 setup_wait_dev ${NETIFS[p$i]} 0
550 done
551
552 # Make sure links are ready.
553 sleep $WAIT_TIME
554}
555
556setup_wait()
557{
558 setup_wait_n "$NUM_NETIFS"
559}
560
561wait_for_dev()
562{
563 local dev=$1; shift
564 local timeout=${1:-$WAIT_TIMEOUT}; shift
565
566 slowwait $timeout ip link show dev $dev &> /dev/null
567 if (( $? )); then
568 check_err 1
569 log_test wait_for_dev "Interface $dev did not appear."
570 exit $EXIT_STATUS
571 fi
572}
573
574pre_cleanup()
575{
576 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
577 echo "Pausing before cleanup, hit any key to continue"
578 read
579 fi
580
581 if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then
582 mac_addr_restore
583 fi
584}
585
586vrf_prepare()
587{
588 ip -4 rule add pref 32765 table local
589 ip -4 rule del pref 0
590 ip -6 rule add pref 32765 table local
591 ip -6 rule del pref 0
592}
593
594vrf_cleanup()
595{
596 ip -6 rule add pref 0 table local
597 ip -6 rule del pref 32765
598 ip -4 rule add pref 0 table local
599 ip -4 rule del pref 32765
600}
601
602adf_vrf_prepare()
603{
604 vrf_prepare
605 defer vrf_cleanup
606}
607
608__last_tb_id=0
609declare -A __TB_IDS
610
611__vrf_td_id_assign()
612{
613 local vrf_name=$1
614
615 __last_tb_id=$((__last_tb_id + 1))
616 __TB_IDS[$vrf_name]=$__last_tb_id
617 return $__last_tb_id
618}
619
620__vrf_td_id_lookup()
621{
622 local vrf_name=$1
623
624 return ${__TB_IDS[$vrf_name]}
625}
626
627vrf_create()
628{
629 local vrf_name=$1
630 local tb_id
631
632 __vrf_td_id_assign $vrf_name
633 tb_id=$?
634
635 ip link add dev $vrf_name type vrf table $tb_id
636 ip -4 route add table $tb_id unreachable default metric 4278198272
637 ip -6 route add table $tb_id unreachable default metric 4278198272
638}
639
640vrf_destroy()
641{
642 local vrf_name=$1
643 local tb_id
644
645 __vrf_td_id_lookup $vrf_name
646 tb_id=$?
647
648 ip -6 route del table $tb_id unreachable default metric 4278198272
649 ip -4 route del table $tb_id unreachable default metric 4278198272
650 ip link del dev $vrf_name
651}
652
653__addr_add_del()
654{
655 local if_name=$1
656 local add_del=$2
657 local array
658
659 shift
660 shift
661 array=("${@}")
662
663 for addrstr in "${array[@]}"; do
664 ip address $add_del $addrstr dev $if_name
665 done
666}
667
668__simple_if_init()
669{
670 local if_name=$1; shift
671 local vrf_name=$1; shift
672 local addrs=("${@}")
673
674 ip link set dev $if_name master $vrf_name
675 ip link set dev $if_name up
676
677 __addr_add_del $if_name add "${addrs[@]}"
678}
679
680__simple_if_fini()
681{
682 local if_name=$1; shift
683 local addrs=("${@}")
684
685 __addr_add_del $if_name del "${addrs[@]}"
686
687 ip link set dev $if_name down
688 ip link set dev $if_name nomaster
689}
690
691simple_if_init()
692{
693 local if_name=$1
694 local vrf_name
695 local array
696
697 shift
698 vrf_name=v$if_name
699 array=("${@}")
700
701 vrf_create $vrf_name
702 ip link set dev $vrf_name up
703 __simple_if_init $if_name $vrf_name "${array[@]}"
704}
705
706simple_if_fini()
707{
708 local if_name=$1
709 local vrf_name
710 local array
711
712 shift
713 vrf_name=v$if_name
714 array=("${@}")
715
716 __simple_if_fini $if_name "${array[@]}"
717 vrf_destroy $vrf_name
718}
719
720adf_simple_if_init()
721{
722 simple_if_init "$@"
723 defer simple_if_fini "$@"
724}
725
726tunnel_create()
727{
728 local name=$1; shift
729 local type=$1; shift
730 local local=$1; shift
731 local remote=$1; shift
732
733 ip link add name $name type $type \
734 local $local remote $remote "$@"
735 ip link set dev $name up
736}
737
738tunnel_destroy()
739{
740 local name=$1; shift
741
742 ip link del dev $name
743}
744
745vlan_create()
746{
747 local if_name=$1; shift
748 local vid=$1; shift
749 local vrf=$1; shift
750 local ips=("${@}")
751 local name=$if_name.$vid
752
753 ip link add name $name link $if_name type vlan id $vid
754 if [ "$vrf" != "" ]; then
755 ip link set dev $name master $vrf
756 fi
757 ip link set dev $name up
758 __addr_add_del $name add "${ips[@]}"
759}
760
761vlan_destroy()
762{
763 local if_name=$1; shift
764 local vid=$1; shift
765 local name=$if_name.$vid
766
767 ip link del dev $name
768}
769
770team_create()
771{
772 local if_name=$1; shift
773 local mode=$1; shift
774
775 require_command $TEAMD
776 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
777 for slave in "$@"; do
778 ip link set dev $slave down
779 ip link set dev $slave master $if_name
780 ip link set dev $slave up
781 done
782 ip link set dev $if_name up
783}
784
785team_destroy()
786{
787 local if_name=$1; shift
788
789 $TEAMD -t $if_name -k
790}
791
792master_name_get()
793{
794 local if_name=$1
795
796 ip -j link show dev $if_name | jq -r '.[]["master"]'
797}
798
799link_stats_get()
800{
801 local if_name=$1; shift
802 local dir=$1; shift
803 local stat=$1; shift
804
805 ip -j -s link show dev $if_name \
806 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]'
807}
808
809link_stats_tx_packets_get()
810{
811 link_stats_get $1 tx packets
812}
813
814link_stats_rx_errors_get()
815{
816 link_stats_get $1 rx errors
817}
818
819ethtool_stats_get()
820{
821 local dev=$1; shift
822 local stat=$1; shift
823
824 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
825}
826
827ethtool_std_stats_get()
828{
829 local dev=$1; shift
830 local grp=$1; shift
831 local name=$1; shift
832 local src=$1; shift
833
834 ethtool --json -S $dev --groups $grp -- --src $src | \
835 jq '.[]."'"$grp"'"."'$name'"'
836}
837
838qdisc_stats_get()
839{
840 local dev=$1; shift
841 local handle=$1; shift
842 local selector=$1; shift
843
844 tc -j -s qdisc show dev "$dev" \
845 | jq '.[] | select(.handle == "'"$handle"'") | '"$selector"
846}
847
848qdisc_parent_stats_get()
849{
850 local dev=$1; shift
851 local parent=$1; shift
852 local selector=$1; shift
853
854 tc -j -s qdisc show dev "$dev" invisible \
855 | jq '.[] | select(.parent == "'"$parent"'") | '"$selector"
856}
857
858ipv6_stats_get()
859{
860 local dev=$1; shift
861 local stat=$1; shift
862
863 cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f2
864}
865
866hw_stats_get()
867{
868 local suite=$1; shift
869 local if_name=$1; shift
870 local dir=$1; shift
871 local stat=$1; shift
872
873 ip -j stats show dev $if_name group offload subgroup $suite |
874 jq ".[0].stats64.$dir.$stat"
875}
876
877__nh_stats_get()
878{
879 local key=$1; shift
880 local group_id=$1; shift
881 local member_id=$1; shift
882
883 ip -j -s -s nexthop show id $group_id |
884 jq --argjson member_id "$member_id" --arg key "$key" \
885 '.[].group_stats[] | select(.id == $member_id) | .[$key]'
886}
887
888nh_stats_get()
889{
890 local group_id=$1; shift
891 local member_id=$1; shift
892
893 __nh_stats_get packets "$group_id" "$member_id"
894}
895
896nh_stats_get_hw()
897{
898 local group_id=$1; shift
899 local member_id=$1; shift
900
901 __nh_stats_get packets_hw "$group_id" "$member_id"
902}
903
904humanize()
905{
906 local speed=$1; shift
907
908 for unit in bps Kbps Mbps Gbps; do
909 if (($(echo "$speed < 1024" | bc))); then
910 break
911 fi
912
913 speed=$(echo "scale=1; $speed / 1024" | bc)
914 done
915
916 echo "$speed${unit}"
917}
918
919rate()
920{
921 local t0=$1; shift
922 local t1=$1; shift
923 local interval=$1; shift
924
925 echo $((8 * (t1 - t0) / interval))
926}
927
928packets_rate()
929{
930 local t0=$1; shift
931 local t1=$1; shift
932 local interval=$1; shift
933
934 echo $(((t1 - t0) / interval))
935}
936
937ether_addr_to_u64()
938{
939 local addr="$1"
940 local order="$((1 << 40))"
941 local val=0
942 local byte
943
944 addr="${addr//:/ }"
945
946 for byte in $addr; do
947 byte="0x$byte"
948 val=$((val + order * byte))
949 order=$((order >> 8))
950 done
951
952 printf "0x%x" $val
953}
954
955u64_to_ether_addr()
956{
957 local val=$1
958 local byte
959 local i
960
961 for ((i = 40; i >= 0; i -= 8)); do
962 byte=$(((val & (0xff << i)) >> i))
963 printf "%02x" $byte
964 if [ $i -ne 0 ]; then
965 printf ":"
966 fi
967 done
968}
969
970ipv6_lladdr_get()
971{
972 local if_name=$1
973
974 ip -j addr show dev $if_name | \
975 jq -r '.[]["addr_info"][] | select(.scope == "link").local' | \
976 head -1
977}
978
979bridge_ageing_time_get()
980{
981 local bridge=$1
982 local ageing_time
983
984 # Need to divide by 100 to convert to seconds.
985 ageing_time=$(ip -j -d link show dev $bridge \
986 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
987 echo $((ageing_time / 100))
988}
989
990declare -A SYSCTL_ORIG
991sysctl_save()
992{
993 local key=$1; shift
994
995 SYSCTL_ORIG[$key]=$(sysctl -n $key)
996}
997
998sysctl_set()
999{
1000 local key=$1; shift
1001 local value=$1; shift
1002
1003 sysctl_save "$key"
1004 sysctl -qw $key="$value"
1005}
1006
1007sysctl_restore()
1008{
1009 local key=$1; shift
1010
1011 sysctl -qw $key="${SYSCTL_ORIG[$key]}"
1012}
1013
1014forwarding_enable()
1015{
1016 sysctl_set net.ipv4.conf.all.forwarding 1
1017 sysctl_set net.ipv6.conf.all.forwarding 1
1018}
1019
1020forwarding_restore()
1021{
1022 sysctl_restore net.ipv6.conf.all.forwarding
1023 sysctl_restore net.ipv4.conf.all.forwarding
1024}
1025
1026adf_forwarding_enable()
1027{
1028 forwarding_enable
1029 defer forwarding_restore
1030}
1031
1032declare -A MTU_ORIG
1033mtu_set()
1034{
1035 local dev=$1; shift
1036 local mtu=$1; shift
1037
1038 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')
1039 ip link set dev $dev mtu $mtu
1040}
1041
1042mtu_restore()
1043{
1044 local dev=$1; shift
1045
1046 ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
1047}
1048
1049tc_offload_check()
1050{
1051 local num_netifs=${1:-$NUM_NETIFS}
1052
1053 for ((i = 1; i <= num_netifs; ++i)); do
1054 ethtool -k ${NETIFS[p$i]} \
1055 | grep "hw-tc-offload: on" &> /dev/null
1056 if [[ $? -ne 0 ]]; then
1057 return 1
1058 fi
1059 done
1060
1061 return 0
1062}
1063
1064trap_install()
1065{
1066 local dev=$1; shift
1067 local direction=$1; shift
1068
1069 # Some devices may not support or need in-hardware trapping of traffic
1070 # (e.g. the veth pairs that this library creates for non-existent
1071 # loopbacks). Use continue instead, so that there is a filter in there
1072 # (some tests check counters), and so that other filters are still
1073 # processed.
1074 tc filter add dev $dev $direction pref 1 \
1075 flower skip_sw action trap 2>/dev/null \
1076 || tc filter add dev $dev $direction pref 1 \
1077 flower action continue
1078}
1079
1080trap_uninstall()
1081{
1082 local dev=$1; shift
1083 local direction=$1; shift
1084
1085 tc filter del dev $dev $direction pref 1 flower
1086}
1087
1088__icmp_capture_add_del()
1089{
1090 local add_del=$1; shift
1091 local pref=$1; shift
1092 local vsuf=$1; shift
1093 local tundev=$1; shift
1094 local filter=$1; shift
1095
1096 tc filter $add_del dev "$tundev" ingress \
1097 proto ip$vsuf pref $pref \
1098 flower ip_proto icmp$vsuf $filter \
1099 action pass
1100}
1101
1102icmp_capture_install()
1103{
1104 local tundev=$1; shift
1105 local filter=$1; shift
1106
1107 __icmp_capture_add_del add 100 "" "$tundev" "$filter"
1108}
1109
1110icmp_capture_uninstall()
1111{
1112 local tundev=$1; shift
1113 local filter=$1; shift
1114
1115 __icmp_capture_add_del del 100 "" "$tundev" "$filter"
1116}
1117
1118icmp6_capture_install()
1119{
1120 local tundev=$1; shift
1121 local filter=$1; shift
1122
1123 __icmp_capture_add_del add 100 v6 "$tundev" "$filter"
1124}
1125
1126icmp6_capture_uninstall()
1127{
1128 local tundev=$1; shift
1129 local filter=$1; shift
1130
1131 __icmp_capture_add_del del 100 v6 "$tundev" "$filter"
1132}
1133
1134__vlan_capture_add_del()
1135{
1136 local add_del=$1; shift
1137 local pref=$1; shift
1138 local dev=$1; shift
1139 local filter=$1; shift
1140
1141 tc filter $add_del dev "$dev" ingress \
1142 proto 802.1q pref $pref \
1143 flower $filter \
1144 action pass
1145}
1146
1147vlan_capture_install()
1148{
1149 local dev=$1; shift
1150 local filter=$1; shift
1151
1152 __vlan_capture_add_del add 100 "$dev" "$filter"
1153}
1154
1155vlan_capture_uninstall()
1156{
1157 local dev=$1; shift
1158 local filter=$1; shift
1159
1160 __vlan_capture_add_del del 100 "$dev" "$filter"
1161}
1162
1163__dscp_capture_add_del()
1164{
1165 local add_del=$1; shift
1166 local dev=$1; shift
1167 local base=$1; shift
1168 local dscp;
1169
1170 for prio in {0..7}; do
1171 dscp=$((base + prio))
1172 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
1173 "skip_hw ip_tos $((dscp << 2))"
1174 done
1175}
1176
1177dscp_capture_install()
1178{
1179 local dev=$1; shift
1180 local base=$1; shift
1181
1182 __dscp_capture_add_del add $dev $base
1183}
1184
1185dscp_capture_uninstall()
1186{
1187 local dev=$1; shift
1188 local base=$1; shift
1189
1190 __dscp_capture_add_del del $dev $base
1191}
1192
1193dscp_fetch_stats()
1194{
1195 local dev=$1; shift
1196 local base=$1; shift
1197
1198 for prio in {0..7}; do
1199 local dscp=$((base + prio))
1200 local t=$(tc_rule_stats_get $dev $((dscp + 100)))
1201 echo "[$dscp]=$t "
1202 done
1203}
1204
1205matchall_sink_create()
1206{
1207 local dev=$1; shift
1208
1209 tc qdisc add dev $dev clsact
1210 tc filter add dev $dev ingress \
1211 pref 10000 \
1212 matchall \
1213 action drop
1214}
1215
1216cleanup()
1217{
1218 pre_cleanup
1219 defer_scopes_cleanup
1220}
1221
1222multipath_eval()
1223{
1224 local desc="$1"
1225 local weight_rp12=$2
1226 local weight_rp13=$3
1227 local packets_rp12=$4
1228 local packets_rp13=$5
1229 local weights_ratio packets_ratio diff
1230
1231 RET=0
1232
1233 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1234 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
1235 | bc -l)
1236 else
1237 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
1238 | bc -l)
1239 fi
1240
1241 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
1242 check_err 1 "Packet difference is 0"
1243 log_test "Multipath"
1244 log_info "Expected ratio $weights_ratio"
1245 return
1246 fi
1247
1248 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1249 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
1250 | bc -l)
1251 else
1252 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
1253 | bc -l)
1254 fi
1255
1256 diff=$(echo $weights_ratio - $packets_ratio | bc -l)
1257 diff=${diff#-}
1258
1259 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
1260 check_err $? "Too large discrepancy between expected and measured ratios"
1261 log_test "$desc"
1262 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
1263}
1264
1265in_ns()
1266{
1267 local name=$1; shift
1268
1269 ip netns exec $name bash <<-EOF
1270 NUM_NETIFS=0
1271 source lib.sh
1272 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
1273 EOF
1274}
1275
1276##############################################################################
1277# Tests
1278
1279ping_do()
1280{
1281 local if_name=$1
1282 local dip=$2
1283 local args=$3
1284 local vrf_name
1285
1286 vrf_name=$(master_name_get $if_name)
1287 ip vrf exec $vrf_name \
1288 $PING $args -c $PING_COUNT -i 0.1 \
1289 -w $PING_TIMEOUT $dip &> /dev/null
1290}
1291
1292ping_test()
1293{
1294 RET=0
1295
1296 ping_do $1 $2
1297 check_err $?
1298 log_test "ping$3"
1299}
1300
1301ping_test_fails()
1302{
1303 RET=0
1304
1305 ping_do $1 $2
1306 check_fail $?
1307 log_test "ping fails$3"
1308}
1309
1310ping6_do()
1311{
1312 local if_name=$1
1313 local dip=$2
1314 local args=$3
1315 local vrf_name
1316
1317 vrf_name=$(master_name_get $if_name)
1318 ip vrf exec $vrf_name \
1319 $PING6 $args -c $PING_COUNT -i 0.1 \
1320 -w $PING_TIMEOUT $dip &> /dev/null
1321}
1322
1323ping6_test()
1324{
1325 RET=0
1326
1327 ping6_do $1 $2
1328 check_err $?
1329 log_test "ping6$3"
1330}
1331
1332ping6_test_fails()
1333{
1334 RET=0
1335
1336 ping6_do $1 $2
1337 check_fail $?
1338 log_test "ping6 fails$3"
1339}
1340
1341learning_test()
1342{
1343 local bridge=$1
1344 local br_port1=$2 # Connected to `host1_if`.
1345 local host1_if=$3
1346 local host2_if=$4
1347 local mac=de:ad:be:ef:13:37
1348 local ageing_time
1349
1350 RET=0
1351
1352 bridge -j fdb show br $bridge brport $br_port1 \
1353 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1354 check_fail $? "Found FDB record when should not"
1355
1356 # Disable unknown unicast flooding on `br_port1` to make sure
1357 # packets are only forwarded through the port after a matching
1358 # FDB entry was installed.
1359 bridge link set dev $br_port1 flood off
1360
1361 ip link set $host1_if promisc on
1362 tc qdisc add dev $host1_if ingress
1363 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
1364 flower dst_mac $mac action drop
1365
1366 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1367 sleep 1
1368
1369 tc -j -s filter show dev $host1_if ingress \
1370 | jq -e ".[] | select(.options.handle == 101) \
1371 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1372 check_fail $? "Packet reached first host when should not"
1373
1374 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1375 sleep 1
1376
1377 bridge -j fdb show br $bridge brport $br_port1 \
1378 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1379 check_err $? "Did not find FDB record when should"
1380
1381 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1382 sleep 1
1383
1384 tc -j -s filter show dev $host1_if ingress \
1385 | jq -e ".[] | select(.options.handle == 101) \
1386 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1387 check_err $? "Packet did not reach second host when should"
1388
1389 # Wait for 10 seconds after the ageing time to make sure FDB
1390 # record was aged-out.
1391 ageing_time=$(bridge_ageing_time_get $bridge)
1392 sleep $((ageing_time + 10))
1393
1394 bridge -j fdb show br $bridge brport $br_port1 \
1395 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1396 check_fail $? "Found FDB record when should not"
1397
1398 bridge link set dev $br_port1 learning off
1399
1400 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1401 sleep 1
1402
1403 bridge -j fdb show br $bridge brport $br_port1 \
1404 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1405 check_fail $? "Found FDB record when should not"
1406
1407 bridge link set dev $br_port1 learning on
1408
1409 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
1410 tc qdisc del dev $host1_if ingress
1411 ip link set $host1_if promisc off
1412
1413 bridge link set dev $br_port1 flood on
1414
1415 log_test "FDB learning"
1416}
1417
1418flood_test_do()
1419{
1420 local should_flood=$1
1421 local mac=$2
1422 local ip=$3
1423 local host1_if=$4
1424 local host2_if=$5
1425 local err=0
1426
1427 # Add an ACL on `host2_if` which will tell us whether the packet
1428 # was flooded to it or not.
1429 ip link set $host2_if promisc on
1430 tc qdisc add dev $host2_if ingress
1431 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
1432 flower dst_mac $mac action drop
1433
1434 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
1435 sleep 1
1436
1437 tc -j -s filter show dev $host2_if ingress \
1438 | jq -e ".[] | select(.options.handle == 101) \
1439 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1440 if [[ $? -ne 0 && $should_flood == "true" || \
1441 $? -eq 0 && $should_flood == "false" ]]; then
1442 err=1
1443 fi
1444
1445 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
1446 tc qdisc del dev $host2_if ingress
1447 ip link set $host2_if promisc off
1448
1449 return $err
1450}
1451
1452flood_unicast_test()
1453{
1454 local br_port=$1
1455 local host1_if=$2
1456 local host2_if=$3
1457 local mac=de:ad:be:ef:13:37
1458 local ip=192.0.2.100
1459
1460 RET=0
1461
1462 bridge link set dev $br_port flood off
1463
1464 flood_test_do false $mac $ip $host1_if $host2_if
1465 check_err $? "Packet flooded when should not"
1466
1467 bridge link set dev $br_port flood on
1468
1469 flood_test_do true $mac $ip $host1_if $host2_if
1470 check_err $? "Packet was not flooded when should"
1471
1472 log_test "Unknown unicast flood"
1473}
1474
1475flood_multicast_test()
1476{
1477 local br_port=$1
1478 local host1_if=$2
1479 local host2_if=$3
1480 local mac=01:00:5e:00:00:01
1481 local ip=239.0.0.1
1482
1483 RET=0
1484
1485 bridge link set dev $br_port mcast_flood off
1486
1487 flood_test_do false $mac $ip $host1_if $host2_if
1488 check_err $? "Packet flooded when should not"
1489
1490 bridge link set dev $br_port mcast_flood on
1491
1492 flood_test_do true $mac $ip $host1_if $host2_if
1493 check_err $? "Packet was not flooded when should"
1494
1495 log_test "Unregistered multicast flood"
1496}
1497
1498flood_test()
1499{
1500 # `br_port` is connected to `host2_if`
1501 local br_port=$1
1502 local host1_if=$2
1503 local host2_if=$3
1504
1505 flood_unicast_test $br_port $host1_if $host2_if
1506 flood_multicast_test $br_port $host1_if $host2_if
1507}
1508
1509__start_traffic()
1510{
1511 local pktsize=$1; shift
1512 local proto=$1; shift
1513 local h_in=$1; shift # Where the traffic egresses the host
1514 local sip=$1; shift
1515 local dip=$1; shift
1516 local dmac=$1; shift
1517 local -a mz_args=("$@")
1518
1519 $MZ $h_in -p $pktsize -A $sip -B $dip -c 0 \
1520 -a own -b $dmac -t "$proto" -q "${mz_args[@]}" &
1521 sleep 1
1522}
1523
1524start_traffic_pktsize()
1525{
1526 local pktsize=$1; shift
1527 local h_in=$1; shift
1528 local sip=$1; shift
1529 local dip=$1; shift
1530 local dmac=$1; shift
1531 local -a mz_args=("$@")
1532
1533 __start_traffic $pktsize udp "$h_in" "$sip" "$dip" "$dmac" \
1534 "${mz_args[@]}"
1535}
1536
1537start_tcp_traffic_pktsize()
1538{
1539 local pktsize=$1; shift
1540 local h_in=$1; shift
1541 local sip=$1; shift
1542 local dip=$1; shift
1543 local dmac=$1; shift
1544 local -a mz_args=("$@")
1545
1546 __start_traffic $pktsize tcp "$h_in" "$sip" "$dip" "$dmac" \
1547 "${mz_args[@]}"
1548}
1549
1550start_traffic()
1551{
1552 local h_in=$1; shift
1553 local sip=$1; shift
1554 local dip=$1; shift
1555 local dmac=$1; shift
1556 local -a mz_args=("$@")
1557
1558 start_traffic_pktsize 8000 "$h_in" "$sip" "$dip" "$dmac" \
1559 "${mz_args[@]}"
1560}
1561
1562start_tcp_traffic()
1563{
1564 local h_in=$1; shift
1565 local sip=$1; shift
1566 local dip=$1; shift
1567 local dmac=$1; shift
1568 local -a mz_args=("$@")
1569
1570 start_tcp_traffic_pktsize 8000 "$h_in" "$sip" "$dip" "$dmac" \
1571 "${mz_args[@]}"
1572}
1573
1574stop_traffic()
1575{
1576 local pid=${1-%%}; shift
1577
1578 kill_process "$pid"
1579}
1580
1581declare -A cappid
1582declare -A capfile
1583declare -A capout
1584
1585tcpdump_start()
1586{
1587 local if_name=$1; shift
1588 local ns=$1; shift
1589
1590 capfile[$if_name]=$(mktemp)
1591 capout[$if_name]=$(mktemp)
1592
1593 if [ -z $ns ]; then
1594 ns_cmd=""
1595 else
1596 ns_cmd="ip netns exec ${ns}"
1597 fi
1598
1599 if [ -z $SUDO_USER ] ; then
1600 capuser=""
1601 else
1602 capuser="-Z $SUDO_USER"
1603 fi
1604
1605 $ns_cmd tcpdump $TCPDUMP_EXTRA_FLAGS -e -n -Q in -i $if_name \
1606 -s 65535 -B 32768 $capuser -w ${capfile[$if_name]} \
1607 > "${capout[$if_name]}" 2>&1 &
1608 cappid[$if_name]=$!
1609
1610 sleep 1
1611}
1612
1613tcpdump_stop()
1614{
1615 local if_name=$1
1616 local pid=${cappid[$if_name]}
1617
1618 $ns_cmd kill "$pid" && wait "$pid"
1619 sleep 1
1620}
1621
1622tcpdump_cleanup()
1623{
1624 local if_name=$1
1625
1626 rm ${capfile[$if_name]} ${capout[$if_name]}
1627}
1628
1629tcpdump_show()
1630{
1631 local if_name=$1
1632
1633 tcpdump -e -n -r ${capfile[$if_name]} 2>&1
1634}
1635
1636# return 0 if the packet wasn't seen on host2_if or 1 if it was
1637mcast_packet_test()
1638{
1639 local mac=$1
1640 local src_ip=$2
1641 local ip=$3
1642 local host1_if=$4
1643 local host2_if=$5
1644 local seen=0
1645 local tc_proto="ip"
1646 local mz_v6arg=""
1647
1648 # basic check to see if we were passed an IPv4 address, if not assume IPv6
1649 if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
1650 tc_proto="ipv6"
1651 mz_v6arg="-6"
1652 fi
1653
1654 # Add an ACL on `host2_if` which will tell us whether the packet
1655 # was received by it or not.
1656 tc qdisc add dev $host2_if ingress
1657 tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \
1658 flower ip_proto udp dst_mac $mac action drop
1659
1660 $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q
1661 sleep 1
1662
1663 tc -j -s filter show dev $host2_if ingress \
1664 | jq -e ".[] | select(.options.handle == 101) \
1665 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1666 if [[ $? -eq 0 ]]; then
1667 seen=1
1668 fi
1669
1670 tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower
1671 tc qdisc del dev $host2_if ingress
1672
1673 return $seen
1674}
1675
1676brmcast_check_sg_entries()
1677{
1678 local report=$1; shift
1679 local slist=("$@")
1680 local sarg=""
1681
1682 for src in "${slist[@]}"; do
1683 sarg="${sarg} and .source_list[].address == \"$src\""
1684 done
1685 bridge -j -d -s mdb show dev br0 \
1686 | jq -e ".[].mdb[] | \
1687 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null
1688 check_err $? "Wrong *,G entry source list after $report report"
1689
1690 for sgent in "${slist[@]}"; do
1691 bridge -j -d -s mdb show dev br0 \
1692 | jq -e ".[].mdb[] | \
1693 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null
1694 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)"
1695 done
1696}
1697
1698brmcast_check_sg_fwding()
1699{
1700 local should_fwd=$1; shift
1701 local sources=("$@")
1702
1703 for src in "${sources[@]}"; do
1704 local retval=0
1705
1706 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1
1707 retval=$?
1708 if [ $should_fwd -eq 1 ]; then
1709 check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)"
1710 else
1711 check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)"
1712 fi
1713 done
1714}
1715
1716brmcast_check_sg_state()
1717{
1718 local is_blocked=$1; shift
1719 local sources=("$@")
1720 local should_fail=1
1721
1722 if [ $is_blocked -eq 1 ]; then
1723 should_fail=0
1724 fi
1725
1726 for src in "${sources[@]}"; do
1727 bridge -j -d -s mdb show dev br0 \
1728 | jq -e ".[].mdb[] | \
1729 select(.grp == \"$TEST_GROUP\" and .source_list != null) |
1730 .source_list[] |
1731 select(.address == \"$src\") |
1732 select(.timer == \"0.00\")" &>/dev/null
1733 check_err_fail $should_fail $? "Entry $src has zero timer"
1734
1735 bridge -j -d -s mdb show dev br0 \
1736 | jq -e ".[].mdb[] | \
1737 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \
1738 .flags[] == \"blocked\")" &>/dev/null
1739 check_err_fail $should_fail $? "Entry $src has blocked flag"
1740 done
1741}
1742
1743mc_join()
1744{
1745 local if_name=$1
1746 local group=$2
1747 local vrf_name=$(master_name_get $if_name)
1748
1749 # We don't care about actual reception, just about joining the
1750 # IP multicast group and adding the L2 address to the device's
1751 # MAC filtering table
1752 ip vrf exec $vrf_name \
1753 mreceive -g $group -I $if_name > /dev/null 2>&1 &
1754 mreceive_pid=$!
1755
1756 sleep 1
1757}
1758
1759mc_leave()
1760{
1761 kill "$mreceive_pid" && wait "$mreceive_pid"
1762}
1763
1764mc_send()
1765{
1766 local if_name=$1
1767 local groups=$2
1768 local vrf_name=$(master_name_get $if_name)
1769
1770 ip vrf exec $vrf_name \
1771 msend -g $groups -I $if_name -c 1 > /dev/null 2>&1
1772}
1773
1774adf_mcd_start()
1775{
1776 local ifs=("$@")
1777
1778 local table_name="$MCD_TABLE_NAME"
1779 local smcroutedir
1780 local pid
1781 local if
1782 local i
1783
1784 check_command "$MCD" || return 1
1785 check_command "$MC_CLI" || return 1
1786
1787 smcroutedir=$(mktemp -d)
1788 defer rm -rf "$smcroutedir"
1789
1790 for ((i = 1; i <= NUM_NETIFS; ++i)); do
1791 echo "phyint ${NETIFS[p$i]} enable" >> \
1792 "$smcroutedir/$table_name.conf"
1793 done
1794
1795 for if in "${ifs[@]}"; do
1796 if ! ip_link_has_flag "$if" MULTICAST; then
1797 ip link set dev "$if" multicast on
1798 defer ip link set dev "$if" multicast off
1799 fi
1800
1801 echo "phyint $if enable" >> \
1802 "$smcroutedir/$table_name.conf"
1803 done
1804
1805 "$MCD" -N -I "$table_name" -f "$smcroutedir/$table_name.conf" \
1806 -P "$smcroutedir/$table_name.pid"
1807 busywait "$BUSYWAIT_TIMEOUT" test -e "$smcroutedir/$table_name.pid"
1808 pid=$(cat "$smcroutedir/$table_name.pid")
1809 defer kill_process "$pid"
1810}
1811
1812mc_cli()
1813{
1814 local table_name="$MCD_TABLE_NAME"
1815
1816 "$MC_CLI" -I "$table_name" "$@"
1817}
1818
1819start_ip_monitor()
1820{
1821 local mtype=$1; shift
1822 local ip=${1-ip}; shift
1823
1824 # start the monitor in the background
1825 tmpfile=`mktemp /var/run/nexthoptestXXX`
1826 mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null`
1827 sleep 0.2
1828 echo "$mpid $tmpfile"
1829}
1830
1831stop_ip_monitor()
1832{
1833 local mpid=$1; shift
1834 local tmpfile=$1; shift
1835 local el=$1; shift
1836 local what=$1; shift
1837
1838 sleep 0.2
1839 kill $mpid
1840 local lines=`grep '^\w' $tmpfile | wc -l`
1841 test $lines -eq $el
1842 check_err $? "$what: $lines lines of events, expected $el"
1843 rm -rf $tmpfile
1844}
1845
1846hw_stats_monitor_test()
1847{
1848 local dev=$1; shift
1849 local type=$1; shift
1850 local make_suitable=$1; shift
1851 local make_unsuitable=$1; shift
1852 local ip=${1-ip}; shift
1853
1854 RET=0
1855
1856 # Expect a notification about enablement.
1857 local ipmout=$(start_ip_monitor stats "$ip")
1858 $ip stats set dev $dev ${type}_stats on
1859 stop_ip_monitor $ipmout 1 "${type}_stats enablement"
1860
1861 # Expect a notification about offload.
1862 local ipmout=$(start_ip_monitor stats "$ip")
1863 $make_suitable
1864 stop_ip_monitor $ipmout 1 "${type}_stats installation"
1865
1866 # Expect a notification about loss of offload.
1867 local ipmout=$(start_ip_monitor stats "$ip")
1868 $make_unsuitable
1869 stop_ip_monitor $ipmout 1 "${type}_stats deinstallation"
1870
1871 # Expect a notification about disablement
1872 local ipmout=$(start_ip_monitor stats "$ip")
1873 $ip stats set dev $dev ${type}_stats off
1874 stop_ip_monitor $ipmout 1 "${type}_stats disablement"
1875
1876 log_test "${type}_stats notifications"
1877}
1878
1879ipv4_to_bytes()
1880{
1881 local IP=$1; shift
1882
1883 printf '%02x:' ${IP//./ } |
1884 sed 's/:$//'
1885}
1886
1887# Convert a given IPv6 address, `IP' such that the :: token, if present, is
1888# expanded, and each 16-bit group is padded with zeroes to be 4 hexadecimal
1889# digits. An optional `BYTESEP' parameter can be given to further separate
1890# individual bytes of each 16-bit group.
1891expand_ipv6()
1892{
1893 local IP=$1; shift
1894 local bytesep=$1; shift
1895
1896 local cvt_ip=${IP/::/_}
1897 local colons=${cvt_ip//[^:]/}
1898 local allcol=:::::::
1899 # IP where :: -> the appropriate number of colons:
1900 local allcol_ip=${cvt_ip/_/${allcol:${#colons}}}
1901
1902 echo $allcol_ip | tr : '\n' |
1903 sed s/^/0000/ |
1904 sed 's/.*\(..\)\(..\)/\1'"$bytesep"'\2/' |
1905 tr '\n' : |
1906 sed 's/:$//'
1907}
1908
1909ipv6_to_bytes()
1910{
1911 local IP=$1; shift
1912
1913 expand_ipv6 "$IP" :
1914}
1915
1916u16_to_bytes()
1917{
1918 local u16=$1; shift
1919
1920 printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/'
1921}
1922
1923# Given a mausezahn-formatted payload (colon-separated bytes given as %02x),
1924# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be,
1925# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any)
1926# stands for 00:00.
1927payload_template_calc_checksum()
1928{
1929 local payload=$1; shift
1930
1931 (
1932 # Set input radix.
1933 echo "16i"
1934 # Push zero for the initial checksum.
1935 echo 0
1936
1937 # Pad the payload with a terminating 00: in case we get an odd
1938 # number of bytes.
1939 echo "${payload%:}:00:" |
1940 sed 's/CHECKSUM/00:00/g' |
1941 tr '[:lower:]' '[:upper:]' |
1942 # Add the word to the checksum.
1943 sed 's/\(..\):\(..\):/\1\2+\n/g' |
1944 # Strip the extra odd byte we pushed if left unconverted.
1945 sed 's/\(..\):$//'
1946
1947 echo "10000 ~ +" # Calculate and add carry.
1948 echo "FFFF r - p" # Bit-flip and print.
1949 ) |
1950 dc |
1951 tr '[:upper:]' '[:lower:]'
1952}
1953
1954payload_template_expand_checksum()
1955{
1956 local payload=$1; shift
1957 local checksum=$1; shift
1958
1959 local ckbytes=$(u16_to_bytes $checksum)
1960
1961 echo "$payload" | sed "s/CHECKSUM/$ckbytes/g"
1962}
1963
1964payload_template_nbytes()
1965{
1966 local payload=$1; shift
1967
1968 payload_template_expand_checksum "${payload%:}" 0 |
1969 sed 's/:/\n/g' | wc -l
1970}
1971
1972igmpv3_is_in_get()
1973{
1974 local GRP=$1; shift
1975 local sources=("$@")
1976
1977 local igmpv3
1978 local nsources=$(u16_to_bytes ${#sources[@]})
1979
1980 # IS_IN ( $sources )
1981 igmpv3=$(:
1982 )"22:"$( : Type - Membership Report
1983 )"00:"$( : Reserved
1984 )"CHECKSUM:"$( : Checksum
1985 )"00:00:"$( : Reserved
1986 )"00:01:"$( : Number of Group Records
1987 )"01:"$( : Record Type - IS_IN
1988 )"00:"$( : Aux Data Len
1989 )"${nsources}:"$( : Number of Sources
1990 )"$(ipv4_to_bytes $GRP):"$( : Multicast Address
1991 )"$(for src in "${sources[@]}"; do
1992 ipv4_to_bytes $src
1993 echo -n :
1994 done)"$( : Source Addresses
1995 )
1996 local checksum=$(payload_template_calc_checksum "$igmpv3")
1997
1998 payload_template_expand_checksum "$igmpv3" $checksum
1999}
2000
2001igmpv2_leave_get()
2002{
2003 local GRP=$1; shift
2004
2005 local payload=$(:
2006 )"17:"$( : Type - Leave Group
2007 )"00:"$( : Max Resp Time - not meaningful
2008 )"CHECKSUM:"$( : Checksum
2009 )"$(ipv4_to_bytes $GRP)"$( : Group Address
2010 )
2011 local checksum=$(payload_template_calc_checksum "$payload")
2012
2013 payload_template_expand_checksum "$payload" $checksum
2014}
2015
2016mldv2_is_in_get()
2017{
2018 local SIP=$1; shift
2019 local GRP=$1; shift
2020 local sources=("$@")
2021
2022 local hbh
2023 local icmpv6
2024 local nsources=$(u16_to_bytes ${#sources[@]})
2025
2026 hbh=$(:
2027 )"3a:"$( : Next Header - ICMPv6
2028 )"00:"$( : Hdr Ext Len
2029 )"00:00:00:00:00:00:"$( : Options and Padding
2030 )
2031
2032 icmpv6=$(:
2033 )"8f:"$( : Type - MLDv2 Report
2034 )"00:"$( : Code
2035 )"CHECKSUM:"$( : Checksum
2036 )"00:00:"$( : Reserved
2037 )"00:01:"$( : Number of Group Records
2038 )"01:"$( : Record Type - IS_IN
2039 )"00:"$( : Aux Data Len
2040 )"${nsources}:"$( : Number of Sources
2041 )"$(ipv6_to_bytes $GRP):"$( : Multicast address
2042 )"$(for src in "${sources[@]}"; do
2043 ipv6_to_bytes $src
2044 echo -n :
2045 done)"$( : Source Addresses
2046 )
2047
2048 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6))
2049 local sudohdr=$(:
2050 )"$(ipv6_to_bytes $SIP):"$( : SIP
2051 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address
2052 )"${len}:"$( : Upper-layer length
2053 )"00:3a:"$( : Zero and next-header
2054 )
2055 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6})
2056
2057 payload_template_expand_checksum "$hbh$icmpv6" $checksum
2058}
2059
2060mldv1_done_get()
2061{
2062 local SIP=$1; shift
2063 local GRP=$1; shift
2064
2065 local hbh
2066 local icmpv6
2067
2068 hbh=$(:
2069 )"3a:"$( : Next Header - ICMPv6
2070 )"00:"$( : Hdr Ext Len
2071 )"00:00:00:00:00:00:"$( : Options and Padding
2072 )
2073
2074 icmpv6=$(:
2075 )"84:"$( : Type - MLDv1 Done
2076 )"00:"$( : Code
2077 )"CHECKSUM:"$( : Checksum
2078 )"00:00:"$( : Max Resp Delay - not meaningful
2079 )"00:00:"$( : Reserved
2080 )"$(ipv6_to_bytes $GRP):"$( : Multicast address
2081 )
2082
2083 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6))
2084 local sudohdr=$(:
2085 )"$(ipv6_to_bytes $SIP):"$( : SIP
2086 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address
2087 )"${len}:"$( : Upper-layer length
2088 )"00:3a:"$( : Zero and next-header
2089 )
2090 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6})
2091
2092 payload_template_expand_checksum "$hbh$icmpv6" $checksum
2093}
2094
2095bail_on_lldpad()
2096{
2097 local reason1="$1"; shift
2098 local reason2="$1"; shift
2099 local caller=${FUNCNAME[1]}
2100 local src=${BASH_SOURCE[1]}
2101
2102 if systemctl is-active --quiet lldpad; then
2103
2104 cat >/dev/stderr <<-EOF
2105 WARNING: lldpad is running
2106
2107 lldpad will likely $reason1, and this test will
2108 $reason2. Both are not supported at the same time,
2109 one of them is arbitrarily going to overwrite the
2110 other. That will cause spurious failures (or, unlikely,
2111 passes) of this test.
2112 EOF
2113
2114 if [[ -z $ALLOW_LLDPAD ]]; then
2115 cat >/dev/stderr <<-EOF
2116
2117 If you want to run the test anyway, please set
2118 an environment variable ALLOW_LLDPAD to a
2119 non-empty string.
2120 EOF
2121 log_test_skip $src:$caller
2122 exit $EXIT_STATUS
2123 else
2124 return
2125 fi
2126 fi
2127}
2128
2129absval()
2130{
2131 local v=$1; shift
2132
2133 echo $((v > 0 ? v : -v))
2134}
2135
2136has_unicast_flt()
2137{
2138 local dev=$1; shift
2139 local mac_addr=$(mac_get $dev)
2140 local tmp=$(ether_addr_to_u64 $mac_addr)
2141 local promisc
2142
2143 ip link set $dev up
2144 ip link add link $dev name macvlan-tmp type macvlan mode private
2145 ip link set macvlan-tmp address $(u64_to_ether_addr $((tmp + 1)))
2146 ip link set macvlan-tmp up
2147
2148 promisc=$(ip -j -d link show dev $dev | jq -r '.[].promiscuity')
2149
2150 ip link del macvlan-tmp
2151
2152 [[ $promisc == 1 ]] && echo "no" || echo "yes"
2153}