DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd
@ 2017-03-05 19:47 Ravi Kerur
  2017-03-05 19:47 ` [dpdk-dev] [v4 1/3] Merge l3fwd-acl and l3fwd code Ravi Kerur
                   ` (3 more replies)
  0 siblings, 4 replies; 34+ messages in thread
From: Ravi Kerur @ 2017-03-05 19:47 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bruce.richardson, Ravi Kerur

This patchset merges l3fwd-acl and l3fwd code into common directory.
Adds file read options to build LPM and EM tables.

Ravi Kerur (3):
  Merge l3fwd-acl and l3fwd code.
  LPM config file read option.
  EM config file read option.

 examples/l3fwd-acl/Makefile       |   56 -
 examples/l3fwd-acl/main.c         | 2079 -------------------------------------
 examples/l3fwd/Makefile           |    2 +-
 examples/l3fwd/l3fwd.h            |   92 ++
 examples/l3fwd/l3fwd_acl.c        | 1033 ++++++++++++++++++
 examples/l3fwd/l3fwd_acl.h        |  234 +++++
 examples/l3fwd/l3fwd_acl_scalar.h |  182 ++++
 examples/l3fwd/l3fwd_em.c         |  390 +++++--
 examples/l3fwd/l3fwd_lpm.c        |  323 ++++--
 examples/l3fwd/main.c             |  232 +++--
 10 files changed, 2283 insertions(+), 2340 deletions(-)
 delete mode 100644 examples/l3fwd-acl/Makefile
 delete mode 100644 examples/l3fwd-acl/main.c
 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

-- 
2.7.4

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [dpdk-dev] [v4 1/3] Merge l3fwd-acl and l3fwd code.
  2017-03-05 19:47 [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd Ravi Kerur
@ 2017-03-05 19:47 ` Ravi Kerur
  2017-03-08 21:32   ` [dpdk-dev] [PATCH v5 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd Ravi Kerur
  2017-03-05 19:47 ` [dpdk-dev] [v4 2/3] LPM config file read option Ravi Kerur
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 34+ messages in thread
From: Ravi Kerur @ 2017-03-05 19:47 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bruce.richardson, Ravi Kerur

v4:
	> Initialize rss_hf to IP for LPM, EM and ACL.
	> Update rss_hf with l4 in parse_args for ACL.
	> Fix pending checkpatch code indentation warning.

v3:
	> Fix additional checkpatch coding style issues.

v2:
	> Fix checkpatch errors and warnings related to non strings
		greater than 80 characters.
	> MACRO GET_CB_FIELD and strings greater than 80 characters
		warnings are not fixed.

v1:
	l3fwd-acl changes:
		> Merge common init code in l3fwd-acl and l3fwd into
			main.c.
		> Move non-critical inline functions to l3fwd_acl.h.
		> Move critial packet processing inline functions to
			l3fwd_acl_scalar.h
		> Move l3fwd-acl init code to l3fwd_acl.c.
		> Delete l3fwd-acl directory.

	l3fwd changes:
		> Add '-A' as an option for ACL processing.
		> Merge parsing options from l3fwd-acl and l3fwd. Retain
			l3fwd-acl definitions.
		> Move specific setup functions (setup_acl, setup_lpm
			and setup_hash).
	Testing:
		> Compiled successfully for x86_64-native-linuxapp-gcc
			> Tested LPM, EM and ACL basic functionality.

Signed-off-by: Ravi Kerur <rkerur@gmail.com>
---
 examples/l3fwd-acl/Makefile       |   56 -
 examples/l3fwd-acl/main.c         | 2079 -------------------------------------
 examples/l3fwd/Makefile           |    2 +-
 examples/l3fwd/l3fwd.h            |   49 +
 examples/l3fwd/l3fwd_acl.c        | 1064 +++++++++++++++++++
 examples/l3fwd/l3fwd_acl.h        |  263 +++++
 examples/l3fwd/l3fwd_acl_scalar.h |  182 ++++
 examples/l3fwd/l3fwd_em.c         |   14 +-
 examples/l3fwd/l3fwd_lpm.c        |   23 +-
 examples/l3fwd/main.c             |  209 ++--
 10 files changed, 1722 insertions(+), 2219 deletions(-)
 delete mode 100644 examples/l3fwd-acl/Makefile
 delete mode 100644 examples/l3fwd-acl/main.c
 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-acl/Makefile b/examples/l3fwd-acl/Makefile
deleted file mode 100644
index a3473a8..0000000
--- a/examples/l3fwd-acl/Makefile
+++ /dev/null
@@ -1,56 +0,0 @@
-#   BSD LICENSE
-#
-#   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
-#   All rights reserved.
-#
-#   Redistribution and use in source and binary forms, with or without
-#   modification, are permitted provided that the following conditions
-#   are met:
-#
-#     * Redistributions of source code must retain the above copyright
-#       notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above copyright
-#       notice, this list of conditions and the following disclaimer in
-#       the documentation and/or other materials provided with the
-#       distribution.
-#     * Neither the name of Intel Corporation nor the names of its
-#       contributors may be used to endorse or promote products derived
-#       from this software without specific prior written permission.
-#
-#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-ifeq ($(RTE_SDK),)
-$(error "Please define RTE_SDK environment variable")
-endif
-
-# Default target, can be overriden by command line or environment
-RTE_TARGET ?= x86_64-native-linuxapp-gcc
-
-include $(RTE_SDK)/mk/rte.vars.mk
-
-# binary name
-APP = l3fwd-acl
-
-# all source are stored in SRCS-y
-SRCS-y := main.c
-
-CFLAGS += -O3
-CFLAGS += $(WERROR_FLAGS)
-
-# workaround for a gcc bug with noreturn attribute
-# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
-ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y)
-CFLAGS_main.o += -Wno-return-type
-endif
-
-include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/l3fwd-acl/main.c b/examples/l3fwd-acl/main.c
deleted file mode 100644
index 3cfbb40..0000000
--- a/examples/l3fwd-acl/main.c
+++ /dev/null
@@ -1,2079 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <sys/types.h>
-#include <string.h>
-#include <sys/queue.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <getopt.h>
-
-#include <rte_common.h>
-#include <rte_byteorder.h>
-#include <rte_log.h>
-#include <rte_memory.h>
-#include <rte_memcpy.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_per_lcore.h>
-#include <rte_launch.h>
-#include <rte_atomic.h>
-#include <rte_cycles.h>
-#include <rte_prefetch.h>
-#include <rte_lcore.h>
-#include <rte_per_lcore.h>
-#include <rte_branch_prediction.h>
-#include <rte_interrupts.h>
-#include <rte_pci.h>
-#include <rte_random.h>
-#include <rte_debug.h>
-#include <rte_ether.h>
-#include <rte_ethdev.h>
-#include <rte_mempool.h>
-#include <rte_mbuf.h>
-#include <rte_ip.h>
-#include <rte_tcp.h>
-#include <rte_udp.h>
-#include <rte_string_fns.h>
-#include <rte_acl.h>
-
-#if RTE_LOG_LEVEL >= RTE_LOG_DEBUG
-#define L3FWDACL_DEBUG
-#endif
-#define DO_RFC_1812_CHECKS
-
-#define RTE_LOGTYPE_L3FWD RTE_LOGTYPE_USER1
-
-#define MAX_JUMBO_PKT_LEN  9600
-
-#define MEMPOOL_CACHE_SIZE 256
-
-/*
- * This expression is used to calculate the number of mbufs needed
- * depending on user input, taking into account memory for rx and tx hardware
- * rings, cache per lcore and mtable per port per lcore.
- * RTE_MAX is used to ensure that NB_MBUF never goes below a
- * minimum value of 8192
- */
-
-#define NB_MBUF	RTE_MAX(\
-	(nb_ports * nb_rx_queue*RTE_TEST_RX_DESC_DEFAULT +	\
-	nb_ports * nb_lcores * MAX_PKT_BURST +			\
-	nb_ports * n_tx_queue * RTE_TEST_TX_DESC_DEFAULT +	\
-	nb_lcores * MEMPOOL_CACHE_SIZE),			\
-	(unsigned)8192)
-
-#define MAX_PKT_BURST 32
-#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */
-
-#define NB_SOCKETS 8
-
-/* Configure how many packets ahead to prefetch, when reading packets */
-#define PREFETCH_OFFSET	3
-
-/*
- * Configurable number of RX/TX ring descriptors
- */
-#define RTE_TEST_RX_DESC_DEFAULT 128
-#define RTE_TEST_TX_DESC_DEFAULT 512
-static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
-static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
-
-/* ethernet addresses of ports */
-static struct ether_addr ports_eth_addr[RTE_MAX_ETHPORTS];
-
-/* mask of enabled ports */
-static uint32_t enabled_port_mask;
-static int promiscuous_on; /**< Ports set in promiscuous mode off by default. */
-static int numa_on = 1; /**< NUMA is enabled by default. */
-
-struct lcore_rx_queue {
-	uint8_t port_id;
-	uint8_t queue_id;
-} __rte_cache_aligned;
-
-#define MAX_RX_QUEUE_PER_LCORE 16
-#define MAX_TX_QUEUE_PER_PORT RTE_MAX_ETHPORTS
-#define MAX_RX_QUEUE_PER_PORT 128
-
-#define MAX_LCORE_PARAMS 1024
-struct lcore_params {
-	uint8_t port_id;
-	uint8_t queue_id;
-	uint8_t lcore_id;
-} __rte_cache_aligned;
-
-static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS];
-static struct lcore_params lcore_params_array_default[] = {
-	{0, 0, 2},
-	{0, 1, 2},
-	{0, 2, 2},
-	{1, 0, 2},
-	{1, 1, 2},
-	{1, 2, 2},
-	{2, 0, 2},
-	{3, 0, 3},
-	{3, 1, 3},
-};
-
-static struct lcore_params *lcore_params = lcore_params_array_default;
-static uint16_t nb_lcore_params = sizeof(lcore_params_array_default) /
-				sizeof(lcore_params_array_default[0]);
-
-static struct rte_eth_conf port_conf = {
-	.rxmode = {
-		.mq_mode	= ETH_MQ_RX_RSS,
-		.max_rx_pkt_len = ETHER_MAX_LEN,
-		.split_hdr_size = 0,
-		.header_split   = 0, /**< Header Split disabled */
-		.hw_ip_checksum = 1, /**< IP checksum offload enabled */
-		.hw_vlan_filter = 0, /**< VLAN filtering disabled */
-		.jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
-		.hw_strip_crc   = 0, /**< CRC stripped by hardware */
-	},
-	.rx_adv_conf = {
-		.rss_conf = {
-			.rss_key = NULL,
-			.rss_hf = ETH_RSS_IP | ETH_RSS_UDP |
-				ETH_RSS_TCP | ETH_RSS_SCTP,
-		},
-	},
-	.txmode = {
-		.mq_mode = ETH_MQ_TX_NONE,
-	},
-};
-
-static struct rte_mempool *pktmbuf_pool[NB_SOCKETS];
-
-/***********************start of ACL part******************************/
-#ifdef DO_RFC_1812_CHECKS
-static inline int
-is_valid_ipv4_pkt(struct ipv4_hdr *pkt, uint32_t link_len);
-#endif
-static inline void
-send_single_packet(struct rte_mbuf *m, uint8_t port);
-
-#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_LEAD_CHAR		('@')
-#define ROUTE_LEAD_CHAR		('R')
-#define COMMENT_LEAD_CHAR	('#')
-#define OPTION_CONFIG		"config"
-#define OPTION_NONUMA		"no-numa"
-#define OPTION_ENBJMO		"enable-jumbo"
-#define OPTION_RULE_IPV4	"rule_ipv4"
-#define OPTION_RULE_IPV6	"rule_ipv6"
-#define OPTION_SCALAR		"scalar"
-#define ACL_DENY_SIGNATURE	0xf0000000
-#define RTE_LOGTYPE_L3FWDACL	RTE_LOGTYPE_USER3
-#define acl_log(format, ...)	RTE_LOG(ERR, L3FWDACL, format, ##__VA_ARGS__)
-#define uint32_t_to_char(ip, a, b, c, d) do {\
-		*a = (unsigned char)(ip >> 24 & 0xff);\
-		*b = (unsigned char)(ip >> 16 & 0xff);\
-		*c = (unsigned char)(ip >> 8 & 0xff);\
-		*d = (unsigned char)(ip & 0xff);\
-	} while (0)
-#define OFF_ETHHEAD	(sizeof(struct ether_hdr))
-#define OFF_IPV42PROTO (offsetof(struct ipv4_hdr, next_proto_id))
-#define OFF_IPV62PROTO (offsetof(struct 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)
-
-#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
-	unsigned long val;                                      \
-	char *end;                                              \
-	errno = 0;                                              \
-	val = strtoul((in), &end, (base));                      \
-	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
-		return -EINVAL;                               \
-	(fd) = (typeof(fd))val;                                 \
-	(in) = end + 1;                                         \
-} while (0)
-
-/*
-  * 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
-
-/*
- * 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 ipv4_hdr, src_addr) -
-			offsetof(struct 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 ipv4_hdr, dst_addr) -
-			offsetof(struct 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 ipv4_hdr) -
-			offsetof(struct 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 ipv4_hdr) -
-			offsetof(struct ipv4_hdr, next_proto_id) +
-			sizeof(uint16_t),
-	},
-};
-
-#define	IPV6_ADDR_LEN	16
-#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
-#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_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 ipv6_hdr, src_addr) -
-			offsetof(struct 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 ipv6_hdr, src_addr) -
-			offsetof(struct 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 ipv6_hdr, src_addr) -
-			offsetof(struct 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 ipv6_hdr, src_addr) -
-			offsetof(struct 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 ipv6_hdr, dst_addr)
-				- offsetof(struct 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 ipv6_hdr, dst_addr) -
-			offsetof(struct 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 ipv6_hdr, dst_addr) -
-			offsetof(struct 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 ipv6_hdr, dst_addr) -
-			offsetof(struct 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 ipv6_hdr) -
-			offsetof(struct 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 ipv6_hdr) -
-			offsetof(struct 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 {
-	char mapped[NB_SOCKETS];
-	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 struct{
-	const char *rule_ipv4_name;
-	const char *rule_ipv6_name;
-	int scalar;
-} parm_config;
-
-const char cb_port_delim[] = ":";
-
-static inline void
-print_one_ipv4_rule(struct acl4_rule *rule, int extra)
-{
-	unsigned char a, b, c, d;
-
-	uint32_t_to_char(rule->field[SRC_FIELD_IPV4].value.u32,
-			&a, &b, &c, &d);
-	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
-			rule->field[SRC_FIELD_IPV4].mask_range.u32);
-	uint32_t_to_char(rule->field[DST_FIELD_IPV4].value.u32,
-			&a, &b, &c, &d);
-	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
-			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 inline void
-print_one_ipv6_rule(struct acl6_rule *rule, int extra)
-{
-	unsigned char a, b, c, d;
-
-	uint32_t_to_char(rule->field[SRC1_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[SRC2_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[SRC3_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[SRC4_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
-			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);
-
-	uint32_t_to_char(rule->field[DST1_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[DST2_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[DST3_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[DST4_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
-			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);
-}
-
-/* Bypass comment and empty lines */
-static inline int
-is_bypass_line(char *buff)
-{
-	int i = 0;
-
-	/* comment line */
-	if (buff[0] == COMMENT_LEAD_CHAR)
-		return 1;
-	/* empty line */
-	while (buff[i] != '\0') {
-		if (!isspace(buff[i]))
-			return 0;
-		i++;
-	}
-	return 1;
-}
-
-#ifdef L3FWDACL_DEBUG
-static inline void
-dump_acl4_rule(struct rte_mbuf *m, uint32_t sig)
-{
-	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
-	unsigned char a, b, c, d;
-	struct ipv4_hdr *ipv4_hdr = rte_pktmbuf_mtod_offset(m,
-							    struct ipv4_hdr *,
-							    sizeof(struct ether_hdr));
-
-	uint32_t_to_char(rte_bswap32(ipv4_hdr->src_addr), &a, &b, &c, &d);
-	printf("Packet Src:%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
-	uint32_t_to_char(rte_bswap32(ipv4_hdr->dst_addr), &a, &b, &c, &d);
-	printf("Dst:%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
-
-	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)
-{
-	unsigned i;
-	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
-	struct ipv6_hdr *ipv6_hdr = rte_pktmbuf_mtod_offset(m,
-							    struct ipv6_hdr *,
-							    sizeof(struct ether_hdr));
-
-	printf("Packet Src");
-	for (i = 0; i < RTE_DIM(ipv6_hdr->src_addr); i += sizeof(uint16_t))
-		printf(":%.2x%.2x",
-			ipv6_hdr->src_addr[i], ipv6_hdr->src_addr[i + 1]);
-
-	printf("\nDst");
-	for (i = 0; i < RTE_DIM(ipv6_hdr->dst_addr); i += sizeof(uint16_t))
-		printf(":%.2x%.2x",
-			ipv6_hdr->dst_addr[i], ipv6_hdr->dst_addr[i + 1]);
-
-	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");
-	}
-}
-
-#ifdef DO_RFC_1812_CHECKS
-static inline void
-prepare_one_packet(struct rte_mbuf **pkts_in, struct acl_search_t *acl,
-	int index)
-{
-	struct 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 ipv4_hdr *,
-						   sizeof(struct 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
-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 */
-
-static inline void
-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 *));
-		prepare_one_packet(pkts_in, acl, i);
-	}
-
-	/* Process left packets */
-	for (; i < nb_rx; i++)
-		prepare_one_packet(pkts_in, acl, i);
-}
-
-static inline void
-send_one_packet(struct rte_mbuf *m, uint32_t res)
-{
-	if (likely((res & ACL_DENY_SIGNATURE) == 0 && res != 0)) {
-		/* forward packets */
-		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
-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 *));
-		send_one_packet(m[i], res[i]);
-	}
-
-	/* Process left packets */
-	for (; i < num; i++)
-		send_one_packet(m[i], res[i]);
-}
-
-/*
- * Parses IPV6 address, exepcts the following format:
- * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X - is a hexedecimal digit).
- */
-static int
-parse_ipv6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
-	char dlm)
-{
-	uint32_t addr[IPV6_ADDR_U16];
-
-	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
-
-	*end = in;
-
-	v[0] = (addr[0] << 16) + addr[1];
-	v[1] = (addr[2] << 16) + addr[3];
-	v[2] = (addr[4] << 16) + addr[5];
-	v[3] = (addr[6] << 16) + addr[7];
-
-	return 0;
-}
-
-static int
-parse_ipv6_net(const char *in, struct rte_acl_field field[4])
-{
-	int32_t rc;
-	const char *mp;
-	uint32_t i, m, v[4];
-	const uint32_t nbu32 = sizeof(uint32_t) * CHAR_BIT;
-
-	/* get address. */
-	rc = parse_ipv6_addr(in, &mp, v, '/');
-	if (rc != 0)
-		return rc;
-
-	/* get mask. */
-	GET_CB_FIELD(mp, m, 0, CHAR_BIT * sizeof(v), 0);
-
-	/* 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(const char *in, uint32_t *addr, uint32_t *mask_len)
-{
-	uint8_t a, b, c, d, m;
-
-	GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
-	GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
-	GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
-	GET_CB_FIELD(in, d, 0, UINT8_MAX, '/');
-	GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
-
-	addr[0] = IPv4(a, b, c, d);
-	mask_len[0] = m;
-
-	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
-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;
-
-	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 (0 == route_num)
-		rte_exit(EXIT_FAILURE, "Not find any route entries in %s!\n",
-				rule_path);
-
-	fseek(fh, 0, SEEK_SET);
-
-	acl_rules = calloc(acl_num, rule_size);
-
-	if (NULL == acl_rules)
-		rte_exit(EXIT_FAILURE, "%s: failed to malloc memory\n",
-			__func__);
-
-	route_rules = calloc(route_num, rule_size);
-
-	if (NULL == route_rules)
-		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 void
-dump_acl_config(void)
-{
-	printf("ACL option are:\n");
-	printf(OPTION_RULE_IPV4": %s\n", parm_config.rule_ipv4_name);
-	printf(OPTION_RULE_IPV6": %s\n", parm_config.rule_ipv6_name);
-	printf(OPTION_SCALAR": %d\n", parm_config.scalar);
-}
-
-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;
-}
-
-static struct rte_acl_ctx*
-setup_acl(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;
-
-	if ((context = rte_acl_create(&acl_param)) == NULL)
-		rte_exit(EXIT_FAILURE, "Failed to create ACL context\n");
-
-	if (parm_config.scalar && rte_acl_set_ctx_classify(context,
-			RTE_ACL_CLASSIFY_SCALAR) != 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;
-}
-
-static int
-app_acl_init(void)
-{
-	unsigned lcore_id;
-	unsigned int i;
-	int socketid;
-	struct rte_acl_rule *acl_base_ipv4, *route_base_ipv4,
-		*acl_base_ipv6, *route_base_ipv6;
-	unsigned int acl_num_ipv4 = 0, route_num_ipv4 = 0,
-		acl_num_ipv6 = 0, route_num_ipv6 = 0;
-
-	if (check_acl_config() != 0)
-		rte_exit(EXIT_FAILURE, "Failed to get valid ACL options\n");
-
-	dump_acl_config();
-
-	/* Load  rules from the input file */
-	if (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)
-		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
-
-	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);
-
-	if (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)
-		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
-
-	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 (!numa_on)
-		acl_config.mapped[0] = 1;
-	else {
-		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-			if (rte_lcore_is_enabled(lcore_id) == 0)
-				continue;
-
-			socketid = rte_lcore_to_socket_id(lcore_id);
-			if (socketid >= NB_SOCKETS) {
-				acl_log("Socket %d of lcore %u is out "
-					"of range %d\n",
-					socketid, lcore_id, NB_SOCKETS);
-				free(route_base_ipv4);
-				free(route_base_ipv6);
-				free(acl_base_ipv4);
-				free(acl_base_ipv6);
-				return -1;
-			}
-
-			acl_config.mapped[socketid] = 1;
-		}
-	}
-
-	for (i = 0; i < NB_SOCKETS; i++) {
-		if (acl_config.mapped[i]) {
-			acl_config.acx_ipv4[i] = setup_acl(route_base_ipv4,
-				acl_base_ipv4, route_num_ipv4, acl_num_ipv4,
-				0, i);
-
-			acl_config.acx_ipv6[i] = setup_acl(route_base_ipv6,
-				acl_base_ipv6, route_num_ipv6, acl_num_ipv6,
-				1, i);
-		}
-	}
-
-	free(route_base_ipv4);
-	free(route_base_ipv6);
-
-#ifdef L3FWDACL_DEBUG
-	acl_config.rule_ipv4 = (struct acl4_rule *)acl_base_ipv4;
-	acl_config.rule_ipv6 = (struct acl6_rule *)acl_base_ipv6;
-#else
-	free(acl_base_ipv4);
-	free(acl_base_ipv6);
-#endif
-
-	return 0;
-}
-
-/***********************end of ACL part******************************/
-
-struct lcore_conf {
-	uint16_t n_rx_queue;
-	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
-	uint16_t n_tx_port;
-	uint16_t tx_port_id[RTE_MAX_ETHPORTS];
-	uint16_t tx_queue_id[RTE_MAX_ETHPORTS];
-	struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS];
-} __rte_cache_aligned;
-
-static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
-
-/* Enqueue a single packet, and send burst if queue is filled */
-static inline void
-send_single_packet(struct rte_mbuf *m, uint8_t port)
-{
-	uint32_t lcore_id;
-	struct lcore_conf *qconf;
-
-	lcore_id = rte_lcore_id();
-
-	qconf = &lcore_conf[lcore_id];
-	rte_eth_tx_buffer(port, qconf->tx_queue_id[port],
-			qconf->tx_buffer[port], m);
-}
-
-#ifdef DO_RFC_1812_CHECKS
-static inline int
-is_valid_ipv4_pkt(struct ipv4_hdr *pkt, uint32_t link_len)
-{
-	/* From http://www.rfc-editor.org/rfc/rfc1812.txt section 5.2.2 */
-	/*
-	 * 1. The packet length reported by the Link Layer must be large
-	 * enough to hold the minimum length legal IP datagram (20 bytes).
-	 */
-	if (link_len < sizeof(struct ipv4_hdr))
-		return -1;
-
-	/* 2. The IP checksum must be correct. */
-	/* this is checked in H/W */
-
-	/*
-	 * 3. The IP version number must be 4. If the version number is not 4
-	 * then the packet may be another version of IP, such as IPng or
-	 * ST-II.
-	 */
-	if (((pkt->version_ihl) >> 4) != 4)
-		return -3;
-	/*
-	 * 4. The IP header length field must be large enough to hold the
-	 * minimum length legal IP datagram (20 bytes = 5 words).
-	 */
-	if ((pkt->version_ihl & 0xf) < 5)
-		return -4;
-
-	/*
-	 * 5. The IP total length field must be large enough to hold the IP
-	 * datagram header, whose length is specified in the IP header length
-	 * field.
-	 */
-	if (rte_cpu_to_be_16(pkt->total_length) < sizeof(struct ipv4_hdr))
-		return -5;
-
-	return 0;
-}
-#endif
-
-/* main processing loop */
-static int
-main_loop(__attribute__((unused)) void *dummy)
-{
-	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
-	unsigned lcore_id;
-	uint64_t prev_tsc, diff_tsc, cur_tsc;
-	int i, nb_rx;
-	uint8_t portid, 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=%hhu rxqueueid=%hhu\n",
-			lcore_id, portid, queueid);
-	}
-
-	while (1) {
-
-		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];
-				rte_eth_tx_buffer_flush(portid,
-						qconf->tx_queue_id[portid],
-						qconf->tx_buffer[portid]);
-			}
-			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;
-
-				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);
-
-					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);
-
-					send_packets(acl_search.m_ipv6,
-						acl_search.res_ipv6,
-						acl_search.num_ipv6);
-				}
-			}
-		}
-	}
-}
-
-static int
-check_lcore_params(void)
-{
-	uint8_t queue, lcore;
-	uint16_t i;
-	int socketid;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		queue = lcore_params[i].queue_id;
-		if (queue >= MAX_RX_QUEUE_PER_PORT) {
-			printf("invalid queue number: %hhu\n", queue);
-			return -1;
-		}
-		lcore = lcore_params[i].lcore_id;
-		if (!rte_lcore_is_enabled(lcore)) {
-			printf("error: lcore %hhu is not enabled in "
-				"lcore mask\n", lcore);
-			return -1;
-		}
-		socketid = rte_lcore_to_socket_id(lcore);
-		if (socketid != 0 && numa_on == 0) {
-			printf("warning: lcore %hhu is on socket %d "
-				"with numa off\n",
-				lcore, socketid);
-		}
-	}
-	return 0;
-}
-
-static int
-check_port_config(const unsigned nb_ports)
-{
-	unsigned portid;
-	uint16_t i;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		portid = lcore_params[i].port_id;
-
-		if ((enabled_port_mask & (1 << portid)) == 0) {
-			printf("port %u is not enabled in port mask\n", portid);
-			return -1;
-		}
-		if (portid >= nb_ports) {
-			printf("port %u is not present on the board\n", portid);
-			return -1;
-		}
-	}
-	return 0;
-}
-
-static uint8_t
-get_port_n_rx_queues(const uint8_t port)
-{
-	int queue = -1;
-	uint16_t i;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		if (lcore_params[i].port_id == port &&
-				lcore_params[i].queue_id > queue)
-			queue = lcore_params[i].queue_id;
-	}
-	return (uint8_t)(++queue);
-}
-
-static int
-init_lcore_rx_queues(void)
-{
-	uint16_t i, nb_rx_queue;
-	uint8_t lcore;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		lcore = lcore_params[i].lcore_id;
-		nb_rx_queue = lcore_conf[lcore].n_rx_queue;
-		if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
-			printf("error: too many queues (%u) for lcore: %u\n",
-				(unsigned)nb_rx_queue + 1, (unsigned)lcore);
-			return -1;
-		} else {
-			lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
-				lcore_params[i].port_id;
-			lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
-				lcore_params[i].queue_id;
-			lcore_conf[lcore].n_rx_queue++;
-		}
-	}
-	return 0;
-}
-
-/* display usage */
-static void
-print_usage(const char *prgname)
-{
-	printf("%s [EAL options] -- -p PORTMASK -P"
-		"--"OPTION_RULE_IPV4"=FILE"
-		"--"OPTION_RULE_IPV6"=FILE"
-		"  [--"OPTION_CONFIG" (port,queue,lcore)[,(port,queue,lcore]]"
-		"  [--"OPTION_ENBJMO" [--max-pkt-len PKTLEN]]\n"
-		"  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
-		"  -P : enable promiscuous mode\n"
-		"  --"OPTION_CONFIG": (port,queue,lcore): "
-		"rx queues configuration\n"
-		"  --"OPTION_NONUMA": optional, disable numa awareness\n"
-		"  --"OPTION_ENBJMO": enable jumbo frame"
-		" which max packet len is PKTLEN in decimal (64-9600)\n"
-		"  --"OPTION_RULE_IPV4"=FILE: specify the ipv4 rules entries "
-		"file. "
-		"Each rule occupy one line. "
-		"2 kinds of rules are supported. "
-		"One is ACL entry at while line leads with character '%c', "
-		"another is route entry at while line leads with "
-		"character '%c'.\n"
-		"  --"OPTION_RULE_IPV6"=FILE: specify the ipv6 rules "
-		"entries file.\n"
-		"  --"OPTION_SCALAR": Use scalar function to do lookup\n",
-		prgname, ACL_LEAD_CHAR, ROUTE_LEAD_CHAR);
-}
-
-static int
-parse_max_pkt_len(const char *pktlen)
-{
-	char *end = NULL;
-	unsigned long len;
-
-	/* parse decimal string */
-	len = strtoul(pktlen, &end, 10);
-	if ((pktlen[0] == '\0') || (end == NULL) || (*end != '\0'))
-		return -1;
-
-	if (len == 0)
-		return -1;
-
-	return len;
-}
-
-static int
-parse_portmask(const char *portmask)
-{
-	char *end = NULL;
-	unsigned long pm;
-
-	/* parse hexadecimal string */
-	pm = strtoul(portmask, &end, 16);
-	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
-		return -1;
-
-	if (pm == 0)
-		return -1;
-
-	return pm;
-}
-
-static int
-parse_config(const char *q_arg)
-{
-	char s[256];
-	const char *p, *p0 = q_arg;
-	char *end;
-	enum fieldnames {
-		FLD_PORT = 0,
-		FLD_QUEUE,
-		FLD_LCORE,
-		_NUM_FLD
-	};
-	unsigned long int_fld[_NUM_FLD];
-	char *str_fld[_NUM_FLD];
-	int i;
-	unsigned size;
-
-	nb_lcore_params = 0;
-
-	while ((p = strchr(p0, '(')) != NULL) {
-		++p;
-		if ((p0 = strchr(p, ')')) == NULL)
-			return -1;
-
-		size = p0 - p;
-		if (size >= sizeof(s))
-			return -1;
-
-		snprintf(s, sizeof(s), "%.*s", size, p);
-		if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') !=
-				_NUM_FLD)
-			return -1;
-		for (i = 0; i < _NUM_FLD; i++) {
-			errno = 0;
-			int_fld[i] = strtoul(str_fld[i], &end, 0);
-			if (errno != 0 || end == str_fld[i] || int_fld[i] > 255)
-				return -1;
-		}
-		if (nb_lcore_params >= MAX_LCORE_PARAMS) {
-			printf("exceeded max number of lcore params: %hu\n",
-				nb_lcore_params);
-			return -1;
-		}
-		lcore_params_array[nb_lcore_params].port_id =
-			(uint8_t)int_fld[FLD_PORT];
-		lcore_params_array[nb_lcore_params].queue_id =
-			(uint8_t)int_fld[FLD_QUEUE];
-		lcore_params_array[nb_lcore_params].lcore_id =
-			(uint8_t)int_fld[FLD_LCORE];
-		++nb_lcore_params;
-	}
-	lcore_params = lcore_params_array;
-	return 0;
-}
-
-/* Parse the argument given in the command line of the application */
-static int
-parse_args(int argc, char **argv)
-{
-	int opt, ret;
-	char **argvopt;
-	int option_index;
-	char *prgname = argv[0];
-	static struct option lgopts[] = {
-		{OPTION_CONFIG, 1, 0, 0},
-		{OPTION_NONUMA, 0, 0, 0},
-		{OPTION_ENBJMO, 0, 0, 0},
-		{OPTION_RULE_IPV4, 1, 0, 0},
-		{OPTION_RULE_IPV6, 1, 0, 0},
-		{OPTION_SCALAR, 0, 0, 0},
-		{NULL, 0, 0, 0}
-	};
-
-	argvopt = argv;
-
-	while ((opt = getopt_long(argc, argvopt, "p:P",
-				lgopts, &option_index)) != EOF) {
-
-		switch (opt) {
-		/* portmask */
-		case 'p':
-			enabled_port_mask = parse_portmask(optarg);
-			if (enabled_port_mask == 0) {
-				printf("invalid portmask\n");
-				print_usage(prgname);
-				return -1;
-			}
-			break;
-		case 'P':
-			printf("Promiscuous mode selected\n");
-			promiscuous_on = 1;
-			break;
-
-		/* long options */
-		case 0:
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_CONFIG,
-					sizeof(OPTION_CONFIG))) {
-				ret = parse_config(optarg);
-				if (ret) {
-					printf("invalid config\n");
-					print_usage(prgname);
-					return -1;
-				}
-			}
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_NONUMA,
-					sizeof(OPTION_NONUMA))) {
-				printf("numa is disabled\n");
-				numa_on = 0;
-			}
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_ENBJMO, sizeof(OPTION_ENBJMO))) {
-				struct option lenopts = {
-					"max-pkt-len",
-					required_argument,
-					0,
-					0
-				};
-
-				printf("jumbo frame is enabled\n");
-				port_conf.rxmode.jumbo_frame = 1;
-
-				/*
-				 * if no max-pkt-len set, then use the
-				 * default value ETHER_MAX_LEN
-				 */
-				if (0 == getopt_long(argc, argvopt, "",
-						&lenopts, &option_index)) {
-					ret = parse_max_pkt_len(optarg);
-					if ((ret < 64) ||
-						(ret > MAX_JUMBO_PKT_LEN)) {
-						printf("invalid packet "
-							"length\n");
-						print_usage(prgname);
-						return -1;
-					}
-					port_conf.rxmode.max_rx_pkt_len = ret;
-				}
-				printf("set jumbo frame max packet length "
-					"to %u\n",
-					(unsigned int)
-					port_conf.rxmode.max_rx_pkt_len);
-			}
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_RULE_IPV4,
-					sizeof(OPTION_RULE_IPV4)))
-				parm_config.rule_ipv4_name = optarg;
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_RULE_IPV6,
-					sizeof(OPTION_RULE_IPV6))) {
-				parm_config.rule_ipv6_name = optarg;
-			}
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_SCALAR, sizeof(OPTION_SCALAR)))
-				parm_config.scalar = 1;
-
-
-			break;
-
-		default:
-			print_usage(prgname);
-			return -1;
-		}
-	}
-
-	if (optind >= 0)
-		argv[optind-1] = prgname;
-
-	ret = optind-1;
-	optind = 0; /* reset getopt lib */
-	return ret;
-}
-
-static void
-print_ethaddr(const char *name, const struct ether_addr *eth_addr)
-{
-	char buf[ETHER_ADDR_FMT_SIZE];
-	ether_format_addr(buf, ETHER_ADDR_FMT_SIZE, eth_addr);
-	printf("%s%s", name, buf);
-}
-
-static int
-init_mem(unsigned nb_mbuf)
-{
-	int socketid;
-	unsigned lcore_id;
-	char s[64];
-
-	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-		if (rte_lcore_is_enabled(lcore_id) == 0)
-			continue;
-
-		if (numa_on)
-			socketid = rte_lcore_to_socket_id(lcore_id);
-		else
-			socketid = 0;
-
-		if (socketid >= NB_SOCKETS) {
-			rte_exit(EXIT_FAILURE,
-				"Socket %d of lcore %u is out of range %d\n",
-				socketid, lcore_id, NB_SOCKETS);
-		}
-		if (pktmbuf_pool[socketid] == NULL) {
-			snprintf(s, sizeof(s), "mbuf_pool_%d", socketid);
-			pktmbuf_pool[socketid] =
-				rte_pktmbuf_pool_create(s, nb_mbuf,
-					MEMPOOL_CACHE_SIZE, 0,
-					RTE_MBUF_DEFAULT_BUF_SIZE,
-					socketid);
-			if (pktmbuf_pool[socketid] == NULL)
-				rte_exit(EXIT_FAILURE,
-					"Cannot init mbuf pool on socket %d\n",
-					socketid);
-			else
-				printf("Allocated mbuf pool on socket %d\n",
-					socketid);
-		}
-	}
-	return 0;
-}
-
-/* Check the link status of all ports in up to 9s, and print them finally */
-static void
-check_all_ports_link_status(uint8_t port_num, uint32_t port_mask)
-{
-#define CHECK_INTERVAL 100 /* 100ms */
-#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
-	uint8_t portid, count, all_ports_up, print_flag = 0;
-	struct rte_eth_link link;
-
-	printf("\nChecking link status");
-	fflush(stdout);
-	for (count = 0; count <= MAX_CHECK_TIME; count++) {
-		all_ports_up = 1;
-		for (portid = 0; portid < port_num; portid++) {
-			if ((port_mask & (1 << portid)) == 0)
-				continue;
-			memset(&link, 0, sizeof(link));
-			rte_eth_link_get_nowait(portid, &link);
-			/* print link status if flag set */
-			if (print_flag == 1) {
-				if (link.link_status)
-					printf("Port %d Link Up - speed %u "
-						"Mbps - %s\n", (uint8_t)portid,
-						(unsigned)link.link_speed,
-				(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
-					("full-duplex") : ("half-duplex\n"));
-				else
-					printf("Port %d Link Down\n",
-						(uint8_t)portid);
-				continue;
-			}
-			/* clear all_ports_up flag if any link down */
-			if (link.link_status == ETH_LINK_DOWN) {
-				all_ports_up = 0;
-				break;
-			}
-		}
-		/* after finally printing all link status, get out */
-		if (print_flag == 1)
-			break;
-
-		if (all_ports_up == 0) {
-			printf(".");
-			fflush(stdout);
-			rte_delay_ms(CHECK_INTERVAL);
-		}
-
-		/* set the print_flag if all ports up or timeout */
-		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
-			print_flag = 1;
-			printf("done\n");
-		}
-	}
-}
-
-int
-main(int argc, char **argv)
-{
-	struct lcore_conf *qconf;
-	struct rte_eth_dev_info dev_info;
-	struct rte_eth_txconf *txconf;
-	int ret;
-	unsigned nb_ports;
-	uint16_t queueid;
-	unsigned lcore_id;
-	uint32_t n_tx_queue, nb_lcores;
-	uint8_t portid, nb_rx_queue, queue, socketid;
-
-	/* init EAL */
-	ret = rte_eal_init(argc, argv);
-	if (ret < 0)
-		rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
-	argc -= ret;
-	argv += ret;
-
-	/* parse application arguments (after the EAL ones) */
-	ret = parse_args(argc, argv);
-	if (ret < 0)
-		rte_exit(EXIT_FAILURE, "Invalid L3FWD parameters\n");
-
-	if (check_lcore_params() < 0)
-		rte_exit(EXIT_FAILURE, "check_lcore_params failed\n");
-
-	ret = init_lcore_rx_queues();
-	if (ret < 0)
-		rte_exit(EXIT_FAILURE, "init_lcore_rx_queues failed\n");
-
-	nb_ports = rte_eth_dev_count();
-
-	if (check_port_config(nb_ports) < 0)
-		rte_exit(EXIT_FAILURE, "check_port_config failed\n");
-
-	/* Add ACL rules and route entries, build trie */
-	if (app_acl_init() < 0)
-		rte_exit(EXIT_FAILURE, "app_acl_init failed\n");
-
-	nb_lcores = rte_lcore_count();
-
-	/* initialize all ports */
-	for (portid = 0; portid < nb_ports; portid++) {
-		/* skip ports that are not enabled */
-		if ((enabled_port_mask & (1 << portid)) == 0) {
-			printf("\nSkipping disabled port %d\n", portid);
-			continue;
-		}
-
-		/* init port */
-		printf("Initializing port %d ... ", portid);
-		fflush(stdout);
-
-		nb_rx_queue = get_port_n_rx_queues(portid);
-		n_tx_queue = nb_lcores;
-		if (n_tx_queue > MAX_TX_QUEUE_PER_PORT)
-			n_tx_queue = MAX_TX_QUEUE_PER_PORT;
-		printf("Creating queues: nb_rxq=%d nb_txq=%u... ",
-			nb_rx_queue, (unsigned)n_tx_queue);
-		ret = rte_eth_dev_configure(portid, nb_rx_queue,
-					(uint16_t)n_tx_queue, &port_conf);
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE,
-				"Cannot configure device: err=%d, port=%d\n",
-				ret, portid);
-
-		rte_eth_macaddr_get(portid, &ports_eth_addr[portid]);
-		print_ethaddr(" Address:", &ports_eth_addr[portid]);
-		printf(", ");
-
-		/* init memory */
-		ret = init_mem(NB_MBUF);
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "init_mem failed\n");
-
-		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-			if (rte_lcore_is_enabled(lcore_id) == 0)
-				continue;
-
-			/* Initialize TX buffers */
-			qconf = &lcore_conf[lcore_id];
-			qconf->tx_buffer[portid] = rte_zmalloc_socket("tx_buffer",
-					RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0,
-					rte_eth_dev_socket_id(portid));
-			if (qconf->tx_buffer[portid] == NULL)
-				rte_exit(EXIT_FAILURE, "Can't allocate tx buffer for port %u\n",
-						(unsigned) portid);
-
-			rte_eth_tx_buffer_init(qconf->tx_buffer[portid], MAX_PKT_BURST);
-		}
-
-		/* init one TX queue per couple (lcore,port) */
-		queueid = 0;
-		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-			if (rte_lcore_is_enabled(lcore_id) == 0)
-				continue;
-
-			if (numa_on)
-				socketid = (uint8_t)
-					rte_lcore_to_socket_id(lcore_id);
-			else
-				socketid = 0;
-
-			printf("txq=%u,%d,%d ", lcore_id, queueid, socketid);
-			fflush(stdout);
-
-			rte_eth_dev_info_get(portid, &dev_info);
-			txconf = &dev_info.default_txconf;
-			if (port_conf.rxmode.jumbo_frame)
-				txconf->txq_flags = 0;
-			ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
-						     socketid, txconf);
-			if (ret < 0)
-				rte_exit(EXIT_FAILURE,
-					"rte_eth_tx_queue_setup: err=%d, "
-					"port=%d\n", ret, portid);
-
-			qconf = &lcore_conf[lcore_id];
-			qconf->tx_queue_id[portid] = queueid;
-			queueid++;
-
-			qconf->tx_port_id[qconf->n_tx_port] = portid;
-			qconf->n_tx_port++;
-		}
-		printf("\n");
-	}
-
-	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-		if (rte_lcore_is_enabled(lcore_id) == 0)
-			continue;
-		qconf = &lcore_conf[lcore_id];
-		printf("\nInitializing rx queues on lcore %u ... ", lcore_id);
-		fflush(stdout);
-		/* init RX queues */
-		for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
-			portid = qconf->rx_queue_list[queue].port_id;
-			queueid = qconf->rx_queue_list[queue].queue_id;
-
-			if (numa_on)
-				socketid = (uint8_t)
-					rte_lcore_to_socket_id(lcore_id);
-			else
-				socketid = 0;
-
-			printf("rxq=%d,%d,%d ", portid, queueid, socketid);
-			fflush(stdout);
-
-			ret = rte_eth_rx_queue_setup(portid, queueid, nb_rxd,
-					socketid, NULL,
-					pktmbuf_pool[socketid]);
-			if (ret < 0)
-				rte_exit(EXIT_FAILURE,
-					"rte_eth_rx_queue_setup: err=%d,"
-					"port=%d\n", ret, portid);
-		}
-	}
-
-	printf("\n");
-
-	/* start ports */
-	for (portid = 0; portid < nb_ports; portid++) {
-		if ((enabled_port_mask & (1 << portid)) == 0)
-			continue;
-
-		/* Start device */
-		ret = rte_eth_dev_start(portid);
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE,
-				"rte_eth_dev_start: err=%d, port=%d\n",
-				ret, portid);
-
-		/*
-		 * If enabled, put device in promiscuous mode.
-		 * This allows IO forwarding mode to forward packets
-		 * to itself through 2 cross-connected  ports of the
-		 * target machine.
-		 */
-		if (promiscuous_on)
-			rte_eth_promiscuous_enable(portid);
-	}
-
-	check_all_ports_link_status((uint8_t)nb_ports, enabled_port_mask);
-
-	/* launch per-lcore init on every lcore */
-	rte_eal_mp_remote_launch(main_loop, NULL, CALL_MASTER);
-	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
-		if (rte_eal_wait_lcore(lcore_id) < 0)
-			return -1;
-	}
-
-	return 0;
-}
diff --git a/examples/l3fwd/Makefile b/examples/l3fwd/Makefile
index 5ce0ce0..151dbe3 100644
--- a/examples/l3fwd/Makefile
+++ b/examples/l3fwd/Makefile
@@ -42,7 +42,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
 APP = l3fwd
 
 # all source are stored in SRCS-y
-SRCS-y := main.c l3fwd_lpm.c l3fwd_em.c
+SRCS-y := main.c l3fwd_lpm.c l3fwd_em.c l3fwd_acl.c
 
 CFLAGS += -I$(SRCDIR)
 CFLAGS += -O3 $(USER_FLAGS)
diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 011ba14..93e08f6 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -77,6 +77,24 @@
 #endif
 #define HASH_ENTRY_NUMBER_DEFAULT	4
 
+/* Config options */
+#define OPTION_CONFIG           "config"
+#define OPTION_NONUMA           "no-numa"
+#define OPTION_ENBJMO           "enable-jumbo"
+#define OPTION_RULE_IPV4        "rule_ipv4"
+#define OPTION_RULE_IPV6        "rule_ipv6"
+#define OPTION_SCALAR           "scalar"
+#define OPTION_ETH_DEST         "eth-dest"
+#define OPTION_IPV6		"ipv6"
+#define OPTION_HASH_ENTRY_NUM	"hash-entry-num"
+#define OPTION_PARSE_PTYPE	"parse-ptype"
+#define OPTION_MAX_PKT_LEN	"max-pkt-len"
+
+/*Log file related character defs. */
+#define ACL_LEAD_CHAR		('@')
+#define ROUTE_LEAD_CHAR		('R')
+#define COMMENT_LEAD_CHAR	('#')
+
 struct mbuf_table {
 	uint16_t len;
 	struct rte_mbuf *m_table[MAX_PKT_BURST];
@@ -94,6 +112,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;
@@ -115,6 +134,8 @@ extern xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+extern int numa_on; /**< NUMA is enabled by default. */
+
 /* Send burst of packets on an output interface */
 static inline int
 send_burst(struct lcore_conf *qconf, uint16_t n, uint8_t port)
@@ -205,12 +226,18 @@ setup_lpm(const int socketid);
 void
 setup_hash(const int socketid);
 
+void
+setup_acl(const int socketid);
+
 int
 em_check_ptype(int portid);
 
 int
 lpm_check_ptype(int portid);
 
+int
+acl_check_ptype(int portid);
+
 uint16_t
 em_cb_parse_ptype(uint8_t port, uint16_t queue, struct rte_mbuf *pkts[],
 		  uint16_t nb_pkts, uint16_t max_pkts, void *user_param);
@@ -219,12 +246,19 @@ uint16_t
 lpm_cb_parse_ptype(uint8_t port, uint16_t queue, struct rte_mbuf *pkts[],
 		   uint16_t nb_pkts, uint16_t max_pkts, void *user_param);
 
+uint16_t
+acl_cb_parse_ptype(uint8_t port, uint16_t queue, struct rte_mbuf *pkts[],
+		  uint16_t nb_pkts, uint16_t max_pkts, void *user_param);
+
 int
 em_main_loop(__attribute__((unused)) void *dummy);
 
 int
 lpm_main_loop(__attribute__((unused)) void *dummy);
 
+int
+acl_main_loop(__attribute__((unused)) void *dummy);
+
 /* Return ipv4/ipv6 fwd lookup struct for LPM or EM. */
 void *
 em_get_ipv4_l3fwd_lookup_struct(const int socketid);
@@ -238,4 +272,19 @@ lpm_get_ipv4_l3fwd_lookup_struct(const int socketid);
 void *
 lpm_get_ipv6_l3fwd_lookup_struct(const int socketid);
 
+void *
+acl_get_ipv4_l3fwd_lookup_struct(const int socketid);
+
+void *
+acl_get_ipv6_l3fwd_lookup_struct(const int socketid);
+
+void
+l3fwd_acl_set_scalar(void);
+
+void
+l3fwd_acl_set_rule_ipv6_name(const char *optarg);
+
+void
+l3fwd_acl_set_rule_ipv4_name(const char *optarg);
+
 #endif  /* __L3_FWD_H__ */
diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
new file mode 100644
index 0000000..388b978
--- /dev/null
+++ b/examples/l3fwd/l3fwd_acl.c
@@ -0,0 +1,1064 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include <rte_common.h>
+#include <rte_byteorder.h>
+#include <rte_log.h>
+#include <rte_memory.h>
+#include <rte_memcpy.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_per_lcore.h>
+#include <rte_launch.h>
+#include <rte_atomic.h>
+#include <rte_cycles.h>
+#include <rte_prefetch.h>
+#include <rte_lcore.h>
+#include <rte_per_lcore.h>
+#include <rte_branch_prediction.h>
+#include <rte_interrupts.h>
+#include <rte_pci.h>
+#include <rte_random.h>
+#include <rte_debug.h>
+#include <rte_ether.h>
+#include <rte_ethdev.h>
+#include <rte_ring.h>
+#include <rte_mempool.h>
+#include <rte_mbuf.h>
+#include <rte_ip.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+#include <rte_string_fns.h>
+#include <rte_acl.h>
+
+#include "l3fwd.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 ipv4_hdr, src_addr) -
+			offsetof(struct 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 ipv4_hdr, dst_addr) -
+			offsetof(struct 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 ipv4_hdr) -
+			offsetof(struct 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 ipv4_hdr) -
+			offsetof(struct ipv4_hdr, next_proto_id) +
+			sizeof(uint16_t),
+	},
+};
+
+#define	IPV6_ADDR_LEN	16
+#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
+#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_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 ipv6_hdr, src_addr) -
+			offsetof(struct 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 ipv6_hdr, src_addr) -
+			offsetof(struct 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 ipv6_hdr, src_addr) -
+			offsetof(struct 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 ipv6_hdr, src_addr) -
+			offsetof(struct 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 ipv6_hdr, dst_addr)
+				- offsetof(struct 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 ipv6_hdr, dst_addr) -
+			offsetof(struct 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 ipv6_hdr, dst_addr) -
+			offsetof(struct 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 ipv6_hdr, dst_addr) -
+			offsetof(struct 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 ipv6_hdr) -
+			offsetof(struct 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 ipv6_hdr) -
+			offsetof(struct 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 {
+	char mapped[NB_SOCKETS];
+	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;
+
+const char cb_port_delim[] = ":";
+
+static struct {
+	const char *rule_ipv4_name;
+	const char *rule_ipv6_name;
+	int scalar;
+} parm_config;
+
+/*
+ * Print and dump ACL/Route rules functions are defined in
+ * following header file.
+ */
+#include "l3fwd_acl.h"
+
+/*
+ * Data path related inline functions are defined in
+ * following header file.
+ */
+#include "l3fwd_acl_scalar.h"
+
+/*
+ * API's called during initialization to setup ACL rules.
+ */
+void
+l3fwd_acl_set_rule_ipv4_name(const char *optarg)
+{
+	parm_config.rule_ipv4_name = optarg;
+}
+
+void
+l3fwd_acl_set_rule_ipv6_name(const char *optarg)
+{
+	parm_config.rule_ipv6_name = optarg;
+}
+
+void
+l3fwd_acl_set_scalar(void)
+{
+	parm_config.scalar = 1;
+}
+
+/*
+ * Parses IPV6 address, exepcts the following format:
+ * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X - is a hexedecimal digit).
+ */
+static int
+parse_ipv6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
+	char dlm)
+{
+	uint32_t addr[IPV6_ADDR_U16];
+
+	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
+
+	*end = in;
+
+	v[0] = (addr[0] << 16) + addr[1];
+	v[1] = (addr[2] << 16) + addr[3];
+	v[2] = (addr[4] << 16) + addr[5];
+	v[3] = (addr[6] << 16) + addr[7];
+
+	return 0;
+}
+
+static int
+parse_ipv6_net(const char *in, struct rte_acl_field field[4])
+{
+	int32_t rc;
+	const char *mp;
+	uint32_t i, m, v[4];
+	const uint32_t nbu32 = sizeof(uint32_t) * CHAR_BIT;
+
+	/* get address. */
+	rc = parse_ipv6_addr(in, &mp, v, '/');
+	if (rc != 0)
+		return rc;
+
+	/* get mask. */
+	GET_CB_FIELD(mp, m, 0, CHAR_BIT * sizeof(v), 0);
+
+	/* 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(const char *in, uint32_t *addr, uint32_t *mask_len)
+{
+	uint8_t a, b, c, d, m;
+
+	GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, d, 0, UINT8_MAX, '/');
+	GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
+
+	addr[0] = IPv4(a, b, c, d);
+	mask_len[0] = m;
+
+	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
+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;
+
+	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);
+
+	fseek(fh, 0, SEEK_SET);
+
+	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 void
+dump_acl_config(void)
+{
+	printf("ACL option are:\n");
+	printf(OPTION_RULE_IPV4": %s\n", parm_config.rule_ipv4_name);
+	printf(OPTION_RULE_IPV6": %s\n", parm_config.rule_ipv6_name);
+	printf(OPTION_SCALAR": %d\n", parm_config.scalar);
+}
+
+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;
+}
+
+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.scalar && rte_acl_set_ctx_classify(context,
+			RTE_ACL_CLASSIFY_SCALAR) != 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;
+}
+
+void
+setup_acl(const int socket_id __attribute__((unused)))
+{
+	unsigned int lcore_id;
+	unsigned int i;
+	int socketid;
+	struct rte_acl_rule *acl_base_ipv4, *route_base_ipv4,
+		*acl_base_ipv6, *route_base_ipv6;
+	unsigned int acl_num_ipv4 = 0, route_num_ipv4 = 0,
+		acl_num_ipv6 = 0, route_num_ipv6 = 0;
+
+	printf("\nsocket %d\n", socket_id);
+
+	if (check_acl_config() != 0)
+		rte_exit(EXIT_FAILURE, "Failed to get valid ACL options\n");
+
+	dump_acl_config();
+
+	/* Load  rules from the input file */
+	if (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)
+		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
+
+	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);
+
+	if (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)
+		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
+
+	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 (!numa_on)
+		acl_config.mapped[0] = 1;
+	else {
+		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+			if (rte_lcore_is_enabled(lcore_id) == 0)
+				continue;
+
+			socketid = rte_lcore_to_socket_id(lcore_id);
+			if (socketid >= NB_SOCKETS) {
+				acl_log("Socket %d of lcore %u is out "
+					"of range %d\n",
+					socketid, lcore_id, NB_SOCKETS);
+				free(route_base_ipv4);
+				free(route_base_ipv6);
+				free(acl_base_ipv4);
+				free(acl_base_ipv6);
+				return;
+			}
+
+			acl_config.mapped[socketid] = 1;
+		}
+	}
+
+	for (i = 0; i < NB_SOCKETS; i++) {
+		if (acl_config.mapped[i]) {
+			acl_config.acx_ipv4[i] = app_acl_init(route_base_ipv4,
+				acl_base_ipv4, route_num_ipv4, acl_num_ipv4,
+				0, i);
+
+			acl_config.acx_ipv6[i] = app_acl_init(route_base_ipv6,
+				acl_base_ipv6, route_num_ipv6, acl_num_ipv6,
+				1, i);
+		}
+	}
+
+	free(route_base_ipv4);
+	free(route_base_ipv6);
+
+#ifdef L3FWDACL_DEBUG
+	acl_config.rule_ipv4 = (struct acl4_rule *)acl_base_ipv4;
+	acl_config.rule_ipv6 = (struct acl6_rule *)acl_base_ipv6;
+#else
+	free(acl_base_ipv4);
+	free(acl_base_ipv6);
+#endif
+
+}
+
+/* main processing loop */
+int
+acl_main_loop(__attribute__((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;
+	uint8_t portid, 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=%hhu 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];
+				rte_eth_tx_buffer_flush(portid,
+						qconf->tx_queue_id[portid],
+						qconf->tx_buffer[portid]);
+			}
+			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;
+}
+
+static inline void
+acl_parse_ptype(struct rte_mbuf *m)
+{
+	struct ether_hdr *eth_hdr;
+	uint32_t packet_type = RTE_PTYPE_UNKNOWN;
+	uint16_t ether_type;
+	void *l3;
+	int hdr_len;
+	struct ipv4_hdr *ipv4_hdr;
+	struct ipv6_hdr *ipv6_hdr;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	ether_type = eth_hdr->ether_type;
+	l3 = (uint8_t *)eth_hdr + sizeof(struct ether_hdr);
+	if (ether_type == rte_cpu_to_be_16(ETHER_TYPE_IPv4)) {
+		ipv4_hdr = (struct ipv4_hdr *)l3;
+		hdr_len = (ipv4_hdr->version_ihl & IPV4_HDR_IHL_MASK) *
+			  IPV4_IHL_MULTIPLIER;
+		if (hdr_len == sizeof(struct ipv4_hdr)) {
+			packet_type |= RTE_PTYPE_L3_IPV4;
+			if (ipv4_hdr->next_proto_id == IPPROTO_TCP)
+				packet_type |= RTE_PTYPE_L4_TCP;
+			else if (ipv4_hdr->next_proto_id == IPPROTO_UDP)
+				packet_type |= RTE_PTYPE_L4_UDP;
+		} else
+			packet_type |= RTE_PTYPE_L3_IPV4_EXT;
+	} else if (ether_type == rte_cpu_to_be_16(ETHER_TYPE_IPv4)) {
+		ipv6_hdr = (struct ipv6_hdr *)l3;
+		if (ipv6_hdr->proto == IPPROTO_TCP)
+			packet_type |= RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP;
+		else if (ipv6_hdr->proto == IPPROTO_UDP)
+			packet_type |= RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_UDP;
+		else
+			packet_type |= RTE_PTYPE_L3_IPV6_EXT_UNKNOWN;
+	}
+
+	m->packet_type = packet_type;
+}
+
+uint16_t
+acl_cb_parse_ptype(uint8_t port __rte_unused, uint16_t queue __rte_unused,
+		  struct rte_mbuf *pkts[], uint16_t nb_pkts,
+		  uint16_t max_pkts __rte_unused,
+		  void *user_param __rte_unused)
+{
+	unsigned int i;
+
+	for (i = 0; i < nb_pkts; ++i)
+		acl_parse_ptype(pkts[i]);
+
+	return nb_pkts;
+}
+
+/* Requirements:
+ * 1. IP packets without extension;
+ * 2. L4 payload should be either TCP or UDP.
+ */
+int
+acl_check_ptype(int portid)
+{
+	int i, ret;
+	int ptype_l3_ipv4_ext = 0;
+	int ptype_l3_ipv6_ext = 0;
+	int ptype_l4_tcp = 0;
+	int ptype_l4_udp = 0;
+	uint32_t ptype_mask = RTE_PTYPE_L3_MASK | RTE_PTYPE_L4_MASK;
+
+	ret = rte_eth_dev_get_supported_ptypes(portid, ptype_mask, NULL, 0);
+	if (ret <= 0)
+		return 0;
+
+	uint32_t ptypes[ret];
+
+	ret = rte_eth_dev_get_supported_ptypes(portid, ptype_mask, ptypes, ret);
+	for (i = 0; i < ret; ++i) {
+		switch (ptypes[i]) {
+		case RTE_PTYPE_L3_IPV4_EXT:
+			ptype_l3_ipv4_ext = 1;
+			break;
+		case RTE_PTYPE_L3_IPV6_EXT:
+			ptype_l3_ipv6_ext = 1;
+			break;
+		case RTE_PTYPE_L4_TCP:
+			ptype_l4_tcp = 1;
+			break;
+		case RTE_PTYPE_L4_UDP:
+			ptype_l4_udp = 1;
+			break;
+		}
+	}
+
+	if (ptype_l3_ipv4_ext == 0)
+		printf("port %d cannot parse RTE_PTYPE_L3_IPV4_EXT\n", portid);
+	if (ptype_l3_ipv6_ext == 0)
+		printf("port %d cannot parse RTE_PTYPE_L3_IPV6_EXT\n", portid);
+	if (!ptype_l3_ipv4_ext || !ptype_l3_ipv6_ext)
+		return 0;
+
+	if (ptype_l4_tcp == 0)
+		printf("port %d cannot parse RTE_PTYPE_L4_TCP\n", portid);
+	if (ptype_l4_udp == 0)
+		printf("port %d cannot parse RTE_PTYPE_L4_UDP\n", portid);
+	if (ptype_l4_tcp && ptype_l4_udp)
+		return 1;
+
+	return 0;
+}
+
+/* Not used by L3fwd ACL. */
+void *
+acl_get_ipv4_l3fwd_lookup_struct(__attribute__((unused)) const int socketid)
+{
+	return NULL;
+}
+
+void *
+acl_get_ipv6_l3fwd_lookup_struct(__attribute__((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 0000000..b79784b
--- /dev/null
+++ b/examples/l3fwd/l3fwd_acl.h
@@ -0,0 +1,263 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __L3FWD_ACL_H__
+#define __L3FWD_ACL_H__
+
+#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 uint32_t_to_char(ip, a, b, c, d) do {\
+		*a = (unsigned char)(ip >> 24 & 0xff);\
+		*b = (unsigned char)(ip >> 16 & 0xff);\
+		*c = (unsigned char)(ip >> 8 & 0xff);\
+		*d = (unsigned char)(ip & 0xff);\
+	} while (0)
+#define OFF_ETHHEAD	(sizeof(struct ether_hdr))
+#define OFF_IPV42PROTO (offsetof(struct ipv4_hdr, next_proto_id))
+#define OFF_IPV62PROTO (offsetof(struct 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)
+
+#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
+	unsigned long val;                                      \
+	char *end;                                              \
+	errno = 0;                                              \
+	val = strtoul((in), &end, (base));                      \
+	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
+		return -EINVAL;                               \
+	(fd) = (typeof(fd))val;                                 \
+	(in) = end + 1;                                         \
+} while (0)
+
+/*
+ * 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 inline void
+print_one_ipv4_rule(struct acl4_rule *rule, int extra)
+{
+	unsigned char a, b, c, d;
+
+	uint32_t_to_char(rule->field[SRC_FIELD_IPV4].value.u32,
+			&a, &b, &c, &d);
+	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
+			rule->field[SRC_FIELD_IPV4].mask_range.u32);
+	uint32_t_to_char(rule->field[DST_FIELD_IPV4].value.u32,
+			&a, &b, &c, &d);
+	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
+			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 inline void
+print_one_ipv6_rule(struct acl6_rule *rule, int extra)
+{
+	unsigned char a, b, c, d;
+
+	uint32_t_to_char(rule->field[SRC1_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[SRC2_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[SRC3_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[SRC4_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
+			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);
+
+	uint32_t_to_char(rule->field[DST1_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[DST2_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[DST3_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[DST4_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
+			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);
+}
+
+/* Bypass comment and empty lines */
+static inline int
+is_bypass_line(char *buff)
+{
+	int i = 0;
+
+	/* comment line */
+	if (buff[0] == COMMENT_LEAD_CHAR)
+		return 1;
+	/* empty line */
+	while (buff[i] != '\0') {
+		if (!isspace(buff[i]))
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
+#ifdef L3FWDACL_DEBUG
+static inline void
+dump_acl4_rule(struct rte_mbuf *m, uint32_t sig)
+{
+	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
+	unsigned char a, b, c, d;
+	struct ipv4_hdr *ipv4_hdr = rte_pktmbuf_mtod_offset(m,
+					    struct ipv4_hdr *,
+					    sizeof(struct ether_hdr));
+
+	uint32_t_to_char(rte_bswap32(ipv4_hdr->src_addr), &a, &b, &c, &d);
+	printf("Packet Src:%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
+	uint32_t_to_char(rte_bswap32(ipv4_hdr->dst_addr), &a, &b, &c, &d);
+	printf("Dst:%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
+
+	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)
+{
+	unsigned int i;
+	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
+	struct ipv6_hdr *ipv6_hdr = rte_pktmbuf_mtod_offset(m,
+					    struct ipv6_hdr *,
+					    sizeof(struct ether_hdr));
+
+	printf("Packet Src");
+	for (i = 0; i < RTE_DIM(ipv6_hdr->src_addr); i += sizeof(uint16_t))
+		printf(":%.2x%.2x",
+			ipv6_hdr->src_addr[i], ipv6_hdr->src_addr[i + 1]);
+
+	printf("\nDst");
+	for (i = 0; i < RTE_DIM(ipv6_hdr->dst_addr); i += sizeof(uint16_t))
+		printf(":%.2x%.2x",
+			ipv6_hdr->dst_addr[i], ipv6_hdr->dst_addr[i + 1]);
+
+	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");
+	}
+}
+
+#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 0000000..2ac02f5
--- /dev/null
+++ b/examples/l3fwd/l3fwd_acl_scalar.h
@@ -0,0 +1,182 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __L3FWD_ACL_SCALAR_H__
+#define __L3FWD_ACL_SCALAR_H__
+
+#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 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 ipv4_hdr *,
+						   sizeof(struct 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 */
+
+/* Enqueue a single packet, and send burst if queue is filled */
+static inline void
+l3fwd_acl_send_single_packet(struct rte_mbuf *m, uint8_t port)
+{
+	uint32_t lcore_id;
+	struct lcore_conf *qconf;
+
+	lcore_id = rte_lcore_id();
+	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_em.c b/examples/l3fwd/l3fwd_em.c
index 9cc4460..6fdabf7 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -754,7 +754,7 @@ setup_hash(const int socketid)
 		rte_hash_create(&ipv4_l3fwd_hash_params);
 	if (ipv4_l3fwd_em_lookup_struct[socketid] == NULL)
 		rte_exit(EXIT_FAILURE,
-			"Unable to create the l3fwd hash on socket %d\n",
+			"Unable to create the v4 hash on socket %d\n",
 			socketid);
 
 	/* create ipv6 hash */
@@ -765,13 +765,15 @@ setup_hash(const int socketid)
 		rte_hash_create(&ipv6_l3fwd_hash_params);
 	if (ipv6_l3fwd_em_lookup_struct[socketid] == NULL)
 		rte_exit(EXIT_FAILURE,
-			"Unable to create the l3fwd hash on socket %d\n",
+			"Unable to create the v6 hash on socket %d\n",
 			socketid);
 
 	if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-		/* For testing hash matching with a large number of flows we
-		 * generate millions of IP 5-tuples with an incremented dst
-		 * address to initialize the hash table. */
+		/* For testing hash matching with a large number
+		 * of flows we generate millions of IP 5-tuples
+		 * with an incremented dst address to initialize
+		 * the hash table.
+		 */
 		if (ipv6 == 0) {
 			/* populate the ipv4 hash */
 			populate_ipv4_many_flow_into_table(
@@ -781,7 +783,7 @@ setup_hash(const int socketid)
 			/* populate the ipv6 hash */
 			populate_ipv6_many_flow_into_table(
 				ipv6_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
+			hash_entry_number);
 		}
 	} else {
 		/*
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index f621269..ab31210 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -209,7 +209,7 @@ setup_lpm(const int socketid)
 			rte_lpm_create(s, socketid, &config_ipv4);
 	if (ipv4_l3fwd_lpm_lookup_struct[socketid] == NULL)
 		rte_exit(EXIT_FAILURE,
-			"Unable to create the l3fwd LPM table on socket %d\n",
+			"Unable to create v4 LPM table on socket %d\n",
 			socketid);
 
 	/* populate the LPM table */
@@ -221,14 +221,13 @@ setup_lpm(const int socketid)
 			continue;
 
 		ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
-			ipv4_l3fwd_lpm_route_array[i].ip,
-			ipv4_l3fwd_lpm_route_array[i].depth,
-			ipv4_l3fwd_lpm_route_array[i].if_out);
+				ipv4_l3fwd_lpm_route_array[i].ip,
+				ipv4_l3fwd_lpm_route_array[i].depth,
+				ipv4_l3fwd_lpm_route_array[i].if_out);
 
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE,
-				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
-				i, socketid);
+				"Unable to add entry %u to v4 LPM table on socket %d\n", i, socketid);
 		}
 
 		printf("LPM: Adding route 0x%08x / %d (%d)\n",
@@ -243,11 +242,11 @@ setup_lpm(const int socketid)
 	config.max_rules = IPV6_L3FWD_LPM_MAX_RULES;
 	config.number_tbl8s = IPV6_L3FWD_LPM_NUMBER_TBL8S;
 	config.flags = 0;
-	ipv6_l3fwd_lpm_lookup_struct[socketid] = rte_lpm6_create(s, socketid,
-				&config);
+	ipv6_l3fwd_lpm_lookup_struct[socketid] =
+			rte_lpm6_create(s, socketid, &config);
 	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL)
 		rte_exit(EXIT_FAILURE,
-			"Unable to create the l3fwd LPM table on socket %d\n",
+			"Unable to create v6 LPM table on socket %d\n",
 			socketid);
 
 	/* populate the LPM table */
@@ -265,13 +264,11 @@ setup_lpm(const int socketid)
 
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE,
-				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
-				i, socketid);
+				"Unable to add entry %u to the l3fwd LPM table on socket %d\n", i, socketid);
 		}
 
 		printf("LPM: Adding route %s / %d (%d)\n",
-			"IPV6",
-			ipv6_l3fwd_lpm_route_array[i].depth,
+			"IPV6", ipv6_l3fwd_lpm_route_array[i].depth,
 			ipv6_l3fwd_lpm_route_array[i].if_out);
 	}
 }
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index a50d628..3f35762 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -51,6 +51,7 @@
 #include <rte_memory.h>
 #include <rte_memcpy.h>
 #include <rte_memzone.h>
+#include <rte_malloc.h>
 #include <rte_eal.h>
 #include <rte_per_lcore.h>
 #include <rte_launch.h>
@@ -97,11 +98,12 @@ static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
 /**< Ports set in promiscuous mode off by default. */
 static int promiscuous_on;
 
-/* Select Longest-Prefix or Exact match. */
+/* Select Longest-Prefix(aka LPM), Exact match(aka EM) or ACL. */
 static int l3fwd_lpm_on;
 static int l3fwd_em_on;
+static int l3fwd_acl_on;
 
-static int numa_on = 1; /**< NUMA is enabled by default. */
+int numa_on = 1; /**< NUMA is enabled by default. */
 static int parse_ptype; /**< Parse packet type using rx callback, and */
 			/**< disabled by default */
 
@@ -172,7 +174,7 @@ static struct rte_eth_conf port_conf = {
 static struct rte_mempool * pktmbuf_pool[NB_SOCKETS];
 
 struct l3fwd_lkp_mode {
-	void  (*setup)(int);
+	void  (*setup)(const int);
 	int   (*check_ptype)(int);
 	rte_rx_callback_fn cb_parse_ptype;
 	int   (*main_loop)(void *);
@@ -200,6 +202,15 @@ static struct l3fwd_lkp_mode l3fwd_lpm_lkp = {
 	.get_ipv6_lookup_struct = lpm_get_ipv6_l3fwd_lookup_struct,
 };
 
+static struct l3fwd_lkp_mode l3fwd_acl_lkp = {
+	.setup                  = setup_acl,
+	.check_ptype		= acl_check_ptype,
+	.cb_parse_ptype		= acl_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,
+};
+
 /*
  * Setup lookup methods for forwarding.
  * Currently exact-match and longest-prefix-match
@@ -211,8 +222,11 @@ setup_l3fwd_lookup_tables(void)
 	/* Setup HASH lookup functions. */
 	if (l3fwd_em_on)
 		l3fwd_lkp = l3fwd_em_lkp;
-	/* Setup LPM lookup functions. */
+	/* Setup ACL lookup functions. */
+	else if (l3fwd_acl_on)
+		l3fwd_lkp = l3fwd_acl_lkp;
 	else
+	/* Setup LPM lookup functions. */
 		l3fwd_lkp = l3fwd_lpm_lkp;
 }
 
@@ -312,31 +326,45 @@ print_usage(const char *prgname)
 {
 	printf("%s [EAL options] --"
 		" -p PORTMASK"
+		"--"OPTION_RULE_IPV4"=FILE"
+		"--"OPTION_RULE_IPV6"=FILE"
 		" [-P]"
 		" [-E]"
+		" [-A]"
 		" [-L]"
-		" --config (port,queue,lcore)[,(port,queue,lcore)]"
-		" [--eth-dest=X,MM:MM:MM:MM:MM:MM]"
-		" [--enable-jumbo [--max-pkt-len PKTLEN]]"
-		" [--no-numa]"
-		" [--hash-entry-num]"
-		" [--ipv6]"
-		" [--parse-ptype]\n\n"
+		" [--"OPTION_CONFIG" (port,queue,lcore)[,(port,queue,lcore]]"
+		" [--"OPTION_ETH_DEST" =X,MM:MM:MM:MM:MM:MM]"
+		" [--"OPTION_ENBJMO" [--"OPTION_MAX_PKT_LEN" PKTLEN]]"
+		" [--"OPTION_NONUMA"]"
+		" [--"OPTION_HASH_ENTRY_NUM"]"
+		" [--"OPTION_IPV6"]"
+		" [--"OPTION_PARSE_PTYPE"]\n\n"
 
 		"  -p PORTMASK: Hexadecimal bitmask of ports to configure\n"
 		"  -P : Enable promiscuous mode\n"
 		"  -E : Enable exact match\n"
+		"  -A : Enable access control list match\n"
 		"  -L : Enable longest prefix match (default)\n"
-		"  --config (port,queue,lcore): Rx queue configuration\n"
-		"  --eth-dest=X,MM:MM:MM:MM:MM:MM: Ethernet destination for port X\n"
-		"  --enable-jumbo: Enable jumbo frames\n"
-		"  --max-pkt-len: Under the premise of enabling jumbo,\n"
+		"  --"OPTION_CONFIG" (port,queue,lcore): Rx queue configuration\n"
+		"  --"OPTION_ETH_DEST"=X,MM:MM:MM:MM:MM:MM: Ethernet destination for port X\n"
+		"  --"OPTION_ENBJMO": Enable jumbo frames\n"
+		"  --"OPTION_MAX_PKT_LEN": Under the premise of enabling jumbo,\n"
 		"                 maximum packet length in decimal (64-9600)\n"
-		"  --no-numa: Disable numa awareness\n"
-		"  --hash-entry-num: Specify the hash entry number in hexadecimal to be setup\n"
-		"  --ipv6: Set if running ipv6 packets\n"
-		"  --parse-ptype: Set to use software to analyze packet type\n\n",
-		prgname);
+		"  --"OPTION_NONUMA": Disable numa awareness\n"
+		"  --"OPTION_HASH_ENTRY_NUM": Specify the hash entry number in hexadecimal to be setup\n"
+		"  --"OPTION_IPV6": Set if running ipv6 packets\n"
+		"  --"OPTION_PARSE_PTYPE": Set to use software to analyze packet type\n\n"
+		"  --"OPTION_RULE_IPV4"=FILE: specify the ipv4 rules "
+		"entries file. "
+		"Each rule occupy one line. "
+		"2 kinds of rules are supported. "
+		"One is ACL entry at while line leads with character '%c', "
+		"another is route entry at while line leads with "
+		"character '%c'.\n"
+		"  --"OPTION_RULE_IPV6"=FILE: specify the ipv6 rules "
+		"entries file.\n"
+		"  --"OPTION_SCALAR": Use scalar function to do lookup\n",
+		prgname, ACL_LEAD_CHAR, ROUTE_LEAD_CHAR);
 }
 
 static int
@@ -477,17 +505,11 @@ parse_eth_dest(const char *optarg)
 static const char short_options[] =
 	"p:"  /* portmask */
 	"P"   /* promiscuous */
+	"A"   /* enable access control list match */
 	"L"   /* enable long prefix match */
 	"E"   /* enable exact match */
 	;
 
-#define CMD_LINE_OPT_CONFIG "config"
-#define CMD_LINE_OPT_ETH_DEST "eth-dest"
-#define CMD_LINE_OPT_NO_NUMA "no-numa"
-#define CMD_LINE_OPT_IPV6 "ipv6"
-#define CMD_LINE_OPT_ENABLE_JUMBO "enable-jumbo"
-#define CMD_LINE_OPT_HASH_ENTRY_NUM "hash-entry-num"
-#define CMD_LINE_OPT_PARSE_PTYPE "parse-ptype"
 enum {
 	/* long options mapped to a short option */
 
@@ -501,16 +523,22 @@ enum {
 	CMD_LINE_OPT_ENABLE_JUMBO_NUM,
 	CMD_LINE_OPT_HASH_ENTRY_NUM_NUM,
 	CMD_LINE_OPT_PARSE_PTYPE_NUM,
+	CMD_LINE_OPT_RULE_IPV4,
+	CMD_LINE_OPT_RULE_IPV6,
+	CMD_LINE_OPT_SCALAR,
 };
 
 static const struct option lgopts[] = {
-	{CMD_LINE_OPT_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM},
-	{CMD_LINE_OPT_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM},
-	{CMD_LINE_OPT_NO_NUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM},
-	{CMD_LINE_OPT_IPV6, 0, 0, CMD_LINE_OPT_IPV6_NUM},
-	{CMD_LINE_OPT_ENABLE_JUMBO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM},
-	{CMD_LINE_OPT_HASH_ENTRY_NUM, 1, 0, CMD_LINE_OPT_HASH_ENTRY_NUM_NUM},
-	{CMD_LINE_OPT_PARSE_PTYPE, 0, 0, CMD_LINE_OPT_PARSE_PTYPE_NUM},
+	{OPTION_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM},
+	{OPTION_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM},
+	{OPTION_NONUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM},
+	{OPTION_IPV6, 0, 0, CMD_LINE_OPT_IPV6_NUM},
+	{OPTION_ENBJMO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM},
+	{OPTION_HASH_ENTRY_NUM, 1, 0, CMD_LINE_OPT_HASH_ENTRY_NUM_NUM},
+	{OPTION_PARSE_PTYPE, 0, 0, CMD_LINE_OPT_PARSE_PTYPE_NUM},
+	{OPTION_RULE_IPV4, 1, 0, CMD_LINE_OPT_RULE_IPV4},
+	{OPTION_RULE_IPV6, 1, 0, CMD_LINE_OPT_RULE_IPV6},
+	{OPTION_SCALAR, 0, 0, CMD_LINE_OPT_SCALAR},
 	{NULL, 0, 0, 0}
 };
 
@@ -522,17 +550,17 @@ static const struct option lgopts[] = {
  * value of 8192
  */
 #define NB_MBUF RTE_MAX(	\
-	(nb_ports*nb_rx_queue*RTE_TEST_RX_DESC_DEFAULT +	\
-	nb_ports*nb_lcores*MAX_PKT_BURST +			\
-	nb_ports*n_tx_queue*RTE_TEST_TX_DESC_DEFAULT +		\
-	nb_lcores*MEMPOOL_CACHE_SIZE),				\
+	(nb_ports * nb_rx_queue * RTE_TEST_RX_DESC_DEFAULT +	\
+	nb_ports * nb_lcores * MAX_PKT_BURST +			\
+	nb_ports * n_tx_queue * RTE_TEST_TX_DESC_DEFAULT +		\
+	nb_lcores * MEMPOOL_CACHE_SIZE),				\
 	(unsigned)8192)
 
 /* Parse the argument given in the command line of the application */
 static int
 parse_args(int argc, char **argv)
 {
-	int opt, ret;
+	int opt, ret, chk_cond;
 	char **argvopt;
 	int option_index;
 	char *prgname = argv[0];
@@ -544,17 +572,19 @@ parse_args(int argc, char **argv)
 	const char *str2 = "L3FWD: Promiscuous mode selected";
 	const char *str3 = "L3FWD: Exact match selected";
 	const char *str4 = "L3FWD: Longest-prefix match selected";
-	const char *str5 = "L3FWD: Invalid config";
-	const char *str6 = "L3FWD: NUMA is disabled";
-	const char *str7 = "L3FWD: IPV6 is specified";
-	const char *str8 =
+	const char *str5 = "L3FWD: Access Control List match selected";
+	const char *str6 = "L3FWD: Invalid config";
+	const char *str7 = "L3FWD: NUMA is disabled";
+	const char *str8 = "L3FWD: IPV6 is specified";
+	const char *str9 =
 		"L3FWD: Jumbo frame is enabled - disabling simple TX path";
-	const char *str9 = "L3FWD: Invalid packet length";
-	const char *str10 = "L3FWD: Set jumbo frame max packet len to ";
-	const char *str11 = "L3FWD: Invalid hash entry number";
-	const char *str12 =
-		"L3FWD: LPM and EM are mutually exclusive, select only one";
-	const char *str13 = "L3FWD: LPM or EM none selected, default LPM on";
+	const char *str10 = "L3FWD: Invalid packet length";
+	const char *str11 = "L3FWD: Set jumbo frame max packet len to ";
+	const char *str12 = "L3FWD: Invalid hash entry number";
+	const char *str13 =
+		"L3FWD: LPM, EM and ACL are mutually exclusive, select only one";
+	const char *str14 =
+		"L3FWD: LPM, EM or ACL none selected, default LPM on";
 
 	while ((opt = getopt_long(argc, argvopt, short_options,
 				lgopts, &option_index)) != EOF) {
@@ -585,11 +615,16 @@ parse_args(int argc, char **argv)
 			l3fwd_lpm_on = 1;
 			break;
 
+		case 'A':
+			printf("%s\n", str5);
+			l3fwd_acl_on = 1;
+			break;
+
 		/* long options */
 		case CMD_LINE_OPT_CONFIG_NUM:
 			ret = parse_config(optarg);
 			if (ret) {
-				printf("%s\n", str5);
+				printf("%s\n", str6);
 				print_usage(prgname);
 				return -1;
 			}
@@ -600,12 +635,12 @@ parse_args(int argc, char **argv)
 			break;
 
 		case CMD_LINE_OPT_NO_NUMA_NUM:
-			printf("%s\n", str6);
+			printf("%s\n", str7);
 			numa_on = 0;
 			break;
 
 		case CMD_LINE_OPT_IPV6_NUM:
-			printf("%sn", str7);
+			printf("%sn", str8);
 			ipv6 = 1;
 			break;
 
@@ -614,7 +649,7 @@ parse_args(int argc, char **argv)
 				"max-pkt-len", required_argument, 0, 0
 			};
 
-			printf("%s\n", str8);
+			printf("%s\n", str9);
 			port_conf.rxmode.jumbo_frame = 1;
 
 			/*
@@ -626,13 +661,13 @@ parse_args(int argc, char **argv)
 				ret = parse_max_pkt_len(optarg);
 				if ((ret < 64) ||
 					(ret > MAX_JUMBO_PKT_LEN)) {
-					printf("%s\n", str9);
+					printf("%s\n", str10);
 					print_usage(prgname);
 					return -1;
 				}
 				port_conf.rxmode.max_rx_pkt_len = ret;
 			}
-			printf("%s %u\n", str10,
+			printf("%s %u\n", str11,
 				(unsigned int)port_conf.rxmode.max_rx_pkt_len);
 			break;
 		}
@@ -642,7 +677,7 @@ parse_args(int argc, char **argv)
 			if ((ret > 0) && (ret <= L3FWD_HASH_ENTRIES)) {
 				hash_entry_number = ret;
 			} else {
-				printf("%s\n", str11);
+				printf("%s\n", str12);
 				print_usage(prgname);
 				return -1;
 			}
@@ -653,15 +688,29 @@ parse_args(int argc, char **argv)
 			parse_ptype = 1;
 			break;
 
+		case CMD_LINE_OPT_RULE_IPV4:
+			l3fwd_acl_set_rule_ipv4_name(optarg);
+			break;
+
+		case CMD_LINE_OPT_RULE_IPV6:
+			l3fwd_acl_set_rule_ipv6_name(optarg);
+			break;
+
+		case CMD_LINE_OPT_SCALAR:
+			l3fwd_acl_set_scalar();
+			break;
+
 		default:
 			print_usage(prgname);
 			return -1;
 		}
 	}
 
-	/* If both LPM and EM are selected, return error. */
-	if (l3fwd_lpm_on && l3fwd_em_on) {
-		printf("%s\n", str12);
+	/* If more than one(LPM, EM and ACL) are selected, return error. */
+	chk_cond = l3fwd_lpm_on ? (l3fwd_em_on || l3fwd_acl_on) :
+				(l3fwd_em_on && l3fwd_acl_on);
+	if (chk_cond) {
+		printf("%s\n", str13);
 		return -1;
 	}
 
@@ -669,9 +718,9 @@ parse_args(int argc, char **argv)
 	 * Nothing is selected, pick longest-prefix match
 	 * as default match.
 	 */
-	if (!l3fwd_lpm_on && !l3fwd_em_on) {
+	if (!l3fwd_lpm_on && !l3fwd_em_on && !l3fwd_acl_on) {
 		l3fwd_lpm_on = 1;
-		printf("%s\n", str13);
+		printf("%s\n", str14);
 	}
 
 	/*
@@ -684,6 +733,11 @@ parse_args(int argc, char **argv)
 		hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT;
 	}
 
+	/* For ACL, update port config rss hash filter. */
+	if (l3fwd_acl_on)
+		port_conf.rx_adv_conf.rss_conf.rss_hf |=
+			ETH_RSS_UDP | ETH_RSS_TCP | ETH_RSS_SCTP;
+
 	if (optind >= 0)
 		argv[optind-1] = prgname;
 
@@ -737,8 +791,9 @@ init_mem(unsigned nb_mbuf)
 				printf("Allocated mbuf pool on socket %d\n",
 					socketid);
 
-			/* Setup either LPM or EM(f.e Hash).  */
-			l3fwd_lkp.setup(socketid);
+			/* Setup LPM/EM (f.e. Hash) functions */
+			if (l3fwd_lpm_on || l3fwd_em_on)
+				l3fwd_lkp.setup(socketid);
 		}
 		qconf = &lcore_conf[lcore_id];
 		qconf->ipv4_lookup_struct =
@@ -836,7 +891,7 @@ prepare_ptype_parser(uint8_t portid, uint16_t queueid)
 		return 1;
 
 	printf("port %d cannot parse packet type, please add --%s\n",
-	       portid, CMD_LINE_OPT_PARSE_PTYPE);
+	       portid, OPTION_PARSE_PTYPE);
 	return 0;
 }
 
@@ -893,6 +948,10 @@ main(int argc, char **argv)
 	/* Setup function pointers for lookup method. */
 	setup_l3fwd_lookup_tables();
 
+	/* Setup ACL functions */
+	if (l3fwd_acl_on)
+		l3fwd_lkp.setup(-1);
+
 	/* initialize all ports */
 	for (portid = 0; portid < nb_ports; portid++) {
 		/* skip ports that are not enabled */
@@ -936,6 +995,28 @@ main(int argc, char **argv)
 		if (ret < 0)
 			rte_exit(EXIT_FAILURE, "init_mem failed\n");
 
+		/* ACL specific. */
+		if (l3fwd_acl_on) {
+			for (lcore_id = 0;
+				lcore_id < RTE_MAX_LCORE; lcore_id++) {
+				if (rte_lcore_is_enabled(lcore_id) == 0)
+					continue;
+
+				/* Initialize TX buffers */
+				qconf = &lcore_conf[lcore_id];
+				qconf->tx_buffer[portid] =
+					rte_zmalloc_socket("tx_buffer",
+					RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST),
+					0, rte_eth_dev_socket_id(portid));
+
+				if (qconf->tx_buffer[portid] == NULL)
+					rte_exit(EXIT_FAILURE, "Can't allocate tx buffer for port %u\n", (unsigned int) portid);
+
+				rte_eth_tx_buffer_init(qconf->tx_buffer[portid],
+							MAX_PKT_BURST);
+			}
+		}
+
 		/* init one TX queue per couple (lcore,port) */
 		queueid = 0;
 		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-- 
2.7.4

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [dpdk-dev] [v4 2/3] LPM config file read option.
  2017-03-05 19:47 [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd Ravi Kerur
  2017-03-05 19:47 ` [dpdk-dev] [v4 1/3] Merge l3fwd-acl and l3fwd code Ravi Kerur
@ 2017-03-05 19:47 ` Ravi Kerur
  2017-03-06 13:57   ` Mcnamara, John
  2017-03-05 19:47 ` [dpdk-dev] [v4 3/3] EM " Ravi Kerur
  2017-03-06 13:33 ` [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd Mcnamara, John
  3 siblings, 1 reply; 34+ messages in thread
From: Ravi Kerur @ 2017-03-05 19:47 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bruce.richardson, Ravi Kerur

v4:
	> No changes.

v3:
	> Fix additional checkpatch coding style issues.

v2:
	> Fix checkpatch warnings related to code
	> MACRO GET_CB_FIELD checkpatch warning not fixed

v1:
	> Remove static array configuration of Destination IP, MASK
		and IF_OUT for LPM and LPM6 config.
	> Add reading configuration from a file.
	> Format of configuration file is as follows
		#LPM route entries Dest-IP/Mask IF_OUT
		L1.1.1.0/24 0
		L2.1.1.0/24 1
		L3.1.1.0/24 2
		...

		#LPM6 route entries Dest-IP/Mask IF_OUT
		L1111:1111:1111:1111:0000:0000:0000:0000/48 0
		L2111:1111:1111:1111:0000:0000:0000:0000/48 1
		L3111:1111:1111:1111:0000:0000:0000:0000/48 2
		...

Signed-off-by: Ravi Kerur <rkerur@gmail.com>
---
 examples/l3fwd/l3fwd.h     |  43 +++++++
 examples/l3fwd/l3fwd_acl.c |  39 +-----
 examples/l3fwd/l3fwd_acl.h |  29 -----
 examples/l3fwd/l3fwd_lpm.c | 308 +++++++++++++++++++++++++++++++++++++--------
 examples/l3fwd/main.c      |  29 ++++-
 5 files changed, 329 insertions(+), 119 deletions(-)

diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 93e08f6..4b1cdc8 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -94,6 +94,47 @@
 #define ACL_LEAD_CHAR		('@')
 #define ROUTE_LEAD_CHAR		('R')
 #define COMMENT_LEAD_CHAR	('#')
+#define LPM_LEAD_CHAR		('L')
+#define EM_LEAD_CHAR		('E')
+
+#define	IPV6_ADDR_LEN	16
+#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
+#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
+
+#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
+	unsigned long val;                                      \
+	char *end;                                              \
+	errno = 0;                                              \
+	val = strtoul((in), &end, (base));                      \
+	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
+		return -EINVAL;                               \
+	(fd) = (typeof(fd))val;                                 \
+	(in) = end + 1;                                         \
+} while (0)
+
+/* Bypass comment and empty lines */
+static inline int
+is_bypass_line(char *buff)
+{
+	int i = 0;
+
+	/* comment line */
+	if (buff[0] == COMMENT_LEAD_CHAR)
+		return 1;
+	/* empty line */
+	while (buff[i] != '\0') {
+		if (!isspace(buff[i]))
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
+struct parm_cfg {
+	const char *rule_ipv4_name;
+	const char *rule_ipv6_name;
+	int scalar;
+};
 
 struct mbuf_table {
 	uint16_t len;
@@ -134,6 +175,8 @@ extern xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+extern struct parm_cfg parm_config;
+
 extern int numa_on; /**< NUMA is enabled by default. */
 
 /* Send burst of packets on an output interface */
diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
index 388b978..66ed23d 100644
--- a/examples/l3fwd/l3fwd_acl.c
+++ b/examples/l3fwd/l3fwd_acl.c
@@ -147,10 +147,6 @@ struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
 	},
 };
 
-#define	IPV6_ADDR_LEN	16
-#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
-#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
-
 enum {
 	PROTO_FIELD_IPV6,
 	SRC1_FIELD_IPV6,
@@ -297,12 +293,6 @@ static struct {
 
 const char cb_port_delim[] = ":";
 
-static struct {
-	const char *rule_ipv4_name;
-	const char *rule_ipv6_name;
-	int scalar;
-} parm_config;
-
 /*
  * Print and dump ACL/Route rules functions are defined in
  * following header file.
@@ -316,27 +306,6 @@ static struct {
 #include "l3fwd_acl_scalar.h"
 
 /*
- * API's called during initialization to setup ACL rules.
- */
-void
-l3fwd_acl_set_rule_ipv4_name(const char *optarg)
-{
-	parm_config.rule_ipv4_name = optarg;
-}
-
-void
-l3fwd_acl_set_rule_ipv6_name(const char *optarg)
-{
-	parm_config.rule_ipv6_name = optarg;
-}
-
-void
-l3fwd_acl_set_scalar(void)
-{
-	parm_config.scalar = 1;
-}
-
-/*
  * Parses IPV6 address, exepcts the following format:
  * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X - is a hexedecimal digit).
  */
@@ -566,7 +535,7 @@ parse_cb_ipv4vlan_rule(char *str, struct rte_acl_rule *v, int has_userdata)
 }
 
 static int
-add_rules(const char *rule_path,
+acl_add_rules(const char *rule_path,
 		struct rte_acl_rule **proute_base,
 		unsigned int *proute_num,
 		struct rte_acl_rule **pacl_base,
@@ -764,8 +733,8 @@ setup_acl(const int socket_id __attribute__((unused)))
 
 	dump_acl_config();
 
-	/* Load  rules from the input file */
-	if (add_rules(parm_config.rule_ipv4_name, &route_base_ipv4,
+	/* Load rules from the input file */
+	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)
 		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
@@ -776,7 +745,7 @@ setup_acl(const int socket_id __attribute__((unused)))
 	acl_log("IPv4 ACL entries %u:\n", acl_num_ipv4);
 	dump_ipv4_rules((struct acl4_rule *)acl_base_ipv4, acl_num_ipv4, 1);
 
-	if (add_rules(parm_config.rule_ipv6_name, &route_base_ipv6,
+	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)
diff --git a/examples/l3fwd/l3fwd_acl.h b/examples/l3fwd/l3fwd_acl.h
index b79784b..da0b5a8 100644
--- a/examples/l3fwd/l3fwd_acl.h
+++ b/examples/l3fwd/l3fwd_acl.h
@@ -55,17 +55,6 @@
 #define MBUF_IPV6_2PROTO(m)	\
 	rte_pktmbuf_mtod_offset((m), uint8_t *, OFF_ETHHEAD + OFF_IPV62PROTO)
 
-#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
-	unsigned long val;                                      \
-	char *end;                                              \
-	errno = 0;                                              \
-	val = strtoul((in), &end, (base));                      \
-	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
-		return -EINVAL;                               \
-	(fd) = (typeof(fd))val;                                 \
-	(in) = end + 1;                                         \
-} while (0)
-
 /*
  * 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.
@@ -163,24 +152,6 @@ print_one_ipv6_rule(struct acl6_rule *rule, int extra)
 			rule->data.userdata);
 }
 
-/* Bypass comment and empty lines */
-static inline int
-is_bypass_line(char *buff)
-{
-	int i = 0;
-
-	/* comment line */
-	if (buff[0] == COMMENT_LEAD_CHAR)
-		return 1;
-	/* empty line */
-	while (buff[i] != '\0') {
-		if (!isspace(buff[i]))
-			return 0;
-		i++;
-	}
-	return 1;
-}
-
 #ifdef L3FWDACL_DEBUG
 static inline void
 dump_acl4_rule(struct rte_mbuf *m, uint32_t sig)
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index ab31210..97f99cf 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -57,45 +57,24 @@
 
 #include "l3fwd.h"
 
-struct ipv4_l3fwd_lpm_route {
-	uint32_t ip;
-	uint8_t  depth;
-	uint8_t  if_out;
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
 };
 
-struct ipv6_l3fwd_lpm_route {
-	uint8_t ip[16];
+struct lpm_rule {
+	union {
+		uint32_t ip;
+		union {
+			uint32_t ip_32[4];
+			uint8_t ip_8[16];
+		};
+	};
 	uint8_t  depth;
 	uint8_t  if_out;
 };
 
-static struct ipv4_l3fwd_lpm_route ipv4_l3fwd_lpm_route_array[] = {
-	{IPv4(1, 1, 1, 0), 24, 0},
-	{IPv4(2, 1, 1, 0), 24, 1},
-	{IPv4(3, 1, 1, 0), 24, 2},
-	{IPv4(4, 1, 1, 0), 24, 3},
-	{IPv4(5, 1, 1, 0), 24, 4},
-	{IPv4(6, 1, 1, 0), 24, 5},
-	{IPv4(7, 1, 1, 0), 24, 6},
-	{IPv4(8, 1, 1, 0), 24, 7},
-};
-
-static struct ipv6_l3fwd_lpm_route ipv6_l3fwd_lpm_route_array[] = {
-	{{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 0},
-	{{2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 1},
-	{{3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 2},
-	{{4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 3},
-	{{5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 4},
-	{{6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 5},
-	{{7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 6},
-	{{8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 7},
-};
-
-#define IPV4_L3FWD_LPM_NUM_ROUTES \
-	(sizeof(ipv4_l3fwd_lpm_route_array) / sizeof(ipv4_l3fwd_lpm_route_array[0]))
-#define IPV6_L3FWD_LPM_NUM_ROUTES \
-	(sizeof(ipv6_l3fwd_lpm_route_array) / sizeof(ipv6_l3fwd_lpm_route_array[0]))
-
 #define IPV4_L3FWD_LPM_MAX_RULES         1024
 #define IPV4_L3FWD_LPM_NUMBER_TBL8S (1 << 8)
 #define IPV6_L3FWD_LPM_MAX_RULES         1024
@@ -110,6 +89,211 @@ struct rte_lpm6 *ipv6_l3fwd_lpm_lookup_struct[NB_SOCKETS];
 #include "l3fwd_lpm.h"
 #endif
 
+static int
+lpm_parse_v6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
+	char dlm)
+{
+	uint32_t addr[IPV6_ADDR_U16];
+
+	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
+
+	*end = in;
+
+	v[0] = (addr[0] << 16) + addr[1];
+	v[1] = (addr[2] << 16) + addr[3];
+	v[2] = (addr[4] << 16) + addr[5];
+	v[3] = (addr[6] << 16) + addr[7];
+
+	return 0;
+}
+
+static int
+lpm_parse_v6_net(const char *in, uint32_t *v, uint8_t *mask_len)
+{
+	int32_t rc;
+	const char *mp;
+	uint8_t m;
+	uint32_t tmp[4];
+
+	/* get address. */
+	rc = lpm_parse_v6_addr(in, &mp, v, '/');
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_addr failed %d\n", rc);
+		return rc;
+	}
+
+	/* get mask. */
+	GET_CB_FIELD(mp, m, 0, sizeof(tmp) * CHAR_BIT, 0);
+	*mask_len = m;
+
+	return 0;
+}
+
+static int
+lpm_parse_v6_rule(char *str, struct lpm_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL) {
+			RTE_LOG(ERR, L3FWD,
+				"\nparse_v6_rule strtok_r failed\n");
+			return -EINVAL;
+		}
+	}
+
+	rc = lpm_parse_v6_net(in[CB_FLD_DST_ADDR], v->ip_32, &v->depth);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_net failed\n");
+		return rc;
+	}
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+lpm_parse_v4_net(const char *in, uint32_t *addr, uint8_t *mask_len)
+{
+	uint8_t a, b, c, d, m;
+
+	GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, d, 0, UINT8_MAX, '/');
+	GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
+
+	addr[0] = IPv4(a, b, c, d);
+	*mask_len = m;
+
+	return 0;
+}
+
+static int
+lpm_parse_v4_rule(char *str, struct lpm_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = lpm_parse_v4_net(in[CB_FLD_DST_ADDR], &v->ip, &v->depth);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v4_net failed %d\n", rc);
+		return rc;
+	}
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+lpm_add_rules(const char *rule_path,
+		struct lpm_rule **proute_base,
+		unsigned int *proute_num,
+		int (*parser)(char *, struct lpm_rule *))
+{
+	uint8_t *route_rules;
+	struct lpm_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh = fopen(rule_path, "rb");
+	unsigned int i = 0, rule_size = sizeof(*next);
+
+	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] == LPM_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0)
+		rte_exit(EXIT_FAILURE, "Not find any route entries in %s!\n",
+				rule_path);
+
+	fseek(fh, 0, SEEK_SET);
+
+	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 == LPM_LEAD_CHAR)
+			next = (struct lpm_rule *)
+				(route_rules + route_cnt * rule_size);
+
+		/* Illegal line */
+		else
+			rte_exit(EXIT_FAILURE,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, LPM_LEAD_CHAR);
+
+		if (parser(buff + 1, next) != 0)
+			rte_exit(EXIT_FAILURE,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = (struct lpm_rule *)route_rules;
+	*proute_num = route_cnt;
+
+	return 0;
+}
+
+static int
+check_lpm_config(void)
+{
+	if (parm_config.rule_ipv4_name == NULL) {
+		RTE_LOG(ERR, L3FWD, "LPM IPv4 rule file not specified\n");
+		return -1;
+	} else if (parm_config.rule_ipv6_name == NULL) {
+		RTE_LOG(ERR, L3FWD, "LPM IPv6 rule file not specified\n");
+		return -1;
+	}
+
+	return 0;
+}
+
 /* main processing loop */
 int
 lpm_main_loop(__attribute__((unused)) void *dummy)
@@ -196,10 +380,16 @@ setup_lpm(const int socketid)
 {
 	struct rte_lpm6_config config;
 	struct rte_lpm_config config_ipv4;
+	struct lpm_rule *route_base_v4;
+	struct lpm_rule *route_base_v6;
+	unsigned int route_num_v4 = 0, route_num_v6 = 0;
 	unsigned i;
 	int ret;
 	char s[64];
 
+	if (check_lpm_config() != 0)
+		rte_exit(EXIT_FAILURE, "Failed to get valid LPM options\n");
+
 	/* create the LPM table */
 	config_ipv4.max_rules = IPV4_L3FWD_LPM_MAX_RULES;
 	config_ipv4.number_tbl8s = IPV4_L3FWD_LPM_NUMBER_TBL8S;
@@ -212,18 +402,25 @@ setup_lpm(const int socketid)
 			"Unable to create v4 LPM table on socket %d\n",
 			socketid);
 
+	/* Load rules from the input file */
+	if (lpm_add_rules(parm_config.rule_ipv4_name,
+		&route_base_v4, &route_num_v4,
+		&lpm_parse_v4_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add lpm v4 rules\n");
+
 	/* populate the LPM table */
-	for (i = 0; i < IPV4_L3FWD_LPM_NUM_ROUTES; i++) {
+	for (i = 0; i < route_num_v4; i++) {
 
 		/* skip unused ports */
-		if ((1 << ipv4_l3fwd_lpm_route_array[i].if_out &
-				enabled_port_mask) == 0)
+		if ((1 << route_base_v4[i].if_out &
+			enabled_port_mask) == 0)
 			continue;
 
-		ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
-				ipv4_l3fwd_lpm_route_array[i].ip,
-				ipv4_l3fwd_lpm_route_array[i].depth,
-				ipv4_l3fwd_lpm_route_array[i].if_out);
+		ret = rte_lpm_add(
+			ipv4_l3fwd_lpm_lookup_struct[socketid],
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE,
@@ -231,9 +428,9 @@ setup_lpm(const int socketid)
 		}
 
 		printf("LPM: Adding route 0x%08x / %d (%d)\n",
-			(unsigned)ipv4_l3fwd_lpm_route_array[i].ip,
-			ipv4_l3fwd_lpm_route_array[i].depth,
-			ipv4_l3fwd_lpm_route_array[i].if_out);
+			(unsigned int)route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 	}
 
 	/* create the LPM6 table */
@@ -249,18 +446,25 @@ setup_lpm(const int socketid)
 			"Unable to create v6 LPM table on socket %d\n",
 			socketid);
 
+	/* Load rules from the input file */
+	if (lpm_add_rules(parm_config.rule_ipv6_name,
+		&route_base_v6, &route_num_v6,
+		&lpm_parse_v6_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add lpm v6 rules\n");
+
 	/* populate the LPM table */
-	for (i = 0; i < IPV6_L3FWD_LPM_NUM_ROUTES; i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* skip unused ports */
-		if ((1 << ipv6_l3fwd_lpm_route_array[i].if_out &
-				enabled_port_mask) == 0)
+		if ((1 << route_base_v6[i].if_out &
+			enabled_port_mask) == 0)
 			continue;
 
-		ret = rte_lpm6_add(ipv6_l3fwd_lpm_lookup_struct[socketid],
-			ipv6_l3fwd_lpm_route_array[i].ip,
-			ipv6_l3fwd_lpm_route_array[i].depth,
-			ipv6_l3fwd_lpm_route_array[i].if_out);
+		ret = rte_lpm6_add(
+			ipv6_l3fwd_lpm_lookup_struct[socketid],
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE,
@@ -268,8 +472,8 @@ setup_lpm(const int socketid)
 		}
 
 		printf("LPM: Adding route %s / %d (%d)\n",
-			"IPV6", ipv6_l3fwd_lpm_route_array[i].depth,
-			ipv6_l3fwd_lpm_route_array[i].if_out);
+			"IPV6", route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 	}
 }
 
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index 3f35762..c66c123 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -126,6 +126,8 @@ uint32_t hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT;
 
 struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+struct parm_cfg parm_config;
+
 struct lcore_params {
 	uint8_t port_id;
 	uint8_t queue_id;
@@ -212,6 +214,27 @@ static struct l3fwd_lkp_mode l3fwd_acl_lkp = {
 };
 
 /*
+ * API's called during initialization to setup ACL/LPM/EM rules.
+ */
+static void
+l3fwd_set_rule_ipv4_name(const char *optarg)
+{
+	parm_config.rule_ipv4_name = optarg;
+}
+
+static void
+l3fwd_set_rule_ipv6_name(const char *optarg)
+{
+	parm_config.rule_ipv6_name = optarg;
+}
+
+static void
+l3fwd_set_scalar(void)
+{
+	parm_config.scalar = 1;
+}
+
+/*
  * Setup lookup methods for forwarding.
  * Currently exact-match and longest-prefix-match
  * are supported ones.
@@ -689,15 +712,15 @@ parse_args(int argc, char **argv)
 			break;
 
 		case CMD_LINE_OPT_RULE_IPV4:
-			l3fwd_acl_set_rule_ipv4_name(optarg);
+			l3fwd_set_rule_ipv4_name(optarg);
 			break;
 
 		case CMD_LINE_OPT_RULE_IPV6:
-			l3fwd_acl_set_rule_ipv6_name(optarg);
+			l3fwd_set_rule_ipv6_name(optarg);
 			break;
 
 		case CMD_LINE_OPT_SCALAR:
-			l3fwd_acl_set_scalar();
+			l3fwd_set_scalar();
 			break;
 
 		default:
-- 
2.7.4

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [dpdk-dev] [v4 3/3] EM config file read option.
  2017-03-05 19:47 [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd Ravi Kerur
  2017-03-05 19:47 ` [dpdk-dev] [v4 1/3] Merge l3fwd-acl and l3fwd code Ravi Kerur
  2017-03-05 19:47 ` [dpdk-dev] [v4 2/3] LPM config file read option Ravi Kerur
@ 2017-03-05 19:47 ` Ravi Kerur
  2017-03-06 13:42   ` Mcnamara, John
  2017-03-06 13:33 ` [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd Mcnamara, John
  3 siblings, 1 reply; 34+ messages in thread
From: Ravi Kerur @ 2017-03-05 19:47 UTC (permalink / raw)
  To: dev; +Cc: konstantin.ananyev, bruce.richardson, Ravi Kerur

v4:
	> No changes.

v3:
	> Fix additional checkpatch coding style issues.

v2:
	> Fix checkpatch warnings.

v1:
	> Remove static array configuration of Dest IP,Src IP, Dest
		port, Src port, Proto and IF_OUT for EM and EM6 config.
	> Add reading configuration from a file.
	> Format of configuration file is as follows
		#EM route entries,
		#Dest-IP Src-IP Dest-port Src-port Proto IF_OUT
		E101.0.0.0 100.10.0.0 101 11 0x06 0
		E201.0.0.0 200.20.0.0 102 12 0x06 1
		E111.0.0.0 211.30.0.0 101 11 0x06 2
		...

		#EM6 route entries
		#Dest-IP Src-IP Dest-port Src-port Proto IF_OUT
		Efe80:0000:0000:0000:021e:67ff:fe00:0000
			fe80:0000:0000:0000:021b:21ff:fe91:3805 101 11 0x06 0
		Efe90:0000:0000:0000:021e:67ff:fe00:0000
			fe90:0000:0000:0000:021b:21ff:fe91:3805 102 12 0x06 1
                ...

Signed-off-by: Ravi Kerur <rkerur@gmail.com>
---
 examples/l3fwd/l3fwd_em.c | 376 +++++++++++++++++++++++++++++++++++++---------
 1 file changed, 303 insertions(+), 73 deletions(-)

diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index 6fdabf7..cd6b443 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -95,8 +95,14 @@ union ipv4_5tuple_host {
 #define XMM_NUM_IN_IPV6_5TUPLE 3
 
 struct ipv6_5tuple {
-	uint8_t  ip_dst[IPV6_ADDR_LEN];
-	uint8_t  ip_src[IPV6_ADDR_LEN];
+	union {
+		uint8_t  ip_dst[IPV6_ADDR_LEN];
+		uint32_t ip32_dst[4];
+	};
+	union {
+		uint8_t  ip_src[IPV6_ADDR_LEN];
+		uint32_t ip32_src[4];
+	};
 	uint16_t port_dst;
 	uint16_t port_src;
 	uint8_t  proto;
@@ -116,47 +122,24 @@ union ipv6_5tuple_host {
 	xmm_t xmm[XMM_NUM_IN_IPV6_5TUPLE];
 };
 
-
-
-struct ipv4_l3fwd_em_route {
-	struct ipv4_5tuple key;
-	uint8_t if_out;
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_SRC_ADDR,
+	CB_FLD_DST_PORT,
+	CB_FLD_SRC_PORT,
+	CB_FLD_PROTO,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
 };
 
-struct ipv6_l3fwd_em_route {
-	struct ipv6_5tuple key;
+struct em_rule {
+	union {
+		struct ipv4_5tuple v4_key;
+		struct ipv6_5tuple v6_key;
+	};
 	uint8_t if_out;
 };
 
-static struct ipv4_l3fwd_em_route ipv4_l3fwd_em_route_array[] = {
-	{{IPv4(101, 0, 0, 0), IPv4(100, 10, 0, 1),  101, 11, IPPROTO_TCP}, 0},
-	{{IPv4(201, 0, 0, 0), IPv4(200, 20, 0, 1),  102, 12, IPPROTO_TCP}, 1},
-	{{IPv4(111, 0, 0, 0), IPv4(100, 30, 0, 1),  101, 11, IPPROTO_TCP}, 2},
-	{{IPv4(211, 0, 0, 0), IPv4(200, 40, 0, 1),  102, 12, IPPROTO_TCP}, 3},
-};
-
-static struct ipv6_l3fwd_em_route ipv6_l3fwd_em_route_array[] = {
-	{{
-	{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x02, 0x1e, 0x67, 0xff, 0xfe, 0, 0, 0},
-	{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x02, 0x1b, 0x21, 0xff, 0xfe, 0x91, 0x38, 0x05},
-	101, 11, IPPROTO_TCP}, 0},
-
-	{{
-	{0xfe, 0x90, 0, 0, 0, 0, 0, 0, 0x02, 0x1e, 0x67, 0xff, 0xfe, 0, 0, 0},
-	{0xfe, 0x90, 0, 0, 0, 0, 0, 0, 0x02, 0x1b, 0x21, 0xff, 0xfe, 0x91, 0x38, 0x05},
-	102, 12, IPPROTO_TCP}, 1},
-
-	{{
-	{0xfe, 0xa0, 0, 0, 0, 0, 0, 0, 0x02, 0x1e, 0x67, 0xff, 0xfe, 0, 0, 0},
-	{0xfe, 0xa0, 0, 0, 0, 0, 0, 0, 0x02, 0x1b, 0x21, 0xff, 0xfe, 0x91, 0x38, 0x05},
-	101, 11, IPPROTO_TCP}, 2},
-
-	{{
-	{0xfe, 0xb0, 0, 0, 0, 0, 0, 0, 0x02, 0x1e, 0x67, 0xff, 0xfe, 0, 0, 0},
-	{0xfe, 0xb0, 0, 0, 0, 0, 0, 0, 0x02, 0x1b, 0x21, 0xff, 0xfe, 0x91, 0x38, 0x05},
-	102, 12, IPPROTO_TCP}, 3},
-};
-
 struct rte_hash *ipv4_l3fwd_em_lookup_struct[NB_SOCKETS];
 struct rte_hash *ipv6_l3fwd_em_lookup_struct[NB_SOCKETS];
 
@@ -233,12 +216,6 @@ ipv6_hash_crc(const void *data, __rte_unused uint32_t data_len,
 	return init_val;
 }
 
-#define IPV4_L3FWD_EM_NUM_ROUTES \
-	(sizeof(ipv4_l3fwd_em_route_array) / sizeof(ipv4_l3fwd_em_route_array[0]))
-
-#define IPV6_L3FWD_EM_NUM_ROUTES \
-	(sizeof(ipv6_l3fwd_em_route_array) / sizeof(ipv6_l3fwd_em_route_array[0]))
-
 static uint8_t ipv4_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 static uint8_t ipv6_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 
@@ -338,6 +315,224 @@ em_get_ipv6_dst_port(void *ipv6_hdr,  uint8_t portid, void *lookup_struct)
 #include "l3fwd_em.h"
 #endif
 
+static int
+em_parse_v6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
+	char dlm)
+{
+	uint32_t addr[IPV6_ADDR_U16];
+
+	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
+
+	*end = in;
+
+	v[0] = (addr[0] << 16) + addr[1];
+	v[1] = (addr[2] << 16) + addr[3];
+	v[2] = (addr[4] << 16) + addr[5];
+	v[3] = (addr[6] << 16) + addr[7];
+
+	return 0;
+}
+
+static int
+em_parse_v6_net(const char *in, uint32_t *v)
+{
+	int32_t rc;
+	const char *mp;
+
+	/* get address. */
+	rc = em_parse_v6_addr(in, &mp, v, 0);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_addr failed %d\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int
+em_parse_v6_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL) {
+			RTE_LOG(ERR, L3FWD, "strtok failed\n");
+			return -EINVAL;
+		}
+	}
+
+	rc = em_parse_v6_net(in[CB_FLD_DST_ADDR], v->v6_key.ip32_dst);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_net failed for dst %d\n", rc);
+		return rc;
+	}
+	rc = em_parse_v6_net(in[CB_FLD_SRC_ADDR], v->v6_key.ip32_src);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_net failed for src %d\n", rc);
+		return rc;
+	}
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v6_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v6_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v6_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_parse_v4_net(const char *in, uint32_t *addr)
+{
+	uint8_t a, b, c, d;
+
+	GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, d, 0, UINT8_MAX, 0);
+
+	addr[0] = IPv4(a, b, c, d);
+	return 0;
+}
+
+static int
+em_parse_v4_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL) {
+			RTE_LOG(ERR, L3FWD, "parse_v4_rule strtok fail\n");
+			return -EINVAL;
+		}
+	}
+
+	rc = em_parse_v4_net(in[CB_FLD_DST_ADDR], &(v->v4_key.ip_dst));
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v4_net dst failed %d\n", rc);
+		return rc;
+	}
+	rc = em_parse_v4_net(in[CB_FLD_SRC_ADDR], &(v->v4_key.ip_src));
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v4_net src failed %d\n", rc);
+		return rc;
+	}
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v4_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v4_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v4_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_add_rules(const char *rule_path,
+		struct em_rule **proute_base,
+		unsigned int *proute_num,
+		int (*parser)(char *, struct em_rule*))
+{
+	uint8_t *route_rules;
+	struct em_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh = fopen(rule_path, "rb");
+	unsigned int i = 0, rule_size = sizeof(*next);
+
+	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] == EM_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0)
+		rte_exit(EXIT_FAILURE, "Not find any route entries in %s!\n",
+				rule_path);
+
+	fseek(fh, 0, SEEK_SET);
+
+	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 == EM_LEAD_CHAR)
+			next = (struct em_rule *)(route_rules +
+				route_cnt * rule_size);
+
+		/* Illegal line */
+		else
+			rte_exit(EXIT_FAILURE,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, EM_LEAD_CHAR);
+
+		if (parser(buff + 1, next) != 0)
+			rte_exit(EXIT_FAILURE,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = (struct em_rule *)route_rules;
+	*proute_num = route_cnt;
+
+	return 0;
+}
+
+static int
+check_em_config(void)
+{
+	if (parm_config.rule_ipv4_name == NULL) {
+		RTE_LOG(ERR, L3FWD, "EM IPv4 rule file not specified\n");
+		return -1;
+	} else if (parm_config.rule_ipv6_name == NULL) {
+		RTE_LOG(ERR, L3FWD, "EM IPv6 rule file not specified\n");
+		return -1;
+	}
+
+	return 0;
+}
+
 static void
 convert_ipv4_5tuple(struct ipv4_5tuple *key1,
 		union ipv4_5tuple_host *key2)
@@ -378,16 +573,24 @@ populate_ipv4_few_flow_into_table(const struct rte_hash *h)
 {
 	uint32_t i;
 	int32_t ret;
+	struct em_rule *route_base_v4;
+	unsigned int route_num_v4 = 0;
 
 	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
-	for (i = 0; i < IPV4_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv4_l3fwd_em_route  entry;
+	/* Load rules from the input file */
+	if (em_add_rules(parm_config.rule_ipv4_name,
+		&route_base_v4, &route_num_v4,
+		&em_parse_v4_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add em v4 rules\n");
+
+	for (i = 0; i < route_num_v4; i++) {
+		struct em_rule  entry;
 		union ipv4_5tuple_host newkey;
 
-		entry = ipv4_l3fwd_em_route_array[i];
-		convert_ipv4_5tuple(&entry.key, &newkey);
+		entry = route_base_v4[i];
+		convert_ipv4_5tuple(&entry.v4_key, &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
@@ -396,7 +599,7 @@ populate_ipv4_few_flow_into_table(const struct rte_hash *h)
 		ipv4_l3fwd_out_if[ret] = entry.if_out;
 	}
 	printf("Hash: Adding 0x%" PRIx64 " keys\n",
-		(uint64_t)IPV4_L3FWD_EM_NUM_ROUTES);
+		(uint64_t)route_num_v4);
 }
 
 #define BIT_16_TO_23 0x00ff0000
@@ -405,18 +608,26 @@ populate_ipv6_few_flow_into_table(const struct rte_hash *h)
 {
 	uint32_t i;
 	int32_t ret;
+	struct em_rule *route_base_v6;
+	unsigned int route_num_v6 = 0;
 
 	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
 	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
 
-	for (i = 0; i < IPV6_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv6_l3fwd_em_route entry;
+	/* Load rules from the input file */
+	if (em_add_rules(parm_config.rule_ipv6_name,
+		&route_base_v6, &route_num_v6,
+		&em_parse_v6_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add em v6 rules\n");
+
+	for (i = 0; i < route_num_v6; i++) {
+		struct em_rule entry;
 		union ipv6_5tuple_host newkey;
 
-		entry = ipv6_l3fwd_em_route_array[i];
-		convert_ipv6_5tuple(&entry.key, &newkey);
+		entry = route_base_v6[i];
+		convert_ipv6_5tuple(&entry.v6_key, &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
@@ -425,7 +636,7 @@ populate_ipv6_few_flow_into_table(const struct rte_hash *h)
 		ipv6_l3fwd_out_if[ret] = entry.if_out;
 	}
 	printf("Hash: Adding 0x%" PRIx64 "keys\n",
-		(uint64_t)IPV6_L3FWD_EM_NUM_ROUTES);
+		(uint64_t)route_num_v6);
 }
 
 #define NUMBER_PORT_USED 4
@@ -434,12 +645,20 @@ populate_ipv4_many_flow_into_table(const struct rte_hash *h,
 		unsigned int nr_flow)
 {
 	unsigned i;
+	struct em_rule *route_base_v4;
+	unsigned int route_num_v4 = 0;
 
 	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
+	/* Load rules from the input file */
+	if (em_add_rules(parm_config.rule_ipv4_name,
+		&route_base_v4, &route_num_v4,
+		&em_parse_v4_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add em v4 rules\n");
+
 	for (i = 0; i < nr_flow; i++) {
-		struct ipv4_l3fwd_em_route entry;
+		struct em_rule entry;
 		union ipv4_5tuple_host newkey;
 
 		uint8_t a = (uint8_t)
@@ -453,23 +672,23 @@ populate_ipv4_many_flow_into_table(const struct rte_hash *h,
 		memset(&entry, 0, sizeof(entry));
 		switch (i & (NUMBER_PORT_USED - 1)) {
 		case 0:
-			entry = ipv4_l3fwd_em_route_array[0];
-			entry.key.ip_dst = IPv4(101, c, b, a);
+			entry = route_base_v4[0];
+			entry.v4_key.ip_dst = IPv4(101, c, b, a);
 			break;
 		case 1:
-			entry = ipv4_l3fwd_em_route_array[1];
-			entry.key.ip_dst = IPv4(201, c, b, a);
+			entry = route_base_v4[1];
+			entry.v4_key.ip_dst = IPv4(201, c, b, a);
 			break;
 		case 2:
-			entry = ipv4_l3fwd_em_route_array[2];
-			entry.key.ip_dst = IPv4(111, c, b, a);
+			entry = route_base_v4[2];
+			entry.v4_key.ip_dst = IPv4(111, c, b, a);
 			break;
 		case 3:
-			entry = ipv4_l3fwd_em_route_array[3];
-			entry.key.ip_dst = IPv4(211, c, b, a);
+			entry = route_base_v4[3];
+			entry.v4_key.ip_dst = IPv4(211, c, b, a);
 			break;
 		};
-		convert_ipv4_5tuple(&entry.key, &newkey);
+		convert_ipv4_5tuple(&entry.v4_key, &newkey);
 		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
 
 		if (ret < 0)
@@ -486,13 +705,21 @@ populate_ipv6_many_flow_into_table(const struct rte_hash *h,
 		unsigned int nr_flow)
 {
 	unsigned i;
+	struct em_rule *route_base_v6;
+	unsigned int route_num_v6 = 0;
 
 	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
 
+	/* Load rules from the input file */
+	if (em_add_rules(parm_config.rule_ipv6_name,
+		&route_base_v6, &route_num_v6,
+		&em_parse_v6_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add em v6 rules\n");
+
 	for (i = 0; i < nr_flow; i++) {
-		struct ipv6_l3fwd_em_route entry;
+		struct em_rule entry;
 		union ipv6_5tuple_host newkey;
 
 		uint8_t a = (uint8_t)
@@ -506,22 +733,22 @@ populate_ipv6_many_flow_into_table(const struct rte_hash *h,
 		memset(&entry, 0, sizeof(entry));
 		switch (i & (NUMBER_PORT_USED - 1)) {
 		case 0:
-			entry = ipv6_l3fwd_em_route_array[0];
+			entry = route_base_v6[0];
 			break;
 		case 1:
-			entry = ipv6_l3fwd_em_route_array[1];
+			entry = route_base_v6[1];
 			break;
 		case 2:
-			entry = ipv6_l3fwd_em_route_array[2];
+			entry = route_base_v6[2];
 			break;
 		case 3:
-			entry = ipv6_l3fwd_em_route_array[3];
+			entry = route_base_v6[3];
 			break;
 		};
-		entry.key.ip_dst[13] = c;
-		entry.key.ip_dst[14] = b;
-		entry.key.ip_dst[15] = a;
-		convert_ipv6_5tuple(&entry.key, &newkey);
+		entry.v6_key.ip_dst[13] = c;
+		entry.v6_key.ip_dst[14] = b;
+		entry.v6_key.ip_dst[15] = a;
+		convert_ipv6_5tuple(&entry.v6_key, &newkey);
 		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
 
 		if (ret < 0)
@@ -746,6 +973,9 @@ setup_hash(const int socketid)
 
 	char s[64];
 
+	if (check_em_config() != 0)
+		rte_exit(EXIT_FAILURE, "Failed to get valid EM options\n");
+
 	/* create ipv4 hash */
 	snprintf(s, sizeof(s), "ipv4_l3fwd_hash_%d", socketid);
 	ipv4_l3fwd_hash_params.name = s;
-- 
2.7.4

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd
  2017-03-05 19:47 [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd Ravi Kerur
                   ` (2 preceding siblings ...)
  2017-03-05 19:47 ` [dpdk-dev] [v4 3/3] EM " Ravi Kerur
@ 2017-03-06 13:33 ` Mcnamara, John
  2017-03-06 23:20   ` Ravi Kerur
  3 siblings, 1 reply; 34+ messages in thread
From: Mcnamara, John @ 2017-03-06 13:33 UTC (permalink / raw)
  To: Ravi Kerur, dev; +Cc: Ananyev, Konstantin, Richardson, Bruce

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ravi Kerur
> Sent: Sunday, March 5, 2017 7:47 PM
> To: dev@dpdk.org
> Cc: Ananyev, Konstantin <konstantin.ananyev@intel.com>; Richardson, Bruce
> <bruce.richardson@intel.com>; Ravi Kerur <rkerur@gmail.com>
> Subject: [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd
> 
> This patchset merges l3fwd-acl and l3fwd code into common directory.
> Adds file read options to build LPM and EM tables.

Hi Ravi,

Thanks to this. It seems like a good change. There are probably too many
L2/L3 variants and some of them should be merged.

Note, you will also have to merge the sample app guides in some sensible
way:

    http://dpdk.org/doc/guides/sample_app_ug/l3_forward.html
    http://dpdk.org/doc/guides/sample_app_ug/l3_forward_access_ctrl.html

John

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [v4 3/3] EM config file read option.
  2017-03-05 19:47 ` [dpdk-dev] [v4 3/3] EM " Ravi Kerur
@ 2017-03-06 13:42   ` Mcnamara, John
  0 siblings, 0 replies; 34+ messages in thread
From: Mcnamara, John @ 2017-03-06 13:42 UTC (permalink / raw)
  To: Ravi Kerur, dev; +Cc: Ananyev, Konstantin, Richardson, Bruce



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ravi Kerur
> Sent: Sunday, March 5, 2017 7:47 PM
> To: dev@dpdk.org
> Cc: Ananyev, Konstantin <konstantin.ananyev@intel.com>; Richardson, Bruce
> <bruce.richardson@intel.com>; Ravi Kerur <rkerur@gmail.com>
> Subject: [dpdk-dev] [v4 3/3] EM config file read option.
> 
> v4:
> 	> No changes.
> 
> v3:
> 	> Fix additional checkpatch coding style issues.
> 
> v2:
> 	> Fix checkpatch warnings.
> 
> v1:
> 	> Remove static array configuration of Dest IP,Src IP, Dest
> 		port, Src port, Proto and IF_OUT for EM and EM6 config.
> 	> Add reading configuration from a file.
> 	> Format of configuration file is as follows
> 		#EM route entries,
> 		#Dest-IP Src-IP Dest-port Src-port Proto IF_OUT
> 		E101.0.0.0 100.10.0.0 101 11 0x06 0
> 		E201.0.0.0 200.20.0.0 102 12 0x06 1
> 		E111.0.0.0 211.30.0.0 101 11 0x06 2
> 		...
> 
> 		#EM6 route entries
> 		#Dest-IP Src-IP Dest-port Src-port Proto IF_OUT
> 		Efe80:0000:0000:0000:021e:67ff:fe00:0000
> 			fe80:0000:0000:0000:021b:21ff:fe91:3805 101 11 0x06 0
> 		Efe90:0000:0000:0000:021e:67ff:fe00:0000
> 			fe90:0000:0000:0000:021b:21ff:fe91:3805 102 12 0x06 1
>                 ...

Hi Ravi,

Just a note on the structure of the commit message for this and the other
patches. The first thing to do if you are new to contributing to DPDK is
to have a read through the guidelines on "Contributing Code to DPDK":

   http://dpdk.org/doc/guides/contributing/patches.html

In particular the subject line should contain the component that is being
modified. Something like:

    examples/l3fwd: add config file support for exact match

Do a git log examples/l3fwd to see some other examples and have a look at
the following "Commit Messages: Subject Line" section of the above guidelines

    http://dpdk.org/doc/guides/contributing/patches.html#commit-messages-subject-line

The body of the message should contain a short description of what you are changing,
at a high level, and why you are changing it. See also:

    http://dpdk.org/doc/guides/contributing/patches.html#commit-messages-body

The version information in your commit message is good but that should be included
after the --- line so that it is part of the patch but not part of the commit
message.

John.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [v4 2/3] LPM config file read option.
  2017-03-05 19:47 ` [dpdk-dev] [v4 2/3] LPM config file read option Ravi Kerur
@ 2017-03-06 13:57   ` Mcnamara, John
  2017-03-06 14:10     ` Mcnamara, John
  0 siblings, 1 reply; 34+ messages in thread
From: Mcnamara, John @ 2017-03-06 13:57 UTC (permalink / raw)
  To: Ravi Kerur, dev; +Cc: Ananyev, Konstantin, Richardson, Bruce



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ravi Kerur
> Sent: Sunday, March 5, 2017 7:47 PM
> To: dev@dpdk.org
> Cc: Ananyev, Konstantin <konstantin.ananyev@intel.com>; Richardson, Bruce
> <bruce.richardson@intel.com>; Ravi Kerur <rkerur@gmail.com>
> Subject: [dpdk-dev] [v4 2/3] LPM config file read option.
>
> ...
> +
> +#define	IPV6_ADDR_LEN	16
> +#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
> +#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
> +
> +#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
> +	unsigned long val;                                      \
> +	char *end;                                              \
> +	errno = 0;                                              \
> +	val = strtoul((in), &end, (base));                      \
> +	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
> +		return -EINVAL;                               \
> +	(fd) = (typeof(fd))val;                                 \
> +	(in) = end + 1;                                         \
> +} while (0)

Hi,

It is probably worth putting a comment before this macro to
explain what it does. Also, it isn't clear, to me, what CB
stands for. Also, having a return in the middle of the macro
might be problematic if it is used in a function with a
different, or not, return value.

John

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [v4 2/3] LPM config file read option.
  2017-03-06 13:57   ` Mcnamara, John
@ 2017-03-06 14:10     ` Mcnamara, John
  0 siblings, 0 replies; 34+ messages in thread
From: Mcnamara, John @ 2017-03-06 14:10 UTC (permalink / raw)
  To: Mcnamara, John, Ravi Kerur, dev; +Cc: Ananyev, Konstantin, Richardson, Bruce



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Mcnamara, John
> Sent: Monday, March 6, 2017 1:57 PM
> To: Ravi Kerur <rkerur@gmail.com>; dev@dpdk.org
> Cc: Ananyev, Konstantin <konstantin.ananyev@intel.com>; Richardson, Bruce
> <bruce.richardson@intel.com>
> Subject: Re: [dpdk-dev] [v4 2/3] LPM config file read option.
> 
> 
> 
> > -----Original Message-----
> > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ravi Kerur
> > Sent: Sunday, March 5, 2017 7:47 PM
> > To: dev@dpdk.org
> > Cc: Ananyev, Konstantin <konstantin.ananyev@intel.com>; Richardson,
> > Bruce <bruce.richardson@intel.com>; Ravi Kerur <rkerur@gmail.com>
> > Subject: [dpdk-dev] [v4 2/3] LPM config file read option.
> >
> > ...
> > +
> > +#define	IPV6_ADDR_LEN	16
> > +#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
> > +#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
> > +
> > +#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
> > +	unsigned long val;                                      \
> > +	char *end;                                              \
> > +	errno = 0;                                              \
> > +	val = strtoul((in), &end, (base));                      \
> > +	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
> > +		return -EINVAL;                               \
> > +	(fd) = (typeof(fd))val;                                 \
> > +	(in) = end + 1;                                         \
> > +} while (0)
> 
> Hi,
> 
> It is probably worth putting a comment before this macro to explain what
> it does. Also, it isn't clear, to me, what CB stands for. Also, having a
> return in the middle of the macro might be problematic if it is used in a
> function with a different, or not, return value.
> 

Nevermind. I see that this macro was already there and you just moved it.
So, it is not your problem. :-)

John

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd
  2017-03-06 13:33 ` [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd Mcnamara, John
@ 2017-03-06 23:20   ` Ravi Kerur
  2017-03-08 21:50     ` Ravi Kerur
  2017-03-09 10:23     ` Mcnamara, John
  0 siblings, 2 replies; 34+ messages in thread
From: Ravi Kerur @ 2017-03-06 23:20 UTC (permalink / raw)
  To: Mcnamara, John; +Cc: dev, Ananyev, Konstantin, Richardson, Bruce

Hi John,

Should I work with documentation team to update the document? If yes,
please let me know the contact information.

Thanks.

On Mon, Mar 6, 2017 at 5:33 AM, Mcnamara, John <john.mcnamara@intel.com>
wrote:

> > -----Original Message-----
> > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ravi Kerur
> > Sent: Sunday, March 5, 2017 7:47 PM
> > To: dev@dpdk.org
> > Cc: Ananyev, Konstantin <konstantin.ananyev@intel.com>; Richardson,
> Bruce
> > <bruce.richardson@intel.com>; Ravi Kerur <rkerur@gmail.com>
> > Subject: [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd
> >
> > This patchset merges l3fwd-acl and l3fwd code into common directory.
> > Adds file read options to build LPM and EM tables.
>
> Hi Ravi,
>
> Thanks to this. It seems like a good change. There are probably too many
> L2/L3 variants and some of them should be merged.
>
> Note, you will also have to merge the sample app guides in some sensible
> way:
>
>     http://dpdk.org/doc/guides/sample_app_ug/l3_forward.html
>     http://dpdk.org/doc/guides/sample_app_ug/l3_forward_access_ctrl.html
>
> John
>
>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [dpdk-dev] [PATCH v5 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
  2017-03-05 19:47 ` [dpdk-dev] [v4 1/3] Merge l3fwd-acl and l3fwd code Ravi Kerur
@ 2017-03-08 21:32   ` Ravi Kerur
  2017-03-08 21:32     ` [dpdk-dev] [PATCH v5 1/3] " Ravi Kerur
                       ` (2 more replies)
  0 siblings, 3 replies; 34+ messages in thread
From: Ravi Kerur @ 2017-03-08 21:32 UTC (permalink / raw)
  To: dev, konstantin.ananyev, bruce.richardson; +Cc: Ravi Kerur

This patchset merges l3fwd-acl and l3fwd code into common directory.
Adds file read options to build LPM and EM tables.

Ravi Kerur (3):
  examples/l3fwd: Merge l3fwd-acl code into l3fwd
  examples/l3fwd: add config file support for LPM
  examples/l3fwd: add config file support for EM

 examples/l3fwd-acl/Makefile       |   56 -
 examples/l3fwd-acl/main.c         | 2079 -------------------------------------
 examples/l3fwd/Makefile           |    2 +-
 examples/l3fwd/l3fwd.h            |   77 ++
 examples/l3fwd/l3fwd_acl.c        | 1033 ++++++++++++++++++
 examples/l3fwd/l3fwd_acl.h        |  234 +++++
 examples/l3fwd/l3fwd_acl_scalar.h |  182 ++++
 examples/l3fwd/l3fwd_em.c         |  390 +++++--
 examples/l3fwd/l3fwd_lpm.c        |  323 ++++--
 examples/l3fwd/main.c             |  250 +++--
 10 files changed, 2286 insertions(+), 2340 deletions(-)
 delete mode 100644 examples/l3fwd-acl/Makefile
 delete mode 100644 examples/l3fwd-acl/main.c
 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

-- 
2.7.4

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [dpdk-dev] [PATCH v5 1/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
  2017-03-08 21:32   ` [dpdk-dev] [PATCH v5 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd Ravi Kerur
@ 2017-03-08 21:32     ` Ravi Kerur
  2017-03-09 10:42       ` Mcnamara, John
  2017-03-10 20:58       ` [dpdk-dev] [PATCH v6 0/3] " Ravi Kerur
  2017-03-08 21:32     ` [dpdk-dev] [PATCH v5 2/3] examples/l3fwd: add config file support for LPM Ravi Kerur
  2017-03-08 21:32     ` [dpdk-dev] [PATCH v5 3/3] examples/l3fwd: add config file support for EM Ravi Kerur
  2 siblings, 2 replies; 34+ messages in thread
From: Ravi Kerur @ 2017-03-08 21:32 UTC (permalink / raw)
  To: dev, konstantin.ananyev, bruce.richardson; +Cc: Ravi Kerur

Merge l3fwd-acl code into l3fwd with '-A' cmdline option to run ACL.

v5:
	> None.

v4:
        > Initialize rss_hf to IP for LPM, EM and ACL.
        > Update rss_hf with l4 in parse_args for ACL.
        > Fix pending checkpatch code indentation warning.

v3:
        > Fix additional checkpatch coding style issues.

v2:
        > Fix checkpatch errors and warnings related to non strings
                greater than 80 characters.
        > MACRO GET_CB_FIELD and strings greater than 80 characters
                warnings are not fixed.

v1:
        l3fwd-acl changes:
                > Merge common init code in l3fwd-acl and l3fwd into
                        main.c.
                > Move non-critical inline functions to l3fwd_acl.h.
                > Move critial packet processing inline functions to
                        l3fwd_acl_scalar.h
                > Move l3fwd-acl init code to l3fwd_acl.c.
                > Delete l3fwd-acl directory.

        l3fwd changes:
                > Add '-A' as an option for ACL processing.
                > Merge parsing options from l3fwd-acl and l3fwd. Retain
                        l3fwd-acl definitions.
                > Move specific setup functions (setup_acl, setup_lpm
                        and setup_hash).
        Testing:
                > Compiled successfully for x86_64-native-linuxapp-gcc
                        > Tested LPM, EM and ACL basic functionality.

Signed-off-by: Ravi Kerur <rkerur@gmail.com>
---
 examples/l3fwd-acl/Makefile       |   56 -
 examples/l3fwd-acl/main.c         | 2079 -------------------------------------
 examples/l3fwd/Makefile           |    2 +-
 examples/l3fwd/l3fwd.h            |   49 +
 examples/l3fwd/l3fwd_acl.c        | 1064 +++++++++++++++++++
 examples/l3fwd/l3fwd_acl.h        |  263 +++++
 examples/l3fwd/l3fwd_acl_scalar.h |  182 ++++
 examples/l3fwd/l3fwd_em.c         |   14 +-
 examples/l3fwd/l3fwd_lpm.c        |   23 +-
 examples/l3fwd/main.c             |  209 ++--
 10 files changed, 1722 insertions(+), 2219 deletions(-)
 delete mode 100644 examples/l3fwd-acl/Makefile
 delete mode 100644 examples/l3fwd-acl/main.c
 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-acl/Makefile b/examples/l3fwd-acl/Makefile
deleted file mode 100644
index a3473a8..0000000
--- a/examples/l3fwd-acl/Makefile
+++ /dev/null
@@ -1,56 +0,0 @@
-#   BSD LICENSE
-#
-#   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
-#   All rights reserved.
-#
-#   Redistribution and use in source and binary forms, with or without
-#   modification, are permitted provided that the following conditions
-#   are met:
-#
-#     * Redistributions of source code must retain the above copyright
-#       notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above copyright
-#       notice, this list of conditions and the following disclaimer in
-#       the documentation and/or other materials provided with the
-#       distribution.
-#     * Neither the name of Intel Corporation nor the names of its
-#       contributors may be used to endorse or promote products derived
-#       from this software without specific prior written permission.
-#
-#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-ifeq ($(RTE_SDK),)
-$(error "Please define RTE_SDK environment variable")
-endif
-
-# Default target, can be overriden by command line or environment
-RTE_TARGET ?= x86_64-native-linuxapp-gcc
-
-include $(RTE_SDK)/mk/rte.vars.mk
-
-# binary name
-APP = l3fwd-acl
-
-# all source are stored in SRCS-y
-SRCS-y := main.c
-
-CFLAGS += -O3
-CFLAGS += $(WERROR_FLAGS)
-
-# workaround for a gcc bug with noreturn attribute
-# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
-ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y)
-CFLAGS_main.o += -Wno-return-type
-endif
-
-include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/l3fwd-acl/main.c b/examples/l3fwd-acl/main.c
deleted file mode 100644
index 3cfbb40..0000000
--- a/examples/l3fwd-acl/main.c
+++ /dev/null
@@ -1,2079 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <sys/types.h>
-#include <string.h>
-#include <sys/queue.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <getopt.h>
-
-#include <rte_common.h>
-#include <rte_byteorder.h>
-#include <rte_log.h>
-#include <rte_memory.h>
-#include <rte_memcpy.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_per_lcore.h>
-#include <rte_launch.h>
-#include <rte_atomic.h>
-#include <rte_cycles.h>
-#include <rte_prefetch.h>
-#include <rte_lcore.h>
-#include <rte_per_lcore.h>
-#include <rte_branch_prediction.h>
-#include <rte_interrupts.h>
-#include <rte_pci.h>
-#include <rte_random.h>
-#include <rte_debug.h>
-#include <rte_ether.h>
-#include <rte_ethdev.h>
-#include <rte_mempool.h>
-#include <rte_mbuf.h>
-#include <rte_ip.h>
-#include <rte_tcp.h>
-#include <rte_udp.h>
-#include <rte_string_fns.h>
-#include <rte_acl.h>
-
-#if RTE_LOG_LEVEL >= RTE_LOG_DEBUG
-#define L3FWDACL_DEBUG
-#endif
-#define DO_RFC_1812_CHECKS
-
-#define RTE_LOGTYPE_L3FWD RTE_LOGTYPE_USER1
-
-#define MAX_JUMBO_PKT_LEN  9600
-
-#define MEMPOOL_CACHE_SIZE 256
-
-/*
- * This expression is used to calculate the number of mbufs needed
- * depending on user input, taking into account memory for rx and tx hardware
- * rings, cache per lcore and mtable per port per lcore.
- * RTE_MAX is used to ensure that NB_MBUF never goes below a
- * minimum value of 8192
- */
-
-#define NB_MBUF	RTE_MAX(\
-	(nb_ports * nb_rx_queue*RTE_TEST_RX_DESC_DEFAULT +	\
-	nb_ports * nb_lcores * MAX_PKT_BURST +			\
-	nb_ports * n_tx_queue * RTE_TEST_TX_DESC_DEFAULT +	\
-	nb_lcores * MEMPOOL_CACHE_SIZE),			\
-	(unsigned)8192)
-
-#define MAX_PKT_BURST 32
-#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */
-
-#define NB_SOCKETS 8
-
-/* Configure how many packets ahead to prefetch, when reading packets */
-#define PREFETCH_OFFSET	3
-
-/*
- * Configurable number of RX/TX ring descriptors
- */
-#define RTE_TEST_RX_DESC_DEFAULT 128
-#define RTE_TEST_TX_DESC_DEFAULT 512
-static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
-static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
-
-/* ethernet addresses of ports */
-static struct ether_addr ports_eth_addr[RTE_MAX_ETHPORTS];
-
-/* mask of enabled ports */
-static uint32_t enabled_port_mask;
-static int promiscuous_on; /**< Ports set in promiscuous mode off by default. */
-static int numa_on = 1; /**< NUMA is enabled by default. */
-
-struct lcore_rx_queue {
-	uint8_t port_id;
-	uint8_t queue_id;
-} __rte_cache_aligned;
-
-#define MAX_RX_QUEUE_PER_LCORE 16
-#define MAX_TX_QUEUE_PER_PORT RTE_MAX_ETHPORTS
-#define MAX_RX_QUEUE_PER_PORT 128
-
-#define MAX_LCORE_PARAMS 1024
-struct lcore_params {
-	uint8_t port_id;
-	uint8_t queue_id;
-	uint8_t lcore_id;
-} __rte_cache_aligned;
-
-static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS];
-static struct lcore_params lcore_params_array_default[] = {
-	{0, 0, 2},
-	{0, 1, 2},
-	{0, 2, 2},
-	{1, 0, 2},
-	{1, 1, 2},
-	{1, 2, 2},
-	{2, 0, 2},
-	{3, 0, 3},
-	{3, 1, 3},
-};
-
-static struct lcore_params *lcore_params = lcore_params_array_default;
-static uint16_t nb_lcore_params = sizeof(lcore_params_array_default) /
-				sizeof(lcore_params_array_default[0]);
-
-static struct rte_eth_conf port_conf = {
-	.rxmode = {
-		.mq_mode	= ETH_MQ_RX_RSS,
-		.max_rx_pkt_len = ETHER_MAX_LEN,
-		.split_hdr_size = 0,
-		.header_split   = 0, /**< Header Split disabled */
-		.hw_ip_checksum = 1, /**< IP checksum offload enabled */
-		.hw_vlan_filter = 0, /**< VLAN filtering disabled */
-		.jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
-		.hw_strip_crc   = 0, /**< CRC stripped by hardware */
-	},
-	.rx_adv_conf = {
-		.rss_conf = {
-			.rss_key = NULL,
-			.rss_hf = ETH_RSS_IP | ETH_RSS_UDP |
-				ETH_RSS_TCP | ETH_RSS_SCTP,
-		},
-	},
-	.txmode = {
-		.mq_mode = ETH_MQ_TX_NONE,
-	},
-};
-
-static struct rte_mempool *pktmbuf_pool[NB_SOCKETS];
-
-/***********************start of ACL part******************************/
-#ifdef DO_RFC_1812_CHECKS
-static inline int
-is_valid_ipv4_pkt(struct ipv4_hdr *pkt, uint32_t link_len);
-#endif
-static inline void
-send_single_packet(struct rte_mbuf *m, uint8_t port);
-
-#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_LEAD_CHAR		('@')
-#define ROUTE_LEAD_CHAR		('R')
-#define COMMENT_LEAD_CHAR	('#')
-#define OPTION_CONFIG		"config"
-#define OPTION_NONUMA		"no-numa"
-#define OPTION_ENBJMO		"enable-jumbo"
-#define OPTION_RULE_IPV4	"rule_ipv4"
-#define OPTION_RULE_IPV6	"rule_ipv6"
-#define OPTION_SCALAR		"scalar"
-#define ACL_DENY_SIGNATURE	0xf0000000
-#define RTE_LOGTYPE_L3FWDACL	RTE_LOGTYPE_USER3
-#define acl_log(format, ...)	RTE_LOG(ERR, L3FWDACL, format, ##__VA_ARGS__)
-#define uint32_t_to_char(ip, a, b, c, d) do {\
-		*a = (unsigned char)(ip >> 24 & 0xff);\
-		*b = (unsigned char)(ip >> 16 & 0xff);\
-		*c = (unsigned char)(ip >> 8 & 0xff);\
-		*d = (unsigned char)(ip & 0xff);\
-	} while (0)
-#define OFF_ETHHEAD	(sizeof(struct ether_hdr))
-#define OFF_IPV42PROTO (offsetof(struct ipv4_hdr, next_proto_id))
-#define OFF_IPV62PROTO (offsetof(struct 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)
-
-#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
-	unsigned long val;                                      \
-	char *end;                                              \
-	errno = 0;                                              \
-	val = strtoul((in), &end, (base));                      \
-	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
-		return -EINVAL;                               \
-	(fd) = (typeof(fd))val;                                 \
-	(in) = end + 1;                                         \
-} while (0)
-
-/*
-  * 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
-
-/*
- * 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 ipv4_hdr, src_addr) -
-			offsetof(struct 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 ipv4_hdr, dst_addr) -
-			offsetof(struct 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 ipv4_hdr) -
-			offsetof(struct 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 ipv4_hdr) -
-			offsetof(struct ipv4_hdr, next_proto_id) +
-			sizeof(uint16_t),
-	},
-};
-
-#define	IPV6_ADDR_LEN	16
-#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
-#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_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 ipv6_hdr, src_addr) -
-			offsetof(struct 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 ipv6_hdr, src_addr) -
-			offsetof(struct 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 ipv6_hdr, src_addr) -
-			offsetof(struct 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 ipv6_hdr, src_addr) -
-			offsetof(struct 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 ipv6_hdr, dst_addr)
-				- offsetof(struct 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 ipv6_hdr, dst_addr) -
-			offsetof(struct 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 ipv6_hdr, dst_addr) -
-			offsetof(struct 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 ipv6_hdr, dst_addr) -
-			offsetof(struct 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 ipv6_hdr) -
-			offsetof(struct 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 ipv6_hdr) -
-			offsetof(struct 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 {
-	char mapped[NB_SOCKETS];
-	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 struct{
-	const char *rule_ipv4_name;
-	const char *rule_ipv6_name;
-	int scalar;
-} parm_config;
-
-const char cb_port_delim[] = ":";
-
-static inline void
-print_one_ipv4_rule(struct acl4_rule *rule, int extra)
-{
-	unsigned char a, b, c, d;
-
-	uint32_t_to_char(rule->field[SRC_FIELD_IPV4].value.u32,
-			&a, &b, &c, &d);
-	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
-			rule->field[SRC_FIELD_IPV4].mask_range.u32);
-	uint32_t_to_char(rule->field[DST_FIELD_IPV4].value.u32,
-			&a, &b, &c, &d);
-	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
-			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 inline void
-print_one_ipv6_rule(struct acl6_rule *rule, int extra)
-{
-	unsigned char a, b, c, d;
-
-	uint32_t_to_char(rule->field[SRC1_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[SRC2_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[SRC3_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[SRC4_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
-			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);
-
-	uint32_t_to_char(rule->field[DST1_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[DST2_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[DST3_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[DST4_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
-			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);
-}
-
-/* Bypass comment and empty lines */
-static inline int
-is_bypass_line(char *buff)
-{
-	int i = 0;
-
-	/* comment line */
-	if (buff[0] == COMMENT_LEAD_CHAR)
-		return 1;
-	/* empty line */
-	while (buff[i] != '\0') {
-		if (!isspace(buff[i]))
-			return 0;
-		i++;
-	}
-	return 1;
-}
-
-#ifdef L3FWDACL_DEBUG
-static inline void
-dump_acl4_rule(struct rte_mbuf *m, uint32_t sig)
-{
-	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
-	unsigned char a, b, c, d;
-	struct ipv4_hdr *ipv4_hdr = rte_pktmbuf_mtod_offset(m,
-							    struct ipv4_hdr *,
-							    sizeof(struct ether_hdr));
-
-	uint32_t_to_char(rte_bswap32(ipv4_hdr->src_addr), &a, &b, &c, &d);
-	printf("Packet Src:%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
-	uint32_t_to_char(rte_bswap32(ipv4_hdr->dst_addr), &a, &b, &c, &d);
-	printf("Dst:%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
-
-	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)
-{
-	unsigned i;
-	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
-	struct ipv6_hdr *ipv6_hdr = rte_pktmbuf_mtod_offset(m,
-							    struct ipv6_hdr *,
-							    sizeof(struct ether_hdr));
-
-	printf("Packet Src");
-	for (i = 0; i < RTE_DIM(ipv6_hdr->src_addr); i += sizeof(uint16_t))
-		printf(":%.2x%.2x",
-			ipv6_hdr->src_addr[i], ipv6_hdr->src_addr[i + 1]);
-
-	printf("\nDst");
-	for (i = 0; i < RTE_DIM(ipv6_hdr->dst_addr); i += sizeof(uint16_t))
-		printf(":%.2x%.2x",
-			ipv6_hdr->dst_addr[i], ipv6_hdr->dst_addr[i + 1]);
-
-	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");
-	}
-}
-
-#ifdef DO_RFC_1812_CHECKS
-static inline void
-prepare_one_packet(struct rte_mbuf **pkts_in, struct acl_search_t *acl,
-	int index)
-{
-	struct 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 ipv4_hdr *,
-						   sizeof(struct 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
-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 */
-
-static inline void
-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 *));
-		prepare_one_packet(pkts_in, acl, i);
-	}
-
-	/* Process left packets */
-	for (; i < nb_rx; i++)
-		prepare_one_packet(pkts_in, acl, i);
-}
-
-static inline void
-send_one_packet(struct rte_mbuf *m, uint32_t res)
-{
-	if (likely((res & ACL_DENY_SIGNATURE) == 0 && res != 0)) {
-		/* forward packets */
-		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
-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 *));
-		send_one_packet(m[i], res[i]);
-	}
-
-	/* Process left packets */
-	for (; i < num; i++)
-		send_one_packet(m[i], res[i]);
-}
-
-/*
- * Parses IPV6 address, exepcts the following format:
- * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X - is a hexedecimal digit).
- */
-static int
-parse_ipv6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
-	char dlm)
-{
-	uint32_t addr[IPV6_ADDR_U16];
-
-	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
-
-	*end = in;
-
-	v[0] = (addr[0] << 16) + addr[1];
-	v[1] = (addr[2] << 16) + addr[3];
-	v[2] = (addr[4] << 16) + addr[5];
-	v[3] = (addr[6] << 16) + addr[7];
-
-	return 0;
-}
-
-static int
-parse_ipv6_net(const char *in, struct rte_acl_field field[4])
-{
-	int32_t rc;
-	const char *mp;
-	uint32_t i, m, v[4];
-	const uint32_t nbu32 = sizeof(uint32_t) * CHAR_BIT;
-
-	/* get address. */
-	rc = parse_ipv6_addr(in, &mp, v, '/');
-	if (rc != 0)
-		return rc;
-
-	/* get mask. */
-	GET_CB_FIELD(mp, m, 0, CHAR_BIT * sizeof(v), 0);
-
-	/* 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(const char *in, uint32_t *addr, uint32_t *mask_len)
-{
-	uint8_t a, b, c, d, m;
-
-	GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
-	GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
-	GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
-	GET_CB_FIELD(in, d, 0, UINT8_MAX, '/');
-	GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
-
-	addr[0] = IPv4(a, b, c, d);
-	mask_len[0] = m;
-
-	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
-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;
-
-	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 (0 == route_num)
-		rte_exit(EXIT_FAILURE, "Not find any route entries in %s!\n",
-				rule_path);
-
-	fseek(fh, 0, SEEK_SET);
-
-	acl_rules = calloc(acl_num, rule_size);
-
-	if (NULL == acl_rules)
-		rte_exit(EXIT_FAILURE, "%s: failed to malloc memory\n",
-			__func__);
-
-	route_rules = calloc(route_num, rule_size);
-
-	if (NULL == route_rules)
-		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 void
-dump_acl_config(void)
-{
-	printf("ACL option are:\n");
-	printf(OPTION_RULE_IPV4": %s\n", parm_config.rule_ipv4_name);
-	printf(OPTION_RULE_IPV6": %s\n", parm_config.rule_ipv6_name);
-	printf(OPTION_SCALAR": %d\n", parm_config.scalar);
-}
-
-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;
-}
-
-static struct rte_acl_ctx*
-setup_acl(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;
-
-	if ((context = rte_acl_create(&acl_param)) == NULL)
-		rte_exit(EXIT_FAILURE, "Failed to create ACL context\n");
-
-	if (parm_config.scalar && rte_acl_set_ctx_classify(context,
-			RTE_ACL_CLASSIFY_SCALAR) != 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;
-}
-
-static int
-app_acl_init(void)
-{
-	unsigned lcore_id;
-	unsigned int i;
-	int socketid;
-	struct rte_acl_rule *acl_base_ipv4, *route_base_ipv4,
-		*acl_base_ipv6, *route_base_ipv6;
-	unsigned int acl_num_ipv4 = 0, route_num_ipv4 = 0,
-		acl_num_ipv6 = 0, route_num_ipv6 = 0;
-
-	if (check_acl_config() != 0)
-		rte_exit(EXIT_FAILURE, "Failed to get valid ACL options\n");
-
-	dump_acl_config();
-
-	/* Load  rules from the input file */
-	if (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)
-		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
-
-	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);
-
-	if (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)
-		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
-
-	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 (!numa_on)
-		acl_config.mapped[0] = 1;
-	else {
-		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-			if (rte_lcore_is_enabled(lcore_id) == 0)
-				continue;
-
-			socketid = rte_lcore_to_socket_id(lcore_id);
-			if (socketid >= NB_SOCKETS) {
-				acl_log("Socket %d of lcore %u is out "
-					"of range %d\n",
-					socketid, lcore_id, NB_SOCKETS);
-				free(route_base_ipv4);
-				free(route_base_ipv6);
-				free(acl_base_ipv4);
-				free(acl_base_ipv6);
-				return -1;
-			}
-
-			acl_config.mapped[socketid] = 1;
-		}
-	}
-
-	for (i = 0; i < NB_SOCKETS; i++) {
-		if (acl_config.mapped[i]) {
-			acl_config.acx_ipv4[i] = setup_acl(route_base_ipv4,
-				acl_base_ipv4, route_num_ipv4, acl_num_ipv4,
-				0, i);
-
-			acl_config.acx_ipv6[i] = setup_acl(route_base_ipv6,
-				acl_base_ipv6, route_num_ipv6, acl_num_ipv6,
-				1, i);
-		}
-	}
-
-	free(route_base_ipv4);
-	free(route_base_ipv6);
-
-#ifdef L3FWDACL_DEBUG
-	acl_config.rule_ipv4 = (struct acl4_rule *)acl_base_ipv4;
-	acl_config.rule_ipv6 = (struct acl6_rule *)acl_base_ipv6;
-#else
-	free(acl_base_ipv4);
-	free(acl_base_ipv6);
-#endif
-
-	return 0;
-}
-
-/***********************end of ACL part******************************/
-
-struct lcore_conf {
-	uint16_t n_rx_queue;
-	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
-	uint16_t n_tx_port;
-	uint16_t tx_port_id[RTE_MAX_ETHPORTS];
-	uint16_t tx_queue_id[RTE_MAX_ETHPORTS];
-	struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS];
-} __rte_cache_aligned;
-
-static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
-
-/* Enqueue a single packet, and send burst if queue is filled */
-static inline void
-send_single_packet(struct rte_mbuf *m, uint8_t port)
-{
-	uint32_t lcore_id;
-	struct lcore_conf *qconf;
-
-	lcore_id = rte_lcore_id();
-
-	qconf = &lcore_conf[lcore_id];
-	rte_eth_tx_buffer(port, qconf->tx_queue_id[port],
-			qconf->tx_buffer[port], m);
-}
-
-#ifdef DO_RFC_1812_CHECKS
-static inline int
-is_valid_ipv4_pkt(struct ipv4_hdr *pkt, uint32_t link_len)
-{
-	/* From http://www.rfc-editor.org/rfc/rfc1812.txt section 5.2.2 */
-	/*
-	 * 1. The packet length reported by the Link Layer must be large
-	 * enough to hold the minimum length legal IP datagram (20 bytes).
-	 */
-	if (link_len < sizeof(struct ipv4_hdr))
-		return -1;
-
-	/* 2. The IP checksum must be correct. */
-	/* this is checked in H/W */
-
-	/*
-	 * 3. The IP version number must be 4. If the version number is not 4
-	 * then the packet may be another version of IP, such as IPng or
-	 * ST-II.
-	 */
-	if (((pkt->version_ihl) >> 4) != 4)
-		return -3;
-	/*
-	 * 4. The IP header length field must be large enough to hold the
-	 * minimum length legal IP datagram (20 bytes = 5 words).
-	 */
-	if ((pkt->version_ihl & 0xf) < 5)
-		return -4;
-
-	/*
-	 * 5. The IP total length field must be large enough to hold the IP
-	 * datagram header, whose length is specified in the IP header length
-	 * field.
-	 */
-	if (rte_cpu_to_be_16(pkt->total_length) < sizeof(struct ipv4_hdr))
-		return -5;
-
-	return 0;
-}
-#endif
-
-/* main processing loop */
-static int
-main_loop(__attribute__((unused)) void *dummy)
-{
-	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
-	unsigned lcore_id;
-	uint64_t prev_tsc, diff_tsc, cur_tsc;
-	int i, nb_rx;
-	uint8_t portid, 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=%hhu rxqueueid=%hhu\n",
-			lcore_id, portid, queueid);
-	}
-
-	while (1) {
-
-		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];
-				rte_eth_tx_buffer_flush(portid,
-						qconf->tx_queue_id[portid],
-						qconf->tx_buffer[portid]);
-			}
-			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;
-
-				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);
-
-					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);
-
-					send_packets(acl_search.m_ipv6,
-						acl_search.res_ipv6,
-						acl_search.num_ipv6);
-				}
-			}
-		}
-	}
-}
-
-static int
-check_lcore_params(void)
-{
-	uint8_t queue, lcore;
-	uint16_t i;
-	int socketid;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		queue = lcore_params[i].queue_id;
-		if (queue >= MAX_RX_QUEUE_PER_PORT) {
-			printf("invalid queue number: %hhu\n", queue);
-			return -1;
-		}
-		lcore = lcore_params[i].lcore_id;
-		if (!rte_lcore_is_enabled(lcore)) {
-			printf("error: lcore %hhu is not enabled in "
-				"lcore mask\n", lcore);
-			return -1;
-		}
-		socketid = rte_lcore_to_socket_id(lcore);
-		if (socketid != 0 && numa_on == 0) {
-			printf("warning: lcore %hhu is on socket %d "
-				"with numa off\n",
-				lcore, socketid);
-		}
-	}
-	return 0;
-}
-
-static int
-check_port_config(const unsigned nb_ports)
-{
-	unsigned portid;
-	uint16_t i;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		portid = lcore_params[i].port_id;
-
-		if ((enabled_port_mask & (1 << portid)) == 0) {
-			printf("port %u is not enabled in port mask\n", portid);
-			return -1;
-		}
-		if (portid >= nb_ports) {
-			printf("port %u is not present on the board\n", portid);
-			return -1;
-		}
-	}
-	return 0;
-}
-
-static uint8_t
-get_port_n_rx_queues(const uint8_t port)
-{
-	int queue = -1;
-	uint16_t i;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		if (lcore_params[i].port_id == port &&
-				lcore_params[i].queue_id > queue)
-			queue = lcore_params[i].queue_id;
-	}
-	return (uint8_t)(++queue);
-}
-
-static int
-init_lcore_rx_queues(void)
-{
-	uint16_t i, nb_rx_queue;
-	uint8_t lcore;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		lcore = lcore_params[i].lcore_id;
-		nb_rx_queue = lcore_conf[lcore].n_rx_queue;
-		if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
-			printf("error: too many queues (%u) for lcore: %u\n",
-				(unsigned)nb_rx_queue + 1, (unsigned)lcore);
-			return -1;
-		} else {
-			lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
-				lcore_params[i].port_id;
-			lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
-				lcore_params[i].queue_id;
-			lcore_conf[lcore].n_rx_queue++;
-		}
-	}
-	return 0;
-}
-
-/* display usage */
-static void
-print_usage(const char *prgname)
-{
-	printf("%s [EAL options] -- -p PORTMASK -P"
-		"--"OPTION_RULE_IPV4"=FILE"
-		"--"OPTION_RULE_IPV6"=FILE"
-		"  [--"OPTION_CONFIG" (port,queue,lcore)[,(port,queue,lcore]]"
-		"  [--"OPTION_ENBJMO" [--max-pkt-len PKTLEN]]\n"
-		"  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
-		"  -P : enable promiscuous mode\n"
-		"  --"OPTION_CONFIG": (port,queue,lcore): "
-		"rx queues configuration\n"
-		"  --"OPTION_NONUMA": optional, disable numa awareness\n"
-		"  --"OPTION_ENBJMO": enable jumbo frame"
-		" which max packet len is PKTLEN in decimal (64-9600)\n"
-		"  --"OPTION_RULE_IPV4"=FILE: specify the ipv4 rules entries "
-		"file. "
-		"Each rule occupy one line. "
-		"2 kinds of rules are supported. "
-		"One is ACL entry at while line leads with character '%c', "
-		"another is route entry at while line leads with "
-		"character '%c'.\n"
-		"  --"OPTION_RULE_IPV6"=FILE: specify the ipv6 rules "
-		"entries file.\n"
-		"  --"OPTION_SCALAR": Use scalar function to do lookup\n",
-		prgname, ACL_LEAD_CHAR, ROUTE_LEAD_CHAR);
-}
-
-static int
-parse_max_pkt_len(const char *pktlen)
-{
-	char *end = NULL;
-	unsigned long len;
-
-	/* parse decimal string */
-	len = strtoul(pktlen, &end, 10);
-	if ((pktlen[0] == '\0') || (end == NULL) || (*end != '\0'))
-		return -1;
-
-	if (len == 0)
-		return -1;
-
-	return len;
-}
-
-static int
-parse_portmask(const char *portmask)
-{
-	char *end = NULL;
-	unsigned long pm;
-
-	/* parse hexadecimal string */
-	pm = strtoul(portmask, &end, 16);
-	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
-		return -1;
-
-	if (pm == 0)
-		return -1;
-
-	return pm;
-}
-
-static int
-parse_config(const char *q_arg)
-{
-	char s[256];
-	const char *p, *p0 = q_arg;
-	char *end;
-	enum fieldnames {
-		FLD_PORT = 0,
-		FLD_QUEUE,
-		FLD_LCORE,
-		_NUM_FLD
-	};
-	unsigned long int_fld[_NUM_FLD];
-	char *str_fld[_NUM_FLD];
-	int i;
-	unsigned size;
-
-	nb_lcore_params = 0;
-
-	while ((p = strchr(p0, '(')) != NULL) {
-		++p;
-		if ((p0 = strchr(p, ')')) == NULL)
-			return -1;
-
-		size = p0 - p;
-		if (size >= sizeof(s))
-			return -1;
-
-		snprintf(s, sizeof(s), "%.*s", size, p);
-		if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') !=
-				_NUM_FLD)
-			return -1;
-		for (i = 0; i < _NUM_FLD; i++) {
-			errno = 0;
-			int_fld[i] = strtoul(str_fld[i], &end, 0);
-			if (errno != 0 || end == str_fld[i] || int_fld[i] > 255)
-				return -1;
-		}
-		if (nb_lcore_params >= MAX_LCORE_PARAMS) {
-			printf("exceeded max number of lcore params: %hu\n",
-				nb_lcore_params);
-			return -1;
-		}
-		lcore_params_array[nb_lcore_params].port_id =
-			(uint8_t)int_fld[FLD_PORT];
-		lcore_params_array[nb_lcore_params].queue_id =
-			(uint8_t)int_fld[FLD_QUEUE];
-		lcore_params_array[nb_lcore_params].lcore_id =
-			(uint8_t)int_fld[FLD_LCORE];
-		++nb_lcore_params;
-	}
-	lcore_params = lcore_params_array;
-	return 0;
-}
-
-/* Parse the argument given in the command line of the application */
-static int
-parse_args(int argc, char **argv)
-{
-	int opt, ret;
-	char **argvopt;
-	int option_index;
-	char *prgname = argv[0];
-	static struct option lgopts[] = {
-		{OPTION_CONFIG, 1, 0, 0},
-		{OPTION_NONUMA, 0, 0, 0},
-		{OPTION_ENBJMO, 0, 0, 0},
-		{OPTION_RULE_IPV4, 1, 0, 0},
-		{OPTION_RULE_IPV6, 1, 0, 0},
-		{OPTION_SCALAR, 0, 0, 0},
-		{NULL, 0, 0, 0}
-	};
-
-	argvopt = argv;
-
-	while ((opt = getopt_long(argc, argvopt, "p:P",
-				lgopts, &option_index)) != EOF) {
-
-		switch (opt) {
-		/* portmask */
-		case 'p':
-			enabled_port_mask = parse_portmask(optarg);
-			if (enabled_port_mask == 0) {
-				printf("invalid portmask\n");
-				print_usage(prgname);
-				return -1;
-			}
-			break;
-		case 'P':
-			printf("Promiscuous mode selected\n");
-			promiscuous_on = 1;
-			break;
-
-		/* long options */
-		case 0:
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_CONFIG,
-					sizeof(OPTION_CONFIG))) {
-				ret = parse_config(optarg);
-				if (ret) {
-					printf("invalid config\n");
-					print_usage(prgname);
-					return -1;
-				}
-			}
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_NONUMA,
-					sizeof(OPTION_NONUMA))) {
-				printf("numa is disabled\n");
-				numa_on = 0;
-			}
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_ENBJMO, sizeof(OPTION_ENBJMO))) {
-				struct option lenopts = {
-					"max-pkt-len",
-					required_argument,
-					0,
-					0
-				};
-
-				printf("jumbo frame is enabled\n");
-				port_conf.rxmode.jumbo_frame = 1;
-
-				/*
-				 * if no max-pkt-len set, then use the
-				 * default value ETHER_MAX_LEN
-				 */
-				if (0 == getopt_long(argc, argvopt, "",
-						&lenopts, &option_index)) {
-					ret = parse_max_pkt_len(optarg);
-					if ((ret < 64) ||
-						(ret > MAX_JUMBO_PKT_LEN)) {
-						printf("invalid packet "
-							"length\n");
-						print_usage(prgname);
-						return -1;
-					}
-					port_conf.rxmode.max_rx_pkt_len = ret;
-				}
-				printf("set jumbo frame max packet length "
-					"to %u\n",
-					(unsigned int)
-					port_conf.rxmode.max_rx_pkt_len);
-			}
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_RULE_IPV4,
-					sizeof(OPTION_RULE_IPV4)))
-				parm_config.rule_ipv4_name = optarg;
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_RULE_IPV6,
-					sizeof(OPTION_RULE_IPV6))) {
-				parm_config.rule_ipv6_name = optarg;
-			}
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_SCALAR, sizeof(OPTION_SCALAR)))
-				parm_config.scalar = 1;
-
-
-			break;
-
-		default:
-			print_usage(prgname);
-			return -1;
-		}
-	}
-
-	if (optind >= 0)
-		argv[optind-1] = prgname;
-
-	ret = optind-1;
-	optind = 0; /* reset getopt lib */
-	return ret;
-}
-
-static void
-print_ethaddr(const char *name, const struct ether_addr *eth_addr)
-{
-	char buf[ETHER_ADDR_FMT_SIZE];
-	ether_format_addr(buf, ETHER_ADDR_FMT_SIZE, eth_addr);
-	printf("%s%s", name, buf);
-}
-
-static int
-init_mem(unsigned nb_mbuf)
-{
-	int socketid;
-	unsigned lcore_id;
-	char s[64];
-
-	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-		if (rte_lcore_is_enabled(lcore_id) == 0)
-			continue;
-
-		if (numa_on)
-			socketid = rte_lcore_to_socket_id(lcore_id);
-		else
-			socketid = 0;
-
-		if (socketid >= NB_SOCKETS) {
-			rte_exit(EXIT_FAILURE,
-				"Socket %d of lcore %u is out of range %d\n",
-				socketid, lcore_id, NB_SOCKETS);
-		}
-		if (pktmbuf_pool[socketid] == NULL) {
-			snprintf(s, sizeof(s), "mbuf_pool_%d", socketid);
-			pktmbuf_pool[socketid] =
-				rte_pktmbuf_pool_create(s, nb_mbuf,
-					MEMPOOL_CACHE_SIZE, 0,
-					RTE_MBUF_DEFAULT_BUF_SIZE,
-					socketid);
-			if (pktmbuf_pool[socketid] == NULL)
-				rte_exit(EXIT_FAILURE,
-					"Cannot init mbuf pool on socket %d\n",
-					socketid);
-			else
-				printf("Allocated mbuf pool on socket %d\n",
-					socketid);
-		}
-	}
-	return 0;
-}
-
-/* Check the link status of all ports in up to 9s, and print them finally */
-static void
-check_all_ports_link_status(uint8_t port_num, uint32_t port_mask)
-{
-#define CHECK_INTERVAL 100 /* 100ms */
-#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
-	uint8_t portid, count, all_ports_up, print_flag = 0;
-	struct rte_eth_link link;
-
-	printf("\nChecking link status");
-	fflush(stdout);
-	for (count = 0; count <= MAX_CHECK_TIME; count++) {
-		all_ports_up = 1;
-		for (portid = 0; portid < port_num; portid++) {
-			if ((port_mask & (1 << portid)) == 0)
-				continue;
-			memset(&link, 0, sizeof(link));
-			rte_eth_link_get_nowait(portid, &link);
-			/* print link status if flag set */
-			if (print_flag == 1) {
-				if (link.link_status)
-					printf("Port %d Link Up - speed %u "
-						"Mbps - %s\n", (uint8_t)portid,
-						(unsigned)link.link_speed,
-				(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
-					("full-duplex") : ("half-duplex\n"));
-				else
-					printf("Port %d Link Down\n",
-						(uint8_t)portid);
-				continue;
-			}
-			/* clear all_ports_up flag if any link down */
-			if (link.link_status == ETH_LINK_DOWN) {
-				all_ports_up = 0;
-				break;
-			}
-		}
-		/* after finally printing all link status, get out */
-		if (print_flag == 1)
-			break;
-
-		if (all_ports_up == 0) {
-			printf(".");
-			fflush(stdout);
-			rte_delay_ms(CHECK_INTERVAL);
-		}
-
-		/* set the print_flag if all ports up or timeout */
-		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
-			print_flag = 1;
-			printf("done\n");
-		}
-	}
-}
-
-int
-main(int argc, char **argv)
-{
-	struct lcore_conf *qconf;
-	struct rte_eth_dev_info dev_info;
-	struct rte_eth_txconf *txconf;
-	int ret;
-	unsigned nb_ports;
-	uint16_t queueid;
-	unsigned lcore_id;
-	uint32_t n_tx_queue, nb_lcores;
-	uint8_t portid, nb_rx_queue, queue, socketid;
-
-	/* init EAL */
-	ret = rte_eal_init(argc, argv);
-	if (ret < 0)
-		rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
-	argc -= ret;
-	argv += ret;
-
-	/* parse application arguments (after the EAL ones) */
-	ret = parse_args(argc, argv);
-	if (ret < 0)
-		rte_exit(EXIT_FAILURE, "Invalid L3FWD parameters\n");
-
-	if (check_lcore_params() < 0)
-		rte_exit(EXIT_FAILURE, "check_lcore_params failed\n");
-
-	ret = init_lcore_rx_queues();
-	if (ret < 0)
-		rte_exit(EXIT_FAILURE, "init_lcore_rx_queues failed\n");
-
-	nb_ports = rte_eth_dev_count();
-
-	if (check_port_config(nb_ports) < 0)
-		rte_exit(EXIT_FAILURE, "check_port_config failed\n");
-
-	/* Add ACL rules and route entries, build trie */
-	if (app_acl_init() < 0)
-		rte_exit(EXIT_FAILURE, "app_acl_init failed\n");
-
-	nb_lcores = rte_lcore_count();
-
-	/* initialize all ports */
-	for (portid = 0; portid < nb_ports; portid++) {
-		/* skip ports that are not enabled */
-		if ((enabled_port_mask & (1 << portid)) == 0) {
-			printf("\nSkipping disabled port %d\n", portid);
-			continue;
-		}
-
-		/* init port */
-		printf("Initializing port %d ... ", portid);
-		fflush(stdout);
-
-		nb_rx_queue = get_port_n_rx_queues(portid);
-		n_tx_queue = nb_lcores;
-		if (n_tx_queue > MAX_TX_QUEUE_PER_PORT)
-			n_tx_queue = MAX_TX_QUEUE_PER_PORT;
-		printf("Creating queues: nb_rxq=%d nb_txq=%u... ",
-			nb_rx_queue, (unsigned)n_tx_queue);
-		ret = rte_eth_dev_configure(portid, nb_rx_queue,
-					(uint16_t)n_tx_queue, &port_conf);
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE,
-				"Cannot configure device: err=%d, port=%d\n",
-				ret, portid);
-
-		rte_eth_macaddr_get(portid, &ports_eth_addr[portid]);
-		print_ethaddr(" Address:", &ports_eth_addr[portid]);
-		printf(", ");
-
-		/* init memory */
-		ret = init_mem(NB_MBUF);
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "init_mem failed\n");
-
-		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-			if (rte_lcore_is_enabled(lcore_id) == 0)
-				continue;
-
-			/* Initialize TX buffers */
-			qconf = &lcore_conf[lcore_id];
-			qconf->tx_buffer[portid] = rte_zmalloc_socket("tx_buffer",
-					RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0,
-					rte_eth_dev_socket_id(portid));
-			if (qconf->tx_buffer[portid] == NULL)
-				rte_exit(EXIT_FAILURE, "Can't allocate tx buffer for port %u\n",
-						(unsigned) portid);
-
-			rte_eth_tx_buffer_init(qconf->tx_buffer[portid], MAX_PKT_BURST);
-		}
-
-		/* init one TX queue per couple (lcore,port) */
-		queueid = 0;
-		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-			if (rte_lcore_is_enabled(lcore_id) == 0)
-				continue;
-
-			if (numa_on)
-				socketid = (uint8_t)
-					rte_lcore_to_socket_id(lcore_id);
-			else
-				socketid = 0;
-
-			printf("txq=%u,%d,%d ", lcore_id, queueid, socketid);
-			fflush(stdout);
-
-			rte_eth_dev_info_get(portid, &dev_info);
-			txconf = &dev_info.default_txconf;
-			if (port_conf.rxmode.jumbo_frame)
-				txconf->txq_flags = 0;
-			ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
-						     socketid, txconf);
-			if (ret < 0)
-				rte_exit(EXIT_FAILURE,
-					"rte_eth_tx_queue_setup: err=%d, "
-					"port=%d\n", ret, portid);
-
-			qconf = &lcore_conf[lcore_id];
-			qconf->tx_queue_id[portid] = queueid;
-			queueid++;
-
-			qconf->tx_port_id[qconf->n_tx_port] = portid;
-			qconf->n_tx_port++;
-		}
-		printf("\n");
-	}
-
-	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-		if (rte_lcore_is_enabled(lcore_id) == 0)
-			continue;
-		qconf = &lcore_conf[lcore_id];
-		printf("\nInitializing rx queues on lcore %u ... ", lcore_id);
-		fflush(stdout);
-		/* init RX queues */
-		for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
-			portid = qconf->rx_queue_list[queue].port_id;
-			queueid = qconf->rx_queue_list[queue].queue_id;
-
-			if (numa_on)
-				socketid = (uint8_t)
-					rte_lcore_to_socket_id(lcore_id);
-			else
-				socketid = 0;
-
-			printf("rxq=%d,%d,%d ", portid, queueid, socketid);
-			fflush(stdout);
-
-			ret = rte_eth_rx_queue_setup(portid, queueid, nb_rxd,
-					socketid, NULL,
-					pktmbuf_pool[socketid]);
-			if (ret < 0)
-				rte_exit(EXIT_FAILURE,
-					"rte_eth_rx_queue_setup: err=%d,"
-					"port=%d\n", ret, portid);
-		}
-	}
-
-	printf("\n");
-
-	/* start ports */
-	for (portid = 0; portid < nb_ports; portid++) {
-		if ((enabled_port_mask & (1 << portid)) == 0)
-			continue;
-
-		/* Start device */
-		ret = rte_eth_dev_start(portid);
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE,
-				"rte_eth_dev_start: err=%d, port=%d\n",
-				ret, portid);
-
-		/*
-		 * If enabled, put device in promiscuous mode.
-		 * This allows IO forwarding mode to forward packets
-		 * to itself through 2 cross-connected  ports of the
-		 * target machine.
-		 */
-		if (promiscuous_on)
-			rte_eth_promiscuous_enable(portid);
-	}
-
-	check_all_ports_link_status((uint8_t)nb_ports, enabled_port_mask);
-
-	/* launch per-lcore init on every lcore */
-	rte_eal_mp_remote_launch(main_loop, NULL, CALL_MASTER);
-	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
-		if (rte_eal_wait_lcore(lcore_id) < 0)
-			return -1;
-	}
-
-	return 0;
-}
diff --git a/examples/l3fwd/Makefile b/examples/l3fwd/Makefile
index 5ce0ce0..151dbe3 100644
--- a/examples/l3fwd/Makefile
+++ b/examples/l3fwd/Makefile
@@ -42,7 +42,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
 APP = l3fwd
 
 # all source are stored in SRCS-y
-SRCS-y := main.c l3fwd_lpm.c l3fwd_em.c
+SRCS-y := main.c l3fwd_lpm.c l3fwd_em.c l3fwd_acl.c
 
 CFLAGS += -I$(SRCDIR)
 CFLAGS += -O3 $(USER_FLAGS)
diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 011ba14..93e08f6 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -77,6 +77,24 @@
 #endif
 #define HASH_ENTRY_NUMBER_DEFAULT	4
 
+/* Config options */
+#define OPTION_CONFIG           "config"
+#define OPTION_NONUMA           "no-numa"
+#define OPTION_ENBJMO           "enable-jumbo"
+#define OPTION_RULE_IPV4        "rule_ipv4"
+#define OPTION_RULE_IPV6        "rule_ipv6"
+#define OPTION_SCALAR           "scalar"
+#define OPTION_ETH_DEST         "eth-dest"
+#define OPTION_IPV6		"ipv6"
+#define OPTION_HASH_ENTRY_NUM	"hash-entry-num"
+#define OPTION_PARSE_PTYPE	"parse-ptype"
+#define OPTION_MAX_PKT_LEN	"max-pkt-len"
+
+/*Log file related character defs. */
+#define ACL_LEAD_CHAR		('@')
+#define ROUTE_LEAD_CHAR		('R')
+#define COMMENT_LEAD_CHAR	('#')
+
 struct mbuf_table {
 	uint16_t len;
 	struct rte_mbuf *m_table[MAX_PKT_BURST];
@@ -94,6 +112,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;
@@ -115,6 +134,8 @@ extern xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+extern int numa_on; /**< NUMA is enabled by default. */
+
 /* Send burst of packets on an output interface */
 static inline int
 send_burst(struct lcore_conf *qconf, uint16_t n, uint8_t port)
@@ -205,12 +226,18 @@ setup_lpm(const int socketid);
 void
 setup_hash(const int socketid);
 
+void
+setup_acl(const int socketid);
+
 int
 em_check_ptype(int portid);
 
 int
 lpm_check_ptype(int portid);
 
+int
+acl_check_ptype(int portid);
+
 uint16_t
 em_cb_parse_ptype(uint8_t port, uint16_t queue, struct rte_mbuf *pkts[],
 		  uint16_t nb_pkts, uint16_t max_pkts, void *user_param);
@@ -219,12 +246,19 @@ uint16_t
 lpm_cb_parse_ptype(uint8_t port, uint16_t queue, struct rte_mbuf *pkts[],
 		   uint16_t nb_pkts, uint16_t max_pkts, void *user_param);
 
+uint16_t
+acl_cb_parse_ptype(uint8_t port, uint16_t queue, struct rte_mbuf *pkts[],
+		  uint16_t nb_pkts, uint16_t max_pkts, void *user_param);
+
 int
 em_main_loop(__attribute__((unused)) void *dummy);
 
 int
 lpm_main_loop(__attribute__((unused)) void *dummy);
 
+int
+acl_main_loop(__attribute__((unused)) void *dummy);
+
 /* Return ipv4/ipv6 fwd lookup struct for LPM or EM. */
 void *
 em_get_ipv4_l3fwd_lookup_struct(const int socketid);
@@ -238,4 +272,19 @@ lpm_get_ipv4_l3fwd_lookup_struct(const int socketid);
 void *
 lpm_get_ipv6_l3fwd_lookup_struct(const int socketid);
 
+void *
+acl_get_ipv4_l3fwd_lookup_struct(const int socketid);
+
+void *
+acl_get_ipv6_l3fwd_lookup_struct(const int socketid);
+
+void
+l3fwd_acl_set_scalar(void);
+
+void
+l3fwd_acl_set_rule_ipv6_name(const char *optarg);
+
+void
+l3fwd_acl_set_rule_ipv4_name(const char *optarg);
+
 #endif  /* __L3_FWD_H__ */
diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
new file mode 100644
index 0000000..388b978
--- /dev/null
+++ b/examples/l3fwd/l3fwd_acl.c
@@ -0,0 +1,1064 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include <rte_common.h>
+#include <rte_byteorder.h>
+#include <rte_log.h>
+#include <rte_memory.h>
+#include <rte_memcpy.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_per_lcore.h>
+#include <rte_launch.h>
+#include <rte_atomic.h>
+#include <rte_cycles.h>
+#include <rte_prefetch.h>
+#include <rte_lcore.h>
+#include <rte_per_lcore.h>
+#include <rte_branch_prediction.h>
+#include <rte_interrupts.h>
+#include <rte_pci.h>
+#include <rte_random.h>
+#include <rte_debug.h>
+#include <rte_ether.h>
+#include <rte_ethdev.h>
+#include <rte_ring.h>
+#include <rte_mempool.h>
+#include <rte_mbuf.h>
+#include <rte_ip.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+#include <rte_string_fns.h>
+#include <rte_acl.h>
+
+#include "l3fwd.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 ipv4_hdr, src_addr) -
+			offsetof(struct 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 ipv4_hdr, dst_addr) -
+			offsetof(struct 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 ipv4_hdr) -
+			offsetof(struct 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 ipv4_hdr) -
+			offsetof(struct ipv4_hdr, next_proto_id) +
+			sizeof(uint16_t),
+	},
+};
+
+#define	IPV6_ADDR_LEN	16
+#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
+#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_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 ipv6_hdr, src_addr) -
+			offsetof(struct 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 ipv6_hdr, src_addr) -
+			offsetof(struct 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 ipv6_hdr, src_addr) -
+			offsetof(struct 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 ipv6_hdr, src_addr) -
+			offsetof(struct 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 ipv6_hdr, dst_addr)
+				- offsetof(struct 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 ipv6_hdr, dst_addr) -
+			offsetof(struct 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 ipv6_hdr, dst_addr) -
+			offsetof(struct 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 ipv6_hdr, dst_addr) -
+			offsetof(struct 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 ipv6_hdr) -
+			offsetof(struct 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 ipv6_hdr) -
+			offsetof(struct 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 {
+	char mapped[NB_SOCKETS];
+	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;
+
+const char cb_port_delim[] = ":";
+
+static struct {
+	const char *rule_ipv4_name;
+	const char *rule_ipv6_name;
+	int scalar;
+} parm_config;
+
+/*
+ * Print and dump ACL/Route rules functions are defined in
+ * following header file.
+ */
+#include "l3fwd_acl.h"
+
+/*
+ * Data path related inline functions are defined in
+ * following header file.
+ */
+#include "l3fwd_acl_scalar.h"
+
+/*
+ * API's called during initialization to setup ACL rules.
+ */
+void
+l3fwd_acl_set_rule_ipv4_name(const char *optarg)
+{
+	parm_config.rule_ipv4_name = optarg;
+}
+
+void
+l3fwd_acl_set_rule_ipv6_name(const char *optarg)
+{
+	parm_config.rule_ipv6_name = optarg;
+}
+
+void
+l3fwd_acl_set_scalar(void)
+{
+	parm_config.scalar = 1;
+}
+
+/*
+ * Parses IPV6 address, exepcts the following format:
+ * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X - is a hexedecimal digit).
+ */
+static int
+parse_ipv6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
+	char dlm)
+{
+	uint32_t addr[IPV6_ADDR_U16];
+
+	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
+
+	*end = in;
+
+	v[0] = (addr[0] << 16) + addr[1];
+	v[1] = (addr[2] << 16) + addr[3];
+	v[2] = (addr[4] << 16) + addr[5];
+	v[3] = (addr[6] << 16) + addr[7];
+
+	return 0;
+}
+
+static int
+parse_ipv6_net(const char *in, struct rte_acl_field field[4])
+{
+	int32_t rc;
+	const char *mp;
+	uint32_t i, m, v[4];
+	const uint32_t nbu32 = sizeof(uint32_t) * CHAR_BIT;
+
+	/* get address. */
+	rc = parse_ipv6_addr(in, &mp, v, '/');
+	if (rc != 0)
+		return rc;
+
+	/* get mask. */
+	GET_CB_FIELD(mp, m, 0, CHAR_BIT * sizeof(v), 0);
+
+	/* 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(const char *in, uint32_t *addr, uint32_t *mask_len)
+{
+	uint8_t a, b, c, d, m;
+
+	GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, d, 0, UINT8_MAX, '/');
+	GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
+
+	addr[0] = IPv4(a, b, c, d);
+	mask_len[0] = m;
+
+	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
+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;
+
+	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);
+
+	fseek(fh, 0, SEEK_SET);
+
+	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 void
+dump_acl_config(void)
+{
+	printf("ACL option are:\n");
+	printf(OPTION_RULE_IPV4": %s\n", parm_config.rule_ipv4_name);
+	printf(OPTION_RULE_IPV6": %s\n", parm_config.rule_ipv6_name);
+	printf(OPTION_SCALAR": %d\n", parm_config.scalar);
+}
+
+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;
+}
+
+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.scalar && rte_acl_set_ctx_classify(context,
+			RTE_ACL_CLASSIFY_SCALAR) != 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;
+}
+
+void
+setup_acl(const int socket_id __attribute__((unused)))
+{
+	unsigned int lcore_id;
+	unsigned int i;
+	int socketid;
+	struct rte_acl_rule *acl_base_ipv4, *route_base_ipv4,
+		*acl_base_ipv6, *route_base_ipv6;
+	unsigned int acl_num_ipv4 = 0, route_num_ipv4 = 0,
+		acl_num_ipv6 = 0, route_num_ipv6 = 0;
+
+	printf("\nsocket %d\n", socket_id);
+
+	if (check_acl_config() != 0)
+		rte_exit(EXIT_FAILURE, "Failed to get valid ACL options\n");
+
+	dump_acl_config();
+
+	/* Load  rules from the input file */
+	if (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)
+		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
+
+	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);
+
+	if (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)
+		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
+
+	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 (!numa_on)
+		acl_config.mapped[0] = 1;
+	else {
+		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+			if (rte_lcore_is_enabled(lcore_id) == 0)
+				continue;
+
+			socketid = rte_lcore_to_socket_id(lcore_id);
+			if (socketid >= NB_SOCKETS) {
+				acl_log("Socket %d of lcore %u is out "
+					"of range %d\n",
+					socketid, lcore_id, NB_SOCKETS);
+				free(route_base_ipv4);
+				free(route_base_ipv6);
+				free(acl_base_ipv4);
+				free(acl_base_ipv6);
+				return;
+			}
+
+			acl_config.mapped[socketid] = 1;
+		}
+	}
+
+	for (i = 0; i < NB_SOCKETS; i++) {
+		if (acl_config.mapped[i]) {
+			acl_config.acx_ipv4[i] = app_acl_init(route_base_ipv4,
+				acl_base_ipv4, route_num_ipv4, acl_num_ipv4,
+				0, i);
+
+			acl_config.acx_ipv6[i] = app_acl_init(route_base_ipv6,
+				acl_base_ipv6, route_num_ipv6, acl_num_ipv6,
+				1, i);
+		}
+	}
+
+	free(route_base_ipv4);
+	free(route_base_ipv6);
+
+#ifdef L3FWDACL_DEBUG
+	acl_config.rule_ipv4 = (struct acl4_rule *)acl_base_ipv4;
+	acl_config.rule_ipv6 = (struct acl6_rule *)acl_base_ipv6;
+#else
+	free(acl_base_ipv4);
+	free(acl_base_ipv6);
+#endif
+
+}
+
+/* main processing loop */
+int
+acl_main_loop(__attribute__((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;
+	uint8_t portid, 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=%hhu 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];
+				rte_eth_tx_buffer_flush(portid,
+						qconf->tx_queue_id[portid],
+						qconf->tx_buffer[portid]);
+			}
+			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;
+}
+
+static inline void
+acl_parse_ptype(struct rte_mbuf *m)
+{
+	struct ether_hdr *eth_hdr;
+	uint32_t packet_type = RTE_PTYPE_UNKNOWN;
+	uint16_t ether_type;
+	void *l3;
+	int hdr_len;
+	struct ipv4_hdr *ipv4_hdr;
+	struct ipv6_hdr *ipv6_hdr;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	ether_type = eth_hdr->ether_type;
+	l3 = (uint8_t *)eth_hdr + sizeof(struct ether_hdr);
+	if (ether_type == rte_cpu_to_be_16(ETHER_TYPE_IPv4)) {
+		ipv4_hdr = (struct ipv4_hdr *)l3;
+		hdr_len = (ipv4_hdr->version_ihl & IPV4_HDR_IHL_MASK) *
+			  IPV4_IHL_MULTIPLIER;
+		if (hdr_len == sizeof(struct ipv4_hdr)) {
+			packet_type |= RTE_PTYPE_L3_IPV4;
+			if (ipv4_hdr->next_proto_id == IPPROTO_TCP)
+				packet_type |= RTE_PTYPE_L4_TCP;
+			else if (ipv4_hdr->next_proto_id == IPPROTO_UDP)
+				packet_type |= RTE_PTYPE_L4_UDP;
+		} else
+			packet_type |= RTE_PTYPE_L3_IPV4_EXT;
+	} else if (ether_type == rte_cpu_to_be_16(ETHER_TYPE_IPv4)) {
+		ipv6_hdr = (struct ipv6_hdr *)l3;
+		if (ipv6_hdr->proto == IPPROTO_TCP)
+			packet_type |= RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP;
+		else if (ipv6_hdr->proto == IPPROTO_UDP)
+			packet_type |= RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_UDP;
+		else
+			packet_type |= RTE_PTYPE_L3_IPV6_EXT_UNKNOWN;
+	}
+
+	m->packet_type = packet_type;
+}
+
+uint16_t
+acl_cb_parse_ptype(uint8_t port __rte_unused, uint16_t queue __rte_unused,
+		  struct rte_mbuf *pkts[], uint16_t nb_pkts,
+		  uint16_t max_pkts __rte_unused,
+		  void *user_param __rte_unused)
+{
+	unsigned int i;
+
+	for (i = 0; i < nb_pkts; ++i)
+		acl_parse_ptype(pkts[i]);
+
+	return nb_pkts;
+}
+
+/* Requirements:
+ * 1. IP packets without extension;
+ * 2. L4 payload should be either TCP or UDP.
+ */
+int
+acl_check_ptype(int portid)
+{
+	int i, ret;
+	int ptype_l3_ipv4_ext = 0;
+	int ptype_l3_ipv6_ext = 0;
+	int ptype_l4_tcp = 0;
+	int ptype_l4_udp = 0;
+	uint32_t ptype_mask = RTE_PTYPE_L3_MASK | RTE_PTYPE_L4_MASK;
+
+	ret = rte_eth_dev_get_supported_ptypes(portid, ptype_mask, NULL, 0);
+	if (ret <= 0)
+		return 0;
+
+	uint32_t ptypes[ret];
+
+	ret = rte_eth_dev_get_supported_ptypes(portid, ptype_mask, ptypes, ret);
+	for (i = 0; i < ret; ++i) {
+		switch (ptypes[i]) {
+		case RTE_PTYPE_L3_IPV4_EXT:
+			ptype_l3_ipv4_ext = 1;
+			break;
+		case RTE_PTYPE_L3_IPV6_EXT:
+			ptype_l3_ipv6_ext = 1;
+			break;
+		case RTE_PTYPE_L4_TCP:
+			ptype_l4_tcp = 1;
+			break;
+		case RTE_PTYPE_L4_UDP:
+			ptype_l4_udp = 1;
+			break;
+		}
+	}
+
+	if (ptype_l3_ipv4_ext == 0)
+		printf("port %d cannot parse RTE_PTYPE_L3_IPV4_EXT\n", portid);
+	if (ptype_l3_ipv6_ext == 0)
+		printf("port %d cannot parse RTE_PTYPE_L3_IPV6_EXT\n", portid);
+	if (!ptype_l3_ipv4_ext || !ptype_l3_ipv6_ext)
+		return 0;
+
+	if (ptype_l4_tcp == 0)
+		printf("port %d cannot parse RTE_PTYPE_L4_TCP\n", portid);
+	if (ptype_l4_udp == 0)
+		printf("port %d cannot parse RTE_PTYPE_L4_UDP\n", portid);
+	if (ptype_l4_tcp && ptype_l4_udp)
+		return 1;
+
+	return 0;
+}
+
+/* Not used by L3fwd ACL. */
+void *
+acl_get_ipv4_l3fwd_lookup_struct(__attribute__((unused)) const int socketid)
+{
+	return NULL;
+}
+
+void *
+acl_get_ipv6_l3fwd_lookup_struct(__attribute__((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 0000000..b79784b
--- /dev/null
+++ b/examples/l3fwd/l3fwd_acl.h
@@ -0,0 +1,263 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __L3FWD_ACL_H__
+#define __L3FWD_ACL_H__
+
+#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 uint32_t_to_char(ip, a, b, c, d) do {\
+		*a = (unsigned char)(ip >> 24 & 0xff);\
+		*b = (unsigned char)(ip >> 16 & 0xff);\
+		*c = (unsigned char)(ip >> 8 & 0xff);\
+		*d = (unsigned char)(ip & 0xff);\
+	} while (0)
+#define OFF_ETHHEAD	(sizeof(struct ether_hdr))
+#define OFF_IPV42PROTO (offsetof(struct ipv4_hdr, next_proto_id))
+#define OFF_IPV62PROTO (offsetof(struct 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)
+
+#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
+	unsigned long val;                                      \
+	char *end;                                              \
+	errno = 0;                                              \
+	val = strtoul((in), &end, (base));                      \
+	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
+		return -EINVAL;                               \
+	(fd) = (typeof(fd))val;                                 \
+	(in) = end + 1;                                         \
+} while (0)
+
+/*
+ * 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 inline void
+print_one_ipv4_rule(struct acl4_rule *rule, int extra)
+{
+	unsigned char a, b, c, d;
+
+	uint32_t_to_char(rule->field[SRC_FIELD_IPV4].value.u32,
+			&a, &b, &c, &d);
+	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
+			rule->field[SRC_FIELD_IPV4].mask_range.u32);
+	uint32_t_to_char(rule->field[DST_FIELD_IPV4].value.u32,
+			&a, &b, &c, &d);
+	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
+			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 inline void
+print_one_ipv6_rule(struct acl6_rule *rule, int extra)
+{
+	unsigned char a, b, c, d;
+
+	uint32_t_to_char(rule->field[SRC1_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[SRC2_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[SRC3_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[SRC4_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
+			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);
+
+	uint32_t_to_char(rule->field[DST1_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[DST2_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[DST3_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[DST4_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
+			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);
+}
+
+/* Bypass comment and empty lines */
+static inline int
+is_bypass_line(char *buff)
+{
+	int i = 0;
+
+	/* comment line */
+	if (buff[0] == COMMENT_LEAD_CHAR)
+		return 1;
+	/* empty line */
+	while (buff[i] != '\0') {
+		if (!isspace(buff[i]))
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
+#ifdef L3FWDACL_DEBUG
+static inline void
+dump_acl4_rule(struct rte_mbuf *m, uint32_t sig)
+{
+	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
+	unsigned char a, b, c, d;
+	struct ipv4_hdr *ipv4_hdr = rte_pktmbuf_mtod_offset(m,
+					    struct ipv4_hdr *,
+					    sizeof(struct ether_hdr));
+
+	uint32_t_to_char(rte_bswap32(ipv4_hdr->src_addr), &a, &b, &c, &d);
+	printf("Packet Src:%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
+	uint32_t_to_char(rte_bswap32(ipv4_hdr->dst_addr), &a, &b, &c, &d);
+	printf("Dst:%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
+
+	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)
+{
+	unsigned int i;
+	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
+	struct ipv6_hdr *ipv6_hdr = rte_pktmbuf_mtod_offset(m,
+					    struct ipv6_hdr *,
+					    sizeof(struct ether_hdr));
+
+	printf("Packet Src");
+	for (i = 0; i < RTE_DIM(ipv6_hdr->src_addr); i += sizeof(uint16_t))
+		printf(":%.2x%.2x",
+			ipv6_hdr->src_addr[i], ipv6_hdr->src_addr[i + 1]);
+
+	printf("\nDst");
+	for (i = 0; i < RTE_DIM(ipv6_hdr->dst_addr); i += sizeof(uint16_t))
+		printf(":%.2x%.2x",
+			ipv6_hdr->dst_addr[i], ipv6_hdr->dst_addr[i + 1]);
+
+	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");
+	}
+}
+
+#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 0000000..2ac02f5
--- /dev/null
+++ b/examples/l3fwd/l3fwd_acl_scalar.h
@@ -0,0 +1,182 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __L3FWD_ACL_SCALAR_H__
+#define __L3FWD_ACL_SCALAR_H__
+
+#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 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 ipv4_hdr *,
+						   sizeof(struct 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 */
+
+/* Enqueue a single packet, and send burst if queue is filled */
+static inline void
+l3fwd_acl_send_single_packet(struct rte_mbuf *m, uint8_t port)
+{
+	uint32_t lcore_id;
+	struct lcore_conf *qconf;
+
+	lcore_id = rte_lcore_id();
+	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_em.c b/examples/l3fwd/l3fwd_em.c
index 9cc4460..6fdabf7 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -754,7 +754,7 @@ setup_hash(const int socketid)
 		rte_hash_create(&ipv4_l3fwd_hash_params);
 	if (ipv4_l3fwd_em_lookup_struct[socketid] == NULL)
 		rte_exit(EXIT_FAILURE,
-			"Unable to create the l3fwd hash on socket %d\n",
+			"Unable to create the v4 hash on socket %d\n",
 			socketid);
 
 	/* create ipv6 hash */
@@ -765,13 +765,15 @@ setup_hash(const int socketid)
 		rte_hash_create(&ipv6_l3fwd_hash_params);
 	if (ipv6_l3fwd_em_lookup_struct[socketid] == NULL)
 		rte_exit(EXIT_FAILURE,
-			"Unable to create the l3fwd hash on socket %d\n",
+			"Unable to create the v6 hash on socket %d\n",
 			socketid);
 
 	if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-		/* For testing hash matching with a large number of flows we
-		 * generate millions of IP 5-tuples with an incremented dst
-		 * address to initialize the hash table. */
+		/* For testing hash matching with a large number
+		 * of flows we generate millions of IP 5-tuples
+		 * with an incremented dst address to initialize
+		 * the hash table.
+		 */
 		if (ipv6 == 0) {
 			/* populate the ipv4 hash */
 			populate_ipv4_many_flow_into_table(
@@ -781,7 +783,7 @@ setup_hash(const int socketid)
 			/* populate the ipv6 hash */
 			populate_ipv6_many_flow_into_table(
 				ipv6_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
+			hash_entry_number);
 		}
 	} else {
 		/*
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index f621269..ab31210 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -209,7 +209,7 @@ setup_lpm(const int socketid)
 			rte_lpm_create(s, socketid, &config_ipv4);
 	if (ipv4_l3fwd_lpm_lookup_struct[socketid] == NULL)
 		rte_exit(EXIT_FAILURE,
-			"Unable to create the l3fwd LPM table on socket %d\n",
+			"Unable to create v4 LPM table on socket %d\n",
 			socketid);
 
 	/* populate the LPM table */
@@ -221,14 +221,13 @@ setup_lpm(const int socketid)
 			continue;
 
 		ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
-			ipv4_l3fwd_lpm_route_array[i].ip,
-			ipv4_l3fwd_lpm_route_array[i].depth,
-			ipv4_l3fwd_lpm_route_array[i].if_out);
+				ipv4_l3fwd_lpm_route_array[i].ip,
+				ipv4_l3fwd_lpm_route_array[i].depth,
+				ipv4_l3fwd_lpm_route_array[i].if_out);
 
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE,
-				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
-				i, socketid);
+				"Unable to add entry %u to v4 LPM table on socket %d\n", i, socketid);
 		}
 
 		printf("LPM: Adding route 0x%08x / %d (%d)\n",
@@ -243,11 +242,11 @@ setup_lpm(const int socketid)
 	config.max_rules = IPV6_L3FWD_LPM_MAX_RULES;
 	config.number_tbl8s = IPV6_L3FWD_LPM_NUMBER_TBL8S;
 	config.flags = 0;
-	ipv6_l3fwd_lpm_lookup_struct[socketid] = rte_lpm6_create(s, socketid,
-				&config);
+	ipv6_l3fwd_lpm_lookup_struct[socketid] =
+			rte_lpm6_create(s, socketid, &config);
 	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL)
 		rte_exit(EXIT_FAILURE,
-			"Unable to create the l3fwd LPM table on socket %d\n",
+			"Unable to create v6 LPM table on socket %d\n",
 			socketid);
 
 	/* populate the LPM table */
@@ -265,13 +264,11 @@ setup_lpm(const int socketid)
 
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE,
-				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
-				i, socketid);
+				"Unable to add entry %u to the l3fwd LPM table on socket %d\n", i, socketid);
 		}
 
 		printf("LPM: Adding route %s / %d (%d)\n",
-			"IPV6",
-			ipv6_l3fwd_lpm_route_array[i].depth,
+			"IPV6", ipv6_l3fwd_lpm_route_array[i].depth,
 			ipv6_l3fwd_lpm_route_array[i].if_out);
 	}
 }
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index a50d628..3f35762 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -51,6 +51,7 @@
 #include <rte_memory.h>
 #include <rte_memcpy.h>
 #include <rte_memzone.h>
+#include <rte_malloc.h>
 #include <rte_eal.h>
 #include <rte_per_lcore.h>
 #include <rte_launch.h>
@@ -97,11 +98,12 @@ static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
 /**< Ports set in promiscuous mode off by default. */
 static int promiscuous_on;
 
-/* Select Longest-Prefix or Exact match. */
+/* Select Longest-Prefix(aka LPM), Exact match(aka EM) or ACL. */
 static int l3fwd_lpm_on;
 static int l3fwd_em_on;
+static int l3fwd_acl_on;
 
-static int numa_on = 1; /**< NUMA is enabled by default. */
+int numa_on = 1; /**< NUMA is enabled by default. */
 static int parse_ptype; /**< Parse packet type using rx callback, and */
 			/**< disabled by default */
 
@@ -172,7 +174,7 @@ static struct rte_eth_conf port_conf = {
 static struct rte_mempool * pktmbuf_pool[NB_SOCKETS];
 
 struct l3fwd_lkp_mode {
-	void  (*setup)(int);
+	void  (*setup)(const int);
 	int   (*check_ptype)(int);
 	rte_rx_callback_fn cb_parse_ptype;
 	int   (*main_loop)(void *);
@@ -200,6 +202,15 @@ static struct l3fwd_lkp_mode l3fwd_lpm_lkp = {
 	.get_ipv6_lookup_struct = lpm_get_ipv6_l3fwd_lookup_struct,
 };
 
+static struct l3fwd_lkp_mode l3fwd_acl_lkp = {
+	.setup                  = setup_acl,
+	.check_ptype		= acl_check_ptype,
+	.cb_parse_ptype		= acl_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,
+};
+
 /*
  * Setup lookup methods for forwarding.
  * Currently exact-match and longest-prefix-match
@@ -211,8 +222,11 @@ setup_l3fwd_lookup_tables(void)
 	/* Setup HASH lookup functions. */
 	if (l3fwd_em_on)
 		l3fwd_lkp = l3fwd_em_lkp;
-	/* Setup LPM lookup functions. */
+	/* Setup ACL lookup functions. */
+	else if (l3fwd_acl_on)
+		l3fwd_lkp = l3fwd_acl_lkp;
 	else
+	/* Setup LPM lookup functions. */
 		l3fwd_lkp = l3fwd_lpm_lkp;
 }
 
@@ -312,31 +326,45 @@ print_usage(const char *prgname)
 {
 	printf("%s [EAL options] --"
 		" -p PORTMASK"
+		"--"OPTION_RULE_IPV4"=FILE"
+		"--"OPTION_RULE_IPV6"=FILE"
 		" [-P]"
 		" [-E]"
+		" [-A]"
 		" [-L]"
-		" --config (port,queue,lcore)[,(port,queue,lcore)]"
-		" [--eth-dest=X,MM:MM:MM:MM:MM:MM]"
-		" [--enable-jumbo [--max-pkt-len PKTLEN]]"
-		" [--no-numa]"
-		" [--hash-entry-num]"
-		" [--ipv6]"
-		" [--parse-ptype]\n\n"
+		" [--"OPTION_CONFIG" (port,queue,lcore)[,(port,queue,lcore]]"
+		" [--"OPTION_ETH_DEST" =X,MM:MM:MM:MM:MM:MM]"
+		" [--"OPTION_ENBJMO" [--"OPTION_MAX_PKT_LEN" PKTLEN]]"
+		" [--"OPTION_NONUMA"]"
+		" [--"OPTION_HASH_ENTRY_NUM"]"
+		" [--"OPTION_IPV6"]"
+		" [--"OPTION_PARSE_PTYPE"]\n\n"
 
 		"  -p PORTMASK: Hexadecimal bitmask of ports to configure\n"
 		"  -P : Enable promiscuous mode\n"
 		"  -E : Enable exact match\n"
+		"  -A : Enable access control list match\n"
 		"  -L : Enable longest prefix match (default)\n"
-		"  --config (port,queue,lcore): Rx queue configuration\n"
-		"  --eth-dest=X,MM:MM:MM:MM:MM:MM: Ethernet destination for port X\n"
-		"  --enable-jumbo: Enable jumbo frames\n"
-		"  --max-pkt-len: Under the premise of enabling jumbo,\n"
+		"  --"OPTION_CONFIG" (port,queue,lcore): Rx queue configuration\n"
+		"  --"OPTION_ETH_DEST"=X,MM:MM:MM:MM:MM:MM: Ethernet destination for port X\n"
+		"  --"OPTION_ENBJMO": Enable jumbo frames\n"
+		"  --"OPTION_MAX_PKT_LEN": Under the premise of enabling jumbo,\n"
 		"                 maximum packet length in decimal (64-9600)\n"
-		"  --no-numa: Disable numa awareness\n"
-		"  --hash-entry-num: Specify the hash entry number in hexadecimal to be setup\n"
-		"  --ipv6: Set if running ipv6 packets\n"
-		"  --parse-ptype: Set to use software to analyze packet type\n\n",
-		prgname);
+		"  --"OPTION_NONUMA": Disable numa awareness\n"
+		"  --"OPTION_HASH_ENTRY_NUM": Specify the hash entry number in hexadecimal to be setup\n"
+		"  --"OPTION_IPV6": Set if running ipv6 packets\n"
+		"  --"OPTION_PARSE_PTYPE": Set to use software to analyze packet type\n\n"
+		"  --"OPTION_RULE_IPV4"=FILE: specify the ipv4 rules "
+		"entries file. "
+		"Each rule occupy one line. "
+		"2 kinds of rules are supported. "
+		"One is ACL entry at while line leads with character '%c', "
+		"another is route entry at while line leads with "
+		"character '%c'.\n"
+		"  --"OPTION_RULE_IPV6"=FILE: specify the ipv6 rules "
+		"entries file.\n"
+		"  --"OPTION_SCALAR": Use scalar function to do lookup\n",
+		prgname, ACL_LEAD_CHAR, ROUTE_LEAD_CHAR);
 }
 
 static int
@@ -477,17 +505,11 @@ parse_eth_dest(const char *optarg)
 static const char short_options[] =
 	"p:"  /* portmask */
 	"P"   /* promiscuous */
+	"A"   /* enable access control list match */
 	"L"   /* enable long prefix match */
 	"E"   /* enable exact match */
 	;
 
-#define CMD_LINE_OPT_CONFIG "config"
-#define CMD_LINE_OPT_ETH_DEST "eth-dest"
-#define CMD_LINE_OPT_NO_NUMA "no-numa"
-#define CMD_LINE_OPT_IPV6 "ipv6"
-#define CMD_LINE_OPT_ENABLE_JUMBO "enable-jumbo"
-#define CMD_LINE_OPT_HASH_ENTRY_NUM "hash-entry-num"
-#define CMD_LINE_OPT_PARSE_PTYPE "parse-ptype"
 enum {
 	/* long options mapped to a short option */
 
@@ -501,16 +523,22 @@ enum {
 	CMD_LINE_OPT_ENABLE_JUMBO_NUM,
 	CMD_LINE_OPT_HASH_ENTRY_NUM_NUM,
 	CMD_LINE_OPT_PARSE_PTYPE_NUM,
+	CMD_LINE_OPT_RULE_IPV4,
+	CMD_LINE_OPT_RULE_IPV6,
+	CMD_LINE_OPT_SCALAR,
 };
 
 static const struct option lgopts[] = {
-	{CMD_LINE_OPT_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM},
-	{CMD_LINE_OPT_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM},
-	{CMD_LINE_OPT_NO_NUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM},
-	{CMD_LINE_OPT_IPV6, 0, 0, CMD_LINE_OPT_IPV6_NUM},
-	{CMD_LINE_OPT_ENABLE_JUMBO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM},
-	{CMD_LINE_OPT_HASH_ENTRY_NUM, 1, 0, CMD_LINE_OPT_HASH_ENTRY_NUM_NUM},
-	{CMD_LINE_OPT_PARSE_PTYPE, 0, 0, CMD_LINE_OPT_PARSE_PTYPE_NUM},
+	{OPTION_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM},
+	{OPTION_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM},
+	{OPTION_NONUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM},
+	{OPTION_IPV6, 0, 0, CMD_LINE_OPT_IPV6_NUM},
+	{OPTION_ENBJMO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM},
+	{OPTION_HASH_ENTRY_NUM, 1, 0, CMD_LINE_OPT_HASH_ENTRY_NUM_NUM},
+	{OPTION_PARSE_PTYPE, 0, 0, CMD_LINE_OPT_PARSE_PTYPE_NUM},
+	{OPTION_RULE_IPV4, 1, 0, CMD_LINE_OPT_RULE_IPV4},
+	{OPTION_RULE_IPV6, 1, 0, CMD_LINE_OPT_RULE_IPV6},
+	{OPTION_SCALAR, 0, 0, CMD_LINE_OPT_SCALAR},
 	{NULL, 0, 0, 0}
 };
 
@@ -522,17 +550,17 @@ static const struct option lgopts[] = {
  * value of 8192
  */
 #define NB_MBUF RTE_MAX(	\
-	(nb_ports*nb_rx_queue*RTE_TEST_RX_DESC_DEFAULT +	\
-	nb_ports*nb_lcores*MAX_PKT_BURST +			\
-	nb_ports*n_tx_queue*RTE_TEST_TX_DESC_DEFAULT +		\
-	nb_lcores*MEMPOOL_CACHE_SIZE),				\
+	(nb_ports * nb_rx_queue * RTE_TEST_RX_DESC_DEFAULT +	\
+	nb_ports * nb_lcores * MAX_PKT_BURST +			\
+	nb_ports * n_tx_queue * RTE_TEST_TX_DESC_DEFAULT +		\
+	nb_lcores * MEMPOOL_CACHE_SIZE),				\
 	(unsigned)8192)
 
 /* Parse the argument given in the command line of the application */
 static int
 parse_args(int argc, char **argv)
 {
-	int opt, ret;
+	int opt, ret, chk_cond;
 	char **argvopt;
 	int option_index;
 	char *prgname = argv[0];
@@ -544,17 +572,19 @@ parse_args(int argc, char **argv)
 	const char *str2 = "L3FWD: Promiscuous mode selected";
 	const char *str3 = "L3FWD: Exact match selected";
 	const char *str4 = "L3FWD: Longest-prefix match selected";
-	const char *str5 = "L3FWD: Invalid config";
-	const char *str6 = "L3FWD: NUMA is disabled";
-	const char *str7 = "L3FWD: IPV6 is specified";
-	const char *str8 =
+	const char *str5 = "L3FWD: Access Control List match selected";
+	const char *str6 = "L3FWD: Invalid config";
+	const char *str7 = "L3FWD: NUMA is disabled";
+	const char *str8 = "L3FWD: IPV6 is specified";
+	const char *str9 =
 		"L3FWD: Jumbo frame is enabled - disabling simple TX path";
-	const char *str9 = "L3FWD: Invalid packet length";
-	const char *str10 = "L3FWD: Set jumbo frame max packet len to ";
-	const char *str11 = "L3FWD: Invalid hash entry number";
-	const char *str12 =
-		"L3FWD: LPM and EM are mutually exclusive, select only one";
-	const char *str13 = "L3FWD: LPM or EM none selected, default LPM on";
+	const char *str10 = "L3FWD: Invalid packet length";
+	const char *str11 = "L3FWD: Set jumbo frame max packet len to ";
+	const char *str12 = "L3FWD: Invalid hash entry number";
+	const char *str13 =
+		"L3FWD: LPM, EM and ACL are mutually exclusive, select only one";
+	const char *str14 =
+		"L3FWD: LPM, EM or ACL none selected, default LPM on";
 
 	while ((opt = getopt_long(argc, argvopt, short_options,
 				lgopts, &option_index)) != EOF) {
@@ -585,11 +615,16 @@ parse_args(int argc, char **argv)
 			l3fwd_lpm_on = 1;
 			break;
 
+		case 'A':
+			printf("%s\n", str5);
+			l3fwd_acl_on = 1;
+			break;
+
 		/* long options */
 		case CMD_LINE_OPT_CONFIG_NUM:
 			ret = parse_config(optarg);
 			if (ret) {
-				printf("%s\n", str5);
+				printf("%s\n", str6);
 				print_usage(prgname);
 				return -1;
 			}
@@ -600,12 +635,12 @@ parse_args(int argc, char **argv)
 			break;
 
 		case CMD_LINE_OPT_NO_NUMA_NUM:
-			printf("%s\n", str6);
+			printf("%s\n", str7);
 			numa_on = 0;
 			break;
 
 		case CMD_LINE_OPT_IPV6_NUM:
-			printf("%sn", str7);
+			printf("%sn", str8);
 			ipv6 = 1;
 			break;
 
@@ -614,7 +649,7 @@ parse_args(int argc, char **argv)
 				"max-pkt-len", required_argument, 0, 0
 			};
 
-			printf("%s\n", str8);
+			printf("%s\n", str9);
 			port_conf.rxmode.jumbo_frame = 1;
 
 			/*
@@ -626,13 +661,13 @@ parse_args(int argc, char **argv)
 				ret = parse_max_pkt_len(optarg);
 				if ((ret < 64) ||
 					(ret > MAX_JUMBO_PKT_LEN)) {
-					printf("%s\n", str9);
+					printf("%s\n", str10);
 					print_usage(prgname);
 					return -1;
 				}
 				port_conf.rxmode.max_rx_pkt_len = ret;
 			}
-			printf("%s %u\n", str10,
+			printf("%s %u\n", str11,
 				(unsigned int)port_conf.rxmode.max_rx_pkt_len);
 			break;
 		}
@@ -642,7 +677,7 @@ parse_args(int argc, char **argv)
 			if ((ret > 0) && (ret <= L3FWD_HASH_ENTRIES)) {
 				hash_entry_number = ret;
 			} else {
-				printf("%s\n", str11);
+				printf("%s\n", str12);
 				print_usage(prgname);
 				return -1;
 			}
@@ -653,15 +688,29 @@ parse_args(int argc, char **argv)
 			parse_ptype = 1;
 			break;
 
+		case CMD_LINE_OPT_RULE_IPV4:
+			l3fwd_acl_set_rule_ipv4_name(optarg);
+			break;
+
+		case CMD_LINE_OPT_RULE_IPV6:
+			l3fwd_acl_set_rule_ipv6_name(optarg);
+			break;
+
+		case CMD_LINE_OPT_SCALAR:
+			l3fwd_acl_set_scalar();
+			break;
+
 		default:
 			print_usage(prgname);
 			return -1;
 		}
 	}
 
-	/* If both LPM and EM are selected, return error. */
-	if (l3fwd_lpm_on && l3fwd_em_on) {
-		printf("%s\n", str12);
+	/* If more than one(LPM, EM and ACL) are selected, return error. */
+	chk_cond = l3fwd_lpm_on ? (l3fwd_em_on || l3fwd_acl_on) :
+				(l3fwd_em_on && l3fwd_acl_on);
+	if (chk_cond) {
+		printf("%s\n", str13);
 		return -1;
 	}
 
@@ -669,9 +718,9 @@ parse_args(int argc, char **argv)
 	 * Nothing is selected, pick longest-prefix match
 	 * as default match.
 	 */
-	if (!l3fwd_lpm_on && !l3fwd_em_on) {
+	if (!l3fwd_lpm_on && !l3fwd_em_on && !l3fwd_acl_on) {
 		l3fwd_lpm_on = 1;
-		printf("%s\n", str13);
+		printf("%s\n", str14);
 	}
 
 	/*
@@ -684,6 +733,11 @@ parse_args(int argc, char **argv)
 		hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT;
 	}
 
+	/* For ACL, update port config rss hash filter. */
+	if (l3fwd_acl_on)
+		port_conf.rx_adv_conf.rss_conf.rss_hf |=
+			ETH_RSS_UDP | ETH_RSS_TCP | ETH_RSS_SCTP;
+
 	if (optind >= 0)
 		argv[optind-1] = prgname;
 
@@ -737,8 +791,9 @@ init_mem(unsigned nb_mbuf)
 				printf("Allocated mbuf pool on socket %d\n",
 					socketid);
 
-			/* Setup either LPM or EM(f.e Hash).  */
-			l3fwd_lkp.setup(socketid);
+			/* Setup LPM/EM (f.e. Hash) functions */
+			if (l3fwd_lpm_on || l3fwd_em_on)
+				l3fwd_lkp.setup(socketid);
 		}
 		qconf = &lcore_conf[lcore_id];
 		qconf->ipv4_lookup_struct =
@@ -836,7 +891,7 @@ prepare_ptype_parser(uint8_t portid, uint16_t queueid)
 		return 1;
 
 	printf("port %d cannot parse packet type, please add --%s\n",
-	       portid, CMD_LINE_OPT_PARSE_PTYPE);
+	       portid, OPTION_PARSE_PTYPE);
 	return 0;
 }
 
@@ -893,6 +948,10 @@ main(int argc, char **argv)
 	/* Setup function pointers for lookup method. */
 	setup_l3fwd_lookup_tables();
 
+	/* Setup ACL functions */
+	if (l3fwd_acl_on)
+		l3fwd_lkp.setup(-1);
+
 	/* initialize all ports */
 	for (portid = 0; portid < nb_ports; portid++) {
 		/* skip ports that are not enabled */
@@ -936,6 +995,28 @@ main(int argc, char **argv)
 		if (ret < 0)
 			rte_exit(EXIT_FAILURE, "init_mem failed\n");
 
+		/* ACL specific. */
+		if (l3fwd_acl_on) {
+			for (lcore_id = 0;
+				lcore_id < RTE_MAX_LCORE; lcore_id++) {
+				if (rte_lcore_is_enabled(lcore_id) == 0)
+					continue;
+
+				/* Initialize TX buffers */
+				qconf = &lcore_conf[lcore_id];
+				qconf->tx_buffer[portid] =
+					rte_zmalloc_socket("tx_buffer",
+					RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST),
+					0, rte_eth_dev_socket_id(portid));
+
+				if (qconf->tx_buffer[portid] == NULL)
+					rte_exit(EXIT_FAILURE, "Can't allocate tx buffer for port %u\n", (unsigned int) portid);
+
+				rte_eth_tx_buffer_init(qconf->tx_buffer[portid],
+							MAX_PKT_BURST);
+			}
+		}
+
 		/* init one TX queue per couple (lcore,port) */
 		queueid = 0;
 		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-- 
2.7.4

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [dpdk-dev] [PATCH v5 2/3] examples/l3fwd: add config file support for LPM
  2017-03-08 21:32   ` [dpdk-dev] [PATCH v5 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd Ravi Kerur
  2017-03-08 21:32     ` [dpdk-dev] [PATCH v5 1/3] " Ravi Kerur
@ 2017-03-08 21:32     ` Ravi Kerur
  2017-03-09 10:43       ` Mcnamara, John
  2017-03-08 21:32     ` [dpdk-dev] [PATCH v5 3/3] examples/l3fwd: add config file support for EM Ravi Kerur
  2 siblings, 1 reply; 34+ messages in thread
From: Ravi Kerur @ 2017-03-08 21:32 UTC (permalink / raw)
  To: dev, konstantin.ananyev, bruce.richardson; +Cc: Ravi Kerur

Add config file support for v4 and v6 to build forwarding
tables.

v5:
	> Changes is_bypass_line from inline to non-line.

v4:
	> No changes.

v3:
	> Fix additional checkpatch coding style issues.

v2:
	> Fix checkpatch warnings related to code
	> MACRO GET_CB_FIELD checkpatch warning not fixed

v1:
	> Remove static array configuration of Destination IP, MASK
		and IF_OUT for LPM and LPM6 config.
	> Add reading configuration from a file.
	> Format of configuration file is as follows
		#LPM route entries Dest-IP/Mask IF_OUT
		L1.1.1.0/24 0
		L2.1.1.0/24 1
		L3.1.1.0/24 2
		...

		#LPM6 route entries Dest-IP/Mask IF_OUT
		L1111:1111:1111:1111:0000:0000:0000:0000/48 0
		L2111:1111:1111:1111:0000:0000:0000:0000/48 1
		L3111:1111:1111:1111:0000:0000:0000:0000/48 2
		...

Signed-off-by: Ravi Kerur <rkerur@gmail.com>
---
 examples/l3fwd/l3fwd.h     |  28 +++++
 examples/l3fwd/l3fwd_acl.c |  39 +-----
 examples/l3fwd/l3fwd_acl.h |  29 -----
 examples/l3fwd/l3fwd_lpm.c | 308 +++++++++++++++++++++++++++++++++++++--------
 examples/l3fwd/main.c      |  47 ++++++-
 5 files changed, 332 insertions(+), 119 deletions(-)

diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 93e08f6..35f7b72 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -94,6 +94,29 @@
 #define ACL_LEAD_CHAR		('@')
 #define ROUTE_LEAD_CHAR		('R')
 #define COMMENT_LEAD_CHAR	('#')
+#define LPM_LEAD_CHAR		('L')
+#define EM_LEAD_CHAR		('E')
+
+#define	IPV6_ADDR_LEN	16
+#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
+#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
+
+#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
+	unsigned long val;                                      \
+	char *end;                                              \
+	errno = 0;                                              \
+	val = strtoul((in), &end, (base));                      \
+	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
+		return -EINVAL;                               \
+	(fd) = (typeof(fd))val;                                 \
+	(in) = end + 1;                                         \
+} while (0)
+
+struct parm_cfg {
+	const char *rule_ipv4_name;
+	const char *rule_ipv6_name;
+	int scalar;
+};
 
 struct mbuf_table {
 	uint16_t len;
@@ -134,6 +157,8 @@ extern xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+extern struct parm_cfg parm_config;
+
 extern int numa_on; /**< NUMA is enabled by default. */
 
 /* Send burst of packets on an output interface */
@@ -287,4 +312,7 @@ l3fwd_acl_set_rule_ipv6_name(const char *optarg);
 void
 l3fwd_acl_set_rule_ipv4_name(const char *optarg);
 
+int
+is_bypass_line(char *buff);
+
 #endif  /* __L3_FWD_H__ */
diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
index 388b978..66ed23d 100644
--- a/examples/l3fwd/l3fwd_acl.c
+++ b/examples/l3fwd/l3fwd_acl.c
@@ -147,10 +147,6 @@ struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
 	},
 };
 
-#define	IPV6_ADDR_LEN	16
-#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
-#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
-
 enum {
 	PROTO_FIELD_IPV6,
 	SRC1_FIELD_IPV6,
@@ -297,12 +293,6 @@ static struct {
 
 const char cb_port_delim[] = ":";
 
-static struct {
-	const char *rule_ipv4_name;
-	const char *rule_ipv6_name;
-	int scalar;
-} parm_config;
-
 /*
  * Print and dump ACL/Route rules functions are defined in
  * following header file.
@@ -316,27 +306,6 @@ static struct {
 #include "l3fwd_acl_scalar.h"
 
 /*
- * API's called during initialization to setup ACL rules.
- */
-void
-l3fwd_acl_set_rule_ipv4_name(const char *optarg)
-{
-	parm_config.rule_ipv4_name = optarg;
-}
-
-void
-l3fwd_acl_set_rule_ipv6_name(const char *optarg)
-{
-	parm_config.rule_ipv6_name = optarg;
-}
-
-void
-l3fwd_acl_set_scalar(void)
-{
-	parm_config.scalar = 1;
-}
-
-/*
  * Parses IPV6 address, exepcts the following format:
  * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X - is a hexedecimal digit).
  */
@@ -566,7 +535,7 @@ parse_cb_ipv4vlan_rule(char *str, struct rte_acl_rule *v, int has_userdata)
 }
 
 static int
-add_rules(const char *rule_path,
+acl_add_rules(const char *rule_path,
 		struct rte_acl_rule **proute_base,
 		unsigned int *proute_num,
 		struct rte_acl_rule **pacl_base,
@@ -764,8 +733,8 @@ setup_acl(const int socket_id __attribute__((unused)))
 
 	dump_acl_config();
 
-	/* Load  rules from the input file */
-	if (add_rules(parm_config.rule_ipv4_name, &route_base_ipv4,
+	/* Load rules from the input file */
+	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)
 		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
@@ -776,7 +745,7 @@ setup_acl(const int socket_id __attribute__((unused)))
 	acl_log("IPv4 ACL entries %u:\n", acl_num_ipv4);
 	dump_ipv4_rules((struct acl4_rule *)acl_base_ipv4, acl_num_ipv4, 1);
 
-	if (add_rules(parm_config.rule_ipv6_name, &route_base_ipv6,
+	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)
diff --git a/examples/l3fwd/l3fwd_acl.h b/examples/l3fwd/l3fwd_acl.h
index b79784b..da0b5a8 100644
--- a/examples/l3fwd/l3fwd_acl.h
+++ b/examples/l3fwd/l3fwd_acl.h
@@ -55,17 +55,6 @@
 #define MBUF_IPV6_2PROTO(m)	\
 	rte_pktmbuf_mtod_offset((m), uint8_t *, OFF_ETHHEAD + OFF_IPV62PROTO)
 
-#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
-	unsigned long val;                                      \
-	char *end;                                              \
-	errno = 0;                                              \
-	val = strtoul((in), &end, (base));                      \
-	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
-		return -EINVAL;                               \
-	(fd) = (typeof(fd))val;                                 \
-	(in) = end + 1;                                         \
-} while (0)
-
 /*
  * 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.
@@ -163,24 +152,6 @@ print_one_ipv6_rule(struct acl6_rule *rule, int extra)
 			rule->data.userdata);
 }
 
-/* Bypass comment and empty lines */
-static inline int
-is_bypass_line(char *buff)
-{
-	int i = 0;
-
-	/* comment line */
-	if (buff[0] == COMMENT_LEAD_CHAR)
-		return 1;
-	/* empty line */
-	while (buff[i] != '\0') {
-		if (!isspace(buff[i]))
-			return 0;
-		i++;
-	}
-	return 1;
-}
-
 #ifdef L3FWDACL_DEBUG
 static inline void
 dump_acl4_rule(struct rte_mbuf *m, uint32_t sig)
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index ab31210..97f99cf 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -57,45 +57,24 @@
 
 #include "l3fwd.h"
 
-struct ipv4_l3fwd_lpm_route {
-	uint32_t ip;
-	uint8_t  depth;
-	uint8_t  if_out;
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
 };
 
-struct ipv6_l3fwd_lpm_route {
-	uint8_t ip[16];
+struct lpm_rule {
+	union {
+		uint32_t ip;
+		union {
+			uint32_t ip_32[4];
+			uint8_t ip_8[16];
+		};
+	};
 	uint8_t  depth;
 	uint8_t  if_out;
 };
 
-static struct ipv4_l3fwd_lpm_route ipv4_l3fwd_lpm_route_array[] = {
-	{IPv4(1, 1, 1, 0), 24, 0},
-	{IPv4(2, 1, 1, 0), 24, 1},
-	{IPv4(3, 1, 1, 0), 24, 2},
-	{IPv4(4, 1, 1, 0), 24, 3},
-	{IPv4(5, 1, 1, 0), 24, 4},
-	{IPv4(6, 1, 1, 0), 24, 5},
-	{IPv4(7, 1, 1, 0), 24, 6},
-	{IPv4(8, 1, 1, 0), 24, 7},
-};
-
-static struct ipv6_l3fwd_lpm_route ipv6_l3fwd_lpm_route_array[] = {
-	{{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 0},
-	{{2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 1},
-	{{3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 2},
-	{{4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 3},
-	{{5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 4},
-	{{6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 5},
-	{{7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 6},
-	{{8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 7},
-};
-
-#define IPV4_L3FWD_LPM_NUM_ROUTES \
-	(sizeof(ipv4_l3fwd_lpm_route_array) / sizeof(ipv4_l3fwd_lpm_route_array[0]))
-#define IPV6_L3FWD_LPM_NUM_ROUTES \
-	(sizeof(ipv6_l3fwd_lpm_route_array) / sizeof(ipv6_l3fwd_lpm_route_array[0]))
-
 #define IPV4_L3FWD_LPM_MAX_RULES         1024
 #define IPV4_L3FWD_LPM_NUMBER_TBL8S (1 << 8)
 #define IPV6_L3FWD_LPM_MAX_RULES         1024
@@ -110,6 +89,211 @@ struct rte_lpm6 *ipv6_l3fwd_lpm_lookup_struct[NB_SOCKETS];
 #include "l3fwd_lpm.h"
 #endif
 
+static int
+lpm_parse_v6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
+	char dlm)
+{
+	uint32_t addr[IPV6_ADDR_U16];
+
+	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
+
+	*end = in;
+
+	v[0] = (addr[0] << 16) + addr[1];
+	v[1] = (addr[2] << 16) + addr[3];
+	v[2] = (addr[4] << 16) + addr[5];
+	v[3] = (addr[6] << 16) + addr[7];
+
+	return 0;
+}
+
+static int
+lpm_parse_v6_net(const char *in, uint32_t *v, uint8_t *mask_len)
+{
+	int32_t rc;
+	const char *mp;
+	uint8_t m;
+	uint32_t tmp[4];
+
+	/* get address. */
+	rc = lpm_parse_v6_addr(in, &mp, v, '/');
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_addr failed %d\n", rc);
+		return rc;
+	}
+
+	/* get mask. */
+	GET_CB_FIELD(mp, m, 0, sizeof(tmp) * CHAR_BIT, 0);
+	*mask_len = m;
+
+	return 0;
+}
+
+static int
+lpm_parse_v6_rule(char *str, struct lpm_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL) {
+			RTE_LOG(ERR, L3FWD,
+				"\nparse_v6_rule strtok_r failed\n");
+			return -EINVAL;
+		}
+	}
+
+	rc = lpm_parse_v6_net(in[CB_FLD_DST_ADDR], v->ip_32, &v->depth);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_net failed\n");
+		return rc;
+	}
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+lpm_parse_v4_net(const char *in, uint32_t *addr, uint8_t *mask_len)
+{
+	uint8_t a, b, c, d, m;
+
+	GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, d, 0, UINT8_MAX, '/');
+	GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
+
+	addr[0] = IPv4(a, b, c, d);
+	*mask_len = m;
+
+	return 0;
+}
+
+static int
+lpm_parse_v4_rule(char *str, struct lpm_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = lpm_parse_v4_net(in[CB_FLD_DST_ADDR], &v->ip, &v->depth);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v4_net failed %d\n", rc);
+		return rc;
+	}
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+lpm_add_rules(const char *rule_path,
+		struct lpm_rule **proute_base,
+		unsigned int *proute_num,
+		int (*parser)(char *, struct lpm_rule *))
+{
+	uint8_t *route_rules;
+	struct lpm_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh = fopen(rule_path, "rb");
+	unsigned int i = 0, rule_size = sizeof(*next);
+
+	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] == LPM_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0)
+		rte_exit(EXIT_FAILURE, "Not find any route entries in %s!\n",
+				rule_path);
+
+	fseek(fh, 0, SEEK_SET);
+
+	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 == LPM_LEAD_CHAR)
+			next = (struct lpm_rule *)
+				(route_rules + route_cnt * rule_size);
+
+		/* Illegal line */
+		else
+			rte_exit(EXIT_FAILURE,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, LPM_LEAD_CHAR);
+
+		if (parser(buff + 1, next) != 0)
+			rte_exit(EXIT_FAILURE,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = (struct lpm_rule *)route_rules;
+	*proute_num = route_cnt;
+
+	return 0;
+}
+
+static int
+check_lpm_config(void)
+{
+	if (parm_config.rule_ipv4_name == NULL) {
+		RTE_LOG(ERR, L3FWD, "LPM IPv4 rule file not specified\n");
+		return -1;
+	} else if (parm_config.rule_ipv6_name == NULL) {
+		RTE_LOG(ERR, L3FWD, "LPM IPv6 rule file not specified\n");
+		return -1;
+	}
+
+	return 0;
+}
+
 /* main processing loop */
 int
 lpm_main_loop(__attribute__((unused)) void *dummy)
@@ -196,10 +380,16 @@ setup_lpm(const int socketid)
 {
 	struct rte_lpm6_config config;
 	struct rte_lpm_config config_ipv4;
+	struct lpm_rule *route_base_v4;
+	struct lpm_rule *route_base_v6;
+	unsigned int route_num_v4 = 0, route_num_v6 = 0;
 	unsigned i;
 	int ret;
 	char s[64];
 
+	if (check_lpm_config() != 0)
+		rte_exit(EXIT_FAILURE, "Failed to get valid LPM options\n");
+
 	/* create the LPM table */
 	config_ipv4.max_rules = IPV4_L3FWD_LPM_MAX_RULES;
 	config_ipv4.number_tbl8s = IPV4_L3FWD_LPM_NUMBER_TBL8S;
@@ -212,18 +402,25 @@ setup_lpm(const int socketid)
 			"Unable to create v4 LPM table on socket %d\n",
 			socketid);
 
+	/* Load rules from the input file */
+	if (lpm_add_rules(parm_config.rule_ipv4_name,
+		&route_base_v4, &route_num_v4,
+		&lpm_parse_v4_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add lpm v4 rules\n");
+
 	/* populate the LPM table */
-	for (i = 0; i < IPV4_L3FWD_LPM_NUM_ROUTES; i++) {
+	for (i = 0; i < route_num_v4; i++) {
 
 		/* skip unused ports */
-		if ((1 << ipv4_l3fwd_lpm_route_array[i].if_out &
-				enabled_port_mask) == 0)
+		if ((1 << route_base_v4[i].if_out &
+			enabled_port_mask) == 0)
 			continue;
 
-		ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
-				ipv4_l3fwd_lpm_route_array[i].ip,
-				ipv4_l3fwd_lpm_route_array[i].depth,
-				ipv4_l3fwd_lpm_route_array[i].if_out);
+		ret = rte_lpm_add(
+			ipv4_l3fwd_lpm_lookup_struct[socketid],
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE,
@@ -231,9 +428,9 @@ setup_lpm(const int socketid)
 		}
 
 		printf("LPM: Adding route 0x%08x / %d (%d)\n",
-			(unsigned)ipv4_l3fwd_lpm_route_array[i].ip,
-			ipv4_l3fwd_lpm_route_array[i].depth,
-			ipv4_l3fwd_lpm_route_array[i].if_out);
+			(unsigned int)route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 	}
 
 	/* create the LPM6 table */
@@ -249,18 +446,25 @@ setup_lpm(const int socketid)
 			"Unable to create v6 LPM table on socket %d\n",
 			socketid);
 
+	/* Load rules from the input file */
+	if (lpm_add_rules(parm_config.rule_ipv6_name,
+		&route_base_v6, &route_num_v6,
+		&lpm_parse_v6_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add lpm v6 rules\n");
+
 	/* populate the LPM table */
-	for (i = 0; i < IPV6_L3FWD_LPM_NUM_ROUTES; i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* skip unused ports */
-		if ((1 << ipv6_l3fwd_lpm_route_array[i].if_out &
-				enabled_port_mask) == 0)
+		if ((1 << route_base_v6[i].if_out &
+			enabled_port_mask) == 0)
 			continue;
 
-		ret = rte_lpm6_add(ipv6_l3fwd_lpm_lookup_struct[socketid],
-			ipv6_l3fwd_lpm_route_array[i].ip,
-			ipv6_l3fwd_lpm_route_array[i].depth,
-			ipv6_l3fwd_lpm_route_array[i].if_out);
+		ret = rte_lpm6_add(
+			ipv6_l3fwd_lpm_lookup_struct[socketid],
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE,
@@ -268,8 +472,8 @@ setup_lpm(const int socketid)
 		}
 
 		printf("LPM: Adding route %s / %d (%d)\n",
-			"IPV6", ipv6_l3fwd_lpm_route_array[i].depth,
-			ipv6_l3fwd_lpm_route_array[i].if_out);
+			"IPV6", route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 	}
 }
 
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index 3f35762..2624cd4 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -126,6 +126,8 @@ uint32_t hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT;
 
 struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+struct parm_cfg parm_config;
+
 struct lcore_params {
 	uint8_t port_id;
 	uint8_t queue_id;
@@ -212,6 +214,27 @@ static struct l3fwd_lkp_mode l3fwd_acl_lkp = {
 };
 
 /*
+ * API's called during initialization to setup ACL/LPM/EM rules.
+ */
+static void
+l3fwd_set_rule_ipv4_name(const char *optarg)
+{
+	parm_config.rule_ipv4_name = optarg;
+}
+
+static void
+l3fwd_set_rule_ipv6_name(const char *optarg)
+{
+	parm_config.rule_ipv6_name = optarg;
+}
+
+static void
+l3fwd_set_scalar(void)
+{
+	parm_config.scalar = 1;
+}
+
+/*
  * Setup lookup methods for forwarding.
  * Currently exact-match and longest-prefix-match
  * are supported ones.
@@ -689,15 +712,15 @@ parse_args(int argc, char **argv)
 			break;
 
 		case CMD_LINE_OPT_RULE_IPV4:
-			l3fwd_acl_set_rule_ipv4_name(optarg);
+			l3fwd_set_rule_ipv4_name(optarg);
 			break;
 
 		case CMD_LINE_OPT_RULE_IPV6:
-			l3fwd_acl_set_rule_ipv6_name(optarg);
+			l3fwd_set_rule_ipv6_name(optarg);
 			break;
 
 		case CMD_LINE_OPT_SCALAR:
-			l3fwd_acl_set_scalar();
+			l3fwd_set_scalar();
 			break;
 
 		default:
@@ -895,6 +918,24 @@ prepare_ptype_parser(uint8_t portid, uint16_t queueid)
 	return 0;
 }
 
+/* Bypass comment and empty lines */
+int
+is_bypass_line(char *buff)
+{
+	int i = 0;
+
+	/* comment line */
+	if (buff[0] == COMMENT_LEAD_CHAR)
+		return 1;
+	/* empty line */
+	while (buff[i] != '\0') {
+		if (!isspace(buff[i]))
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
 int
 main(int argc, char **argv)
 {
-- 
2.7.4

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [dpdk-dev] [PATCH v5 3/3] examples/l3fwd: add config file support for EM
  2017-03-08 21:32   ` [dpdk-dev] [PATCH v5 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd Ravi Kerur
  2017-03-08 21:32     ` [dpdk-dev] [PATCH v5 1/3] " Ravi Kerur
  2017-03-08 21:32     ` [dpdk-dev] [PATCH v5 2/3] examples/l3fwd: add config file support for LPM Ravi Kerur
@ 2017-03-08 21:32     ` Ravi Kerur
  2017-03-09 10:45       ` Mcnamara, John
  2 siblings, 1 reply; 34+ messages in thread
From: Ravi Kerur @ 2017-03-08 21:32 UTC (permalink / raw)
  To: dev, konstantin.ananyev, bruce.richardson; +Cc: Ravi Kerur

Add config file support for v4 and v6 to build exact match
forwarding tables.

v5:
	> No changes.

v4:
	> No changes.

v3:
	> Fix additional checkpatch coding style issues.

v2:
	> Fix checkpatch warnings.

v1:
	> Remove static array configuration of Dest IP,Src IP, Dest
		port, Src port, Proto and IF_OUT for EM and EM6 config.
	> Add reading configuration from a file.
	> Format of configuration file is as follows
		#EM route entries,
		#Dest-IP Src-IP Dest-port Src-port Proto IF_OUT
		E101.0.0.0 100.10.0.0 101 11 0x06 0
		E201.0.0.0 200.20.0.0 102 12 0x06 1
		E111.0.0.0 211.30.0.0 101 11 0x06 2
		...

		#EM6 route entries
		#Dest-IP Src-IP Dest-port Src-port Proto IF_OUT
		Efe80:0000:0000:0000:021e:67ff:fe00:0000
			fe80:0000:0000:0000:021b:21ff:fe91:3805 101 11 0x06 0
		Efe90:0000:0000:0000:021e:67ff:fe00:0000
			fe90:0000:0000:0000:021b:21ff:fe91:3805 102 12 0x06 1
		...

Signed-off-by: Ravi Kerur <rkerur@gmail.com>
---
 examples/l3fwd/l3fwd_em.c | 376 +++++++++++++++++++++++++++++++++++++---------
 1 file changed, 303 insertions(+), 73 deletions(-)

diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index 6fdabf7..cd6b443 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -95,8 +95,14 @@ union ipv4_5tuple_host {
 #define XMM_NUM_IN_IPV6_5TUPLE 3
 
 struct ipv6_5tuple {
-	uint8_t  ip_dst[IPV6_ADDR_LEN];
-	uint8_t  ip_src[IPV6_ADDR_LEN];
+	union {
+		uint8_t  ip_dst[IPV6_ADDR_LEN];
+		uint32_t ip32_dst[4];
+	};
+	union {
+		uint8_t  ip_src[IPV6_ADDR_LEN];
+		uint32_t ip32_src[4];
+	};
 	uint16_t port_dst;
 	uint16_t port_src;
 	uint8_t  proto;
@@ -116,47 +122,24 @@ union ipv6_5tuple_host {
 	xmm_t xmm[XMM_NUM_IN_IPV6_5TUPLE];
 };
 
-
-
-struct ipv4_l3fwd_em_route {
-	struct ipv4_5tuple key;
-	uint8_t if_out;
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_SRC_ADDR,
+	CB_FLD_DST_PORT,
+	CB_FLD_SRC_PORT,
+	CB_FLD_PROTO,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
 };
 
-struct ipv6_l3fwd_em_route {
-	struct ipv6_5tuple key;
+struct em_rule {
+	union {
+		struct ipv4_5tuple v4_key;
+		struct ipv6_5tuple v6_key;
+	};
 	uint8_t if_out;
 };
 
-static struct ipv4_l3fwd_em_route ipv4_l3fwd_em_route_array[] = {
-	{{IPv4(101, 0, 0, 0), IPv4(100, 10, 0, 1),  101, 11, IPPROTO_TCP}, 0},
-	{{IPv4(201, 0, 0, 0), IPv4(200, 20, 0, 1),  102, 12, IPPROTO_TCP}, 1},
-	{{IPv4(111, 0, 0, 0), IPv4(100, 30, 0, 1),  101, 11, IPPROTO_TCP}, 2},
-	{{IPv4(211, 0, 0, 0), IPv4(200, 40, 0, 1),  102, 12, IPPROTO_TCP}, 3},
-};
-
-static struct ipv6_l3fwd_em_route ipv6_l3fwd_em_route_array[] = {
-	{{
-	{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x02, 0x1e, 0x67, 0xff, 0xfe, 0, 0, 0},
-	{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x02, 0x1b, 0x21, 0xff, 0xfe, 0x91, 0x38, 0x05},
-	101, 11, IPPROTO_TCP}, 0},
-
-	{{
-	{0xfe, 0x90, 0, 0, 0, 0, 0, 0, 0x02, 0x1e, 0x67, 0xff, 0xfe, 0, 0, 0},
-	{0xfe, 0x90, 0, 0, 0, 0, 0, 0, 0x02, 0x1b, 0x21, 0xff, 0xfe, 0x91, 0x38, 0x05},
-	102, 12, IPPROTO_TCP}, 1},
-
-	{{
-	{0xfe, 0xa0, 0, 0, 0, 0, 0, 0, 0x02, 0x1e, 0x67, 0xff, 0xfe, 0, 0, 0},
-	{0xfe, 0xa0, 0, 0, 0, 0, 0, 0, 0x02, 0x1b, 0x21, 0xff, 0xfe, 0x91, 0x38, 0x05},
-	101, 11, IPPROTO_TCP}, 2},
-
-	{{
-	{0xfe, 0xb0, 0, 0, 0, 0, 0, 0, 0x02, 0x1e, 0x67, 0xff, 0xfe, 0, 0, 0},
-	{0xfe, 0xb0, 0, 0, 0, 0, 0, 0, 0x02, 0x1b, 0x21, 0xff, 0xfe, 0x91, 0x38, 0x05},
-	102, 12, IPPROTO_TCP}, 3},
-};
-
 struct rte_hash *ipv4_l3fwd_em_lookup_struct[NB_SOCKETS];
 struct rte_hash *ipv6_l3fwd_em_lookup_struct[NB_SOCKETS];
 
@@ -233,12 +216,6 @@ ipv6_hash_crc(const void *data, __rte_unused uint32_t data_len,
 	return init_val;
 }
 
-#define IPV4_L3FWD_EM_NUM_ROUTES \
-	(sizeof(ipv4_l3fwd_em_route_array) / sizeof(ipv4_l3fwd_em_route_array[0]))
-
-#define IPV6_L3FWD_EM_NUM_ROUTES \
-	(sizeof(ipv6_l3fwd_em_route_array) / sizeof(ipv6_l3fwd_em_route_array[0]))
-
 static uint8_t ipv4_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 static uint8_t ipv6_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 
@@ -338,6 +315,224 @@ em_get_ipv6_dst_port(void *ipv6_hdr,  uint8_t portid, void *lookup_struct)
 #include "l3fwd_em.h"
 #endif
 
+static int
+em_parse_v6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
+	char dlm)
+{
+	uint32_t addr[IPV6_ADDR_U16];
+
+	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
+
+	*end = in;
+
+	v[0] = (addr[0] << 16) + addr[1];
+	v[1] = (addr[2] << 16) + addr[3];
+	v[2] = (addr[4] << 16) + addr[5];
+	v[3] = (addr[6] << 16) + addr[7];
+
+	return 0;
+}
+
+static int
+em_parse_v6_net(const char *in, uint32_t *v)
+{
+	int32_t rc;
+	const char *mp;
+
+	/* get address. */
+	rc = em_parse_v6_addr(in, &mp, v, 0);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_addr failed %d\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int
+em_parse_v6_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL) {
+			RTE_LOG(ERR, L3FWD, "strtok failed\n");
+			return -EINVAL;
+		}
+	}
+
+	rc = em_parse_v6_net(in[CB_FLD_DST_ADDR], v->v6_key.ip32_dst);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_net failed for dst %d\n", rc);
+		return rc;
+	}
+	rc = em_parse_v6_net(in[CB_FLD_SRC_ADDR], v->v6_key.ip32_src);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_net failed for src %d\n", rc);
+		return rc;
+	}
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v6_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v6_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v6_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_parse_v4_net(const char *in, uint32_t *addr)
+{
+	uint8_t a, b, c, d;
+
+	GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, d, 0, UINT8_MAX, 0);
+
+	addr[0] = IPv4(a, b, c, d);
+	return 0;
+}
+
+static int
+em_parse_v4_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL) {
+			RTE_LOG(ERR, L3FWD, "parse_v4_rule strtok fail\n");
+			return -EINVAL;
+		}
+	}
+
+	rc = em_parse_v4_net(in[CB_FLD_DST_ADDR], &(v->v4_key.ip_dst));
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v4_net dst failed %d\n", rc);
+		return rc;
+	}
+	rc = em_parse_v4_net(in[CB_FLD_SRC_ADDR], &(v->v4_key.ip_src));
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v4_net src failed %d\n", rc);
+		return rc;
+	}
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v4_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v4_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v4_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_add_rules(const char *rule_path,
+		struct em_rule **proute_base,
+		unsigned int *proute_num,
+		int (*parser)(char *, struct em_rule*))
+{
+	uint8_t *route_rules;
+	struct em_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh = fopen(rule_path, "rb");
+	unsigned int i = 0, rule_size = sizeof(*next);
+
+	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] == EM_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0)
+		rte_exit(EXIT_FAILURE, "Not find any route entries in %s!\n",
+				rule_path);
+
+	fseek(fh, 0, SEEK_SET);
+
+	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 == EM_LEAD_CHAR)
+			next = (struct em_rule *)(route_rules +
+				route_cnt * rule_size);
+
+		/* Illegal line */
+		else
+			rte_exit(EXIT_FAILURE,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, EM_LEAD_CHAR);
+
+		if (parser(buff + 1, next) != 0)
+			rte_exit(EXIT_FAILURE,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = (struct em_rule *)route_rules;
+	*proute_num = route_cnt;
+
+	return 0;
+}
+
+static int
+check_em_config(void)
+{
+	if (parm_config.rule_ipv4_name == NULL) {
+		RTE_LOG(ERR, L3FWD, "EM IPv4 rule file not specified\n");
+		return -1;
+	} else if (parm_config.rule_ipv6_name == NULL) {
+		RTE_LOG(ERR, L3FWD, "EM IPv6 rule file not specified\n");
+		return -1;
+	}
+
+	return 0;
+}
+
 static void
 convert_ipv4_5tuple(struct ipv4_5tuple *key1,
 		union ipv4_5tuple_host *key2)
@@ -378,16 +573,24 @@ populate_ipv4_few_flow_into_table(const struct rte_hash *h)
 {
 	uint32_t i;
 	int32_t ret;
+	struct em_rule *route_base_v4;
+	unsigned int route_num_v4 = 0;
 
 	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
-	for (i = 0; i < IPV4_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv4_l3fwd_em_route  entry;
+	/* Load rules from the input file */
+	if (em_add_rules(parm_config.rule_ipv4_name,
+		&route_base_v4, &route_num_v4,
+		&em_parse_v4_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add em v4 rules\n");
+
+	for (i = 0; i < route_num_v4; i++) {
+		struct em_rule  entry;
 		union ipv4_5tuple_host newkey;
 
-		entry = ipv4_l3fwd_em_route_array[i];
-		convert_ipv4_5tuple(&entry.key, &newkey);
+		entry = route_base_v4[i];
+		convert_ipv4_5tuple(&entry.v4_key, &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
@@ -396,7 +599,7 @@ populate_ipv4_few_flow_into_table(const struct rte_hash *h)
 		ipv4_l3fwd_out_if[ret] = entry.if_out;
 	}
 	printf("Hash: Adding 0x%" PRIx64 " keys\n",
-		(uint64_t)IPV4_L3FWD_EM_NUM_ROUTES);
+		(uint64_t)route_num_v4);
 }
 
 #define BIT_16_TO_23 0x00ff0000
@@ -405,18 +608,26 @@ populate_ipv6_few_flow_into_table(const struct rte_hash *h)
 {
 	uint32_t i;
 	int32_t ret;
+	struct em_rule *route_base_v6;
+	unsigned int route_num_v6 = 0;
 
 	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
 	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
 
-	for (i = 0; i < IPV6_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv6_l3fwd_em_route entry;
+	/* Load rules from the input file */
+	if (em_add_rules(parm_config.rule_ipv6_name,
+		&route_base_v6, &route_num_v6,
+		&em_parse_v6_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add em v6 rules\n");
+
+	for (i = 0; i < route_num_v6; i++) {
+		struct em_rule entry;
 		union ipv6_5tuple_host newkey;
 
-		entry = ipv6_l3fwd_em_route_array[i];
-		convert_ipv6_5tuple(&entry.key, &newkey);
+		entry = route_base_v6[i];
+		convert_ipv6_5tuple(&entry.v6_key, &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
@@ -425,7 +636,7 @@ populate_ipv6_few_flow_into_table(const struct rte_hash *h)
 		ipv6_l3fwd_out_if[ret] = entry.if_out;
 	}
 	printf("Hash: Adding 0x%" PRIx64 "keys\n",
-		(uint64_t)IPV6_L3FWD_EM_NUM_ROUTES);
+		(uint64_t)route_num_v6);
 }
 
 #define NUMBER_PORT_USED 4
@@ -434,12 +645,20 @@ populate_ipv4_many_flow_into_table(const struct rte_hash *h,
 		unsigned int nr_flow)
 {
 	unsigned i;
+	struct em_rule *route_base_v4;
+	unsigned int route_num_v4 = 0;
 
 	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
+	/* Load rules from the input file */
+	if (em_add_rules(parm_config.rule_ipv4_name,
+		&route_base_v4, &route_num_v4,
+		&em_parse_v4_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add em v4 rules\n");
+
 	for (i = 0; i < nr_flow; i++) {
-		struct ipv4_l3fwd_em_route entry;
+		struct em_rule entry;
 		union ipv4_5tuple_host newkey;
 
 		uint8_t a = (uint8_t)
@@ -453,23 +672,23 @@ populate_ipv4_many_flow_into_table(const struct rte_hash *h,
 		memset(&entry, 0, sizeof(entry));
 		switch (i & (NUMBER_PORT_USED - 1)) {
 		case 0:
-			entry = ipv4_l3fwd_em_route_array[0];
-			entry.key.ip_dst = IPv4(101, c, b, a);
+			entry = route_base_v4[0];
+			entry.v4_key.ip_dst = IPv4(101, c, b, a);
 			break;
 		case 1:
-			entry = ipv4_l3fwd_em_route_array[1];
-			entry.key.ip_dst = IPv4(201, c, b, a);
+			entry = route_base_v4[1];
+			entry.v4_key.ip_dst = IPv4(201, c, b, a);
 			break;
 		case 2:
-			entry = ipv4_l3fwd_em_route_array[2];
-			entry.key.ip_dst = IPv4(111, c, b, a);
+			entry = route_base_v4[2];
+			entry.v4_key.ip_dst = IPv4(111, c, b, a);
 			break;
 		case 3:
-			entry = ipv4_l3fwd_em_route_array[3];
-			entry.key.ip_dst = IPv4(211, c, b, a);
+			entry = route_base_v4[3];
+			entry.v4_key.ip_dst = IPv4(211, c, b, a);
 			break;
 		};
-		convert_ipv4_5tuple(&entry.key, &newkey);
+		convert_ipv4_5tuple(&entry.v4_key, &newkey);
 		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
 
 		if (ret < 0)
@@ -486,13 +705,21 @@ populate_ipv6_many_flow_into_table(const struct rte_hash *h,
 		unsigned int nr_flow)
 {
 	unsigned i;
+	struct em_rule *route_base_v6;
+	unsigned int route_num_v6 = 0;
 
 	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
 
+	/* Load rules from the input file */
+	if (em_add_rules(parm_config.rule_ipv6_name,
+		&route_base_v6, &route_num_v6,
+		&em_parse_v6_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add em v6 rules\n");
+
 	for (i = 0; i < nr_flow; i++) {
-		struct ipv6_l3fwd_em_route entry;
+		struct em_rule entry;
 		union ipv6_5tuple_host newkey;
 
 		uint8_t a = (uint8_t)
@@ -506,22 +733,22 @@ populate_ipv6_many_flow_into_table(const struct rte_hash *h,
 		memset(&entry, 0, sizeof(entry));
 		switch (i & (NUMBER_PORT_USED - 1)) {
 		case 0:
-			entry = ipv6_l3fwd_em_route_array[0];
+			entry = route_base_v6[0];
 			break;
 		case 1:
-			entry = ipv6_l3fwd_em_route_array[1];
+			entry = route_base_v6[1];
 			break;
 		case 2:
-			entry = ipv6_l3fwd_em_route_array[2];
+			entry = route_base_v6[2];
 			break;
 		case 3:
-			entry = ipv6_l3fwd_em_route_array[3];
+			entry = route_base_v6[3];
 			break;
 		};
-		entry.key.ip_dst[13] = c;
-		entry.key.ip_dst[14] = b;
-		entry.key.ip_dst[15] = a;
-		convert_ipv6_5tuple(&entry.key, &newkey);
+		entry.v6_key.ip_dst[13] = c;
+		entry.v6_key.ip_dst[14] = b;
+		entry.v6_key.ip_dst[15] = a;
+		convert_ipv6_5tuple(&entry.v6_key, &newkey);
 		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
 
 		if (ret < 0)
@@ -746,6 +973,9 @@ setup_hash(const int socketid)
 
 	char s[64];
 
+	if (check_em_config() != 0)
+		rte_exit(EXIT_FAILURE, "Failed to get valid EM options\n");
+
 	/* create ipv4 hash */
 	snprintf(s, sizeof(s), "ipv4_l3fwd_hash_%d", socketid);
 	ipv4_l3fwd_hash_params.name = s;
-- 
2.7.4

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd
  2017-03-06 23:20   ` Ravi Kerur
@ 2017-03-08 21:50     ` Ravi Kerur
  2017-03-09 10:37       ` Mcnamara, John
  2017-03-09 10:23     ` Mcnamara, John
  1 sibling, 1 reply; 34+ messages in thread
From: Ravi Kerur @ 2017-03-08 21:50 UTC (permalink / raw)
  To: Mcnamara, John; +Cc: dev, Ananyev, Konstantin, Richardson, Bruce

John, Konstantin,

Kindly let me know new 'v5' patch follows dpdk guidelines?

I just followed what's in the dpdk contributing code guidelines.


   - git format-patch -3 -o patch/ -v 5 --cover-letter
   - git send-email --to dev@dpdk.org --in-reply-to <v4-MSGID> patch/*

Thanks.


On Mon, Mar 6, 2017 at 3:20 PM, Ravi Kerur <rkerur@gmail.com> wrote:

> Hi John,
>
> Should I work with documentation team to update the document? If yes,
> please let me know the contact information.
>
> Thanks.
>
> On Mon, Mar 6, 2017 at 5:33 AM, Mcnamara, John <john.mcnamara@intel.com>
> wrote:
>
>> > -----Original Message-----
>> > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ravi Kerur
>> > Sent: Sunday, March 5, 2017 7:47 PM
>> > To: dev@dpdk.org
>> > Cc: Ananyev, Konstantin <konstantin.ananyev@intel.com>; Richardson,
>> Bruce
>> > <bruce.richardson@intel.com>; Ravi Kerur <rkerur@gmail.com>
>> > Subject: [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd
>> >
>> > This patchset merges l3fwd-acl and l3fwd code into common directory.
>> > Adds file read options to build LPM and EM tables.
>>
>> Hi Ravi,
>>
>> Thanks to this. It seems like a good change. There are probably too many
>> L2/L3 variants and some of them should be merged.
>>
>> Note, you will also have to merge the sample app guides in some sensible
>> way:
>>
>>     http://dpdk.org/doc/guides/sample_app_ug/l3_forward.html
>>     http://dpdk.org/doc/guides/sample_app_ug/l3_forward_access_ctrl.html
>>
>> John
>>
>>
>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd
  2017-03-06 23:20   ` Ravi Kerur
  2017-03-08 21:50     ` Ravi Kerur
@ 2017-03-09 10:23     ` Mcnamara, John
  2017-03-10  0:07       ` Ravi Kerur
  1 sibling, 1 reply; 34+ messages in thread
From: Mcnamara, John @ 2017-03-09 10:23 UTC (permalink / raw)
  To: Ravi Kerur; +Cc: dev, Ananyev, Konstantin, Richardson, Bruce

> From: Ravi Kerur [mailto:rkerur@gmail.com] 
> Sent: Monday, March 6, 2017 11:21 PM
> To: Mcnamara, John <john.mcnamara@intel.com>
> Cc: dev@dpdk.org; Ananyev, Konstantin <konstantin.ananyev@intel.com>; 
>     Richardson, Bruce <bruce.richardson@intel.com>
> Subject: Re: [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd
> 
> 
> Should I work with documentation team to update the document? If yes, 
> please let me know the contact information.

Hi Ravi,

There isn't any documentation team. Unfortunately. :-)

The raw documentation is in text format and is generally updated by
the developer submitting a patch.

There is already docs for l3fwd and l3fwd_acl so all that is required
is to merge them. However, you will need to take some care to remove
the duplicate sections and to make sure that the merged doc makes
sense as a whole.

See the documentation guidelines:

    http://dpdk.org/doc/guides/contributing/documentation.html
 
John

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd
  2017-03-08 21:50     ` Ravi Kerur
@ 2017-03-09 10:37       ` Mcnamara, John
  0 siblings, 0 replies; 34+ messages in thread
From: Mcnamara, John @ 2017-03-09 10:37 UTC (permalink / raw)
  To: Ravi Kerur; +Cc: dev, Ananyev, Konstantin, Richardson, Bruce



> From: Ravi Kerur [mailto:rkerur@gmail.com] 
> Sent: Wednesday, March 8, 2017 9:51 PM
> To: Mcnamara, John <john.mcnamara@intel.com>
> Cc: dev@dpdk.org; Ananyev, Konstantin <konstantin.ananyev@intel.com>; Richardson, Bruce <bruce.richardson@intel.com>
> Subject: Re: [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd


> Kindly let me know new 'v5' patch follows dpdk guidelines?


Hi Ravi,

In general the commit messages don't meet the guidelines, so it is 
probably worth reading through the guidelines again:

   http://dpdk.org/doc/guides/contributing/patches.html

In particular have a look at the sections on the "subject line" and the
"message body".

I'll make some other comments to your patches to help you along.

Thanks,

John

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [PATCH v5 1/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
  2017-03-08 21:32     ` [dpdk-dev] [PATCH v5 1/3] " Ravi Kerur
@ 2017-03-09 10:42       ` Mcnamara, John
  2017-03-10 20:58       ` [dpdk-dev] [PATCH v6 0/3] " Ravi Kerur
  1 sibling, 0 replies; 34+ messages in thread
From: Mcnamara, John @ 2017-03-09 10:42 UTC (permalink / raw)
  To: Ravi Kerur, dev, Ananyev, Konstantin, Richardson, Bruce



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ravi Kerur
> Sent: Wednesday, March 8, 2017 9:32 PM
> To: dev@dpdk.org; Ananyev, Konstantin <konstantin.ananyev@intel.com>;
> Richardson, Bruce <bruce.richardson@intel.com>
> Cc: Ravi Kerur <rkerur@gmail.com>
> Subject: [dpdk-dev] [PATCH v5 1/3] examples/l3fwd: merge l3fwd-acl code
> into l3fwd
> 
> Merge l3fwd-acl code into l3fwd with '-A' cmdline option to run ACL.

Hi Ravi,

The subject line and commit message are okay. the only issue is that
the version information should be after a triple dash line so that
is doesn't appear in the commit message.

The version information is intended for reviewers so that know what
has changed since the last version. Just put the following before it:

---


> 
> v5:
> 	> None.
> 
> v4:
>         > Initialize rss_hf to IP for LPM, EM and ACL.
>         > Update rss_hf with l4 in parse_args for ACL.
>         > Fix pending checkpatch code indentation warning.


This applies to all the commit messages.

John

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [PATCH v5 2/3] examples/l3fwd: add config file support for LPM
  2017-03-08 21:32     ` [dpdk-dev] [PATCH v5 2/3] examples/l3fwd: add config file support for LPM Ravi Kerur
@ 2017-03-09 10:43       ` Mcnamara, John
  0 siblings, 0 replies; 34+ messages in thread
From: Mcnamara, John @ 2017-03-09 10:43 UTC (permalink / raw)
  To: Ravi Kerur, dev, Ananyev, Konstantin, Richardson, Bruce



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ravi Kerur
> Sent: Wednesday, March 8, 2017 9:32 PM
> To: dev@dpdk.org; Ananyev, Konstantin <konstantin.ananyev@intel.com>;
> Richardson, Bruce <bruce.richardson@intel.com>
> Cc: Ravi Kerur <rkerur@gmail.com>
> Subject: [dpdk-dev] [PATCH v5 2/3] examples/l3fwd: add config file support
> for LPM
> 
> Add config file support for v4 and v6 to build forwarding tables.


Hi Ravi,

The subject line is fine. The body might be better saying IPv4 and IPv6 since
I initially thought you were referring to the versions below.

> 
> v5:
> 	> Changes is_bypass_line from inline to non-line.
> 
> v4:
> 	> No changes.


Same comment as before about ---.

John.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [PATCH v5 3/3] examples/l3fwd: add config file support for EM
  2017-03-08 21:32     ` [dpdk-dev] [PATCH v5 3/3] examples/l3fwd: add config file support for EM Ravi Kerur
@ 2017-03-09 10:45       ` Mcnamara, John
  2017-03-10  0:00         ` Ravi Kerur
  0 siblings, 1 reply; 34+ messages in thread
From: Mcnamara, John @ 2017-03-09 10:45 UTC (permalink / raw)
  To: Ravi Kerur, dev, Ananyev, Konstantin, Richardson, Bruce



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ravi Kerur
> Sent: Wednesday, March 8, 2017 9:32 PM
> To: dev@dpdk.org; Ananyev, Konstantin <konstantin.ananyev@intel.com>;
> Richardson, Bruce <bruce.richardson@intel.com>
> Cc: Ravi Kerur <rkerur@gmail.com>
> Subject: [dpdk-dev] [PATCH v5 3/3] examples/l3fwd: add config file support
> for EM
> 
> Add config file support for v4 and v6 to build exact match forwarding
> tables.

Hi Ravi,

Same comment about Ipv4 and IPv6 and ---.

Also it is probably better to use "exact match" instead of "EM" in the
subject. It isn't as well known an acronym as LPM.

John

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [PATCH v5 3/3] examples/l3fwd: add config file support for EM
  2017-03-09 10:45       ` Mcnamara, John
@ 2017-03-10  0:00         ` Ravi Kerur
  0 siblings, 0 replies; 34+ messages in thread
From: Ravi Kerur @ 2017-03-10  0:00 UTC (permalink / raw)
  To: Mcnamara, John; +Cc: dev, Ananyev, Konstantin, Richardson, Bruce

Hi John,

On Thu, Mar 9, 2017 at 2:45 AM, Mcnamara, John <john.mcnamara@intel.com>
wrote:

>
>
> > -----Original Message-----
> > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ravi Kerur
> > Sent: Wednesday, March 8, 2017 9:32 PM
> > To: dev@dpdk.org; Ananyev, Konstantin <konstantin.ananyev@intel.com>;
> > Richardson, Bruce <bruce.richardson@intel.com>
> > Cc: Ravi Kerur <rkerur@gmail.com>
> > Subject: [dpdk-dev] [PATCH v5 3/3] examples/l3fwd: add config file
> support
> > for EM
> >
> > Add config file support for v4 and v6 to build exact match forwarding
> > tables.
>
> Hi Ravi,
>
> Same comment about Ipv4 and IPv6 and ---.
>
> Also it is probably better to use "exact match" instead of "EM" in the
> subject. It isn't as well known an acronym as LPM.
>
> I will make changes to all patches and send out updated version.

Thanks.

> John
>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd
  2017-03-09 10:23     ` Mcnamara, John
@ 2017-03-10  0:07       ` Ravi Kerur
  2017-03-14 21:14         ` Ravi Kerur
  0 siblings, 1 reply; 34+ messages in thread
From: Ravi Kerur @ 2017-03-10  0:07 UTC (permalink / raw)
  To: Mcnamara, John; +Cc: dev, Ananyev, Konstantin, Richardson, Bruce

Hi John,

On Thu, Mar 9, 2017 at 2:23 AM, Mcnamara, John <john.mcnamara@intel.com>
wrote:

> > From: Ravi Kerur [mailto:rkerur@gmail.com]
> > Sent: Monday, March 6, 2017 11:21 PM
> > To: Mcnamara, John <john.mcnamara@intel.com>
> > Cc: dev@dpdk.org; Ananyev, Konstantin <konstantin.ananyev@intel.com>;
> >     Richardson, Bruce <bruce.richardson@intel.com>
> > Subject: Re: [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd
> >
> >
> > Should I work with documentation team to update the document? If yes,
> > please let me know the contact information.
>
> Hi Ravi,
>
> There isn't any documentation team. Unfortunately. :-)
>
> The raw documentation is in text format and is generally updated by
> the developer submitting a patch.
>
> There is already docs for l3fwd and l3fwd_acl so all that is required
> is to merge them. However, you will need to take some care to remove
> the duplicate sections and to make sure that the merged doc makes
> sense as a whole.
>
> See the documentation guidelines:
>
>     http://dpdk.org/doc/guides/contributing/documentation.html


Thanks. I realized after skimming through the documents and seeing a 'doc:'
prefix for review. Some clarifications on merging 'l3fwd' and 'l3fwd-acl'

1. Instead of wholesale merger of both 'l3fwd' and 'l3fwd-acl', does it
make sense to

a. Modify 'l3fwd' document by adding ACL processing information ('-A'
cmdline) and providing a link or reference to existing 'l3fwd-acl'
documentation since nothing has changed in terms of 'l3fwd-acl'.

b. Add file read options for 'exact match' and 'longest prefix match' in
'l3fwd' documentation.

 Thanks.

>
>
> John
>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [dpdk-dev] [PATCH v6 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
  2017-03-08 21:32     ` [dpdk-dev] [PATCH v5 1/3] " Ravi Kerur
  2017-03-09 10:42       ` Mcnamara, John
@ 2017-03-10 20:58       ` Ravi Kerur
  2017-03-10 20:58         ` [dpdk-dev] [PATCH v6 1/3] " Ravi Kerur
                           ` (4 more replies)
  1 sibling, 5 replies; 34+ messages in thread
From: Ravi Kerur @ 2017-03-10 20:58 UTC (permalink / raw)
  To: dev; +Cc: john.mcnamara, Ravi Kerur

This patchset merges l3fwd-acl and l3fwd code into common directory.
Adds config file read option to build LPM and EM tables.

Ravi Kerur (3):
  examples/l3fwd: merge l3fwd-acl code into l3fwd
  examples/l3fwd: add config file support for lpm
  examples/l3fwd: add config file support for exact

 examples/l3fwd-acl/Makefile       |   56 -
 examples/l3fwd-acl/main.c         | 2079 -------------------------------------
 examples/l3fwd/Makefile           |    2 +-
 examples/l3fwd/l3fwd.h            |   77 ++
 examples/l3fwd/l3fwd_acl.c        | 1033 ++++++++++++++++++
 examples/l3fwd/l3fwd_acl.h        |  234 +++++
 examples/l3fwd/l3fwd_acl_scalar.h |  182 ++++
 examples/l3fwd/l3fwd_em.c         |  390 +++++--
 examples/l3fwd/l3fwd_lpm.c        |  323 ++++--
 examples/l3fwd/main.c             |  250 +++--
 10 files changed, 2286 insertions(+), 2340 deletions(-)
 delete mode 100644 examples/l3fwd-acl/Makefile
 delete mode 100644 examples/l3fwd-acl/main.c
 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

-- 
2.7.4

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [dpdk-dev] [PATCH v6 1/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
  2017-03-10 20:58       ` [dpdk-dev] [PATCH v6 0/3] " Ravi Kerur
@ 2017-03-10 20:58         ` Ravi Kerur
  2017-03-10 20:58         ` [dpdk-dev] [PATCH v6 2/3] examples/l3fwd: add config file support for lpm Ravi Kerur
                           ` (3 subsequent siblings)
  4 siblings, 0 replies; 34+ messages in thread
From: Ravi Kerur @ 2017-03-10 20:58 UTC (permalink / raw)
  To: dev; +Cc: john.mcnamara, Ravi Kerur

Merge l3fwd-acl code into l3fwd with '-A' cmdline option
to run ACL. Performance critical ACL inline functions are moved
from l3fwd-acl/main.c to l3fwd/l3fwd_acl_scalar.h.
---

v6:
	> Change commit message format.

v5:
	> None.

v4:
	> Initialize rss_hf to IP for LPM, EM and ACL.
	> Update rss_hf with l4 in parse_args for ACL.
	> Fix pending checkpatch code indentation warning.

v3:
	> Fix additional checkpatch coding style issues.

v2:
	> Fix checkpatch errors and warnings related to non strings
		greater than 80 characters.
	> MACRO GET_CB_FIELD and strings greater than 80 characters
		warnings are not fixed.

v1:
	l3fwd-acl changes:
		> Merge common init code in l3fwd-acl and l3fwd into main.c.
		> Move non-critical inline functions to l3fwd_acl.h.
		> Move critial packet processing inline functions to
			l3fwd_acl_scalar.h
		> Move l3fwd-acl init code to l3fwd_acl.c.
		> Delete l3fwd-acl directory.

	l3fwd changes:
		> Add '-A' as an option for ACL processing.
		> Merge parsing options from l3fwd-acl and l3fwd. Retain
			l3fwd-acl definitions.
		> Move specific setup functions (setup_acl, setup_lpm
			and setup_hash).
	Testing:
		> Compiled successfully for x86_64-native-linuxapp-gcc
		> Tested LPM, EM and ACL basic functionality.

Signed-off-by: Ravi Kerur <rkerur@gmail.com>
---
 examples/l3fwd-acl/Makefile       |   56 -
 examples/l3fwd-acl/main.c         | 2079 -------------------------------------
 examples/l3fwd/Makefile           |    2 +-
 examples/l3fwd/l3fwd.h            |   49 +
 examples/l3fwd/l3fwd_acl.c        | 1064 +++++++++++++++++++
 examples/l3fwd/l3fwd_acl.h        |  263 +++++
 examples/l3fwd/l3fwd_acl_scalar.h |  182 ++++
 examples/l3fwd/l3fwd_em.c         |   14 +-
 examples/l3fwd/l3fwd_lpm.c        |   23 +-
 examples/l3fwd/main.c             |  209 ++--
 10 files changed, 1722 insertions(+), 2219 deletions(-)
 delete mode 100644 examples/l3fwd-acl/Makefile
 delete mode 100644 examples/l3fwd-acl/main.c
 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-acl/Makefile b/examples/l3fwd-acl/Makefile
deleted file mode 100644
index a3473a8..0000000
--- a/examples/l3fwd-acl/Makefile
+++ /dev/null
@@ -1,56 +0,0 @@
-#   BSD LICENSE
-#
-#   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
-#   All rights reserved.
-#
-#   Redistribution and use in source and binary forms, with or without
-#   modification, are permitted provided that the following conditions
-#   are met:
-#
-#     * Redistributions of source code must retain the above copyright
-#       notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above copyright
-#       notice, this list of conditions and the following disclaimer in
-#       the documentation and/or other materials provided with the
-#       distribution.
-#     * Neither the name of Intel Corporation nor the names of its
-#       contributors may be used to endorse or promote products derived
-#       from this software without specific prior written permission.
-#
-#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-ifeq ($(RTE_SDK),)
-$(error "Please define RTE_SDK environment variable")
-endif
-
-# Default target, can be overriden by command line or environment
-RTE_TARGET ?= x86_64-native-linuxapp-gcc
-
-include $(RTE_SDK)/mk/rte.vars.mk
-
-# binary name
-APP = l3fwd-acl
-
-# all source are stored in SRCS-y
-SRCS-y := main.c
-
-CFLAGS += -O3
-CFLAGS += $(WERROR_FLAGS)
-
-# workaround for a gcc bug with noreturn attribute
-# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
-ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y)
-CFLAGS_main.o += -Wno-return-type
-endif
-
-include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/l3fwd-acl/main.c b/examples/l3fwd-acl/main.c
deleted file mode 100644
index 0e3daad..0000000
--- a/examples/l3fwd-acl/main.c
+++ /dev/null
@@ -1,2079 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <sys/types.h>
-#include <string.h>
-#include <sys/queue.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <getopt.h>
-
-#include <rte_common.h>
-#include <rte_byteorder.h>
-#include <rte_log.h>
-#include <rte_memory.h>
-#include <rte_memcpy.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_per_lcore.h>
-#include <rte_launch.h>
-#include <rte_atomic.h>
-#include <rte_cycles.h>
-#include <rte_prefetch.h>
-#include <rte_lcore.h>
-#include <rte_per_lcore.h>
-#include <rte_branch_prediction.h>
-#include <rte_interrupts.h>
-#include <rte_pci.h>
-#include <rte_random.h>
-#include <rte_debug.h>
-#include <rte_ether.h>
-#include <rte_ethdev.h>
-#include <rte_mempool.h>
-#include <rte_mbuf.h>
-#include <rte_ip.h>
-#include <rte_tcp.h>
-#include <rte_udp.h>
-#include <rte_string_fns.h>
-#include <rte_acl.h>
-
-#if RTE_LOG_LEVEL >= RTE_LOG_DEBUG
-#define L3FWDACL_DEBUG
-#endif
-#define DO_RFC_1812_CHECKS
-
-#define RTE_LOGTYPE_L3FWD RTE_LOGTYPE_USER1
-
-#define MAX_JUMBO_PKT_LEN  9600
-
-#define MEMPOOL_CACHE_SIZE 256
-
-/*
- * This expression is used to calculate the number of mbufs needed
- * depending on user input, taking into account memory for rx and tx hardware
- * rings, cache per lcore and mtable per port per lcore.
- * RTE_MAX is used to ensure that NB_MBUF never goes below a
- * minimum value of 8192
- */
-
-#define NB_MBUF	RTE_MAX(\
-	(nb_ports * nb_rx_queue*RTE_TEST_RX_DESC_DEFAULT +	\
-	nb_ports * nb_lcores * MAX_PKT_BURST +			\
-	nb_ports * n_tx_queue * RTE_TEST_TX_DESC_DEFAULT +	\
-	nb_lcores * MEMPOOL_CACHE_SIZE),			\
-	(unsigned)8192)
-
-#define MAX_PKT_BURST 32
-#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */
-
-#define NB_SOCKETS 8
-
-/* Configure how many packets ahead to prefetch, when reading packets */
-#define PREFETCH_OFFSET	3
-
-/*
- * Configurable number of RX/TX ring descriptors
- */
-#define RTE_TEST_RX_DESC_DEFAULT 128
-#define RTE_TEST_TX_DESC_DEFAULT 512
-static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
-static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
-
-/* ethernet addresses of ports */
-static struct ether_addr ports_eth_addr[RTE_MAX_ETHPORTS];
-
-/* mask of enabled ports */
-static uint32_t enabled_port_mask;
-static int promiscuous_on; /**< Ports set in promiscuous mode off by default. */
-static int numa_on = 1; /**< NUMA is enabled by default. */
-
-struct lcore_rx_queue {
-	uint8_t port_id;
-	uint8_t queue_id;
-} __rte_cache_aligned;
-
-#define MAX_RX_QUEUE_PER_LCORE 16
-#define MAX_TX_QUEUE_PER_PORT RTE_MAX_ETHPORTS
-#define MAX_RX_QUEUE_PER_PORT 128
-
-#define MAX_LCORE_PARAMS 1024
-struct lcore_params {
-	uint8_t port_id;
-	uint8_t queue_id;
-	uint8_t lcore_id;
-} __rte_cache_aligned;
-
-static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS];
-static struct lcore_params lcore_params_array_default[] = {
-	{0, 0, 2},
-	{0, 1, 2},
-	{0, 2, 2},
-	{1, 0, 2},
-	{1, 1, 2},
-	{1, 2, 2},
-	{2, 0, 2},
-	{3, 0, 3},
-	{3, 1, 3},
-};
-
-static struct lcore_params *lcore_params = lcore_params_array_default;
-static uint16_t nb_lcore_params = sizeof(lcore_params_array_default) /
-				sizeof(lcore_params_array_default[0]);
-
-static struct rte_eth_conf port_conf = {
-	.rxmode = {
-		.mq_mode	= ETH_MQ_RX_RSS,
-		.max_rx_pkt_len = ETHER_MAX_LEN,
-		.split_hdr_size = 0,
-		.header_split   = 0, /**< Header Split disabled */
-		.hw_ip_checksum = 1, /**< IP checksum offload enabled */
-		.hw_vlan_filter = 0, /**< VLAN filtering disabled */
-		.jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
-		.hw_strip_crc   = 0, /**< CRC stripped by hardware */
-	},
-	.rx_adv_conf = {
-		.rss_conf = {
-			.rss_key = NULL,
-			.rss_hf = ETH_RSS_IP | ETH_RSS_UDP |
-				ETH_RSS_TCP | ETH_RSS_SCTP,
-		},
-	},
-	.txmode = {
-		.mq_mode = ETH_MQ_TX_NONE,
-	},
-};
-
-static struct rte_mempool *pktmbuf_pool[NB_SOCKETS];
-
-/***********************start of ACL part******************************/
-#ifdef DO_RFC_1812_CHECKS
-static inline int
-is_valid_ipv4_pkt(struct ipv4_hdr *pkt, uint32_t link_len);
-#endif
-static inline void
-send_single_packet(struct rte_mbuf *m, uint8_t port);
-
-#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_LEAD_CHAR		('@')
-#define ROUTE_LEAD_CHAR		('R')
-#define COMMENT_LEAD_CHAR	('#')
-#define OPTION_CONFIG		"config"
-#define OPTION_NONUMA		"no-numa"
-#define OPTION_ENBJMO		"enable-jumbo"
-#define OPTION_RULE_IPV4	"rule_ipv4"
-#define OPTION_RULE_IPV6	"rule_ipv6"
-#define OPTION_SCALAR		"scalar"
-#define ACL_DENY_SIGNATURE	0xf0000000
-#define RTE_LOGTYPE_L3FWDACL	RTE_LOGTYPE_USER3
-#define acl_log(format, ...)	RTE_LOG(ERR, L3FWDACL, format, ##__VA_ARGS__)
-#define uint32_t_to_char(ip, a, b, c, d) do {\
-		*a = (unsigned char)(ip >> 24 & 0xff);\
-		*b = (unsigned char)(ip >> 16 & 0xff);\
-		*c = (unsigned char)(ip >> 8 & 0xff);\
-		*d = (unsigned char)(ip & 0xff);\
-	} while (0)
-#define OFF_ETHHEAD	(sizeof(struct ether_hdr))
-#define OFF_IPV42PROTO (offsetof(struct ipv4_hdr, next_proto_id))
-#define OFF_IPV62PROTO (offsetof(struct 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)
-
-#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
-	unsigned long val;                                      \
-	char *end;                                              \
-	errno = 0;                                              \
-	val = strtoul((in), &end, (base));                      \
-	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
-		return -EINVAL;                               \
-	(fd) = (typeof(fd))val;                                 \
-	(in) = end + 1;                                         \
-} while (0)
-
-/*
-  * 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
-
-/*
- * 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 ipv4_hdr, src_addr) -
-			offsetof(struct 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 ipv4_hdr, dst_addr) -
-			offsetof(struct 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 ipv4_hdr) -
-			offsetof(struct 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 ipv4_hdr) -
-			offsetof(struct ipv4_hdr, next_proto_id) +
-			sizeof(uint16_t),
-	},
-};
-
-#define	IPV6_ADDR_LEN	16
-#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
-#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_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 ipv6_hdr, src_addr) -
-			offsetof(struct 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 ipv6_hdr, src_addr) -
-			offsetof(struct 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 ipv6_hdr, src_addr) -
-			offsetof(struct 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 ipv6_hdr, src_addr) -
-			offsetof(struct 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 ipv6_hdr, dst_addr)
-				- offsetof(struct 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 ipv6_hdr, dst_addr) -
-			offsetof(struct 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 ipv6_hdr, dst_addr) -
-			offsetof(struct 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 ipv6_hdr, dst_addr) -
-			offsetof(struct 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 ipv6_hdr) -
-			offsetof(struct 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 ipv6_hdr) -
-			offsetof(struct 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 {
-	char mapped[NB_SOCKETS];
-	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 struct{
-	const char *rule_ipv4_name;
-	const char *rule_ipv6_name;
-	int scalar;
-} parm_config;
-
-const char cb_port_delim[] = ":";
-
-static inline void
-print_one_ipv4_rule(struct acl4_rule *rule, int extra)
-{
-	unsigned char a, b, c, d;
-
-	uint32_t_to_char(rule->field[SRC_FIELD_IPV4].value.u32,
-			&a, &b, &c, &d);
-	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
-			rule->field[SRC_FIELD_IPV4].mask_range.u32);
-	uint32_t_to_char(rule->field[DST_FIELD_IPV4].value.u32,
-			&a, &b, &c, &d);
-	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
-			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 inline void
-print_one_ipv6_rule(struct acl6_rule *rule, int extra)
-{
-	unsigned char a, b, c, d;
-
-	uint32_t_to_char(rule->field[SRC1_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[SRC2_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[SRC3_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[SRC4_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
-			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);
-
-	uint32_t_to_char(rule->field[DST1_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[DST2_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[DST3_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[DST4_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
-			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);
-}
-
-/* Bypass comment and empty lines */
-static inline int
-is_bypass_line(char *buff)
-{
-	int i = 0;
-
-	/* comment line */
-	if (buff[0] == COMMENT_LEAD_CHAR)
-		return 1;
-	/* empty line */
-	while (buff[i] != '\0') {
-		if (!isspace(buff[i]))
-			return 0;
-		i++;
-	}
-	return 1;
-}
-
-#ifdef L3FWDACL_DEBUG
-static inline void
-dump_acl4_rule(struct rte_mbuf *m, uint32_t sig)
-{
-	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
-	unsigned char a, b, c, d;
-	struct ipv4_hdr *ipv4_hdr = rte_pktmbuf_mtod_offset(m,
-							    struct ipv4_hdr *,
-							    sizeof(struct ether_hdr));
-
-	uint32_t_to_char(rte_bswap32(ipv4_hdr->src_addr), &a, &b, &c, &d);
-	printf("Packet Src:%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
-	uint32_t_to_char(rte_bswap32(ipv4_hdr->dst_addr), &a, &b, &c, &d);
-	printf("Dst:%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
-
-	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)
-{
-	unsigned i;
-	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
-	struct ipv6_hdr *ipv6_hdr = rte_pktmbuf_mtod_offset(m,
-							    struct ipv6_hdr *,
-							    sizeof(struct ether_hdr));
-
-	printf("Packet Src");
-	for (i = 0; i < RTE_DIM(ipv6_hdr->src_addr); i += sizeof(uint16_t))
-		printf(":%.2x%.2x",
-			ipv6_hdr->src_addr[i], ipv6_hdr->src_addr[i + 1]);
-
-	printf("\nDst");
-	for (i = 0; i < RTE_DIM(ipv6_hdr->dst_addr); i += sizeof(uint16_t))
-		printf(":%.2x%.2x",
-			ipv6_hdr->dst_addr[i], ipv6_hdr->dst_addr[i + 1]);
-
-	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");
-	}
-}
-
-#ifdef DO_RFC_1812_CHECKS
-static inline void
-prepare_one_packet(struct rte_mbuf **pkts_in, struct acl_search_t *acl,
-	int index)
-{
-	struct 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 ipv4_hdr *,
-						   sizeof(struct 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
-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 */
-
-static inline void
-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 *));
-		prepare_one_packet(pkts_in, acl, i);
-	}
-
-	/* Process left packets */
-	for (; i < nb_rx; i++)
-		prepare_one_packet(pkts_in, acl, i);
-}
-
-static inline void
-send_one_packet(struct rte_mbuf *m, uint32_t res)
-{
-	if (likely((res & ACL_DENY_SIGNATURE) == 0 && res != 0)) {
-		/* forward packets */
-		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
-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 *));
-		send_one_packet(m[i], res[i]);
-	}
-
-	/* Process left packets */
-	for (; i < num; i++)
-		send_one_packet(m[i], res[i]);
-}
-
-/*
- * Parses IPV6 address, exepcts the following format:
- * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X - is a hexedecimal digit).
- */
-static int
-parse_ipv6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
-	char dlm)
-{
-	uint32_t addr[IPV6_ADDR_U16];
-
-	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
-
-	*end = in;
-
-	v[0] = (addr[0] << 16) + addr[1];
-	v[1] = (addr[2] << 16) + addr[3];
-	v[2] = (addr[4] << 16) + addr[5];
-	v[3] = (addr[6] << 16) + addr[7];
-
-	return 0;
-}
-
-static int
-parse_ipv6_net(const char *in, struct rte_acl_field field[4])
-{
-	int32_t rc;
-	const char *mp;
-	uint32_t i, m, v[4];
-	const uint32_t nbu32 = sizeof(uint32_t) * CHAR_BIT;
-
-	/* get address. */
-	rc = parse_ipv6_addr(in, &mp, v, '/');
-	if (rc != 0)
-		return rc;
-
-	/* get mask. */
-	GET_CB_FIELD(mp, m, 0, CHAR_BIT * sizeof(v), 0);
-
-	/* 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(const char *in, uint32_t *addr, uint32_t *mask_len)
-{
-	uint8_t a, b, c, d, m;
-
-	GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
-	GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
-	GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
-	GET_CB_FIELD(in, d, 0, UINT8_MAX, '/');
-	GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
-
-	addr[0] = IPv4(a, b, c, d);
-	mask_len[0] = m;
-
-	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
-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;
-
-	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 (0 == route_num)
-		rte_exit(EXIT_FAILURE, "Not find any route entries in %s!\n",
-				rule_path);
-
-	fseek(fh, 0, SEEK_SET);
-
-	acl_rules = calloc(acl_num, rule_size);
-
-	if (NULL == acl_rules)
-		rte_exit(EXIT_FAILURE, "%s: failed to malloc memory\n",
-			__func__);
-
-	route_rules = calloc(route_num, rule_size);
-
-	if (NULL == route_rules)
-		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 void
-dump_acl_config(void)
-{
-	printf("ACL option are:\n");
-	printf(OPTION_RULE_IPV4": %s\n", parm_config.rule_ipv4_name);
-	printf(OPTION_RULE_IPV6": %s\n", parm_config.rule_ipv6_name);
-	printf(OPTION_SCALAR": %d\n", parm_config.scalar);
-}
-
-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;
-}
-
-static struct rte_acl_ctx*
-setup_acl(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;
-
-	if ((context = rte_acl_create(&acl_param)) == NULL)
-		rte_exit(EXIT_FAILURE, "Failed to create ACL context\n");
-
-	if (parm_config.scalar && rte_acl_set_ctx_classify(context,
-			RTE_ACL_CLASSIFY_SCALAR) != 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;
-}
-
-static int
-app_acl_init(void)
-{
-	unsigned lcore_id;
-	unsigned int i;
-	int socketid;
-	struct rte_acl_rule *acl_base_ipv4, *route_base_ipv4,
-		*acl_base_ipv6, *route_base_ipv6;
-	unsigned int acl_num_ipv4 = 0, route_num_ipv4 = 0,
-		acl_num_ipv6 = 0, route_num_ipv6 = 0;
-
-	if (check_acl_config() != 0)
-		rte_exit(EXIT_FAILURE, "Failed to get valid ACL options\n");
-
-	dump_acl_config();
-
-	/* Load  rules from the input file */
-	if (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)
-		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
-
-	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);
-
-	if (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)
-		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
-
-	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 (!numa_on)
-		acl_config.mapped[0] = 1;
-	else {
-		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-			if (rte_lcore_is_enabled(lcore_id) == 0)
-				continue;
-
-			socketid = rte_lcore_to_socket_id(lcore_id);
-			if (socketid >= NB_SOCKETS) {
-				acl_log("Socket %d of lcore %u is out "
-					"of range %d\n",
-					socketid, lcore_id, NB_SOCKETS);
-				free(route_base_ipv4);
-				free(route_base_ipv6);
-				free(acl_base_ipv4);
-				free(acl_base_ipv6);
-				return -1;
-			}
-
-			acl_config.mapped[socketid] = 1;
-		}
-	}
-
-	for (i = 0; i < NB_SOCKETS; i++) {
-		if (acl_config.mapped[i]) {
-			acl_config.acx_ipv4[i] = setup_acl(route_base_ipv4,
-				acl_base_ipv4, route_num_ipv4, acl_num_ipv4,
-				0, i);
-
-			acl_config.acx_ipv6[i] = setup_acl(route_base_ipv6,
-				acl_base_ipv6, route_num_ipv6, acl_num_ipv6,
-				1, i);
-		}
-	}
-
-	free(route_base_ipv4);
-	free(route_base_ipv6);
-
-#ifdef L3FWDACL_DEBUG
-	acl_config.rule_ipv4 = (struct acl4_rule *)acl_base_ipv4;
-	acl_config.rule_ipv6 = (struct acl6_rule *)acl_base_ipv6;
-#else
-	free(acl_base_ipv4);
-	free(acl_base_ipv6);
-#endif
-
-	return 0;
-}
-
-/***********************end of ACL part******************************/
-
-struct lcore_conf {
-	uint16_t n_rx_queue;
-	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
-	uint16_t n_tx_port;
-	uint16_t tx_port_id[RTE_MAX_ETHPORTS];
-	uint16_t tx_queue_id[RTE_MAX_ETHPORTS];
-	struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS];
-} __rte_cache_aligned;
-
-static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
-
-/* Enqueue a single packet, and send burst if queue is filled */
-static inline void
-send_single_packet(struct rte_mbuf *m, uint8_t port)
-{
-	uint32_t lcore_id;
-	struct lcore_conf *qconf;
-
-	lcore_id = rte_lcore_id();
-
-	qconf = &lcore_conf[lcore_id];
-	rte_eth_tx_buffer(port, qconf->tx_queue_id[port],
-			qconf->tx_buffer[port], m);
-}
-
-#ifdef DO_RFC_1812_CHECKS
-static inline int
-is_valid_ipv4_pkt(struct ipv4_hdr *pkt, uint32_t link_len)
-{
-	/* From http://www.rfc-editor.org/rfc/rfc1812.txt section 5.2.2 */
-	/*
-	 * 1. The packet length reported by the Link Layer must be large
-	 * enough to hold the minimum length legal IP datagram (20 bytes).
-	 */
-	if (link_len < sizeof(struct ipv4_hdr))
-		return -1;
-
-	/* 2. The IP checksum must be correct. */
-	/* this is checked in H/W */
-
-	/*
-	 * 3. The IP version number must be 4. If the version number is not 4
-	 * then the packet may be another version of IP, such as IPng or
-	 * ST-II.
-	 */
-	if (((pkt->version_ihl) >> 4) != 4)
-		return -3;
-	/*
-	 * 4. The IP header length field must be large enough to hold the
-	 * minimum length legal IP datagram (20 bytes = 5 words).
-	 */
-	if ((pkt->version_ihl & 0xf) < 5)
-		return -4;
-
-	/*
-	 * 5. The IP total length field must be large enough to hold the IP
-	 * datagram header, whose length is specified in the IP header length
-	 * field.
-	 */
-	if (rte_cpu_to_be_16(pkt->total_length) < sizeof(struct ipv4_hdr))
-		return -5;
-
-	return 0;
-}
-#endif
-
-/* main processing loop */
-static int
-main_loop(__attribute__((unused)) void *dummy)
-{
-	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
-	unsigned lcore_id;
-	uint64_t prev_tsc, diff_tsc, cur_tsc;
-	int i, nb_rx;
-	uint8_t portid, 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=%hhu rxqueueid=%hhu\n",
-			lcore_id, portid, queueid);
-	}
-
-	while (1) {
-
-		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];
-				rte_eth_tx_buffer_flush(portid,
-						qconf->tx_queue_id[portid],
-						qconf->tx_buffer[portid]);
-			}
-			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;
-
-				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);
-
-					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);
-
-					send_packets(acl_search.m_ipv6,
-						acl_search.res_ipv6,
-						acl_search.num_ipv6);
-				}
-			}
-		}
-	}
-}
-
-static int
-check_lcore_params(void)
-{
-	uint8_t queue, lcore;
-	uint16_t i;
-	int socketid;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		queue = lcore_params[i].queue_id;
-		if (queue >= MAX_RX_QUEUE_PER_PORT) {
-			printf("invalid queue number: %hhu\n", queue);
-			return -1;
-		}
-		lcore = lcore_params[i].lcore_id;
-		if (!rte_lcore_is_enabled(lcore)) {
-			printf("error: lcore %hhu is not enabled in "
-				"lcore mask\n", lcore);
-			return -1;
-		}
-		socketid = rte_lcore_to_socket_id(lcore);
-		if (socketid != 0 && numa_on == 0) {
-			printf("warning: lcore %hhu is on socket %d "
-				"with numa off\n",
-				lcore, socketid);
-		}
-	}
-	return 0;
-}
-
-static int
-check_port_config(const unsigned nb_ports)
-{
-	unsigned portid;
-	uint16_t i;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		portid = lcore_params[i].port_id;
-
-		if ((enabled_port_mask & (1 << portid)) == 0) {
-			printf("port %u is not enabled in port mask\n", portid);
-			return -1;
-		}
-		if (portid >= nb_ports) {
-			printf("port %u is not present on the board\n", portid);
-			return -1;
-		}
-	}
-	return 0;
-}
-
-static uint8_t
-get_port_n_rx_queues(const uint8_t port)
-{
-	int queue = -1;
-	uint16_t i;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		if (lcore_params[i].port_id == port &&
-				lcore_params[i].queue_id > queue)
-			queue = lcore_params[i].queue_id;
-	}
-	return (uint8_t)(++queue);
-}
-
-static int
-init_lcore_rx_queues(void)
-{
-	uint16_t i, nb_rx_queue;
-	uint8_t lcore;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		lcore = lcore_params[i].lcore_id;
-		nb_rx_queue = lcore_conf[lcore].n_rx_queue;
-		if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
-			printf("error: too many queues (%u) for lcore: %u\n",
-				(unsigned)nb_rx_queue + 1, (unsigned)lcore);
-			return -1;
-		} else {
-			lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
-				lcore_params[i].port_id;
-			lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
-				lcore_params[i].queue_id;
-			lcore_conf[lcore].n_rx_queue++;
-		}
-	}
-	return 0;
-}
-
-/* display usage */
-static void
-print_usage(const char *prgname)
-{
-	printf("%s [EAL options] -- -p PORTMASK -P"
-		"--"OPTION_RULE_IPV4"=FILE"
-		"--"OPTION_RULE_IPV6"=FILE"
-		"  [--"OPTION_CONFIG" (port,queue,lcore)[,(port,queue,lcore]]"
-		"  [--"OPTION_ENBJMO" [--max-pkt-len PKTLEN]]\n"
-		"  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
-		"  -P : enable promiscuous mode\n"
-		"  --"OPTION_CONFIG": (port,queue,lcore): "
-		"rx queues configuration\n"
-		"  --"OPTION_NONUMA": optional, disable numa awareness\n"
-		"  --"OPTION_ENBJMO": enable jumbo frame"
-		" which max packet len is PKTLEN in decimal (64-9600)\n"
-		"  --"OPTION_RULE_IPV4"=FILE: specify the ipv4 rules entries "
-		"file. "
-		"Each rule occupy one line. "
-		"2 kinds of rules are supported. "
-		"One is ACL entry at while line leads with character '%c', "
-		"another is route entry at while line leads with "
-		"character '%c'.\n"
-		"  --"OPTION_RULE_IPV6"=FILE: specify the ipv6 rules "
-		"entries file.\n"
-		"  --"OPTION_SCALAR": Use scalar function to do lookup\n",
-		prgname, ACL_LEAD_CHAR, ROUTE_LEAD_CHAR);
-}
-
-static int
-parse_max_pkt_len(const char *pktlen)
-{
-	char *end = NULL;
-	unsigned long len;
-
-	/* parse decimal string */
-	len = strtoul(pktlen, &end, 10);
-	if ((pktlen[0] == '\0') || (end == NULL) || (*end != '\0'))
-		return -1;
-
-	if (len == 0)
-		return -1;
-
-	return len;
-}
-
-static int
-parse_portmask(const char *portmask)
-{
-	char *end = NULL;
-	unsigned long pm;
-
-	/* parse hexadecimal string */
-	pm = strtoul(portmask, &end, 16);
-	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
-		return -1;
-
-	if (pm == 0)
-		return -1;
-
-	return pm;
-}
-
-static int
-parse_config(const char *q_arg)
-{
-	char s[256];
-	const char *p, *p0 = q_arg;
-	char *end;
-	enum fieldnames {
-		FLD_PORT = 0,
-		FLD_QUEUE,
-		FLD_LCORE,
-		_NUM_FLD
-	};
-	unsigned long int_fld[_NUM_FLD];
-	char *str_fld[_NUM_FLD];
-	int i;
-	unsigned size;
-
-	nb_lcore_params = 0;
-
-	while ((p = strchr(p0, '(')) != NULL) {
-		++p;
-		if ((p0 = strchr(p, ')')) == NULL)
-			return -1;
-
-		size = p0 - p;
-		if (size >= sizeof(s))
-			return -1;
-
-		snprintf(s, sizeof(s), "%.*s", size, p);
-		if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') !=
-				_NUM_FLD)
-			return -1;
-		for (i = 0; i < _NUM_FLD; i++) {
-			errno = 0;
-			int_fld[i] = strtoul(str_fld[i], &end, 0);
-			if (errno != 0 || end == str_fld[i] || int_fld[i] > 255)
-				return -1;
-		}
-		if (nb_lcore_params >= MAX_LCORE_PARAMS) {
-			printf("exceeded max number of lcore params: %hu\n",
-				nb_lcore_params);
-			return -1;
-		}
-		lcore_params_array[nb_lcore_params].port_id =
-			(uint8_t)int_fld[FLD_PORT];
-		lcore_params_array[nb_lcore_params].queue_id =
-			(uint8_t)int_fld[FLD_QUEUE];
-		lcore_params_array[nb_lcore_params].lcore_id =
-			(uint8_t)int_fld[FLD_LCORE];
-		++nb_lcore_params;
-	}
-	lcore_params = lcore_params_array;
-	return 0;
-}
-
-/* Parse the argument given in the command line of the application */
-static int
-parse_args(int argc, char **argv)
-{
-	int opt, ret;
-	char **argvopt;
-	int option_index;
-	char *prgname = argv[0];
-	static struct option lgopts[] = {
-		{OPTION_CONFIG, 1, 0, 0},
-		{OPTION_NONUMA, 0, 0, 0},
-		{OPTION_ENBJMO, 0, 0, 0},
-		{OPTION_RULE_IPV4, 1, 0, 0},
-		{OPTION_RULE_IPV6, 1, 0, 0},
-		{OPTION_SCALAR, 0, 0, 0},
-		{NULL, 0, 0, 0}
-	};
-
-	argvopt = argv;
-
-	while ((opt = getopt_long(argc, argvopt, "p:P",
-				lgopts, &option_index)) != EOF) {
-
-		switch (opt) {
-		/* portmask */
-		case 'p':
-			enabled_port_mask = parse_portmask(optarg);
-			if (enabled_port_mask == 0) {
-				printf("invalid portmask\n");
-				print_usage(prgname);
-				return -1;
-			}
-			break;
-		case 'P':
-			printf("Promiscuous mode selected\n");
-			promiscuous_on = 1;
-			break;
-
-		/* long options */
-		case 0:
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_CONFIG,
-					sizeof(OPTION_CONFIG))) {
-				ret = parse_config(optarg);
-				if (ret) {
-					printf("invalid config\n");
-					print_usage(prgname);
-					return -1;
-				}
-			}
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_NONUMA,
-					sizeof(OPTION_NONUMA))) {
-				printf("numa is disabled\n");
-				numa_on = 0;
-			}
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_ENBJMO, sizeof(OPTION_ENBJMO))) {
-				struct option lenopts = {
-					"max-pkt-len",
-					required_argument,
-					0,
-					0
-				};
-
-				printf("jumbo frame is enabled\n");
-				port_conf.rxmode.jumbo_frame = 1;
-
-				/*
-				 * if no max-pkt-len set, then use the
-				 * default value ETHER_MAX_LEN
-				 */
-				if (0 == getopt_long(argc, argvopt, "",
-						&lenopts, &option_index)) {
-					ret = parse_max_pkt_len(optarg);
-					if ((ret < 64) ||
-						(ret > MAX_JUMBO_PKT_LEN)) {
-						printf("invalid packet "
-							"length\n");
-						print_usage(prgname);
-						return -1;
-					}
-					port_conf.rxmode.max_rx_pkt_len = ret;
-				}
-				printf("set jumbo frame max packet length "
-					"to %u\n",
-					(unsigned int)
-					port_conf.rxmode.max_rx_pkt_len);
-			}
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_RULE_IPV4,
-					sizeof(OPTION_RULE_IPV4)))
-				parm_config.rule_ipv4_name = optarg;
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_RULE_IPV6,
-					sizeof(OPTION_RULE_IPV6))) {
-				parm_config.rule_ipv6_name = optarg;
-			}
-
-			if (!strncmp(lgopts[option_index].name,
-					OPTION_SCALAR, sizeof(OPTION_SCALAR)))
-				parm_config.scalar = 1;
-
-
-			break;
-
-		default:
-			print_usage(prgname);
-			return -1;
-		}
-	}
-
-	if (optind >= 0)
-		argv[optind-1] = prgname;
-
-	ret = optind-1;
-	optind = 1; /* reset getopt lib */
-	return ret;
-}
-
-static void
-print_ethaddr(const char *name, const struct ether_addr *eth_addr)
-{
-	char buf[ETHER_ADDR_FMT_SIZE];
-	ether_format_addr(buf, ETHER_ADDR_FMT_SIZE, eth_addr);
-	printf("%s%s", name, buf);
-}
-
-static int
-init_mem(unsigned nb_mbuf)
-{
-	int socketid;
-	unsigned lcore_id;
-	char s[64];
-
-	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-		if (rte_lcore_is_enabled(lcore_id) == 0)
-			continue;
-
-		if (numa_on)
-			socketid = rte_lcore_to_socket_id(lcore_id);
-		else
-			socketid = 0;
-
-		if (socketid >= NB_SOCKETS) {
-			rte_exit(EXIT_FAILURE,
-				"Socket %d of lcore %u is out of range %d\n",
-				socketid, lcore_id, NB_SOCKETS);
-		}
-		if (pktmbuf_pool[socketid] == NULL) {
-			snprintf(s, sizeof(s), "mbuf_pool_%d", socketid);
-			pktmbuf_pool[socketid] =
-				rte_pktmbuf_pool_create(s, nb_mbuf,
-					MEMPOOL_CACHE_SIZE, 0,
-					RTE_MBUF_DEFAULT_BUF_SIZE,
-					socketid);
-			if (pktmbuf_pool[socketid] == NULL)
-				rte_exit(EXIT_FAILURE,
-					"Cannot init mbuf pool on socket %d\n",
-					socketid);
-			else
-				printf("Allocated mbuf pool on socket %d\n",
-					socketid);
-		}
-	}
-	return 0;
-}
-
-/* Check the link status of all ports in up to 9s, and print them finally */
-static void
-check_all_ports_link_status(uint8_t port_num, uint32_t port_mask)
-{
-#define CHECK_INTERVAL 100 /* 100ms */
-#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
-	uint8_t portid, count, all_ports_up, print_flag = 0;
-	struct rte_eth_link link;
-
-	printf("\nChecking link status");
-	fflush(stdout);
-	for (count = 0; count <= MAX_CHECK_TIME; count++) {
-		all_ports_up = 1;
-		for (portid = 0; portid < port_num; portid++) {
-			if ((port_mask & (1 << portid)) == 0)
-				continue;
-			memset(&link, 0, sizeof(link));
-			rte_eth_link_get_nowait(portid, &link);
-			/* print link status if flag set */
-			if (print_flag == 1) {
-				if (link.link_status)
-					printf("Port %d Link Up - speed %u "
-						"Mbps - %s\n", (uint8_t)portid,
-						(unsigned)link.link_speed,
-				(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
-					("full-duplex") : ("half-duplex\n"));
-				else
-					printf("Port %d Link Down\n",
-						(uint8_t)portid);
-				continue;
-			}
-			/* clear all_ports_up flag if any link down */
-			if (link.link_status == ETH_LINK_DOWN) {
-				all_ports_up = 0;
-				break;
-			}
-		}
-		/* after finally printing all link status, get out */
-		if (print_flag == 1)
-			break;
-
-		if (all_ports_up == 0) {
-			printf(".");
-			fflush(stdout);
-			rte_delay_ms(CHECK_INTERVAL);
-		}
-
-		/* set the print_flag if all ports up or timeout */
-		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
-			print_flag = 1;
-			printf("done\n");
-		}
-	}
-}
-
-int
-main(int argc, char **argv)
-{
-	struct lcore_conf *qconf;
-	struct rte_eth_dev_info dev_info;
-	struct rte_eth_txconf *txconf;
-	int ret;
-	unsigned nb_ports;
-	uint16_t queueid;
-	unsigned lcore_id;
-	uint32_t n_tx_queue, nb_lcores;
-	uint8_t portid, nb_rx_queue, queue, socketid;
-
-	/* init EAL */
-	ret = rte_eal_init(argc, argv);
-	if (ret < 0)
-		rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
-	argc -= ret;
-	argv += ret;
-
-	/* parse application arguments (after the EAL ones) */
-	ret = parse_args(argc, argv);
-	if (ret < 0)
-		rte_exit(EXIT_FAILURE, "Invalid L3FWD parameters\n");
-
-	if (check_lcore_params() < 0)
-		rte_exit(EXIT_FAILURE, "check_lcore_params failed\n");
-
-	ret = init_lcore_rx_queues();
-	if (ret < 0)
-		rte_exit(EXIT_FAILURE, "init_lcore_rx_queues failed\n");
-
-	nb_ports = rte_eth_dev_count();
-
-	if (check_port_config(nb_ports) < 0)
-		rte_exit(EXIT_FAILURE, "check_port_config failed\n");
-
-	/* Add ACL rules and route entries, build trie */
-	if (app_acl_init() < 0)
-		rte_exit(EXIT_FAILURE, "app_acl_init failed\n");
-
-	nb_lcores = rte_lcore_count();
-
-	/* initialize all ports */
-	for (portid = 0; portid < nb_ports; portid++) {
-		/* skip ports that are not enabled */
-		if ((enabled_port_mask & (1 << portid)) == 0) {
-			printf("\nSkipping disabled port %d\n", portid);
-			continue;
-		}
-
-		/* init port */
-		printf("Initializing port %d ... ", portid);
-		fflush(stdout);
-
-		nb_rx_queue = get_port_n_rx_queues(portid);
-		n_tx_queue = nb_lcores;
-		if (n_tx_queue > MAX_TX_QUEUE_PER_PORT)
-			n_tx_queue = MAX_TX_QUEUE_PER_PORT;
-		printf("Creating queues: nb_rxq=%d nb_txq=%u... ",
-			nb_rx_queue, (unsigned)n_tx_queue);
-		ret = rte_eth_dev_configure(portid, nb_rx_queue,
-					(uint16_t)n_tx_queue, &port_conf);
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE,
-				"Cannot configure device: err=%d, port=%d\n",
-				ret, portid);
-
-		rte_eth_macaddr_get(portid, &ports_eth_addr[portid]);
-		print_ethaddr(" Address:", &ports_eth_addr[portid]);
-		printf(", ");
-
-		/* init memory */
-		ret = init_mem(NB_MBUF);
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "init_mem failed\n");
-
-		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-			if (rte_lcore_is_enabled(lcore_id) == 0)
-				continue;
-
-			/* Initialize TX buffers */
-			qconf = &lcore_conf[lcore_id];
-			qconf->tx_buffer[portid] = rte_zmalloc_socket("tx_buffer",
-					RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0,
-					rte_eth_dev_socket_id(portid));
-			if (qconf->tx_buffer[portid] == NULL)
-				rte_exit(EXIT_FAILURE, "Can't allocate tx buffer for port %u\n",
-						(unsigned) portid);
-
-			rte_eth_tx_buffer_init(qconf->tx_buffer[portid], MAX_PKT_BURST);
-		}
-
-		/* init one TX queue per couple (lcore,port) */
-		queueid = 0;
-		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-			if (rte_lcore_is_enabled(lcore_id) == 0)
-				continue;
-
-			if (numa_on)
-				socketid = (uint8_t)
-					rte_lcore_to_socket_id(lcore_id);
-			else
-				socketid = 0;
-
-			printf("txq=%u,%d,%d ", lcore_id, queueid, socketid);
-			fflush(stdout);
-
-			rte_eth_dev_info_get(portid, &dev_info);
-			txconf = &dev_info.default_txconf;
-			if (port_conf.rxmode.jumbo_frame)
-				txconf->txq_flags = 0;
-			ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
-						     socketid, txconf);
-			if (ret < 0)
-				rte_exit(EXIT_FAILURE,
-					"rte_eth_tx_queue_setup: err=%d, "
-					"port=%d\n", ret, portid);
-
-			qconf = &lcore_conf[lcore_id];
-			qconf->tx_queue_id[portid] = queueid;
-			queueid++;
-
-			qconf->tx_port_id[qconf->n_tx_port] = portid;
-			qconf->n_tx_port++;
-		}
-		printf("\n");
-	}
-
-	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-		if (rte_lcore_is_enabled(lcore_id) == 0)
-			continue;
-		qconf = &lcore_conf[lcore_id];
-		printf("\nInitializing rx queues on lcore %u ... ", lcore_id);
-		fflush(stdout);
-		/* init RX queues */
-		for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
-			portid = qconf->rx_queue_list[queue].port_id;
-			queueid = qconf->rx_queue_list[queue].queue_id;
-
-			if (numa_on)
-				socketid = (uint8_t)
-					rte_lcore_to_socket_id(lcore_id);
-			else
-				socketid = 0;
-
-			printf("rxq=%d,%d,%d ", portid, queueid, socketid);
-			fflush(stdout);
-
-			ret = rte_eth_rx_queue_setup(portid, queueid, nb_rxd,
-					socketid, NULL,
-					pktmbuf_pool[socketid]);
-			if (ret < 0)
-				rte_exit(EXIT_FAILURE,
-					"rte_eth_rx_queue_setup: err=%d,"
-					"port=%d\n", ret, portid);
-		}
-	}
-
-	printf("\n");
-
-	/* start ports */
-	for (portid = 0; portid < nb_ports; portid++) {
-		if ((enabled_port_mask & (1 << portid)) == 0)
-			continue;
-
-		/* Start device */
-		ret = rte_eth_dev_start(portid);
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE,
-				"rte_eth_dev_start: err=%d, port=%d\n",
-				ret, portid);
-
-		/*
-		 * If enabled, put device in promiscuous mode.
-		 * This allows IO forwarding mode to forward packets
-		 * to itself through 2 cross-connected  ports of the
-		 * target machine.
-		 */
-		if (promiscuous_on)
-			rte_eth_promiscuous_enable(portid);
-	}
-
-	check_all_ports_link_status((uint8_t)nb_ports, enabled_port_mask);
-
-	/* launch per-lcore init on every lcore */
-	rte_eal_mp_remote_launch(main_loop, NULL, CALL_MASTER);
-	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
-		if (rte_eal_wait_lcore(lcore_id) < 0)
-			return -1;
-	}
-
-	return 0;
-}
diff --git a/examples/l3fwd/Makefile b/examples/l3fwd/Makefile
index 5ce0ce0..151dbe3 100644
--- a/examples/l3fwd/Makefile
+++ b/examples/l3fwd/Makefile
@@ -42,7 +42,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
 APP = l3fwd
 
 # all source are stored in SRCS-y
-SRCS-y := main.c l3fwd_lpm.c l3fwd_em.c
+SRCS-y := main.c l3fwd_lpm.c l3fwd_em.c l3fwd_acl.c
 
 CFLAGS += -I$(SRCDIR)
 CFLAGS += -O3 $(USER_FLAGS)
diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 011ba14..93e08f6 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -77,6 +77,24 @@
 #endif
 #define HASH_ENTRY_NUMBER_DEFAULT	4
 
+/* Config options */
+#define OPTION_CONFIG           "config"
+#define OPTION_NONUMA           "no-numa"
+#define OPTION_ENBJMO           "enable-jumbo"
+#define OPTION_RULE_IPV4        "rule_ipv4"
+#define OPTION_RULE_IPV6        "rule_ipv6"
+#define OPTION_SCALAR           "scalar"
+#define OPTION_ETH_DEST         "eth-dest"
+#define OPTION_IPV6		"ipv6"
+#define OPTION_HASH_ENTRY_NUM	"hash-entry-num"
+#define OPTION_PARSE_PTYPE	"parse-ptype"
+#define OPTION_MAX_PKT_LEN	"max-pkt-len"
+
+/*Log file related character defs. */
+#define ACL_LEAD_CHAR		('@')
+#define ROUTE_LEAD_CHAR		('R')
+#define COMMENT_LEAD_CHAR	('#')
+
 struct mbuf_table {
 	uint16_t len;
 	struct rte_mbuf *m_table[MAX_PKT_BURST];
@@ -94,6 +112,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;
@@ -115,6 +134,8 @@ extern xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+extern int numa_on; /**< NUMA is enabled by default. */
+
 /* Send burst of packets on an output interface */
 static inline int
 send_burst(struct lcore_conf *qconf, uint16_t n, uint8_t port)
@@ -205,12 +226,18 @@ setup_lpm(const int socketid);
 void
 setup_hash(const int socketid);
 
+void
+setup_acl(const int socketid);
+
 int
 em_check_ptype(int portid);
 
 int
 lpm_check_ptype(int portid);
 
+int
+acl_check_ptype(int portid);
+
 uint16_t
 em_cb_parse_ptype(uint8_t port, uint16_t queue, struct rte_mbuf *pkts[],
 		  uint16_t nb_pkts, uint16_t max_pkts, void *user_param);
@@ -219,12 +246,19 @@ uint16_t
 lpm_cb_parse_ptype(uint8_t port, uint16_t queue, struct rte_mbuf *pkts[],
 		   uint16_t nb_pkts, uint16_t max_pkts, void *user_param);
 
+uint16_t
+acl_cb_parse_ptype(uint8_t port, uint16_t queue, struct rte_mbuf *pkts[],
+		  uint16_t nb_pkts, uint16_t max_pkts, void *user_param);
+
 int
 em_main_loop(__attribute__((unused)) void *dummy);
 
 int
 lpm_main_loop(__attribute__((unused)) void *dummy);
 
+int
+acl_main_loop(__attribute__((unused)) void *dummy);
+
 /* Return ipv4/ipv6 fwd lookup struct for LPM or EM. */
 void *
 em_get_ipv4_l3fwd_lookup_struct(const int socketid);
@@ -238,4 +272,19 @@ lpm_get_ipv4_l3fwd_lookup_struct(const int socketid);
 void *
 lpm_get_ipv6_l3fwd_lookup_struct(const int socketid);
 
+void *
+acl_get_ipv4_l3fwd_lookup_struct(const int socketid);
+
+void *
+acl_get_ipv6_l3fwd_lookup_struct(const int socketid);
+
+void
+l3fwd_acl_set_scalar(void);
+
+void
+l3fwd_acl_set_rule_ipv6_name(const char *optarg);
+
+void
+l3fwd_acl_set_rule_ipv4_name(const char *optarg);
+
 #endif  /* __L3_FWD_H__ */
diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
new file mode 100644
index 0000000..388b978
--- /dev/null
+++ b/examples/l3fwd/l3fwd_acl.c
@@ -0,0 +1,1064 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include <rte_common.h>
+#include <rte_byteorder.h>
+#include <rte_log.h>
+#include <rte_memory.h>
+#include <rte_memcpy.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_per_lcore.h>
+#include <rte_launch.h>
+#include <rte_atomic.h>
+#include <rte_cycles.h>
+#include <rte_prefetch.h>
+#include <rte_lcore.h>
+#include <rte_per_lcore.h>
+#include <rte_branch_prediction.h>
+#include <rte_interrupts.h>
+#include <rte_pci.h>
+#include <rte_random.h>
+#include <rte_debug.h>
+#include <rte_ether.h>
+#include <rte_ethdev.h>
+#include <rte_ring.h>
+#include <rte_mempool.h>
+#include <rte_mbuf.h>
+#include <rte_ip.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+#include <rte_string_fns.h>
+#include <rte_acl.h>
+
+#include "l3fwd.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 ipv4_hdr, src_addr) -
+			offsetof(struct 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 ipv4_hdr, dst_addr) -
+			offsetof(struct 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 ipv4_hdr) -
+			offsetof(struct 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 ipv4_hdr) -
+			offsetof(struct ipv4_hdr, next_proto_id) +
+			sizeof(uint16_t),
+	},
+};
+
+#define	IPV6_ADDR_LEN	16
+#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
+#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_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 ipv6_hdr, src_addr) -
+			offsetof(struct 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 ipv6_hdr, src_addr) -
+			offsetof(struct 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 ipv6_hdr, src_addr) -
+			offsetof(struct 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 ipv6_hdr, src_addr) -
+			offsetof(struct 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 ipv6_hdr, dst_addr)
+				- offsetof(struct 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 ipv6_hdr, dst_addr) -
+			offsetof(struct 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 ipv6_hdr, dst_addr) -
+			offsetof(struct 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 ipv6_hdr, dst_addr) -
+			offsetof(struct 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 ipv6_hdr) -
+			offsetof(struct 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 ipv6_hdr) -
+			offsetof(struct 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 {
+	char mapped[NB_SOCKETS];
+	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;
+
+const char cb_port_delim[] = ":";
+
+static struct {
+	const char *rule_ipv4_name;
+	const char *rule_ipv6_name;
+	int scalar;
+} parm_config;
+
+/*
+ * Print and dump ACL/Route rules functions are defined in
+ * following header file.
+ */
+#include "l3fwd_acl.h"
+
+/*
+ * Data path related inline functions are defined in
+ * following header file.
+ */
+#include "l3fwd_acl_scalar.h"
+
+/*
+ * API's called during initialization to setup ACL rules.
+ */
+void
+l3fwd_acl_set_rule_ipv4_name(const char *optarg)
+{
+	parm_config.rule_ipv4_name = optarg;
+}
+
+void
+l3fwd_acl_set_rule_ipv6_name(const char *optarg)
+{
+	parm_config.rule_ipv6_name = optarg;
+}
+
+void
+l3fwd_acl_set_scalar(void)
+{
+	parm_config.scalar = 1;
+}
+
+/*
+ * Parses IPV6 address, exepcts the following format:
+ * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X - is a hexedecimal digit).
+ */
+static int
+parse_ipv6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
+	char dlm)
+{
+	uint32_t addr[IPV6_ADDR_U16];
+
+	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
+
+	*end = in;
+
+	v[0] = (addr[0] << 16) + addr[1];
+	v[1] = (addr[2] << 16) + addr[3];
+	v[2] = (addr[4] << 16) + addr[5];
+	v[3] = (addr[6] << 16) + addr[7];
+
+	return 0;
+}
+
+static int
+parse_ipv6_net(const char *in, struct rte_acl_field field[4])
+{
+	int32_t rc;
+	const char *mp;
+	uint32_t i, m, v[4];
+	const uint32_t nbu32 = sizeof(uint32_t) * CHAR_BIT;
+
+	/* get address. */
+	rc = parse_ipv6_addr(in, &mp, v, '/');
+	if (rc != 0)
+		return rc;
+
+	/* get mask. */
+	GET_CB_FIELD(mp, m, 0, CHAR_BIT * sizeof(v), 0);
+
+	/* 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(const char *in, uint32_t *addr, uint32_t *mask_len)
+{
+	uint8_t a, b, c, d, m;
+
+	GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, d, 0, UINT8_MAX, '/');
+	GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
+
+	addr[0] = IPv4(a, b, c, d);
+	mask_len[0] = m;
+
+	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
+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;
+
+	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);
+
+	fseek(fh, 0, SEEK_SET);
+
+	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 void
+dump_acl_config(void)
+{
+	printf("ACL option are:\n");
+	printf(OPTION_RULE_IPV4": %s\n", parm_config.rule_ipv4_name);
+	printf(OPTION_RULE_IPV6": %s\n", parm_config.rule_ipv6_name);
+	printf(OPTION_SCALAR": %d\n", parm_config.scalar);
+}
+
+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;
+}
+
+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.scalar && rte_acl_set_ctx_classify(context,
+			RTE_ACL_CLASSIFY_SCALAR) != 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;
+}
+
+void
+setup_acl(const int socket_id __attribute__((unused)))
+{
+	unsigned int lcore_id;
+	unsigned int i;
+	int socketid;
+	struct rte_acl_rule *acl_base_ipv4, *route_base_ipv4,
+		*acl_base_ipv6, *route_base_ipv6;
+	unsigned int acl_num_ipv4 = 0, route_num_ipv4 = 0,
+		acl_num_ipv6 = 0, route_num_ipv6 = 0;
+
+	printf("\nsocket %d\n", socket_id);
+
+	if (check_acl_config() != 0)
+		rte_exit(EXIT_FAILURE, "Failed to get valid ACL options\n");
+
+	dump_acl_config();
+
+	/* Load  rules from the input file */
+	if (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)
+		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
+
+	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);
+
+	if (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)
+		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
+
+	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 (!numa_on)
+		acl_config.mapped[0] = 1;
+	else {
+		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+			if (rte_lcore_is_enabled(lcore_id) == 0)
+				continue;
+
+			socketid = rte_lcore_to_socket_id(lcore_id);
+			if (socketid >= NB_SOCKETS) {
+				acl_log("Socket %d of lcore %u is out "
+					"of range %d\n",
+					socketid, lcore_id, NB_SOCKETS);
+				free(route_base_ipv4);
+				free(route_base_ipv6);
+				free(acl_base_ipv4);
+				free(acl_base_ipv6);
+				return;
+			}
+
+			acl_config.mapped[socketid] = 1;
+		}
+	}
+
+	for (i = 0; i < NB_SOCKETS; i++) {
+		if (acl_config.mapped[i]) {
+			acl_config.acx_ipv4[i] = app_acl_init(route_base_ipv4,
+				acl_base_ipv4, route_num_ipv4, acl_num_ipv4,
+				0, i);
+
+			acl_config.acx_ipv6[i] = app_acl_init(route_base_ipv6,
+				acl_base_ipv6, route_num_ipv6, acl_num_ipv6,
+				1, i);
+		}
+	}
+
+	free(route_base_ipv4);
+	free(route_base_ipv6);
+
+#ifdef L3FWDACL_DEBUG
+	acl_config.rule_ipv4 = (struct acl4_rule *)acl_base_ipv4;
+	acl_config.rule_ipv6 = (struct acl6_rule *)acl_base_ipv6;
+#else
+	free(acl_base_ipv4);
+	free(acl_base_ipv6);
+#endif
+
+}
+
+/* main processing loop */
+int
+acl_main_loop(__attribute__((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;
+	uint8_t portid, 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=%hhu 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];
+				rte_eth_tx_buffer_flush(portid,
+						qconf->tx_queue_id[portid],
+						qconf->tx_buffer[portid]);
+			}
+			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;
+}
+
+static inline void
+acl_parse_ptype(struct rte_mbuf *m)
+{
+	struct ether_hdr *eth_hdr;
+	uint32_t packet_type = RTE_PTYPE_UNKNOWN;
+	uint16_t ether_type;
+	void *l3;
+	int hdr_len;
+	struct ipv4_hdr *ipv4_hdr;
+	struct ipv6_hdr *ipv6_hdr;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	ether_type = eth_hdr->ether_type;
+	l3 = (uint8_t *)eth_hdr + sizeof(struct ether_hdr);
+	if (ether_type == rte_cpu_to_be_16(ETHER_TYPE_IPv4)) {
+		ipv4_hdr = (struct ipv4_hdr *)l3;
+		hdr_len = (ipv4_hdr->version_ihl & IPV4_HDR_IHL_MASK) *
+			  IPV4_IHL_MULTIPLIER;
+		if (hdr_len == sizeof(struct ipv4_hdr)) {
+			packet_type |= RTE_PTYPE_L3_IPV4;
+			if (ipv4_hdr->next_proto_id == IPPROTO_TCP)
+				packet_type |= RTE_PTYPE_L4_TCP;
+			else if (ipv4_hdr->next_proto_id == IPPROTO_UDP)
+				packet_type |= RTE_PTYPE_L4_UDP;
+		} else
+			packet_type |= RTE_PTYPE_L3_IPV4_EXT;
+	} else if (ether_type == rte_cpu_to_be_16(ETHER_TYPE_IPv4)) {
+		ipv6_hdr = (struct ipv6_hdr *)l3;
+		if (ipv6_hdr->proto == IPPROTO_TCP)
+			packet_type |= RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP;
+		else if (ipv6_hdr->proto == IPPROTO_UDP)
+			packet_type |= RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_UDP;
+		else
+			packet_type |= RTE_PTYPE_L3_IPV6_EXT_UNKNOWN;
+	}
+
+	m->packet_type = packet_type;
+}
+
+uint16_t
+acl_cb_parse_ptype(uint8_t port __rte_unused, uint16_t queue __rte_unused,
+		  struct rte_mbuf *pkts[], uint16_t nb_pkts,
+		  uint16_t max_pkts __rte_unused,
+		  void *user_param __rte_unused)
+{
+	unsigned int i;
+
+	for (i = 0; i < nb_pkts; ++i)
+		acl_parse_ptype(pkts[i]);
+
+	return nb_pkts;
+}
+
+/* Requirements:
+ * 1. IP packets without extension;
+ * 2. L4 payload should be either TCP or UDP.
+ */
+int
+acl_check_ptype(int portid)
+{
+	int i, ret;
+	int ptype_l3_ipv4_ext = 0;
+	int ptype_l3_ipv6_ext = 0;
+	int ptype_l4_tcp = 0;
+	int ptype_l4_udp = 0;
+	uint32_t ptype_mask = RTE_PTYPE_L3_MASK | RTE_PTYPE_L4_MASK;
+
+	ret = rte_eth_dev_get_supported_ptypes(portid, ptype_mask, NULL, 0);
+	if (ret <= 0)
+		return 0;
+
+	uint32_t ptypes[ret];
+
+	ret = rte_eth_dev_get_supported_ptypes(portid, ptype_mask, ptypes, ret);
+	for (i = 0; i < ret; ++i) {
+		switch (ptypes[i]) {
+		case RTE_PTYPE_L3_IPV4_EXT:
+			ptype_l3_ipv4_ext = 1;
+			break;
+		case RTE_PTYPE_L3_IPV6_EXT:
+			ptype_l3_ipv6_ext = 1;
+			break;
+		case RTE_PTYPE_L4_TCP:
+			ptype_l4_tcp = 1;
+			break;
+		case RTE_PTYPE_L4_UDP:
+			ptype_l4_udp = 1;
+			break;
+		}
+	}
+
+	if (ptype_l3_ipv4_ext == 0)
+		printf("port %d cannot parse RTE_PTYPE_L3_IPV4_EXT\n", portid);
+	if (ptype_l3_ipv6_ext == 0)
+		printf("port %d cannot parse RTE_PTYPE_L3_IPV6_EXT\n", portid);
+	if (!ptype_l3_ipv4_ext || !ptype_l3_ipv6_ext)
+		return 0;
+
+	if (ptype_l4_tcp == 0)
+		printf("port %d cannot parse RTE_PTYPE_L4_TCP\n", portid);
+	if (ptype_l4_udp == 0)
+		printf("port %d cannot parse RTE_PTYPE_L4_UDP\n", portid);
+	if (ptype_l4_tcp && ptype_l4_udp)
+		return 1;
+
+	return 0;
+}
+
+/* Not used by L3fwd ACL. */
+void *
+acl_get_ipv4_l3fwd_lookup_struct(__attribute__((unused)) const int socketid)
+{
+	return NULL;
+}
+
+void *
+acl_get_ipv6_l3fwd_lookup_struct(__attribute__((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 0000000..b79784b
--- /dev/null
+++ b/examples/l3fwd/l3fwd_acl.h
@@ -0,0 +1,263 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __L3FWD_ACL_H__
+#define __L3FWD_ACL_H__
+
+#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 uint32_t_to_char(ip, a, b, c, d) do {\
+		*a = (unsigned char)(ip >> 24 & 0xff);\
+		*b = (unsigned char)(ip >> 16 & 0xff);\
+		*c = (unsigned char)(ip >> 8 & 0xff);\
+		*d = (unsigned char)(ip & 0xff);\
+	} while (0)
+#define OFF_ETHHEAD	(sizeof(struct ether_hdr))
+#define OFF_IPV42PROTO (offsetof(struct ipv4_hdr, next_proto_id))
+#define OFF_IPV62PROTO (offsetof(struct 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)
+
+#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
+	unsigned long val;                                      \
+	char *end;                                              \
+	errno = 0;                                              \
+	val = strtoul((in), &end, (base));                      \
+	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
+		return -EINVAL;                               \
+	(fd) = (typeof(fd))val;                                 \
+	(in) = end + 1;                                         \
+} while (0)
+
+/*
+ * 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 inline void
+print_one_ipv4_rule(struct acl4_rule *rule, int extra)
+{
+	unsigned char a, b, c, d;
+
+	uint32_t_to_char(rule->field[SRC_FIELD_IPV4].value.u32,
+			&a, &b, &c, &d);
+	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
+			rule->field[SRC_FIELD_IPV4].mask_range.u32);
+	uint32_t_to_char(rule->field[DST_FIELD_IPV4].value.u32,
+			&a, &b, &c, &d);
+	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
+			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 inline void
+print_one_ipv6_rule(struct acl6_rule *rule, int extra)
+{
+	unsigned char a, b, c, d;
+
+	uint32_t_to_char(rule->field[SRC1_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[SRC2_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[SRC3_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[SRC4_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
+			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);
+
+	uint32_t_to_char(rule->field[DST1_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[DST2_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[DST3_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
+	uint32_t_to_char(rule->field[DST4_FIELD_IPV6].value.u32,
+		&a, &b, &c, &d);
+	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
+			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);
+}
+
+/* Bypass comment and empty lines */
+static inline int
+is_bypass_line(char *buff)
+{
+	int i = 0;
+
+	/* comment line */
+	if (buff[0] == COMMENT_LEAD_CHAR)
+		return 1;
+	/* empty line */
+	while (buff[i] != '\0') {
+		if (!isspace(buff[i]))
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
+#ifdef L3FWDACL_DEBUG
+static inline void
+dump_acl4_rule(struct rte_mbuf *m, uint32_t sig)
+{
+	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
+	unsigned char a, b, c, d;
+	struct ipv4_hdr *ipv4_hdr = rte_pktmbuf_mtod_offset(m,
+					    struct ipv4_hdr *,
+					    sizeof(struct ether_hdr));
+
+	uint32_t_to_char(rte_bswap32(ipv4_hdr->src_addr), &a, &b, &c, &d);
+	printf("Packet Src:%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
+	uint32_t_to_char(rte_bswap32(ipv4_hdr->dst_addr), &a, &b, &c, &d);
+	printf("Dst:%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
+
+	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)
+{
+	unsigned int i;
+	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
+	struct ipv6_hdr *ipv6_hdr = rte_pktmbuf_mtod_offset(m,
+					    struct ipv6_hdr *,
+					    sizeof(struct ether_hdr));
+
+	printf("Packet Src");
+	for (i = 0; i < RTE_DIM(ipv6_hdr->src_addr); i += sizeof(uint16_t))
+		printf(":%.2x%.2x",
+			ipv6_hdr->src_addr[i], ipv6_hdr->src_addr[i + 1]);
+
+	printf("\nDst");
+	for (i = 0; i < RTE_DIM(ipv6_hdr->dst_addr); i += sizeof(uint16_t))
+		printf(":%.2x%.2x",
+			ipv6_hdr->dst_addr[i], ipv6_hdr->dst_addr[i + 1]);
+
+	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");
+	}
+}
+
+#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 0000000..2ac02f5
--- /dev/null
+++ b/examples/l3fwd/l3fwd_acl_scalar.h
@@ -0,0 +1,182 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __L3FWD_ACL_SCALAR_H__
+#define __L3FWD_ACL_SCALAR_H__
+
+#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 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 ipv4_hdr *,
+						   sizeof(struct 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 */
+
+/* Enqueue a single packet, and send burst if queue is filled */
+static inline void
+l3fwd_acl_send_single_packet(struct rte_mbuf *m, uint8_t port)
+{
+	uint32_t lcore_id;
+	struct lcore_conf *qconf;
+
+	lcore_id = rte_lcore_id();
+	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_em.c b/examples/l3fwd/l3fwd_em.c
index 9cc4460..6fdabf7 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -754,7 +754,7 @@ setup_hash(const int socketid)
 		rte_hash_create(&ipv4_l3fwd_hash_params);
 	if (ipv4_l3fwd_em_lookup_struct[socketid] == NULL)
 		rte_exit(EXIT_FAILURE,
-			"Unable to create the l3fwd hash on socket %d\n",
+			"Unable to create the v4 hash on socket %d\n",
 			socketid);
 
 	/* create ipv6 hash */
@@ -765,13 +765,15 @@ setup_hash(const int socketid)
 		rte_hash_create(&ipv6_l3fwd_hash_params);
 	if (ipv6_l3fwd_em_lookup_struct[socketid] == NULL)
 		rte_exit(EXIT_FAILURE,
-			"Unable to create the l3fwd hash on socket %d\n",
+			"Unable to create the v6 hash on socket %d\n",
 			socketid);
 
 	if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-		/* For testing hash matching with a large number of flows we
-		 * generate millions of IP 5-tuples with an incremented dst
-		 * address to initialize the hash table. */
+		/* For testing hash matching with a large number
+		 * of flows we generate millions of IP 5-tuples
+		 * with an incremented dst address to initialize
+		 * the hash table.
+		 */
 		if (ipv6 == 0) {
 			/* populate the ipv4 hash */
 			populate_ipv4_many_flow_into_table(
@@ -781,7 +783,7 @@ setup_hash(const int socketid)
 			/* populate the ipv6 hash */
 			populate_ipv6_many_flow_into_table(
 				ipv6_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
+			hash_entry_number);
 		}
 	} else {
 		/*
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index f621269..ab31210 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -209,7 +209,7 @@ setup_lpm(const int socketid)
 			rte_lpm_create(s, socketid, &config_ipv4);
 	if (ipv4_l3fwd_lpm_lookup_struct[socketid] == NULL)
 		rte_exit(EXIT_FAILURE,
-			"Unable to create the l3fwd LPM table on socket %d\n",
+			"Unable to create v4 LPM table on socket %d\n",
 			socketid);
 
 	/* populate the LPM table */
@@ -221,14 +221,13 @@ setup_lpm(const int socketid)
 			continue;
 
 		ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
-			ipv4_l3fwd_lpm_route_array[i].ip,
-			ipv4_l3fwd_lpm_route_array[i].depth,
-			ipv4_l3fwd_lpm_route_array[i].if_out);
+				ipv4_l3fwd_lpm_route_array[i].ip,
+				ipv4_l3fwd_lpm_route_array[i].depth,
+				ipv4_l3fwd_lpm_route_array[i].if_out);
 
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE,
-				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
-				i, socketid);
+				"Unable to add entry %u to v4 LPM table on socket %d\n", i, socketid);
 		}
 
 		printf("LPM: Adding route 0x%08x / %d (%d)\n",
@@ -243,11 +242,11 @@ setup_lpm(const int socketid)
 	config.max_rules = IPV6_L3FWD_LPM_MAX_RULES;
 	config.number_tbl8s = IPV6_L3FWD_LPM_NUMBER_TBL8S;
 	config.flags = 0;
-	ipv6_l3fwd_lpm_lookup_struct[socketid] = rte_lpm6_create(s, socketid,
-				&config);
+	ipv6_l3fwd_lpm_lookup_struct[socketid] =
+			rte_lpm6_create(s, socketid, &config);
 	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL)
 		rte_exit(EXIT_FAILURE,
-			"Unable to create the l3fwd LPM table on socket %d\n",
+			"Unable to create v6 LPM table on socket %d\n",
 			socketid);
 
 	/* populate the LPM table */
@@ -265,13 +264,11 @@ setup_lpm(const int socketid)
 
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE,
-				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
-				i, socketid);
+				"Unable to add entry %u to the l3fwd LPM table on socket %d\n", i, socketid);
 		}
 
 		printf("LPM: Adding route %s / %d (%d)\n",
-			"IPV6",
-			ipv6_l3fwd_lpm_route_array[i].depth,
+			"IPV6", ipv6_l3fwd_lpm_route_array[i].depth,
 			ipv6_l3fwd_lpm_route_array[i].if_out);
 	}
 }
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index 3ce1b65..65d792b 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -51,6 +51,7 @@
 #include <rte_memory.h>
 #include <rte_memcpy.h>
 #include <rte_memzone.h>
+#include <rte_malloc.h>
 #include <rte_eal.h>
 #include <rte_per_lcore.h>
 #include <rte_launch.h>
@@ -97,11 +98,12 @@ static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
 /**< Ports set in promiscuous mode off by default. */
 static int promiscuous_on;
 
-/* Select Longest-Prefix or Exact match. */
+/* Select Longest-Prefix(aka LPM), Exact match(aka EM) or ACL. */
 static int l3fwd_lpm_on;
 static int l3fwd_em_on;
+static int l3fwd_acl_on;
 
-static int numa_on = 1; /**< NUMA is enabled by default. */
+int numa_on = 1; /**< NUMA is enabled by default. */
 static int parse_ptype; /**< Parse packet type using rx callback, and */
 			/**< disabled by default */
 
@@ -172,7 +174,7 @@ static struct rte_eth_conf port_conf = {
 static struct rte_mempool * pktmbuf_pool[NB_SOCKETS];
 
 struct l3fwd_lkp_mode {
-	void  (*setup)(int);
+	void  (*setup)(const int);
 	int   (*check_ptype)(int);
 	rte_rx_callback_fn cb_parse_ptype;
 	int   (*main_loop)(void *);
@@ -200,6 +202,15 @@ static struct l3fwd_lkp_mode l3fwd_lpm_lkp = {
 	.get_ipv6_lookup_struct = lpm_get_ipv6_l3fwd_lookup_struct,
 };
 
+static struct l3fwd_lkp_mode l3fwd_acl_lkp = {
+	.setup                  = setup_acl,
+	.check_ptype		= acl_check_ptype,
+	.cb_parse_ptype		= acl_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,
+};
+
 /*
  * Setup lookup methods for forwarding.
  * Currently exact-match and longest-prefix-match
@@ -211,8 +222,11 @@ setup_l3fwd_lookup_tables(void)
 	/* Setup HASH lookup functions. */
 	if (l3fwd_em_on)
 		l3fwd_lkp = l3fwd_em_lkp;
-	/* Setup LPM lookup functions. */
+	/* Setup ACL lookup functions. */
+	else if (l3fwd_acl_on)
+		l3fwd_lkp = l3fwd_acl_lkp;
 	else
+	/* Setup LPM lookup functions. */
 		l3fwd_lkp = l3fwd_lpm_lkp;
 }
 
@@ -312,31 +326,45 @@ print_usage(const char *prgname)
 {
 	printf("%s [EAL options] --"
 		" -p PORTMASK"
+		"--"OPTION_RULE_IPV4"=FILE"
+		"--"OPTION_RULE_IPV6"=FILE"
 		" [-P]"
 		" [-E]"
+		" [-A]"
 		" [-L]"
-		" --config (port,queue,lcore)[,(port,queue,lcore)]"
-		" [--eth-dest=X,MM:MM:MM:MM:MM:MM]"
-		" [--enable-jumbo [--max-pkt-len PKTLEN]]"
-		" [--no-numa]"
-		" [--hash-entry-num]"
-		" [--ipv6]"
-		" [--parse-ptype]\n\n"
+		" [--"OPTION_CONFIG" (port,queue,lcore)[,(port,queue,lcore]]"
+		" [--"OPTION_ETH_DEST" =X,MM:MM:MM:MM:MM:MM]"
+		" [--"OPTION_ENBJMO" [--"OPTION_MAX_PKT_LEN" PKTLEN]]"
+		" [--"OPTION_NONUMA"]"
+		" [--"OPTION_HASH_ENTRY_NUM"]"
+		" [--"OPTION_IPV6"]"
+		" [--"OPTION_PARSE_PTYPE"]\n\n"
 
 		"  -p PORTMASK: Hexadecimal bitmask of ports to configure\n"
 		"  -P : Enable promiscuous mode\n"
 		"  -E : Enable exact match\n"
+		"  -A : Enable access control list match\n"
 		"  -L : Enable longest prefix match (default)\n"
-		"  --config (port,queue,lcore): Rx queue configuration\n"
-		"  --eth-dest=X,MM:MM:MM:MM:MM:MM: Ethernet destination for port X\n"
-		"  --enable-jumbo: Enable jumbo frames\n"
-		"  --max-pkt-len: Under the premise of enabling jumbo,\n"
+		"  --"OPTION_CONFIG" (port,queue,lcore): Rx queue configuration\n"
+		"  --"OPTION_ETH_DEST"=X,MM:MM:MM:MM:MM:MM: Ethernet destination for port X\n"
+		"  --"OPTION_ENBJMO": Enable jumbo frames\n"
+		"  --"OPTION_MAX_PKT_LEN": Under the premise of enabling jumbo,\n"
 		"                 maximum packet length in decimal (64-9600)\n"
-		"  --no-numa: Disable numa awareness\n"
-		"  --hash-entry-num: Specify the hash entry number in hexadecimal to be setup\n"
-		"  --ipv6: Set if running ipv6 packets\n"
-		"  --parse-ptype: Set to use software to analyze packet type\n\n",
-		prgname);
+		"  --"OPTION_NONUMA": Disable numa awareness\n"
+		"  --"OPTION_HASH_ENTRY_NUM": Specify the hash entry number in hexadecimal to be setup\n"
+		"  --"OPTION_IPV6": Set if running ipv6 packets\n"
+		"  --"OPTION_PARSE_PTYPE": Set to use software to analyze packet type\n\n"
+		"  --"OPTION_RULE_IPV4"=FILE: specify the ipv4 rules "
+		"entries file. "
+		"Each rule occupy one line. "
+		"2 kinds of rules are supported. "
+		"One is ACL entry at while line leads with character '%c', "
+		"another is route entry at while line leads with "
+		"character '%c'.\n"
+		"  --"OPTION_RULE_IPV6"=FILE: specify the ipv6 rules "
+		"entries file.\n"
+		"  --"OPTION_SCALAR": Use scalar function to do lookup\n",
+		prgname, ACL_LEAD_CHAR, ROUTE_LEAD_CHAR);
 }
 
 static int
@@ -477,17 +505,11 @@ parse_eth_dest(const char *optarg)
 static const char short_options[] =
 	"p:"  /* portmask */
 	"P"   /* promiscuous */
+	"A"   /* enable access control list match */
 	"L"   /* enable long prefix match */
 	"E"   /* enable exact match */
 	;
 
-#define CMD_LINE_OPT_CONFIG "config"
-#define CMD_LINE_OPT_ETH_DEST "eth-dest"
-#define CMD_LINE_OPT_NO_NUMA "no-numa"
-#define CMD_LINE_OPT_IPV6 "ipv6"
-#define CMD_LINE_OPT_ENABLE_JUMBO "enable-jumbo"
-#define CMD_LINE_OPT_HASH_ENTRY_NUM "hash-entry-num"
-#define CMD_LINE_OPT_PARSE_PTYPE "parse-ptype"
 enum {
 	/* long options mapped to a short option */
 
@@ -501,16 +523,22 @@ enum {
 	CMD_LINE_OPT_ENABLE_JUMBO_NUM,
 	CMD_LINE_OPT_HASH_ENTRY_NUM_NUM,
 	CMD_LINE_OPT_PARSE_PTYPE_NUM,
+	CMD_LINE_OPT_RULE_IPV4,
+	CMD_LINE_OPT_RULE_IPV6,
+	CMD_LINE_OPT_SCALAR,
 };
 
 static const struct option lgopts[] = {
-	{CMD_LINE_OPT_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM},
-	{CMD_LINE_OPT_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM},
-	{CMD_LINE_OPT_NO_NUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM},
-	{CMD_LINE_OPT_IPV6, 0, 0, CMD_LINE_OPT_IPV6_NUM},
-	{CMD_LINE_OPT_ENABLE_JUMBO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM},
-	{CMD_LINE_OPT_HASH_ENTRY_NUM, 1, 0, CMD_LINE_OPT_HASH_ENTRY_NUM_NUM},
-	{CMD_LINE_OPT_PARSE_PTYPE, 0, 0, CMD_LINE_OPT_PARSE_PTYPE_NUM},
+	{OPTION_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM},
+	{OPTION_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM},
+	{OPTION_NONUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM},
+	{OPTION_IPV6, 0, 0, CMD_LINE_OPT_IPV6_NUM},
+	{OPTION_ENBJMO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM},
+	{OPTION_HASH_ENTRY_NUM, 1, 0, CMD_LINE_OPT_HASH_ENTRY_NUM_NUM},
+	{OPTION_PARSE_PTYPE, 0, 0, CMD_LINE_OPT_PARSE_PTYPE_NUM},
+	{OPTION_RULE_IPV4, 1, 0, CMD_LINE_OPT_RULE_IPV4},
+	{OPTION_RULE_IPV6, 1, 0, CMD_LINE_OPT_RULE_IPV6},
+	{OPTION_SCALAR, 0, 0, CMD_LINE_OPT_SCALAR},
 	{NULL, 0, 0, 0}
 };
 
@@ -522,17 +550,17 @@ static const struct option lgopts[] = {
  * value of 8192
  */
 #define NB_MBUF RTE_MAX(	\
-	(nb_ports*nb_rx_queue*RTE_TEST_RX_DESC_DEFAULT +	\
-	nb_ports*nb_lcores*MAX_PKT_BURST +			\
-	nb_ports*n_tx_queue*RTE_TEST_TX_DESC_DEFAULT +		\
-	nb_lcores*MEMPOOL_CACHE_SIZE),				\
+	(nb_ports * nb_rx_queue * RTE_TEST_RX_DESC_DEFAULT +	\
+	nb_ports * nb_lcores * MAX_PKT_BURST +			\
+	nb_ports * n_tx_queue * RTE_TEST_TX_DESC_DEFAULT +		\
+	nb_lcores * MEMPOOL_CACHE_SIZE),				\
 	(unsigned)8192)
 
 /* Parse the argument given in the command line of the application */
 static int
 parse_args(int argc, char **argv)
 {
-	int opt, ret;
+	int opt, ret, chk_cond;
 	char **argvopt;
 	int option_index;
 	char *prgname = argv[0];
@@ -544,17 +572,19 @@ parse_args(int argc, char **argv)
 	const char *str2 = "L3FWD: Promiscuous mode selected";
 	const char *str3 = "L3FWD: Exact match selected";
 	const char *str4 = "L3FWD: Longest-prefix match selected";
-	const char *str5 = "L3FWD: Invalid config";
-	const char *str6 = "L3FWD: NUMA is disabled";
-	const char *str7 = "L3FWD: IPV6 is specified";
-	const char *str8 =
+	const char *str5 = "L3FWD: Access Control List match selected";
+	const char *str6 = "L3FWD: Invalid config";
+	const char *str7 = "L3FWD: NUMA is disabled";
+	const char *str8 = "L3FWD: IPV6 is specified";
+	const char *str9 =
 		"L3FWD: Jumbo frame is enabled - disabling simple TX path";
-	const char *str9 = "L3FWD: Invalid packet length";
-	const char *str10 = "L3FWD: Set jumbo frame max packet len to ";
-	const char *str11 = "L3FWD: Invalid hash entry number";
-	const char *str12 =
-		"L3FWD: LPM and EM are mutually exclusive, select only one";
-	const char *str13 = "L3FWD: LPM or EM none selected, default LPM on";
+	const char *str10 = "L3FWD: Invalid packet length";
+	const char *str11 = "L3FWD: Set jumbo frame max packet len to ";
+	const char *str12 = "L3FWD: Invalid hash entry number";
+	const char *str13 =
+		"L3FWD: LPM, EM and ACL are mutually exclusive, select only one";
+	const char *str14 =
+		"L3FWD: LPM, EM or ACL none selected, default LPM on";
 
 	while ((opt = getopt_long(argc, argvopt, short_options,
 				lgopts, &option_index)) != EOF) {
@@ -585,11 +615,16 @@ parse_args(int argc, char **argv)
 			l3fwd_lpm_on = 1;
 			break;
 
+		case 'A':
+			printf("%s\n", str5);
+			l3fwd_acl_on = 1;
+			break;
+
 		/* long options */
 		case CMD_LINE_OPT_CONFIG_NUM:
 			ret = parse_config(optarg);
 			if (ret) {
-				printf("%s\n", str5);
+				printf("%s\n", str6);
 				print_usage(prgname);
 				return -1;
 			}
@@ -600,12 +635,12 @@ parse_args(int argc, char **argv)
 			break;
 
 		case CMD_LINE_OPT_NO_NUMA_NUM:
-			printf("%s\n", str6);
+			printf("%s\n", str7);
 			numa_on = 0;
 			break;
 
 		case CMD_LINE_OPT_IPV6_NUM:
-			printf("%sn", str7);
+			printf("%sn", str8);
 			ipv6 = 1;
 			break;
 
@@ -614,7 +649,7 @@ parse_args(int argc, char **argv)
 				"max-pkt-len", required_argument, 0, 0
 			};
 
-			printf("%s\n", str8);
+			printf("%s\n", str9);
 			port_conf.rxmode.jumbo_frame = 1;
 
 			/*
@@ -626,13 +661,13 @@ parse_args(int argc, char **argv)
 				ret = parse_max_pkt_len(optarg);
 				if ((ret < 64) ||
 					(ret > MAX_JUMBO_PKT_LEN)) {
-					printf("%s\n", str9);
+					printf("%s\n", str10);
 					print_usage(prgname);
 					return -1;
 				}
 				port_conf.rxmode.max_rx_pkt_len = ret;
 			}
-			printf("%s %u\n", str10,
+			printf("%s %u\n", str11,
 				(unsigned int)port_conf.rxmode.max_rx_pkt_len);
 			break;
 		}
@@ -642,7 +677,7 @@ parse_args(int argc, char **argv)
 			if ((ret > 0) && (ret <= L3FWD_HASH_ENTRIES)) {
 				hash_entry_number = ret;
 			} else {
-				printf("%s\n", str11);
+				printf("%s\n", str12);
 				print_usage(prgname);
 				return -1;
 			}
@@ -653,15 +688,29 @@ parse_args(int argc, char **argv)
 			parse_ptype = 1;
 			break;
 
+		case CMD_LINE_OPT_RULE_IPV4:
+			l3fwd_acl_set_rule_ipv4_name(optarg);
+			break;
+
+		case CMD_LINE_OPT_RULE_IPV6:
+			l3fwd_acl_set_rule_ipv6_name(optarg);
+			break;
+
+		case CMD_LINE_OPT_SCALAR:
+			l3fwd_acl_set_scalar();
+			break;
+
 		default:
 			print_usage(prgname);
 			return -1;
 		}
 	}
 
-	/* If both LPM and EM are selected, return error. */
-	if (l3fwd_lpm_on && l3fwd_em_on) {
-		printf("%s\n", str12);
+	/* If more than one(LPM, EM and ACL) are selected, return error. */
+	chk_cond = l3fwd_lpm_on ? (l3fwd_em_on || l3fwd_acl_on) :
+				(l3fwd_em_on && l3fwd_acl_on);
+	if (chk_cond) {
+		printf("%s\n", str13);
 		return -1;
 	}
 
@@ -669,9 +718,9 @@ parse_args(int argc, char **argv)
 	 * Nothing is selected, pick longest-prefix match
 	 * as default match.
 	 */
-	if (!l3fwd_lpm_on && !l3fwd_em_on) {
+	if (!l3fwd_lpm_on && !l3fwd_em_on && !l3fwd_acl_on) {
 		l3fwd_lpm_on = 1;
-		printf("%s\n", str13);
+		printf("%s\n", str14);
 	}
 
 	/*
@@ -684,6 +733,11 @@ parse_args(int argc, char **argv)
 		hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT;
 	}
 
+	/* For ACL, update port config rss hash filter. */
+	if (l3fwd_acl_on)
+		port_conf.rx_adv_conf.rss_conf.rss_hf |=
+			ETH_RSS_UDP | ETH_RSS_TCP | ETH_RSS_SCTP;
+
 	if (optind >= 0)
 		argv[optind-1] = prgname;
 
@@ -737,8 +791,9 @@ init_mem(unsigned nb_mbuf)
 				printf("Allocated mbuf pool on socket %d\n",
 					socketid);
 
-			/* Setup either LPM or EM(f.e Hash).  */
-			l3fwd_lkp.setup(socketid);
+			/* Setup LPM/EM (f.e. Hash) functions */
+			if (l3fwd_lpm_on || l3fwd_em_on)
+				l3fwd_lkp.setup(socketid);
 		}
 		qconf = &lcore_conf[lcore_id];
 		qconf->ipv4_lookup_struct =
@@ -836,7 +891,7 @@ prepare_ptype_parser(uint8_t portid, uint16_t queueid)
 		return 1;
 
 	printf("port %d cannot parse packet type, please add --%s\n",
-	       portid, CMD_LINE_OPT_PARSE_PTYPE);
+	       portid, OPTION_PARSE_PTYPE);
 	return 0;
 }
 
@@ -893,6 +948,10 @@ main(int argc, char **argv)
 	/* Setup function pointers for lookup method. */
 	setup_l3fwd_lookup_tables();
 
+	/* Setup ACL functions */
+	if (l3fwd_acl_on)
+		l3fwd_lkp.setup(-1);
+
 	/* initialize all ports */
 	for (portid = 0; portid < nb_ports; portid++) {
 		/* skip ports that are not enabled */
@@ -936,6 +995,28 @@ main(int argc, char **argv)
 		if (ret < 0)
 			rte_exit(EXIT_FAILURE, "init_mem failed\n");
 
+		/* ACL specific. */
+		if (l3fwd_acl_on) {
+			for (lcore_id = 0;
+				lcore_id < RTE_MAX_LCORE; lcore_id++) {
+				if (rte_lcore_is_enabled(lcore_id) == 0)
+					continue;
+
+				/* Initialize TX buffers */
+				qconf = &lcore_conf[lcore_id];
+				qconf->tx_buffer[portid] =
+					rte_zmalloc_socket("tx_buffer",
+					RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST),
+					0, rte_eth_dev_socket_id(portid));
+
+				if (qconf->tx_buffer[portid] == NULL)
+					rte_exit(EXIT_FAILURE, "Can't allocate tx buffer for port %u\n", (unsigned int) portid);
+
+				rte_eth_tx_buffer_init(qconf->tx_buffer[portid],
+							MAX_PKT_BURST);
+			}
+		}
+
 		/* init one TX queue per couple (lcore,port) */
 		queueid = 0;
 		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-- 
2.7.4

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [dpdk-dev] [PATCH v6 2/3] examples/l3fwd: add config file support for lpm
  2017-03-10 20:58       ` [dpdk-dev] [PATCH v6 0/3] " Ravi Kerur
  2017-03-10 20:58         ` [dpdk-dev] [PATCH v6 1/3] " Ravi Kerur
@ 2017-03-10 20:58         ` Ravi Kerur
  2017-03-10 20:58         ` [dpdk-dev] [PATCH v6 3/3] examples/l3fwd: add config file support for exact Ravi Kerur
                           ` (2 subsequent siblings)
  4 siblings, 0 replies; 34+ messages in thread
From: Ravi Kerur @ 2017-03-10 20:58 UTC (permalink / raw)
  To: dev; +Cc: john.mcnamara, Ravi Kerur

Add support to read from config file to build ipv4 and ipv6
longest prefix match forwarding tables.
---

v6:
	> Change commit message format.

v5:
	> Change is_bypass_line from inline to non-line.

v4:
	> No changes.

v3:
	> Fix additional checkpatch coding style issues.

v2:
	> Fix checkpatch warnings related to code
	> MACRO GET_CB_FIELD checkpatch warning not fixed

v1:
	> Remove static array configuration of Destination IP, MASK
		and IF_OUT for LPM and LPM6 config.
	> Add reading configuration from a file.
	> Format of configuration file is as follows
		#LPM route entries Dest-IP/Mask IF_OUT
		L1.1.1.0/24 0
		L2.1.1.0/24 1
		L3.1.1.0/24 2
		...

		#LPM6 route entries Dest-IP/Mask IF_OUT
		L1111:1111:1111:1111:0000:0000:0000:0000/48 0
		L2111:1111:1111:1111:0000:0000:0000:0000/48 1
		L3111:1111:1111:1111:0000:0000:0000:0000/48 2
		...

Signed-off-by: Ravi Kerur <rkerur@gmail.com>
---
 examples/l3fwd/l3fwd.h     |  28 +++++
 examples/l3fwd/l3fwd_acl.c |  39 +-----
 examples/l3fwd/l3fwd_acl.h |  29 -----
 examples/l3fwd/l3fwd_lpm.c | 308 +++++++++++++++++++++++++++++++++++++--------
 examples/l3fwd/main.c      |  47 ++++++-
 5 files changed, 332 insertions(+), 119 deletions(-)

diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 93e08f6..aa4bd25 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -94,6 +94,29 @@
 #define ACL_LEAD_CHAR		('@')
 #define ROUTE_LEAD_CHAR		('R')
 #define COMMENT_LEAD_CHAR	('#')
+#define LPM_LEAD_CHAR		('L')
+#define EM_LEAD_CHAR		('E')
+
+#define	IPV6_ADDR_LEN	16
+#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
+#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
+
+#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
+	unsigned long val;                                      \
+	char *end;                                              \
+	errno = 0;                                              \
+	val = strtoul((in), &end, (base));                      \
+	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
+		return -EINVAL;                               \
+	(fd) = (typeof(fd))val;                                 \
+	(in) = end + 1;                                         \
+} while (0)
+
+struct parm_cfg {
+	const char *rule_ipv4_name;
+	const char *rule_ipv6_name;
+	int scalar;
+};
 
 struct mbuf_table {
 	uint16_t len;
@@ -134,6 +157,8 @@ extern xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+extern struct parm_cfg parm_config;
+
 extern int numa_on; /**< NUMA is enabled by default. */
 
 /* Send burst of packets on an output interface */
@@ -287,4 +312,7 @@ l3fwd_acl_set_rule_ipv6_name(const char *optarg);
 void
 l3fwd_acl_set_rule_ipv4_name(const char *optarg);
 
+int
+is_bypass_line(const char *buff);
+
 #endif  /* __L3_FWD_H__ */
diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
index 388b978..66ed23d 100644
--- a/examples/l3fwd/l3fwd_acl.c
+++ b/examples/l3fwd/l3fwd_acl.c
@@ -147,10 +147,6 @@ struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
 	},
 };
 
-#define	IPV6_ADDR_LEN	16
-#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
-#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
-
 enum {
 	PROTO_FIELD_IPV6,
 	SRC1_FIELD_IPV6,
@@ -297,12 +293,6 @@ static struct {
 
 const char cb_port_delim[] = ":";
 
-static struct {
-	const char *rule_ipv4_name;
-	const char *rule_ipv6_name;
-	int scalar;
-} parm_config;
-
 /*
  * Print and dump ACL/Route rules functions are defined in
  * following header file.
@@ -316,27 +306,6 @@ static struct {
 #include "l3fwd_acl_scalar.h"
 
 /*
- * API's called during initialization to setup ACL rules.
- */
-void
-l3fwd_acl_set_rule_ipv4_name(const char *optarg)
-{
-	parm_config.rule_ipv4_name = optarg;
-}
-
-void
-l3fwd_acl_set_rule_ipv6_name(const char *optarg)
-{
-	parm_config.rule_ipv6_name = optarg;
-}
-
-void
-l3fwd_acl_set_scalar(void)
-{
-	parm_config.scalar = 1;
-}
-
-/*
  * Parses IPV6 address, exepcts the following format:
  * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X - is a hexedecimal digit).
  */
@@ -566,7 +535,7 @@ parse_cb_ipv4vlan_rule(char *str, struct rte_acl_rule *v, int has_userdata)
 }
 
 static int
-add_rules(const char *rule_path,
+acl_add_rules(const char *rule_path,
 		struct rte_acl_rule **proute_base,
 		unsigned int *proute_num,
 		struct rte_acl_rule **pacl_base,
@@ -764,8 +733,8 @@ setup_acl(const int socket_id __attribute__((unused)))
 
 	dump_acl_config();
 
-	/* Load  rules from the input file */
-	if (add_rules(parm_config.rule_ipv4_name, &route_base_ipv4,
+	/* Load rules from the input file */
+	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)
 		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
@@ -776,7 +745,7 @@ setup_acl(const int socket_id __attribute__((unused)))
 	acl_log("IPv4 ACL entries %u:\n", acl_num_ipv4);
 	dump_ipv4_rules((struct acl4_rule *)acl_base_ipv4, acl_num_ipv4, 1);
 
-	if (add_rules(parm_config.rule_ipv6_name, &route_base_ipv6,
+	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)
diff --git a/examples/l3fwd/l3fwd_acl.h b/examples/l3fwd/l3fwd_acl.h
index b79784b..da0b5a8 100644
--- a/examples/l3fwd/l3fwd_acl.h
+++ b/examples/l3fwd/l3fwd_acl.h
@@ -55,17 +55,6 @@
 #define MBUF_IPV6_2PROTO(m)	\
 	rte_pktmbuf_mtod_offset((m), uint8_t *, OFF_ETHHEAD + OFF_IPV62PROTO)
 
-#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
-	unsigned long val;                                      \
-	char *end;                                              \
-	errno = 0;                                              \
-	val = strtoul((in), &end, (base));                      \
-	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
-		return -EINVAL;                               \
-	(fd) = (typeof(fd))val;                                 \
-	(in) = end + 1;                                         \
-} while (0)
-
 /*
  * 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.
@@ -163,24 +152,6 @@ print_one_ipv6_rule(struct acl6_rule *rule, int extra)
 			rule->data.userdata);
 }
 
-/* Bypass comment and empty lines */
-static inline int
-is_bypass_line(char *buff)
-{
-	int i = 0;
-
-	/* comment line */
-	if (buff[0] == COMMENT_LEAD_CHAR)
-		return 1;
-	/* empty line */
-	while (buff[i] != '\0') {
-		if (!isspace(buff[i]))
-			return 0;
-		i++;
-	}
-	return 1;
-}
-
 #ifdef L3FWDACL_DEBUG
 static inline void
 dump_acl4_rule(struct rte_mbuf *m, uint32_t sig)
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index ab31210..97f99cf 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -57,45 +57,24 @@
 
 #include "l3fwd.h"
 
-struct ipv4_l3fwd_lpm_route {
-	uint32_t ip;
-	uint8_t  depth;
-	uint8_t  if_out;
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
 };
 
-struct ipv6_l3fwd_lpm_route {
-	uint8_t ip[16];
+struct lpm_rule {
+	union {
+		uint32_t ip;
+		union {
+			uint32_t ip_32[4];
+			uint8_t ip_8[16];
+		};
+	};
 	uint8_t  depth;
 	uint8_t  if_out;
 };
 
-static struct ipv4_l3fwd_lpm_route ipv4_l3fwd_lpm_route_array[] = {
-	{IPv4(1, 1, 1, 0), 24, 0},
-	{IPv4(2, 1, 1, 0), 24, 1},
-	{IPv4(3, 1, 1, 0), 24, 2},
-	{IPv4(4, 1, 1, 0), 24, 3},
-	{IPv4(5, 1, 1, 0), 24, 4},
-	{IPv4(6, 1, 1, 0), 24, 5},
-	{IPv4(7, 1, 1, 0), 24, 6},
-	{IPv4(8, 1, 1, 0), 24, 7},
-};
-
-static struct ipv6_l3fwd_lpm_route ipv6_l3fwd_lpm_route_array[] = {
-	{{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 0},
-	{{2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 1},
-	{{3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 2},
-	{{4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 3},
-	{{5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 4},
-	{{6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 5},
-	{{7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 6},
-	{{8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 48, 7},
-};
-
-#define IPV4_L3FWD_LPM_NUM_ROUTES \
-	(sizeof(ipv4_l3fwd_lpm_route_array) / sizeof(ipv4_l3fwd_lpm_route_array[0]))
-#define IPV6_L3FWD_LPM_NUM_ROUTES \
-	(sizeof(ipv6_l3fwd_lpm_route_array) / sizeof(ipv6_l3fwd_lpm_route_array[0]))
-
 #define IPV4_L3FWD_LPM_MAX_RULES         1024
 #define IPV4_L3FWD_LPM_NUMBER_TBL8S (1 << 8)
 #define IPV6_L3FWD_LPM_MAX_RULES         1024
@@ -110,6 +89,211 @@ struct rte_lpm6 *ipv6_l3fwd_lpm_lookup_struct[NB_SOCKETS];
 #include "l3fwd_lpm.h"
 #endif
 
+static int
+lpm_parse_v6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
+	char dlm)
+{
+	uint32_t addr[IPV6_ADDR_U16];
+
+	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
+
+	*end = in;
+
+	v[0] = (addr[0] << 16) + addr[1];
+	v[1] = (addr[2] << 16) + addr[3];
+	v[2] = (addr[4] << 16) + addr[5];
+	v[3] = (addr[6] << 16) + addr[7];
+
+	return 0;
+}
+
+static int
+lpm_parse_v6_net(const char *in, uint32_t *v, uint8_t *mask_len)
+{
+	int32_t rc;
+	const char *mp;
+	uint8_t m;
+	uint32_t tmp[4];
+
+	/* get address. */
+	rc = lpm_parse_v6_addr(in, &mp, v, '/');
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_addr failed %d\n", rc);
+		return rc;
+	}
+
+	/* get mask. */
+	GET_CB_FIELD(mp, m, 0, sizeof(tmp) * CHAR_BIT, 0);
+	*mask_len = m;
+
+	return 0;
+}
+
+static int
+lpm_parse_v6_rule(char *str, struct lpm_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL) {
+			RTE_LOG(ERR, L3FWD,
+				"\nparse_v6_rule strtok_r failed\n");
+			return -EINVAL;
+		}
+	}
+
+	rc = lpm_parse_v6_net(in[CB_FLD_DST_ADDR], v->ip_32, &v->depth);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_net failed\n");
+		return rc;
+	}
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+lpm_parse_v4_net(const char *in, uint32_t *addr, uint8_t *mask_len)
+{
+	uint8_t a, b, c, d, m;
+
+	GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, d, 0, UINT8_MAX, '/');
+	GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
+
+	addr[0] = IPv4(a, b, c, d);
+	*mask_len = m;
+
+	return 0;
+}
+
+static int
+lpm_parse_v4_rule(char *str, struct lpm_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = lpm_parse_v4_net(in[CB_FLD_DST_ADDR], &v->ip, &v->depth);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v4_net failed %d\n", rc);
+		return rc;
+	}
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+lpm_add_rules(const char *rule_path,
+		struct lpm_rule **proute_base,
+		unsigned int *proute_num,
+		int (*parser)(char *, struct lpm_rule *))
+{
+	uint8_t *route_rules;
+	struct lpm_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh = fopen(rule_path, "rb");
+	unsigned int i = 0, rule_size = sizeof(*next);
+
+	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] == LPM_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0)
+		rte_exit(EXIT_FAILURE, "Not find any route entries in %s!\n",
+				rule_path);
+
+	fseek(fh, 0, SEEK_SET);
+
+	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 == LPM_LEAD_CHAR)
+			next = (struct lpm_rule *)
+				(route_rules + route_cnt * rule_size);
+
+		/* Illegal line */
+		else
+			rte_exit(EXIT_FAILURE,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, LPM_LEAD_CHAR);
+
+		if (parser(buff + 1, next) != 0)
+			rte_exit(EXIT_FAILURE,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = (struct lpm_rule *)route_rules;
+	*proute_num = route_cnt;
+
+	return 0;
+}
+
+static int
+check_lpm_config(void)
+{
+	if (parm_config.rule_ipv4_name == NULL) {
+		RTE_LOG(ERR, L3FWD, "LPM IPv4 rule file not specified\n");
+		return -1;
+	} else if (parm_config.rule_ipv6_name == NULL) {
+		RTE_LOG(ERR, L3FWD, "LPM IPv6 rule file not specified\n");
+		return -1;
+	}
+
+	return 0;
+}
+
 /* main processing loop */
 int
 lpm_main_loop(__attribute__((unused)) void *dummy)
@@ -196,10 +380,16 @@ setup_lpm(const int socketid)
 {
 	struct rte_lpm6_config config;
 	struct rte_lpm_config config_ipv4;
+	struct lpm_rule *route_base_v4;
+	struct lpm_rule *route_base_v6;
+	unsigned int route_num_v4 = 0, route_num_v6 = 0;
 	unsigned i;
 	int ret;
 	char s[64];
 
+	if (check_lpm_config() != 0)
+		rte_exit(EXIT_FAILURE, "Failed to get valid LPM options\n");
+
 	/* create the LPM table */
 	config_ipv4.max_rules = IPV4_L3FWD_LPM_MAX_RULES;
 	config_ipv4.number_tbl8s = IPV4_L3FWD_LPM_NUMBER_TBL8S;
@@ -212,18 +402,25 @@ setup_lpm(const int socketid)
 			"Unable to create v4 LPM table on socket %d\n",
 			socketid);
 
+	/* Load rules from the input file */
+	if (lpm_add_rules(parm_config.rule_ipv4_name,
+		&route_base_v4, &route_num_v4,
+		&lpm_parse_v4_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add lpm v4 rules\n");
+
 	/* populate the LPM table */
-	for (i = 0; i < IPV4_L3FWD_LPM_NUM_ROUTES; i++) {
+	for (i = 0; i < route_num_v4; i++) {
 
 		/* skip unused ports */
-		if ((1 << ipv4_l3fwd_lpm_route_array[i].if_out &
-				enabled_port_mask) == 0)
+		if ((1 << route_base_v4[i].if_out &
+			enabled_port_mask) == 0)
 			continue;
 
-		ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
-				ipv4_l3fwd_lpm_route_array[i].ip,
-				ipv4_l3fwd_lpm_route_array[i].depth,
-				ipv4_l3fwd_lpm_route_array[i].if_out);
+		ret = rte_lpm_add(
+			ipv4_l3fwd_lpm_lookup_struct[socketid],
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE,
@@ -231,9 +428,9 @@ setup_lpm(const int socketid)
 		}
 
 		printf("LPM: Adding route 0x%08x / %d (%d)\n",
-			(unsigned)ipv4_l3fwd_lpm_route_array[i].ip,
-			ipv4_l3fwd_lpm_route_array[i].depth,
-			ipv4_l3fwd_lpm_route_array[i].if_out);
+			(unsigned int)route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 	}
 
 	/* create the LPM6 table */
@@ -249,18 +446,25 @@ setup_lpm(const int socketid)
 			"Unable to create v6 LPM table on socket %d\n",
 			socketid);
 
+	/* Load rules from the input file */
+	if (lpm_add_rules(parm_config.rule_ipv6_name,
+		&route_base_v6, &route_num_v6,
+		&lpm_parse_v6_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add lpm v6 rules\n");
+
 	/* populate the LPM table */
-	for (i = 0; i < IPV6_L3FWD_LPM_NUM_ROUTES; i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* skip unused ports */
-		if ((1 << ipv6_l3fwd_lpm_route_array[i].if_out &
-				enabled_port_mask) == 0)
+		if ((1 << route_base_v6[i].if_out &
+			enabled_port_mask) == 0)
 			continue;
 
-		ret = rte_lpm6_add(ipv6_l3fwd_lpm_lookup_struct[socketid],
-			ipv6_l3fwd_lpm_route_array[i].ip,
-			ipv6_l3fwd_lpm_route_array[i].depth,
-			ipv6_l3fwd_lpm_route_array[i].if_out);
+		ret = rte_lpm6_add(
+			ipv6_l3fwd_lpm_lookup_struct[socketid],
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE,
@@ -268,8 +472,8 @@ setup_lpm(const int socketid)
 		}
 
 		printf("LPM: Adding route %s / %d (%d)\n",
-			"IPV6", ipv6_l3fwd_lpm_route_array[i].depth,
-			ipv6_l3fwd_lpm_route_array[i].if_out);
+			"IPV6", route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 	}
 }
 
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index 65d792b..1098f78 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -126,6 +126,8 @@ uint32_t hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT;
 
 struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+struct parm_cfg parm_config;
+
 struct lcore_params {
 	uint8_t port_id;
 	uint8_t queue_id;
@@ -212,6 +214,27 @@ static struct l3fwd_lkp_mode l3fwd_acl_lkp = {
 };
 
 /*
+ * API's called during initialization to setup ACL/LPM/EM rules.
+ */
+static void
+l3fwd_set_rule_ipv4_name(const char *optarg)
+{
+	parm_config.rule_ipv4_name = optarg;
+}
+
+static void
+l3fwd_set_rule_ipv6_name(const char *optarg)
+{
+	parm_config.rule_ipv6_name = optarg;
+}
+
+static void
+l3fwd_set_scalar(void)
+{
+	parm_config.scalar = 1;
+}
+
+/*
  * Setup lookup methods for forwarding.
  * Currently exact-match and longest-prefix-match
  * are supported ones.
@@ -689,15 +712,15 @@ parse_args(int argc, char **argv)
 			break;
 
 		case CMD_LINE_OPT_RULE_IPV4:
-			l3fwd_acl_set_rule_ipv4_name(optarg);
+			l3fwd_set_rule_ipv4_name(optarg);
 			break;
 
 		case CMD_LINE_OPT_RULE_IPV6:
-			l3fwd_acl_set_rule_ipv6_name(optarg);
+			l3fwd_set_rule_ipv6_name(optarg);
 			break;
 
 		case CMD_LINE_OPT_SCALAR:
-			l3fwd_acl_set_scalar();
+			l3fwd_set_scalar();
 			break;
 
 		default:
@@ -895,6 +918,24 @@ prepare_ptype_parser(uint8_t portid, uint16_t queueid)
 	return 0;
 }
 
+/* Bypass comment and empty lines */
+int
+is_bypass_line(const char *buff)
+{
+	int i = 0;
+
+	/* comment line */
+	if (buff[0] == COMMENT_LEAD_CHAR)
+		return 1;
+	/* empty line */
+	while (buff[i] != '\0') {
+		if (!isspace(buff[i]))
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
 int
 main(int argc, char **argv)
 {
-- 
2.7.4

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [dpdk-dev] [PATCH v6 3/3] examples/l3fwd: add config file support for exact
  2017-03-10 20:58       ` [dpdk-dev] [PATCH v6 0/3] " Ravi Kerur
  2017-03-10 20:58         ` [dpdk-dev] [PATCH v6 1/3] " Ravi Kerur
  2017-03-10 20:58         ` [dpdk-dev] [PATCH v6 2/3] examples/l3fwd: add config file support for lpm Ravi Kerur
@ 2017-03-10 20:58         ` Ravi Kerur
  2017-06-04 11:14         ` [dpdk-dev] [PATCH v6 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd Thomas Monjalon
  2019-01-23 16:32         ` Ferruh Yigit
  4 siblings, 0 replies; 34+ messages in thread
From: Ravi Kerur @ 2017-03-10 20:58 UTC (permalink / raw)
  To: dev; +Cc: john.mcnamara, Ravi Kerur

Add support to read from config file to build ipv4 and ipv6
exact match forwarding tables.
---

v6:
	> Change commit message format

v5:
	> No changes.

v4:
	> No changes.

v3:
	> Fix additional checkpatch coding style issues.

v2:
	> Fix checkpatch warnings.

v1:
	> Remove static array configuration of Dest IP,Src IP, Dest
		port, Src port, Proto and IF_OUT for EM and EM6 config.
	> Add reading configuration from a file.
	> Format of configuration file is as follows
		#EM route entries,
		#Dest-IP Src-IP Dest-port Src-port Proto IF_OUT
		E101.0.0.0 100.10.0.0 101 11 0x06 0
		E201.0.0.0 200.20.0.0 102 12 0x06 1
		E111.0.0.0 211.30.0.0 101 11 0x06 2
		...

		#EM6 route entries
		#Dest-IP Src-IP Dest-port Src-port Proto IF_OUT
		Efe80:0000:0000:0000:021e:67ff:fe00:0000
			fe80:0000:0000:0000:021b:21ff:fe91:3805 101 11 0x06 0
		Efe90:0000:0000:0000:021e:67ff:fe00:0000
			fe90:0000:0000:0000:021b:21ff:fe91:3805 102 12 0x06 1
		...

Signed-off-by: Ravi Kerur <rkerur@gmail.com>
---
 examples/l3fwd/l3fwd_em.c | 376 +++++++++++++++++++++++++++++++++++++---------
 1 file changed, 303 insertions(+), 73 deletions(-)

diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index 6fdabf7..cd6b443 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -95,8 +95,14 @@ union ipv4_5tuple_host {
 #define XMM_NUM_IN_IPV6_5TUPLE 3
 
 struct ipv6_5tuple {
-	uint8_t  ip_dst[IPV6_ADDR_LEN];
-	uint8_t  ip_src[IPV6_ADDR_LEN];
+	union {
+		uint8_t  ip_dst[IPV6_ADDR_LEN];
+		uint32_t ip32_dst[4];
+	};
+	union {
+		uint8_t  ip_src[IPV6_ADDR_LEN];
+		uint32_t ip32_src[4];
+	};
 	uint16_t port_dst;
 	uint16_t port_src;
 	uint8_t  proto;
@@ -116,47 +122,24 @@ union ipv6_5tuple_host {
 	xmm_t xmm[XMM_NUM_IN_IPV6_5TUPLE];
 };
 
-
-
-struct ipv4_l3fwd_em_route {
-	struct ipv4_5tuple key;
-	uint8_t if_out;
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_SRC_ADDR,
+	CB_FLD_DST_PORT,
+	CB_FLD_SRC_PORT,
+	CB_FLD_PROTO,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
 };
 
-struct ipv6_l3fwd_em_route {
-	struct ipv6_5tuple key;
+struct em_rule {
+	union {
+		struct ipv4_5tuple v4_key;
+		struct ipv6_5tuple v6_key;
+	};
 	uint8_t if_out;
 };
 
-static struct ipv4_l3fwd_em_route ipv4_l3fwd_em_route_array[] = {
-	{{IPv4(101, 0, 0, 0), IPv4(100, 10, 0, 1),  101, 11, IPPROTO_TCP}, 0},
-	{{IPv4(201, 0, 0, 0), IPv4(200, 20, 0, 1),  102, 12, IPPROTO_TCP}, 1},
-	{{IPv4(111, 0, 0, 0), IPv4(100, 30, 0, 1),  101, 11, IPPROTO_TCP}, 2},
-	{{IPv4(211, 0, 0, 0), IPv4(200, 40, 0, 1),  102, 12, IPPROTO_TCP}, 3},
-};
-
-static struct ipv6_l3fwd_em_route ipv6_l3fwd_em_route_array[] = {
-	{{
-	{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x02, 0x1e, 0x67, 0xff, 0xfe, 0, 0, 0},
-	{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x02, 0x1b, 0x21, 0xff, 0xfe, 0x91, 0x38, 0x05},
-	101, 11, IPPROTO_TCP}, 0},
-
-	{{
-	{0xfe, 0x90, 0, 0, 0, 0, 0, 0, 0x02, 0x1e, 0x67, 0xff, 0xfe, 0, 0, 0},
-	{0xfe, 0x90, 0, 0, 0, 0, 0, 0, 0x02, 0x1b, 0x21, 0xff, 0xfe, 0x91, 0x38, 0x05},
-	102, 12, IPPROTO_TCP}, 1},
-
-	{{
-	{0xfe, 0xa0, 0, 0, 0, 0, 0, 0, 0x02, 0x1e, 0x67, 0xff, 0xfe, 0, 0, 0},
-	{0xfe, 0xa0, 0, 0, 0, 0, 0, 0, 0x02, 0x1b, 0x21, 0xff, 0xfe, 0x91, 0x38, 0x05},
-	101, 11, IPPROTO_TCP}, 2},
-
-	{{
-	{0xfe, 0xb0, 0, 0, 0, 0, 0, 0, 0x02, 0x1e, 0x67, 0xff, 0xfe, 0, 0, 0},
-	{0xfe, 0xb0, 0, 0, 0, 0, 0, 0, 0x02, 0x1b, 0x21, 0xff, 0xfe, 0x91, 0x38, 0x05},
-	102, 12, IPPROTO_TCP}, 3},
-};
-
 struct rte_hash *ipv4_l3fwd_em_lookup_struct[NB_SOCKETS];
 struct rte_hash *ipv6_l3fwd_em_lookup_struct[NB_SOCKETS];
 
@@ -233,12 +216,6 @@ ipv6_hash_crc(const void *data, __rte_unused uint32_t data_len,
 	return init_val;
 }
 
-#define IPV4_L3FWD_EM_NUM_ROUTES \
-	(sizeof(ipv4_l3fwd_em_route_array) / sizeof(ipv4_l3fwd_em_route_array[0]))
-
-#define IPV6_L3FWD_EM_NUM_ROUTES \
-	(sizeof(ipv6_l3fwd_em_route_array) / sizeof(ipv6_l3fwd_em_route_array[0]))
-
 static uint8_t ipv4_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 static uint8_t ipv6_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 
@@ -338,6 +315,224 @@ em_get_ipv6_dst_port(void *ipv6_hdr,  uint8_t portid, void *lookup_struct)
 #include "l3fwd_em.h"
 #endif
 
+static int
+em_parse_v6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
+	char dlm)
+{
+	uint32_t addr[IPV6_ADDR_U16];
+
+	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
+	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
+
+	*end = in;
+
+	v[0] = (addr[0] << 16) + addr[1];
+	v[1] = (addr[2] << 16) + addr[3];
+	v[2] = (addr[4] << 16) + addr[5];
+	v[3] = (addr[6] << 16) + addr[7];
+
+	return 0;
+}
+
+static int
+em_parse_v6_net(const char *in, uint32_t *v)
+{
+	int32_t rc;
+	const char *mp;
+
+	/* get address. */
+	rc = em_parse_v6_addr(in, &mp, v, 0);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_addr failed %d\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int
+em_parse_v6_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL) {
+			RTE_LOG(ERR, L3FWD, "strtok failed\n");
+			return -EINVAL;
+		}
+	}
+
+	rc = em_parse_v6_net(in[CB_FLD_DST_ADDR], v->v6_key.ip32_dst);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_net failed for dst %d\n", rc);
+		return rc;
+	}
+	rc = em_parse_v6_net(in[CB_FLD_SRC_ADDR], v->v6_key.ip32_src);
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v6_net failed for src %d\n", rc);
+		return rc;
+	}
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v6_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v6_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v6_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_parse_v4_net(const char *in, uint32_t *addr)
+{
+	uint8_t a, b, c, d;
+
+	GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
+	GET_CB_FIELD(in, d, 0, UINT8_MAX, 0);
+
+	addr[0] = IPv4(a, b, c, d);
+	return 0;
+}
+
+static int
+em_parse_v4_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL) {
+			RTE_LOG(ERR, L3FWD, "parse_v4_rule strtok fail\n");
+			return -EINVAL;
+		}
+	}
+
+	rc = em_parse_v4_net(in[CB_FLD_DST_ADDR], &(v->v4_key.ip_dst));
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v4_net dst failed %d\n", rc);
+		return rc;
+	}
+	rc = em_parse_v4_net(in[CB_FLD_SRC_ADDR], &(v->v4_key.ip_src));
+	if (rc != 0) {
+		RTE_LOG(ERR, L3FWD, "parse_v4_net src failed %d\n", rc);
+		return rc;
+	}
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v4_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v4_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v4_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_add_rules(const char *rule_path,
+		struct em_rule **proute_base,
+		unsigned int *proute_num,
+		int (*parser)(char *, struct em_rule*))
+{
+	uint8_t *route_rules;
+	struct em_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh = fopen(rule_path, "rb");
+	unsigned int i = 0, rule_size = sizeof(*next);
+
+	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] == EM_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0)
+		rte_exit(EXIT_FAILURE, "Not find any route entries in %s!\n",
+				rule_path);
+
+	fseek(fh, 0, SEEK_SET);
+
+	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 == EM_LEAD_CHAR)
+			next = (struct em_rule *)(route_rules +
+				route_cnt * rule_size);
+
+		/* Illegal line */
+		else
+			rte_exit(EXIT_FAILURE,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, EM_LEAD_CHAR);
+
+		if (parser(buff + 1, next) != 0)
+			rte_exit(EXIT_FAILURE,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = (struct em_rule *)route_rules;
+	*proute_num = route_cnt;
+
+	return 0;
+}
+
+static int
+check_em_config(void)
+{
+	if (parm_config.rule_ipv4_name == NULL) {
+		RTE_LOG(ERR, L3FWD, "EM IPv4 rule file not specified\n");
+		return -1;
+	} else if (parm_config.rule_ipv6_name == NULL) {
+		RTE_LOG(ERR, L3FWD, "EM IPv6 rule file not specified\n");
+		return -1;
+	}
+
+	return 0;
+}
+
 static void
 convert_ipv4_5tuple(struct ipv4_5tuple *key1,
 		union ipv4_5tuple_host *key2)
@@ -378,16 +573,24 @@ populate_ipv4_few_flow_into_table(const struct rte_hash *h)
 {
 	uint32_t i;
 	int32_t ret;
+	struct em_rule *route_base_v4;
+	unsigned int route_num_v4 = 0;
 
 	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
-	for (i = 0; i < IPV4_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv4_l3fwd_em_route  entry;
+	/* Load rules from the input file */
+	if (em_add_rules(parm_config.rule_ipv4_name,
+		&route_base_v4, &route_num_v4,
+		&em_parse_v4_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add em v4 rules\n");
+
+	for (i = 0; i < route_num_v4; i++) {
+		struct em_rule  entry;
 		union ipv4_5tuple_host newkey;
 
-		entry = ipv4_l3fwd_em_route_array[i];
-		convert_ipv4_5tuple(&entry.key, &newkey);
+		entry = route_base_v4[i];
+		convert_ipv4_5tuple(&entry.v4_key, &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
@@ -396,7 +599,7 @@ populate_ipv4_few_flow_into_table(const struct rte_hash *h)
 		ipv4_l3fwd_out_if[ret] = entry.if_out;
 	}
 	printf("Hash: Adding 0x%" PRIx64 " keys\n",
-		(uint64_t)IPV4_L3FWD_EM_NUM_ROUTES);
+		(uint64_t)route_num_v4);
 }
 
 #define BIT_16_TO_23 0x00ff0000
@@ -405,18 +608,26 @@ populate_ipv6_few_flow_into_table(const struct rte_hash *h)
 {
 	uint32_t i;
 	int32_t ret;
+	struct em_rule *route_base_v6;
+	unsigned int route_num_v6 = 0;
 
 	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
 	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
 
-	for (i = 0; i < IPV6_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv6_l3fwd_em_route entry;
+	/* Load rules from the input file */
+	if (em_add_rules(parm_config.rule_ipv6_name,
+		&route_base_v6, &route_num_v6,
+		&em_parse_v6_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add em v6 rules\n");
+
+	for (i = 0; i < route_num_v6; i++) {
+		struct em_rule entry;
 		union ipv6_5tuple_host newkey;
 
-		entry = ipv6_l3fwd_em_route_array[i];
-		convert_ipv6_5tuple(&entry.key, &newkey);
+		entry = route_base_v6[i];
+		convert_ipv6_5tuple(&entry.v6_key, &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
@@ -425,7 +636,7 @@ populate_ipv6_few_flow_into_table(const struct rte_hash *h)
 		ipv6_l3fwd_out_if[ret] = entry.if_out;
 	}
 	printf("Hash: Adding 0x%" PRIx64 "keys\n",
-		(uint64_t)IPV6_L3FWD_EM_NUM_ROUTES);
+		(uint64_t)route_num_v6);
 }
 
 #define NUMBER_PORT_USED 4
@@ -434,12 +645,20 @@ populate_ipv4_many_flow_into_table(const struct rte_hash *h,
 		unsigned int nr_flow)
 {
 	unsigned i;
+	struct em_rule *route_base_v4;
+	unsigned int route_num_v4 = 0;
 
 	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
+	/* Load rules from the input file */
+	if (em_add_rules(parm_config.rule_ipv4_name,
+		&route_base_v4, &route_num_v4,
+		&em_parse_v4_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add em v4 rules\n");
+
 	for (i = 0; i < nr_flow; i++) {
-		struct ipv4_l3fwd_em_route entry;
+		struct em_rule entry;
 		union ipv4_5tuple_host newkey;
 
 		uint8_t a = (uint8_t)
@@ -453,23 +672,23 @@ populate_ipv4_many_flow_into_table(const struct rte_hash *h,
 		memset(&entry, 0, sizeof(entry));
 		switch (i & (NUMBER_PORT_USED - 1)) {
 		case 0:
-			entry = ipv4_l3fwd_em_route_array[0];
-			entry.key.ip_dst = IPv4(101, c, b, a);
+			entry = route_base_v4[0];
+			entry.v4_key.ip_dst = IPv4(101, c, b, a);
 			break;
 		case 1:
-			entry = ipv4_l3fwd_em_route_array[1];
-			entry.key.ip_dst = IPv4(201, c, b, a);
+			entry = route_base_v4[1];
+			entry.v4_key.ip_dst = IPv4(201, c, b, a);
 			break;
 		case 2:
-			entry = ipv4_l3fwd_em_route_array[2];
-			entry.key.ip_dst = IPv4(111, c, b, a);
+			entry = route_base_v4[2];
+			entry.v4_key.ip_dst = IPv4(111, c, b, a);
 			break;
 		case 3:
-			entry = ipv4_l3fwd_em_route_array[3];
-			entry.key.ip_dst = IPv4(211, c, b, a);
+			entry = route_base_v4[3];
+			entry.v4_key.ip_dst = IPv4(211, c, b, a);
 			break;
 		};
-		convert_ipv4_5tuple(&entry.key, &newkey);
+		convert_ipv4_5tuple(&entry.v4_key, &newkey);
 		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
 
 		if (ret < 0)
@@ -486,13 +705,21 @@ populate_ipv6_many_flow_into_table(const struct rte_hash *h,
 		unsigned int nr_flow)
 {
 	unsigned i;
+	struct em_rule *route_base_v6;
+	unsigned int route_num_v6 = 0;
 
 	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
 
+	/* Load rules from the input file */
+	if (em_add_rules(parm_config.rule_ipv6_name,
+		&route_base_v6, &route_num_v6,
+		&em_parse_v6_rule) < 0)
+		rte_exit(EXIT_FAILURE, "Failed to add em v6 rules\n");
+
 	for (i = 0; i < nr_flow; i++) {
-		struct ipv6_l3fwd_em_route entry;
+		struct em_rule entry;
 		union ipv6_5tuple_host newkey;
 
 		uint8_t a = (uint8_t)
@@ -506,22 +733,22 @@ populate_ipv6_many_flow_into_table(const struct rte_hash *h,
 		memset(&entry, 0, sizeof(entry));
 		switch (i & (NUMBER_PORT_USED - 1)) {
 		case 0:
-			entry = ipv6_l3fwd_em_route_array[0];
+			entry = route_base_v6[0];
 			break;
 		case 1:
-			entry = ipv6_l3fwd_em_route_array[1];
+			entry = route_base_v6[1];
 			break;
 		case 2:
-			entry = ipv6_l3fwd_em_route_array[2];
+			entry = route_base_v6[2];
 			break;
 		case 3:
-			entry = ipv6_l3fwd_em_route_array[3];
+			entry = route_base_v6[3];
 			break;
 		};
-		entry.key.ip_dst[13] = c;
-		entry.key.ip_dst[14] = b;
-		entry.key.ip_dst[15] = a;
-		convert_ipv6_5tuple(&entry.key, &newkey);
+		entry.v6_key.ip_dst[13] = c;
+		entry.v6_key.ip_dst[14] = b;
+		entry.v6_key.ip_dst[15] = a;
+		convert_ipv6_5tuple(&entry.v6_key, &newkey);
 		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
 
 		if (ret < 0)
@@ -746,6 +973,9 @@ setup_hash(const int socketid)
 
 	char s[64];
 
+	if (check_em_config() != 0)
+		rte_exit(EXIT_FAILURE, "Failed to get valid EM options\n");
+
 	/* create ipv4 hash */
 	snprintf(s, sizeof(s), "ipv4_l3fwd_hash_%d", socketid);
 	ipv4_l3fwd_hash_params.name = s;
-- 
2.7.4

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd
  2017-03-10  0:07       ` Ravi Kerur
@ 2017-03-14 21:14         ` Ravi Kerur
  0 siblings, 0 replies; 34+ messages in thread
From: Ravi Kerur @ 2017-03-14 21:14 UTC (permalink / raw)
  To: Mcnamara, John; +Cc: dev, Ananyev, Konstantin, Richardson, Bruce

Hi John, Konstantin,

On Thu, Mar 9, 2017 at 4:07 PM, Ravi Kerur <rkerur@gmail.com> wrote:

> Hi John,
>
> On Thu, Mar 9, 2017 at 2:23 AM, Mcnamara, John <john.mcnamara@intel.com>
> wrote:
>
>> > From: Ravi Kerur [mailto:rkerur@gmail.com]
>> > Sent: Monday, March 6, 2017 11:21 PM
>> > To: Mcnamara, John <john.mcnamara@intel.com>
>> > Cc: dev@dpdk.org; Ananyev, Konstantin <konstantin.ananyev@intel.com>;
>> >     Richardson, Bruce <bruce.richardson@intel.com>
>> > Subject: Re: [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd
>> >
>> >
>> > Should I work with documentation team to update the document? If yes,
>> > please let me know the contact information.
>>
>> Hi Ravi,
>>
>> There isn't any documentation team. Unfortunately. :-)
>>
>> The raw documentation is in text format and is generally updated by
>> the developer submitting a patch.
>>
>> There is already docs for l3fwd and l3fwd_acl so all that is required
>> is to merge them. However, you will need to take some care to remove
>> the duplicate sections and to make sure that the merged doc makes
>> sense as a whole.
>>
>> See the documentation guidelines:
>>
>>     http://dpdk.org/doc/guides/contributing/documentation.html
>
>
> Thanks. I realized after skimming through the documents and seeing a
> 'doc:' prefix for review. Some clarifications on merging 'l3fwd' and
> 'l3fwd-acl'
>
> 1. Instead of wholesale merger of both 'l3fwd' and 'l3fwd-acl', does it
> make sense to
>
> a. Modify 'l3fwd' document by adding ACL processing information ('-A'
> cmdline) and providing a link or reference to existing 'l3fwd-acl'
> documentation since nothing has changed in terms of 'l3fwd-acl'.
>
> b. Add file read options for 'exact match' and 'longest prefix match' in
> 'l3fwd' documentation.
>

Kindly let me know how you want me to proceed with the changes to
documentation?

Thanks.


>
>  Thanks.
>
>>
>>
>> John
>>
>
>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [PATCH v6 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
  2017-03-10 20:58       ` [dpdk-dev] [PATCH v6 0/3] " Ravi Kerur
                           ` (2 preceding siblings ...)
  2017-03-10 20:58         ` [dpdk-dev] [PATCH v6 3/3] examples/l3fwd: add config file support for exact Ravi Kerur
@ 2017-06-04 11:14         ` Thomas Monjalon
  2019-01-23 16:32         ` Ferruh Yigit
  4 siblings, 0 replies; 34+ messages in thread
From: Thomas Monjalon @ 2017-06-04 11:14 UTC (permalink / raw)
  To: dev; +Cc: Ravi Kerur, john.mcnamara

10/03/2017 21:58, Ravi Kerur:
> This patchset merges l3fwd-acl and l3fwd code into common directory.
> Adds config file read option to build LPM and EM tables.
> 
> Ravi Kerur (3):
>   examples/l3fwd: merge l3fwd-acl code into l3fwd
>   examples/l3fwd: add config file support for lpm
>   examples/l3fwd: add config file support for exact

Anyone to review please?

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [PATCH v6 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
  2017-03-10 20:58       ` [dpdk-dev] [PATCH v6 0/3] " Ravi Kerur
                           ` (3 preceding siblings ...)
  2017-06-04 11:14         ` [dpdk-dev] [PATCH v6 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd Thomas Monjalon
@ 2019-01-23 16:32         ` Ferruh Yigit
  2019-01-23 17:10           ` Thomas Monjalon
  4 siblings, 1 reply; 34+ messages in thread
From: Ferruh Yigit @ 2019-01-23 16:32 UTC (permalink / raw)
  To: Ravi Kerur; +Cc: dpdk-dev, Thomas Monjalon, John McNamara

On 3/10/2017 8:58 PM, rkerur at gmail.com (Ravi Kerur) wrote:
> This patchset merges l3fwd-acl and l3fwd code into common directory.
> Adds config file read option to build LPM and EM tables.
> 
> Ravi Kerur (3):
>   examples/l3fwd: merge l3fwd-acl code into l3fwd
>   examples/l3fwd: add config file support for lpm
>   examples/l3fwd: add config file support for exact

Hi Ravi,

These l3fwd patches are in patchwork for a long time, I am updating the patchset
as rejected, if it is still relevant please send a new version on top of latest
repo.

Sorry for any inconvenience caused.

For reference patches:
https://patches.dpdk.org/patch/21696/
https://patches.dpdk.org/patch/21695/
https://patches.dpdk.org/patch/21697/

doc one:
https://patches.dpdk.org/patch/24211/

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [PATCH v6 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
  2019-01-23 16:32         ` Ferruh Yigit
@ 2019-01-23 17:10           ` Thomas Monjalon
  2019-01-23 17:20             ` Ananyev, Konstantin
  0 siblings, 1 reply; 34+ messages in thread
From: Thomas Monjalon @ 2019-01-23 17:10 UTC (permalink / raw)
  To: Ferruh Yigit, konstantin.ananyev; +Cc: Ravi Kerur, dpdk-dev, John McNamara

23/01/2019 17:32, Ferruh Yigit:
> On 3/10/2017 8:58 PM, rkerur at gmail.com (Ravi Kerur) wrote:
> > This patchset merges l3fwd-acl and l3fwd code into common directory.
> > Adds config file read option to build LPM and EM tables.
> > 
> > Ravi Kerur (3):
> >   examples/l3fwd: merge l3fwd-acl code into l3fwd
> >   examples/l3fwd: add config file support for lpm
> >   examples/l3fwd: add config file support for exact
> 
> Hi Ravi,
> 
> These l3fwd patches are in patchwork for a long time, I am updating the patchset
> as rejected, if it is still relevant please send a new version on top of latest
> repo.
> 
> Sorry for any inconvenience caused.
> 
> For reference patches:
> https://patches.dpdk.org/patch/21696/
> https://patches.dpdk.org/patch/21695/
> https://patches.dpdk.org/patch/21697/
> 
> doc one:
> https://patches.dpdk.org/patch/24211/

This work was going in the right direction.

Konstantin, as the maintainer of the ACL library,
do you think it is worth to keep this example as standalone or merged?

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [PATCH v6 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
  2019-01-23 17:10           ` Thomas Monjalon
@ 2019-01-23 17:20             ` Ananyev, Konstantin
  2019-01-23 17:26               ` Thomas Monjalon
  2019-01-24  9:14               ` Rami Rosen
  0 siblings, 2 replies; 34+ messages in thread
From: Ananyev, Konstantin @ 2019-01-23 17:20 UTC (permalink / raw)
  To: Thomas Monjalon, Yigit, Ferruh; +Cc: Ravi Kerur, dpdk-dev, Mcnamara, John



> -----Original Message-----
> From: Thomas Monjalon [mailto:thomas@monjalon.net]
> Sent: Wednesday, January 23, 2019 5:11 PM
> To: Yigit, Ferruh <ferruh.yigit@intel.com>; Ananyev, Konstantin <konstantin.ananyev@intel.com>
> Cc: Ravi Kerur <rkerur@gmail.com>; dpdk-dev <dev@dpdk.org>; Mcnamara, John <john.mcnamara@intel.com>
> Subject: Re: [dpdk-dev] [PATCH v6 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
> 
> 23/01/2019 17:32, Ferruh Yigit:
> > On 3/10/2017 8:58 PM, rkerur at gmail.com (Ravi Kerur) wrote:
> > > This patchset merges l3fwd-acl and l3fwd code into common directory.
> > > Adds config file read option to build LPM and EM tables.
> > >
> > > Ravi Kerur (3):
> > >   examples/l3fwd: merge l3fwd-acl code into l3fwd
> > >   examples/l3fwd: add config file support for lpm
> > >   examples/l3fwd: add config file support for exact
> >
> > Hi Ravi,
> >
> > These l3fwd patches are in patchwork for a long time, I am updating the patchset
> > as rejected, if it is still relevant please send a new version on top of latest
> > repo.
> >
> > Sorry for any inconvenience caused.
> >
> > For reference patches:
> > https://patches.dpdk.org/patch/21696/
> > https://patches.dpdk.org/patch/21695/
> > https://patches.dpdk.org/patch/21697/
> >
> > doc one:
> > https://patches.dpdk.org/patch/24211/
> 
> This work was going in the right direction.
> 

Totally agree.

> Konstantin, as the maintainer of the ACL library,
> do you think it is worth to keep this example as standalone or merged?

My vote is definitely for merging.
That would give us single l3fwd app with 3 different routing methods
(lpm, hash, acl) selectable at run-time, plus routing tables in config file.
Konstantin 

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [PATCH v6 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
  2019-01-23 17:20             ` Ananyev, Konstantin
@ 2019-01-23 17:26               ` Thomas Monjalon
  2019-01-23 19:36                 ` Ferruh Yigit
  2019-01-24  9:14               ` Rami Rosen
  1 sibling, 1 reply; 34+ messages in thread
From: Thomas Monjalon @ 2019-01-23 17:26 UTC (permalink / raw)
  To: Ananyev, Konstantin; +Cc: Yigit, Ferruh, Ravi Kerur, dpdk-dev, Mcnamara, John

23/01/2019 18:20, Ananyev, Konstantin:
> From: Thomas Monjalon [mailto:thomas@monjalon.net]
> > 23/01/2019 17:32, Ferruh Yigit:
> > > On 3/10/2017 8:58 PM, rkerur at gmail.com (Ravi Kerur) wrote:
> > > > This patchset merges l3fwd-acl and l3fwd code into common directory.
> > > > Adds config file read option to build LPM and EM tables.
> > > >
> > > > Ravi Kerur (3):
> > > >   examples/l3fwd: merge l3fwd-acl code into l3fwd
> > > >   examples/l3fwd: add config file support for lpm
> > > >   examples/l3fwd: add config file support for exact
> > >
> > > Hi Ravi,
> > >
> > > These l3fwd patches are in patchwork for a long time, I am updating the patchset
> > > as rejected, if it is still relevant please send a new version on top of latest
> > > repo.
> > >
> > > Sorry for any inconvenience caused.
> > >
> > > For reference patches:
> > > https://patches.dpdk.org/patch/21696/
> > > https://patches.dpdk.org/patch/21695/
> > > https://patches.dpdk.org/patch/21697/
> > >
> > > doc one:
> > > https://patches.dpdk.org/patch/24211/
> > 
> > This work was going in the right direction.
> > 
> 
> Totally agree.
> 
> > Konstantin, as the maintainer of the ACL library,
> > do you think it is worth to keep this example as standalone or merged?
> 
> My vote is definitely for merging.
> That would give us single l3fwd app with 3 different routing methods
> (lpm, hash, acl) selectable at run-time, plus routing tables in config file.

OK, so we just need to find a volunteer.
Ravi started the work a long time ago and did not receive enough review.
I'm afraid he's not available anymore.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [PATCH v6 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
  2019-01-23 17:26               ` Thomas Monjalon
@ 2019-01-23 19:36                 ` Ferruh Yigit
  2019-01-23 20:27                   ` Thomas Monjalon
  0 siblings, 1 reply; 34+ messages in thread
From: Ferruh Yigit @ 2019-01-23 19:36 UTC (permalink / raw)
  To: Thomas Monjalon, Ananyev, Konstantin, Stephen Hemminger
  Cc: Ravi Kerur, dpdk-dev, Mcnamara, John

On 1/23/2019 5:26 PM, Thomas Monjalon wrote:
> 23/01/2019 18:20, Ananyev, Konstantin:
>> From: Thomas Monjalon [mailto:thomas@monjalon.net]
>>> 23/01/2019 17:32, Ferruh Yigit:
>>>> On 3/10/2017 8:58 PM, rkerur at gmail.com (Ravi Kerur) wrote:
>>>>> This patchset merges l3fwd-acl and l3fwd code into common directory.
>>>>> Adds config file read option to build LPM and EM tables.
>>>>>
>>>>> Ravi Kerur (3):
>>>>>   examples/l3fwd: merge l3fwd-acl code into l3fwd
>>>>>   examples/l3fwd: add config file support for lpm
>>>>>   examples/l3fwd: add config file support for exact
>>>>
>>>> Hi Ravi,
>>>>
>>>> These l3fwd patches are in patchwork for a long time, I am updating the patchset
>>>> as rejected, if it is still relevant please send a new version on top of latest
>>>> repo.
>>>>
>>>> Sorry for any inconvenience caused.
>>>>
>>>> For reference patches:
>>>> https://patches.dpdk.org/patch/21696/
>>>> https://patches.dpdk.org/patch/21695/
>>>> https://patches.dpdk.org/patch/21697/
>>>>
>>>> doc one:
>>>> https://patches.dpdk.org/patch/24211/
>>>
>>> This work was going in the right direction.
>>>
>>
>> Totally agree.
>>
>>> Konstantin, as the maintainer of the ACL library,
>>> do you think it is worth to keep this example as standalone or merged?
>>
>> My vote is definitely for merging.
>> That would give us single l3fwd app with 3 different routing methods
>> (lpm, hash, acl) selectable at run-time, plus routing tables in config file.
> 
> OK, so we just need to find a volunteer.

There was a "Nice to have - Future" section in Roadmap webpage [1], does it help
putting there?

Also we talked about GSOC recently, can this be an item for it?


[1]
http://core.dpdk.org/roadmap/#future

> Ravi started the work a long time ago and did not receive enough review.
> I'm afraid he's not available anymore.
> 
> 

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [PATCH v6 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
  2019-01-23 19:36                 ` Ferruh Yigit
@ 2019-01-23 20:27                   ` Thomas Monjalon
  0 siblings, 0 replies; 34+ messages in thread
From: Thomas Monjalon @ 2019-01-23 20:27 UTC (permalink / raw)
  To: Ferruh Yigit
  Cc: Ananyev, Konstantin, Stephen Hemminger, Ravi Kerur, dpdk-dev,
	Mcnamara, John

23/01/2019 20:36, Ferruh Yigit:
> On 1/23/2019 5:26 PM, Thomas Monjalon wrote:
> > 23/01/2019 18:20, Ananyev, Konstantin:
> >> From: Thomas Monjalon [mailto:thomas@monjalon.net]
> >>> 23/01/2019 17:32, Ferruh Yigit:
> >>>> On 3/10/2017 8:58 PM, rkerur at gmail.com (Ravi Kerur) wrote:
> >>>>> This patchset merges l3fwd-acl and l3fwd code into common directory.
> >>>>> Adds config file read option to build LPM and EM tables.
> >>>>>
> >>>>> Ravi Kerur (3):
> >>>>>   examples/l3fwd: merge l3fwd-acl code into l3fwd
> >>>>>   examples/l3fwd: add config file support for lpm
> >>>>>   examples/l3fwd: add config file support for exact
> >>>>
> >>>> Hi Ravi,
> >>>>
> >>>> These l3fwd patches are in patchwork for a long time, I am updating the patchset
> >>>> as rejected, if it is still relevant please send a new version on top of latest
> >>>> repo.
> >>>>
> >>>> Sorry for any inconvenience caused.
> >>>>
> >>>> For reference patches:
> >>>> https://patches.dpdk.org/patch/21696/
> >>>> https://patches.dpdk.org/patch/21695/
> >>>> https://patches.dpdk.org/patch/21697/
> >>>>
> >>>> doc one:
> >>>> https://patches.dpdk.org/patch/24211/
> >>>
> >>> This work was going in the right direction.
> >>>
> >>
> >> Totally agree.
> >>
> >>> Konstantin, as the maintainer of the ACL library,
> >>> do you think it is worth to keep this example as standalone or merged?
> >>
> >> My vote is definitely for merging.
> >> That would give us single l3fwd app with 3 different routing methods
> >> (lpm, hash, acl) selectable at run-time, plus routing tables in config file.
> > 
> > OK, so we just need to find a volunteer.
> 
> There was a "Nice to have - Future" section in Roadmap webpage [1], does it help
> putting there?

Yes

> Also we talked about GSOC recently, can this be an item for it?

Yes

Good suggestions :)

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [dpdk-dev] [PATCH v6 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
  2019-01-23 17:20             ` Ananyev, Konstantin
  2019-01-23 17:26               ` Thomas Monjalon
@ 2019-01-24  9:14               ` Rami Rosen
  1 sibling, 0 replies; 34+ messages in thread
From: Rami Rosen @ 2019-01-24  9:14 UTC (permalink / raw)
  To: Ananyev, Konstantin
  Cc: Thomas Monjalon, Yigit, Ferruh, Ravi Kerur, dpdk-dev, Mcnamara, John

Hi all,
>My vote is definitely for merging.

+1 for this, this seems very reasonable

Regards,
Rami Rosen

On Wed, 23 Jan 2019 at 19:20, Ananyev, Konstantin
<konstantin.ananyev@intel.com> wrote:
>
>
>
> > -----Original Message-----
> > From: Thomas Monjalon [mailto:thomas@monjalon.net]
> > Sent: Wednesday, January 23, 2019 5:11 PM
> > To: Yigit, Ferruh <ferruh.yigit@intel.com>; Ananyev, Konstantin <konstantin.ananyev@intel.com>
> > Cc: Ravi Kerur <rkerur@gmail.com>; dpdk-dev <dev@dpdk.org>; Mcnamara, John <john.mcnamara@intel.com>
> > Subject: Re: [dpdk-dev] [PATCH v6 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd
> >
> > 23/01/2019 17:32, Ferruh Yigit:
> > > On 3/10/2017 8:58 PM, rkerur at gmail.com (Ravi Kerur) wrote:
> > > > This patchset merges l3fwd-acl and l3fwd code into common directory.
> > > > Adds config file read option to build LPM and EM tables.
> > > >
> > > > Ravi Kerur (3):
> > > >   examples/l3fwd: merge l3fwd-acl code into l3fwd
> > > >   examples/l3fwd: add config file support for lpm
> > > >   examples/l3fwd: add config file support for exact
> > >
> > > Hi Ravi,
> > >
> > > These l3fwd patches are in patchwork for a long time, I am updating the patchset
> > > as rejected, if it is still relevant please send a new version on top of latest
> > > repo.
> > >
> > > Sorry for any inconvenience caused.
> > >
> > > For reference patches:
> > > https://patches.dpdk.org/patch/21696/
> > > https://patches.dpdk.org/patch/21695/
> > > https://patches.dpdk.org/patch/21697/
> > >
> > > doc one:
> > > https://patches.dpdk.org/patch/24211/
> >
> > This work was going in the right direction.
> >
>
> Totally agree.
>
> > Konstantin, as the maintainer of the ACL library,
> > do you think it is worth to keep this example as standalone or merged?
>
> My vote is definitely for merging.
> That would give us single l3fwd app with 3 different routing methods
> (lpm, hash, acl) selectable at run-time, plus routing tables in config file.
> Konstantin
>
>
>
>
>
>
>
>

^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2019-01-24  9:14 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-05 19:47 [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd Ravi Kerur
2017-03-05 19:47 ` [dpdk-dev] [v4 1/3] Merge l3fwd-acl and l3fwd code Ravi Kerur
2017-03-08 21:32   ` [dpdk-dev] [PATCH v5 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd Ravi Kerur
2017-03-08 21:32     ` [dpdk-dev] [PATCH v5 1/3] " Ravi Kerur
2017-03-09 10:42       ` Mcnamara, John
2017-03-10 20:58       ` [dpdk-dev] [PATCH v6 0/3] " Ravi Kerur
2017-03-10 20:58         ` [dpdk-dev] [PATCH v6 1/3] " Ravi Kerur
2017-03-10 20:58         ` [dpdk-dev] [PATCH v6 2/3] examples/l3fwd: add config file support for lpm Ravi Kerur
2017-03-10 20:58         ` [dpdk-dev] [PATCH v6 3/3] examples/l3fwd: add config file support for exact Ravi Kerur
2017-06-04 11:14         ` [dpdk-dev] [PATCH v6 0/3] examples/l3fwd: merge l3fwd-acl code into l3fwd Thomas Monjalon
2019-01-23 16:32         ` Ferruh Yigit
2019-01-23 17:10           ` Thomas Monjalon
2019-01-23 17:20             ` Ananyev, Konstantin
2019-01-23 17:26               ` Thomas Monjalon
2019-01-23 19:36                 ` Ferruh Yigit
2019-01-23 20:27                   ` Thomas Monjalon
2019-01-24  9:14               ` Rami Rosen
2017-03-08 21:32     ` [dpdk-dev] [PATCH v5 2/3] examples/l3fwd: add config file support for LPM Ravi Kerur
2017-03-09 10:43       ` Mcnamara, John
2017-03-08 21:32     ` [dpdk-dev] [PATCH v5 3/3] examples/l3fwd: add config file support for EM Ravi Kerur
2017-03-09 10:45       ` Mcnamara, John
2017-03-10  0:00         ` Ravi Kerur
2017-03-05 19:47 ` [dpdk-dev] [v4 2/3] LPM config file read option Ravi Kerur
2017-03-06 13:57   ` Mcnamara, John
2017-03-06 14:10     ` Mcnamara, John
2017-03-05 19:47 ` [dpdk-dev] [v4 3/3] EM " Ravi Kerur
2017-03-06 13:42   ` Mcnamara, John
2017-03-06 13:33 ` [dpdk-dev] [v4 0/3] Merge l3fwd-acl and l3fwd Mcnamara, John
2017-03-06 23:20   ` Ravi Kerur
2017-03-08 21:50     ` Ravi Kerur
2017-03-09 10:37       ` Mcnamara, John
2017-03-09 10:23     ` Mcnamara, John
2017-03-10  0:07       ` Ravi Kerur
2017-03-14 21:14         ` Ravi Kerur

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).