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