]> Git Repo - linux.git/blob - tools/testing/selftests/net/forwarding/lib.sh
Linux 6.14-rc3
[linux.git] / tools / testing / selftests / net / forwarding / lib.sh
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
7 declare -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
90 driver_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
100 netif_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
118 if [[ $NETIF_FIND_DRIVER ]]; then
119         unset NETIFS
120         declare -A NETIFS
121         netif_find_driver
122 fi
123
124 net_forwarding_dir=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
125
126 if [[ -f $net_forwarding_dir/forwarding.config ]]; then
127         source "$net_forwarding_dir/forwarding.config"
128 fi
129
130 source "$net_forwarding_dir/../lib.sh"
131
132 ##############################################################################
133 # Sanity checks
134
135 check_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"
145 check_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
160 check_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
185 check_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
194 check_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
203 check_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
212 check_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
221 check_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
230 check_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
239 check_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
248 check_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
257 check_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
265 check_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
273 if [[ "$(id -u)" -ne 0 ]]; then
274         echo "SKIP: need root privileges"
275         exit $ksft_skip
276 fi
277
278 check_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
290 if [[ "$CHECK_TC" = "yes" ]]; then
291         check_tc_version
292 fi
293
294 require_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
305 check_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
319 if [[ "$REQUIRE_JQ" = "yes" ]]; then
320         require_command jq
321 fi
322 if [[ "$REQUIRE_MZ" = "yes" ]]; then
323         require_command $MZ
324 fi
325 if [[ "$REQUIRE_TEAMD" = "yes" ]]; then
326         require_command $TEAMD
327 fi
328 if [[ "$REQUIRE_MTOOLS" = "yes" ]]; then
329         # https://github.com/troglobit/mtools
330         require_command msend
331         require_command mreceive
332         check_mtools_version
333 fi
334
335 ##############################################################################
336 # Command line options handling
337
338 count=0
339
340 while [[ $# -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
348 done
349
350 ##############################################################################
351 # Network interfaces configuration
352
353 if [[ ! -v NUM_NETIFS ]]; then
354         echo "SKIP: importer does not define \"NUM_NETIFS\""
355         exit $ksft_skip
356 fi
357
358 if (( NUM_NETIFS > ${#NETIFS[@]} )); then
359         echo "SKIP: Importer requires $NUM_NETIFS NETIFS, but only ${#NETIFS[@]} are defined (${NETIFS[@]})"
360         exit $ksft_skip
361 fi
362
363 for 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
368 done
369
370 create_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
395 create_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
406 declare -A MAC_ADDR_ORIG
407 mac_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
423 mac_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
433 if [[ "$NETIF_CREATE" = "yes" ]]; then
434         create_netif
435 fi
436
437 if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then
438         mac_addr_prepare
439 fi
440
441 for ((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
447 done
448
449 ##############################################################################
450 # Helpers
451
452 not()
453 {
454         "$@"
455         [[ $? != 0 ]]
456 }
457
458 get_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
472 grep_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
488 wait_for_port_up()
489 {
490         "$@" | grep -q "Link detected: yes"
491 }
492
493 wait_for_offload()
494 {
495         "$@" | grep -q offload
496 }
497
498 wait_for_trap()
499 {
500         "$@" | grep -q trap
501 }
502
503 setup_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
517 setup_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
538 setup_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
551 wait_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
564 cmd_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
588 pre_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
600 vrf_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
608 vrf_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
617 declare -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
635 vrf_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
648 vrf_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
699 simple_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
714 simple_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
728 tunnel_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
740 tunnel_destroy()
741 {
742         local name=$1; shift
743
744         ip link del dev $name
745 }
746
747 vlan_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
763 vlan_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
772 team_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
787 team_destroy()
788 {
789         local if_name=$1; shift
790
791         $TEAMD -t $if_name -k
792 }
793
794 master_name_get()
795 {
796         local if_name=$1
797
798         ip -j link show dev $if_name | jq -r '.[]["master"]'
799 }
800
801 link_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
811 link_stats_tx_packets_get()
812 {
813         link_stats_get $1 tx packets
814 }
815
816 link_stats_rx_errors_get()
817 {
818         link_stats_get $1 rx errors
819 }
820
821 ethtool_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
829 ethtool_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
840 qdisc_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
850 qdisc_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
860 ipv6_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
868 hw_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
890 nh_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
898 nh_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
906 humanize()
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
921 rate()
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
930 packets_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
939 ether_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
957 u64_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
972 ipv6_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
981 bridge_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
992 declare -A SYSCTL_ORIG
993 sysctl_save()
994 {
995         local key=$1; shift
996
997         SYSCTL_ORIG[$key]=$(sysctl -n $key)
998 }
999
1000 sysctl_set()
1001 {
1002         local key=$1; shift
1003         local value=$1; shift
1004
1005         sysctl_save "$key"
1006         sysctl -qw $key="$value"
1007 }
1008
1009 sysctl_restore()
1010 {
1011         local key=$1; shift
1012
1013         sysctl -qw $key="${SYSCTL_ORIG[$key]}"
1014 }
1015
1016 forwarding_enable()
1017 {
1018         sysctl_set net.ipv4.conf.all.forwarding 1
1019         sysctl_set net.ipv6.conf.all.forwarding 1
1020 }
1021
1022 forwarding_restore()
1023 {
1024         sysctl_restore net.ipv6.conf.all.forwarding
1025         sysctl_restore net.ipv4.conf.all.forwarding
1026 }
1027
1028 declare -A MTU_ORIG
1029 mtu_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
1038 mtu_restore()
1039 {
1040         local dev=$1; shift
1041
1042         ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
1043 }
1044
1045 tc_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
1060 trap_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
1076 trap_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
1098 icmp_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
1106 icmp_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
1114 icmp6_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
1122 icmp6_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
1143 vlan_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
1151 vlan_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
1173 dscp_capture_install()
1174 {
1175         local dev=$1; shift
1176         local base=$1; shift
1177
1178         __dscp_capture_add_del add $dev $base
1179 }
1180
1181 dscp_capture_uninstall()
1182 {
1183         local dev=$1; shift
1184         local base=$1; shift
1185
1186         __dscp_capture_add_del del $dev $base
1187 }
1188
1189 dscp_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
1201 matchall_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
1212 cleanup()
1213 {
1214         pre_cleanup
1215         defer_scopes_cleanup
1216 }
1217
1218 multipath_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
1261 in_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
1275 ping_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
1288 ping_test()
1289 {
1290         RET=0
1291
1292         ping_do $1 $2
1293         check_err $?
1294         log_test "ping$3"
1295 }
1296
1297 ping_test_fails()
1298 {
1299         RET=0
1300
1301         ping_do $1 $2
1302         check_fail $?
1303         log_test "ping fails$3"
1304 }
1305
1306 ping6_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
1319 ping6_test()
1320 {
1321         RET=0
1322
1323         ping6_do $1 $2
1324         check_err $?
1325         log_test "ping6$3"
1326 }
1327
1328 ping6_test_fails()
1329 {
1330         RET=0
1331
1332         ping6_do $1 $2
1333         check_fail $?
1334         log_test "ping6 fails$3"
1335 }
1336
1337 learning_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
1414 flood_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
1448 flood_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
1471 flood_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
1494 flood_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
1520 start_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
1533 start_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
1546 start_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
1558 start_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
1570 stop_traffic()
1571 {
1572         local pid=${1-%%}; shift
1573
1574         kill_process "$pid"
1575 }
1576
1577 declare -A cappid
1578 declare -A capfile
1579 declare -A capout
1580
1581 tcpdump_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
1609 tcpdump_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
1618 tcpdump_cleanup()
1619 {
1620         local if_name=$1
1621
1622         rm ${capfile[$if_name]} ${capout[$if_name]}
1623 }
1624
1625 tcpdump_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
1633 mcast_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
1672 brmcast_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
1694 brmcast_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
1712 brmcast_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
1739 mc_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
1755 mc_leave()
1756 {
1757         kill "$mreceive_pid" && wait "$mreceive_pid"
1758 }
1759
1760 mc_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
1770 start_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
1782 stop_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
1797 hw_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
1830 ipv4_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.
1842 expand_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
1860 ipv6_to_bytes()
1861 {
1862         local IP=$1; shift
1863
1864         expand_ipv6 "$IP" :
1865 }
1866
1867 u16_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.
1878 payload_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
1905 payload_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
1915 payload_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
1923 igmpv3_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
1952 igmpv2_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
1967 mldv2_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
2011 mldv1_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
2046 bail_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
2080 absval()
2081 {
2082         local v=$1; shift
2083
2084         echo $((v > 0 ? v : -v))
2085 }
2086
2087 has_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 }
This page took 0.15255 seconds and 4 git commands to generate.