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