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