projects/linux·blob master · refs

2313 lines · 46.9 KiB

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