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