DPDK patches and discussions
 help / color / mirror / Atom feed
From: "Ananyev, Konstantin" <konstantin.ananyev@intel.com>
To: "Morrissey, Sean" <sean.morrissey@intel.com>
Cc: "dev@dpdk.org" <dev@dpdk.org>
Subject: RE: [PATCH v2] examples/l3fwd: merge l3fwd-acl into l3fwd
Date: Sat, 2 Apr 2022 13:53:34 +0000	[thread overview]
Message-ID: <DM6PR11MB44919647823D7FB9C06F9FEC9AE39@DM6PR11MB4491.namprd11.prod.outlook.com> (raw)
In-Reply-To: <20220330140407.1616634-1-sean.morrissey@intel.com>


Hi Sean,

My comments inline.
Thanks 
Konstantin

> l3fwd-acl contains duplicate functions to l3fwd.
> For this reason we merge l3fwd-acl code into l3fwd
> with '--lookup acl' cmdline option to run ACL.
> 
> Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
> ---
> V2:
> * add doc changes
> * minor code cleanup
> ---
>  doc/guides/rel_notes/release_22_07.rst        |    4 +
>  doc/guides/sample_app_ug/index.rst            |    1 -
>  doc/guides/sample_app_ug/l3_forward.rst       |   63 +-
>  .../sample_app_ug/l3_forward_access_ctrl.rst  |  340 ---
>  examples/l3fwd-acl/Makefile                   |   51 -
>  examples/l3fwd-acl/main.c                     | 2272 -----------------
>  examples/l3fwd-acl/meson.build                |   13 -
>  examples/l3fwd/Makefile                       |    2 +-
>  examples/l3fwd/l3fwd.h                        |   60 +-
>  examples/l3fwd/l3fwd_acl.c                    |  960 +++++++
>  examples/l3fwd/l3fwd_acl.h                    |  118 +
>  examples/l3fwd/l3fwd_acl_scalar.h             |  163 ++
>  examples/l3fwd/l3fwd_route.h                  |   16 +
>  examples/l3fwd/main.c                         |   97 +-
>  examples/l3fwd/meson.build                    |    3 +-
>  examples/meson.build                          |    1 -
>  16 files changed, 1466 insertions(+), 2698 deletions(-)
>  delete mode 100644 doc/guides/sample_app_ug/l3_forward_access_ctrl.rst
>  delete mode 100644 examples/l3fwd-acl/Makefile
>  delete mode 100644 examples/l3fwd-acl/main.c
>  delete mode 100644 examples/l3fwd-acl/meson.build
>  create mode 100644 examples/l3fwd/l3fwd_acl.c
>  create mode 100644 examples/l3fwd/l3fwd_acl.h
>  create mode 100644 examples/l3fwd/l3fwd_acl_scalar.h
> 

...

> diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
> index ad39496e64..6503cc2f4e 100644
> --- a/examples/l3fwd/l3fwd.h
> +++ b/examples/l3fwd/l3fwd.h
> @@ -7,6 +7,7 @@
> 
>  #include <rte_ethdev.h>
>  #include <rte_vect.h>
> +#include <rte_acl.h>
> 
>  #define DO_RFC_1812_CHECKS
> 
> @@ -61,6 +62,41 @@
>  struct parm_cfg {
>  	const char *rule_ipv4_name;
>  	const char *rule_ipv6_name;
> +	enum rte_acl_classify_alg alg;
> +};
> +
> +static const struct {
> +	const char *name;
> +	enum rte_acl_classify_alg alg;
> +} acl_alg[] = {
> +	{
> +		.name = "scalar",
> +		.alg = RTE_ACL_CLASSIFY_SCALAR,
> +	},
> +	{
> +		.name = "sse",
> +		.alg = RTE_ACL_CLASSIFY_SSE,
> +	},
> +	{
> +		.name = "avx2",
> +		.alg = RTE_ACL_CLASSIFY_AVX2,
> +	},
> +	{
> +		.name = "neon",
> +		.alg = RTE_ACL_CLASSIFY_NEON,
> +	},
> +	{
> +		.name = "altivec",
> +		.alg = RTE_ACL_CLASSIFY_ALTIVEC,
> +	},
> +	{
> +		.name = "avx512x16",
> +		.alg = RTE_ACL_CLASSIFY_AVX512X16,
> +	},
> +	{
> +		.name = "avx512x32",
> +		.alg = RTE_ACL_CLASSIFY_AVX512X32,
> +	},
>  };

I probably wasn't clear enough in my previous comments.
I think there is no need to move definition of acl_alg[] into .h and making it static.
Instead I .h we can just do:
extern struct acl_algorithms acl_alg[];
and keep actual definition in .c.

> 
>  struct mbuf_table {
> @@ -80,6 +116,7 @@ struct lcore_conf {
>  	uint16_t tx_port_id[RTE_MAX_ETHPORTS];
>  	uint16_t tx_queue_id[RTE_MAX_ETHPORTS];
>  	struct mbuf_table tx_mbufs[RTE_MAX_ETHPORTS];
> +	struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS];
>  	void *ipv4_lookup_struct;
>  	void *ipv6_lookup_struct;
>  } __rte_cache_aligned;
> @@ -193,7 +230,10 @@ is_valid_ipv4_pkt(struct rte_ipv4_hdr *pkt, uint32_t link_len)
>  int
>  init_mem(uint16_t portid, unsigned int nb_mbuf);
> 
> -/* Function pointers for LPM, EM or FIB functionality. */
> +/* Function pointers for ACL, LPM, EM or FIB functionality. */
> +void
> +setup_acl(const int socketid);
> +
>  void
>  setup_lpm(const int socketid);
> 
> @@ -203,12 +243,19 @@ setup_hash(const int socketid);
>  void
>  setup_fib(const int socketid);
> 
> +int
> +acl_check_ptype(int portid);
> +
>  int
>  em_check_ptype(int portid);
> 
>  int
>  lpm_check_ptype(int portid);
> 
> +uint16_t
> +acl_cb_parse_ptype(uint16_t port, uint16_t queue, struct rte_mbuf *pkts[],
> +		  uint16_t nb_pkts, uint16_t max_pkts, void *user_param);
> +
>  uint16_t
>  em_cb_parse_ptype(uint16_t port, uint16_t queue, struct rte_mbuf *pkts[],
>  		  uint16_t nb_pkts, uint16_t max_pkts, void *user_param);
> @@ -217,6 +264,9 @@ uint16_t
>  lpm_cb_parse_ptype(uint16_t port, uint16_t queue, struct rte_mbuf *pkts[],
>  		   uint16_t nb_pkts, uint16_t max_pkts, void *user_param);
> 
> +int
> +acl_main_loop(__rte_unused void *dummy);
> +
>  int
>  em_main_loop(__rte_unused void *dummy);
> 
> @@ -278,7 +328,13 @@ int
>  fib_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy);
> 
> 
> -/* Return ipv4/ipv6 fwd lookup struct for LPM, EM or FIB. */
> +/* Return ipv4/ipv6 fwd lookup struct for ACL, LPM, EM or FIB. */
> +void *
> +acl_get_ipv4_l3fwd_lookup_struct(const int socketid);
> +
> +void *
> +acl_get_ipv6_l3fwd_lookup_struct(const int socketid);
> +
>  void *
>  em_get_ipv4_l3fwd_lookup_struct(const int socketid);
> 
> diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
> new file mode 100644
> index 0000000000..ebbee37aaa
> --- /dev/null
> +++ b/examples/l3fwd/l3fwd_acl.c
> @@ -0,0 +1,960 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2022 Intel Corporation
> + */
> +
> +#include "l3fwd.h"
> +#include "l3fwd_route.h"
> +
> +/*
> + * Rule and trace formats definitions.
> + */
> +
> +enum {
> +	PROTO_FIELD_IPV4,
> +	SRC_FIELD_IPV4,
> +	DST_FIELD_IPV4,
> +	SRCP_FIELD_IPV4,
> +	DSTP_FIELD_IPV4,
> +	NUM_FIELDS_IPV4
> +};
> +
> +/*
> + * That effectively defines order of IPV4VLAN classifications:
> + *  - PROTO
> + *  - VLAN (TAG and DOMAIN)
> + *  - SRC IP ADDRESS
> + *  - DST IP ADDRESS
> + *  - PORTS (SRC and DST)
> + */
> +enum {
> +	RTE_ACL_IPV4VLAN_PROTO,
> +	RTE_ACL_IPV4VLAN_VLAN,
> +	RTE_ACL_IPV4VLAN_SRC,
> +	RTE_ACL_IPV4VLAN_DST,
> +	RTE_ACL_IPV4VLAN_PORTS,
> +	RTE_ACL_IPV4VLAN_NUM
> +};
> +
> +struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_BITMASK,
> +		.size = sizeof(uint8_t),
> +		.field_index = PROTO_FIELD_IPV4,
> +		.input_index = RTE_ACL_IPV4VLAN_PROTO,
> +		.offset = 0,
> +	},
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_MASK,
> +		.size = sizeof(uint32_t),
> +		.field_index = SRC_FIELD_IPV4,
> +		.input_index = RTE_ACL_IPV4VLAN_SRC,
> +		.offset = offsetof(struct rte_ipv4_hdr, src_addr) -
> +			offsetof(struct rte_ipv4_hdr, next_proto_id),
> +	},
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_MASK,
> +		.size = sizeof(uint32_t),
> +		.field_index = DST_FIELD_IPV4,
> +		.input_index = RTE_ACL_IPV4VLAN_DST,
> +		.offset = offsetof(struct rte_ipv4_hdr, dst_addr) -
> +			offsetof(struct rte_ipv4_hdr, next_proto_id),
> +	},
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_RANGE,
> +		.size = sizeof(uint16_t),
> +		.field_index = SRCP_FIELD_IPV4,
> +		.input_index = RTE_ACL_IPV4VLAN_PORTS,
> +		.offset = sizeof(struct rte_ipv4_hdr) -
> +			offsetof(struct rte_ipv4_hdr, next_proto_id),
> +	},
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_RANGE,
> +		.size = sizeof(uint16_t),
> +		.field_index = DSTP_FIELD_IPV4,
> +		.input_index = RTE_ACL_IPV4VLAN_PORTS,
> +		.offset = sizeof(struct rte_ipv4_hdr) -
> +			offsetof(struct rte_ipv4_hdr, next_proto_id) +
> +			sizeof(uint16_t),
> +	},
> +};
> +
> +enum {
> +	PROTO_FIELD_IPV6,
> +	SRC1_FIELD_IPV6,
> +	SRC2_FIELD_IPV6,
> +	SRC3_FIELD_IPV6,
> +	SRC4_FIELD_IPV6,
> +	DST1_FIELD_IPV6,
> +	DST2_FIELD_IPV6,
> +	DST3_FIELD_IPV6,
> +	DST4_FIELD_IPV6,
> +	SRCP_FIELD_IPV6,
> +	DSTP_FIELD_IPV6,
> +	NUM_FIELDS_IPV6
> +};
> +
> +struct rte_acl_field_def ipv6_defs[NUM_FIELDS_IPV6] = {
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_BITMASK,
> +		.size = sizeof(uint8_t),
> +		.field_index = PROTO_FIELD_IPV6,
> +		.input_index = PROTO_FIELD_IPV6,
> +		.offset = 0,
> +	},
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_MASK,
> +		.size = sizeof(uint32_t),
> +		.field_index = SRC1_FIELD_IPV6,
> +		.input_index = SRC1_FIELD_IPV6,
> +		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
> +			offsetof(struct rte_ipv6_hdr, proto),
> +	},
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_MASK,
> +		.size = sizeof(uint32_t),
> +		.field_index = SRC2_FIELD_IPV6,
> +		.input_index = SRC2_FIELD_IPV6,
> +		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
> +			offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint32_t),
> +	},
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_MASK,
> +		.size = sizeof(uint32_t),
> +		.field_index = SRC3_FIELD_IPV6,
> +		.input_index = SRC3_FIELD_IPV6,
> +		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
> +			offsetof(struct rte_ipv6_hdr, proto) +
> +			2 * sizeof(uint32_t),
> +	},
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_MASK,
> +		.size = sizeof(uint32_t),
> +		.field_index = SRC4_FIELD_IPV6,
> +		.input_index = SRC4_FIELD_IPV6,
> +		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
> +			offsetof(struct rte_ipv6_hdr, proto) +
> +			3 * sizeof(uint32_t),
> +	},
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_MASK,
> +		.size = sizeof(uint32_t),
> +		.field_index = DST1_FIELD_IPV6,
> +		.input_index = DST1_FIELD_IPV6,
> +		.offset = offsetof(struct rte_ipv6_hdr, dst_addr)
> +				- offsetof(struct rte_ipv6_hdr, proto),
> +	},
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_MASK,
> +		.size = sizeof(uint32_t),
> +		.field_index = DST2_FIELD_IPV6,
> +		.input_index = DST2_FIELD_IPV6,
> +		.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
> +			offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint32_t),
> +	},
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_MASK,
> +		.size = sizeof(uint32_t),
> +		.field_index = DST3_FIELD_IPV6,
> +		.input_index = DST3_FIELD_IPV6,
> +		.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
> +			offsetof(struct rte_ipv6_hdr, proto) +
> +			2 * sizeof(uint32_t),
> +	},
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_MASK,
> +		.size = sizeof(uint32_t),
> +		.field_index = DST4_FIELD_IPV6,
> +		.input_index = DST4_FIELD_IPV6,
> +		.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
> +			offsetof(struct rte_ipv6_hdr, proto) +
> +			3 * sizeof(uint32_t),
> +	},
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_RANGE,
> +		.size = sizeof(uint16_t),
> +		.field_index = SRCP_FIELD_IPV6,
> +		.input_index = SRCP_FIELD_IPV6,
> +		.offset = sizeof(struct rte_ipv6_hdr) -
> +			offsetof(struct rte_ipv6_hdr, proto),
> +	},
> +	{
> +		.type = RTE_ACL_FIELD_TYPE_RANGE,
> +		.size = sizeof(uint16_t),
> +		.field_index = DSTP_FIELD_IPV6,
> +		.input_index = SRCP_FIELD_IPV6,
> +		.offset = sizeof(struct rte_ipv6_hdr) -
> +			offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint16_t),
> +	},
> +};
> +
> +enum {
> +	CB_FLD_SRC_ADDR,
> +	CB_FLD_DST_ADDR,
> +	CB_FLD_SRC_PORT_LOW,
> +	CB_FLD_SRC_PORT_DLM,
> +	CB_FLD_SRC_PORT_HIGH,
> +	CB_FLD_DST_PORT_LOW,
> +	CB_FLD_DST_PORT_DLM,
> +	CB_FLD_DST_PORT_HIGH,
> +	CB_FLD_PROTO,
> +	CB_FLD_USERDATA,
> +	CB_FLD_NUM,
> +};
> +
> +RTE_ACL_RULE_DEF(acl4_rule, RTE_DIM(ipv4_defs));
> +RTE_ACL_RULE_DEF(acl6_rule, RTE_DIM(ipv6_defs));
> +
> +struct acl_search_t {
> +	const uint8_t *data_ipv4[MAX_PKT_BURST];
> +	struct rte_mbuf *m_ipv4[MAX_PKT_BURST];
> +	uint32_t res_ipv4[MAX_PKT_BURST];
> +	int num_ipv4;
> +
> +	const uint8_t *data_ipv6[MAX_PKT_BURST];
> +	struct rte_mbuf *m_ipv6[MAX_PKT_BURST];
> +	uint32_t res_ipv6[MAX_PKT_BURST];
> +	int num_ipv6;
> +};
> +
> +static struct {
> +	struct rte_acl_ctx *acx_ipv4[NB_SOCKETS];
> +	struct rte_acl_ctx *acx_ipv6[NB_SOCKETS];
> +#ifdef L3FWDACL_DEBUG
> +	struct acl4_rule *rule_ipv4;
> +	struct acl6_rule *rule_ipv6;
> +#endif
> +} acl_config;
> +
> +static const char cb_port_delim[] = ":";
> +
> +static struct rte_acl_rule *acl_base_ipv4, *route_base_ipv4,
> +		*acl_base_ipv6, *route_base_ipv6;
> +static unsigned int acl_num_ipv4, route_num_ipv4,
> +		acl_num_ipv6, route_num_ipv6;
> +
> +#include "l3fwd_acl.h"
> +
> +#include "l3fwd_acl_scalar.h"
> +
> +/*
> + * Parse IPV6 address, expects the following format:
> + * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X is a hexadecimal digit).
> + */
> +static int
> +parse_ipv6_addr(char *in, uint32_t v[IPV6_ADDR_U32], uint32_t *mask)
> +{
> +	char *sa, *sm, *sv;
> +	const char *dlm =  "/";
> +
> +	sv = NULL;
> +	sa = strtok_r(in, dlm, &sv);
> +	if (sa == NULL)
> +		return -EINVAL;
> +	sm = strtok_r(NULL, dlm, &sv);
> +	if (sm == NULL)
> +		return -EINVAL;
> +
> +	if (inet_pton(AF_INET6, sa, v) != 1)
> +		return -EINVAL;
> +
> +	GET_CB_FIELD(sm, *mask, 0, 128, 0);
> +	return 0;
> +}
> +
> +static int
> +parse_ipv6_net(char *in, struct rte_acl_field field[4])
> +{
> +	int32_t rc;
> +	uint32_t i, v[4];
> +	uint32_t m;
> +	const uint32_t nbu32 = sizeof(uint32_t) * CHAR_BIT;
> +
> +	/* get address. */
> +	rc = parse_ipv6_addr(in, v, &m);
> +	if (rc != 0)
> +		return rc;
> +
> +	/* put all together. */
> +	for (i = 0; i != RTE_DIM(v); i++) {
> +		if (m >= (i + 1) * nbu32)
> +			field[i].mask_range.u32 = nbu32;
> +		else
> +			field[i].mask_range.u32 = m > (i * nbu32) ?
> +				m - (i * 32) : 0;
> +
> +		field[i].value.u32 = v[i];
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +parse_cb_ipv6_rule(char *str, struct rte_acl_rule *v, int has_userdata)
> +{
> +	int i, rc;
> +	char *s, *sp, *in[CB_FLD_NUM];
> +	static const char *dlm = " \t\n";
> +	int dim = has_userdata ? CB_FLD_NUM : CB_FLD_USERDATA;
> +	s = str;
> +
> +	for (i = 0; i != dim; i++, s = NULL) {
> +		in[i] = strtok_r(s, dlm, &sp);
> +		if (in[i] == NULL)
> +			return -EINVAL;
> +	}
> +
> +	rc = parse_ipv6_net(in[CB_FLD_SRC_ADDR], v->field + SRC1_FIELD_IPV6);
> +	if (rc != 0) {
> +		acl_log("failed to read source address/mask: %s\n",
> +			in[CB_FLD_SRC_ADDR]);
> +		return rc;
> +	}
> +
> +	rc = parse_ipv6_net(in[CB_FLD_DST_ADDR], v->field + DST1_FIELD_IPV6);
> +	if (rc != 0) {
> +		acl_log("failed to read destination address/mask: %s\n",
> +			in[CB_FLD_DST_ADDR]);
> +		return rc;
> +	}
> +
> +	/* source port. */
> +	GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW],
> +		v->field[SRCP_FIELD_IPV6].value.u16,
> +		0, UINT16_MAX, 0);
> +	GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH],
> +		v->field[SRCP_FIELD_IPV6].mask_range.u16,
> +		0, UINT16_MAX, 0);
> +
> +	if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
> +			sizeof(cb_port_delim)) != 0)
> +		return -EINVAL;
> +
> +	/* destination port. */
> +	GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW],
> +		v->field[DSTP_FIELD_IPV6].value.u16,
> +		0, UINT16_MAX, 0);
> +	GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH],
> +		v->field[DSTP_FIELD_IPV6].mask_range.u16,
> +		0, UINT16_MAX, 0);
> +
> +	if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
> +			sizeof(cb_port_delim)) != 0)
> +		return -EINVAL;
> +
> +	if (v->field[SRCP_FIELD_IPV6].mask_range.u16
> +			< v->field[SRCP_FIELD_IPV6].value.u16
> +			|| v->field[DSTP_FIELD_IPV6].mask_range.u16
> +			< v->field[DSTP_FIELD_IPV6].value.u16)
> +		return -EINVAL;
> +
> +	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV6].value.u8,
> +		0, UINT8_MAX, '/');
> +	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV6].mask_range.u8,
> +		0, UINT8_MAX, 0);
> +
> +	if (has_userdata)
> +		GET_CB_FIELD(in[CB_FLD_USERDATA], v->data.userdata,
> +			0, UINT32_MAX, 0);
> +
> +	return 0;
> +}
> +
> +/*
> + * Parse ClassBench rules file.
> + * Expected format:
> + * '@'<src_ipv4_addr>'/'<masklen> <space> \
> + * <dst_ipv4_addr>'/'<masklen> <space> \
> + * <src_port_low> <space> ":" <src_port_high> <space> \
> + * <dst_port_low> <space> ":" <dst_port_high> <space> \
> + * <proto>'/'<mask>
> + */
> +static int
> +parse_ipv4_net(char *in, uint32_t *addr, uint32_t *mask_len)
> +{
> +	char *sa, *sm, *sv;
> +	const char *dlm =  "/";
> +
> +	sv = NULL;
> +	sa = strtok_r(in, dlm, &sv);
> +	if (sa == NULL)
> +		return -EINVAL;
> +	sm = strtok_r(NULL, dlm, &sv);
> +	if (sm == NULL)
> +		return -EINVAL;
> +
> +	if (inet_pton(AF_INET, sa, addr) != 1)
> +		return -EINVAL;
> +
> +	GET_CB_FIELD(sm, *mask_len, 0, 32, 0);
> +	*addr = ntohl(*addr);
> +	return 0;
> +}
> +
> +static int
> +parse_cb_ipv4vlan_rule(char *str, struct rte_acl_rule *v, int has_userdata)
> +{
> +	int i, rc;
> +	char *s, *sp, *in[CB_FLD_NUM];
> +	static const char *dlm = " \t\n";
> +	int dim = has_userdata ? CB_FLD_NUM : CB_FLD_USERDATA;
> +	s = str;
> +
> +	for (i = 0; i != dim; i++, s = NULL) {
> +		in[i] = strtok_r(s, dlm, &sp);
> +		if (in[i] == NULL)
> +			return -EINVAL;
> +	}
> +
> +	rc = parse_ipv4_net(in[CB_FLD_SRC_ADDR],
> +			&v->field[SRC_FIELD_IPV4].value.u32,
> +			&v->field[SRC_FIELD_IPV4].mask_range.u32);
> +	if (rc != 0) {
> +		acl_log("failed to read source address/mask: %s\n",
> +			in[CB_FLD_SRC_ADDR]);
> +		return rc;
> +	}
> +
> +	rc = parse_ipv4_net(in[CB_FLD_DST_ADDR],
> +			&v->field[DST_FIELD_IPV4].value.u32,
> +			&v->field[DST_FIELD_IPV4].mask_range.u32);
> +	if (rc != 0) {
> +		acl_log("failed to read destination address/mask: %s\n",
> +			in[CB_FLD_DST_ADDR]);
> +		return rc;
> +	}
> +
> +	GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW],
> +		v->field[SRCP_FIELD_IPV4].value.u16,
> +		0, UINT16_MAX, 0);
> +	GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH],
> +		v->field[SRCP_FIELD_IPV4].mask_range.u16,
> +		0, UINT16_MAX, 0);
> +
> +	if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
> +			sizeof(cb_port_delim)) != 0) {
> +		return -EINVAL;
> +	}
> +
> +	GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW],
> +		v->field[DSTP_FIELD_IPV4].value.u16,
> +		0, UINT16_MAX, 0);
> +	GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH],
> +		v->field[DSTP_FIELD_IPV4].mask_range.u16,
> +		0, UINT16_MAX, 0);
> +
> +	if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
> +			sizeof(cb_port_delim)) != 0) {
> +		return -EINVAL;
> +	}
> +
> +	if (v->field[SRCP_FIELD_IPV4].mask_range.u16
> +			< v->field[SRCP_FIELD_IPV4].value.u16
> +			|| v->field[DSTP_FIELD_IPV4].mask_range.u16
> +			< v->field[DSTP_FIELD_IPV4].value.u16) {
> +		return -EINVAL;
> +	}
> +
> +	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].value.u8,
> +		0, UINT8_MAX, '/');
> +	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].mask_range.u8,
> +		0, UINT8_MAX, 0);
> +
> +	if (has_userdata)
> +		GET_CB_FIELD(in[CB_FLD_USERDATA], v->data.userdata, 0,
> +			UINT32_MAX, 0);
> +
> +	return 0;
> +}
> +
> +static int
> +acl_add_rules(const char *rule_path,
> +		struct rte_acl_rule **proute_base,
> +		unsigned int *proute_num,
> +		struct rte_acl_rule **pacl_base,
> +		unsigned int *pacl_num, uint32_t rule_size,
> +		int (*parser)(char *, struct rte_acl_rule*, int))
> +{
> +	uint8_t *acl_rules, *route_rules;
> +	struct rte_acl_rule *next;
> +	unsigned int acl_num = 0, route_num = 0, total_num = 0;
> +	unsigned int acl_cnt = 0, route_cnt = 0;
> +	char buff[LINE_MAX];
> +	FILE *fh = fopen(rule_path, "rb");
> +	unsigned int i = 0;
> +	int val;
> +
> +	if (fh == NULL)
> +		rte_exit(EXIT_FAILURE, "%s: Open %s failed\n", __func__,
> +			rule_path);
> +
> +	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
> +		if (buff[0] == ROUTE_LEAD_CHAR)
> +			route_num++;
> +		else if (buff[0] == ACL_LEAD_CHAR)
> +			acl_num++;
> +	}
> +
> +	if (route_num == 0)
> +		rte_exit(EXIT_FAILURE, "Not find any route entries in %s!\n",
> +				rule_path);
> +
> +	val = fseek(fh, 0, SEEK_SET);
> +	if (val < 0) {
> +		rte_exit(EXIT_FAILURE, "%s: File seek operation failed\n",
> +			__func__);
> +	}
> +
> +	acl_rules = calloc(acl_num, rule_size);
> +
> +	if (acl_rules == NULL)
> +		rte_exit(EXIT_FAILURE, "%s: failed to malloc memory\n",
> +			__func__);
> +
> +	route_rules = calloc(route_num, rule_size);
> +
> +	if (route_rules == NULL)
> +		rte_exit(EXIT_FAILURE, "%s: failed to malloc memory\n",
> +			__func__);
> +
> +	i = 0;
> +	while (fgets(buff, LINE_MAX, fh) != NULL) {
> +		i++;
> +
> +		if (is_bypass_line(buff))
> +			continue;
> +
> +		char s = buff[0];
> +
> +		/* Route entry */
> +		if (s == ROUTE_LEAD_CHAR)
> +			next = (struct rte_acl_rule *)(route_rules +
> +				route_cnt * rule_size);
> +
> +		/* ACL entry */
> +		else if (s == ACL_LEAD_CHAR)
> +			next = (struct rte_acl_rule *)(acl_rules +
> +				acl_cnt * rule_size);
> +
> +		/* Illegal line */
> +		else
> +			rte_exit(EXIT_FAILURE,
> +				"%s Line %u: should start with leading "
> +				"char %c or %c\n",
> +				rule_path, i, ROUTE_LEAD_CHAR, ACL_LEAD_CHAR);
> +
> +		if (parser(buff + 1, next, s == ROUTE_LEAD_CHAR) != 0)
> +			rte_exit(EXIT_FAILURE,
> +				"%s Line %u: parse rules error\n",
> +				rule_path, i);
> +
> +		if (s == ROUTE_LEAD_CHAR) {
> +			/* Check the forwarding port number */
> +			if ((enabled_port_mask & (1 << next->data.userdata)) ==
> +					0)
> +				rte_exit(EXIT_FAILURE,
> +					"%s Line %u: fwd number illegal:%u\n",
> +					rule_path, i, next->data.userdata);
> +			next->data.userdata += FWD_PORT_SHIFT;
> +			route_cnt++;
> +		} else {
> +			next->data.userdata = ACL_DENY_SIGNATURE + acl_cnt;
> +			acl_cnt++;
> +		}
> +
> +		next->data.priority = RTE_ACL_MAX_PRIORITY - total_num;
> +		next->data.category_mask = -1;
> +		total_num++;
> +	}
> +
> +	fclose(fh);
> +
> +	*pacl_base = (struct rte_acl_rule *)acl_rules;
> +	*pacl_num = acl_num;
> +	*proute_base = (struct rte_acl_rule *)route_rules;
> +	*proute_num = route_cnt;
> +
> +	return 0;
> +}
> +
> +static const char *
> +str_acl_alg(enum rte_acl_classify_alg alg)
> +{
> +	uint32_t i;
> +
> +	for (i = 0; i != RTE_DIM(acl_alg); i++) {
> +		if (alg == acl_alg[i].alg)
> +			return acl_alg[i].name;
> +	}
> +
> +	return "default";
> +}
> +
> +static void
> +dump_acl_config(void)
> +{
> +	printf("ACL options are:\n");
> +	printf("rule_ipv4: %s\n", parm_config.rule_ipv4_name);
> +	printf("rule_ipv6: %s\n", parm_config.rule_ipv6_name);
> +	printf("alg: %s\n", str_acl_alg(parm_config.alg));
> +}
> +
> +static int
> +check_acl_config(void)
> +{
> +	if (parm_config.rule_ipv4_name == NULL) {
> +		acl_log("ACL IPv4 rule file not specified\n");
> +		return -1;
> +	} else if (parm_config.rule_ipv6_name == NULL) {
> +		acl_log("ACL IPv6 rule file not specified\n");
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +/* Setup ACL context. 8< */
> +static struct rte_acl_ctx*
> +app_acl_init(struct rte_acl_rule *route_base,
> +		struct rte_acl_rule *acl_base, unsigned int route_num,
> +		unsigned int acl_num, int ipv6, int socketid)
> +{
> +	char name[PATH_MAX];
> +	struct rte_acl_param acl_param;
> +	struct rte_acl_config acl_build_param;
> +	struct rte_acl_ctx *context;
> +	int dim = ipv6 ? RTE_DIM(ipv6_defs) : RTE_DIM(ipv4_defs);
> +
> +	/* Create ACL contexts */
> +	snprintf(name, sizeof(name), "%s%d",
> +			ipv6 ? L3FWD_ACL_IPV6_NAME : L3FWD_ACL_IPV4_NAME,
> +			socketid);
> +
> +	acl_param.name = name;
> +	acl_param.socket_id = socketid;
> +	acl_param.rule_size = RTE_ACL_RULE_SZ(dim);
> +	acl_param.max_rule_num = MAX_ACL_RULE_NUM;
> +
> +	context = rte_acl_create(&acl_param);
> +	if (context == NULL)
> +		rte_exit(EXIT_FAILURE, "Failed to create ACL context\n");
> +
> +	if (parm_config.alg != RTE_ACL_CLASSIFY_DEFAULT &&
> +			rte_acl_set_ctx_classify(context, parm_config.alg) != 0)
> +		rte_exit(EXIT_FAILURE,
> +			"Failed to setup classify method for  ACL context\n");
> +
> +	if (rte_acl_add_rules(context, route_base, route_num) < 0)
> +		rte_exit(EXIT_FAILURE, "add rules failed\n");
> +
> +	if (rte_acl_add_rules(context, acl_base, acl_num) < 0)
> +		rte_exit(EXIT_FAILURE, "add rules failed\n");
> +
> +	/* Perform builds */
> +	memset(&acl_build_param, 0, sizeof(acl_build_param));
> +
> +	acl_build_param.num_categories = DEFAULT_MAX_CATEGORIES;
> +	acl_build_param.num_fields = dim;
> +	memcpy(&acl_build_param.defs, ipv6 ? ipv6_defs : ipv4_defs,
> +		ipv6 ? sizeof(ipv6_defs) : sizeof(ipv4_defs));
> +
> +	if (rte_acl_build(context, &acl_build_param) != 0)
> +		rte_exit(EXIT_FAILURE, "Failed to build ACL trie\n");
> +
> +	rte_acl_dump(context);
> +
> +	return context;
> +}
> +/* >8 End of ACL context setup. */
> +
> +void
> +acl_free_routes(void)
> +{
> +	free(route_base_ipv4);
> +	free(route_base_ipv6);
> +	route_base_ipv4 = NULL;
> +	route_base_ipv6 = NULL;
> +	route_num_ipv4 = 0;
> +	route_num_ipv6 = 0;
> +	free(acl_base_ipv4);
> +	free(acl_base_ipv6);
> +	acl_base_ipv4 = NULL;
> +	acl_base_ipv6 = NULL;
> +	acl_num_ipv4 = 0;
> +	acl_num_ipv6 = 0;
> +}
> +
> +/* Load rules from the input file */
> +void
> +read_config_files_acl(void)
> +{
> +	/* ipv4 check */
> +	if (parm_config.rule_ipv4_name != NULL) {
> +		if (acl_add_rules(parm_config.rule_ipv4_name, &route_base_ipv4,
> +				&route_num_ipv4, &acl_base_ipv4, &acl_num_ipv4,
> +				sizeof(struct acl4_rule), &parse_cb_ipv4vlan_rule) < 0) {
> +			acl_free_routes();
> +			rte_exit(EXIT_FAILURE, "Failed to add IPv4 rules\n");
> +		}
> +	} else {
> +		RTE_LOG(ERR, L3FWD, "IPv4 rule file not specified\n");
> +		rte_exit(EXIT_FAILURE, "Failed to get valid route options\n");
> +	}
> +
> +	/* ipv6 check */
> +	if (parm_config.rule_ipv6_name != NULL) {
> +		if (acl_add_rules(parm_config.rule_ipv6_name, &route_base_ipv6,
> +				&route_num_ipv6,
> +				&acl_base_ipv6, &acl_num_ipv6,
> +				sizeof(struct acl6_rule), &parse_cb_ipv6_rule) < 0) {
> +			acl_free_routes();
> +			rte_exit(EXIT_FAILURE, "Failed to add IPv6 rules\n");
> +		}
> +	} else {
> +		RTE_LOG(ERR, L3FWD, "IPv6 rule file not specified\n");
> +		rte_exit(EXIT_FAILURE, "Failed to get valid route options\n");
> +	}
> +}
> +
> +#ifdef L3FWDACL_DEBUG
> +static inline void
> +dump_acl4_rule(struct rte_mbuf *m, uint32_t sig)
> +{
> +	char abuf[INET6_ADDRSTRLEN];
> +	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
> +	struct rte_ipv4_hdr *ipv4_hdr =
> +		rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *,
> +					sizeof(struct rte_ether_hdr));
> +
> +	printf("Packet Src:%s ", inet_ntop(AF_INET, ipv4_hdr->src_addr,
> +		abuf, sizeof(abuf)));
> +	printf("Dst:%s ", inet_ntop(AF_INET, ipv4_hdr->dst_addr,
> +		abuf, sizeof(abuf)));
> +
> +	printf("Src port:%hu,Dst port:%hu ",
> +			rte_bswap16(*(uint16_t *)(ipv4_hdr + 1)),
> +			rte_bswap16(*((uint16_t *)(ipv4_hdr + 1) + 1)));
> +	printf("hit ACL %d - ", offset);
> +
> +	print_one_ipv4_rule(acl_config.rule_ipv4 + offset, 1);
> +
> +	printf("\n\n");
> +}
> +
> +static inline void
> +dump_acl6_rule(struct rte_mbuf *m, uint32_t sig)
> +{
> +	char abuf[INET6_ADDRSTRLEN];
> +	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
> +	struct rte_ipv6_hdr *ipv6_hdr =
> +		rte_pktmbuf_mtod_offset(m, struct rte_ipv6_hdr *,
> +					sizeof(struct rte_ether_hdr));
> +
> +	printf("Packet Src");
> +	printf("%s", inet_ntop(AF_INET6, ipv6_hdr->src_addr,
> +		abuf, sizeof(abuf)));
> +	printf("\nDst");
> +	printf("%s", inet_ntop(AF_INET6, ipv6_hdr->dst_addr,
> +		abuf, sizeof(abuf)));
> +
> +	printf("\nSrc port:%hu,Dst port:%hu ",
> +			rte_bswap16(*(uint16_t *)(ipv6_hdr + 1)),
> +			rte_bswap16(*((uint16_t *)(ipv6_hdr + 1) + 1)));
> +	printf("hit ACL %d - ", offset);
> +
> +	print_one_ipv6_rule(acl_config.rule_ipv6 + offset, 1);
> +
> +	printf("\n\n");
> +}
> +#endif /* L3FWDACL_DEBUG */
> +
> +static inline void
> +dump_ipv4_rules(struct acl4_rule *rule, int num, int extra)
> +{
> +	int i;
> +
> +	for (i = 0; i < num; i++, rule++) {
> +		printf("\t%d:", i + 1);
> +		print_one_ipv4_rule(rule, extra);
> +		printf("\n");
> +	}
> +}
> +
> +static inline void
> +dump_ipv6_rules(struct acl6_rule *rule, int num, int extra)
> +{
> +	int i;
> +
> +	for (i = 0; i < num; i++, rule++) {
> +		printf("\t%d:", i + 1);
> +		print_one_ipv6_rule(rule, extra);
> +		printf("\n");
> +	}
> +}
> +
> +/* Function to setup acl. */
> +void
> +setup_acl(const int socket_id)
> +{
> +	if (check_acl_config() != 0)
> +		rte_exit(EXIT_FAILURE, "Failed to get valid ACL options\n");
> +
> +	dump_acl_config();
> +
> +	acl_log("IPv4 Route entries %u:\n", route_num_ipv4);
> +	dump_ipv4_rules((struct acl4_rule *)route_base_ipv4, route_num_ipv4, 1);
> +
> +	acl_log("IPv4 ACL entries %u:\n", acl_num_ipv4);
> +	dump_ipv4_rules((struct acl4_rule *)acl_base_ipv4, acl_num_ipv4, 1);
> +
> +	acl_log("IPv6 Route entries %u:\n", route_num_ipv6);
> +	dump_ipv6_rules((struct acl6_rule *)route_base_ipv6, route_num_ipv6, 1);
> +
> +	acl_log("IPv6 ACL entries %u:\n", acl_num_ipv6);
> +	dump_ipv6_rules((struct acl6_rule *)acl_base_ipv6, acl_num_ipv6, 1);
> +
> +	memset(&acl_config, 0, sizeof(acl_config));
> +
> +	/* Check sockets a context should be created on */
> +	if (socket_id >= NB_SOCKETS) {
> +		acl_log("Socket %d is out "
> +			"of range %d\n",
> +			socket_id, NB_SOCKETS);
> +		acl_free_routes();
> +		return;
> +	}
> +
> +	acl_config.acx_ipv4[socket_id] = app_acl_init(route_base_ipv4,
> +		acl_base_ipv4, route_num_ipv4, acl_num_ipv4,
> +		0, socket_id);
> +
> +	acl_config.acx_ipv6[socket_id] = app_acl_init(route_base_ipv6,
> +		acl_base_ipv6, route_num_ipv6, acl_num_ipv6,
> +		1, socket_id);
> +
> +#ifdef L3FWDACL_DEBUG
> +	acl_config.rule_ipv4 = (struct acl4_rule *)acl_base_ipv4;
> +	acl_config.rule_ipv6 = (struct acl6_rule *)acl_base_ipv6;
> +#endif
> +
> +}
> +
> +/* main processing loop */
> +int
> +acl_main_loop(__rte_unused void *dummy)
> +{
> +	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
> +	unsigned int lcore_id;
> +	uint64_t prev_tsc, diff_tsc, cur_tsc;
> +	int i, nb_rx;
> +	uint16_t portid;
> +	uint8_t queueid;
> +	struct lcore_conf *qconf;
> +	int socketid;
> +	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1)
> +			/ US_PER_S * BURST_TX_DRAIN_US;
> +
> +	prev_tsc = 0;
> +	lcore_id = rte_lcore_id();
> +	qconf = &lcore_conf[lcore_id];
> +	socketid = rte_lcore_to_socket_id(lcore_id);
> +
> +	if (qconf->n_rx_queue == 0) {
> +		RTE_LOG(INFO, L3FWD, "lcore %u has nothing to do\n", lcore_id);
> +		return 0;
> +	}
> +
> +	RTE_LOG(INFO, L3FWD, "entering main loop on lcore %u\n", lcore_id);
> +
> +	for (i = 0; i < qconf->n_rx_queue; i++) {
> +
> +		portid = qconf->rx_queue_list[i].port_id;
> +		queueid = qconf->rx_queue_list[i].queue_id;
> +		RTE_LOG(INFO, L3FWD,
> +			" -- lcoreid=%u portid=%u rxqueueid=%hhu\n",
> +			lcore_id, portid, queueid);
> +	}
> +
> +	while (!force_quit) {
> +
> +		cur_tsc = rte_rdtsc();
> +
> +		/*
> +		 * TX burst queue drain
> +		 */
> +		diff_tsc = cur_tsc - prev_tsc;
> +		if (unlikely(diff_tsc > drain_tsc)) {
> +
> +			for (i = 0; i < qconf->n_tx_port; ++i) {
> +				portid = qconf->tx_port_id[i];
> +				if (qconf->tx_mbufs[portid].len == 0)
> +					continue;
> +				send_burst(qconf,
> +					qconf->tx_mbufs[portid].len,
> +					portid);
> +				qconf->tx_mbufs[portid].len = 0;
> +			}
> +
> +			prev_tsc = cur_tsc;
> +		}
> +
> +		/*
> +		 * Read packet from RX queues
> +		 */
> +		for (i = 0; i < qconf->n_rx_queue; ++i) {
> +
> +			portid = qconf->rx_queue_list[i].port_id;
> +			queueid = qconf->rx_queue_list[i].queue_id;
> +			nb_rx = rte_eth_rx_burst(portid, queueid,
> +				pkts_burst, MAX_PKT_BURST);
> +
> +			if (nb_rx > 0) {
> +				struct acl_search_t acl_search;
> +
> +				l3fwd_acl_prepare_acl_parameter(pkts_burst, &acl_search,
> +					nb_rx);
> +
> +				if (acl_search.num_ipv4) {
> +					rte_acl_classify(
> +						acl_config.acx_ipv4[socketid],
> +						acl_search.data_ipv4,
> +						acl_search.res_ipv4,
> +						acl_search.num_ipv4,
> +						DEFAULT_MAX_CATEGORIES);
> +
> +					l3fwd_acl_send_packets(
> +						acl_search.m_ipv4,
> +						acl_search.res_ipv4,
> +						acl_search.num_ipv4);
> +				}
> +
> +				if (acl_search.num_ipv6) {
> +					rte_acl_classify(
> +						acl_config.acx_ipv6[socketid],
> +						acl_search.data_ipv6,
> +						acl_search.res_ipv6,
> +						acl_search.num_ipv6,
> +						DEFAULT_MAX_CATEGORIES);
> +
> +					l3fwd_acl_send_packets(
> +						acl_search.m_ipv6,
> +						acl_search.res_ipv6,
> +						acl_search.num_ipv6);
> +				}
> +			}
> +		}
> +	}
> +	return 0;
> +}
> +
> +/* Not used by L3fwd ACL. */
> +void *
> +acl_get_ipv4_l3fwd_lookup_struct(__rte_unused const int socketid)
> +{
> +	return NULL;
> +}
> +
> +void *
> +acl_get_ipv6_l3fwd_lookup_struct(__rte_unused const int socketid)
> +{
> +	return NULL;
> +}



> diff --git a/examples/l3fwd/l3fwd_acl.h b/examples/l3fwd/l3fwd_acl.h
> new file mode 100644
> index 0000000000..9b762e4ec2
> --- /dev/null
> +++ b/examples/l3fwd/l3fwd_acl.h
> @@ -0,0 +1,118 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2022 Intel Corporation
> + */
> +
> +#ifndef __L3FWD_ACL_H__
> +#define __L3FWD_ACL_H__
> +
> +#if RTE_LOG_DP_LEVEL >= RTE_LOG_DEBUG
> +#define L3FWDACL_DEBUG
> +#endif
> +
> +#define MAX_ACL_RULE_NUM	100000
> +#define DEFAULT_MAX_CATEGORIES	1
> +#define L3FWD_ACL_IPV4_NAME	"l3fwd-acl-ipv4"
> +#define L3FWD_ACL_IPV6_NAME	"l3fwd-acl-ipv6"
> +
> +#define ACL_DENY_SIGNATURE	0xf0000000
> +#define RTE_LOGTYPE_L3FWDACL	RTE_LOGTYPE_USER3
> +#define acl_log(format, ...)	RTE_LOG(ERR, L3FWDACL, format, ##__VA_ARGS__)
> +#define OFF_ETHHEAD	(sizeof(struct rte_ether_hdr))
> +#define OFF_IPV42PROTO (offsetof(struct rte_ipv4_hdr, next_proto_id))
> +#define OFF_IPV62PROTO (offsetof(struct rte_ipv6_hdr, proto))
> +#define MBUF_IPV4_2PROTO(m)	\
> +	rte_pktmbuf_mtod_offset((m), uint8_t *, OFF_ETHHEAD + OFF_IPV42PROTO)
> +#define MBUF_IPV6_2PROTO(m)	\
> +	rte_pktmbuf_mtod_offset((m), uint8_t *, OFF_ETHHEAD + OFF_IPV62PROTO)
> +
> +/*
> + * ACL rules should have higher priorities than route ones to ensure ACL rule
> + * always be found when input packets have multi-matches in the database.
> + * A exception case is performance measure, which can define route rules with
> + * higher priority and route rules will always be returned in each lookup.
> + * Reserve range from ACL_RULE_PRIORITY_MAX + 1 to
> + * RTE_ACL_MAX_PRIORITY for route entries in performance measure
> + */
> +#define ACL_RULE_PRIORITY_MAX 0x10000000
> +
> +/*
> + * Forward port info save in ACL lib starts from 1
> + * since ACL assume 0 is invalid.
> + * So, need add 1 when saving and minus 1 when forwarding packets.
> + */
> +#define FWD_PORT_SHIFT 1
> +
> +static void
> +print_one_ipv4_rule(struct acl4_rule *rule, int extra)

Repetition of my previous comments to (v1):
Probably no point to have print routines as 'static online'.
They do not belong to fast-path.
Same for print_one_ipv6_rule().

> +{
> +	char abuf[INET6_ADDRSTRLEN];
> +	uint32_t ipv4_addr;
> +	ipv4_addr = ntohl(rule->field[SRC_FIELD_IPV4].value.u32);
> +	printf("%s/%u ", inet_ntop(AF_INET,
> +			&(ipv4_addr), abuf,
> +			sizeof(abuf)), rule->field[SRC_FIELD_IPV4].mask_range.u32);
> +	ipv4_addr = ntohl(rule->field[DST_FIELD_IPV4].value.u32);
> +	printf("%s/%u ", inet_ntop(AF_INET,
> +			&(ipv4_addr), abuf,
> +			sizeof(abuf)), rule->field[DST_FIELD_IPV4].mask_range.u32);
> +	printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
> +		rule->field[SRCP_FIELD_IPV4].value.u16,
> +		rule->field[SRCP_FIELD_IPV4].mask_range.u16,
> +		rule->field[DSTP_FIELD_IPV4].value.u16,
> +		rule->field[DSTP_FIELD_IPV4].mask_range.u16,
> +		rule->field[PROTO_FIELD_IPV4].value.u8,
> +		rule->field[PROTO_FIELD_IPV4].mask_range.u8);
> +	if (extra)
> +		printf("0x%x-0x%x-0x%x ",
> +			rule->data.category_mask,
> +			rule->data.priority,
> +			rule->data.userdata);
> +}
> +
> +static void
> +print_one_ipv6_rule(struct acl6_rule *rule, int extra)
> +{
> +	char abuf[INET6_ADDRSTRLEN];
> +	uint32_t ipv6_addr[4];
> +
> +	ipv6_addr[0] = rule->field[SRC1_FIELD_IPV6].value.u32;
> +	ipv6_addr[1] = rule->field[SRC2_FIELD_IPV6].value.u32;
> +	ipv6_addr[2] = rule->field[SRC3_FIELD_IPV6].value.u32;
> +	ipv6_addr[3] = rule->field[SRC4_FIELD_IPV6].value.u32;
> +
> +	printf("%s", inet_ntop(AF_INET6, ipv6_addr,
> +		abuf, sizeof(abuf)));
> +	printf("/%u ",
> +			rule->field[SRC1_FIELD_IPV6].mask_range.u32
> +			+ rule->field[SRC2_FIELD_IPV6].mask_range.u32
> +			+ rule->field[SRC3_FIELD_IPV6].mask_range.u32
> +			+ rule->field[SRC4_FIELD_IPV6].mask_range.u32);
> +
> +	ipv6_addr[0] = rule->field[DST1_FIELD_IPV6].value.u32;
> +	ipv6_addr[1] = rule->field[DST2_FIELD_IPV6].value.u32;
> +	ipv6_addr[2] = rule->field[DST3_FIELD_IPV6].value.u32;
> +	ipv6_addr[3] = rule->field[DST4_FIELD_IPV6].value.u32;
> +
> +	printf("%s", inet_ntop(AF_INET6, ipv6_addr,
> +		abuf, sizeof(abuf)));
> +	printf("/%u ",
> +			rule->field[DST1_FIELD_IPV6].mask_range.u32
> +			+ rule->field[DST2_FIELD_IPV6].mask_range.u32
> +			+ rule->field[DST3_FIELD_IPV6].mask_range.u32
> +			+ rule->field[DST4_FIELD_IPV6].mask_range.u32);
> +
> +	printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
> +		rule->field[SRCP_FIELD_IPV6].value.u16,
> +		rule->field[SRCP_FIELD_IPV6].mask_range.u16,
> +		rule->field[DSTP_FIELD_IPV6].value.u16,
> +		rule->field[DSTP_FIELD_IPV6].mask_range.u16,
> +		rule->field[PROTO_FIELD_IPV6].value.u8,
> +		rule->field[PROTO_FIELD_IPV6].mask_range.u8);
> +	if (extra)
> +		printf("0x%x-0x%x-0x%x ",
> +			rule->data.category_mask,
> +			rule->data.priority,
> +			rule->data.userdata);
> +}
> +
> +#endif /* __L3FWD_ACL_H__ */
> diff --git a/examples/l3fwd/l3fwd_acl_scalar.h b/examples/l3fwd/l3fwd_acl_scalar.h
> new file mode 100644
> index 0000000000..fb1991ed1d
> --- /dev/null
> +++ b/examples/l3fwd/l3fwd_acl_scalar.h
> @@ -0,0 +1,163 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2022 Intel Corporation
> + */
> +
> +#ifndef __L3FWD_ACL_SCALAR_H__
> +#define __L3FWD_ACL_SCALAR_H__
> +
> +#include "l3fwd.h"
> +
> +/* ethernet addresses of ports */
> +static struct rte_ether_hdr port_l2hdr[RTE_MAX_ETHPORTS];

There already exist:
rte_ether_addr ports_eth_addr[] and dest_eth_addr[]
that are used by all other methods (lpm/em/fib).
Please do the same here, no need to have a special copy for acl.

> +
> +#ifdef DO_RFC_1812_CHECKS
> +static inline void
> +l3fwd_acl_prepare_one_packet(struct rte_mbuf **pkts_in, struct acl_search_t *acl,
> +	int index)
> +{
> +	struct rte_ipv4_hdr *ipv4_hdr;
> +	struct rte_mbuf *pkt = pkts_in[index];
> +
> +	if (RTE_ETH_IS_IPV4_HDR(pkt->packet_type)) {
> +		ipv4_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_ipv4_hdr *,
> +						sizeof(struct rte_ether_hdr));
> +
> +		/* Check to make sure the packet is valid (RFC1812) */
> +		if (is_valid_ipv4_pkt(ipv4_hdr, pkt->pkt_len) >= 0) {
> +
> +			/* Update time to live and header checksum */
> +			--(ipv4_hdr->time_to_live);
> +			++(ipv4_hdr->hdr_checksum);
> +
> +			/* Fill acl structure */
> +			acl->data_ipv4[acl->num_ipv4] = MBUF_IPV4_2PROTO(pkt);
> +			acl->m_ipv4[(acl->num_ipv4)++] = pkt;
> +
> +		} else {
> +			/* Not a valid IPv4 packet */
> +			rte_pktmbuf_free(pkt);
> +		}
> +	} else if (RTE_ETH_IS_IPV6_HDR(pkt->packet_type)) {
> +		/* Fill acl structure */
> +		acl->data_ipv6[acl->num_ipv6] = MBUF_IPV6_2PROTO(pkt);
> +		acl->m_ipv6[(acl->num_ipv6)++] = pkt;
> +
> +	} else {
> +		/* Unknown type, drop the packet */
> +		rte_pktmbuf_free(pkt);
> +	}
> +}
> +
> +#else
> +static inline void
> +l3fwd_acl_prepare_one_packet(struct rte_mbuf **pkts_in, struct acl_search_t *acl,
> +	int index)
> +{
> +	struct rte_mbuf *pkt = pkts_in[index];
> +
> +	if (RTE_ETH_IS_IPV4_HDR(pkt->packet_type)) {
> +		/* Fill acl structure */
> +		acl->data_ipv4[acl->num_ipv4] = MBUF_IPV4_2PROTO(pkt);
> +		acl->m_ipv4[(acl->num_ipv4)++] = pkt;
> +
> +	} else if (RTE_ETH_IS_IPV6_HDR(pkt->packet_type)) {
> +		/* Fill acl structure */
> +		acl->data_ipv6[acl->num_ipv6] = MBUF_IPV6_2PROTO(pkt);
> +		acl->m_ipv6[(acl->num_ipv6)++] = pkt;
> +	} else {
> +		/* Unknown type, drop the packet */
> +		rte_pktmbuf_free(pkt);
> +	}
> +}
> +#endif /* DO_RFC_1812_CHECKS */

It seems that I wasn't clear enough in my previous comments.
What I suggest - move all packet modifications from _prepare_ to
_send_ routines. That as I understand, should allow us to use
optimized send_multi() on TX path.
To be more specific, I am talking about something like that:
- have just one version of l3fwd_acl_prepare_one_packet() - second one above.
- Make l3fwd_acl_send_packets() to look something like that:

#if defined RTE_ARCH_X86 || defined __ARM_NEON \
                || defined RTE_ARCH_PPC_64
#define SEND_MULTI
#endif

static inline void
send_packets_single(struct lcore_conf *qconf, struct rte_mbuf *pkts[], uint16_t hops[], uint32_t nb_tx)
{
        int32_t j;
        struct rte_ether_hdr *eth_hdr;

        for (j = 0; j < nb_tx; j++) {
                /* Run rfc1812 if packet is ipv4 and checks enabled. */
                rfc1812_process((struct rte_ipv4_hdr *)(rte_pktmbuf_mtod(
                                pkts[j], struct rte_ether_hdr *) + 1),
                                &hops[j], pkts[j]->packet_type);

                /* Set MAC addresses. */
                eth_hdr = rte_pktmbuf_mtod(pkts[j], struct rte_ether_hdr *);
                *(uint64_t *)&eth_hdr->dst_addr = dest_eth_addr[hops[j]];
                rte_ether_addr_copy(&ports_eth_addr[hops[j]],
                                &eth_hdr->src_addr);
        }

        for (j  = 0; j != nb_tx; j++) { 
	if (hops[j] != BAD_PORT)
                      send_single_packet(qconf, pktst[j], hops[j]);
	else
	      rte_pktmbuf_free(pkts[j]);
        }
} 

static inline void
l3fwd_acl_send_packets(struct lcore_conf *qconf, struct rte_mbuf *pkts[], uint16_t hops[], uint32_t nb_tx)
{
	uint32_t i;
	uint16_t dst_port[nb_tx];

	for (i = 0; i != nb_tx; i++) {
		if (likely((res[i] & ACL_DENY_SIGNATURE) == 0 && res[i] != 0)) {
			dst_port[i] = res[i] - FWD_PORT_SHIFT;
		} else {
			dst_port[i] = BAD_PORT;
#ifdef L3FWDACL_DEBUG
			if ((res & ACL_DENY_SIGNATURE) != 0) {
				if (RTE_ETH_IS_IPV4_HDR(pkts[i]->packet_type))
					dump_acl4_rule(pkts[i], res[i]);
				else if (RTE_ETH_IS_IPV6_HDR(pkt[i]->packet_type))
					dump_acl6_rule(pkt[i], res[i]);
                		}
#endif
		}
	}

#if defined SEND_MULTI
	send_packets_multi(qconf, pkts, dst_port, nb_tx);
# else
	send_packets_single(qconf, pkts, dst_port, nb_tx);
#endif
}

> +
> +/* Enqueue a single packet, and send burst if queue is filled */
> +static inline void
> +l3fwd_acl_send_single_packet(struct rte_mbuf *m, uint16_t port)
> +{
> +	uint32_t lcore_id;
> +	struct lcore_conf *qconf;
> +	struct rte_ether_hdr *eh;
> +
> +	lcore_id = rte_lcore_id();
> +
> +	/* update src and dst mac*/
> +	eh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
> +	memcpy(eh, &port_l2hdr[port],
> +			sizeof(eh->dst_addr) + sizeof(eh->src_addr));
> +
> +	qconf = &lcore_conf[lcore_id];
> +	rte_eth_tx_buffer(port, qconf->tx_queue_id[port],
> +			qconf->tx_buffer[port], m);
> +}
> +
> +static inline void
> +l3fwd_acl_prepare_acl_parameter(struct rte_mbuf **pkts_in, struct acl_search_t *acl,
> +	int nb_rx)
> +{
> +	int i;
> +
> +	acl->num_ipv4 = 0;
> +	acl->num_ipv6 = 0;
> +
> +	/* Prefetch first packets */
> +	for (i = 0; i < PREFETCH_OFFSET && i < nb_rx; i++) {
> +		rte_prefetch0(rte_pktmbuf_mtod(
> +				pkts_in[i], void *));
> +	}
> +
> +	for (i = 0; i < (nb_rx - PREFETCH_OFFSET); i++) {
> +		rte_prefetch0(rte_pktmbuf_mtod(pkts_in[
> +				i + PREFETCH_OFFSET], void *));
> +		l3fwd_acl_prepare_one_packet(pkts_in, acl, i);
> +	}
> +
> +	/* Process left packets */
> +	for (; i < nb_rx; i++)
> +		l3fwd_acl_prepare_one_packet(pkts_in, acl, i);
> +}
> +
> +static inline void
> +l3fwd_acl_send_one_packet(struct rte_mbuf *m, uint32_t res)
> +{
> +	if (likely((res & ACL_DENY_SIGNATURE) == 0 && res != 0)) {
> +		/* forward packets */
> +		l3fwd_acl_send_single_packet(m,
> +			(uint8_t)(res - FWD_PORT_SHIFT));
> +	} else {
> +		/* in the ACL list, drop it */
> +#ifdef L3FWDACL_DEBUG
> +		if ((res & ACL_DENY_SIGNATURE) != 0) {
> +			if (RTE_ETH_IS_IPV4_HDR(m->packet_type))
> +				dump_acl4_rule(m, res);
> +			else if (RTE_ETH_IS_IPV6_HDR(m->packet_type))
> +				dump_acl6_rule(m, res);
> +		}
> +#endif
> +		rte_pktmbuf_free(m);
> +	}
> +}
> +
> +static inline void
> +l3fwd_acl_send_packets(struct rte_mbuf **m, uint32_t *res, int num)
> +{
> +	int i;
> +
> +	/* Prefetch first packets */
> +	for (i = 0; i < PREFETCH_OFFSET && i < num; i++) {
> +		rte_prefetch0(rte_pktmbuf_mtod(
> +				m[i], void *));
> +	}
> +
> +	for (i = 0; i < (num - PREFETCH_OFFSET); i++) {
> +		rte_prefetch0(rte_pktmbuf_mtod(m[
> +				i + PREFETCH_OFFSET], void *));
> +		l3fwd_acl_send_one_packet(m[i], res[i]);
> +	}
> +
> +	/* Process left packets */
> +	for (; i < num; i++)
> +		l3fwd_acl_send_one_packet(m[i], res[i]);
> +}
> +
> +#endif /* __L3FWD_ACL_SCALAR_H__ */
> diff --git a/examples/l3fwd/l3fwd_route.h b/examples/l3fwd/l3fwd_route.h
> index adf33dbf46..467c4d2859 100644
> --- a/examples/l3fwd/l3fwd_route.h
> +++ b/examples/l3fwd/l3fwd_route.h
> @@ -5,6 +5,7 @@
>  /* Log file related character defs. */
>  #define COMMENT_LEAD_CHAR	('#')
>  #define ROUTE_LEAD_CHAR		('R')
> +#define ACL_LEAD_CHAR		('@')
> 
>  #define	IPV6_ADDR_LEN	16
>  #define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
> @@ -96,11 +97,26 @@ read_config_files_lpm(void);
>  void
>  read_config_files_em(void);
> 
> +void
> +read_config_files_acl(void);
> +
>  void
>  em_free_routes(void);
> 
>  void
>  lpm_free_routes(void);
> 
> +void
> +acl_free_routes(void);
> +
> +void
> +l3fwd_set_alg(const char *optarg);
> +
> +void
> +l3fwd_set_rule_ipv6_name(const char *optarg);
> +
> +void
> +l3fwd_set_rule_ipv4_name(const char *optarg);
> +
>  int
>  is_bypass_line(const char *buff);
> diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
> index a629198223..4409d346df 100644
> --- a/examples/l3fwd/main.c
> +++ b/examples/l3fwd/main.c
> @@ -59,17 +59,17 @@ uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
>  /**< Ports set in promiscuous mode off by default. */
>  static int promiscuous_on;
> 
> -/* Select Longest-Prefix, Exact match or Forwarding Information Base. */
> +/* Select Longest-Prefix, Exact match, Forwarding Information Base or Access Control. */
>  enum L3FWD_LOOKUP_MODE {
>  	L3FWD_LOOKUP_DEFAULT,
>  	L3FWD_LOOKUP_LPM,
>  	L3FWD_LOOKUP_EM,
> -	L3FWD_LOOKUP_FIB
> +	L3FWD_LOOKUP_FIB,
> +	L3FWD_LOOKUP_ACL
>  };
>  static enum L3FWD_LOOKUP_MODE lookup_mode;
> 
>  /* Global variables. */
> -
>  static int numa_on = 1; /**< NUMA is enabled by default. */
>  static int parse_ptype; /**< Parse packet type using rx callback, and */
>  			/**< disabled by default */
> @@ -187,6 +187,30 @@ static struct l3fwd_lkp_mode l3fwd_fib_lkp = {
>  	.free_routes			= lpm_free_routes,
>  };
> 
> +static struct l3fwd_lkp_mode l3fwd_acl_lkp = {
> +	.read_config_files		= read_config_files_acl,
> +	.setup                  = setup_acl,
> +	.check_ptype            = em_check_ptype,
> +	.cb_parse_ptype         = em_cb_parse_ptype,
> +	.main_loop              = acl_main_loop,
> +	.get_ipv4_lookup_struct = acl_get_ipv4_l3fwd_lookup_struct,
> +	.get_ipv6_lookup_struct = acl_get_ipv6_l3fwd_lookup_struct,
> +	.free_routes			= acl_free_routes,
> +};
> +
> +static enum rte_acl_classify_alg
> +parse_acl_alg(const char *alg)
> +{
> +	uint32_t i;
> +
> +	for (i = 0; i != RTE_DIM(acl_alg); i++) {
> +		if (strcmp(alg, acl_alg[i].name) == 0)
> +			return acl_alg[i].alg;
> +	}
> +
> +	return RTE_ACL_CLASSIFY_DEFAULT;
> +}

I think we can safely move this function into l3fwd_acl.c.

> +
>  /*
>   * 198.18.0.0/16 are set aside for RFC2544 benchmarking (RFC5735).
>   * 198.18.{0-15}.0/24 = Port {0-15}
> @@ -236,18 +260,24 @@ const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[] = {
>  /*
>   * API's called during initialization to setup ACL/EM/LPM rules.
>   */
> -static void
> +void
>  l3fwd_set_rule_ipv4_name(const char *optarg)
>  {
>  	parm_config.rule_ipv4_name = optarg;
>  }
> 
> -static void
> +void
>  l3fwd_set_rule_ipv6_name(const char *optarg)
>  {
>  	parm_config.rule_ipv6_name = optarg;
>  }
> 
> +void
> +l3fwd_set_alg(const char *optarg)
> +{
> +	parm_config.alg = parse_acl_alg(optarg);
> +}
> +
>  /*
>   * Setup lookup methods for forwarding.
>   * Currently exact-match, longest-prefix-match and forwarding information
> @@ -262,6 +292,9 @@ setup_l3fwd_lookup_tables(void)
>  	/* Setup FIB lookup functions. */
>  	else if (lookup_mode == L3FWD_LOOKUP_FIB)
>  		l3fwd_lkp = l3fwd_fib_lkp;
> +	/* Setup ACL lookup functions. */
> +	else if (lookup_mode == L3FWD_LOOKUP_ACL)
> +		l3fwd_lkp = l3fwd_acl_lkp;
>  	/* Setup LPM lookup functions. */
>  	else
>  		l3fwd_lkp = l3fwd_lpm_lkp;
> @@ -357,10 +390,32 @@ init_lcore_rx_queues(void)
>  	return 0;
>  }
> 
> +static int
> +usage_acl_alg(char *buf, size_t sz)
> +{
> +	uint32_t i, n, rc, tn;
> +
> +	n = 0;
> +	tn = 0;
> +	for (i = 0; i < RTE_DIM(acl_alg); i++) {
> +		rc = snprintf(buf + n, sz - n,
> +			i == RTE_DIM(acl_alg) - 1 ? "%s" : "%s|",
> +			acl_alg[i].name);
> +		tn += rc;
> +		if (rc < sz - n)
> +			n += rc;
> +	}
> +
> +	return tn;
> +}
> +
>  /* display usage */
>  static void
>  print_usage(const char *prgname)
>  {
> +	char alg[PATH_MAX];
> +
> +	usage_acl_alg(alg, sizeof(alg));
>  	fprintf(stderr, "%s [EAL options] --"
>  		" -p PORTMASK"
>  		"  --rule_ipv4=FILE"
> @@ -387,7 +442,8 @@ print_usage(const char *prgname)
>  		"  -P : Enable promiscuous mode\n"
>  		"  --lookup: Select the lookup method\n"
>  		"            Default: lpm\n"
> -		"            Accepted: em (Exact Match), lpm (Longest Prefix Match), fib (Forwarding Information Base)\n"
> +		"            Accepted: em (Exact Match), lpm (Longest Prefix Match), fib (Forwarding Information Base),\n"
> +		"                      acl (Access Control List)\n"
>  		"  --config (port,queue,lcore): Rx queue configuration\n"
>  		"  --rx-queue-size NPKTS: Rx queue size in decimal\n"
>  		"            Default: %d\n"
> @@ -416,8 +472,13 @@ print_usage(const char *prgname)
>  		"  -L : Enable longest prefix match, legacy flag please use --lookup=lpm instead\n"
>  		"  --rule_ipv4=FILE: Specify the ipv4 rules entries file.\n"
>  		"                    Each rule occupies one line.\n"
> -		"  --rule_ipv6=FILE: Specify the ipv6 rules entries file.\n\n",
> -		prgname, RTE_TEST_RX_DESC_DEFAULT, RTE_TEST_TX_DESC_DEFAULT);
> +		"                    2 kinds of rules are supported.\n"
> +		"                    One is ACL entry at while line leads with character '%c',\n"
> +		"                    another is route entry at while line leads with character '%c'.\n"
> +		"  --rule_ipv6=FILE: Specify the ipv6 rules entries file.\n"
> +		"  --alg: ACL classify method to use, one of: %s.\n\n",
> +		prgname, RTE_TEST_RX_DESC_DEFAULT, RTE_TEST_TX_DESC_DEFAULT,
> +		ACL_LEAD_CHAR, ROUTE_LEAD_CHAR, alg);
>  }
> 
>  static int
> @@ -632,8 +693,10 @@ parse_lookup(const char *optarg)
>  		lookup_mode = L3FWD_LOOKUP_LPM;
>  	else if (!strcmp(optarg, "fib"))
>  		lookup_mode = L3FWD_LOOKUP_FIB;
> +	else if (!strcmp(optarg, "acl"))
> +		lookup_mode = L3FWD_LOOKUP_ACL;
>  	else {
> -		fprintf(stderr, "Invalid lookup option! Accepted options: em, lpm, fib\n");
> +		fprintf(stderr, "Invalid lookup option! Accepted options: acl, em, lpm, fib\n");
>  		return -1;
>  	}
>  	return 0;
> @@ -667,6 +730,7 @@ static const char short_options[] =
>  #define CMD_LINE_OPT_VECTOR_TMO_NS "event-vector-tmo"
>  #define CMD_LINE_OPT_RULE_IPV4 "rule_ipv4"
>  #define CMD_LINE_OPT_RULE_IPV6 "rule_ipv6"
> +#define CMD_LINE_OPT_ALG "alg"
> 
>  enum {
>  	/* long options mapped to a short option */
> @@ -685,6 +749,7 @@ enum {
>  	CMD_LINE_OPT_PARSE_PTYPE_NUM,
>  	CMD_LINE_OPT_RULE_IPV4_NUM,
>  	CMD_LINE_OPT_RULE_IPV6_NUM,
> +	CMD_LINE_OPT_ALG_NUM,
>  	CMD_LINE_OPT_PARSE_PER_PORT_POOL,
>  	CMD_LINE_OPT_MODE_NUM,
>  	CMD_LINE_OPT_EVENTQ_SYNC_NUM,
> @@ -716,6 +781,7 @@ static const struct option lgopts[] = {
>  	{CMD_LINE_OPT_VECTOR_TMO_NS, 1, 0, CMD_LINE_OPT_VECTOR_TMO_NS_NUM},
>  	{CMD_LINE_OPT_RULE_IPV4,   1, 0, CMD_LINE_OPT_RULE_IPV4_NUM},
>  	{CMD_LINE_OPT_RULE_IPV6,   1, 0, CMD_LINE_OPT_RULE_IPV6_NUM},
> +	{CMD_LINE_OPT_ALG,   1, 0, CMD_LINE_OPT_ALG_NUM},
>  	{NULL, 0, 0, 0}
>  };
> 
> @@ -884,6 +950,9 @@ parse_args(int argc, char **argv)
>  		case CMD_LINE_OPT_RULE_IPV6_NUM:
>  			l3fwd_set_rule_ipv6_name(optarg);
>  			break;
> +		case CMD_LINE_OPT_ALG_NUM:
> +			l3fwd_set_alg(optarg);
> +			break;
>  		default:
>  			print_usage(prgname);
>  			return -1;
> @@ -923,7 +992,7 @@ parse_args(int argc, char **argv)
>  	 * as default match.
>  	 */
>  	if (lookup_mode == L3FWD_LOOKUP_DEFAULT) {
> -		fprintf(stderr, "Neither LPM, EM, or FIB selected, defaulting to LPM\n");
> +		fprintf(stderr, "Neither ACL, LPM, EM, or FIB selected, defaulting to LPM\n");
>  		lookup_mode = L3FWD_LOOKUP_LPM;
>  	}
> 
> @@ -937,6 +1006,12 @@ parse_args(int argc, char **argv)
>  		hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT;
>  	}
> 
> +	/* For ACL, update port config rss hash filter */
> +	if (lookup_mode == L3FWD_LOOKUP_ACL) {
> +		port_conf.rx_adv_conf.rss_conf.rss_hf |=
> +				RTE_ETH_RSS_UDP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_SCTP;
> +	}
> +
>  	if (optind >= 0)
>  		argv[optind-1] = prgname;
> 
> @@ -992,7 +1067,7 @@ init_mem(uint16_t portid, unsigned int nb_mbuf)
>  				printf("Allocated mbuf pool on socket %d\n",
>  					socketid);
> 
> -			/* Setup LPM, EM(f.e Hash) or FIB. But, only once per
> +			/* Setup ACL, LPM, EM(f.e Hash) or FIB. But, only once per
>  			 * available socket.
>  			 */
>  			if (!lkp_per_socket[socketid]) {
> diff --git a/examples/l3fwd/meson.build b/examples/l3fwd/meson.build
> index 0830b3eb31..2fa00e5b74 100644
> --- a/examples/l3fwd/meson.build
> +++ b/examples/l3fwd/meson.build
> @@ -7,8 +7,9 @@
>  # DPDK instance, use 'make'
> 
>  allow_experimental_apis = true
> -deps += ['hash', 'lpm', 'fib', 'eventdev']
> +deps += ['hash', 'lpm', 'fib', 'eventdev', 'acl']
>  sources = files(
> +        'l3fwd_acl.c',
>          'l3fwd_em.c',
>          'l3fwd_event.c',
>          'l3fwd_event_internal_port.c',
> diff --git a/examples/meson.build b/examples/meson.build
> index 268422a257..78de0e1f37 100644
> --- a/examples/meson.build
> +++ b/examples/meson.build
> @@ -32,7 +32,6 @@ all_examples = [
>          'l2fwd-jobstats',
>          'l2fwd-keepalive',
>          'l3fwd',
> -        'l3fwd-acl',
>          'l3fwd-graph',
>          'l3fwd-power',
>          'link_status_interrupt',
> --
> 2.25.1


  reply	other threads:[~2022-04-02 13:53 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-25 15:14 [PATCH v1] " Sean Morrissey
2022-03-29 10:28 ` Bruce Richardson
2022-03-29 10:33 ` Ananyev, Konstantin
2022-03-30 14:04 ` [PATCH v2] " Sean Morrissey
2022-04-02 13:53   ` Ananyev, Konstantin [this message]
2022-04-06 10:29   ` [PATCH v3] " Sean Morrissey
2022-04-06 11:47     ` David Marchand
2022-04-06 12:06     ` [PATCH v4] " Sean Morrissey
2022-04-08 17:26       ` Ananyev, Konstantin
2022-04-08 19:01         ` Morrissey, Sean
2022-04-09 11:58           ` Ananyev, Konstantin
2022-04-11  8:41       ` [PATCH v5] " Sean Morrissey
2022-04-11 11:38         ` Ananyev, Konstantin
2022-04-22  9:57         ` [PATCH v6] " Sean Morrissey
2022-06-08  9:50           ` Thomas Monjalon

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=DM6PR11MB44919647823D7FB9C06F9FEC9AE39@DM6PR11MB4491.namprd11.prod.outlook.com \
    --to=konstantin.ananyev@intel.com \
    --cc=dev@dpdk.org \
    --cc=sean.morrissey@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).