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	test_ttl
72	test_tos
73	test_ecn_encap
74	reapply_config
75	ping_ipv4
76	test_flood
77	test_unicast
78    "}
79
80NUM_NETIFS=6
81source lib.sh
82
83h1_create()
84{
85	simple_if_init $h1 192.0.2.1/28
86	tc qdisc add dev $h1 clsact
87}
88
89h1_destroy()
90{
91	tc qdisc del dev $h1 clsact
92	simple_if_fini $h1 192.0.2.1/28
93}
94
95h2_create()
96{
97	simple_if_init $h2 192.0.2.2/28
98	tc qdisc add dev $h2 clsact
99}
100
101h2_destroy()
102{
103	tc qdisc del dev $h2 clsact
104	simple_if_fini $h2 192.0.2.2/28
105}
106
107rp1_set_addr()
108{
109	ip address add dev $rp1 192.0.2.17/28
110
111	ip route add 192.0.2.32/28 nexthop via 192.0.2.18
112	ip route add 192.0.2.48/28 nexthop via 192.0.2.18
113}
114
115rp1_unset_addr()
116{
117	ip route del 192.0.2.48/28 nexthop via 192.0.2.18
118	ip route del 192.0.2.32/28 nexthop via 192.0.2.18
119
120	ip address del dev $rp1 192.0.2.17/28
121}
122
123switch_create()
124{
125	ip link add name br1 type bridge vlan_filtering 0 mcast_snooping 0
126	# Make sure the bridge uses the MAC address of the local port and not
127	# that of the VxLAN's device.
128	ip link set dev br1 address $(mac_get $swp1)
129	ip link set dev br1 up
130
131	ip link set dev $rp1 up
132	rp1_set_addr
133
134	ip link add name vx1 type vxlan id 1000		\
135		local 192.0.2.17 dstport "$VXPORT"	\
136		nolearning noudpcsum tos inherit ttl 100
137	ip link set dev vx1 up
138
139	ip link set dev vx1 master br1
140	ip link set dev $swp1 master br1
141	ip link set dev $swp1 up
142
143	ip link set dev $swp2 master br1
144	ip link set dev $swp2 up
145
146	bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
147	bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
148}
149
150switch_destroy()
151{
152	rp1_unset_addr
153	ip link set dev $rp1 down
154
155	bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
156	bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
157
158	ip link set dev vx1 nomaster
159	ip link set dev vx1 down
160	ip link del dev vx1
161
162	ip link set dev $swp2 down
163	ip link set dev $swp2 nomaster
164
165	ip link set dev $swp1 down
166	ip link set dev $swp1 nomaster
167
168	ip link set dev br1 down
169	ip link del dev br1
170}
171
172vrp2_create()
173{
174	simple_if_init $rp2 192.0.2.18/28
175	__simple_if_init v1 v$rp2 192.0.2.33/28
176	__simple_if_init v3 v$rp2 192.0.2.49/28
177	tc qdisc add dev v1 clsact
178}
179
180vrp2_destroy()
181{
182	tc qdisc del dev v1 clsact
183	__simple_if_fini v3 192.0.2.49/28
184	__simple_if_fini v1 192.0.2.33/28
185	simple_if_fini $rp2 192.0.2.18/28
186}
187
188ns_init_common()
189{
190	local in_if=$1; shift
191	local in_addr=$1; shift
192	local other_in_addr=$1; shift
193	local nh_addr=$1; shift
194	local host_addr=$1; shift
195
196	ip link set dev $in_if up
197	ip address add dev $in_if $in_addr/28
198	tc qdisc add dev $in_if clsact
199
200	ip link add name br2 type bridge vlan_filtering 0
201	ip link set dev br2 up
202
203	ip link add name w1 type veth peer name w2
204
205	ip link set dev w1 master br2
206	ip link set dev w1 up
207
208	ip link add name vx2 type vxlan id 1000 local $in_addr dstport "$VXPORT"
209	ip link set dev vx2 up
210	bridge fdb append dev vx2 00:00:00:00:00:00 dst 192.0.2.17 self
211	bridge fdb append dev vx2 00:00:00:00:00:00 dst $other_in_addr self
212
213	ip link set dev vx2 master br2
214	tc qdisc add dev vx2 clsact
215
216	simple_if_init w2 $host_addr/28
217
218	ip route add 192.0.2.16/28 nexthop via $nh_addr
219	ip route add $other_in_addr/32 nexthop via $nh_addr
220}
221export -f ns_init_common
222
223ns1_create()
224{
225	ip netns add ns1
226	ip link set dev v2 netns ns1
227	in_ns ns1 \
228	      ns_init_common v2 192.0.2.34 192.0.2.50 192.0.2.33 192.0.2.3
229}
230
231ns1_destroy()
232{
233	ip netns exec ns1 ip link set dev v2 netns 1
234	ip netns del ns1
235}
236
237ns2_create()
238{
239	ip netns add ns2
240	ip link set dev v4 netns ns2
241	in_ns ns2 \
242	      ns_init_common v4 192.0.2.50 192.0.2.34 192.0.2.49 192.0.2.4
243}
244
245ns2_destroy()
246{
247	ip netns exec ns2 ip link set dev v4 netns 1
248	ip netns del ns2
249}
250
251setup_prepare()
252{
253	h1=${NETIFS[p1]}
254	swp1=${NETIFS[p2]}
255
256	swp2=${NETIFS[p3]}
257	h2=${NETIFS[p4]}
258
259	rp1=${NETIFS[p5]}
260	rp2=${NETIFS[p6]}
261
262	vrf_prepare
263	forwarding_enable
264
265	h1_create
266	h2_create
267	switch_create
268
269	ip link add name v1 type veth peer name v2
270	ip link add name v3 type veth peer name v4
271	vrp2_create
272	ns1_create
273	ns2_create
274
275	r1_mac=$(in_ns ns1 mac_get w2)
276	r2_mac=$(in_ns ns2 mac_get w2)
277	h2_mac=$(mac_get $h2)
278}
279
280cleanup()
281{
282	pre_cleanup
283
284	ns2_destroy
285	ns1_destroy
286	vrp2_destroy
287	ip link del dev v3
288	ip link del dev v1
289
290	switch_destroy
291	h2_destroy
292	h1_destroy
293
294	forwarding_restore
295	vrf_cleanup
296}
297
298# For the first round of tests, vx1 is the first device to get attached to the
299# bridge, and that at the point that the local IP is already configured. Try the
300# other scenario of attaching the device to an already-offloaded bridge, and
301# only then attach the local IP.
302reapply_config()
303{
304	echo "Reapplying configuration"
305
306	bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
307	bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
308	rp1_unset_addr
309	ip link set dev vx1 nomaster
310	sleep 5
311
312	ip link set dev vx1 master br1
313	bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
314	bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
315	sleep 1
316	rp1_set_addr
317	sleep 5
318}
319
320ping_ipv4()
321{
322	ping_test $h1 192.0.2.2 ": local->local"
323	ping_test $h1 192.0.2.3 ": local->remote 1"
324	ping_test $h1 192.0.2.4 ": local->remote 2"
325}
326
327maybe_in_ns()
328{
329	echo ${1:+in_ns} $1
330}
331
332__flood_counter_add_del()
333{
334	local add_del=$1; shift
335	local dev=$1; shift
336	local ns=$1; shift
337
338	# Putting the ICMP capture both to HW and to SW will end up
339	# double-counting the packets that are trapped to slow path, such as for
340	# the unicast test. Adding either skip_hw or skip_sw fixes this problem,
341	# but with skip_hw, the flooded packets are not counted at all, because
342	# those are dropped due to MAC address mismatch; and skip_sw is a no-go
343	# for veth-based topologies.
344	#
345	# So try to install with skip_sw and fall back to skip_sw if that fails.
346
347	$(maybe_in_ns $ns) __icmp_capture_add_del          \
348			   $add_del 100 "" $dev skip_sw 2>/dev/null || \
349	$(maybe_in_ns $ns) __icmp_capture_add_del          \
350			   $add_del 100 "" $dev skip_hw
351}
352
353flood_counter_install()
354{
355	__flood_counter_add_del add "$@"
356}
357
358flood_counter_uninstall()
359{
360	__flood_counter_add_del del "$@"
361}
362
363flood_fetch_stat()
364{
365	local dev=$1; shift
366	local ns=$1; shift
367
368	$(maybe_in_ns $ns) tc_rule_stats_get $dev 100 ingress
369}
370
371flood_fetch_stats()
372{
373	local counters=("${@}")
374	local counter
375
376	for counter in "${counters[@]}"; do
377		flood_fetch_stat $counter
378	done
379}
380
381vxlan_flood_test()
382{
383	local mac=$1; shift
384	local dst=$1; shift
385	local -a expects=("${@}")
386
387	local -a counters=($h2 "vx2 ns1" "vx2 ns2")
388	local counter
389	local key
390
391	for counter in "${counters[@]}"; do
392		flood_counter_install $counter
393	done
394
395	local -a t0s=($(flood_fetch_stats "${counters[@]}"))
396	$MZ $h1 -c 10 -d 100msec -p 64 -b $mac -B $dst -t icmp -q
397	sleep 1
398	local -a t1s=($(flood_fetch_stats "${counters[@]}"))
399
400	for key in ${!t0s[@]}; do
401		local delta=$((t1s[$key] - t0s[$key]))
402		local expect=${expects[$key]}
403
404		((expect == delta))
405		check_err $? "${counters[$key]}: Expected to capture $expect packets, got $delta."
406	done
407
408	for counter in "${counters[@]}"; do
409		flood_counter_uninstall $counter
410	done
411}
412
413__test_flood()
414{
415	local mac=$1; shift
416	local dst=$1; shift
417	local what=$1; shift
418
419	RET=0
420
421	vxlan_flood_test $mac $dst 10 10 10
422
423	log_test "VXLAN: $what"
424}
425
426test_flood()
427{
428	__test_flood de:ad:be:ef:13:37 192.0.2.100 "flood"
429}
430
431vxlan_fdb_add_del()
432{
433	local add_del=$1; shift
434	local mac=$1; shift
435	local dev=$1; shift
436	local dst=$1; shift
437
438	bridge fdb $add_del dev $dev $mac self static permanent \
439		${dst:+dst} $dst 2>/dev/null
440	bridge fdb $add_del dev $dev $mac master static 2>/dev/null
441}
442
443__test_unicast()
444{
445	local mac=$1; shift
446	local dst=$1; shift
447	local hit_idx=$1; shift
448	local what=$1; shift
449
450	RET=0
451
452	local -a expects=(0 0 0)
453	expects[$hit_idx]=10
454
455	vxlan_flood_test $mac $dst "${expects[@]}"
456
457	log_test "VXLAN: $what"
458}
459
460test_unicast()
461{
462	local -a targets=("$h2_mac $h2"
463			  "$r1_mac vx1 192.0.2.34"
464			  "$r2_mac vx1 192.0.2.50")
465	local target
466
467	for target in "${targets[@]}"; do
468		vxlan_fdb_add_del add $target
469	done
470
471	__test_unicast $h2_mac 192.0.2.2 0 "local MAC unicast"
472	__test_unicast $r1_mac 192.0.2.3 1 "remote MAC 1 unicast"
473	__test_unicast $r2_mac 192.0.2.4 2 "remote MAC 2 unicast"
474
475	for target in "${targets[@]}"; do
476		vxlan_fdb_add_del del $target
477	done
478}
479
480vxlan_ping_test()
481{
482	local ping_dev=$1; shift
483	local ping_dip=$1; shift
484	local ping_args=$1; shift
485	local capture_dev=$1; shift
486	local capture_dir=$1; shift
487	local capture_pref=$1; shift
488	local expect=$1; shift
489
490	local t0=$(tc_rule_stats_get $capture_dev $capture_pref $capture_dir)
491	ping_do $ping_dev $ping_dip "$ping_args"
492	local t1=$(tc_rule_stats_get $capture_dev $capture_pref $capture_dir)
493	local delta=$((t1 - t0))
494
495	# Tolerate a couple stray extra packets.
496	((expect <= delta && delta <= expect + 2))
497	check_err $? "$capture_dev: Expected to capture $expect packets, got $delta."
498}
499
500test_ttl()
501{
502	RET=0
503
504	tc filter add dev v1 egress pref 77 prot ip \
505		flower ip_ttl 99 action pass
506	vxlan_ping_test $h1 192.0.2.3 "" v1 egress 77 10
507	tc filter del dev v1 egress pref 77 prot ip
508
509	log_test "VXLAN: envelope TTL"
510}
511
512test_tos()
513{
514	RET=0
515
516	tc filter add dev v1 egress pref 77 prot ip \
517		flower ip_tos 0x40 action pass
518	vxlan_ping_test $h1 192.0.2.3 "-Q 0x40" v1 egress 77 10
519	vxlan_ping_test $h1 192.0.2.3 "-Q 0x30" v1 egress 77 0
520	tc filter del dev v1 egress pref 77 prot ip
521
522	log_test "VXLAN: envelope TOS inheritance"
523}
524
525__test_ecn_encap()
526{
527	local q=$1; shift
528	local tos=$1; shift
529
530	RET=0
531
532	tc filter add dev v1 egress pref 77 prot ip \
533		flower ip_tos $tos action pass
534	sleep 1
535	vxlan_ping_test $h1 192.0.2.3 "-Q $q" v1 egress 77 10
536	tc filter del dev v1 egress pref 77 prot ip
537
538	log_test "VXLAN: ECN encap: $q->$tos"
539}
540
541test_ecn_encap()
542{
543	# In accordance with INET_ECN_encapsulate()
544	__test_ecn_encap 0x00 0x00
545	__test_ecn_encap 0x01 0x01
546	__test_ecn_encap 0x02 0x02
547	__test_ecn_encap 0x03 0x02
548}
549
550test_all()
551{
552	echo "Running tests with UDP port $VXPORT"
553	tests_run
554}
555
556trap cleanup EXIT
557
558setup_prepare
559setup_wait
560test_all
561
562exit $EXIT_STATUS
563