xref: /NextBSD/sys/dev/mlx5/mlx5_core/mlx5_eswitch_vacl.c (revision 4557fabb34e865d7f40be64b39c9e34fa41dbb60)
1 /*-
2  * Copyright (c) 2013-2015, Mellanox Technologies, Ltd.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27 
28 #include <linux/etherdevice.h>
29 #include <dev/mlx5/driver.h>
30 #include <dev/mlx5/flow_table.h>
31 #include <dev/mlx5/eswitch_vacl.h>
32 #include "mlx5_core.h"
33 
34 enum {
35 	MLX5_ACL_LOOPBACK_GROUP_IDX	= 0,
36 	MLX5_ACL_UNTAGGED_GROUP_IDX	= 1,
37 	MLX5_ACL_VLAN_GROUP_IDX		= 2,
38 	MLX5_ACL_UNKNOWN_VLAN_GROUP_IDX	= 3,
39 	MLX5_ACL_DEFAULT_GROUP_IDX	= 4,
40 	MLX5_ACL_GROUPS_NUM,
41 };
42 
43 struct mlx_vacl_fr {
44 	bool			applied;
45 	u32			fi;
46 	u16			action;
47 };
48 
49 struct mlx5_vacl_table {
50 	struct mlx5_core_dev	*dev;
51 	u16			vport;
52 	void			*ft;
53 	int			max_ft_size;
54 	int			acl_type;
55 
56 	struct mlx_vacl_fr	loopback_fr;
57 	struct mlx_vacl_fr	untagged_fr;
58 	struct mlx_vacl_fr	unknown_vlan_fr;
59 	struct mlx_vacl_fr	default_fr;
60 
61 	bool			vlan_filter_enabled;
62 	bool			vlan_filter_applied;
63 	unsigned long		*vlan_allowed_bitmap;
64 	u32			vlan_fi_table[4096];
65 
66 	bool			spoofchk_enabled;
67 	u8			smac[ETH_ALEN];
68 };
69 
mlx5_vacl_table_allow_vlan(void * acl_t,u16 vlan)70 static int mlx5_vacl_table_allow_vlan(void *acl_t, u16 vlan)
71 {
72 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
73 	u32 *flow_context = NULL;
74 	void *in_match_criteria = NULL;
75 	void *in_match_value = NULL;
76 	u8 *smac;
77 	int vlan_mc_enable = MLX5_MATCH_OUTER_HEADERS;
78 	int err = 0;
79 
80 	if (!test_bit(vlan, acl_table->vlan_allowed_bitmap))
81 		return -EINVAL;
82 
83 	flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
84 	if (!flow_context) {
85 		err = -ENOMEM;
86 		goto out;
87 	}
88 
89 	in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
90 	if (!in_match_criteria) {
91 		err = -ENOMEM;
92 		goto out;
93 	}
94 
95 	/* Apply vlan rule */
96 	MLX5_SET(flow_context, flow_context, action,
97 		 MLX5_FLOW_CONTEXT_ACTION_ALLOW);
98 	in_match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value);
99 	MLX5_SET(fte_match_param, in_match_value, outer_headers.vlan_tag, 1);
100 	MLX5_SET(fte_match_param, in_match_value, outer_headers.first_vid,
101 		 vlan);
102 	MLX5_SET(fte_match_param, in_match_criteria, outer_headers.vlan_tag, 1);
103 	MLX5_SET(fte_match_param, in_match_criteria, outer_headers.first_vid,
104 		 0xfff);
105 	if (acl_table->spoofchk_enabled) {
106 		smac = MLX5_ADDR_OF(fte_match_param,
107 				    in_match_value,
108 				    outer_headers.smac_47_16);
109 		ether_addr_copy(smac, acl_table->smac);
110 		smac = MLX5_ADDR_OF(fte_match_param,
111 				    in_match_criteria,
112 				    outer_headers.smac_47_16);
113 		memset(smac, 0xff, ETH_ALEN);
114 	}
115 	err = mlx5_add_flow_table_entry(acl_table->ft, vlan_mc_enable,
116 					in_match_criteria, flow_context,
117 					&acl_table->vlan_fi_table[vlan]);
118 out:
119 	if (flow_context)
120 		vfree(flow_context);
121 	if (in_match_criteria)
122 		vfree(in_match_criteria);
123 	return err;
124 }
125 
mlx5_vacl_table_apply_loopback_filter(void * acl_t,u16 new_action)126 static int mlx5_vacl_table_apply_loopback_filter(void *acl_t, u16 new_action)
127 {
128 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
129 	u8 loopback_mc_enable = MLX5_MATCH_MISC_PARAMETERS;
130 	u32 *flow_context = NULL;
131 	void *in_match_criteria = NULL;
132 	void *in_match_value = NULL;
133 	void *mv_misc = NULL;
134 	void *mc_misc = NULL;
135 	int err = 0;
136 
137 	flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
138 	if (!flow_context) {
139 		err = -ENOMEM;
140 		goto out;
141 	}
142 
143 	in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
144 	if (!in_match_criteria) {
145 		err = -ENOMEM;
146 		goto out;
147 	}
148 
149 	if (acl_table->loopback_fr.applied)
150 		mlx5_del_flow_table_entry(acl_table->ft,
151 					  acl_table->loopback_fr.fi);
152 
153 	/* Apply new loopback rule */
154 	MLX5_SET(flow_context, flow_context, action, new_action);
155 	in_match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value);
156 	mv_misc  = MLX5_ADDR_OF(fte_match_param, in_match_value,
157 				misc_parameters);
158 	mc_misc  = MLX5_ADDR_OF(fte_match_param, in_match_criteria,
159 				misc_parameters);
160 	MLX5_SET(fte_match_set_misc, mv_misc, source_port, acl_table->vport);
161 
162 	MLX5_SET_TO_ONES(fte_match_set_misc, mc_misc, source_port);
163 
164 	err = mlx5_add_flow_table_entry(acl_table->ft, loopback_mc_enable,
165 					in_match_criteria, flow_context,
166 					&acl_table->loopback_fr.fi);
167 	if (err) {
168 		acl_table->loopback_fr.applied = false;
169 	} else {
170 		acl_table->loopback_fr.applied = true;
171 		acl_table->loopback_fr.action  = new_action;
172 	}
173 
174 out:
175 	if (flow_context)
176 		vfree(flow_context);
177 	if (in_match_criteria)
178 		vfree(in_match_criteria);
179 	return err;
180 }
181 
mlx5_vacl_table_apply_default(void * acl_t,u16 new_action)182 static int mlx5_vacl_table_apply_default(void *acl_t, u16 new_action)
183 {
184 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
185 	u8 default_mc_enable = 0;
186 	u32 *flow_context = NULL;
187 	void *in_match_criteria = NULL;
188 	int err = 0;
189 
190 	if (!acl_table->spoofchk_enabled)
191 		return -EINVAL;
192 
193 	flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
194 	if (!flow_context) {
195 		err = -ENOMEM;
196 		goto out;
197 	}
198 
199 	in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
200 	if (!in_match_criteria) {
201 		err = -ENOMEM;
202 		goto out;
203 	}
204 
205 	if (acl_table->default_fr.applied)
206 		mlx5_del_flow_table_entry(acl_table->ft,
207 					  acl_table->default_fr.fi);
208 
209 	/* Apply new default rule */
210 	MLX5_SET(flow_context, flow_context, action, new_action);
211 	err = mlx5_add_flow_table_entry(acl_table->ft, default_mc_enable,
212 					in_match_criteria, flow_context,
213 					&acl_table->default_fr.fi);
214 	if (err) {
215 		acl_table->default_fr.applied = false;
216 	} else {
217 		acl_table->default_fr.applied = true;
218 		acl_table->default_fr.action  = new_action;
219 	}
220 
221 out:
222 	if (flow_context)
223 		vfree(flow_context);
224 	if (in_match_criteria)
225 		vfree(in_match_criteria);
226 	return err;
227 }
228 
mlx5_vacl_table_apply_untagged(void * acl_t,u16 new_action)229 static int mlx5_vacl_table_apply_untagged(void *acl_t, u16 new_action)
230 {
231 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
232 	u8 untagged_mc_enable = MLX5_MATCH_OUTER_HEADERS;
233 	u8 *smac;
234 	u32 *flow_context = NULL;
235 	void *in_match_criteria = NULL;
236 	void *in_match_value = NULL;
237 	int err = 0;
238 
239 	flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
240 	if (!flow_context) {
241 		err = -ENOMEM;
242 		goto out;
243 	}
244 
245 	in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
246 	if (!in_match_criteria) {
247 		err = -ENOMEM;
248 		goto out;
249 	}
250 
251 	if (acl_table->untagged_fr.applied)
252 		mlx5_del_flow_table_entry(acl_table->ft,
253 					  acl_table->untagged_fr.fi);
254 
255 	/* Apply new untagged rule */
256 	MLX5_SET(flow_context, flow_context, action, new_action);
257 	in_match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value);
258 	MLX5_SET(fte_match_param, in_match_value, outer_headers.vlan_tag, 0);
259 	MLX5_SET(fte_match_param, in_match_criteria, outer_headers.vlan_tag, 1);
260 	if (acl_table->spoofchk_enabled) {
261 		smac = MLX5_ADDR_OF(fte_match_param,
262 				    in_match_value,
263 				    outer_headers.smac_47_16);
264 		ether_addr_copy(smac, acl_table->smac);
265 		smac = MLX5_ADDR_OF(fte_match_param,
266 				    in_match_criteria,
267 				    outer_headers.smac_47_16);
268 		memset(smac, 0xff, ETH_ALEN);
269 	}
270 	err = mlx5_add_flow_table_entry(acl_table->ft, untagged_mc_enable,
271 					in_match_criteria, flow_context,
272 					&acl_table->untagged_fr.fi);
273 	if (err) {
274 		acl_table->untagged_fr.applied = false;
275 	} else {
276 		acl_table->untagged_fr.applied = true;
277 		acl_table->untagged_fr.action  = new_action;
278 	}
279 
280 out:
281 	if (flow_context)
282 		vfree(flow_context);
283 	if (in_match_criteria)
284 		vfree(in_match_criteria);
285 	return err;
286 }
287 
mlx5_vacl_table_apply_unknown_vlan(void * acl_t,u16 new_action)288 static int mlx5_vacl_table_apply_unknown_vlan(void *acl_t, u16 new_action)
289 {
290 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
291 	u8 default_mc_enable = (!acl_table->spoofchk_enabled) ? 0 :
292 				MLX5_MATCH_OUTER_HEADERS;
293 	u32 *flow_context = NULL;
294 	void *in_match_criteria = NULL;
295 	void *in_match_value = NULL;
296 	u8 *smac;
297 	int err = 0;
298 
299 	flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
300 	if (!flow_context) {
301 		err = -ENOMEM;
302 		goto out;
303 	}
304 
305 	in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
306 	if (!in_match_criteria) {
307 		err = -ENOMEM;
308 		goto out;
309 	}
310 
311 	if (acl_table->unknown_vlan_fr.applied)
312 		mlx5_del_flow_table_entry(acl_table->ft,
313 					  acl_table->unknown_vlan_fr.fi);
314 
315 	/* Apply new unknown vlan rule */
316 	MLX5_SET(flow_context, flow_context, action, new_action);
317 	if (acl_table->spoofchk_enabled) {
318 		in_match_value = MLX5_ADDR_OF(flow_context, flow_context,
319 					      match_value);
320 		smac = MLX5_ADDR_OF(fte_match_param,
321 				    in_match_value,
322 				    outer_headers.smac_47_16);
323 		ether_addr_copy(smac, acl_table->smac);
324 		smac = MLX5_ADDR_OF(fte_match_param,
325 				    in_match_criteria,
326 				    outer_headers.smac_47_16);
327 		memset(smac, 0xff, ETH_ALEN);
328 	}
329 	err = mlx5_add_flow_table_entry(acl_table->ft, default_mc_enable,
330 					in_match_criteria, flow_context,
331 					&acl_table->unknown_vlan_fr.fi);
332 	if (err) {
333 		acl_table->unknown_vlan_fr.applied = false;
334 	} else {
335 		acl_table->unknown_vlan_fr.applied = true;
336 		acl_table->unknown_vlan_fr.action  = new_action;
337 	}
338 
339 out:
340 	if (flow_context)
341 		vfree(flow_context);
342 	if (in_match_criteria)
343 		vfree(in_match_criteria);
344 	return err;
345 }
346 
mlx5_vacl_table_apply_vlan_filter(void * acl_t)347 static int mlx5_vacl_table_apply_vlan_filter(void *acl_t)
348 {
349 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
350 	int index = 0;
351 	int err_index = 0;
352 	int err = 0;
353 
354 	if (acl_table->vlan_filter_applied)
355 		return 0;
356 
357 	for (index = find_first_bit(acl_table->vlan_allowed_bitmap, 4096);
358 		index < 4096;
359 		index = find_next_bit(acl_table->vlan_allowed_bitmap,
360 				      4096, ++index)) {
361 		err = mlx5_vacl_table_allow_vlan(acl_t, index);
362 		if (err)
363 			goto err_disable_vlans;
364 	}
365 
366 	acl_table->vlan_filter_applied = true;
367 	return 0;
368 
369 err_disable_vlans:
370 	for (err_index = find_first_bit(acl_table->vlan_allowed_bitmap, 4096);
371 		err_index < index;
372 		err_index = find_next_bit(acl_table->vlan_allowed_bitmap, 4096,
373 					  ++err_index)) {
374 		mlx5_del_flow_table_entry(acl_table->ft,
375 					  acl_table->vlan_fi_table[err_index]);
376 	}
377 	return err;
378 }
379 
mlx5_vacl_table_disapply_vlan_filter(void * acl_t)380 static void mlx5_vacl_table_disapply_vlan_filter(void *acl_t)
381 {
382 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
383 	int index = 0;
384 
385 	if (!acl_table->vlan_filter_applied)
386 		return;
387 
388 	for (index = find_first_bit(acl_table->vlan_allowed_bitmap, 4096);
389 		index < 4096;
390 		index = find_next_bit(acl_table->vlan_allowed_bitmap, 4096,
391 				      ++index)) {
392 		mlx5_del_flow_table_entry(acl_table->ft,
393 					  acl_table->vlan_fi_table[index]);
394 	}
395 
396 	acl_table->vlan_filter_applied = false;
397 }
398 
mlx5_vacl_table_disapply_all_filters(void * acl_t)399 static void mlx5_vacl_table_disapply_all_filters(void *acl_t)
400 {
401 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
402 
403 	if (acl_table->default_fr.applied) {
404 		mlx5_del_flow_table_entry(acl_table->ft,
405 					  acl_table->default_fr.fi);
406 		acl_table->default_fr.applied = false;
407 	}
408 	if (acl_table->unknown_vlan_fr.applied) {
409 		mlx5_del_flow_table_entry(acl_table->ft,
410 					  acl_table->unknown_vlan_fr.fi);
411 		acl_table->unknown_vlan_fr.applied = false;
412 	}
413 	if (acl_table->loopback_fr.applied) {
414 		mlx5_del_flow_table_entry(acl_table->ft,
415 					  acl_table->loopback_fr.fi);
416 		acl_table->loopback_fr.applied = false;
417 	}
418 	if (acl_table->untagged_fr.applied) {
419 		mlx5_del_flow_table_entry(acl_table->ft,
420 					  acl_table->untagged_fr.fi);
421 		acl_table->untagged_fr.applied = false;
422 	}
423 	if (acl_table->vlan_filter_applied) {
424 		mlx5_vacl_table_disapply_vlan_filter(acl_t);
425 		acl_table->vlan_filter_applied = false;
426 	}
427 }
428 
mlx5_vacl_table_apply_all_filters(void * acl_t)429 static int mlx5_vacl_table_apply_all_filters(void *acl_t)
430 {
431 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
432 	int err = 0;
433 
434 	if (!acl_table->default_fr.applied && acl_table->spoofchk_enabled) {
435 		err = mlx5_vacl_table_apply_default(acl_table,
436 						    acl_table->default_fr.action);
437 		if (err)
438 			goto err_disapply_all;
439 	}
440 
441 	if (!acl_table->unknown_vlan_fr.applied) {
442 		err = mlx5_vacl_table_apply_unknown_vlan(acl_table,
443 							 acl_table->unknown_vlan_fr.action);
444 		if (err)
445 			goto err_disapply_all;
446 	}
447 
448 	if (!acl_table->loopback_fr.applied &&
449 	    acl_table->acl_type == MLX5_FLOW_TABLE_TYPE_EGRESS_ACL) {
450 		err = mlx5_vacl_table_apply_loopback_filter(
451 						acl_table,
452 						acl_table->loopback_fr.action);
453 		if (err)
454 			goto err_disapply_all;
455 	}
456 
457 	if (!acl_table->untagged_fr.applied) {
458 		err = mlx5_vacl_table_apply_untagged(acl_table,
459 						     acl_table->untagged_fr.action);
460 		if (err)
461 			goto err_disapply_all;
462 	}
463 
464 	if (!acl_table->vlan_filter_applied && acl_table->vlan_filter_enabled) {
465 		err = mlx5_vacl_table_apply_vlan_filter(acl_t);
466 		if (err)
467 			goto err_disapply_all;
468 	}
469 
470 	goto out;
471 
472 err_disapply_all:
473 	mlx5_vacl_table_disapply_all_filters(acl_t);
474 
475 out:
476 	return err;
477 }
478 
mlx5_vacl_table_destroy_ft(void * acl_t)479 static void mlx5_vacl_table_destroy_ft(void *acl_t)
480 {
481 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
482 
483 	mlx5_vacl_table_disapply_all_filters(acl_t);
484 	if (acl_table->ft)
485 		mlx5_destroy_flow_table(acl_table->ft);
486 	acl_table->ft = NULL;
487 }
488 
mlx5_vacl_table_create_ft(void * acl_t,bool spoofchk)489 static int mlx5_vacl_table_create_ft(void *acl_t, bool spoofchk)
490 {
491 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
492 	int log_acl_ft_size;
493 	int err = 0;
494 	int groups_num = MLX5_ACL_GROUPS_NUM - 1;
495 	int shift_idx = MLX5_ACL_UNTAGGED_GROUP_IDX;
496 	u8 *smac;
497 	struct mlx5_flow_table_group *g;
498 
499 	if (acl_table->ft)
500 		return -EINVAL;
501 
502 	g = kcalloc(MLX5_ACL_GROUPS_NUM, sizeof(*g), GFP_KERNEL);
503 	if (!g)
504 		goto out;
505 
506 	acl_table->spoofchk_enabled = spoofchk;
507 
508 	/*
509 	 * for vlan group
510 	 */
511 	log_acl_ft_size = 4096;
512 	/*
513 	 * for loopback filter rule
514 	 */
515 	log_acl_ft_size += 1;
516 	/*
517 	 * for untagged rule
518 	 */
519 	log_acl_ft_size += 1;
520 	/*
521 	 * for unknown vlan rule
522 	 */
523 	log_acl_ft_size += 1;
524 	/*
525 	 * for default rule
526 	 */
527 	log_acl_ft_size += 1;
528 
529 	log_acl_ft_size = order_base_2(log_acl_ft_size);
530 	log_acl_ft_size = min_t(int, log_acl_ft_size, acl_table->max_ft_size);
531 
532 	if (log_acl_ft_size < 2)
533 		goto out;
534 
535 	if (acl_table->acl_type == MLX5_FLOW_TABLE_TYPE_EGRESS_ACL) {
536 		/* Loopback filter group */
537 		g[MLX5_ACL_LOOPBACK_GROUP_IDX].log_sz = 0;
538 		g[MLX5_ACL_LOOPBACK_GROUP_IDX].match_criteria_enable =
539 				MLX5_MATCH_MISC_PARAMETERS;
540 		MLX5_SET_TO_ONES(fte_match_param,
541 				 g[MLX5_ACL_LOOPBACK_GROUP_IDX].match_criteria,
542 				 misc_parameters.source_port);
543 		groups_num++;
544 		shift_idx = MLX5_ACL_LOOPBACK_GROUP_IDX;
545 	}
546 	/* Untagged traffic group */
547 	g[MLX5_ACL_UNTAGGED_GROUP_IDX - shift_idx].log_sz = 0;
548 	g[MLX5_ACL_UNTAGGED_GROUP_IDX - shift_idx].match_criteria_enable =
549 			MLX5_MATCH_OUTER_HEADERS;
550 	MLX5_SET(fte_match_param,
551 		 g[MLX5_ACL_UNTAGGED_GROUP_IDX - shift_idx].match_criteria,
552 		 outer_headers.vlan_tag, 1);
553 	if (spoofchk) {
554 		smac = MLX5_ADDR_OF(fte_match_param,
555 				    g[MLX5_ACL_UNTAGGED_GROUP_IDX - shift_idx]
556 				      .match_criteria,
557 				    outer_headers.smac_47_16);
558 		memset(smac, 0xff, ETH_ALEN);
559 	}
560 
561 	/* Allowed vlans group */
562 	g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx].log_sz = log_acl_ft_size - 1;
563 	g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx].match_criteria_enable =
564 			MLX5_MATCH_OUTER_HEADERS;
565 	MLX5_SET(fte_match_param,
566 		 g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx].match_criteria,
567 		 outer_headers.vlan_tag, 1);
568 	MLX5_SET(fte_match_param,
569 		 g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx].match_criteria,
570 		 outer_headers.first_vid, 0xfff);
571 	if (spoofchk) {
572 		smac = MLX5_ADDR_OF(fte_match_param,
573 				    g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx]
574 				      .match_criteria,
575 				    outer_headers.smac_47_16);
576 		memset(smac, 0xff, ETH_ALEN);
577 	}
578 
579 	/* Unknown vlan traffic group */
580 	g[MLX5_ACL_UNKNOWN_VLAN_GROUP_IDX - shift_idx].log_sz = 0;
581 	g[MLX5_ACL_UNKNOWN_VLAN_GROUP_IDX - shift_idx].match_criteria_enable =
582 			(spoofchk ? MLX5_MATCH_OUTER_HEADERS : 0);
583 	if (spoofchk) {
584 		smac = MLX5_ADDR_OF(
585 				fte_match_param,
586 				g[MLX5_ACL_UNKNOWN_VLAN_GROUP_IDX - shift_idx]
587 				  .match_criteria,
588 				outer_headers.smac_47_16);
589 		memset(smac, 0xff, ETH_ALEN);
590 	}
591 
592 	/*
593 	 * Default group - for spoofchk only.
594 	 */
595 	g[MLX5_ACL_DEFAULT_GROUP_IDX - shift_idx].log_sz = 0;
596 	g[MLX5_ACL_DEFAULT_GROUP_IDX - shift_idx].match_criteria_enable = 0;
597 
598 	acl_table->ft = mlx5_create_flow_table(acl_table->dev,
599 					       0,
600 					       acl_table->acl_type,
601 					       acl_table->vport,
602 					       groups_num,
603 					       g);
604 	if (!acl_table->ft) {
605 		err = -ENOMEM;
606 		goto out;
607 	}
608 
609 	err = mlx5_vacl_table_apply_all_filters(acl_t);
610 	if (err)
611 		goto err_destroy_ft;
612 
613 	goto out;
614 
615 err_destroy_ft:
616 	mlx5_vacl_table_destroy_ft(acl_table->ft);
617 	acl_table->ft = NULL;
618 
619 out:
620 	kfree(g);
621 	return err;
622 }
623 
mlx5_vacl_table_create(struct mlx5_core_dev * dev,u16 vport,bool is_egress)624 void *mlx5_vacl_table_create(struct mlx5_core_dev *dev,
625 			     u16 vport, bool is_egress)
626 {
627 	struct mlx5_vacl_table *acl_table;
628 	int err = 0;
629 
630 	if (is_egress && !MLX5_CAP_ESW_FLOWTABLE_EGRESS_ACL(dev, ft_support))
631 		return NULL;
632 
633 	if (!is_egress && !MLX5_CAP_ESW_FLOWTABLE_INGRESS_ACL(dev, ft_support))
634 		return NULL;
635 
636 	acl_table = kzalloc(sizeof(*acl_table), GFP_KERNEL);
637 	if (!acl_table)
638 		return NULL;
639 
640 	acl_table->acl_type = is_egress ? MLX5_FLOW_TABLE_TYPE_EGRESS_ACL :
641 					  MLX5_FLOW_TABLE_TYPE_INGRESS_ACL;
642 	acl_table->max_ft_size = (is_egress ?
643 					MLX5_CAP_ESW_FLOWTABLE_EGRESS_ACL(dev,
644 									  log_max_ft_size) :
645 					MLX5_CAP_ESW_FLOWTABLE_INGRESS_ACL(dev,
646 									   log_max_ft_size));
647 	acl_table->dev = dev;
648 	acl_table->vport = vport;
649 
650 	/*
651 	 * default behavior : Allow and if spoofchk drop the default
652 	 */
653 	acl_table->default_fr.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
654 	acl_table->loopback_fr.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
655 	acl_table->unknown_vlan_fr.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW;
656 	acl_table->untagged_fr.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW;
657 	err = mlx5_vacl_table_create_ft(acl_table, false);
658 	if (err)
659 		goto err_free_acl_table;
660 
661 	acl_table->vlan_allowed_bitmap = kcalloc(BITS_TO_LONGS(4096),
662 						 sizeof(uintptr_t),
663 						 GFP_KERNEL);
664 	if (!acl_table->vlan_allowed_bitmap)
665 		goto err_destroy_ft;
666 
667 	goto out;
668 
669 err_destroy_ft:
670 	mlx5_vacl_table_destroy_ft(acl_table->ft);
671 	acl_table->ft = NULL;
672 
673 err_free_acl_table:
674 	kfree(acl_table);
675 	acl_table = NULL;
676 
677 out:
678 	return (void *)acl_table;
679 }
680 EXPORT_SYMBOL(mlx5_vacl_table_create);
681 
mlx5_vacl_table_cleanup(void * acl_t)682 void mlx5_vacl_table_cleanup(void *acl_t)
683 {
684 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
685 
686 	mlx5_vacl_table_destroy_ft(acl_t);
687 	kfree(acl_table->vlan_allowed_bitmap);
688 	kfree(acl_table);
689 }
690 EXPORT_SYMBOL(mlx5_vacl_table_cleanup);
691 
mlx5_vacl_table_add_vlan(void * acl_t,u16 vlan)692 int mlx5_vacl_table_add_vlan(void *acl_t, u16 vlan)
693 {
694 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
695 	int err = 0;
696 
697 	if (test_bit(vlan, acl_table->vlan_allowed_bitmap))
698 		return 0;
699 	__set_bit(vlan, acl_table->vlan_allowed_bitmap);
700 	if (!acl_table->vlan_filter_applied)
701 		return 0;
702 
703 	err = mlx5_vacl_table_allow_vlan(acl_t, vlan);
704 	if (err)
705 		goto err_clear_vbit;
706 
707 	goto out;
708 
709 err_clear_vbit:
710 	__clear_bit(vlan, acl_table->vlan_allowed_bitmap);
711 
712 out:
713 	return err;
714 }
715 EXPORT_SYMBOL(mlx5_vacl_table_add_vlan);
716 
mlx5_vacl_table_del_vlan(void * acl_t,u16 vlan)717 void mlx5_vacl_table_del_vlan(void *acl_t, u16 vlan)
718 {
719 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
720 
721 	if (!test_bit(vlan, acl_table->vlan_allowed_bitmap))
722 		return;
723 
724 	__clear_bit(vlan, acl_table->vlan_allowed_bitmap);
725 
726 	if (!acl_table->vlan_filter_applied)
727 		return;
728 
729 	mlx5_del_flow_table_entry(acl_table->ft,
730 				  acl_table->vlan_fi_table[vlan]);
731 }
732 EXPORT_SYMBOL(mlx5_vacl_table_del_vlan);
733 
mlx5_vacl_table_enable_vlan_filter(void * acl_t)734 int mlx5_vacl_table_enable_vlan_filter(void *acl_t)
735 {
736 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
737 
738 	acl_table->vlan_filter_enabled = true;
739 	return mlx5_vacl_table_apply_vlan_filter(acl_t);
740 }
741 EXPORT_SYMBOL(mlx5_vacl_table_enable_vlan_filter);
742 
mlx5_vacl_table_disable_vlan_filter(void * acl_t)743 void mlx5_vacl_table_disable_vlan_filter(void *acl_t)
744 {
745 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
746 
747 	acl_table->vlan_filter_enabled = false;
748 	mlx5_vacl_table_disapply_vlan_filter(acl_t);
749 }
750 EXPORT_SYMBOL(mlx5_vacl_table_disable_vlan_filter);
751 
mlx5_vacl_table_drop_untagged(void * acl_t)752 int mlx5_vacl_table_drop_untagged(void *acl_t)
753 {
754 	return mlx5_vacl_table_apply_untagged(acl_t,
755 			MLX5_FLOW_CONTEXT_ACTION_DROP);
756 }
757 EXPORT_SYMBOL(mlx5_vacl_table_drop_untagged);
758 
mlx5_vacl_table_allow_untagged(void * acl_t)759 int mlx5_vacl_table_allow_untagged(void *acl_t)
760 {
761 	return mlx5_vacl_table_apply_untagged(acl_t,
762 			MLX5_FLOW_CONTEXT_ACTION_ALLOW);
763 }
764 EXPORT_SYMBOL(mlx5_vacl_table_allow_untagged);
765 
mlx5_vacl_table_drop_unknown_vlan(void * acl_t)766 int mlx5_vacl_table_drop_unknown_vlan(void *acl_t)
767 {
768 	return mlx5_vacl_table_apply_unknown_vlan(acl_t,
769 			MLX5_FLOW_CONTEXT_ACTION_DROP);
770 }
771 EXPORT_SYMBOL(mlx5_vacl_table_drop_unknown_vlan);
772 
mlx5_vacl_table_allow_unknown_vlan(void * acl_t)773 int mlx5_vacl_table_allow_unknown_vlan(void *acl_t)
774 {
775 	return mlx5_vacl_table_apply_unknown_vlan(acl_t,
776 			MLX5_FLOW_CONTEXT_ACTION_ALLOW);
777 }
778 EXPORT_SYMBOL(mlx5_vacl_table_allow_unknown_vlan);
779 
mlx5_vacl_table_set_spoofchk(void * acl_t,bool spoofchk,u8 * vport_mac)780 int mlx5_vacl_table_set_spoofchk(void *acl_t, bool spoofchk, u8 *vport_mac)
781 {
782 	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
783 	int err = 0;
784 
785 	if (spoofchk == acl_table->spoofchk_enabled) {
786 		if (!spoofchk ||
787 		    (spoofchk && !memcmp(acl_table->smac, vport_mac, ETH_ALEN)))
788 			return 0;
789 	}
790 
791 	ether_addr_copy(acl_table->smac, vport_mac);
792 	if (spoofchk != acl_table->spoofchk_enabled) {
793 		mlx5_vacl_table_destroy_ft(acl_t);
794 		err = mlx5_vacl_table_create_ft(acl_t, spoofchk);
795 	} else {
796 		mlx5_vacl_table_disapply_all_filters(acl_t);
797 		err = mlx5_vacl_table_apply_all_filters(acl_t);
798 	}
799 
800 	return err;
801 }
802 EXPORT_SYMBOL(mlx5_vacl_table_set_spoofchk);
803 
804