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