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