1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# Test devlink-trap L3 drops functionality over mlxsw. Each registered L3 drop
5# packet trap is tested to make sure it is triggered under the right
6# conditions.
7
8# +---------------------------------+
9# | H1 (vrf)                        |
10# |    + $h1                        |
11# |    | 192.0.2.1/24               |
12# |    | 2001:db8:1::1/64           |
13# |    |                            |
14# |    |  default via 192.0.2.2     |
15# |    |  default via 2001:db8:1::2 |
16# +----|----------------------------+
17#      |
18# +----|----------------------------------------------------------------------+
19# | SW |                                                                      |
20# |    + $rp1                                                                 |
21# |        192.0.2.2/24                                                       |
22# |        2001:db8:1::2/64                                                   |
23# |                                                                           |
24# |        2001:db8:2::2/64                                                   |
25# |        198.51.100.2/24                                                    |
26# |    + $rp2                                                                 |
27# |    |                                                                      |
28# +----|----------------------------------------------------------------------+
29#      |
30# +----|----------------------------+
31# |    |  default via 198.51.100.2  |
32# |    |  default via 2001:db8:2::2 |
33# |    |                            |
34# |    | 2001:db8:2::1/64           |
35# |    | 198.51.100.1/24            |
36# |    + $h2                        |
37# | H2 (vrf)                        |
38# +---------------------------------+
39
40lib_dir=$(dirname $0)/../../../net/forwarding
41
42ALL_TESTS="
43	non_ip_test
44	uc_dip_over_mc_dmac_test
45	dip_is_loopback_test
46	sip_is_mc_test
47	sip_is_loopback_test
48	ip_header_corrupted_test
49	ipv4_sip_is_limited_bc_test
50	ipv6_mc_dip_reserved_scope_test
51	ipv6_mc_dip_interface_local_scope_test
52	blackhole_route_test
53	irif_disabled_test
54	erif_disabled_test
55"
56
57NUM_NETIFS=4
58source $lib_dir/lib.sh
59source $lib_dir/tc_common.sh
60source $lib_dir/devlink_lib.sh
61
62h1_create()
63{
64	simple_if_init $h1 192.0.2.1/24 2001:db8:1::1/64
65
66	ip -4 route add default vrf v$h1 nexthop via 192.0.2.2
67	ip -6 route add default vrf v$h1 nexthop via 2001:db8:1::2
68}
69
70h1_destroy()
71{
72	ip -6 route del default vrf v$h1 nexthop via 2001:db8:1::2
73	ip -4 route del default vrf v$h1 nexthop via 192.0.2.2
74
75	simple_if_fini $h1 192.0.2.1/24 2001:db8:1::1/64
76}
77
78h2_create()
79{
80	simple_if_init $h2 $h2_ipv4/24 $h2_ipv6/64
81
82	ip -4 route add default vrf v$h2 nexthop via 198.51.100.2
83	ip -6 route add default vrf v$h2 nexthop via 2001:db8:2::2
84}
85
86h2_destroy()
87{
88	ip -6 route del default vrf v$h2 nexthop via 2001:db8:2::2
89	ip -4 route del default vrf v$h2 nexthop via 198.51.100.2
90
91	simple_if_fini $h2 $h2_ipv4/24 $h2_ipv6/64
92}
93
94router_create()
95{
96	ip link set dev $rp1 up
97	ip link set dev $rp2 up
98
99	tc qdisc add dev $rp2 clsact
100
101	__addr_add_del $rp1 add 192.0.2.2/24 2001:db8:1::2/64
102	__addr_add_del $rp2 add 198.51.100.2/24 2001:db8:2::2/64
103}
104
105router_destroy()
106{
107	__addr_add_del $rp2 del 198.51.100.2/24 2001:db8:2::2/64
108	__addr_add_del $rp1 del 192.0.2.2/24 2001:db8:1::2/64
109
110	tc qdisc del dev $rp2 clsact
111}
112
113setup_prepare()
114{
115	h1=${NETIFS[p1]}
116	rp1=${NETIFS[p2]}
117
118	rp2=${NETIFS[p3]}
119	h2=${NETIFS[p4]}
120
121	h1mac=$(mac_get $h1)
122	rp1mac=$(mac_get $rp1)
123
124	h1_ipv4=192.0.2.1
125	h2_ipv4=198.51.100.1
126	h1_ipv6=2001:db8:1::1
127	h2_ipv6=2001:db8:2::1
128
129	vrf_prepare
130	forwarding_enable
131
132	h1_create
133	h2_create
134
135	router_create
136}
137
138cleanup()
139{
140	pre_cleanup
141
142	router_destroy
143
144	h2_destroy
145	h1_destroy
146
147	forwarding_restore
148	vrf_cleanup
149}
150
151ping_check()
152{
153	trap_name=$1; shift
154
155	devlink_trap_action_set $trap_name "trap"
156	ping_do $h1 $h2_ipv4
157	check_err $? "Packets that should not be trapped were trapped"
158	devlink_trap_action_set $trap_name "drop"
159}
160
161non_ip_test()
162{
163	local trap_name="non_ip"
164	local mz_pid
165
166	RET=0
167
168	ping_check $trap_name
169
170	tc filter add dev $rp2 egress protocol ip pref 1 handle 101 \
171		flower dst_ip $h2_ipv4 action drop
172
173	# Generate non-IP packets to the router
174	$MZ $h1 -c 0 -p 100 -d 1msec -B $h2_ipv4 -q "$rp1mac $h1mac \
175		00:00 de:ad:be:ef" &
176	mz_pid=$!
177
178	devlink_trap_drop_test $trap_name $rp2 101
179
180	log_test "Non IP"
181
182	devlink_trap_drop_cleanup $mz_pid $rp2 "ip" 1 101
183}
184
185__uc_dip_over_mc_dmac_test()
186{
187	local desc=$1; shift
188	local proto=$1; shift
189	local dip=$1; shift
190	local flags=${1:-""}; shift
191	local trap_name="uc_dip_over_mc_dmac"
192	local dmac=01:02:03:04:05:06
193	local mz_pid
194
195	RET=0
196
197	ping_check $trap_name
198
199	tc filter add dev $rp2 egress protocol $proto pref 1 handle 101 \
200		flower ip_proto udp src_port 54321 dst_port 12345 action drop
201
202	# Generate IP packets with a unicast IP and a multicast destination MAC
203	$MZ $h1 $flags -t udp "sp=54321,dp=12345" -c 0 -p 100 -b $dmac \
204		-B $dip -d 1msec -q &
205	mz_pid=$!
206
207	devlink_trap_drop_test $trap_name $rp2 101
208
209	log_test "Unicast destination IP over multicast destination MAC: $desc"
210
211	devlink_trap_drop_cleanup $mz_pid $rp2 $proto 1 101
212}
213
214uc_dip_over_mc_dmac_test()
215{
216	__uc_dip_over_mc_dmac_test "IPv4" "ip" $h2_ipv4
217	__uc_dip_over_mc_dmac_test "IPv6" "ipv6" $h2_ipv6 "-6"
218}
219
220__sip_is_loopback_test()
221{
222	local desc=$1; shift
223	local proto=$1; shift
224	local sip=$1; shift
225	local dip=$1; shift
226	local flags=${1:-""}; shift
227	local trap_name="sip_is_loopback_address"
228	local mz_pid
229
230	RET=0
231
232	ping_check $trap_name
233
234	tc filter add dev $rp2 egress protocol $proto pref 1 handle 101 \
235		flower src_ip $sip action drop
236
237	# Generate packets with loopback source IP
238	$MZ $h1 $flags -t udp "sp=54321,dp=12345" -c 0 -p 100 -A $sip \
239		-b $rp1mac -B $dip -d 1msec -q &
240	mz_pid=$!
241
242	devlink_trap_drop_test $trap_name $rp2 101
243
244	log_test "Source IP is loopback address: $desc"
245
246	devlink_trap_drop_cleanup $mz_pid $rp2 $proto 1 101
247}
248
249sip_is_loopback_test()
250{
251	__sip_is_loopback_test "IPv4" "ip" "127.0.0.0/8" $h2_ipv4
252	__sip_is_loopback_test "IPv6" "ipv6" "::1" $h2_ipv6 "-6"
253}
254
255__dip_is_loopback_test()
256{
257	local desc=$1; shift
258	local proto=$1; shift
259	local dip=$1; shift
260	local flags=${1:-""}; shift
261	local trap_name="dip_is_loopback_address"
262	local mz_pid
263
264	RET=0
265
266	ping_check $trap_name
267
268	tc filter add dev $rp2 egress protocol $proto pref 1 handle 101 \
269		flower dst_ip $dip action drop
270
271	# Generate packets with loopback destination IP
272	$MZ $h1 $flags -t udp "sp=54321,dp=12345" -c 0 -p 100 -b $rp1mac \
273		-B $dip -d 1msec -q &
274	mz_pid=$!
275
276	devlink_trap_drop_test $trap_name $rp2 101
277
278	log_test "Destination IP is loopback address: $desc"
279
280	devlink_trap_drop_cleanup $mz_pid $rp2 $proto 1 101
281}
282
283dip_is_loopback_test()
284{
285	__dip_is_loopback_test "IPv4" "ip" "127.0.0.0/8"
286	__dip_is_loopback_test "IPv6" "ipv6" "::1" "-6"
287}
288
289__sip_is_mc_test()
290{
291	local desc=$1; shift
292	local proto=$1; shift
293	local sip=$1; shift
294	local dip=$1; shift
295	local flags=${1:-""}; shift
296	local trap_name="sip_is_mc"
297	local mz_pid
298
299	RET=0
300
301	ping_check $trap_name
302
303	tc filter add dev $rp2 egress protocol $proto pref 1 handle 101 \
304		flower src_ip $sip action drop
305
306	# Generate packets with multicast source IP
307	$MZ $h1 $flags -t udp "sp=54321,dp=12345" -c 0 -p 100 -A $sip \
308		-b $rp1mac -B $dip -d 1msec -q &
309	mz_pid=$!
310
311	devlink_trap_drop_test $trap_name $rp2 101
312
313	log_test "Source IP is multicast: $desc"
314
315	devlink_trap_drop_cleanup $mz_pid $rp2 $proto 1 101
316}
317
318sip_is_mc_test()
319{
320	__sip_is_mc_test "IPv4" "ip" "239.1.1.1" $h2_ipv4
321	__sip_is_mc_test "IPv6" "ipv6" "FF02::2" $h2_ipv6 "-6"
322}
323
324ipv4_sip_is_limited_bc_test()
325{
326	local trap_name="ipv4_sip_is_limited_bc"
327	local sip=255.255.255.255
328	local mz_pid
329
330	RET=0
331
332	ping_check $trap_name
333
334	tc filter add dev $rp2 egress protocol ip pref 1 handle 101 \
335		flower src_ip $sip action drop
336
337	# Generate packets with limited broadcast source IP
338	$MZ $h1 -t udp "sp=54321,dp=12345" -c 0 -p 100 -A $sip -b $rp1mac \
339		-B $h2_ipv4 -d 1msec -q &
340	mz_pid=$!
341
342	devlink_trap_drop_test $trap_name $rp2 101
343
344	log_test "IPv4 source IP is limited broadcast"
345
346	devlink_trap_drop_cleanup $mz_pid $rp2 "ip" 1 101
347}
348
349ipv4_payload_get()
350{
351	local ipver=$1; shift
352	local ihl=$1; shift
353	local checksum=$1; shift
354
355	p=$(:
356		)"08:00:"$(                   : ETH type
357		)"$ipver"$(                   : IP version
358		)"$ihl:"$(                    : IHL
359		)"00:"$(		      : IP TOS
360		)"00:F4:"$(                   : IP total length
361		)"00:00:"$(                   : IP identification
362		)"20:00:"$(                   : IP flags + frag off
363		)"30:"$(                      : IP TTL
364		)"01:"$(                      : IP proto
365		)"$checksum:"$(               : IP header csum
366		)"$h1_ipv4:"$(                : IP saddr
367	        )"$h2_ipv4:"$(                : IP daddr
368		)
369	echo $p
370}
371
372__ipv4_header_corrupted_test()
373{
374	local desc=$1; shift
375	local ipver=$1; shift
376	local ihl=$1; shift
377	local checksum=$1; shift
378	local trap_name="ip_header_corrupted"
379	local payload
380	local mz_pid
381
382	RET=0
383
384	ping_check $trap_name
385
386	tc filter add dev $rp2 egress protocol ip pref 1 handle 101 \
387		flower dst_ip $h2_ipv4 action drop
388
389	payload=$(ipv4_payload_get $ipver $ihl $checksum)
390
391	# Generate packets with corrupted IP header
392	$MZ $h1 -c 0 -d 1msec -a $h1mac -b $rp1mac -q p=$payload &
393	mz_pid=$!
394
395	devlink_trap_drop_test $trap_name $rp2 101
396
397	log_test "IP header corrupted: $desc: IPv4"
398
399	devlink_trap_drop_cleanup $mz_pid $rp2 "ip" 1 101
400}
401
402ipv6_payload_get()
403{
404	local ipver=$1; shift
405
406	p=$(:
407		)"86:DD:"$(                  : ETH type
408		)"$ipver"$(                  : IP version
409		)"0:0:"$(                    : Traffic class
410		)"0:00:00:"$(		     : Flow label
411		)"00:00:"$(                  : Payload length
412		)"01:"$(                     : Next header
413		)"04:"$(                     : Hop limit
414		)"$h1_ipv6:"$(      	     : IP saddr
415		)"$h2_ipv6:"$(               : IP daddr
416		)
417	echo $p
418}
419
420__ipv6_header_corrupted_test()
421{
422	local desc=$1; shift
423	local ipver=$1; shift
424	local trap_name="ip_header_corrupted"
425	local payload
426	local mz_pid
427
428	RET=0
429
430	ping_check $trap_name
431
432	tc filter add dev $rp2 egress protocol ip pref 1 handle 101 \
433		flower dst_ip $h2_ipv4 action drop
434
435	payload=$(ipv6_payload_get $ipver)
436
437	# Generate packets with corrupted IP header
438	$MZ $h1 -c 0 -d 1msec -a $h1mac -b $rp1mac -q p=$payload &
439	mz_pid=$!
440
441	devlink_trap_drop_test $trap_name $rp2 101
442
443	log_test "IP header corrupted: $desc: IPv6"
444
445	devlink_trap_drop_cleanup $mz_pid $rp2 "ip" 1 101
446}
447
448ip_header_corrupted_test()
449{
450	# Each test uses one wrong value. The three values below are correct.
451	local ipv="4"
452	local ihl="5"
453	local checksum="00:F4"
454
455	__ipv4_header_corrupted_test "wrong IP version" 5 $ihl $checksum
456	__ipv4_header_corrupted_test "wrong IHL" $ipv 4 $checksum
457	__ipv4_header_corrupted_test "wrong checksum" $ipv $ihl "00:00"
458	__ipv6_header_corrupted_test "wrong IP version" 5
459}
460
461ipv6_mc_dip_reserved_scope_test()
462{
463	local trap_name="ipv6_mc_dip_reserved_scope"
464	local dip=FF00::
465	local mz_pid
466
467	RET=0
468
469	ping_check $trap_name
470
471	tc filter add dev $rp2 egress protocol ipv6 pref 1 handle 101 \
472		flower dst_ip $dip action drop
473
474	# Generate packets with reserved scope destination IP
475	$MZ $h1 -6 -t udp "sp=54321,dp=12345" -c 0 -p 100 -b \
476		"33:33:00:00:00:00" -B $dip -d 1msec -q &
477	mz_pid=$!
478
479	devlink_trap_drop_test $trap_name $rp2 101
480
481	log_test "IPv6 multicast destination IP reserved scope"
482
483	devlink_trap_drop_cleanup $mz_pid $rp2 "ipv6" 1 101
484}
485
486ipv6_mc_dip_interface_local_scope_test()
487{
488	local trap_name="ipv6_mc_dip_interface_local_scope"
489	local dip=FF01::
490	local mz_pid
491
492	RET=0
493
494	ping_check $trap_name
495
496	tc filter add dev $rp2 egress protocol ipv6 pref 1 handle 101 \
497		flower dst_ip $dip action drop
498
499	# Generate packets with interface local scope destination IP
500	$MZ $h1 -6 -t udp "sp=54321,dp=12345" -c 0 -p 100 -b \
501		"33:33:00:00:00:00" -B $dip -d 1msec -q &
502	mz_pid=$!
503
504	devlink_trap_drop_test $trap_name $rp2 101
505
506	log_test "IPv6 multicast destination IP interface-local scope"
507
508	devlink_trap_drop_cleanup $mz_pid $rp2 "ipv6" 1 101
509}
510
511__blackhole_route_test()
512{
513	local flags=$1; shift
514	local subnet=$1; shift
515	local proto=$1; shift
516	local dip=$1; shift
517	local ip_proto=${1:-"icmp"}; shift
518	local trap_name="blackhole_route"
519	local mz_pid
520
521	RET=0
522
523	ping_check $trap_name
524
525	ip -$flags route add blackhole $subnet
526	tc filter add dev $rp2 egress protocol $proto pref 1 handle 101 \
527		flower skip_hw dst_ip $dip ip_proto $ip_proto action drop
528
529	# Generate packets to the blackhole route
530	$MZ $h1 -$flags -t udp "sp=54321,dp=12345" -c 0 -p 100 -b $rp1mac \
531		-B $dip -d 1msec -q &
532	mz_pid=$!
533
534	devlink_trap_drop_test $trap_name $rp2 101
535	log_test "Blackhole route: IPv$flags"
536
537	devlink_trap_drop_cleanup $mz_pid $rp2 $proto 1 101
538	ip -$flags route del blackhole $subnet
539}
540
541blackhole_route_test()
542{
543	__blackhole_route_test "4" "198.51.100.0/30" "ip" $h2_ipv4
544	__blackhole_route_test "6" "2001:db8:2::/120" "ipv6" $h2_ipv6 "icmpv6"
545}
546
547irif_disabled_test()
548{
549	local trap_name="irif_disabled"
550	local t0_packets t0_bytes
551	local t1_packets t1_bytes
552	local mz_pid
553
554	RET=0
555
556	ping_check $trap_name
557
558	devlink_trap_action_set $trap_name "trap"
559
560	# When RIF of a physical port ("Sub-port RIF") is destroyed, we first
561	# block the STP of the {Port, VLAN} so packets cannot get into the RIF.
562	# Using bridge enables us to see this trap because when bridge is
563	# destroyed, there is a small time window that packets can go into the
564	# RIF, while it is disabled.
565	ip link add dev br0 type bridge
566	ip link set dev $rp1 master br0
567	ip address flush dev $rp1
568	__addr_add_del br0 add 192.0.2.2/24
569	ip li set dev br0 up
570
571	t0_packets=$(devlink_trap_rx_packets_get $trap_name)
572	t0_bytes=$(devlink_trap_rx_bytes_get $trap_name)
573
574	# Generate packets to h2 through br0 RIF that will be removed later
575	$MZ $h1 -t udp "sp=54321,dp=12345" -c 0 -p 100 -a own -b $rp1mac \
576		-B $h2_ipv4 -q &
577	mz_pid=$!
578
579	# Wait before removing br0 RIF to allow packets to go into the bridge.
580	sleep 1
581
582	# Flushing address will dismantle the RIF
583	ip address flush dev br0
584
585	t1_packets=$(devlink_trap_rx_packets_get $trap_name)
586	t1_bytes=$(devlink_trap_rx_bytes_get $trap_name)
587
588	if [[ $t0_packets -eq $t1_packets && $t0_bytes -eq $t1_bytes ]]; then
589		check_err 1 "Trap stats idle when packets should be trapped"
590	fi
591
592	log_test "Ingress RIF disabled"
593
594	kill $mz_pid && wait $mz_pid &> /dev/null
595	ip link set dev $rp1 nomaster
596	__addr_add_del $rp1 add 192.0.2.2/24 2001:db8:1::2/64
597	ip link del dev br0 type bridge
598	devlink_trap_action_set $trap_name "drop"
599}
600
601erif_disabled_test()
602{
603	local trap_name="erif_disabled"
604	local t0_packets t0_bytes
605	local t1_packets t1_bytes
606	local mz_pid
607
608	RET=0
609
610	ping_check $trap_name
611
612	devlink_trap_action_set $trap_name "trap"
613	ip link add dev br0 type bridge
614	ip add flush dev $rp1
615	ip link set dev $rp1 master br0
616	__addr_add_del br0 add 192.0.2.2/24
617	ip link set dev br0 up
618
619	t0_packets=$(devlink_trap_rx_packets_get $trap_name)
620	t0_bytes=$(devlink_trap_rx_bytes_get $trap_name)
621
622	rp2mac=$(mac_get $rp2)
623
624	# Generate packets that should go out through br0 RIF that will be
625	# removed later
626	$MZ $h2 -t udp "sp=54321,dp=12345" -c 0 -p 100 -a own -b $rp2mac \
627		-B 192.0.2.1 -q &
628	mz_pid=$!
629
630	sleep 5
631	# Unlinking the port from the bridge will disable the RIF associated
632	# with br0 as it is no longer an upper of any mlxsw port.
633	ip link set dev $rp1 nomaster
634
635	t1_packets=$(devlink_trap_rx_packets_get $trap_name)
636	t1_bytes=$(devlink_trap_rx_bytes_get $trap_name)
637
638	if [[ $t0_packets -eq $t1_packets && $t0_bytes -eq $t1_bytes ]]; then
639		check_err 1 "Trap stats idle when packets should be trapped"
640	fi
641
642	log_test "Egress RIF disabled"
643
644	kill $mz_pid && wait $mz_pid &> /dev/null
645	__addr_add_del $rp1 add 192.0.2.2/24 2001:db8:1::2/64
646	ip link del dev br0 type bridge
647	devlink_trap_action_set $trap_name "drop"
648}
649
650trap cleanup EXIT
651
652setup_prepare
653setup_wait
654
655tests_run
656
657exit $EXIT_STATUS
658