Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4##############################################################################
5# Defines
6
7# Can be overridden by the configuration file.
8PING=${PING:=ping}
9PING6=${PING6:=ping6}
10MZ=${MZ:=mausezahn}
11WAIT_TIME=${WAIT_TIME:=5}
12PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
13PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no}
14NETIF_TYPE=${NETIF_TYPE:=veth}
15NETIF_CREATE=${NETIF_CREATE:=yes}
16
17if [[ -f forwarding.config ]]; then
18 source forwarding.config
19fi
20
21##############################################################################
22# Sanity checks
23
24check_tc_version()
25{
26 tc -j &> /dev/null
27 if [[ $? -ne 0 ]]; then
28 echo "SKIP: iproute2 too old; tc is missing JSON support"
29 exit 1
30 fi
31
32 tc filter help 2>&1 | grep block &> /dev/null
33 if [[ $? -ne 0 ]]; then
34 echo "SKIP: iproute2 too old; tc is missing shared block support"
35 exit 1
36 fi
37}
38
39if [[ "$(id -u)" -ne 0 ]]; then
40 echo "SKIP: need root privileges"
41 exit 0
42fi
43
44if [[ "$CHECK_TC" = "yes" ]]; then
45 check_tc_version
46fi
47
48if [[ ! -x "$(command -v jq)" ]]; then
49 echo "SKIP: jq not installed"
50 exit 1
51fi
52
53if [[ ! -x "$(command -v $MZ)" ]]; then
54 echo "SKIP: $MZ not installed"
55 exit 1
56fi
57
58if [[ ! -v NUM_NETIFS ]]; then
59 echo "SKIP: importer does not define \"NUM_NETIFS\""
60 exit 1
61fi
62
63##############################################################################
64# Command line options handling
65
66count=0
67
68while [[ $# -gt 0 ]]; do
69 if [[ "$count" -eq "0" ]]; then
70 unset NETIFS
71 declare -A NETIFS
72 fi
73 count=$((count + 1))
74 NETIFS[p$count]="$1"
75 shift
76done
77
78##############################################################################
79# Network interfaces configuration
80
81create_netif_veth()
82{
83 local i
84
85 for i in $(eval echo {1..$NUM_NETIFS}); do
86 local j=$((i+1))
87
88 ip link show dev ${NETIFS[p$i]} &> /dev/null
89 if [[ $? -ne 0 ]]; then
90 ip link add ${NETIFS[p$i]} type veth \
91 peer name ${NETIFS[p$j]}
92 if [[ $? -ne 0 ]]; then
93 echo "Failed to create netif"
94 exit 1
95 fi
96 fi
97 i=$j
98 done
99}
100
101create_netif()
102{
103 case "$NETIF_TYPE" in
104 veth) create_netif_veth
105 ;;
106 *) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
107 exit 1
108 ;;
109 esac
110}
111
112if [[ "$NETIF_CREATE" = "yes" ]]; then
113 create_netif
114fi
115
116for i in $(eval echo {1..$NUM_NETIFS}); do
117 ip link show dev ${NETIFS[p$i]} &> /dev/null
118 if [[ $? -ne 0 ]]; then
119 echo "SKIP: could not find all required interfaces"
120 exit 1
121 fi
122done
123
124##############################################################################
125# Helpers
126
127# Exit status to return at the end. Set in case one of the tests fails.
128EXIT_STATUS=0
129# Per-test return value. Clear at the beginning of each test.
130RET=0
131
132check_err()
133{
134 local err=$1
135 local msg=$2
136
137 if [[ $RET -eq 0 && $err -ne 0 ]]; then
138 RET=$err
139 retmsg=$msg
140 fi
141}
142
143check_fail()
144{
145 local err=$1
146 local msg=$2
147
148 if [[ $RET -eq 0 && $err -eq 0 ]]; then
149 RET=1
150 retmsg=$msg
151 fi
152}
153
154log_test()
155{
156 local test_name=$1
157 local opt_str=$2
158
159 if [[ $# -eq 2 ]]; then
160 opt_str="($opt_str)"
161 fi
162
163 if [[ $RET -ne 0 ]]; then
164 EXIT_STATUS=1
165 printf "TEST: %-60s [FAIL]\n" "$test_name $opt_str"
166 if [[ ! -z "$retmsg" ]]; then
167 printf "\t%s\n" "$retmsg"
168 fi
169 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
170 echo "Hit enter to continue, 'q' to quit"
171 read a
172 [ "$a" = "q" ] && exit 1
173 fi
174 return 1
175 fi
176
177 printf "TEST: %-60s [PASS]\n" "$test_name $opt_str"
178 return 0
179}
180
181log_info()
182{
183 local msg=$1
184
185 echo "INFO: $msg"
186}
187
188setup_wait()
189{
190 for i in $(eval echo {1..$NUM_NETIFS}); do
191 while true; do
192 ip link show dev ${NETIFS[p$i]} up \
193 | grep 'state UP' &> /dev/null
194 if [[ $? -ne 0 ]]; then
195 sleep 1
196 else
197 break
198 fi
199 done
200 done
201
202 # Make sure links are ready.
203 sleep $WAIT_TIME
204}
205
206pre_cleanup()
207{
208 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
209 echo "Pausing before cleanup, hit any key to continue"
210 read
211 fi
212}
213
214vrf_prepare()
215{
216 ip -4 rule add pref 32765 table local
217 ip -4 rule del pref 0
218 ip -6 rule add pref 32765 table local
219 ip -6 rule del pref 0
220}
221
222vrf_cleanup()
223{
224 ip -6 rule add pref 0 table local
225 ip -6 rule del pref 32765
226 ip -4 rule add pref 0 table local
227 ip -4 rule del pref 32765
228}
229
230__last_tb_id=0
231declare -A __TB_IDS
232
233__vrf_td_id_assign()
234{
235 local vrf_name=$1
236
237 __last_tb_id=$((__last_tb_id + 1))
238 __TB_IDS[$vrf_name]=$__last_tb_id
239 return $__last_tb_id
240}
241
242__vrf_td_id_lookup()
243{
244 local vrf_name=$1
245
246 return ${__TB_IDS[$vrf_name]}
247}
248
249vrf_create()
250{
251 local vrf_name=$1
252 local tb_id
253
254 __vrf_td_id_assign $vrf_name
255 tb_id=$?
256
257 ip link add dev $vrf_name type vrf table $tb_id
258 ip -4 route add table $tb_id unreachable default metric 4278198272
259 ip -6 route add table $tb_id unreachable default metric 4278198272
260}
261
262vrf_destroy()
263{
264 local vrf_name=$1
265 local tb_id
266
267 __vrf_td_id_lookup $vrf_name
268 tb_id=$?
269
270 ip -6 route del table $tb_id unreachable default metric 4278198272
271 ip -4 route del table $tb_id unreachable default metric 4278198272
272 ip link del dev $vrf_name
273}
274
275__addr_add_del()
276{
277 local if_name=$1
278 local add_del=$2
279 local array
280
281 shift
282 shift
283 array=("${@}")
284
285 for addrstr in "${array[@]}"; do
286 ip address $add_del $addrstr dev $if_name
287 done
288}
289
290simple_if_init()
291{
292 local if_name=$1
293 local vrf_name
294 local array
295
296 shift
297 vrf_name=v$if_name
298 array=("${@}")
299
300 vrf_create $vrf_name
301 ip link set dev $if_name master $vrf_name
302 ip link set dev $vrf_name up
303 ip link set dev $if_name up
304
305 __addr_add_del $if_name add "${array[@]}"
306}
307
308simple_if_fini()
309{
310 local if_name=$1
311 local vrf_name
312 local array
313
314 shift
315 vrf_name=v$if_name
316 array=("${@}")
317
318 __addr_add_del $if_name del "${array[@]}"
319
320 ip link set dev $if_name down
321 vrf_destroy $vrf_name
322}
323
324master_name_get()
325{
326 local if_name=$1
327
328 ip -j link show dev $if_name | jq -r '.[]["master"]'
329}
330
331link_stats_tx_packets_get()
332{
333 local if_name=$1
334
335 ip -j -s link show dev $if_name | jq '.[]["stats64"]["tx"]["packets"]'
336}
337
338mac_get()
339{
340 local if_name=$1
341
342 ip -j link show dev $if_name | jq -r '.[]["address"]'
343}
344
345bridge_ageing_time_get()
346{
347 local bridge=$1
348 local ageing_time
349
350 # Need to divide by 100 to convert to seconds.
351 ageing_time=$(ip -j -d link show dev $bridge \
352 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
353 echo $((ageing_time / 100))
354}
355
356forwarding_enable()
357{
358 ipv4_fwd=$(sysctl -n net.ipv4.conf.all.forwarding)
359 ipv6_fwd=$(sysctl -n net.ipv6.conf.all.forwarding)
360
361 sysctl -q -w net.ipv4.conf.all.forwarding=1
362 sysctl -q -w net.ipv6.conf.all.forwarding=1
363}
364
365forwarding_restore()
366{
367 sysctl -q -w net.ipv6.conf.all.forwarding=$ipv6_fwd
368 sysctl -q -w net.ipv4.conf.all.forwarding=$ipv4_fwd
369}
370
371tc_offload_check()
372{
373 for i in $(eval echo {1..$NUM_NETIFS}); do
374 ethtool -k ${NETIFS[p$i]} \
375 | grep "hw-tc-offload: on" &> /dev/null
376 if [[ $? -ne 0 ]]; then
377 return 1
378 fi
379 done
380
381 return 0
382}
383
384##############################################################################
385# Tests
386
387ping_test()
388{
389 local if_name=$1
390 local dip=$2
391 local vrf_name
392
393 RET=0
394
395 vrf_name=$(master_name_get $if_name)
396 ip vrf exec $vrf_name $PING $dip -c 10 -i 0.1 -w 2 &> /dev/null
397 check_err $?
398 log_test "ping"
399}
400
401ping6_test()
402{
403 local if_name=$1
404 local dip=$2
405 local vrf_name
406
407 RET=0
408
409 vrf_name=$(master_name_get $if_name)
410 ip vrf exec $vrf_name $PING6 $dip -c 10 -i 0.1 -w 2 &> /dev/null
411 check_err $?
412 log_test "ping6"
413}
414
415learning_test()
416{
417 local bridge=$1
418 local br_port1=$2 # Connected to `host1_if`.
419 local host1_if=$3
420 local host2_if=$4
421 local mac=de:ad:be:ef:13:37
422 local ageing_time
423
424 RET=0
425
426 bridge -j fdb show br $bridge brport $br_port1 \
427 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
428 check_fail $? "Found FDB record when should not"
429
430 # Disable unknown unicast flooding on `br_port1` to make sure
431 # packets are only forwarded through the port after a matching
432 # FDB entry was installed.
433 bridge link set dev $br_port1 flood off
434
435 tc qdisc add dev $host1_if ingress
436 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
437 flower dst_mac $mac action drop
438
439 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
440 sleep 1
441
442 tc -j -s filter show dev $host1_if ingress \
443 | jq -e ".[] | select(.options.handle == 101) \
444 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
445 check_fail $? "Packet reached second host when should not"
446
447 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
448 sleep 1
449
450 bridge -j fdb show br $bridge brport $br_port1 \
451 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
452 check_err $? "Did not find FDB record when should"
453
454 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
455 sleep 1
456
457 tc -j -s filter show dev $host1_if ingress \
458 | jq -e ".[] | select(.options.handle == 101) \
459 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
460 check_err $? "Packet did not reach second host when should"
461
462 # Wait for 10 seconds after the ageing time to make sure FDB
463 # record was aged-out.
464 ageing_time=$(bridge_ageing_time_get $bridge)
465 sleep $((ageing_time + 10))
466
467 bridge -j fdb show br $bridge brport $br_port1 \
468 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
469 check_fail $? "Found FDB record when should not"
470
471 bridge link set dev $br_port1 learning off
472
473 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
474 sleep 1
475
476 bridge -j fdb show br $bridge brport $br_port1 \
477 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
478 check_fail $? "Found FDB record when should not"
479
480 bridge link set dev $br_port1 learning on
481
482 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
483 tc qdisc del dev $host1_if ingress
484
485 bridge link set dev $br_port1 flood on
486
487 log_test "FDB learning"
488}
489
490flood_test_do()
491{
492 local should_flood=$1
493 local mac=$2
494 local ip=$3
495 local host1_if=$4
496 local host2_if=$5
497 local err=0
498
499 # Add an ACL on `host2_if` which will tell us whether the packet
500 # was flooded to it or not.
501 tc qdisc add dev $host2_if ingress
502 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
503 flower dst_mac $mac action drop
504
505 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
506 sleep 1
507
508 tc -j -s filter show dev $host2_if ingress \
509 | jq -e ".[] | select(.options.handle == 101) \
510 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
511 if [[ $? -ne 0 && $should_flood == "true" || \
512 $? -eq 0 && $should_flood == "false" ]]; then
513 err=1
514 fi
515
516 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
517 tc qdisc del dev $host2_if ingress
518
519 return $err
520}
521
522flood_unicast_test()
523{
524 local br_port=$1
525 local host1_if=$2
526 local host2_if=$3
527 local mac=de:ad:be:ef:13:37
528 local ip=192.0.2.100
529
530 RET=0
531
532 bridge link set dev $br_port flood off
533
534 flood_test_do false $mac $ip $host1_if $host2_if
535 check_err $? "Packet flooded when should not"
536
537 bridge link set dev $br_port flood on
538
539 flood_test_do true $mac $ip $host1_if $host2_if
540 check_err $? "Packet was not flooded when should"
541
542 log_test "Unknown unicast flood"
543}
544
545flood_multicast_test()
546{
547 local br_port=$1
548 local host1_if=$2
549 local host2_if=$3
550 local mac=01:00:5e:00:00:01
551 local ip=239.0.0.1
552
553 RET=0
554
555 bridge link set dev $br_port mcast_flood off
556
557 flood_test_do false $mac $ip $host1_if $host2_if
558 check_err $? "Packet flooded when should not"
559
560 bridge link set dev $br_port mcast_flood on
561
562 flood_test_do true $mac $ip $host1_if $host2_if
563 check_err $? "Packet was not flooded when should"
564
565 log_test "Unregistered multicast flood"
566}
567
568flood_test()
569{
570 # `br_port` is connected to `host2_if`
571 local br_port=$1
572 local host1_if=$2
573 local host2_if=$3
574
575 flood_unicast_test $br_port $host1_if $host2_if
576 flood_multicast_test $br_port $host1_if $host2_if
577}