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