]> Git Repo - linux.git/blob - tools/testing/selftests/net/lib.sh
Linux 6.14-rc3
[linux.git] / tools / testing / selftests / net / lib.sh
1 #!/bin/bash
2 # SPDX-License-Identifier: GPL-2.0
3
4 net_dir=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
5 source "$net_dir/lib/sh/defer.sh"
6
7 ##############################################################################
8 # Defines
9
10 : "${WAIT_TIMEOUT:=20}"
11
12 # Whether to pause on after a failure.
13 : "${PAUSE_ON_FAIL:=no}"
14
15 BUSYWAIT_TIMEOUT=$((WAIT_TIMEOUT * 1000)) # ms
16
17 # Kselftest framework constants.
18 ksft_pass=0
19 ksft_fail=1
20 ksft_xfail=2
21 ksft_skip=4
22
23 # namespace list created by setup_ns
24 NS_LIST=()
25
26 # Exit status to return at the end. Set in case one of the tests fails.
27 EXIT_STATUS=0
28 # Per-test return value. Clear at the beginning of each test.
29 RET=0
30
31 ##############################################################################
32 # Helpers
33
34 __ksft_status_merge()
35 {
36         local a=$1; shift
37         local b=$1; shift
38         local -A weights
39         local weight=0
40
41         local i
42         for i in "$@"; do
43                 weights[$i]=$((weight++))
44         done
45
46         if [[ ${weights[$a]} > ${weights[$b]} ]]; then
47                 echo "$a"
48                 return 0
49         else
50                 echo "$b"
51                 return 1
52         fi
53 }
54
55 ksft_status_merge()
56 {
57         local a=$1; shift
58         local b=$1; shift
59
60         __ksft_status_merge "$a" "$b" \
61                 $ksft_pass $ksft_xfail $ksft_skip $ksft_fail
62 }
63
64 ksft_exit_status_merge()
65 {
66         local a=$1; shift
67         local b=$1; shift
68
69         __ksft_status_merge "$a" "$b" \
70                 $ksft_xfail $ksft_pass $ksft_skip $ksft_fail
71 }
72
73 loopy_wait()
74 {
75         local sleep_cmd=$1; shift
76         local timeout_ms=$1; shift
77
78         local start_time="$(date -u +%s%3N)"
79         while true
80         do
81                 local out
82                 if out=$("$@"); then
83                         echo -n "$out"
84                         return 0
85                 fi
86
87                 local current_time="$(date -u +%s%3N)"
88                 if ((current_time - start_time > timeout_ms)); then
89                         echo -n "$out"
90                         return 1
91                 fi
92
93                 $sleep_cmd
94         done
95 }
96
97 busywait()
98 {
99         local timeout_ms=$1; shift
100
101         loopy_wait : "$timeout_ms" "$@"
102 }
103
104 # timeout in seconds
105 slowwait()
106 {
107         local timeout_sec=$1; shift
108
109         loopy_wait "sleep 0.1" "$((timeout_sec * 1000))" "$@"
110 }
111
112 until_counter_is()
113 {
114         local expr=$1; shift
115         local current=$("$@")
116
117         echo $((current))
118         ((current $expr))
119 }
120
121 busywait_for_counter()
122 {
123         local timeout=$1; shift
124         local delta=$1; shift
125
126         local base=$("$@")
127         busywait "$timeout" until_counter_is ">= $((base + delta))" "$@"
128 }
129
130 slowwait_for_counter()
131 {
132         local timeout=$1; shift
133         local delta=$1; shift
134
135         local base=$("$@")
136         slowwait "$timeout" until_counter_is ">= $((base + delta))" "$@"
137 }
138
139 # Check for existence of tools which are built as part of selftests
140 # but may also already exist in $PATH
141 check_gen_prog()
142 {
143         local prog_name=$1; shift
144
145         if ! which $prog_name >/dev/null 2>/dev/null; then
146                 PATH=$PWD:$PATH
147                 if ! which $prog_name >/dev/null; then
148                         echo "'$prog_name' command not found; skipping tests"
149                         exit $ksft_skip
150                 fi
151         fi
152 }
153
154 remove_ns_list()
155 {
156         local item=$1
157         local ns
158         local ns_list=("${NS_LIST[@]}")
159         NS_LIST=()
160
161         for ns in "${ns_list[@]}"; do
162                 if [ "${ns}" != "${item}" ]; then
163                         NS_LIST+=("${ns}")
164                 fi
165         done
166 }
167
168 cleanup_ns()
169 {
170         local ns=""
171         local ret=0
172
173         for ns in "$@"; do
174                 [ -z "${ns}" ] && continue
175                 ip netns pids "${ns}" 2> /dev/null | xargs -r kill || true
176                 ip netns delete "${ns}" &> /dev/null || true
177                 if ! busywait $BUSYWAIT_TIMEOUT ip netns list \| grep -vq "^$ns$" &> /dev/null; then
178                         echo "Warn: Failed to remove namespace $ns"
179                         ret=1
180                 else
181                         remove_ns_list "${ns}"
182                 fi
183         done
184
185         return $ret
186 }
187
188 cleanup_all_ns()
189 {
190         cleanup_ns "${NS_LIST[@]}"
191 }
192
193 # setup netns with given names as prefix. e.g
194 # setup_ns local remote
195 setup_ns()
196 {
197         local ns_name=""
198         local ns_list=()
199         for ns_name in "$@"; do
200                 # avoid conflicts with local var: internal error
201                 if [ "${ns_name}" = "ns_name" ]; then
202                         echo "Failed to setup namespace '${ns_name}': invalid name"
203                         cleanup_ns "${ns_list[@]}"
204                         exit $ksft_fail
205                 fi
206
207                 # Some test may setup/remove same netns multi times
208                 if [ -z "${!ns_name}" ]; then
209                         eval "${ns_name}=${ns_name,,}-$(mktemp -u XXXXXX)"
210                 else
211                         cleanup_ns "${!ns_name}"
212                 fi
213
214                 if ! ip netns add "${!ns_name}"; then
215                         echo "Failed to create namespace $ns_name"
216                         cleanup_ns "${ns_list[@]}"
217                         return $ksft_skip
218                 fi
219                 ip -n "${!ns_name}" link set lo up
220                 ns_list+=("${!ns_name}")
221         done
222         NS_LIST+=("${ns_list[@]}")
223 }
224
225 tc_rule_stats_get()
226 {
227         local dev=$1; shift
228         local pref=$1; shift
229         local dir=${1:-ingress}; shift
230         local selector=${1:-.packets}; shift
231
232         tc -j -s filter show dev $dev $dir pref $pref \
233             | jq ".[1].options.actions[].stats$selector"
234 }
235
236 tc_rule_handle_stats_get()
237 {
238         local id=$1; shift
239         local handle=$1; shift
240         local selector=${1:-.packets}; shift
241         local netns=${1:-""}; shift
242
243         tc $netns -j -s filter show $id \
244             | jq ".[] | select(.options.handle == $handle) | \
245                   .options.actions[0].stats$selector"
246 }
247
248 ret_set_ksft_status()
249 {
250         local ksft_status=$1; shift
251         local msg=$1; shift
252
253         RET=$(ksft_status_merge $RET $ksft_status)
254         if (( $? )); then
255                 retmsg=$msg
256         fi
257 }
258
259 log_test_result()
260 {
261         local test_name=$1; shift
262         local opt_str=$1; shift
263         local result=$1; shift
264         local retmsg=$1; shift
265
266         printf "TEST: %-60s  [%s]\n" "$test_name $opt_str" "$result"
267         if [[ $retmsg ]]; then
268                 printf "\t%s\n" "$retmsg"
269         fi
270 }
271
272 pause_on_fail()
273 {
274         if [[ $PAUSE_ON_FAIL == yes ]]; then
275                 echo "Hit enter to continue, 'q' to quit"
276                 read a
277                 [[ $a == q ]] && exit 1
278         fi
279 }
280
281 handle_test_result_pass()
282 {
283         local test_name=$1; shift
284         local opt_str=$1; shift
285
286         log_test_result "$test_name" "$opt_str" " OK "
287 }
288
289 handle_test_result_fail()
290 {
291         local test_name=$1; shift
292         local opt_str=$1; shift
293
294         log_test_result "$test_name" "$opt_str" FAIL "$retmsg"
295         pause_on_fail
296 }
297
298 handle_test_result_xfail()
299 {
300         local test_name=$1; shift
301         local opt_str=$1; shift
302
303         log_test_result "$test_name" "$opt_str" XFAIL "$retmsg"
304         pause_on_fail
305 }
306
307 handle_test_result_skip()
308 {
309         local test_name=$1; shift
310         local opt_str=$1; shift
311
312         log_test_result "$test_name" "$opt_str" SKIP "$retmsg"
313 }
314
315 log_test()
316 {
317         local test_name=$1
318         local opt_str=$2
319
320         if [[ $# -eq 2 ]]; then
321                 opt_str="($opt_str)"
322         fi
323
324         if ((RET == ksft_pass)); then
325                 handle_test_result_pass "$test_name" "$opt_str"
326         elif ((RET == ksft_xfail)); then
327                 handle_test_result_xfail "$test_name" "$opt_str"
328         elif ((RET == ksft_skip)); then
329                 handle_test_result_skip "$test_name" "$opt_str"
330         else
331                 handle_test_result_fail "$test_name" "$opt_str"
332         fi
333
334         EXIT_STATUS=$(ksft_exit_status_merge $EXIT_STATUS $RET)
335         return $RET
336 }
337
338 log_test_skip()
339 {
340         RET=$ksft_skip retmsg= log_test "$@"
341 }
342
343 log_test_xfail()
344 {
345         RET=$ksft_xfail retmsg= log_test "$@"
346 }
347
348 log_info()
349 {
350         local msg=$1
351
352         echo "INFO: $msg"
353 }
354
355 tests_run()
356 {
357         local current_test
358
359         for current_test in ${TESTS:-$ALL_TESTS}; do
360                 in_defer_scope \
361                         $current_test
362         done
363 }
364
365 # Whether FAILs should be interpreted as XFAILs. Internal.
366 FAIL_TO_XFAIL=
367
368 check_err()
369 {
370         local err=$1
371         local msg=$2
372
373         if ((err)); then
374                 if [[ $FAIL_TO_XFAIL = yes ]]; then
375                         ret_set_ksft_status $ksft_xfail "$msg"
376                 else
377                         ret_set_ksft_status $ksft_fail "$msg"
378                 fi
379         fi
380 }
381
382 check_fail()
383 {
384         local err=$1
385         local msg=$2
386
387         check_err $((!err)) "$msg"
388 }
389
390 check_err_fail()
391 {
392         local should_fail=$1; shift
393         local err=$1; shift
394         local what=$1; shift
395
396         if ((should_fail)); then
397                 check_fail $err "$what succeeded, but should have failed"
398         else
399                 check_err $err "$what failed"
400         fi
401 }
402
403 xfail()
404 {
405         FAIL_TO_XFAIL=yes "$@"
406 }
407
408 xfail_on_slow()
409 {
410         if [[ $KSFT_MACHINE_SLOW = yes ]]; then
411                 FAIL_TO_XFAIL=yes "$@"
412         else
413                 "$@"
414         fi
415 }
416
417 omit_on_slow()
418 {
419         if [[ $KSFT_MACHINE_SLOW != yes ]]; then
420                 "$@"
421         fi
422 }
423
424 xfail_on_veth()
425 {
426         local dev=$1; shift
427         local kind
428
429         kind=$(ip -j -d link show dev $dev |
430                         jq -r '.[].linkinfo.info_kind')
431         if [[ $kind = veth ]]; then
432                 FAIL_TO_XFAIL=yes "$@"
433         else
434                 "$@"
435         fi
436 }
437
438 mac_get()
439 {
440         local if_name=$1
441
442         ip -j link show dev $if_name | jq -r '.[]["address"]'
443 }
444
445 kill_process()
446 {
447         local pid=$1; shift
448
449         # Suppress noise from killing the process.
450         { kill $pid && wait $pid; } 2>/dev/null
451 }
452
453 ip_link_add()
454 {
455         local name=$1; shift
456
457         ip link add name "$name" "$@"
458         defer ip link del dev "$name"
459 }
460
461 ip_link_set_master()
462 {
463         local member=$1; shift
464         local master=$1; shift
465
466         ip link set dev "$member" master "$master"
467         defer ip link set dev "$member" nomaster
468 }
469
470 ip_link_set_addr()
471 {
472         local name=$1; shift
473         local addr=$1; shift
474
475         local old_addr=$(mac_get "$name")
476         ip link set dev "$name" address "$addr"
477         defer ip link set dev "$name" address "$old_addr"
478 }
479
480 ip_link_is_up()
481 {
482         local name=$1; shift
483
484         local state=$(ip -j link show "$name" |
485                       jq -r '(.[].flags[] | select(. == "UP")) // "DOWN"')
486         [[ $state == "UP" ]]
487 }
488
489 ip_link_set_up()
490 {
491         local name=$1; shift
492
493         if ! ip_link_is_up "$name"; then
494                 ip link set dev "$name" up
495                 defer ip link set dev "$name" down
496         fi
497 }
498
499 ip_link_set_down()
500 {
501         local name=$1; shift
502
503         if ip_link_is_up "$name"; then
504                 ip link set dev "$name" down
505                 defer ip link set dev "$name" up
506         fi
507 }
508
509 ip_addr_add()
510 {
511         local name=$1; shift
512
513         ip addr add dev "$name" "$@"
514         defer ip addr del dev "$name" "$@"
515 }
516
517 ip_route_add()
518 {
519         ip route add "$@"
520         defer ip route del "$@"
521 }
522
523 bridge_vlan_add()
524 {
525         bridge vlan add "$@"
526         defer bridge vlan del "$@"
527 }
This page took 0.06301 seconds and 4 git commands to generate.