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