1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# +--------------------+                               +----------------------+
5# | H1 (vrf)           |                               |             H2 (vrf) |
6# |    + $h1           |                               |  + $h2               |
7# |    | 192.0.2.1/28  |                               |  | 192.0.2.2/28      |
8# +----|---------------+                               +--|-------------------+
9#      |                                                  |
10# +----|--------------------------------------------------|-------------------+
11# | SW |                                                  |                   |
12# | +--|--------------------------------------------------|-----------------+ |
13# | |  + $swp1                   BR1 (802.1d)             + $swp2           | |
14# | |                                                                       | |
15# | |  + vx1 (vxlan)                                                        | |
16# | |    local 192.0.2.17                                                   | |
17# | |    remote 192.0.2.34 192.0.2.50                                       | |
18# | |    id 1000 dstport $VXPORT                                            | |
19# | +-----------------------------------------------------------------------+ |
20# |                                                                           |
21# |  192.0.2.32/28 via 192.0.2.18                                             |
22# |  192.0.2.48/28 via 192.0.2.18                                             |
23# |                                                                           |
24# |    + $rp1                                                                 |
25# |    | 192.0.2.17/28                                                        |
26# +----|----------------------------------------------------------------------+
27#      |
28# +----|--------------------------------------------------------+
29# |    |                                             VRP2 (vrf) |
30# |    + $rp2                                                   |
31# |      192.0.2.18/28                                          |
32# |                                                             |   (maybe) HW
33# =============================================================================
34# |                                                             |  (likely) SW
35# |    + v1 (veth)                             + v3 (veth)      |
36# |    | 192.0.2.33/28                         | 192.0.2.49/28  |
37# +----|---------------------------------------|----------------+
38#      |                                       |
39# +----|------------------------------+   +----|------------------------------+
40# |    + v2 (veth)        NS1 (netns) |   |    + v4 (veth)        NS2 (netns) |
41# |      192.0.2.34/28                |   |      192.0.2.50/28                |
42# |                                   |   |                                   |
43# |   192.0.2.16/28 via 192.0.2.33    |   |   192.0.2.16/28 via 192.0.2.49    |
44# |   192.0.2.50/32 via 192.0.2.33    |   |   192.0.2.34/32 via 192.0.2.49    |
45# |                                   |   |                                   |
46# | +-------------------------------+ |   | +-------------------------------+ |
47# | |                  BR2 (802.1d) | |   | |                  BR2 (802.1d) | |
48# | |  + vx2 (vxlan)                | |   | |  + vx2 (vxlan)                | |
49# | |    local 192.0.2.34           | |   | |    local 192.0.2.50           | |
50# | |    remote 192.0.2.17          | |   | |    remote 192.0.2.17          | |
51# | |    remote 192.0.2.50          | |   | |    remote 192.0.2.34          | |
52# | |    id 1000 dstport $VXPORT    | |   | |    id 1000 dstport $VXPORT    | |
53# | |                               | |   | |                               | |
54# | |  + w1 (veth)                  | |   | |  + w1 (veth)                  | |
55# | +--|----------------------------+ |   | +--|----------------------------+ |
56# |    |                              |   |    |                              |
57# | +--|----------------------------+ |   | +--|----------------------------+ |
58# | |  |                  VW2 (vrf) | |   | |  |                  VW2 (vrf) | |
59# | |  + w2 (veth)                  | |   | |  + w2 (veth)                  | |
60# | |    192.0.2.3/28               | |   | |    192.0.2.4/28               | |
61# | +-------------------------------+ |   | +-------------------------------+ |
62# +-----------------------------------+   +-----------------------------------+
63
64: ${VXPORT:=4789}
65export VXPORT
66
67: ${ALL_TESTS:="
68	ping_ipv4
69	test_flood
70	test_unicast
71	reapply_config
72	ping_ipv4
73	test_flood
74	test_unicast
75    "}
76
77NUM_NETIFS=6
78source lib.sh
79
80h1_create()
81{
82	simple_if_init $h1 192.0.2.1/28
83	tc qdisc add dev $h1 clsact
84}
85
86h1_destroy()
87{
88	tc qdisc del dev $h1 clsact
89	simple_if_fini $h1 192.0.2.1/28
90}
91
92h2_create()
93{
94	simple_if_init $h2 192.0.2.2/28
95	tc qdisc add dev $h2 clsact
96}
97
98h2_destroy()
99{
100	tc qdisc del dev $h2 clsact
101	simple_if_fini $h2 192.0.2.2/28
102}
103
104rp1_set_addr()
105{
106	ip address add dev $rp1 192.0.2.17/28
107
108	ip route add 192.0.2.32/28 nexthop via 192.0.2.18
109	ip route add 192.0.2.48/28 nexthop via 192.0.2.18
110}
111
112rp1_unset_addr()
113{
114	ip route del 192.0.2.48/28 nexthop via 192.0.2.18
115	ip route del 192.0.2.32/28 nexthop via 192.0.2.18
116
117	ip address del dev $rp1 192.0.2.17/28
118}
119
120switch_create()
121{
122	ip link add name br1 type bridge vlan_filtering 0 mcast_snooping 0
123	# Make sure the bridge uses the MAC address of the local port and not
124	# that of the VxLAN's device.
125	ip link set dev br1 address $(mac_get $swp1)
126	ip link set dev br1 up
127
128	ip link set dev $rp1 up
129	rp1_set_addr
130
131	ip link add name vx1 type vxlan id 1000		\
132		local 192.0.2.17 dstport "$VXPORT"	\
133		nolearning noudpcsum tos inherit ttl 100
134	ip link set dev vx1 up
135
136	ip link set dev vx1 master br1
137	ip link set dev $swp1 master br1
138	ip link set dev $swp1 up
139
140	ip link set dev $swp2 master br1
141	ip link set dev $swp2 up
142
143	bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
144	bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
145}
146
147switch_destroy()
148{
149	rp1_unset_addr
150	ip link set dev $rp1 down
151
152	bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
153	bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
154
155	ip link set dev vx1 nomaster
156	ip link set dev vx1 down
157	ip link del dev vx1
158
159	ip link set dev $swp2 down
160	ip link set dev $swp2 nomaster
161
162	ip link set dev $swp1 down
163	ip link set dev $swp1 nomaster
164
165	ip link set dev br1 down
166	ip link del dev br1
167}
168
169vrp2_create()
170{
171	simple_if_init $rp2 192.0.2.18/28
172	__simple_if_init v1 v$rp2 192.0.2.33/28
173	__simple_if_init v3 v$rp2 192.0.2.49/28
174	tc qdisc add dev v1 clsact
175}
176
177vrp2_destroy()
178{
179	tc qdisc del dev v1 clsact
180	__simple_if_fini v3 192.0.2.49/28
181	__simple_if_fini v1 192.0.2.33/28
182	simple_if_fini $rp2 192.0.2.18/28
183}
184
185ns_init_common()
186{
187	local in_if=$1; shift
188	local in_addr=$1; shift
189	local other_in_addr=$1; shift
190	local nh_addr=$1; shift
191	local host_addr=$1; shift
192
193	ip link set dev $in_if up
194	ip address add dev $in_if $in_addr/28
195	tc qdisc add dev $in_if clsact
196
197	ip link add name br2 type bridge vlan_filtering 0
198	ip link set dev br2 up
199
200	ip link add name w1 type veth peer name w2
201
202	ip link set dev w1 master br2
203	ip link set dev w1 up
204
205	ip link add name vx2 type vxlan id 1000 local $in_addr dstport "$VXPORT"
206	ip link set dev vx2 up
207	bridge fdb append dev vx2 00:00:00:00:00:00 dst 192.0.2.17 self
208	bridge fdb append dev vx2 00:00:00:00:00:00 dst $other_in_addr self
209
210	ip link set dev vx2 master br2
211	tc qdisc add dev vx2 clsact
212
213	simple_if_init w2 $host_addr/28
214
215	ip route add 192.0.2.16/28 nexthop via $nh_addr
216	ip route add $other_in_addr/32 nexthop via $nh_addr
217}
218export -f ns_init_common
219
220ns1_create()
221{
222	ip netns add ns1
223	ip link set dev v2 netns ns1
224	in_ns ns1 \
225	      ns_init_common v2 192.0.2.34 192.0.2.50 192.0.2.33 192.0.2.3
226}
227
228ns1_destroy()
229{
230	ip netns exec ns1 ip link set dev v2 netns 1
231	ip netns del ns1
232}
233
234ns2_create()
235{
236	ip netns add ns2
237	ip link set dev v4 netns ns2
238	in_ns ns2 \
239	      ns_init_common v4 192.0.2.50 192.0.2.34 192.0.2.49 192.0.2.4
240}
241
242ns2_destroy()
243{
244	ip netns exec ns2 ip link set dev v4 netns 1
245	ip netns del ns2
246}
247
248setup_prepare()
249{
250	h1=${NETIFS[p1]}
251	swp1=${NETIFS[p2]}
252
253	swp2=${NETIFS[p3]}
254	h2=${NETIFS[p4]}
255
256	rp1=${NETIFS[p5]}
257	rp2=${NETIFS[p6]}
258
259	vrf_prepare
260	forwarding_enable
261
262	h1_create
263	h2_create
264	switch_create
265
266	ip link add name v1 type veth peer name v2
267	ip link add name v3 type veth peer name v4
268	vrp2_create
269	ns1_create
270	ns2_create
271
272	r1_mac=$(in_ns ns1 mac_get w2)
273	r2_mac=$(in_ns ns2 mac_get w2)
274	h2_mac=$(mac_get $h2)
275}
276
277cleanup()
278{
279	pre_cleanup
280
281	ns2_destroy
282	ns1_destroy
283	vrp2_destroy
284	ip link del dev v3
285	ip link del dev v1
286
287	switch_destroy
288	h2_destroy
289	h1_destroy
290
291	forwarding_restore
292	vrf_cleanup
293}
294
295# For the first round of tests, vx1 is the first device to get attached to the
296# bridge, and that at the point that the local IP is already configured. Try the
297# other scenario of attaching the device to an already-offloaded bridge, and
298# only then attach the local IP.
299reapply_config()
300{
301	echo "Reapplying configuration"
302
303	bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
304	bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
305	rp1_unset_addr
306	ip link set dev vx1 nomaster
307	sleep 5
308
309	ip link set dev vx1 master br1
310	bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
311	bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
312	sleep 1
313	rp1_set_addr
314	sleep 5
315}
316
317ping_ipv4()
318{
319	ping_test $h1 192.0.2.2 ": local->local"
320	ping_test $h1 192.0.2.3 ": local->remote 1"
321	ping_test $h1 192.0.2.4 ": local->remote 2"
322}
323
324maybe_in_ns()
325{
326	echo ${1:+in_ns} $1
327}
328
329__flood_counter_add_del()
330{
331	local add_del=$1; shift
332	local dev=$1; shift
333	local ns=$1; shift
334
335	# Putting the ICMP capture both to HW and to SW will end up
336	# double-counting the packets that are trapped to slow path, such as for
337	# the unicast test. Adding either skip_hw or skip_sw fixes this problem,
338	# but with skip_hw, the flooded packets are not counted at all, because
339	# those are dropped due to MAC address mismatch; and skip_sw is a no-go
340	# for veth-based topologies.
341	#
342	# So try to install with skip_sw and fall back to skip_sw if that fails.
343
344	$(maybe_in_ns $ns) __icmp_capture_add_del          \
345			   $add_del 100 "" $dev skip_sw 2>/dev/null || \
346	$(maybe_in_ns $ns) __icmp_capture_add_del          \
347			   $add_del 100 "" $dev skip_hw
348}
349
350flood_counter_install()
351{
352	__flood_counter_add_del add "$@"
353}
354
355flood_counter_uninstall()
356{
357	__flood_counter_add_del del "$@"
358}
359
360flood_fetch_stat()
361{
362	local dev=$1; shift
363	local ns=$1; shift
364
365	$(maybe_in_ns $ns) tc_rule_stats_get $dev 100 ingress
366}
367
368flood_fetch_stats()
369{
370	local counters=("${@}")
371	local counter
372
373	for counter in "${counters[@]}"; do
374		flood_fetch_stat $counter
375	done
376}
377
378vxlan_flood_test()
379{
380	local mac=$1; shift
381	local dst=$1; shift
382	local -a expects=("${@}")
383
384	local -a counters=($h2 "vx2 ns1" "vx2 ns2")
385	local counter
386	local key
387
388	for counter in "${counters[@]}"; do
389		flood_counter_install $counter
390	done
391
392	local -a t0s=($(flood_fetch_stats "${counters[@]}"))
393	$MZ $h1 -c 10 -d 100msec -p 64 -b $mac -B $dst -t icmp -q
394	sleep 1
395	local -a t1s=($(flood_fetch_stats "${counters[@]}"))
396
397	for key in ${!t0s[@]}; do
398		local delta=$((t1s[$key] - t0s[$key]))
399		local expect=${expects[$key]}
400
401		((expect == delta))
402		check_err $? "${counters[$key]}: Expected to capture $expect packets, got $delta."
403	done
404
405	for counter in "${counters[@]}"; do
406		flood_counter_uninstall $counter
407	done
408}
409
410__test_flood()
411{
412	local mac=$1; shift
413	local dst=$1; shift
414	local what=$1; shift
415
416	RET=0
417
418	vxlan_flood_test $mac $dst 10 10 10
419
420	log_test "VXLAN: $what"
421}
422
423test_flood()
424{
425	__test_flood de:ad:be:ef:13:37 192.0.2.100 "flood"
426}
427
428vxlan_fdb_add_del()
429{
430	local add_del=$1; shift
431	local mac=$1; shift
432	local dev=$1; shift
433	local dst=$1; shift
434
435	bridge fdb $add_del dev $dev $mac self static permanent \
436		${dst:+dst} $dst 2>/dev/null
437	bridge fdb $add_del dev $dev $mac master static 2>/dev/null
438}
439
440__test_unicast()
441{
442	local mac=$1; shift
443	local dst=$1; shift
444	local hit_idx=$1; shift
445	local what=$1; shift
446
447	RET=0
448
449	local -a expects=(0 0 0)
450	expects[$hit_idx]=10
451
452	vxlan_flood_test $mac $dst "${expects[@]}"
453
454	log_test "VXLAN: $what"
455}
456
457test_unicast()
458{
459	local -a targets=("$h2_mac $h2"
460			  "$r1_mac vx1 192.0.2.34"
461			  "$r2_mac vx1 192.0.2.50")
462	local target
463
464	for target in "${targets[@]}"; do
465		vxlan_fdb_add_del add $target
466	done
467
468	__test_unicast $h2_mac 192.0.2.2 0 "local MAC unicast"
469	__test_unicast $r1_mac 192.0.2.3 1 "remote MAC 1 unicast"
470	__test_unicast $r2_mac 192.0.2.4 2 "remote MAC 2 unicast"
471
472	for target in "${targets[@]}"; do
473		vxlan_fdb_add_del del $target
474	done
475}
476
477test_all()
478{
479	echo "Running tests with UDP port $VXPORT"
480	tests_run
481}
482
483trap cleanup EXIT
484
485setup_prepare
486setup_wait
487test_all
488
489exit $EXIT_STATUS
490