1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# This test is for checking the A-TCAM and C-TCAM operation in Spectrum-2.
5# It tries to exercise as many code paths in the eRP state machine as
6# possible.
7
8lib_dir=$(dirname $0)/../../../../net/forwarding
9
10ALL_TESTS="single_mask_test identical_filters_test two_masks_test \
11	multiple_masks_test ctcam_edge_cases_test delta_simple_test \
12	bloom_simple_test bloom_complex_test bloom_delta_test"
13NUM_NETIFS=2
14source $lib_dir/tc_common.sh
15source $lib_dir/lib.sh
16
17tcflags="skip_hw"
18
19h1_create()
20{
21	simple_if_init $h1 192.0.2.1/24 198.51.100.1/24
22}
23
24h1_destroy()
25{
26	simple_if_fini $h1 192.0.2.1/24 198.51.100.1/24
27}
28
29h2_create()
30{
31	simple_if_init $h2 192.0.2.2/24 198.51.100.2/24
32	tc qdisc add dev $h2 clsact
33}
34
35h2_destroy()
36{
37	tc qdisc del dev $h2 clsact
38	simple_if_fini $h2 192.0.2.2/24 198.51.100.2/24
39}
40
41single_mask_test()
42{
43	# When only a single mask is required, the device uses the master
44	# mask and not the eRP table. Verify that under this mode the right
45	# filter is matched
46
47	RET=0
48
49	tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
50		$tcflags dst_ip 192.0.2.2 action drop
51
52	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
53		-t ip -q
54
55	tc_check_packets "dev $h2 ingress" 101 1
56	check_err $? "Single filter - did not match"
57
58	tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
59		$tcflags dst_ip 198.51.100.2 action drop
60
61	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
62		-t ip -q
63
64	tc_check_packets "dev $h2 ingress" 101 2
65	check_err $? "Two filters - did not match highest priority"
66
67	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 198.51.100.1 -B 198.51.100.2 \
68		-t ip -q
69
70	tc_check_packets "dev $h2 ingress" 102 1
71	check_err $? "Two filters - did not match lowest priority"
72
73	tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
74
75	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 198.51.100.1 -B 198.51.100.2 \
76		-t ip -q
77
78	tc_check_packets "dev $h2 ingress" 102 2
79	check_err $? "Single filter - did not match after delete"
80
81	tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
82
83	log_test "single mask test ($tcflags)"
84}
85
86identical_filters_test()
87{
88	# When two filters that only differ in their priority are used,
89	# one needs to be inserted into the C-TCAM. This test verifies
90	# that filters are correctly spilled to C-TCAM and that the right
91	# filter is matched
92
93	RET=0
94
95	tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
96		$tcflags dst_ip 192.0.2.2 action drop
97	tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
98		$tcflags dst_ip 192.0.2.2 action drop
99
100	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
101		-t ip -q
102
103	tc_check_packets "dev $h2 ingress" 101 1
104	check_err $? "Did not match A-TCAM filter"
105
106	tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
107
108	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
109		-t ip -q
110
111	tc_check_packets "dev $h2 ingress" 102 1
112	check_err $? "Did not match C-TCAM filter after A-TCAM delete"
113
114	tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \
115		$tcflags dst_ip 192.0.2.2 action drop
116
117	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
118		-t ip -q
119
120	tc_check_packets "dev $h2 ingress" 102 2
121	check_err $? "Did not match C-TCAM filter after A-TCAM add"
122
123	tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
124
125	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
126		-t ip -q
127
128	tc_check_packets "dev $h2 ingress" 103 1
129	check_err $? "Did not match A-TCAM filter after C-TCAM delete"
130
131	tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower
132
133	log_test "identical filters test ($tcflags)"
134}
135
136two_masks_test()
137{
138	# When more than one mask is required, the eRP table is used. This
139	# test verifies that the eRP table is correctly allocated and used
140
141	RET=0
142
143	tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
144		$tcflags dst_ip 192.0.2.2 action drop
145	tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \
146		$tcflags dst_ip 192.0.0.0/8 action drop
147
148	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
149		-t ip -q
150
151	tc_check_packets "dev $h2 ingress" 101 1
152	check_err $? "Two filters - did not match highest priority"
153
154	tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
155
156	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
157		-t ip -q
158
159	tc_check_packets "dev $h2 ingress" 103 1
160	check_err $? "Single filter - did not match"
161
162	tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
163		$tcflags dst_ip 192.0.2.0/24 action drop
164
165	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
166		-t ip -q
167
168	tc_check_packets "dev $h2 ingress" 102 1
169	check_err $? "Two filters - did not match highest priority after add"
170
171	tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower
172	tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
173
174	log_test "two masks test ($tcflags)"
175}
176
177multiple_masks_test()
178{
179	# The number of masks in a region is limited. Once the maximum
180	# number of masks has been reached filters that require new
181	# masks are spilled to the C-TCAM. This test verifies that
182	# spillage is performed correctly and that the right filter is
183	# matched
184
185	local index
186
187	RET=0
188
189	NUM_MASKS=32
190	BASE_INDEX=100
191
192	for i in $(eval echo {1..$NUM_MASKS}); do
193		index=$((BASE_INDEX - i))
194
195		tc filter add dev $h2 ingress protocol ip pref $index \
196			handle $index \
197			flower $tcflags dst_ip 192.0.2.2/${i} src_ip 192.0.2.1 \
198			action drop
199
200		$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 \
201			-B 192.0.2.2 -t ip -q
202
203		tc_check_packets "dev $h2 ingress" $index 1
204		check_err $? "$i filters - did not match highest priority (add)"
205	done
206
207	for i in $(eval echo {$NUM_MASKS..1}); do
208		index=$((BASE_INDEX - i))
209
210		$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 \
211			-B 192.0.2.2 -t ip -q
212
213		tc_check_packets "dev $h2 ingress" $index 2
214		check_err $? "$i filters - did not match highest priority (del)"
215
216		tc filter del dev $h2 ingress protocol ip pref $index \
217			handle $index flower
218	done
219
220	log_test "multiple masks test ($tcflags)"
221}
222
223ctcam_two_atcam_masks_test()
224{
225	RET=0
226
227	# First case: C-TCAM is disabled when there are two A-TCAM masks.
228	# We push a filter into the C-TCAM by using two identical filters
229	# as in identical_filters_test()
230
231	# Filter goes into A-TCAM
232	tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
233		$tcflags dst_ip 192.0.2.2 action drop
234	# Filter goes into C-TCAM
235	tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
236		$tcflags dst_ip 192.0.2.2 action drop
237	# Filter goes into A-TCAM
238	tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \
239		$tcflags dst_ip 192.0.0.0/16 action drop
240
241	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
242		-t ip -q
243
244	tc_check_packets "dev $h2 ingress" 101 1
245	check_err $? "Did not match A-TCAM filter"
246
247	# Delete both A-TCAM and C-TCAM filters and make sure the remaining
248	# A-TCAM filter still works
249	tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
250	tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
251
252	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
253		-t ip -q
254
255	tc_check_packets "dev $h2 ingress" 103 1
256	check_err $? "Did not match A-TCAM filter"
257
258	tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower
259
260	log_test "ctcam with two atcam masks test ($tcflags)"
261}
262
263ctcam_one_atcam_mask_test()
264{
265	RET=0
266
267	# Second case: C-TCAM is disabled when there is one A-TCAM mask.
268	# The test is similar to identical_filters_test()
269
270	# Filter goes into A-TCAM
271	tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
272		$tcflags dst_ip 192.0.2.2 action drop
273	# Filter goes into C-TCAM
274	tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
275		$tcflags dst_ip 192.0.2.2 action drop
276
277	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
278		-t ip -q
279
280	tc_check_packets "dev $h2 ingress" 101 1
281	check_err $? "Did not match C-TCAM filter"
282
283	tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
284
285	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
286		-t ip -q
287
288	tc_check_packets "dev $h2 ingress" 102 1
289	check_err $? "Did not match A-TCAM filter"
290
291	tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
292
293	log_test "ctcam with one atcam mask test ($tcflags)"
294}
295
296ctcam_no_atcam_masks_test()
297{
298	RET=0
299
300	# Third case: C-TCAM is disabled when there are no A-TCAM masks
301	# This test exercises the code path that transitions the eRP table
302	# to its initial state after deleting the last C-TCAM mask
303
304	# Filter goes into A-TCAM
305	tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
306		$tcflags dst_ip 192.0.2.2 action drop
307	# Filter goes into C-TCAM
308	tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
309		$tcflags dst_ip 192.0.2.2 action drop
310
311	tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
312	tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
313
314	log_test "ctcam with no atcam masks test ($tcflags)"
315}
316
317ctcam_edge_cases_test()
318{
319	# When the C-TCAM is disabled after deleting the last C-TCAM
320	# mask, we want to make sure the eRP state machine is put in
321	# the correct state
322
323	ctcam_two_atcam_masks_test
324	ctcam_one_atcam_mask_test
325	ctcam_no_atcam_masks_test
326}
327
328tp_record()
329{
330	local tracepoint=$1
331	local cmd=$2
332
333	perf record -q -e $tracepoint $cmd
334	return $?
335}
336
337tp_check_hits()
338{
339	local tracepoint=$1
340	local count=$2
341
342	perf_output=`perf script -F trace:event,trace`
343	hits=`echo $perf_output | grep "$tracepoint:" | wc -l`
344	if [[ "$count" -ne "$hits" ]]; then
345		return 1
346	fi
347	return 0
348}
349
350delta_simple_test()
351{
352	# The first filter will create eRP, the second filter will fit into
353	# the first eRP with delta. Remove the first rule then and check that
354        # the eRP stays (referenced by the second filter).
355
356	RET=0
357
358	if [[ "$tcflags" != "skip_sw" ]]; then
359		return 0;
360	fi
361
362	tp_record "objagg:*" "tc filter add dev $h2 ingress protocol ip \
363		   pref 1 handle 101 flower $tcflags dst_ip 192.0.0.0/24 \
364		   action drop"
365	tp_check_hits "objagg:objagg_obj_root_create" 1
366	check_err $? "eRP was not created"
367
368	tp_record "objagg:*" "tc filter add dev $h2 ingress protocol ip \
369		   pref 2 handle 102 flower $tcflags dst_ip 192.0.2.2 \
370		   action drop"
371	tp_check_hits "objagg:objagg_obj_root_create" 0
372	check_err $? "eRP was incorrectly created"
373	tp_check_hits "objagg:objagg_obj_parent_assign" 1
374	check_err $? "delta was not created"
375
376	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
377		-t ip -q
378
379	tc_check_packets "dev $h2 ingress" 101 1
380	check_fail $? "Matched a wrong filter"
381
382	tc_check_packets "dev $h2 ingress" 102 1
383	check_err $? "Did not match on correct filter"
384
385	tp_record "objagg:*" "tc filter del dev $h2 ingress protocol ip \
386		   pref 1 handle 101 flower"
387	tp_check_hits "objagg:objagg_obj_root_destroy" 0
388	check_err $? "eRP was incorrectly destroyed"
389	tp_check_hits "objagg:objagg_obj_parent_unassign" 0
390	check_err $? "delta was incorrectly destroyed"
391
392	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
393		-t ip -q
394
395	tc_check_packets "dev $h2 ingress" 102 2
396	check_err $? "Did not match on correct filter after the first was removed"
397
398	tp_record "objagg:*" "tc filter del dev $h2 ingress protocol ip \
399		   pref 2 handle 102 flower"
400	tp_check_hits "objagg:objagg_obj_parent_unassign" 1
401	check_err $? "delta was not destroyed"
402	tp_check_hits "objagg:objagg_obj_root_destroy" 1
403	check_err $? "eRP was not destroyed"
404
405	log_test "delta simple test ($tcflags)"
406}
407
408bloom_simple_test()
409{
410	# Bloom filter requires that the eRP table is used. This test
411	# verifies that Bloom filter is not harming correctness of ACLs.
412	# First, make sure that eRP table is used and then set rule patterns
413	# which are distant enough and will result skipping a lookup after
414	# consulting the Bloom filter. Although some eRP lookups are skipped,
415	# the correct filter should be hit.
416
417	RET=0
418
419	tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
420		$tcflags dst_ip 192.0.2.2 action drop
421	tc filter add dev $h2 ingress protocol ip pref 5 handle 104 flower \
422		$tcflags dst_ip 198.51.100.2 action drop
423	tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \
424		$tcflags dst_ip 192.0.0.0/8 action drop
425
426	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
427		-t ip -q
428
429	tc_check_packets "dev $h2 ingress" 101 1
430	check_err $? "Two filters - did not match highest priority"
431
432	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 198.51.100.1 -B 198.51.100.2 \
433		-t ip -q
434
435	tc_check_packets "dev $h2 ingress" 104 1
436	check_err $? "Single filter - did not match"
437
438	tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
439
440	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
441		-t ip -q
442
443	tc_check_packets "dev $h2 ingress" 103 1
444	check_err $? "Low prio filter - did not match"
445
446	tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
447		$tcflags dst_ip 198.0.0.0/8 action drop
448
449	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 198.51.100.1 -B 198.51.100.2 \
450		-t ip -q
451
452	tc_check_packets "dev $h2 ingress" 102 1
453	check_err $? "Two filters - did not match highest priority after add"
454
455	tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower
456	tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
457	tc filter del dev $h2 ingress protocol ip pref 5 handle 104 flower
458
459	log_test "bloom simple test ($tcflags)"
460}
461
462bloom_complex_test()
463{
464	# Bloom filter index computation is affected from region ID, eRP
465	# ID and from the region key size. In order to excercise those parts
466	# of the Bloom filter code, use a series of regions, each with a
467	# different key size and send packet that should hit all of them.
468	local index
469
470	RET=0
471	NUM_CHAINS=4
472	BASE_INDEX=100
473
474	# Create chain with up to 2 key blocks (ip_proto only)
475	tc chain add dev $h2 ingress chain 1 protocol ip flower \
476		ip_proto tcp &> /dev/null
477	# Create chain with 2-4 key blocks (ip_proto, src MAC)
478	tc chain add dev $h2 ingress chain 2 protocol ip flower \
479		ip_proto tcp \
480		src_mac 00:00:00:00:00:00/FF:FF:FF:FF:FF:FF &> /dev/null
481	# Create chain with 4-8 key blocks (ip_proto, src & dst MAC, IPv4 dest)
482	tc chain add dev $h2 ingress chain 3 protocol ip flower \
483		ip_proto tcp \
484		dst_mac 00:00:00:00:00:00/FF:FF:FF:FF:FF:FF \
485		src_mac 00:00:00:00:00:00/FF:FF:FF:FF:FF:FF \
486		dst_ip 0.0.0.0/32 &> /dev/null
487	# Default chain contains all fields and therefore is 8-12 key blocks
488	tc chain add dev $h2 ingress chain 4
489
490	# We need at least 2 rules in every region to have eRP table active
491	# so create a dummy rule per chain using a different pattern
492	for i in $(eval echo {0..$NUM_CHAINS}); do
493		index=$((BASE_INDEX - 1 - i))
494		tc filter add dev $h2 ingress chain $i protocol ip \
495			pref 2 handle $index flower \
496			$tcflags ip_proto tcp action drop
497	done
498
499	# Add rules to test Bloom filter, each in a different chain
500	index=$BASE_INDEX
501	tc filter add dev $h2 ingress protocol ip \
502		pref 1 handle $((++index)) flower \
503		$tcflags dst_ip 192.0.0.0/16 action goto chain 1
504	tc filter add dev $h2 ingress chain 1 protocol ip \
505		pref 1 handle $((++index)) flower \
506		$tcflags action goto chain 2
507	tc filter add dev $h2 ingress chain 2 protocol ip \
508		pref 1 handle $((++index)) flower \
509		$tcflags src_mac $h1mac action goto chain 3
510	tc filter add dev $h2 ingress chain 3 protocol ip \
511		pref 1 handle $((++index)) flower \
512		$tcflags dst_ip 192.0.0.0/8 action goto chain 4
513	tc filter add dev $h2 ingress chain 4 protocol ip \
514		pref 1 handle $((++index)) flower \
515		$tcflags src_ip 192.0.2.0/24 action drop
516
517	# Send a packet that is supposed to hit all chains
518	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
519		-t ip -q
520
521	for i in $(eval echo {0..$NUM_CHAINS}); do
522		index=$((BASE_INDEX + i + 1))
523		tc_check_packets "dev $h2 ingress" $index 1
524		check_err $? "Did not match chain $i"
525	done
526
527	# Rules cleanup
528	for i in $(eval echo {$NUM_CHAINS..0}); do
529		index=$((BASE_INDEX - i - 1))
530		tc filter del dev $h2 ingress chain $i \
531			pref 2 handle $index flower
532		index=$((BASE_INDEX + i + 1))
533		tc filter del dev $h2 ingress chain $i \
534			pref 1 handle $index flower
535	done
536
537	# Chains cleanup
538	for i in $(eval echo {$NUM_CHAINS..1}); do
539		tc chain del dev $h2 ingress chain $i
540	done
541
542	log_test "bloom complex test ($tcflags)"
543}
544
545
546bloom_delta_test()
547{
548	# When multiple masks are used, the eRP table is activated. When
549	# masks are close enough (delta) the masks reside on the same
550	# eRP table. This test verifies that the eRP table is correctly
551	# allocated and used in delta condition and that Bloom filter is
552	# still functional with delta.
553
554	RET=0
555
556	tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \
557		$tcflags dst_ip 192.1.0.0/16 action drop
558
559	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.1.2.1 -B 192.1.2.2 \
560		-t ip -q
561
562	tc_check_packets "dev $h2 ingress" 103 1
563	check_err $? "Single filter - did not match"
564
565	tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
566		$tcflags dst_ip 192.2.1.0/24 action drop
567
568	$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.2.1.1 -B 192.2.1.2 \
569		-t ip -q
570
571	tc_check_packets "dev $h2 ingress" 102 1
572	check_err $? "Delta filters - did not match second filter"
573
574	tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower
575	tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
576
577	log_test "bloom delta test ($tcflags)"
578}
579
580setup_prepare()
581{
582	h1=${NETIFS[p1]}
583	h2=${NETIFS[p2]}
584	h1mac=$(mac_get $h1)
585	h2mac=$(mac_get $h2)
586
587	vrf_prepare
588
589	h1_create
590	h2_create
591}
592
593cleanup()
594{
595	pre_cleanup
596
597	h2_destroy
598	h1_destroy
599
600	vrf_cleanup
601}
602
603trap cleanup EXIT
604
605setup_prepare
606setup_wait
607
608tests_run
609
610if ! tc_offload_check; then
611	check_err 1 "Could not test offloaded functionality"
612	log_test "mlxsw-specific tests for tc flower"
613	exit
614else
615	tcflags="skip_sw"
616	tests_run
617fi
618
619exit $EXIT_STATUS
620