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
574cmd_jq()
575{
576 local cmd=$1
577 local jq_exp=$2
578 local jq_opts=$3
579 local ret
580 local output
581
582 output="$($cmd)"
583 # it the command fails, return error right away
584 ret=$?
585 if [[ $ret -ne 0 ]]; then
586 return $ret
587 fi
588 output=$(echo $output | jq -r $jq_opts "$jq_exp")
589 ret=$?
590 if [[ $ret -ne 0 ]]; then
591 return $ret
592 fi
593 echo $output
594 # return success only in case of non-empty output
595 [ ! -z "$output" ]
596}
597
598pre_cleanup()
599{
600 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
601 echo "Pausing before cleanup, hit any key to continue"
602 read
603 fi
604
605 if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then
606 mac_addr_restore
607 fi
608}
609
610vrf_prepare()
611{
612 ip -4 rule add pref 32765 table local
613 ip -4 rule del pref 0
614 ip -6 rule add pref 32765 table local
615 ip -6 rule del pref 0
616}
617
618vrf_cleanup()
619{
620 ip -6 rule add pref 0 table local
621 ip -6 rule del pref 32765
622 ip -4 rule add pref 0 table local
623 ip -4 rule del pref 32765
624}
625
626__last_tb_id=0
627declare -A __TB_IDS
628
629__vrf_td_id_assign()
630{
631 local vrf_name=$1
632
633 __last_tb_id=$((__last_tb_id + 1))
634 __TB_IDS[$vrf_name]=$__last_tb_id
635 return $__last_tb_id
636}
637
638__vrf_td_id_lookup()
639{
640 local vrf_name=$1
641
642 return ${__TB_IDS[$vrf_name]}
643}
644
645vrf_create()
646{
647 local vrf_name=$1
648 local tb_id
649
650 __vrf_td_id_assign $vrf_name
651 tb_id=$?
652
653 ip link add dev $vrf_name type vrf table $tb_id
654 ip -4 route add table $tb_id unreachable default metric 4278198272
655 ip -6 route add table $tb_id unreachable default metric 4278198272
656}
657
658vrf_destroy()
659{
660 local vrf_name=$1
661 local tb_id
662
663 __vrf_td_id_lookup $vrf_name
664 tb_id=$?
665
666 ip -6 route del table $tb_id unreachable default metric 4278198272
667 ip -4 route del table $tb_id unreachable default metric 4278198272
668 ip link del dev $vrf_name
669}
670
671__addr_add_del()
672{
673 local if_name=$1
674 local add_del=$2
675 local array
676
677 shift
678 shift
679 array=("${@}")
680
681 for addrstr in "${array[@]}"; do
682 ip address $add_del $addrstr dev $if_name
683 done
684}
685
686__simple_if_init()
687{
688 local if_name=$1; shift
689 local vrf_name=$1; shift
690 local addrs=("${@}")
691
692 ip link set dev $if_name master $vrf_name
693 ip link set dev $if_name up
694
695 __addr_add_del $if_name add "${addrs[@]}"
696}
697
698__simple_if_fini()
699{
700 local if_name=$1; shift
701 local addrs=("${@}")
702
703 __addr_add_del $if_name del "${addrs[@]}"
704
705 ip link set dev $if_name down
706 ip link set dev $if_name nomaster
707}
708
709simple_if_init()
710{
711 local if_name=$1
712 local vrf_name
713 local array
714
715 shift
716 vrf_name=v$if_name
717 array=("${@}")
718
719 vrf_create $vrf_name
720 ip link set dev $vrf_name up
721 __simple_if_init $if_name $vrf_name "${array[@]}"
722}
723
724simple_if_fini()
725{
726 local if_name=$1
727 local vrf_name
728 local array
729
730 shift
731 vrf_name=v$if_name
732 array=("${@}")
733
734 __simple_if_fini $if_name "${array[@]}"
735 vrf_destroy $vrf_name
736}
737
738tunnel_create()
739{
740 local name=$1; shift
741 local type=$1; shift
742 local local=$1; shift
743 local remote=$1; shift
744
745 ip link add name $name type $type \
746 local $local remote $remote "$@"
747 ip link set dev $name up
748}
749
750tunnel_destroy()
751{
752 local name=$1; shift
753
754 ip link del dev $name
755}
756
757vlan_create()
758{
759 local if_name=$1; shift
760 local vid=$1; shift
761 local vrf=$1; shift
762 local ips=("${@}")
763 local name=$if_name.$vid
764
765 ip link add name $name link $if_name type vlan id $vid
766 if [ "$vrf" != "" ]; then
767 ip link set dev $name master $vrf
768 fi
769 ip link set dev $name up
770 __addr_add_del $name add "${ips[@]}"
771}
772
773vlan_destroy()
774{
775 local if_name=$1; shift
776 local vid=$1; shift
777 local name=$if_name.$vid
778
779 ip link del dev $name
780}
781
782team_create()
783{
784 local if_name=$1; shift
785 local mode=$1; shift
786
787 require_command $TEAMD
788 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
789 for slave in "$@"; do
790 ip link set dev $slave down
791 ip link set dev $slave master $if_name
792 ip link set dev $slave up
793 done
794 ip link set dev $if_name up
795}
796
797team_destroy()
798{
799 local if_name=$1; shift
800
801 $TEAMD -t $if_name -k
802}
803
804master_name_get()
805{
806 local if_name=$1
807
808 ip -j link show dev $if_name | jq -r '.[]["master"]'
809}
810
811link_stats_get()
812{
813 local if_name=$1; shift
814 local dir=$1; shift
815 local stat=$1; shift
816
817 ip -j -s link show dev $if_name \
818 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]'
819}
820
821link_stats_tx_packets_get()
822{
823 link_stats_get $1 tx packets
824}
825
826link_stats_rx_errors_get()
827{
828 link_stats_get $1 rx errors
829}
830
831ethtool_stats_get()
832{
833 local dev=$1; shift
834 local stat=$1; shift
835
836 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
837}
838
839ethtool_std_stats_get()
840{
841 local dev=$1; shift
842 local grp=$1; shift
843 local name=$1; shift
844 local src=$1; shift
845
846 ethtool --json -S $dev --groups $grp -- --src $src | \
847 jq '.[]."'"$grp"'"."'$name'"'
848}
849
850qdisc_stats_get()
851{
852 local dev=$1; shift
853 local handle=$1; shift
854 local selector=$1; shift
855
856 tc -j -s qdisc show dev "$dev" \
857 | jq '.[] | select(.handle == "'"$handle"'") | '"$selector"
858}
859
860qdisc_parent_stats_get()
861{
862 local dev=$1; shift
863 local parent=$1; shift
864 local selector=$1; shift
865
866 tc -j -s qdisc show dev "$dev" invisible \
867 | jq '.[] | select(.parent == "'"$parent"'") | '"$selector"
868}
869
870ipv6_stats_get()
871{
872 local dev=$1; shift
873 local stat=$1; shift
874
875 cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f2
876}
877
878hw_stats_get()
879{
880 local suite=$1; shift
881 local if_name=$1; shift
882 local dir=$1; shift
883 local stat=$1; shift
884
885 ip -j stats show dev $if_name group offload subgroup $suite |
886 jq ".[0].stats64.$dir.$stat"
887}
888
889__nh_stats_get()
890{
891 local key=$1; shift
892 local group_id=$1; shift
893 local member_id=$1; shift
894
895 ip -j -s -s nexthop show id $group_id |
896 jq --argjson member_id "$member_id" --arg key "$key" \
897 '.[].group_stats[] | select(.id == $member_id) | .[$key]'
898}
899
900nh_stats_get()
901{
902 local group_id=$1; shift
903 local member_id=$1; shift
904
905 __nh_stats_get packets "$group_id" "$member_id"
906}
907
908nh_stats_get_hw()
909{
910 local group_id=$1; shift
911 local member_id=$1; shift
912
913 __nh_stats_get packets_hw "$group_id" "$member_id"
914}
915
916humanize()
917{
918 local speed=$1; shift
919
920 for unit in bps Kbps Mbps Gbps; do
921 if (($(echo "$speed < 1024" | bc))); then
922 break
923 fi
924
925 speed=$(echo "scale=1; $speed / 1024" | bc)
926 done
927
928 echo "$speed${unit}"
929}
930
931rate()
932{
933 local t0=$1; shift
934 local t1=$1; shift
935 local interval=$1; shift
936
937 echo $((8 * (t1 - t0) / interval))
938}
939
940packets_rate()
941{
942 local t0=$1; shift
943 local t1=$1; shift
944 local interval=$1; shift
945
946 echo $(((t1 - t0) / interval))
947}
948
949ether_addr_to_u64()
950{
951 local addr="$1"
952 local order="$((1 << 40))"
953 local val=0
954 local byte
955
956 addr="${addr//:/ }"
957
958 for byte in $addr; do
959 byte="0x$byte"
960 val=$((val + order * byte))
961 order=$((order >> 8))
962 done
963
964 printf "0x%x" $val
965}
966
967u64_to_ether_addr()
968{
969 local val=$1
970 local byte
971 local i
972
973 for ((i = 40; i >= 0; i -= 8)); do
974 byte=$(((val & (0xff << i)) >> i))
975 printf "%02x" $byte
976 if [ $i -ne 0 ]; then
977 printf ":"
978 fi
979 done
980}
981
982ipv6_lladdr_get()
983{
984 local if_name=$1
985
986 ip -j addr show dev $if_name | \
987 jq -r '.[]["addr_info"][] | select(.scope == "link").local' | \
988 head -1
989}
990
991bridge_ageing_time_get()
992{
993 local bridge=$1
994 local ageing_time
995
996 # Need to divide by 100 to convert to seconds.
997 ageing_time=$(ip -j -d link show dev $bridge \
998 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
999 echo $((ageing_time / 100))
1000}
1001
1002declare -A SYSCTL_ORIG
1003sysctl_save()
1004{
1005 local key=$1; shift
1006
1007 SYSCTL_ORIG[$key]=$(sysctl -n $key)
1008}
1009
1010sysctl_set()
1011{
1012 local key=$1; shift
1013 local value=$1; shift
1014
1015 sysctl_save "$key"
1016 sysctl -qw $key="$value"
1017}
1018
1019sysctl_restore()
1020{
1021 local key=$1; shift
1022
1023 sysctl -qw $key="${SYSCTL_ORIG[$key]}"
1024}
1025
1026forwarding_enable()
1027{
1028 sysctl_set net.ipv4.conf.all.forwarding 1
1029 sysctl_set net.ipv6.conf.all.forwarding 1
1030}
1031
1032forwarding_restore()
1033{
1034 sysctl_restore net.ipv6.conf.all.forwarding
1035 sysctl_restore net.ipv4.conf.all.forwarding
1036}
1037
1038declare -A MTU_ORIG
1039mtu_set()
1040{
1041 local dev=$1; shift
1042 local mtu=$1; shift
1043
1044 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')
1045 ip link set dev $dev mtu $mtu
1046}
1047
1048mtu_restore()
1049{
1050 local dev=$1; shift
1051
1052 ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
1053}
1054
1055tc_offload_check()
1056{
1057 local num_netifs=${1:-$NUM_NETIFS}
1058
1059 for ((i = 1; i <= num_netifs; ++i)); do
1060 ethtool -k ${NETIFS[p$i]} \
1061 | grep "hw-tc-offload: on" &> /dev/null
1062 if [[ $? -ne 0 ]]; then
1063 return 1
1064 fi
1065 done
1066
1067 return 0
1068}
1069
1070trap_install()
1071{
1072 local dev=$1; shift
1073 local direction=$1; shift
1074
1075 # Some devices may not support or need in-hardware trapping of traffic
1076 # (e.g. the veth pairs that this library creates for non-existent
1077 # loopbacks). Use continue instead, so that there is a filter in there
1078 # (some tests check counters), and so that other filters are still
1079 # processed.
1080 tc filter add dev $dev $direction pref 1 \
1081 flower skip_sw action trap 2>/dev/null \
1082 || tc filter add dev $dev $direction pref 1 \
1083 flower action continue
1084}
1085
1086trap_uninstall()
1087{
1088 local dev=$1; shift
1089 local direction=$1; shift
1090
1091 tc filter del dev $dev $direction pref 1 flower
1092}
1093
1094__icmp_capture_add_del()
1095{
1096 local add_del=$1; shift
1097 local pref=$1; shift
1098 local vsuf=$1; shift
1099 local tundev=$1; shift
1100 local filter=$1; shift
1101
1102 tc filter $add_del dev "$tundev" ingress \
1103 proto ip$vsuf pref $pref \
1104 flower ip_proto icmp$vsuf $filter \
1105 action pass
1106}
1107
1108icmp_capture_install()
1109{
1110 local tundev=$1; shift
1111 local filter=$1; shift
1112
1113 __icmp_capture_add_del add 100 "" "$tundev" "$filter"
1114}
1115
1116icmp_capture_uninstall()
1117{
1118 local tundev=$1; shift
1119 local filter=$1; shift
1120
1121 __icmp_capture_add_del del 100 "" "$tundev" "$filter"
1122}
1123
1124icmp6_capture_install()
1125{
1126 local tundev=$1; shift
1127 local filter=$1; shift
1128
1129 __icmp_capture_add_del add 100 v6 "$tundev" "$filter"
1130}
1131
1132icmp6_capture_uninstall()
1133{
1134 local tundev=$1; shift
1135 local filter=$1; shift
1136
1137 __icmp_capture_add_del del 100 v6 "$tundev" "$filter"
1138}
1139
1140__vlan_capture_add_del()
1141{
1142 local add_del=$1; shift
1143 local pref=$1; shift
1144 local dev=$1; shift
1145 local filter=$1; shift
1146
1147 tc filter $add_del dev "$dev" ingress \
1148 proto 802.1q pref $pref \
1149 flower $filter \
1150 action pass
1151}
1152
1153vlan_capture_install()
1154{
1155 local dev=$1; shift
1156 local filter=$1; shift
1157
1158 __vlan_capture_add_del add 100 "$dev" "$filter"
1159}
1160
1161vlan_capture_uninstall()
1162{
1163 local dev=$1; shift
1164 local filter=$1; shift
1165
1166 __vlan_capture_add_del del 100 "$dev" "$filter"
1167}
1168
1169__dscp_capture_add_del()
1170{
1171 local add_del=$1; shift
1172 local dev=$1; shift
1173 local base=$1; shift
1174 local dscp;
1175
1176 for prio in {0..7}; do
1177 dscp=$((base + prio))
1178 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
1179 "skip_hw ip_tos $((dscp << 2))"
1180 done
1181}
1182
1183dscp_capture_install()
1184{
1185 local dev=$1; shift
1186 local base=$1; shift
1187
1188 __dscp_capture_add_del add $dev $base
1189}
1190
1191dscp_capture_uninstall()
1192{
1193 local dev=$1; shift
1194 local base=$1; shift
1195
1196 __dscp_capture_add_del del $dev $base
1197}
1198
1199dscp_fetch_stats()
1200{
1201 local dev=$1; shift
1202 local base=$1; shift
1203
1204 for prio in {0..7}; do
1205 local dscp=$((base + prio))
1206 local t=$(tc_rule_stats_get $dev $((dscp + 100)))
1207 echo "[$dscp]=$t "
1208 done
1209}
1210
1211matchall_sink_create()
1212{
1213 local dev=$1; shift
1214
1215 tc qdisc add dev $dev clsact
1216 tc filter add dev $dev ingress \
1217 pref 10000 \
1218 matchall \
1219 action drop
1220}
1221
1222cleanup()
1223{
1224 pre_cleanup
1225 defer_scopes_cleanup
1226}
1227
1228multipath_eval()
1229{
1230 local desc="$1"
1231 local weight_rp12=$2
1232 local weight_rp13=$3
1233 local packets_rp12=$4
1234 local packets_rp13=$5
1235 local weights_ratio packets_ratio diff
1236
1237 RET=0
1238
1239 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1240 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
1241 | bc -l)
1242 else
1243 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
1244 | bc -l)
1245 fi
1246
1247 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
1248 check_err 1 "Packet difference is 0"
1249 log_test "Multipath"
1250 log_info "Expected ratio $weights_ratio"
1251 return
1252 fi
1253
1254 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1255 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
1256 | bc -l)
1257 else
1258 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
1259 | bc -l)
1260 fi
1261
1262 diff=$(echo $weights_ratio - $packets_ratio | bc -l)
1263 diff=${diff#-}
1264
1265 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
1266 check_err $? "Too large discrepancy between expected and measured ratios"
1267 log_test "$desc"
1268 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
1269}
1270
1271in_ns()
1272{
1273 local name=$1; shift
1274
1275 ip netns exec $name bash <<-EOF
1276 NUM_NETIFS=0
1277 source lib.sh
1278 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
1279 EOF
1280}
1281
1282##############################################################################
1283# Tests
1284
1285ping_do()
1286{
1287 local if_name=$1
1288 local dip=$2
1289 local args=$3
1290 local vrf_name
1291
1292 vrf_name=$(master_name_get $if_name)
1293 ip vrf exec $vrf_name \
1294 $PING $args $dip -c $PING_COUNT -i 0.1 \
1295 -w $PING_TIMEOUT &> /dev/null
1296}
1297
1298ping_test()
1299{
1300 RET=0
1301
1302 ping_do $1 $2
1303 check_err $?
1304 log_test "ping$3"
1305}
1306
1307ping_test_fails()
1308{
1309 RET=0
1310
1311 ping_do $1 $2
1312 check_fail $?
1313 log_test "ping fails$3"
1314}
1315
1316ping6_do()
1317{
1318 local if_name=$1
1319 local dip=$2
1320 local args=$3
1321 local vrf_name
1322
1323 vrf_name=$(master_name_get $if_name)
1324 ip vrf exec $vrf_name \
1325 $PING6 $args $dip -c $PING_COUNT -i 0.1 \
1326 -w $PING_TIMEOUT &> /dev/null
1327}
1328
1329ping6_test()
1330{
1331 RET=0
1332
1333 ping6_do $1 $2
1334 check_err $?
1335 log_test "ping6$3"
1336}
1337
1338ping6_test_fails()
1339{
1340 RET=0
1341
1342 ping6_do $1 $2
1343 check_fail $?
1344 log_test "ping6 fails$3"
1345}
1346
1347learning_test()
1348{
1349 local bridge=$1
1350 local br_port1=$2 # Connected to `host1_if`.
1351 local host1_if=$3
1352 local host2_if=$4
1353 local mac=de:ad:be:ef:13:37
1354 local ageing_time
1355
1356 RET=0
1357
1358 bridge -j fdb show br $bridge brport $br_port1 \
1359 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1360 check_fail $? "Found FDB record when should not"
1361
1362 # Disable unknown unicast flooding on `br_port1` to make sure
1363 # packets are only forwarded through the port after a matching
1364 # FDB entry was installed.
1365 bridge link set dev $br_port1 flood off
1366
1367 ip link set $host1_if promisc on
1368 tc qdisc add dev $host1_if ingress
1369 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
1370 flower dst_mac $mac action drop
1371
1372 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1373 sleep 1
1374
1375 tc -j -s filter show dev $host1_if ingress \
1376 | jq -e ".[] | select(.options.handle == 101) \
1377 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1378 check_fail $? "Packet reached first host when should not"
1379
1380 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1381 sleep 1
1382
1383 bridge -j fdb show br $bridge brport $br_port1 \
1384 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1385 check_err $? "Did not find FDB record when should"
1386
1387 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1388 sleep 1
1389
1390 tc -j -s filter show dev $host1_if ingress \
1391 | jq -e ".[] | select(.options.handle == 101) \
1392 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1393 check_err $? "Packet did not reach second host when should"
1394
1395 # Wait for 10 seconds after the ageing time to make sure FDB
1396 # record was aged-out.
1397 ageing_time=$(bridge_ageing_time_get $bridge)
1398 sleep $((ageing_time + 10))
1399
1400 bridge -j fdb show br $bridge brport $br_port1 \
1401 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1402 check_fail $? "Found FDB record when should not"
1403
1404 bridge link set dev $br_port1 learning off
1405
1406 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1407 sleep 1
1408
1409 bridge -j fdb show br $bridge brport $br_port1 \
1410 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1411 check_fail $? "Found FDB record when should not"
1412
1413 bridge link set dev $br_port1 learning on
1414
1415 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
1416 tc qdisc del dev $host1_if ingress
1417 ip link set $host1_if promisc off
1418
1419 bridge link set dev $br_port1 flood on
1420
1421 log_test "FDB learning"
1422}
1423
1424flood_test_do()
1425{
1426 local should_flood=$1
1427 local mac=$2
1428 local ip=$3
1429 local host1_if=$4
1430 local host2_if=$5
1431 local err=0
1432
1433 # Add an ACL on `host2_if` which will tell us whether the packet
1434 # was flooded to it or not.
1435 ip link set $host2_if promisc on
1436 tc qdisc add dev $host2_if ingress
1437 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
1438 flower dst_mac $mac action drop
1439
1440 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
1441 sleep 1
1442
1443 tc -j -s filter show dev $host2_if ingress \
1444 | jq -e ".[] | select(.options.handle == 101) \
1445 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1446 if [[ $? -ne 0 && $should_flood == "true" || \
1447 $? -eq 0 && $should_flood == "false" ]]; then
1448 err=1
1449 fi
1450
1451 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
1452 tc qdisc del dev $host2_if ingress
1453 ip link set $host2_if promisc off
1454
1455 return $err
1456}
1457
1458flood_unicast_test()
1459{
1460 local br_port=$1
1461 local host1_if=$2
1462 local host2_if=$3
1463 local mac=de:ad:be:ef:13:37
1464 local ip=192.0.2.100
1465
1466 RET=0
1467
1468 bridge link set dev $br_port flood off
1469
1470 flood_test_do false $mac $ip $host1_if $host2_if
1471 check_err $? "Packet flooded when should not"
1472
1473 bridge link set dev $br_port flood on
1474
1475 flood_test_do true $mac $ip $host1_if $host2_if
1476 check_err $? "Packet was not flooded when should"
1477
1478 log_test "Unknown unicast flood"
1479}
1480
1481flood_multicast_test()
1482{
1483 local br_port=$1
1484 local host1_if=$2
1485 local host2_if=$3
1486 local mac=01:00:5e:00:00:01
1487 local ip=239.0.0.1
1488
1489 RET=0
1490
1491 bridge link set dev $br_port mcast_flood off
1492
1493 flood_test_do false $mac $ip $host1_if $host2_if
1494 check_err $? "Packet flooded when should not"
1495
1496 bridge link set dev $br_port mcast_flood on
1497
1498 flood_test_do true $mac $ip $host1_if $host2_if
1499 check_err $? "Packet was not flooded when should"
1500
1501 log_test "Unregistered multicast flood"
1502}
1503
1504flood_test()
1505{
1506 # `br_port` is connected to `host2_if`
1507 local br_port=$1
1508 local host1_if=$2
1509 local host2_if=$3
1510
1511 flood_unicast_test $br_port $host1_if $host2_if
1512 flood_multicast_test $br_port $host1_if $host2_if
1513}
1514
1515__start_traffic()
1516{
1517 local pktsize=$1; shift
1518 local proto=$1; shift
1519 local h_in=$1; shift # Where the traffic egresses the host
1520 local sip=$1; shift
1521 local dip=$1; shift
1522 local dmac=$1; shift
1523 local -a mz_args=("$@")
1524
1525 $MZ $h_in -p $pktsize -A $sip -B $dip -c 0 \
1526 -a own -b $dmac -t "$proto" -q "${mz_args[@]}" &
1527 sleep 1
1528}
1529
1530start_traffic_pktsize()
1531{
1532 local pktsize=$1; shift
1533 local h_in=$1; shift
1534 local sip=$1; shift
1535 local dip=$1; shift
1536 local dmac=$1; shift
1537 local -a mz_args=("$@")
1538
1539 __start_traffic $pktsize udp "$h_in" "$sip" "$dip" "$dmac" \
1540 "${mz_args[@]}"
1541}
1542
1543start_tcp_traffic_pktsize()
1544{
1545 local pktsize=$1; shift
1546 local h_in=$1; shift
1547 local sip=$1; shift
1548 local dip=$1; shift
1549 local dmac=$1; shift
1550 local -a mz_args=("$@")
1551
1552 __start_traffic $pktsize tcp "$h_in" "$sip" "$dip" "$dmac" \
1553 "${mz_args[@]}"
1554}
1555
1556start_traffic()
1557{
1558 local h_in=$1; shift
1559 local sip=$1; shift
1560 local dip=$1; shift
1561 local dmac=$1; shift
1562 local -a mz_args=("$@")
1563
1564 start_traffic_pktsize 8000 "$h_in" "$sip" "$dip" "$dmac" \
1565 "${mz_args[@]}"
1566}
1567
1568start_tcp_traffic()
1569{
1570 local h_in=$1; shift
1571 local sip=$1; shift
1572 local dip=$1; shift
1573 local dmac=$1; shift
1574 local -a mz_args=("$@")
1575
1576 start_tcp_traffic_pktsize 8000 "$h_in" "$sip" "$dip" "$dmac" \
1577 "${mz_args[@]}"
1578}
1579
1580stop_traffic()
1581{
1582 local pid=${1-%%}; shift
1583
1584 kill_process "$pid"
1585}
1586
1587declare -A cappid
1588declare -A capfile
1589declare -A capout
1590
1591tcpdump_start()
1592{
1593 local if_name=$1; shift
1594 local ns=$1; shift
1595
1596 capfile[$if_name]=$(mktemp)
1597 capout[$if_name]=$(mktemp)
1598
1599 if [ -z $ns ]; then
1600 ns_cmd=""
1601 else
1602 ns_cmd="ip netns exec ${ns}"
1603 fi
1604
1605 if [ -z $SUDO_USER ] ; then
1606 capuser=""
1607 else
1608 capuser="-Z $SUDO_USER"
1609 fi
1610
1611 $ns_cmd tcpdump $TCPDUMP_EXTRA_FLAGS -e -n -Q in -i $if_name \
1612 -s 65535 -B 32768 $capuser -w ${capfile[$if_name]} \
1613 > "${capout[$if_name]}" 2>&1 &
1614 cappid[$if_name]=$!
1615
1616 sleep 1
1617}
1618
1619tcpdump_stop()
1620{
1621 local if_name=$1
1622 local pid=${cappid[$if_name]}
1623
1624 $ns_cmd kill "$pid" && wait "$pid"
1625 sleep 1
1626}
1627
1628tcpdump_cleanup()
1629{
1630 local if_name=$1
1631
1632 rm ${capfile[$if_name]} ${capout[$if_name]}
1633}
1634
1635tcpdump_show()
1636{
1637 local if_name=$1
1638
1639 tcpdump -e -n -r ${capfile[$if_name]} 2>&1
1640}
1641
1642# return 0 if the packet wasn't seen on host2_if or 1 if it was
1643mcast_packet_test()
1644{
1645 local mac=$1
1646 local src_ip=$2
1647 local ip=$3
1648 local host1_if=$4
1649 local host2_if=$5
1650 local seen=0
1651 local tc_proto="ip"
1652 local mz_v6arg=""
1653
1654 # basic check to see if we were passed an IPv4 address, if not assume IPv6
1655 if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
1656 tc_proto="ipv6"
1657 mz_v6arg="-6"
1658 fi
1659
1660 # Add an ACL on `host2_if` which will tell us whether the packet
1661 # was received by it or not.
1662 tc qdisc add dev $host2_if ingress
1663 tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \
1664 flower ip_proto udp dst_mac $mac action drop
1665
1666 $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q
1667 sleep 1
1668
1669 tc -j -s filter show dev $host2_if ingress \
1670 | jq -e ".[] | select(.options.handle == 101) \
1671 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1672 if [[ $? -eq 0 ]]; then
1673 seen=1
1674 fi
1675
1676 tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower
1677 tc qdisc del dev $host2_if ingress
1678
1679 return $seen
1680}
1681
1682brmcast_check_sg_entries()
1683{
1684 local report=$1; shift
1685 local slist=("$@")
1686 local sarg=""
1687
1688 for src in "${slist[@]}"; do
1689 sarg="${sarg} and .source_list[].address == \"$src\""
1690 done
1691 bridge -j -d -s mdb show dev br0 \
1692 | jq -e ".[].mdb[] | \
1693 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null
1694 check_err $? "Wrong *,G entry source list after $report report"
1695
1696 for sgent in "${slist[@]}"; do
1697 bridge -j -d -s mdb show dev br0 \
1698 | jq -e ".[].mdb[] | \
1699 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null
1700 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)"
1701 done
1702}
1703
1704brmcast_check_sg_fwding()
1705{
1706 local should_fwd=$1; shift
1707 local sources=("$@")
1708
1709 for src in "${sources[@]}"; do
1710 local retval=0
1711
1712 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1
1713 retval=$?
1714 if [ $should_fwd -eq 1 ]; then
1715 check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)"
1716 else
1717 check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)"
1718 fi
1719 done
1720}
1721
1722brmcast_check_sg_state()
1723{
1724 local is_blocked=$1; shift
1725 local sources=("$@")
1726 local should_fail=1
1727
1728 if [ $is_blocked -eq 1 ]; then
1729 should_fail=0
1730 fi
1731
1732 for src in "${sources[@]}"; do
1733 bridge -j -d -s mdb show dev br0 \
1734 | jq -e ".[].mdb[] | \
1735 select(.grp == \"$TEST_GROUP\" and .source_list != null) |
1736 .source_list[] |
1737 select(.address == \"$src\") |
1738 select(.timer == \"0.00\")" &>/dev/null
1739 check_err_fail $should_fail $? "Entry $src has zero timer"
1740
1741 bridge -j -d -s mdb show dev br0 \
1742 | jq -e ".[].mdb[] | \
1743 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \
1744 .flags[] == \"blocked\")" &>/dev/null
1745 check_err_fail $should_fail $? "Entry $src has blocked flag"
1746 done
1747}
1748
1749mc_join()
1750{
1751 local if_name=$1
1752 local group=$2
1753 local vrf_name=$(master_name_get $if_name)
1754
1755 # We don't care about actual reception, just about joining the
1756 # IP multicast group and adding the L2 address to the device's
1757 # MAC filtering table
1758 ip vrf exec $vrf_name \
1759 mreceive -g $group -I $if_name > /dev/null 2>&1 &
1760 mreceive_pid=$!
1761
1762 sleep 1
1763}
1764
1765mc_leave()
1766{
1767 kill "$mreceive_pid" && wait "$mreceive_pid"
1768}
1769
1770mc_send()
1771{
1772 local if_name=$1
1773 local groups=$2
1774 local vrf_name=$(master_name_get $if_name)
1775
1776 ip vrf exec $vrf_name \
1777 msend -g $groups -I $if_name -c 1 > /dev/null 2>&1
1778}
1779
1780adf_mcd_start()
1781{
1782 local ifs=("$@")
1783
1784 local table_name="$MCD_TABLE_NAME"
1785 local smcroutedir
1786 local pid
1787 local if
1788 local i
1789
1790 check_command "$MCD" || return 1
1791 check_command "$MC_CLI" || return 1
1792
1793 smcroutedir=$(mktemp -d)
1794 defer rm -rf "$smcroutedir"
1795
1796 for ((i = 1; i <= NUM_NETIFS; ++i)); do
1797 echo "phyint ${NETIFS[p$i]} enable" >> \
1798 "$smcroutedir/$table_name.conf"
1799 done
1800
1801 for if in "${ifs[@]}"; do
1802 if ! ip_link_has_flag "$if" MULTICAST; then
1803 ip link set dev "$if" multicast on
1804 defer ip link set dev "$if" multicast off
1805 fi
1806
1807 echo "phyint $if enable" >> \
1808 "$smcroutedir/$table_name.conf"
1809 done
1810
1811 "$MCD" -N -I "$table_name" -f "$smcroutedir/$table_name.conf" \
1812 -P "$smcroutedir/$table_name.pid"
1813 busywait "$BUSYWAIT_TIMEOUT" test -e "$smcroutedir/$table_name.pid"
1814 pid=$(cat "$smcroutedir/$table_name.pid")
1815 defer kill_process "$pid"
1816}
1817
1818mc_cli()
1819{
1820 local table_name="$MCD_TABLE_NAME"
1821
1822 "$MC_CLI" -I "$table_name" "$@"
1823}
1824
1825start_ip_monitor()
1826{
1827 local mtype=$1; shift
1828 local ip=${1-ip}; shift
1829
1830 # start the monitor in the background
1831 tmpfile=`mktemp /var/run/nexthoptestXXX`
1832 mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null`
1833 sleep 0.2
1834 echo "$mpid $tmpfile"
1835}
1836
1837stop_ip_monitor()
1838{
1839 local mpid=$1; shift
1840 local tmpfile=$1; shift
1841 local el=$1; shift
1842 local what=$1; shift
1843
1844 sleep 0.2
1845 kill $mpid
1846 local lines=`grep '^\w' $tmpfile | wc -l`
1847 test $lines -eq $el
1848 check_err $? "$what: $lines lines of events, expected $el"
1849 rm -rf $tmpfile
1850}
1851
1852hw_stats_monitor_test()
1853{
1854 local dev=$1; shift
1855 local type=$1; shift
1856 local make_suitable=$1; shift
1857 local make_unsuitable=$1; shift
1858 local ip=${1-ip}; shift
1859
1860 RET=0
1861
1862 # Expect a notification about enablement.
1863 local ipmout=$(start_ip_monitor stats "$ip")
1864 $ip stats set dev $dev ${type}_stats on
1865 stop_ip_monitor $ipmout 1 "${type}_stats enablement"
1866
1867 # Expect a notification about offload.
1868 local ipmout=$(start_ip_monitor stats "$ip")
1869 $make_suitable
1870 stop_ip_monitor $ipmout 1 "${type}_stats installation"
1871
1872 # Expect a notification about loss of offload.
1873 local ipmout=$(start_ip_monitor stats "$ip")
1874 $make_unsuitable
1875 stop_ip_monitor $ipmout 1 "${type}_stats deinstallation"
1876
1877 # Expect a notification about disablement
1878 local ipmout=$(start_ip_monitor stats "$ip")
1879 $ip stats set dev $dev ${type}_stats off
1880 stop_ip_monitor $ipmout 1 "${type}_stats disablement"
1881
1882 log_test "${type}_stats notifications"
1883}
1884
1885ipv4_to_bytes()
1886{
1887 local IP=$1; shift
1888
1889 printf '%02x:' ${IP//./ } |
1890 sed 's/:$//'
1891}
1892
1893# Convert a given IPv6 address, `IP' such that the :: token, if present, is
1894# expanded, and each 16-bit group is padded with zeroes to be 4 hexadecimal
1895# digits. An optional `BYTESEP' parameter can be given to further separate
1896# individual bytes of each 16-bit group.
1897expand_ipv6()
1898{
1899 local IP=$1; shift
1900 local bytesep=$1; shift
1901
1902 local cvt_ip=${IP/::/_}
1903 local colons=${cvt_ip//[^:]/}
1904 local allcol=:::::::
1905 # IP where :: -> the appropriate number of colons:
1906 local allcol_ip=${cvt_ip/_/${allcol:${#colons}}}
1907
1908 echo $allcol_ip | tr : '\n' |
1909 sed s/^/0000/ |
1910 sed 's/.*\(..\)\(..\)/\1'"$bytesep"'\2/' |
1911 tr '\n' : |
1912 sed 's/:$//'
1913}
1914
1915ipv6_to_bytes()
1916{
1917 local IP=$1; shift
1918
1919 expand_ipv6 "$IP" :
1920}
1921
1922u16_to_bytes()
1923{
1924 local u16=$1; shift
1925
1926 printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/'
1927}
1928
1929# Given a mausezahn-formatted payload (colon-separated bytes given as %02x),
1930# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be,
1931# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any)
1932# stands for 00:00.
1933payload_template_calc_checksum()
1934{
1935 local payload=$1; shift
1936
1937 (
1938 # Set input radix.
1939 echo "16i"
1940 # Push zero for the initial checksum.
1941 echo 0
1942
1943 # Pad the payload with a terminating 00: in case we get an odd
1944 # number of bytes.
1945 echo "${payload%:}:00:" |
1946 sed 's/CHECKSUM/00:00/g' |
1947 tr '[:lower:]' '[:upper:]' |
1948 # Add the word to the checksum.
1949 sed 's/\(..\):\(..\):/\1\2+\n/g' |
1950 # Strip the extra odd byte we pushed if left unconverted.
1951 sed 's/\(..\):$//'
1952
1953 echo "10000 ~ +" # Calculate and add carry.
1954 echo "FFFF r - p" # Bit-flip and print.
1955 ) |
1956 dc |
1957 tr '[:upper:]' '[:lower:]'
1958}
1959
1960payload_template_expand_checksum()
1961{
1962 local payload=$1; shift
1963 local checksum=$1; shift
1964
1965 local ckbytes=$(u16_to_bytes $checksum)
1966
1967 echo "$payload" | sed "s/CHECKSUM/$ckbytes/g"
1968}
1969
1970payload_template_nbytes()
1971{
1972 local payload=$1; shift
1973
1974 payload_template_expand_checksum "${payload%:}" 0 |
1975 sed 's/:/\n/g' | wc -l
1976}
1977
1978igmpv3_is_in_get()
1979{
1980 local GRP=$1; shift
1981 local sources=("$@")
1982
1983 local igmpv3
1984 local nsources=$(u16_to_bytes ${#sources[@]})
1985
1986 # IS_IN ( $sources )
1987 igmpv3=$(:
1988 )"22:"$( : Type - Membership Report
1989 )"00:"$( : Reserved
1990 )"CHECKSUM:"$( : Checksum
1991 )"00:00:"$( : Reserved
1992 )"00:01:"$( : Number of Group Records
1993 )"01:"$( : Record Type - IS_IN
1994 )"00:"$( : Aux Data Len
1995 )"${nsources}:"$( : Number of Sources
1996 )"$(ipv4_to_bytes $GRP):"$( : Multicast Address
1997 )"$(for src in "${sources[@]}"; do
1998 ipv4_to_bytes $src
1999 echo -n :
2000 done)"$( : Source Addresses
2001 )
2002 local checksum=$(payload_template_calc_checksum "$igmpv3")
2003
2004 payload_template_expand_checksum "$igmpv3" $checksum
2005}
2006
2007igmpv2_leave_get()
2008{
2009 local GRP=$1; shift
2010
2011 local payload=$(:
2012 )"17:"$( : Type - Leave Group
2013 )"00:"$( : Max Resp Time - not meaningful
2014 )"CHECKSUM:"$( : Checksum
2015 )"$(ipv4_to_bytes $GRP)"$( : Group Address
2016 )
2017 local checksum=$(payload_template_calc_checksum "$payload")
2018
2019 payload_template_expand_checksum "$payload" $checksum
2020}
2021
2022mldv2_is_in_get()
2023{
2024 local SIP=$1; shift
2025 local GRP=$1; shift
2026 local sources=("$@")
2027
2028 local hbh
2029 local icmpv6
2030 local nsources=$(u16_to_bytes ${#sources[@]})
2031
2032 hbh=$(:
2033 )"3a:"$( : Next Header - ICMPv6
2034 )"00:"$( : Hdr Ext Len
2035 )"00:00:00:00:00:00:"$( : Options and Padding
2036 )
2037
2038 icmpv6=$(:
2039 )"8f:"$( : Type - MLDv2 Report
2040 )"00:"$( : Code
2041 )"CHECKSUM:"$( : Checksum
2042 )"00:00:"$( : Reserved
2043 )"00:01:"$( : Number of Group Records
2044 )"01:"$( : Record Type - IS_IN
2045 )"00:"$( : Aux Data Len
2046 )"${nsources}:"$( : Number of Sources
2047 )"$(ipv6_to_bytes $GRP):"$( : Multicast address
2048 )"$(for src in "${sources[@]}"; do
2049 ipv6_to_bytes $src
2050 echo -n :
2051 done)"$( : Source Addresses
2052 )
2053
2054 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6))
2055 local sudohdr=$(:
2056 )"$(ipv6_to_bytes $SIP):"$( : SIP
2057 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address
2058 )"${len}:"$( : Upper-layer length
2059 )"00:3a:"$( : Zero and next-header
2060 )
2061 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6})
2062
2063 payload_template_expand_checksum "$hbh$icmpv6" $checksum
2064}
2065
2066mldv1_done_get()
2067{
2068 local SIP=$1; shift
2069 local GRP=$1; shift
2070
2071 local hbh
2072 local icmpv6
2073
2074 hbh=$(:
2075 )"3a:"$( : Next Header - ICMPv6
2076 )"00:"$( : Hdr Ext Len
2077 )"00:00:00:00:00:00:"$( : Options and Padding
2078 )
2079
2080 icmpv6=$(:
2081 )"84:"$( : Type - MLDv1 Done
2082 )"00:"$( : Code
2083 )"CHECKSUM:"$( : Checksum
2084 )"00:00:"$( : Max Resp Delay - not meaningful
2085 )"00:00:"$( : Reserved
2086 )"$(ipv6_to_bytes $GRP):"$( : Multicast address
2087 )
2088
2089 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6))
2090 local sudohdr=$(:
2091 )"$(ipv6_to_bytes $SIP):"$( : SIP
2092 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address
2093 )"${len}:"$( : Upper-layer length
2094 )"00:3a:"$( : Zero and next-header
2095 )
2096 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6})
2097
2098 payload_template_expand_checksum "$hbh$icmpv6" $checksum
2099}
2100
2101bail_on_lldpad()
2102{
2103 local reason1="$1"; shift
2104 local reason2="$1"; shift
2105 local caller=${FUNCNAME[1]}
2106 local src=${BASH_SOURCE[1]}
2107
2108 if systemctl is-active --quiet lldpad; then
2109
2110 cat >/dev/stderr <<-EOF
2111 WARNING: lldpad is running
2112
2113 lldpad will likely $reason1, and this test will
2114 $reason2. Both are not supported at the same time,
2115 one of them is arbitrarily going to overwrite the
2116 other. That will cause spurious failures (or, unlikely,
2117 passes) of this test.
2118 EOF
2119
2120 if [[ -z $ALLOW_LLDPAD ]]; then
2121 cat >/dev/stderr <<-EOF
2122
2123 If you want to run the test anyway, please set
2124 an environment variable ALLOW_LLDPAD to a
2125 non-empty string.
2126 EOF
2127 log_test_skip $src:$caller
2128 exit $EXIT_STATUS
2129 else
2130 return
2131 fi
2132 fi
2133}
2134
2135absval()
2136{
2137 local v=$1; shift
2138
2139 echo $((v > 0 ? v : -v))
2140}
2141
2142has_unicast_flt()
2143{
2144 local dev=$1; shift
2145 local mac_addr=$(mac_get $dev)
2146 local tmp=$(ether_addr_to_u64 $mac_addr)
2147 local promisc
2148
2149 ip link set $dev up
2150 ip link add link $dev name macvlan-tmp type macvlan mode private
2151 ip link set macvlan-tmp address $(u64_to_ether_addr $((tmp + 1)))
2152 ip link set macvlan-tmp up
2153
2154 promisc=$(ip -j -d link show dev $dev | jq -r '.[].promiscuity')
2155
2156 ip link del macvlan-tmp
2157
2158 [[ $promisc == 1 ]] && echo "no" || echo "yes"
2159}