DPDK中ACL(Access Control List)的学习(基于5元组增加MAC)

DPDK ACL源网页

ACL原理:
DPDK中的ACL(Access Control List)模块提供了数据包分类功能,Packet Classification and Access Control中所述:

The DPDK provides an Access Control library that gives the ability to classify an input packet based on a set of classification rules.
The ACL library is used to perform an N-tuple search over a set of rules with multiple categories and find the best match (highest priority) for each category. The library API provides the following basic operations:
Create a new Access Control (AC) context.
Add rules into the context.
For all rules in the context, build the runtime structures necessary to perform packet classification.
Perform input packet classifications.
Destroy an AC context and its runtime structures and free the associated memory.

就是事先定义一些基于数据包N元组的规则,每一个规则都有一个优先级,并且归属到一个或多个不同类别;然后对输入的数据包进行规则检查,如果该数据包满足一个类别里的某些规则,返回结果是其中优先级最高的那个,如果类别中的规则都不满足就返回0.

ACL库用来在一系列规则上执行N元组查找,可以实现多个分类和对每个分类查找最佳匹配(最高优先级),ACL库的api提供如下基本操作:

创建一个新的访问控制(AC)环境实例(context)
添加规则到这个环境实例
为这个实例里所有的规则,创建必需的运行时结构体来指针报文分类
执行接收报文分类
删除AC环境实例和对应的运行时结构体,并释放内存

规则定义
ACL的规则是基于N元组的,对N元组内的每一个字段来设置具体的规则内容.其字段定义如下:

rte_acl.h:
/**
 * ACL Field definition.
 * Each field in the ACL rule has an associate definition.
 * It defines the type of field, its size, its offset in the input buffer,
 * the field index, and the input index.
 * For performance reasons, the inner loop of the search function is unrolled
 * to process four input bytes at a time. This requires the input to be grouped
 * into sets of 4 consecutive bytes. The loop processes the first input byte as
 * part of the setup and then subsequent bytes must be in groups of 4
 * consecutive bytes.
 */
struct rte_acl_field_def {
	uint8_t  type;        /**< type - RTE_ACL_FIELD_TYPE_*. */
	uint8_t	 size;        /**< size of field 1,2,4, or 8. */
	uint8_t	 field_index; /**< index of field inside the rule. */
	uint8_t  input_index; /**< 0-N input index. */
	uint32_t offset;      /**< offset to start of field. */
};

各字段的意义:

type:该字段的规则类型,共有3种取值:
RTE_ACL_FIELD_TYPE_MASK: 字段值+掩码,典型的例子是带掩码的IP地址
RTE_ACL_FIELD_TYPE_RANGE: 字段值在一个范围内,典型例子是段口号范围
RTE_ACL_FIELD_TYPE_BITMASK: 字段值由掩码指定的的几个比特位表示,典型例子是协议号
size:该字段的大小,可以是1,2,4,8个字节,不可以随意设置,原因见后面
field_index: 该字段是第几个字段
input_index: 该字段属于那个分组,
offset: 该字段的偏移量
需要注意的是,基于优化考虑,DPDK在内部实现时对输入的N元组数据进行4字节分组(group),
了解了ACL字段之后, 我们就可以对具体的N元组定义规则了。以IPv4的5元组加MAC地址为例:

enum {
	PROTO_FIELD_IPV4,
	DMAC1_FIELD_IPV4,
	DMAC2_FIELD_IPV4,
	SMAC1_FIELD_IPV4,
	SMAC2_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_MAC1,
	RTE_ACL_IPV4VLAN_MAC2,
	RTE_ACL_IPV4VLAN_MAC3,
	//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 = sizeof(struct ether_hdr) + offsetof(struct ipv4_hdr, next_proto_id),
	},

	{
		.type = RTE_ACL_FIELD_TYPE_MASK,
		.size = sizeof(uint32_t),
		.field_index = DMAC1_FIELD_IPV4,
		.input_index = RTE_ACL_IPV4VLAN_MAC1,
		.offset = 0,
	},	
	{
		.type = RTE_ACL_FIELD_TYPE_MASK,
		.size = sizeof(uint16_t),
		.field_index = DMAC2_FIELD_IPV4,
		.input_index = RTE_ACL_IPV4VLAN_MAC2,
		.offset = sizeof(uint32_t),
	},	
	{
		.type = RTE_ACL_FIELD_TYPE_MASK,
		.size = sizeof(uint16_t),
		.field_index = SMAC1_FIELD_IPV4,
		.input_index = RTE_ACL_IPV4VLAN_MAC2,
		.offset = sizeof(uint32_t) + sizeof(uint16_t),
	},	
	{
		.type = RTE_ACL_FIELD_TYPE_MASK,
		.size = sizeof(uint32_t),
		.field_index = SMAC2_FIELD_IPV4,
		.input_index = RTE_ACL_IPV4VLAN_MAC3,
		.offset = sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) ,
	},	
	{
		.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)+sizeof(struct ether_hdr),
	},
	{
		.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)+sizeof(struct ether_hdr),
	},
	{
		.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) +sizeof(struct ether_hdr),
	},
	{
		.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) +
			sizeof(uint16_t)+sizeof(struct ether_hdr),
	},
};

注意事项:
第0个字段要求必须是1字节(size为1),属于第0组(input_field为0),后面每4字节算作一组,所以除了第0个字段以外,其余字段的size必须要能凑成4字节来进行分组

最后,使用宏RTE_ACL_RULE_DEF定义rule结构体:

RTE_ACL_RULE_DEF(acl4_rule, RTE_DIM(ipv4_defs));

该宏的定义如下:

rte_acl.h:
#define	RTE_ACL_RULE_DEF(name, fld_num)	struct name {\
	struct rte_acl_rule_data data;               \
	struct rte_acl_field field[fld_num];         \
}

这样,我们的ipv4规则就被定义为struct acl4_rule。

规则编写
有了规则定义,就可以按照需求编写具体的规则。在实际应用中,分类规则一般是由配置文件等方式按需配置 详细代码可查看 dpdk\software\src\examples\l3fwd-acl 下 的main.c

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;
	//printf("rule: %s,dim = %d\n",s,dim);

	for (i = 0; i != dim; i++, s = NULL) {
		in[i] = strtok_r(s, dlm, &sp);
		if (in[i] == NULL)
			return -EINVAL;
	}
	
	
	rc = parse_mac_addr(in[CB_FLD_DMAC_ADDR],
			&v->field[DMAC1_FIELD_IPV4].value.u32,&v->field[DMAC2_FIELD_IPV4].value.u16);
	v->field[DMAC1_FIELD_IPV4].mask_range.u32 = 32;
	v->field[DMAC2_FIELD_IPV4].mask_range.u16 = 16;
	if (rc != 0) 
	{
		printf("failed to read DMAC address/mask: %s\n",in[CB_FLD_DMAC_ADDR]);
		return rc;
	}
	rc = parse_Smac_addr(in[CB_FLD_SMAC_ADDR],
			&v->field[SMAC2_FIELD_IPV4].value.u32,&v->field[SMAC1_FIELD_IPV4].value.u16);
	v->field[SMAC2_FIELD_IPV4].mask_range.u32 = 32;
	v->field[SMAC1_FIELD_IPV4].mask_range.u16 = 16;
	if (rc != 0) {
			printf("failed to read SMAC address/mask: %s\n",
			in[CB_FLD_SMAC_ADDR]);
		return rc;
	}
	
	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) {
			printf("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) {
		printf("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;
}

下面解释一下编写方法。这里的struct acl4_rule就是刚刚通过RTE_ACL_RULE_DEF定义的规则类型,其中包含两个结构体字段data和field。

data字段的结构体定义为:

struct rte_acl_rule_data {
	uint32_t category_mask; /**< Mask of categories for that rule. */
	int32_t  priority;      /**< Priority for that rule. */
	uint32_t userdata;      /**< Associated with the rule user data. */
};

其中category_mask是类别掩码,32位中每一位代表一个类别,该规则属于哪个类别就将哪一位置1;priority是优先级,值越大表明优先级越高;userdata是用户自定义的一个数值(不能是0),当某个规则匹配成功时,就会返回该规则的userdata。

field字段的结构体定义为:

struct rte_acl_field {
	union rte_acl_field_types value;
	/**< a 1,2,4, or 8 byte value of the field. */
	union rte_acl_field_types mask_range;
	/**<
	 * depending on field type:
	 * mask -> 1.2.3.4/32 value=0x1020304, mask_range=32,
	 * range -> 0 : 65535 value=0, mask_range=65535,
	 * bitmask -> 0x06/0xff value=6, mask_range=0xff.
	 */
};
其中联合体union rte_acl_field_types定义为:

union rte_acl_field_types {
	uint8_t  u8;
	uint16_t u16;
	uint32_t u32;
	uint64_t u64;
};

对应了1,2,4,8四种字段长度。field字段要根据先前定义的字段类型设置其值,具体来说:

执行匹配
要执行匹配,首先需要创建规则上下文,相关代码如下:

struct rte_acl_ctx * acx;
int ret;
struct rte_acl_param prm = {
    .name = "ACL_example",
    .socket_id = SOCKET_ID_ANY,
    .rule_size = RTE_ACL_RULE_SZ(RTE_DIM(ipv4_defs)),
    .max_rule_num = 8,
};
if ((acx = rte_acl_create(&prm)) == NULL) {
    /* handle context create failure. */
}

接下来需要在刚刚创建的空上下文中添加规则:

ret = rte_acl_add_rules(acx, acl_rules, RTE_DIM(acl_rules));
if (ret != 0) {
   /* handle error at adding ACL rules. */
}

这样,就把的规则加入到上下文中。
然后需要构建运行时结构:

struct rte_acl_config cfg;
cfg.num_categories = 2;
cfg.num_fields = RTE_DIM(ipv4_defs);
cfg.max_size = 0x800000;
memcpy(cfg.defs, ipv4_defs, sizeof (ipv4_defs));
ret = rte_acl_build(acx, &cfg);
if (ret != 0 && ret != -ERANGE) {
     /* handle error at build runtime structures for ACL context. */
} else if (ret == -ERANGE) {
   cfg.max_size = 0;
   ret = rte_acl_build(acx, &cfg);
}

这里的cfg是构建运行时结构的配置,其定义如下:

struct rte_acl_config {
	uint32_t num_categories; /**< Number of categories to build with. */
	uint32_t num_fields;     /**< Number of field definitions. */
	struct rte_acl_field_def defs[RTE_ACL_MAX_FIELDS];
	/**< array of field definitions. */
	size_t max_size;
	/**< max memory limit for internal run-time structures. */
};

其他字段都很好理解,主要注意其中的max_size字段,该字段用来规定ACL运行时的内存上限,但是如果内存过小会增加分类时间,因此这是一个折中的设置选项。如果设置为0则会尽量减少内存占用,但不会保证使用限度。

至此执行规则的上下文创建完毕。对于一组输入数据,只要通过函数rte_acl_classify即可执行匹配,该函数原型如下:

int rte_acl_classify(const struct rte_acl_ctx *ctx,
		 const uint8_t **data,
		 uint32_t *results, uint32_t num,
		 uint32_t categories);

其中ctx即构建好的上下文了;data可以理解为一个指针数组:const uint8_t *data[INPUT_COUNT],其每一个元素指针指向一条 待匹配二进制数据。注意要进行匹配的二进制数据必须是按照网络字节序存储的;num表示data中的数据数量;categories是类别的数量;results是用来保存结果的数组,由于data中每一条数据都有categories个匹配结果,因此results的大小至少为categories * num。

以下是我基于5元组基础上增加MAC地址匹配 的源码

/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(c) 2010-2016 Intel Corporation
 */

#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_eal.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_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_DP_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 * nb_rxd +	\
	nb_ports * nb_lcores * MAX_PKT_BURST +	\
	nb_ports * n_tx_queue * nb_txd +	\
	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 1024
#define RTE_TEST_TX_DESC_DEFAULT 1024
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 {
	uint16_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 {
	uint16_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,
		.offloads = (DEV_RX_OFFLOAD_CRC_STRIP |
			     DEV_RX_OFFLOAD_CHECKSUM),
	},
	.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, uint16_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 uint16_t_to_char(ip, a, b) do {\
			*a = (unsigned char)(ip >> 8 & 0xff);\
			*b = (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 *, 0)	
	//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,
	DMAC1_FIELD_IPV4,
	DMAC2_FIELD_IPV4,
	SMAC1_FIELD_IPV4,
	SMAC2_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_MAC1,
	RTE_ACL_IPV4VLAN_MAC2,
	RTE_ACL_IPV4VLAN_MAC3,
	//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 = sizeof(struct ether_hdr) + offsetof(struct ipv4_hdr, next_proto_id),
	},

	{
		.type = RTE_ACL_FIELD_TYPE_MASK,
		.size = sizeof(uint32_t),
		.field_index = DMAC1_FIELD_IPV4,
		.input_index = RTE_ACL_IPV4VLAN_MAC1,
		.offset = 0,
	},	
	{
		.type = RTE_ACL_FIELD_TYPE_MASK,
		.size = sizeof(uint16_t),
		.field_index = DMAC2_FIELD_IPV4,
		.input_index = RTE_ACL_IPV4VLAN_MAC2,
		.offset = sizeof(uint32_t),
	},	
	{
		.type = RTE_ACL_FIELD_TYPE_MASK,
		.size = sizeof(uint16_t),
		.field_index = SMAC1_FIELD_IPV4,
		.input_index = RTE_ACL_IPV4VLAN_MAC2,
		.offset = sizeof(uint32_t) + sizeof(uint16_t),
	},	
	{
		.type = RTE_ACL_FIELD_TYPE_MASK,
		.size = sizeof(uint32_t),
		.field_index = SMAC2_FIELD_IPV4,
		.input_index = RTE_ACL_IPV4VLAN_MAC3,
		.offset = sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) ,
	},	
	{
		.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)+sizeof(struct ether_hdr),
	},
	{
		.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)+sizeof(struct ether_hdr),
	},
	{
		.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) +sizeof(struct ether_hdr),
	},
	{
		.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) +
			sizeof(uint16_t)+sizeof(struct ether_hdr),
	},
};

#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 {
	DMAC1_FIELD_IPV6,
	DMAC2_FIELD_IPV6,
	SMAC1_FIELD_IPV6,
	SMAC2_FIELD_IPV6,
	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_MASK,
		.size = sizeof(uint32_t),
		.field_index = DMAC1_FIELD_IPV6,
		.input_index = DMAC1_FIELD_IPV6,
		.offset = 0,
	},	
	{
		.type = RTE_ACL_FIELD_TYPE_MASK,
		.size = sizeof(uint16_t),
		.field_index = DMAC2_FIELD_IPV6,
		.input_index = DMAC2_FIELD_IPV6,
		.offset = sizeof(uint32_t),
	},	
	{
		.type = RTE_ACL_FIELD_TYPE_MASK,
		.size = sizeof(uint16_t),
		.field_index = SMAC1_FIELD_IPV6,
		.input_index = SMAC1_FIELD_IPV6,
		.offset = sizeof(uint32_t) + sizeof(uint16_t),
	},	
	{
		.type = RTE_ACL_FIELD_TYPE_MASK,
		.size = sizeof(uint32_t),
		.field_index = SMAC2_FIELD_IPV6,
		.input_index = SMAC2_FIELD_IPV6,
		.offset = sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t),
	},	


	{
		.type = RTE_ACL_FIELD_TYPE_BITMASK,
		.size = sizeof(uint8_t),
		.field_index = PROTO_FIELD_IPV6,
		.input_index = PROTO_FIELD_IPV6,
		.offset = sizeof(struct ether_hdr)+offsetof(struct ipv6_hdr, proto),
	},
	{
		.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)+sizeof(struct ether_hdr),
	},
	{
		.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) + sizeof(uint32_t)+sizeof(struct ether_hdr),
	},
	{
		.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) + 2 * sizeof(uint32_t)+sizeof(struct ether_hdr),
	},
	{
		.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) + 3 * sizeof(uint32_t)+sizeof(struct ether_hdr),
	},
	{
		.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)+sizeof(struct ether_hdr),
	},
	{
		.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) + sizeof(uint32_t)+sizeof(struct ether_hdr),
	},
	{
		.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) + 2 * sizeof(uint32_t)+sizeof(struct ether_hdr),
	},
	{
		.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) + 3 * sizeof(uint32_t)+sizeof(struct ether_hdr),
	},
	{
		.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) + sizeof(struct ether_hdr),
	},
	{
		.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) + sizeof(uint16_t)+sizeof(struct ether_hdr),
	},
};

enum {
	CB_FLD_DMAC_ADDR,
	CB_FLD_SMAC_ADDR,
	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,e,f;
	
	uint32_t_to_char(rule->field[DMAC1_FIELD_IPV4].value.u32,
			&a, &b, &c, &d);

	uint16_t_to_char(rule->field[DMAC2_FIELD_IPV4].value.u16,
			&e, &f);
	printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x/%u ", a, b, c, d,e,f,
		rule->field[DMAC1_FIELD_IPV4].mask_range.u32+rule->field[DMAC2_FIELD_IPV4].mask_range.u16);

	uint16_t_to_char(rule->field[SMAC1_FIELD_IPV4].value.u16,
			&e, &f);

	uint32_t_to_char(rule->field[SMAC2_FIELD_IPV4].value.u32,
			&a, &b, &c, &d);
	printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x/%u ", e,f,a, b, c, d,
		rule->field[SMAC2_FIELD_IPV4].mask_range.u32+rule->field[SMAC1_FIELD_IPV4].mask_range.u16);

	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,e,f;

	uint32_t_to_char(rule->field[DMAC1_FIELD_IPV6].value.u32,
			&a, &b, &c, &d);

	uint16_t_to_char(rule->field[DMAC2_FIELD_IPV6].value.u16,
			&e, &f);
	printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ", a, b, c, d,e,f);

	uint16_t_to_char(rule->field[SMAC1_FIELD_IPV6].value.u16,
			&e, &f);

	uint32_t_to_char(rule->field[SMAC2_FIELD_IPV6].value.u32,
			&a, &b, &c, &d);
	printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ", e,f,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);
}


static int parse_mac_addr(const char *in, uint32_t *addr,uint16_t *addr_L)
{

	uint8_t a, b, c, d,e,f;

	GET_CB_FIELD(in, a, 16, UINT8_MAX, ':');
	GET_CB_FIELD(in, b, 16, UINT8_MAX, ':');
	GET_CB_FIELD(in, c, 16, UINT8_MAX, ':');
	GET_CB_FIELD(in, d, 16, UINT8_MAX, ':');
	GET_CB_FIELD(in, e, 16, UINT8_MAX, ':');
	GET_CB_FIELD(in, f, 16, UINT8_MAX, 0);
	//GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
	addr[0] = IPv4(a, b, c, d);
	addr_L[0] =  ((uint16_t)(((e) & 0xff) << 8)  | ((f) & 0xff));	
	//mask_len[0] = m;
	return 0;

}
static int parse_Smac_addr(const char *in, uint32_t *addr,uint16_t *addr_L)
{

	uint8_t a, b, c, d,e,f;

	GET_CB_FIELD(in, a, 16, UINT8_MAX, ':');
	GET_CB_FIELD(in, b, 16, UINT8_MAX, ':');
	GET_CB_FIELD(in, c, 16, UINT8_MAX, ':');
	GET_CB_FIELD(in, d, 16, UINT8_MAX, ':');
	GET_CB_FIELD(in, e, 16, UINT8_MAX, ':');
	GET_CB_FIELD(in, f, 16, UINT8_MAX, 0);
	//GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
	addr[0] = IPv4(c, d ,e ,f);
	addr_L[0] =  ((uint16_t)(((a) & 0xff) << 8)  | ((b) & 0xff));
	//mask_len[0] = m;

	return 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;
}

#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];
	struct ether_hdr *ether;
	unsigned char a, b, c, d;

	if (RTE_ETH_IS_IPV4_HDR(pkt->packet_type)) {
		ipv4_hdr = rte_pktmbuf_mtod_offset(pkt, struct ipv4_hdr *,
						   sizeof(struct ether_hdr));
		
		ether = rte_pktmbuf_mtod_offset(pkt, struct ether_hdr *,
								   0);

		/* 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->data_ipv4[acl->num_ipv4]  = rte_pktmbuf_mtod_offset(pkt, uint8_t  *, 0);
			acl->m_ipv4[(acl->num_ipv4)++] = pkt;
			ether = rte_pktmbuf_mtod_offset(pkt, struct ether_hdr *, 0);
		/*	
		printf("MAC %.2x:%.2x:%.2x:%.2x:%.2x:%.2x \n", ether->d_addr.addr_bytes[0], ether->d_addr.addr_bytes[1],
				ether->d_addr.addr_bytes[2], ether->d_addr.addr_bytes[3],ether->d_addr.addr_bytes[4],
				ether->d_addr.addr_bytes[5]);
		uint32_t_to_char(ipv4_hdr->src_addr,
				&a, &b, &c, &d);
		printf("src %hhu.%hhu.%hhu.%hhu \n", d, c, b, a);
		uint32_t_to_char(ipv4_hdr->dst_addr,
				&a, &b, &c, &d);
		printf("dst %hhu.%hhu.%hhu.%hhu \n", d, c, b, a);
		*/
				
		} 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_mac_addr(in[CB_FLD_DMAC_ADDR],
			&v->field[DMAC1_FIELD_IPV6].value.u32,&v->field[DMAC2_FIELD_IPV6].value.u16);
	if (rc != 0) 
	{
		printf("failed to read DMAC address/mask: %s\n",in[CB_FLD_DMAC_ADDR]);
		return rc;
	}
	rc = parse_Smac_addr(in[CB_FLD_SMAC_ADDR],
			&v->field[SMAC2_FIELD_IPV6].value.u32,&v->field[SMAC1_FIELD_IPV6].value.u16);
	if (rc != 0) {
			printf("failed to read SMAC address/mask: %s\n",
			in[CB_FLD_SMAC_ADDR]);
		return rc;
	}

	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;
	//printf("rule: %s,dim = %d\n",s,dim);

	for (i = 0; i != dim; i++, s = NULL) {
		in[i] = strtok_r(s, dlm, &sp);
		if (in[i] == NULL)
			return -EINVAL;
	}
	
	
	rc = parse_mac_addr(in[CB_FLD_DMAC_ADDR],
			&v->field[DMAC1_FIELD_IPV4].value.u32,&v->field[DMAC2_FIELD_IPV4].value.u16);
	v->field[DMAC1_FIELD_IPV4].mask_range.u32 = 32;
	v->field[DMAC2_FIELD_IPV4].mask_range.u16 = 16;
	if (rc != 0) 
	{
		printf("failed to read DMAC address/mask: %s\n",in[CB_FLD_DMAC_ADDR]);
		return rc;
	}
	rc = parse_Smac_addr(in[CB_FLD_SMAC_ADDR],
			&v->field[SMAC2_FIELD_IPV4].value.u32,&v->field[SMAC1_FIELD_IPV4].value.u16);
	v->field[SMAC2_FIELD_IPV4].mask_range.u32 = 32;
	v->field[SMAC1_FIELD_IPV4].mask_range.u16 = 16;
	if (rc != 0) {
			printf("failed to read SMAC address/mask: %s\n",
			in[CB_FLD_SMAC_ADDR]);
		return rc;
	}
	
	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) {
			printf("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) {
		printf("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;
	int val;

	if (fh == NULL)
		printf("%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)
		printf("Not find any route entries in %s!\n",
				rule_path);

	val = fseek(fh, 0, SEEK_SET);
	if (val < 0) {
		printf("%s: File seek operation failed\n",
			__func__);
	}

	acl_rules = calloc(acl_num, rule_size);

	if (NULL == acl_rules)
		printf("%s: failed to malloc memory\n",
			__func__);

	route_rules = calloc(route_num, rule_size);

	if (NULL == route_rules)
		printf("%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
			printf(
				"%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)
			printf(
				"%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)
				printf(
					"%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);
	printf("setup_acl ipv6 =%d !!\n",ipv6);
	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));
	
	printf("setup_acl rte_acl_build !!\n");
	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)
		printf("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)
		printf("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)
		printf("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, uint16_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;
	uint16_t portid;
	uint8_t queueid;
	struct lcore_conf *qconf;
	int socketid;
	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1)
			/ US_PER_S * BURST_TX_DRAIN_US;

	prev_tsc = 0;
	lcore_id = rte_lcore_id();
	qconf = &lcore_conf[lcore_id];
	socketid = rte_lcore_to_socket_id(lcore_id);

	if (qconf->n_rx_queue == 0) {
		RTE_LOG(INFO, L3FWD, "lcore %u has nothing to do\n", lcore_id);
		return 0;
	}

	RTE_LOG(INFO, L3FWD, "entering main loop on lcore %u\n", lcore_id);

	for (i = 0; i < qconf->n_rx_queue; i++) {

		portid = qconf->rx_queue_list[i].port_id;
		queueid = qconf->rx_queue_list[i].queue_id;
		RTE_LOG(INFO, L3FWD,
			" -- lcoreid=%u portid=%u rxqueueid=%hhu\n",
			lcore_id, portid, queueid);
	}

	while (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(void)
{
	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 (!rte_eth_dev_is_valid_port(portid)) {
			printf("port %u is not present on the board\n", portid);
			return -1;
		}
	}
	return 0;
}

static uint8_t
get_port_n_rx_queues(const uint16_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.offloads |=
						DEV_RX_OFFLOAD_JUMBO_FRAME;
				port_conf.txmode.offloads |=
						DEV_TX_OFFLOAD_MULTI_SEGS;

				/*
				 * 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(uint32_t port_mask)
{
#define CHECK_INTERVAL 100 /* 100ms */
#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
	uint16_t portid;
	uint8_t 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;
		RTE_ETH_FOREACH_DEV(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",
						portid, link.link_speed,
				(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
					("full-duplex") : ("half-duplex\n"));
				else
					printf("Port %d Link Down\n", 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;
	uint16_t portid;
	uint8_t 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_avail();

	if (check_port_config() < 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 */
	RTE_ETH_FOREACH_DEV(portid) {
		struct rte_eth_conf local_port_conf = port_conf;

		/* 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);
		rte_eth_dev_info_get(portid, &dev_info);
		if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
			local_port_conf.txmode.offloads |=
				DEV_TX_OFFLOAD_MBUF_FAST_FREE;

		local_port_conf.rx_adv_conf.rss_conf.rss_hf &=
			dev_info.flow_type_rss_offloads;
		if (local_port_conf.rx_adv_conf.rss_conf.rss_hf !=
				port_conf.rx_adv_conf.rss_conf.rss_hf) {
			printf("Port %u modified RSS hash function based on hardware support,"
				"requested:%#"PRIx64" configured:%#"PRIx64"\n",
				portid,
				port_conf.rx_adv_conf.rss_conf.rss_hf,
				local_port_conf.rx_adv_conf.rss_conf.rss_hf);
		}

		ret = rte_eth_dev_configure(portid, nb_rx_queue,
					(uint16_t)n_tx_queue, &local_port_conf);
		if (ret < 0)
			rte_exit(EXIT_FAILURE,
				"Cannot configure device: err=%d, port=%d\n",
				ret, portid);

		ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd,
						       &nb_txd);
		if (ret < 0)
			rte_exit(EXIT_FAILURE,
				"rte_eth_dev_adjust_nb_rx_tx_desc: 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;
			txconf->offloads = local_port_conf.txmode.offloads;
			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) {
			struct rte_eth_dev *dev;
			struct rte_eth_conf *conf;
			struct rte_eth_rxconf rxq_conf;

			portid = qconf->rx_queue_list[queue].port_id;
			queueid = qconf->rx_queue_list[queue].queue_id;
			dev = &rte_eth_devices[portid];
			conf = &dev->data->dev_conf;

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

			rte_eth_dev_info_get(portid, &dev_info);
			rxq_conf = dev_info.default_rxconf;
			rxq_conf.offloads = conf->rxmode.offloads;
			ret = rte_eth_rx_queue_setup(portid, queueid, nb_rxd,
					socketid, &rxq_conf,
					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 */
	RTE_ETH_FOREACH_DEV(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(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;
}


代码的运行
因为是配置文件形式来添加规则 ,所以需要先创建规则文件

touch rule_ipv4.db
touch rule_ipv6.db

R01:02:03:04:05:06 07:08:00:01:03:02 192.168.0.2/32 192.168.0.10/32 0 : 65535 0 : 65535 0x00/0x00 0
R01:02:03:04:05:06 07:08:00:01:03:02 0:0:0:0:0:0:0:0/0 0:0:0:0:0:0:0:0/0 0 : 65535 0 : 65535 0x00/0x00 0

./l3fwd-acl -c 0xff -n 4 – -P -p 0x3 --config="(0, 0, 1),(1, 0, 0)" --rule_ipv4="/rule_ipv4.db" --rule_ipv6="/rule_ipv6.db"

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值