xref: /dpdk/examples/l3fwd/l3fwd_acl.c (revision d5c4897ecfb2540dc4990d9b367ddbe5013d0e66)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2022 Intel Corporation
3  */
4 
5 #include "l3fwd.h"
6 #include "l3fwd_route.h"
7 
8 /*
9  * Rule and trace formats definitions.
10  */
11 
12 enum {
13 	PROTO_FIELD_IPV4,
14 	SRC_FIELD_IPV4,
15 	DST_FIELD_IPV4,
16 	SRCP_FIELD_IPV4,
17 	DSTP_FIELD_IPV4,
18 	NUM_FIELDS_IPV4
19 };
20 
21 /*
22  * That effectively defines order of IPV4VLAN classifications:
23  *  - PROTO
24  *  - VLAN (TAG and DOMAIN)
25  *  - SRC IP ADDRESS
26  *  - DST IP ADDRESS
27  *  - PORTS (SRC and DST)
28  */
29 enum {
30 	RTE_ACL_IPV4VLAN_PROTO,
31 	RTE_ACL_IPV4VLAN_VLAN,
32 	RTE_ACL_IPV4VLAN_SRC,
33 	RTE_ACL_IPV4VLAN_DST,
34 	RTE_ACL_IPV4VLAN_PORTS,
35 	RTE_ACL_IPV4VLAN_NUM
36 };
37 
38 struct acl_algorithms acl_alg[] = {
39 	{
40 		.name = "scalar",
41 		.alg = RTE_ACL_CLASSIFY_SCALAR,
42 	},
43 	{
44 		.name = "sse",
45 		.alg = RTE_ACL_CLASSIFY_SSE,
46 	},
47 	{
48 		.name = "avx2",
49 		.alg = RTE_ACL_CLASSIFY_AVX2,
50 	},
51 	{
52 		.name = "neon",
53 		.alg = RTE_ACL_CLASSIFY_NEON,
54 	},
55 	{
56 		.name = "altivec",
57 		.alg = RTE_ACL_CLASSIFY_ALTIVEC,
58 	},
59 	{
60 		.name = "avx512x16",
61 		.alg = RTE_ACL_CLASSIFY_AVX512X16,
62 	},
63 	{
64 		.name = "avx512x32",
65 		.alg = RTE_ACL_CLASSIFY_AVX512X32,
66 	},
67 };
68 
69 struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
70 	{
71 		.type = RTE_ACL_FIELD_TYPE_BITMASK,
72 		.size = sizeof(uint8_t),
73 		.field_index = PROTO_FIELD_IPV4,
74 		.input_index = RTE_ACL_IPV4VLAN_PROTO,
75 		.offset = 0,
76 	},
77 	{
78 		.type = RTE_ACL_FIELD_TYPE_MASK,
79 		.size = sizeof(uint32_t),
80 		.field_index = SRC_FIELD_IPV4,
81 		.input_index = RTE_ACL_IPV4VLAN_SRC,
82 		.offset = offsetof(struct rte_ipv4_hdr, src_addr) -
83 			offsetof(struct rte_ipv4_hdr, next_proto_id),
84 	},
85 	{
86 		.type = RTE_ACL_FIELD_TYPE_MASK,
87 		.size = sizeof(uint32_t),
88 		.field_index = DST_FIELD_IPV4,
89 		.input_index = RTE_ACL_IPV4VLAN_DST,
90 		.offset = offsetof(struct rte_ipv4_hdr, dst_addr) -
91 			offsetof(struct rte_ipv4_hdr, next_proto_id),
92 	},
93 	{
94 		.type = RTE_ACL_FIELD_TYPE_RANGE,
95 		.size = sizeof(uint16_t),
96 		.field_index = SRCP_FIELD_IPV4,
97 		.input_index = RTE_ACL_IPV4VLAN_PORTS,
98 		.offset = sizeof(struct rte_ipv4_hdr) -
99 			offsetof(struct rte_ipv4_hdr, next_proto_id),
100 	},
101 	{
102 		.type = RTE_ACL_FIELD_TYPE_RANGE,
103 		.size = sizeof(uint16_t),
104 		.field_index = DSTP_FIELD_IPV4,
105 		.input_index = RTE_ACL_IPV4VLAN_PORTS,
106 		.offset = sizeof(struct rte_ipv4_hdr) -
107 			offsetof(struct rte_ipv4_hdr, next_proto_id) +
108 			sizeof(uint16_t),
109 	},
110 };
111 
112 enum {
113 	PROTO_FIELD_IPV6,
114 	SRC1_FIELD_IPV6,
115 	SRC2_FIELD_IPV6,
116 	SRC3_FIELD_IPV6,
117 	SRC4_FIELD_IPV6,
118 	DST1_FIELD_IPV6,
119 	DST2_FIELD_IPV6,
120 	DST3_FIELD_IPV6,
121 	DST4_FIELD_IPV6,
122 	SRCP_FIELD_IPV6,
123 	DSTP_FIELD_IPV6,
124 	NUM_FIELDS_IPV6
125 };
126 
127 struct rte_acl_field_def ipv6_defs[NUM_FIELDS_IPV6] = {
128 	{
129 		.type = RTE_ACL_FIELD_TYPE_BITMASK,
130 		.size = sizeof(uint8_t),
131 		.field_index = PROTO_FIELD_IPV6,
132 		.input_index = PROTO_FIELD_IPV6,
133 		.offset = 0,
134 	},
135 	{
136 		.type = RTE_ACL_FIELD_TYPE_MASK,
137 		.size = sizeof(uint32_t),
138 		.field_index = SRC1_FIELD_IPV6,
139 		.input_index = SRC1_FIELD_IPV6,
140 		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
141 			offsetof(struct rte_ipv6_hdr, proto),
142 	},
143 	{
144 		.type = RTE_ACL_FIELD_TYPE_MASK,
145 		.size = sizeof(uint32_t),
146 		.field_index = SRC2_FIELD_IPV6,
147 		.input_index = SRC2_FIELD_IPV6,
148 		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
149 			offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint32_t),
150 	},
151 	{
152 		.type = RTE_ACL_FIELD_TYPE_MASK,
153 		.size = sizeof(uint32_t),
154 		.field_index = SRC3_FIELD_IPV6,
155 		.input_index = SRC3_FIELD_IPV6,
156 		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
157 			offsetof(struct rte_ipv6_hdr, proto) +
158 			2 * sizeof(uint32_t),
159 	},
160 	{
161 		.type = RTE_ACL_FIELD_TYPE_MASK,
162 		.size = sizeof(uint32_t),
163 		.field_index = SRC4_FIELD_IPV6,
164 		.input_index = SRC4_FIELD_IPV6,
165 		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
166 			offsetof(struct rte_ipv6_hdr, proto) +
167 			3 * sizeof(uint32_t),
168 	},
169 	{
170 		.type = RTE_ACL_FIELD_TYPE_MASK,
171 		.size = sizeof(uint32_t),
172 		.field_index = DST1_FIELD_IPV6,
173 		.input_index = DST1_FIELD_IPV6,
174 		.offset = offsetof(struct rte_ipv6_hdr, dst_addr)
175 				- offsetof(struct rte_ipv6_hdr, proto),
176 	},
177 	{
178 		.type = RTE_ACL_FIELD_TYPE_MASK,
179 		.size = sizeof(uint32_t),
180 		.field_index = DST2_FIELD_IPV6,
181 		.input_index = DST2_FIELD_IPV6,
182 		.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
183 			offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint32_t),
184 	},
185 	{
186 		.type = RTE_ACL_FIELD_TYPE_MASK,
187 		.size = sizeof(uint32_t),
188 		.field_index = DST3_FIELD_IPV6,
189 		.input_index = DST3_FIELD_IPV6,
190 		.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
191 			offsetof(struct rte_ipv6_hdr, proto) +
192 			2 * sizeof(uint32_t),
193 	},
194 	{
195 		.type = RTE_ACL_FIELD_TYPE_MASK,
196 		.size = sizeof(uint32_t),
197 		.field_index = DST4_FIELD_IPV6,
198 		.input_index = DST4_FIELD_IPV6,
199 		.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
200 			offsetof(struct rte_ipv6_hdr, proto) +
201 			3 * sizeof(uint32_t),
202 	},
203 	{
204 		.type = RTE_ACL_FIELD_TYPE_RANGE,
205 		.size = sizeof(uint16_t),
206 		.field_index = SRCP_FIELD_IPV6,
207 		.input_index = SRCP_FIELD_IPV6,
208 		.offset = sizeof(struct rte_ipv6_hdr) -
209 			offsetof(struct rte_ipv6_hdr, proto),
210 	},
211 	{
212 		.type = RTE_ACL_FIELD_TYPE_RANGE,
213 		.size = sizeof(uint16_t),
214 		.field_index = DSTP_FIELD_IPV6,
215 		.input_index = SRCP_FIELD_IPV6,
216 		.offset = sizeof(struct rte_ipv6_hdr) -
217 			offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint16_t),
218 	},
219 };
220 
221 enum {
222 	CB_FLD_SRC_ADDR,
223 	CB_FLD_DST_ADDR,
224 	CB_FLD_SRC_PORT_LOW,
225 	CB_FLD_SRC_PORT_DLM,
226 	CB_FLD_SRC_PORT_HIGH,
227 	CB_FLD_DST_PORT_LOW,
228 	CB_FLD_DST_PORT_DLM,
229 	CB_FLD_DST_PORT_HIGH,
230 	CB_FLD_PROTO,
231 	CB_FLD_USERDATA,
232 	CB_FLD_NUM,
233 };
234 
235 RTE_ACL_RULE_DEF(acl4_rule, RTE_DIM(ipv4_defs));
236 RTE_ACL_RULE_DEF(acl6_rule, RTE_DIM(ipv6_defs));
237 
238 static struct {
239 	struct rte_acl_ctx *acx_ipv4[NB_SOCKETS];
240 	struct rte_acl_ctx *acx_ipv6[NB_SOCKETS];
241 #ifdef L3FWDACL_DEBUG
242 	struct acl4_rule *rule_ipv4;
243 	struct acl6_rule *rule_ipv6;
244 #endif
245 } acl_config;
246 
247 static const char cb_port_delim[] = ":";
248 
249 static struct rte_acl_rule *acl_base_ipv4, *route_base_ipv4,
250 		*acl_base_ipv6, *route_base_ipv6;
251 static unsigned int acl_num_ipv4, route_num_ipv4,
252 		acl_num_ipv6, route_num_ipv6;
253 
254 #include "l3fwd_acl.h"
255 
256 #include "l3fwd_acl_scalar.h"
257 
258 /*
259  * Parse IPV6 address, expects the following format:
260  * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X is a hexadecimal digit).
261  */
262 static int
263 parse_ipv6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
264 	char dlm)
265 {
266 	uint32_t addr[IPV6_ADDR_U16];
267 
268 	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
269 	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
270 	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
271 	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
272 	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
273 	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
274 	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
275 	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
276 
277 	*end = in;
278 
279 	v[0] = (addr[0] << 16) + addr[1];
280 	v[1] = (addr[2] << 16) + addr[3];
281 	v[2] = (addr[4] << 16) + addr[5];
282 	v[3] = (addr[6] << 16) + addr[7];
283 
284 	return 0;
285 }
286 
287 static int
288 parse_ipv6_net(const char *in, struct rte_acl_field field[4])
289 {
290 	int32_t rc;
291 	const char *mp;
292 	uint32_t i, m, v[4];
293 	const uint32_t nbu32 = sizeof(uint32_t) * CHAR_BIT;
294 
295 	/* get address. */
296 	rc = parse_ipv6_addr(in, &mp, v, '/');
297 	if (rc != 0)
298 		return rc;
299 
300 	/* get mask. */
301 	GET_CB_FIELD(mp, m, 0, CHAR_BIT * sizeof(v), 0);
302 
303 	/* put all together. */
304 	for (i = 0; i != RTE_DIM(v); i++) {
305 		if (m >= (i + 1) * nbu32)
306 			field[i].mask_range.u32 = nbu32;
307 		else
308 			field[i].mask_range.u32 = m > (i * nbu32) ?
309 				m - (i * 32) : 0;
310 
311 		field[i].value.u32 = v[i];
312 	}
313 
314 	return 0;
315 }
316 
317 static int
318 parse_cb_ipv6_rule(char *str, struct rte_acl_rule *v, int has_userdata)
319 {
320 	int i, rc;
321 	char *s, *sp, *in[CB_FLD_NUM];
322 	static const char *dlm = " \t\n";
323 	int dim = has_userdata ? CB_FLD_NUM : CB_FLD_USERDATA;
324 	s = str;
325 
326 	for (i = 0; i != dim; i++, s = NULL) {
327 		in[i] = strtok_r(s, dlm, &sp);
328 		if (in[i] == NULL)
329 			return -EINVAL;
330 	}
331 
332 	rc = parse_ipv6_net(in[CB_FLD_SRC_ADDR], v->field + SRC1_FIELD_IPV6);
333 	if (rc != 0) {
334 		acl_log("failed to read source address/mask: %s\n",
335 			in[CB_FLD_SRC_ADDR]);
336 		return rc;
337 	}
338 
339 	rc = parse_ipv6_net(in[CB_FLD_DST_ADDR], v->field + DST1_FIELD_IPV6);
340 	if (rc != 0) {
341 		acl_log("failed to read destination address/mask: %s\n",
342 			in[CB_FLD_DST_ADDR]);
343 		return rc;
344 	}
345 
346 	/* source port. */
347 	GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW],
348 		v->field[SRCP_FIELD_IPV6].value.u16,
349 		0, UINT16_MAX, 0);
350 	GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH],
351 		v->field[SRCP_FIELD_IPV6].mask_range.u16,
352 		0, UINT16_MAX, 0);
353 
354 	if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
355 			sizeof(cb_port_delim)) != 0)
356 		return -EINVAL;
357 
358 	/* destination port. */
359 	GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW],
360 		v->field[DSTP_FIELD_IPV6].value.u16,
361 		0, UINT16_MAX, 0);
362 	GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH],
363 		v->field[DSTP_FIELD_IPV6].mask_range.u16,
364 		0, UINT16_MAX, 0);
365 
366 	if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
367 			sizeof(cb_port_delim)) != 0)
368 		return -EINVAL;
369 
370 	if (v->field[SRCP_FIELD_IPV6].mask_range.u16
371 			< v->field[SRCP_FIELD_IPV6].value.u16
372 			|| v->field[DSTP_FIELD_IPV6].mask_range.u16
373 			< v->field[DSTP_FIELD_IPV6].value.u16)
374 		return -EINVAL;
375 
376 	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV6].value.u8,
377 		0, UINT8_MAX, '/');
378 	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV6].mask_range.u8,
379 		0, UINT8_MAX, 0);
380 
381 	if (has_userdata)
382 		GET_CB_FIELD(in[CB_FLD_USERDATA], v->data.userdata,
383 			0, UINT32_MAX, 0);
384 
385 	return 0;
386 }
387 
388 /*
389  * Parse ClassBench rules file.
390  * Expected format:
391  * '@'<src_ipv4_addr>'/'<masklen> <space> \
392  * <dst_ipv4_addr>'/'<masklen> <space> \
393  * <src_port_low> <space> ":" <src_port_high> <space> \
394  * <dst_port_low> <space> ":" <dst_port_high> <space> \
395  * <proto>'/'<mask>
396  */
397 static int
398 parse_ipv4_net(char *in, uint32_t *addr, uint32_t *mask_len)
399 {
400 	char *sa, *sm, *sv;
401 	const char *dlm =  "/";
402 
403 	sv = NULL;
404 	sa = strtok_r(in, dlm, &sv);
405 	if (sa == NULL)
406 		return -EINVAL;
407 	sm = strtok_r(NULL, dlm, &sv);
408 	if (sm == NULL)
409 		return -EINVAL;
410 
411 	if (inet_pton(AF_INET, sa, addr) != 1)
412 		return -EINVAL;
413 
414 	GET_CB_FIELD(sm, *mask_len, 0, 32, 0);
415 	*addr = ntohl(*addr);
416 	return 0;
417 }
418 
419 static int
420 parse_cb_ipv4vlan_rule(char *str, struct rte_acl_rule *v, int has_userdata)
421 {
422 	int i, rc;
423 	char *s, *sp, *in[CB_FLD_NUM];
424 	static const char *dlm = " \t\n";
425 	int dim = has_userdata ? CB_FLD_NUM : CB_FLD_USERDATA;
426 	s = str;
427 
428 	for (i = 0; i != dim; i++, s = NULL) {
429 		in[i] = strtok_r(s, dlm, &sp);
430 		if (in[i] == NULL)
431 			return -EINVAL;
432 	}
433 
434 	rc = parse_ipv4_net(in[CB_FLD_SRC_ADDR],
435 			&v->field[SRC_FIELD_IPV4].value.u32,
436 			&v->field[SRC_FIELD_IPV4].mask_range.u32);
437 	if (rc != 0) {
438 		acl_log("failed to read source address/mask: %s\n",
439 			in[CB_FLD_SRC_ADDR]);
440 		return rc;
441 	}
442 
443 	rc = parse_ipv4_net(in[CB_FLD_DST_ADDR],
444 			&v->field[DST_FIELD_IPV4].value.u32,
445 			&v->field[DST_FIELD_IPV4].mask_range.u32);
446 	if (rc != 0) {
447 		acl_log("failed to read destination address/mask: %s\n",
448 			in[CB_FLD_DST_ADDR]);
449 		return rc;
450 	}
451 
452 	GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW],
453 		v->field[SRCP_FIELD_IPV4].value.u16,
454 		0, UINT16_MAX, 0);
455 	GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH],
456 		v->field[SRCP_FIELD_IPV4].mask_range.u16,
457 		0, UINT16_MAX, 0);
458 
459 	if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
460 			sizeof(cb_port_delim)) != 0) {
461 		return -EINVAL;
462 	}
463 
464 	GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW],
465 		v->field[DSTP_FIELD_IPV4].value.u16,
466 		0, UINT16_MAX, 0);
467 	GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH],
468 		v->field[DSTP_FIELD_IPV4].mask_range.u16,
469 		0, UINT16_MAX, 0);
470 
471 	if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
472 			sizeof(cb_port_delim)) != 0) {
473 		return -EINVAL;
474 	}
475 
476 	if (v->field[SRCP_FIELD_IPV4].mask_range.u16
477 			< v->field[SRCP_FIELD_IPV4].value.u16
478 			|| v->field[DSTP_FIELD_IPV4].mask_range.u16
479 			< v->field[DSTP_FIELD_IPV4].value.u16) {
480 		return -EINVAL;
481 	}
482 
483 	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].value.u8,
484 		0, UINT8_MAX, '/');
485 	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].mask_range.u8,
486 		0, UINT8_MAX, 0);
487 
488 	if (has_userdata)
489 		GET_CB_FIELD(in[CB_FLD_USERDATA], v->data.userdata, 0,
490 			UINT32_MAX, 0);
491 
492 	return 0;
493 }
494 
495 static int
496 acl_add_rules(const char *rule_path,
497 		struct rte_acl_rule **proute_base,
498 		unsigned int *proute_num,
499 		struct rte_acl_rule **pacl_base,
500 		unsigned int *pacl_num, uint32_t rule_size,
501 		int (*parser)(char *, struct rte_acl_rule*, int))
502 {
503 	uint8_t *acl_rules, *route_rules;
504 	struct rte_acl_rule *next;
505 	unsigned int acl_num = 0, route_num = 0, total_num = 0;
506 	unsigned int acl_cnt = 0, route_cnt = 0;
507 	char buff[LINE_MAX];
508 	FILE *fh = fopen(rule_path, "rb");
509 	unsigned int i = 0;
510 	int val;
511 
512 	if (fh == NULL)
513 		rte_exit(EXIT_FAILURE, "%s: Open %s failed\n", __func__,
514 			rule_path);
515 
516 	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
517 		if (buff[0] == ROUTE_LEAD_CHAR)
518 			route_num++;
519 		else if (buff[0] == ACL_LEAD_CHAR)
520 			acl_num++;
521 	}
522 
523 	if (route_num == 0)
524 		rte_exit(EXIT_FAILURE, "Not find any route entries in %s!\n",
525 				rule_path);
526 
527 	val = fseek(fh, 0, SEEK_SET);
528 	if (val < 0) {
529 		rte_exit(EXIT_FAILURE, "%s: File seek operation failed\n",
530 			__func__);
531 	}
532 
533 	acl_rules = calloc(acl_num, rule_size);
534 
535 	if (acl_rules == NULL)
536 		rte_exit(EXIT_FAILURE, "%s: failed to malloc memory\n",
537 			__func__);
538 
539 	route_rules = calloc(route_num, rule_size);
540 
541 	if (route_rules == NULL)
542 		rte_exit(EXIT_FAILURE, "%s: failed to malloc memory\n",
543 			__func__);
544 
545 	i = 0;
546 	while (fgets(buff, LINE_MAX, fh) != NULL) {
547 		i++;
548 
549 		if (is_bypass_line(buff))
550 			continue;
551 
552 		char s = buff[0];
553 
554 		/* Route entry */
555 		if (s == ROUTE_LEAD_CHAR)
556 			next = (struct rte_acl_rule *)(route_rules +
557 				route_cnt * rule_size);
558 
559 		/* ACL entry */
560 		else if (s == ACL_LEAD_CHAR)
561 			next = (struct rte_acl_rule *)(acl_rules +
562 				acl_cnt * rule_size);
563 
564 		/* Illegal line */
565 		else
566 			rte_exit(EXIT_FAILURE,
567 				"%s Line %u: should start with leading "
568 				"char %c or %c\n",
569 				rule_path, i, ROUTE_LEAD_CHAR, ACL_LEAD_CHAR);
570 
571 		if (parser(buff + 1, next, s == ROUTE_LEAD_CHAR) != 0)
572 			rte_exit(EXIT_FAILURE,
573 				"%s Line %u: parse rules error\n",
574 				rule_path, i);
575 
576 		if (s == ROUTE_LEAD_CHAR) {
577 			/* Check the forwarding port number */
578 			if ((enabled_port_mask & (1 << next->data.userdata)) ==
579 					0)
580 				rte_exit(EXIT_FAILURE,
581 					"%s Line %u: fwd number illegal:%u\n",
582 					rule_path, i, next->data.userdata);
583 			next->data.userdata += FWD_PORT_SHIFT;
584 			route_cnt++;
585 		} else {
586 			next->data.userdata = ACL_DENY_SIGNATURE + acl_cnt;
587 			acl_cnt++;
588 		}
589 
590 		next->data.priority = RTE_ACL_MAX_PRIORITY - total_num;
591 		next->data.category_mask = -1;
592 		total_num++;
593 	}
594 
595 	fclose(fh);
596 
597 	*pacl_base = (struct rte_acl_rule *)acl_rules;
598 	*pacl_num = acl_num;
599 	*proute_base = (struct rte_acl_rule *)route_rules;
600 	*proute_num = route_cnt;
601 
602 	return 0;
603 }
604 
605 enum rte_acl_classify_alg
606 parse_acl_alg(const char *alg)
607 {
608 	uint32_t i;
609 
610 	for (i = 0; i != RTE_DIM(acl_alg); i++) {
611 		if (strcmp(alg, acl_alg[i].name) == 0)
612 			return acl_alg[i].alg;
613 	}
614 
615 	return RTE_ACL_CLASSIFY_DEFAULT;
616 }
617 
618 int
619 usage_acl_alg(char *buf, size_t sz)
620 {
621 	uint32_t i, n, rc, tn;
622 
623 	n = 0;
624 	tn = 0;
625 	for (i = 0; i < RTE_DIM(acl_alg); i++) {
626 		rc = snprintf(buf + n, sz - n,
627 			i == RTE_DIM(acl_alg) - 1 ? "%s" : "%s|",
628 			acl_alg[i].name);
629 		tn += rc;
630 		if (rc < sz - n)
631 			n += rc;
632 	}
633 
634 	return tn;
635 }
636 
637 static const char *
638 str_acl_alg(enum rte_acl_classify_alg alg)
639 {
640 	uint32_t i;
641 
642 	for (i = 0; i != RTE_DIM(acl_alg); i++) {
643 		if (alg == acl_alg[i].alg)
644 			return acl_alg[i].name;
645 	}
646 
647 	return "default";
648 }
649 
650 static void
651 dump_acl_config(void)
652 {
653 	printf("ACL options are:\n");
654 	printf("rule_ipv4: %s\n", parm_config.rule_ipv4_name);
655 	printf("rule_ipv6: %s\n", parm_config.rule_ipv6_name);
656 	printf("alg: %s\n", str_acl_alg(parm_config.alg));
657 }
658 
659 static int
660 check_acl_config(void)
661 {
662 	if (parm_config.rule_ipv4_name == NULL) {
663 		acl_log("ACL IPv4 rule file not specified\n");
664 		return -1;
665 	} else if (parm_config.rule_ipv6_name == NULL) {
666 		acl_log("ACL IPv6 rule file not specified\n");
667 		return -1;
668 	}
669 
670 	return 0;
671 }
672 
673 /* Setup ACL context. 8< */
674 static struct rte_acl_ctx*
675 app_acl_init(struct rte_acl_rule *route_base,
676 		struct rte_acl_rule *acl_base, unsigned int route_num,
677 		unsigned int acl_num, int ipv6, int socketid)
678 {
679 	char name[PATH_MAX];
680 	struct rte_acl_param acl_param;
681 	struct rte_acl_config acl_build_param;
682 	struct rte_acl_ctx *context;
683 	int dim = ipv6 ? RTE_DIM(ipv6_defs) : RTE_DIM(ipv4_defs);
684 
685 	/* Create ACL contexts */
686 	snprintf(name, sizeof(name), "%s%d",
687 			ipv6 ? L3FWD_ACL_IPV6_NAME : L3FWD_ACL_IPV4_NAME,
688 			socketid);
689 
690 	acl_param.name = name;
691 	acl_param.socket_id = socketid;
692 	acl_param.rule_size = RTE_ACL_RULE_SZ(dim);
693 	acl_param.max_rule_num = MAX_ACL_RULE_NUM;
694 
695 	context = rte_acl_create(&acl_param);
696 	if (context == NULL)
697 		rte_exit(EXIT_FAILURE, "Failed to create ACL context\n");
698 
699 	if (parm_config.alg != RTE_ACL_CLASSIFY_DEFAULT &&
700 			rte_acl_set_ctx_classify(context, parm_config.alg) != 0)
701 		rte_exit(EXIT_FAILURE,
702 			"Failed to setup classify method for  ACL context\n");
703 
704 	if (rte_acl_add_rules(context, route_base, route_num) < 0)
705 		rte_exit(EXIT_FAILURE, "add rules failed\n");
706 
707 	if (rte_acl_add_rules(context, acl_base, acl_num) < 0)
708 		rte_exit(EXIT_FAILURE, "add rules failed\n");
709 
710 	/* Perform builds */
711 	memset(&acl_build_param, 0, sizeof(acl_build_param));
712 
713 	acl_build_param.num_categories = DEFAULT_MAX_CATEGORIES;
714 	acl_build_param.num_fields = dim;
715 	memcpy(&acl_build_param.defs, ipv6 ? ipv6_defs : ipv4_defs,
716 		ipv6 ? sizeof(ipv6_defs) : sizeof(ipv4_defs));
717 
718 	if (rte_acl_build(context, &acl_build_param) != 0)
719 		rte_exit(EXIT_FAILURE, "Failed to build ACL trie\n");
720 
721 	rte_acl_dump(context);
722 
723 	return context;
724 }
725 /* >8 End of ACL context setup. */
726 
727 void
728 acl_free_routes(void)
729 {
730 	free(route_base_ipv4);
731 	free(route_base_ipv6);
732 	route_base_ipv4 = NULL;
733 	route_base_ipv6 = NULL;
734 	route_num_ipv4 = 0;
735 	route_num_ipv6 = 0;
736 	free(acl_base_ipv4);
737 	free(acl_base_ipv6);
738 	acl_base_ipv4 = NULL;
739 	acl_base_ipv6 = NULL;
740 	acl_num_ipv4 = 0;
741 	acl_num_ipv6 = 0;
742 }
743 
744 /* Load rules from the input file */
745 void
746 read_config_files_acl(void)
747 {
748 	/* ipv4 check */
749 	if (parm_config.rule_ipv4_name != NULL) {
750 		if (acl_add_rules(parm_config.rule_ipv4_name, &route_base_ipv4,
751 				&route_num_ipv4, &acl_base_ipv4, &acl_num_ipv4,
752 				sizeof(struct acl4_rule), &parse_cb_ipv4vlan_rule) < 0) {
753 			acl_free_routes();
754 			rte_exit(EXIT_FAILURE, "Failed to add IPv4 rules\n");
755 		}
756 	} else {
757 		RTE_LOG(ERR, L3FWD, "IPv4 rule file not specified\n");
758 		rte_exit(EXIT_FAILURE, "Failed to get valid route options\n");
759 	}
760 
761 	/* ipv6 check */
762 	if (parm_config.rule_ipv6_name != NULL) {
763 		if (acl_add_rules(parm_config.rule_ipv6_name, &route_base_ipv6,
764 				&route_num_ipv6,
765 				&acl_base_ipv6, &acl_num_ipv6,
766 				sizeof(struct acl6_rule), &parse_cb_ipv6_rule) < 0) {
767 			acl_free_routes();
768 			rte_exit(EXIT_FAILURE, "Failed to add IPv6 rules\n");
769 		}
770 	} else {
771 		RTE_LOG(ERR, L3FWD, "IPv6 rule file not specified\n");
772 		rte_exit(EXIT_FAILURE, "Failed to get valid route options\n");
773 	}
774 }
775 
776 void
777 print_one_ipv4_rule(struct acl4_rule *rule, int extra)
778 {
779 	char abuf[INET6_ADDRSTRLEN];
780 	uint32_t ipv4_addr;
781 	ipv4_addr = ntohl(rule->field[SRC_FIELD_IPV4].value.u32);
782 	printf("%s/%u ", inet_ntop(AF_INET,
783 			&(ipv4_addr), abuf,
784 			sizeof(abuf)), rule->field[SRC_FIELD_IPV4].mask_range.u32);
785 	ipv4_addr = ntohl(rule->field[DST_FIELD_IPV4].value.u32);
786 	printf("%s/%u ", inet_ntop(AF_INET,
787 			&(ipv4_addr), abuf,
788 			sizeof(abuf)), rule->field[DST_FIELD_IPV4].mask_range.u32);
789 	printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
790 		rule->field[SRCP_FIELD_IPV4].value.u16,
791 		rule->field[SRCP_FIELD_IPV4].mask_range.u16,
792 		rule->field[DSTP_FIELD_IPV4].value.u16,
793 		rule->field[DSTP_FIELD_IPV4].mask_range.u16,
794 		rule->field[PROTO_FIELD_IPV4].value.u8,
795 		rule->field[PROTO_FIELD_IPV4].mask_range.u8);
796 	if (extra)
797 		printf("0x%x-0x%x-0x%x ",
798 			rule->data.category_mask,
799 			rule->data.priority,
800 			rule->data.userdata);
801 }
802 
803 void
804 print_one_ipv6_rule(struct acl6_rule *rule, int extra)
805 {
806 	unsigned char a, b, c, d;
807 
808 	uint32_t_to_char(rule->field[SRC1_FIELD_IPV6].value.u32,
809 		&a, &b, &c, &d);
810 	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
811 	uint32_t_to_char(rule->field[SRC2_FIELD_IPV6].value.u32,
812 		&a, &b, &c, &d);
813 	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
814 	uint32_t_to_char(rule->field[SRC3_FIELD_IPV6].value.u32,
815 		&a, &b, &c, &d);
816 	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
817 	uint32_t_to_char(rule->field[SRC4_FIELD_IPV6].value.u32,
818 		&a, &b, &c, &d);
819 	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
820 			rule->field[SRC1_FIELD_IPV6].mask_range.u32
821 			+ rule->field[SRC2_FIELD_IPV6].mask_range.u32
822 			+ rule->field[SRC3_FIELD_IPV6].mask_range.u32
823 			+ rule->field[SRC4_FIELD_IPV6].mask_range.u32);
824 
825 	uint32_t_to_char(rule->field[DST1_FIELD_IPV6].value.u32,
826 		&a, &b, &c, &d);
827 	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
828 	uint32_t_to_char(rule->field[DST2_FIELD_IPV6].value.u32,
829 		&a, &b, &c, &d);
830 	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
831 	uint32_t_to_char(rule->field[DST3_FIELD_IPV6].value.u32,
832 		&a, &b, &c, &d);
833 	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
834 	uint32_t_to_char(rule->field[DST4_FIELD_IPV6].value.u32,
835 		&a, &b, &c, &d);
836 	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
837 			rule->field[DST1_FIELD_IPV6].mask_range.u32
838 			+ rule->field[DST2_FIELD_IPV6].mask_range.u32
839 			+ rule->field[DST3_FIELD_IPV6].mask_range.u32
840 			+ rule->field[DST4_FIELD_IPV6].mask_range.u32);
841 
842 	printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
843 		rule->field[SRCP_FIELD_IPV6].value.u16,
844 		rule->field[SRCP_FIELD_IPV6].mask_range.u16,
845 		rule->field[DSTP_FIELD_IPV6].value.u16,
846 		rule->field[DSTP_FIELD_IPV6].mask_range.u16,
847 		rule->field[PROTO_FIELD_IPV6].value.u8,
848 		rule->field[PROTO_FIELD_IPV6].mask_range.u8);
849 	if (extra)
850 		printf("0x%x-0x%x-0x%x ",
851 			rule->data.category_mask,
852 			rule->data.priority,
853 			rule->data.userdata);
854 }
855 
856 #ifdef L3FWDACL_DEBUG
857 static inline void
858 dump_acl4_rule(struct rte_mbuf *m, uint32_t sig)
859 {
860 	char abuf[INET6_ADDRSTRLEN];
861 	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
862 	struct rte_ipv4_hdr *ipv4_hdr =
863 		rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *,
864 					sizeof(struct rte_ether_hdr));
865 
866 	printf("Packet Src:%s ", inet_ntop(AF_INET, ipv4_hdr->src_addr,
867 		abuf, sizeof(abuf)));
868 	printf("Dst:%s ", inet_ntop(AF_INET, ipv4_hdr->dst_addr,
869 		abuf, sizeof(abuf)));
870 
871 	printf("Src port:%hu,Dst port:%hu ",
872 			rte_bswap16(*(uint16_t *)(ipv4_hdr + 1)),
873 			rte_bswap16(*((uint16_t *)(ipv4_hdr + 1) + 1)));
874 	printf("hit ACL %d - ", offset);
875 
876 	print_one_ipv4_rule(acl_config.rule_ipv4 + offset, 1);
877 
878 	printf("\n\n");
879 }
880 
881 static inline void
882 dump_acl6_rule(struct rte_mbuf *m, uint32_t sig)
883 {
884 	char abuf[INET6_ADDRSTRLEN];
885 	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
886 	struct rte_ipv6_hdr *ipv6_hdr =
887 		rte_pktmbuf_mtod_offset(m, struct rte_ipv6_hdr *,
888 					sizeof(struct rte_ether_hdr));
889 
890 	printf("Packet Src");
891 	printf("%s", inet_ntop(AF_INET6, ipv6_hdr->src_addr,
892 		abuf, sizeof(abuf)));
893 	printf("\nDst");
894 	printf("%s", inet_ntop(AF_INET6, ipv6_hdr->dst_addr,
895 		abuf, sizeof(abuf)));
896 
897 	printf("\nSrc port:%hu,Dst port:%hu ",
898 			rte_bswap16(*(uint16_t *)(ipv6_hdr + 1)),
899 			rte_bswap16(*((uint16_t *)(ipv6_hdr + 1) + 1)));
900 	printf("hit ACL %d - ", offset);
901 
902 	print_one_ipv6_rule(acl_config.rule_ipv6 + offset, 1);
903 
904 	printf("\n\n");
905 }
906 #endif /* L3FWDACL_DEBUG */
907 
908 static inline void
909 dump_ipv4_rules(struct acl4_rule *rule, int num, int extra)
910 {
911 	int i;
912 
913 	for (i = 0; i < num; i++, rule++) {
914 		printf("\t%d:", i + 1);
915 		print_one_ipv4_rule(rule, extra);
916 		printf("\n");
917 	}
918 }
919 
920 static inline void
921 dump_ipv6_rules(struct acl6_rule *rule, int num, int extra)
922 {
923 	int i;
924 
925 	for (i = 0; i < num; i++, rule++) {
926 		printf("\t%d:", i + 1);
927 		print_one_ipv6_rule(rule, extra);
928 		printf("\n");
929 	}
930 }
931 
932 /* Function to setup acl. */
933 void
934 setup_acl(const int socket_id)
935 {
936 	if (check_acl_config() != 0)
937 		rte_exit(EXIT_FAILURE, "Failed to get valid ACL options\n");
938 
939 	dump_acl_config();
940 
941 	acl_log("IPv4 Route entries %u:\n", route_num_ipv4);
942 	dump_ipv4_rules((struct acl4_rule *)route_base_ipv4, route_num_ipv4, 1);
943 
944 	acl_log("IPv4 ACL entries %u:\n", acl_num_ipv4);
945 	dump_ipv4_rules((struct acl4_rule *)acl_base_ipv4, acl_num_ipv4, 1);
946 
947 	acl_log("IPv6 Route entries %u:\n", route_num_ipv6);
948 	dump_ipv6_rules((struct acl6_rule *)route_base_ipv6, route_num_ipv6, 1);
949 
950 	acl_log("IPv6 ACL entries %u:\n", acl_num_ipv6);
951 	dump_ipv6_rules((struct acl6_rule *)acl_base_ipv6, acl_num_ipv6, 1);
952 
953 	/* Check sockets a context should be created on */
954 	if (socket_id >= NB_SOCKETS) {
955 		acl_log("Socket %d is out "
956 			"of range %d\n",
957 			socket_id, NB_SOCKETS);
958 		acl_free_routes();
959 		return;
960 	}
961 
962 	rte_acl_free(acl_config.acx_ipv4[socket_id]);
963 	rte_acl_free(acl_config.acx_ipv6[socket_id]);
964 
965 	acl_config.acx_ipv4[socket_id] = app_acl_init(route_base_ipv4,
966 		acl_base_ipv4, route_num_ipv4, acl_num_ipv4,
967 		0, socket_id);
968 
969 	acl_config.acx_ipv6[socket_id] = app_acl_init(route_base_ipv6,
970 		acl_base_ipv6, route_num_ipv6, acl_num_ipv6,
971 		1, socket_id);
972 
973 #ifdef L3FWDACL_DEBUG
974 	acl_config.rule_ipv4 = (struct acl4_rule *)acl_base_ipv4;
975 	acl_config.rule_ipv6 = (struct acl6_rule *)acl_base_ipv6;
976 #endif
977 
978 }
979 
980 static inline void
981 dump_denied_pkt(const struct rte_mbuf *pkt, uint32_t res)
982 {
983 #ifdef L3FWDACL_DEBUG
984 	if ((res & ACL_DENY_SIGNATURE) != 0) {
985 		if (RTE_ETH_IS_IPV4_HDR(pkt->packet_type))
986 			dump_acl4_rule(pkt, res);
987 		else if (RTE_ETH_IS_IPV6_HDR(pkt[i]->packet_type))
988 			dump_acl6_rule(pkt[i], res[i]);
989 	}
990 #else
991 	RTE_SET_USED(pkt);
992 	RTE_SET_USED(res);
993 #endif
994 }
995 
996 /*
997  * run packets through ACL classify.
998  * returns number of packets to be dropped (hops[i] == BAD_PORT)
999  */
1000 static inline uint32_t
1001 acl_process_pkts(struct rte_mbuf *pkts[MAX_PKT_BURST],
1002 	uint16_t hops[MAX_PKT_BURST], uint32_t num, int32_t socketid)
1003 {
1004 	uint32_t i, k, n4, n6, res;
1005 	struct acl_search_t acl_search;
1006 
1007 	/* split packets burst depending on packet type (IPv4/IPv6) */
1008 	l3fwd_acl_prepare_acl_parameter(pkts, &acl_search, num);
1009 
1010 	if (acl_search.num_ipv4)
1011 		rte_acl_classify(acl_config.acx_ipv4[socketid],
1012 				acl_search.data_ipv4,
1013 				acl_search.res_ipv4,
1014 				acl_search.num_ipv4,
1015 				DEFAULT_MAX_CATEGORIES);
1016 
1017 	if (acl_search.num_ipv6)
1018 		rte_acl_classify(acl_config.acx_ipv6[socketid],
1019 				acl_search.data_ipv6,
1020 				acl_search.res_ipv6,
1021 				acl_search.num_ipv6,
1022 				DEFAULT_MAX_CATEGORIES);
1023 
1024 	/* combine lookup results back, into one array of next hops */
1025 	n4 = 0;
1026 	n6 = 0;
1027 	k = 0;
1028 	for (i = 0; i != num; i++) {
1029 		switch (acl_search.types[i]) {
1030 		case TYPE_IPV4:
1031 			res = acl_search.res_ipv4[n4++];
1032 			break;
1033 		case TYPE_IPV6:
1034 			res = acl_search.res_ipv6[n6++];
1035 			break;
1036 		default:
1037 			res = 0;
1038 		}
1039 		if (likely((res & ACL_DENY_SIGNATURE) == 0 && res != 0))
1040 			hops[i] = res - FWD_PORT_SHIFT;
1041 		else {
1042 			/* bad or denied by ACL rule packets */
1043 			hops[i] = BAD_PORT;
1044 			dump_denied_pkt(pkts[i], res);
1045 			k++;
1046 		}
1047 	}
1048 
1049 	return k;
1050 }
1051 
1052 /*
1053  * send_packets_multi() can't deal properly with hops[i] == BAD_PORT
1054  * (it assumes input hops[] contain only valid port numbers),
1055  * so it is ok to use it only when there are no denied packets.
1056  */
1057 static inline void
1058 acl_send_packets(struct lcore_conf *qconf, struct rte_mbuf *pkts[],
1059 	uint16_t hops[], uint32_t num, uint32_t nb_drop)
1060 {
1061 #if defined ACL_SEND_MULTI
1062 	if (nb_drop == 0)
1063 		send_packets_multi(qconf, pkts, hops, num);
1064 	else
1065 #else
1066 		RTE_SET_USED(nb_drop);
1067 #endif
1068 		send_packets_single(qconf, pkts, hops, num);
1069 }
1070 
1071 /* main processing loop */
1072 int
1073 acl_main_loop(__rte_unused void *dummy)
1074 {
1075 	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
1076 	uint16_t hops[SENDM_PORT_OVERHEAD(MAX_PKT_BURST)];
1077 	unsigned int lcore_id;
1078 	uint64_t prev_tsc, diff_tsc, cur_tsc;
1079 	int i, nb_drop, nb_rx;
1080 	uint16_t portid;
1081 	uint16_t queueid;
1082 	struct lcore_conf *qconf;
1083 	int socketid;
1084 	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1)
1085 			/ US_PER_S * BURST_TX_DRAIN_US;
1086 
1087 	prev_tsc = 0;
1088 	lcore_id = rte_lcore_id();
1089 	qconf = &lcore_conf[lcore_id];
1090 	socketid = rte_lcore_to_socket_id(lcore_id);
1091 
1092 	if (qconf->n_rx_queue == 0) {
1093 		RTE_LOG(INFO, L3FWD, "lcore %u has nothing to do\n", lcore_id);
1094 		return 0;
1095 	}
1096 
1097 	RTE_LOG(INFO, L3FWD, "entering main loop on lcore %u\n", lcore_id);
1098 
1099 	for (i = 0; i < qconf->n_rx_queue; i++) {
1100 
1101 		portid = qconf->rx_queue_list[i].port_id;
1102 		queueid = qconf->rx_queue_list[i].queue_id;
1103 		RTE_LOG(INFO, L3FWD,
1104 			" -- lcoreid=%u portid=%u rxqueueid=%" PRIu16 "\n",
1105 			lcore_id, portid, queueid);
1106 	}
1107 
1108 	while (!force_quit) {
1109 
1110 		cur_tsc = rte_rdtsc();
1111 
1112 		/*
1113 		 * TX burst queue drain
1114 		 */
1115 		diff_tsc = cur_tsc - prev_tsc;
1116 		if (unlikely(diff_tsc > drain_tsc)) {
1117 
1118 			for (i = 0; i < qconf->n_tx_port; ++i) {
1119 				portid = qconf->tx_port_id[i];
1120 				if (qconf->tx_mbufs[portid].len == 0)
1121 					continue;
1122 				send_burst(qconf,
1123 					qconf->tx_mbufs[portid].len,
1124 					portid);
1125 				qconf->tx_mbufs[portid].len = 0;
1126 			}
1127 
1128 			prev_tsc = cur_tsc;
1129 		}
1130 
1131 		/*
1132 		 * Read packet from RX queues and process them
1133 		 */
1134 		for (i = 0; i < qconf->n_rx_queue; ++i) {
1135 
1136 			portid = qconf->rx_queue_list[i].port_id;
1137 			queueid = qconf->rx_queue_list[i].queue_id;
1138 			nb_rx = rte_eth_rx_burst(portid, queueid,
1139 				pkts_burst, nb_pkt_per_burst);
1140 
1141 			if (nb_rx > 0) {
1142 				nb_drop = acl_process_pkts(pkts_burst, hops,
1143 					nb_rx, socketid);
1144 				acl_send_packets(qconf, pkts_burst, hops,
1145 					nb_rx, nb_drop);
1146 			}
1147 		}
1148 	}
1149 	return 0;
1150 }
1151 
1152 /* Not used by L3fwd ACL. */
1153 void *
1154 acl_get_ipv4_l3fwd_lookup_struct(__rte_unused const int socketid)
1155 {
1156 	return NULL;
1157 }
1158 
1159 void *
1160 acl_get_ipv6_l3fwd_lookup_struct(__rte_unused const int socketid)
1161 {
1162 	return NULL;
1163 }
1164