前言
Flow Classify示例应用程序基于转发应用程序的简单框架示例。 它旨在演示使用Flow Classify库API的DPDK转发应用程序的基本组件
flow_classify例子对于DPDK的学习具有很重要的意义,是比较重要的章节。有点类似于linux网络中的iptables功能,也有点类似于我们在linux内核中开发的防火墙功能。我们可以使用flow模块对数据包进行统计,丢弃等基本的操作。
程序代码
ACL介绍
首先该例程中主要是面向的对象是IP流量中的五元组信息。即源ip地址,目的ip地址,源端口号,目的端口号,协议号。学过linux网络的都知道,该五元组可以决定一个数据包的唯一性。因为我们操作的是IP流量五元组,所以这里使用的ACL classify算法。ACL规则主要面向的是IP流量中的五元组信息。
关于classify算法可以参考DPDK ACL算法介绍
DPDK报文分类与访问控制
dpdk提供了一个访问控制库,提供了基于一系列分类规则对接收到的报文进行分类的能力。
ACL库用来在一系列规则上执行N元组查找,可以实现多个分类和对每个分类查找最佳匹配(最高优先级)。
ACL库的api提供如下基本操作:
创建一个新的访问控制(AC)环境实例(context)添加规则到这个环境实例为这个实例里所有的规则,创建必需的运行时结构体来指针报文分类执行接收报文分类删除AC环境实例和对应的运行时结构体,并释放内存
该例程是对官方例程flow_classify补充说明flow_classify链接地址
程序
#include <stdint.h>
#include <inttypes.h>
#include <getopt.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_cycles.h>
#include <rte_lcore.h>
#include <rte_mbuf.h>
#include <rte_flow.h>
#include <rte_flow_classify.h>
#include <rte_table_acl.h>
#define RX_RING_SIZE 1024
#define TX_RING_SIZE 1024
#define NUM_MBUFS 8191
#define MBUF_CACHE_SIZE 250
#define BURST_SIZE 32
#define MAX_NUM_CLASSIFY 30
#define FLOW_CLASSIFY_MAX_RULE_NUM 91
#define FLOW_CLASSIFY_MAX_PRIORITY 8
#define FLOW_CLASSIFIER_NAME_SIZE 64
#define COMMENT_LEAD_CHAR ('#')
#define OPTION_RULE_IPV4 "rule_ipv4"
#define RTE_LOGTYPE_FLOW_CLASSIFY RTE_LOGTYPE_USER3
#define flow_classify_log(format, ...) \
RTE_LOG(ERR, FLOW_CLASSIFY, 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)
enum {
CB_FLD_SRC_ADDR
,
CB_FLD_DST_ADDR
,
CB_FLD_SRC_PORT
,
CB_FLD_SRC_PORT_DLM
,
CB_FLD_SRC_PORT_MASK
,
CB_FLD_DST_PORT
,
CB_FLD_DST_PORT_DLM
,
CB_FLD_DST_PORT_MASK
,
CB_FLD_PROTO
,
CB_FLD_PRIORITY
,
CB_FLD_NUM
,
};
static struct{
const char *rule_ipv4_name
;
} parm_config
;
const char cb_port_delim
[] = ":";
static const struct rte_eth_conf port_conf_default
= {
.rxmode
= {
.max_rx_pkt_len
= ETHER_MAX_LEN
,
},
};
struct flow_classifier
{
struct rte_flow_classifier
*cls
;
};
struct flow_classifier_acl
{
struct flow_classifier cls
;
} __rte_cache_aligned
;
enum {
PROTO_FIELD_IPV4
,
SRC_FIELD_IPV4
,
DST_FIELD_IPV4
,
SRCP_FIELD_IPV4
,
DSTP_FIELD_IPV4
,
NUM_FIELDS_IPV4
};
enum {
PROTO_INPUT_IPV4
,
SRC_INPUT_IPV4
,
DST_INPUT_IPV4
,
SRCP_DESTP_INPUT_IPV4
};
static 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
= PROTO_INPUT_IPV4
,
.offset
= sizeof(struct ether_hdr
) +
offsetof(struct ipv4_hdr
, next_proto_id
),
},
{
.type
= RTE_ACL_FIELD_TYPE_BITMASK
,
.size
= sizeof(uint32_t
),
.field_index
= SRC_FIELD_IPV4
,
.input_index
= SRC_INPUT_IPV4
,
.offset
= sizeof(struct ether_hdr
) +
offsetof(struct ipv4_hdr
, src_addr
),
},
{
.type
= RTE_ACL_FIELD_TYPE_BITMASK
,
.size
= sizeof(uint32_t
),
.field_index
= DST_FIELD_IPV4
,
.input_index
= DST_INPUT_IPV4
,
.offset
= sizeof(struct ether_hdr
) +
offsetof(struct ipv4_hdr
, dst_addr
),
},
{
.type
= RTE_ACL_FIELD_TYPE_BITMASK
,
.size
= sizeof(uint16_t
),
.field_index
= SRCP_FIELD_IPV4
,
.input_index
= SRCP_DESTP_INPUT_IPV4
,
.offset
= sizeof(struct ether_hdr
) +
sizeof(struct ipv4_hdr
) +
offsetof(struct tcp_hdr
, src_port
),
},
{
.type
= RTE_ACL_FIELD_TYPE_BITMASK
,
.size
= sizeof(uint16_t
),
.field_index
= DSTP_FIELD_IPV4
,
.input_index
= SRCP_DESTP_INPUT_IPV4
,
.offset
= sizeof(struct ether_hdr
) +
sizeof(struct ipv4_hdr
) +
offsetof(struct tcp_hdr
, dst_port
),
},
};
static int num_classify_rules
;
static struct rte_flow_classify_rule
*rules
[MAX_NUM_CLASSIFY
];
static struct rte_flow_classify_ipv4_5tuple_stats ntuple_stats
;
static struct rte_flow_classify_stats classify_stats
= {
.stats
= (void **)&ntuple_stats
};
static struct rte_flow_item eth_item
= { RTE_FLOW_ITEM_TYPE_ETH
,
0, 0, 0 };
static struct rte_flow_item end_item
= { RTE_FLOW_ITEM_TYPE_END
,
0, 0, 0 };
struct rte_flow_query_count count
= {
.reset
= 1,
.hits_set
= 1,
.bytes_set
= 1,
.hits
= 0,
.bytes
= 0,
};
static struct rte_flow_action count_action
= { RTE_FLOW_ACTION_TYPE_COUNT
,
&count
};
static struct rte_flow_action end_action
= { RTE_FLOW_ACTION_TYPE_END
, 0};
static struct rte_flow_action actions
[2];
static struct rte_flow_attr attr
;
static inline int
port_init(uint8_t port
, struct rte_mempool
*mbuf_pool
)
{
struct rte_eth_conf port_conf
= port_conf_default
;
struct ether_addr addr
;
const uint16_t rx_rings
= 1, tx_rings
= 1;
int retval
;
uint16_t q
;
struct rte_eth_dev_info dev_info
;
struct rte_eth_txconf txconf
;
if (!rte_eth_dev_is_valid_port(port
))
return -1;
rte_eth_dev_info_get(port
, &dev_info
);
if (dev_info
.tx_offload_capa
& DEV_TX_OFFLOAD_MBUF_FAST_FREE
)
port_conf
.txmode
.offloads
|=
DEV_TX_OFFLOAD_MBUF_FAST_FREE
;
retval
= rte_eth_dev_configure(port
, rx_rings
, tx_rings
, &port_conf
);
if (retval
!= 0)
return retval
;
for (q
= 0; q
< rx_rings
; q
++) {
retval
= rte_eth_rx_queue_setup(port
, q
, RX_RING_SIZE
,
rte_eth_dev_socket_id(port
), NULL, mbuf_pool
);
if (retval
< 0)
return retval
;
}
txconf
= dev_info
.default_txconf
;
txconf
.offloads
= port_conf
.txmode
.offloads
;
for (q
= 0; q
< tx_rings
; q
++) {
retval
= rte_eth_tx_queue_setup(port
, q
, TX_RING_SIZE
,
rte_eth_dev_socket_id(port
), &txconf
);
if (retval
< 0)
return retval
;
}
retval
= rte_eth_dev_start(port
);
if (retval
< 0)
return retval
;
rte_eth_macaddr_get(port
, &addr
);
printf("Port %u MAC: %02" PRIx8
" %02" PRIx8
" %02" PRIx8
" %02" PRIx8
" %02" PRIx8
" %02" PRIx8
"\n",
port
,
addr
.addr_bytes
[0], addr
.addr_bytes
[1],
addr
.addr_bytes
[2], addr
.addr_bytes
[3],
addr
.addr_bytes
[4], addr
.addr_bytes
[5]);
rte_eth_promiscuous_enable(port
);
return 0;
}
static __attribute__((noreturn
)) void
lcore_main(struct flow_classifier
*cls_app
)
{
uint16_t port
;
int ret
;
int i
= 0;
ret
= rte_flow_classify_table_entry_delete(cls_app
->cls
,
rules
[7]);
if (ret
)
printf("table_entry_delete failed [7] %d\n\n", ret
);
else
printf("table_entry_delete succeeded [7]\n\n");
RTE_ETH_FOREACH_DEV(port
)
if (rte_eth_dev_socket_id(port
) > 0 &&
rte_eth_dev_socket_id(port
) != (int)rte_socket_id()) {
printf("\n\n");
printf("WARNING: port %u is on remote NUMA node\n",
port
);
printf("to polling thread.\n");
printf("Performance will not be optimal.\n");
}
printf("\nCore %u forwarding packets. ", rte_lcore_id());
printf("[Ctrl+C to quit]\n");
for (;;) {
RTE_ETH_FOREACH_DEV(port
) {
struct rte_mbuf
*bufs
[BURST_SIZE
];
const uint16_t nb_rx
= rte_eth_rx_burst(port
, 0,
bufs
, BURST_SIZE
);
if (unlikely(nb_rx
== 0))
continue;
for (i
= 0; i
< MAX_NUM_CLASSIFY
; i
++) {
if (rules
[i
]) {
ret
= rte_flow_classifier_query(
cls_app
->cls
,
bufs
, nb_rx
, rules
[i
],
&classify_stats
);
if (ret
)
printf(
"rule [%d] query failed ret [%d]\n\n",
i
, ret
);
else {
printf(
"rule[%d] count=%"PRIu64
"\n",
i
, ntuple_stats
.counter1
);
printf("proto = %d\n",
ntuple_stats
.ipv4_5tuple
.proto
);
}
}
}
const uint16_t nb_tx
= rte_eth_tx_burst(port
^ 1, 0,
bufs
, nb_rx
);
if (unlikely(nb_tx
< nb_rx
)) {
uint16_t buf
;
for (buf
= nb_tx
; buf
< nb_rx
; buf
++)
rte_pktmbuf_free(bufs
[buf
]);
}
}
}
}
static int
get_cb_field(char **in
, uint32_t
*fd
, int base
, unsigned long lim
,
char dlm
)
{
unsigned long val
;
char *end
;
errno
= 0;
val
= strtoul(*in
, &end
, base
);
if (errno
!= 0 || end
[0] != dlm
|| val
> lim
)
return -EINVAL
;
*fd
= (uint32_t
)val
;
*in
= end
+ 1;
return 0;
}
static int
parse_ipv4_net(char *in
, uint32_t
*addr
, uint32_t
*mask_len
)
{
uint32_t a
, b
, c
, d
, m
;
if (get_cb_field(&in
, &a
, 0, UINT8_MAX
, '.'))
return -EINVAL
;
if (get_cb_field(&in
, &b
, 0, UINT8_MAX
, '.'))
return -EINVAL
;
if (get_cb_field(&in
, &c
, 0, UINT8_MAX
, '.'))
return -EINVAL
;
if (get_cb_field(&in
, &d
, 0, UINT8_MAX
, '/'))
return -EINVAL
;
if (get_cb_field(&in
, &m
, 0, sizeof(uint32_t
) * CHAR_BIT
, 0))
return -EINVAL
;
addr
[0] = IPv4(a
, b
, c
, d
);
mask_len
[0] = m
;
return 0;
}
static int
parse_ipv4_5tuple_rule(char *str
, struct rte_eth_ntuple_filter
*ntuple_filter
)
{
int i
, ret
;
char *s
, *sp
, *in
[CB_FLD_NUM
];
static const char *dlm
= " \t\n";
int dim
= CB_FLD_NUM
;
uint32_t temp
;
s
= str
;
for (i
= 0; i
!= dim
; i
++, s
= NULL) {
in
[i
] = strtok_r(s
, dlm
, &sp
);
if (in
[i
] == NULL)
return -EINVAL
;
printf("============ %s\n", in
[i
]);
}
ret
= parse_ipv4_net(in
[CB_FLD_SRC_ADDR
],
&ntuple_filter
->src_ip
,
&ntuple_filter
->src_ip_mask
);
if (ret
!= 0) {
flow_classify_log("failed to read source address/mask: %s\n",
in
[CB_FLD_SRC_ADDR
]);
return ret
;
}
ret
= parse_ipv4_net(in
[CB_FLD_DST_ADDR
],
&ntuple_filter
->dst_ip
,
&ntuple_filter
->dst_ip_mask
);
if (ret
!= 0) {
flow_classify_log("failed to read source address/mask: %s\n",
in
[CB_FLD_DST_ADDR
]);
return ret
;
}
if (get_cb_field(&in
[CB_FLD_SRC_PORT
], &temp
, 0, UINT16_MAX
, 0))
return -EINVAL
;
ntuple_filter
->src_port
= (uint16_t
)temp
;
if (strncmp(in
[CB_FLD_SRC_PORT_DLM
], cb_port_delim
,
sizeof(cb_port_delim
)) != 0)
return -EINVAL
;
if (get_cb_field(&in
[CB_FLD_SRC_PORT_MASK
], &temp
, 0, UINT16_MAX
, 0))
return -EINVAL
;
ntuple_filter
->src_port_mask
= (uint16_t
)temp
;
if (get_cb_field(&in
[CB_FLD_DST_PORT
], &temp
, 0, UINT16_MAX
, 0))
return -EINVAL
;
ntuple_filter
->dst_port
= (uint16_t
)temp
;
if (strncmp(in
[CB_FLD_DST_PORT_DLM
], cb_port_delim
,
sizeof(cb_port_delim
)) != 0)
return -EINVAL
;
if (get_cb_field(&in
[CB_FLD_DST_PORT_MASK
], &temp
, 0, UINT16_MAX
, 0))
return -EINVAL
;
ntuple_filter
->dst_port_mask
= (uint16_t
)temp
;
if (get_cb_field(&in
[CB_FLD_PROTO
], &temp
, 0, UINT8_MAX
, '/'))
return -EINVAL
;
ntuple_filter
->proto
= (uint8_t
)temp
;
if (get_cb_field(&in
[CB_FLD_PROTO
], &temp
, 0, UINT8_MAX
, 0))
return -EINVAL
;
ntuple_filter
->proto_mask
= (uint8_t
)temp
;
if (get_cb_field(&in
[CB_FLD_PRIORITY
], &temp
, 0, UINT16_MAX
, 0))
return -EINVAL
;
ntuple_filter
->priority
= (uint16_t
)temp
;
if (ntuple_filter
->priority
> FLOW_CLASSIFY_MAX_PRIORITY
)
ret
= -EINVAL
;
return ret
;
}
static inline int
is_bypass_line(char *buff
)
{
int i
= 0;
if (buff
[0] == COMMENT_LEAD_CHAR
)
return 1;
while (buff
[i
] != '\0') {
if (!isspace(buff
[i
]))
return 0;
i
++;
}
return 1;
}
static uint32_t
convert_depth_to_bitmask(uint32_t depth_val
)
{
uint32_t bitmask
= 0;
int i
, j
;
for (i
= depth_val
, j
= 0; i
> 0; i
--, j
++)
bitmask
|= (1 << (31 - j
));
return bitmask
;
}
static int
add_classify_rule(struct rte_eth_ntuple_filter
*ntuple_filter
,
struct flow_classifier
*cls_app
)
{
int ret
= -1;
int key_found
;
struct rte_flow_error error
;
struct rte_flow_item_ipv4 ipv4_spec
;
struct rte_flow_item_ipv4 ipv4_mask
;
struct rte_flow_item ipv4_udp_item
;
struct rte_flow_item ipv4_tcp_item
;
struct rte_flow_item ipv4_sctp_item
;
struct rte_flow_item_udp udp_spec
;
struct rte_flow_item_udp udp_mask
;
struct rte_flow_item udp_item
;
struct rte_flow_item_tcp tcp_spec
;
struct rte_flow_item_tcp tcp_mask
;
struct rte_flow_item tcp_item
;
struct rte_flow_item_sctp sctp_spec
;
struct rte_flow_item_sctp sctp_mask
;
struct rte_flow_item sctp_item
;
struct rte_flow_item pattern_ipv4_5tuple
[4];
struct rte_flow_classify_rule
*rule
;
uint8_t ipv4_proto
;
if (num_classify_rules
>= MAX_NUM_CLASSIFY
) {
printf(
"\nINFO: classify rule capacity %d reached\n",
num_classify_rules
);
return ret
;
}
memset(&ipv4_spec
, 0, sizeof(ipv4_spec
));
ipv4_spec
.hdr
.next_proto_id
= ntuple_filter
->proto
;
ipv4_spec
.hdr
.src_addr
= ntuple_filter
->src_ip
;
ipv4_spec
.hdr
.dst_addr
= ntuple_filter
->dst_ip
;
ipv4_proto
= ipv4_spec
.hdr
.next_proto_id
;
memset(&ipv4_mask
, 0, sizeof(ipv4_mask
));
ipv4_mask
.hdr
.next_proto_id
= ntuple_filter
->proto_mask
;
ipv4_mask
.hdr
.src_addr
= ntuple_filter
->src_ip_mask
;
ipv4_mask
.hdr
.src_addr
=
convert_depth_to_bitmask(ipv4_mask
.hdr
.src_addr
);
ipv4_mask
.hdr
.dst_addr
= ntuple_filter
->dst_ip_mask
;
ipv4_mask
.hdr
.dst_addr
=
convert_depth_to_bitmask(ipv4_mask
.hdr
.dst_addr
);
switch (ipv4_proto
) {
case IPPROTO_UDP
:
ipv4_udp_item
.type
= RTE_FLOW_ITEM_TYPE_IPV4
;
ipv4_udp_item
.spec
= &ipv4_spec
;
ipv4_udp_item
.mask
= &ipv4_mask
;
ipv4_udp_item
.last
= NULL;
udp_spec
.hdr
.src_port
= ntuple_filter
->src_port
;
udp_spec
.hdr
.dst_port
= ntuple_filter
->dst_port
;
udp_spec
.hdr
.dgram_len
= 0;
udp_spec
.hdr
.dgram_cksum
= 0;
udp_mask
.hdr
.src_port
= ntuple_filter
->src_port_mask
;
udp_mask
.hdr
.dst_port
= ntuple_filter
->dst_port_mask
;
udp_mask
.hdr
.dgram_len
= 0;
udp_mask
.hdr
.dgram_cksum
= 0;
udp_item
.type
= RTE_FLOW_ITEM_TYPE_UDP
;
udp_item
.spec
= &udp_spec
;
udp_item
.mask
= &udp_mask
;
udp_item
.last
= NULL;
attr
.priority
= ntuple_filter
->priority
;
pattern_ipv4_5tuple
[1] = ipv4_udp_item
;
pattern_ipv4_5tuple
[2] = udp_item
;
break;
case IPPROTO_TCP
:
ipv4_tcp_item
.type
= RTE_FLOW_ITEM_TYPE_IPV4
;
ipv4_tcp_item
.spec
= &ipv4_spec
;
ipv4_tcp_item
.mask
= &ipv4_mask
;
ipv4_tcp_item
.last
= NULL;
memset(&tcp_spec
, 0, sizeof(tcp_spec
));
tcp_spec
.hdr
.src_port
= ntuple_filter
->src_port
;
tcp_spec
.hdr
.dst_port
= ntuple_filter
->dst_port
;
memset(&tcp_mask
, 0, sizeof(tcp_mask
));
tcp_mask
.hdr
.src_port
= ntuple_filter
->src_port_mask
;
tcp_mask
.hdr
.dst_port
= ntuple_filter
->dst_port_mask
;
tcp_item
.type
= RTE_FLOW_ITEM_TYPE_TCP
;
tcp_item
.spec
= &tcp_spec
;
tcp_item
.mask
= &tcp_mask
;
tcp_item
.last
= NULL;
attr
.priority
= ntuple_filter
->priority
;
pattern_ipv4_5tuple
[1] = ipv4_tcp_item
;
pattern_ipv4_5tuple
[2] = tcp_item
;
break;
case IPPROTO_SCTP
:
ipv4_sctp_item
.type
= RTE_FLOW_ITEM_TYPE_IPV4
;
ipv4_sctp_item
.spec
= &ipv4_spec
;
ipv4_sctp_item
.mask
= &ipv4_mask
;
ipv4_sctp_item
.last
= NULL;
sctp_spec
.hdr
.src_port
= ntuple_filter
->src_port
;
sctp_spec
.hdr
.dst_port
= ntuple_filter
->dst_port
;
sctp_spec
.hdr
.cksum
= 0;
sctp_spec
.hdr
.tag
= 0;
sctp_mask
.hdr
.src_port
= ntuple_filter
->src_port_mask
;
sctp_mask
.hdr
.dst_port
= ntuple_filter
->dst_port_mask
;
sctp_mask
.hdr
.cksum
= 0;
sctp_mask
.hdr
.tag
= 0;
sctp_item
.type
= RTE_FLOW_ITEM_TYPE_SCTP
;
sctp_item
.spec
= &sctp_spec
;
sctp_item
.mask
= &sctp_mask
;
sctp_item
.last
= NULL;
attr
.priority
= ntuple_filter
->priority
;
pattern_ipv4_5tuple
[1] = ipv4_sctp_item
;
pattern_ipv4_5tuple
[2] = sctp_item
;
break;
default:
return ret
;
}
attr
.ingress
= 1;
pattern_ipv4_5tuple
[0] = eth_item
;
pattern_ipv4_5tuple
[3] = end_item
;
actions
[0] = count_action
;
actions
[1] = end_action
;
ret
= rte_flow_classify_validate(cls_app
->cls
, &attr
,
pattern_ipv4_5tuple
, actions
, &error
);
if (ret
) {
printf("table entry validate failed ipv4_proto = %u\n",
ipv4_proto
);
return ret
;
}
rule
= rte_flow_classify_table_entry_add(
cls_app
->cls
, &attr
, pattern_ipv4_5tuple
,
actions
, &key_found
, &error
);
if (rule
== NULL) {
printf("table entry add failed ipv4_proto = %u\n",
ipv4_proto
);
ret
= -1;
return ret
;
}
rules
[num_classify_rules
] = rule
;
num_classify_rules
++;
return 0;
}
static int
add_rules(const char *rule_path
, struct flow_classifier
*cls_app
)
{
FILE
*fh
;
char buff
[LINE_MAX
];
unsigned int i
= 0;
unsigned int total_num
= 0;
struct rte_eth_ntuple_filter ntuple_filter
;
int ret
;
fh
= fopen(rule_path
, "rb");
if (fh
== NULL)
rte_exit(EXIT_FAILURE
, "%s: fopen %s failed\n", __func__,
rule_path
);
ret
= fseek(fh
, 0, SEEK_SET);
if (ret
)
rte_exit(EXIT_FAILURE
, "%s: fseek %d failed\n", __func__,
ret
);
i
= 0;
while (fgets(buff
, LINE_MAX
, fh
) != NULL) {
if (is_bypass_line(buff
))
continue;
if (total_num
>= FLOW_CLASSIFY_MAX_RULE_NUM
- 1) {
printf("\nINFO: classify rule capacity %d reached\n",
total_num
);
break;
}
if (parse_ipv4_5tuple_rule(buff
, &ntuple_filter
) != 0)
rte_exit(EXIT_FAILURE
,
"%s Line %u: parse rules error\n",
rule_path
, i
);
if (add_classify_rule(&ntuple_filter
, cls_app
) != 0)
rte_exit(EXIT_FAILURE
, "add rule error\n");
total_num
++;
}
fclose(fh
);
return 0;
}
static void
print_usage(const char *prgname
)
{
printf("%s usage:\n", prgname
);
printf("[EAL options] -- --"OPTION_RULE_IPV4
"=FILE: ");
printf("specify the ipv4 rules file.\n");
printf("Each rule occupies one line in the file.\n");
}
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_RULE_IPV4
, 1, 0, 0},
{NULL, 0, 0, 0}
};
argvopt
= argv
;
while ((opt
= getopt_long(argc
, argvopt
, "",
lgopts
, &option_index
)) != EOF) {
switch (opt
) {
case 0:
if (!strncmp(lgopts
[option_index
].name
,
OPTION_RULE_IPV4
,
sizeof(OPTION_RULE_IPV4
)))
parm_config
.rule_ipv4_name
= optarg
;
break;
default:
print_usage(prgname
);
return -1;
}
}
if (optind
>= 0)
argv
[optind
-1] = prgname
;
ret
= optind
-1;
optind
= 1;
return ret
;
}
int
main(int argc
, char *argv
[])
{
struct rte_mempool
*mbuf_pool
;
uint16_t nb_ports
;
uint16_t portid
;
int ret
;
int socket_id
;
struct rte_table_acl_params table_acl_params
;
struct rte_flow_classify_table_params cls_table_params
;
struct flow_classifier
*cls_app
;
struct rte_flow_classifier_params cls_params
;
uint32_t size
;
ret
= rte_eal_init(argc
, argv
);
if (ret
< 0)
rte_exit(EXIT_FAILURE
, "Error with EAL initialization\n");
argc
-= ret
;
argv
+= ret
;
ret
= parse_args(argc
, argv
);
if (ret
< 0)
rte_exit(EXIT_FAILURE
, "Invalid flow_classify parameters\n");
nb_ports
= rte_eth_dev_count_avail();
if (nb_ports
< 2 || (nb_ports
& 1))
rte_exit(EXIT_FAILURE
, "Error: number of ports must be even\n");
mbuf_pool
= rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS
* nb_ports
,
MBUF_CACHE_SIZE
, 0, RTE_MBUF_DEFAULT_BUF_SIZE
, rte_socket_id());
if (mbuf_pool
== NULL)
rte_exit(EXIT_FAILURE
, "Cannot create mbuf pool\n");
RTE_ETH_FOREACH_DEV(portid
)
if (port_init(portid
, mbuf_pool
) != 0)
rte_exit(EXIT_FAILURE
, "Cannot init port %"PRIu8
"\n",
portid
);
if (rte_lcore_count() > 1)
printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
socket_id
= rte_eth_dev_socket_id(0);
size
= RTE_CACHE_LINE_ROUNDUP(sizeof(struct flow_classifier_acl
));
cls_app
= rte_zmalloc(NULL, size
, RTE_CACHE_LINE_SIZE
);
if (cls_app
== NULL)
rte_exit(EXIT_FAILURE
, "Cannot allocate classifier memory\n");
cls_params
.name
= "flow_classifier";
cls_params
.socket_id
= socket_id
;
cls_app
->cls
= rte_flow_classifier_create(&cls_params
);
if (cls_app
->cls
== NULL) {
rte_free(cls_app
);
rte_exit(EXIT_FAILURE
, "Cannot create classifier\n");
}
table_acl_params
.name
= "table_acl_ipv4_5tuple";
table_acl_params
.n_rules
= FLOW_CLASSIFY_MAX_RULE_NUM
;
table_acl_params
.n_rule_fields
= RTE_DIM(ipv4_defs
);
memcpy(table_acl_params
.field_format
, ipv4_defs
, sizeof(ipv4_defs
));
cls_table_params
.ops
= &rte_table_acl_ops
;
cls_table_params
.arg_create
= &table_acl_params
;
cls_table_params
.type
= RTE_FLOW_CLASSIFY_TABLE_ACL_IP4_5TUPLE
;
ret
= rte_flow_classify_table_create(cls_app
->cls
, &cls_table_params
);
if (ret
) {
rte_flow_classifier_free(cls_app
->cls
);
rte_free(cls_app
);
rte_exit(EXIT_FAILURE
, "Failed to create classifier table\n");
}
if (add_rules(parm_config
.rule_ipv4_name
, cls_app
)) {
rte_flow_classifier_free(cls_app
->cls
);
rte_free(cls_app
);
rte_exit(EXIT_FAILURE
, "Failed to add rules\n");
}
lcore_main(cls_app
);
return 0;
}
代码的相关流程和说明请看代码中的注释。
这里简单描述一下该例程实现的功能
首先我们需要至少绑定2的倍数的网卡创建flow分类器然后会读取例程中ipv4_rules_file.txt文件(文件中主要是5元组)。将规则绑定到flow分类器中配置flow分类器中规则的action(该例程中主要是统计的action)配置网卡的属性(配置发送和接收队列)将接收到的数据交给flow分类器。如果匹配则统计数据
这里给出几个网上的参考链接,flow模块比较困难,需要好好的研究。DPDK flow_classify 源码阅读 本篇文章大量参考了上文链接中的文章相关内容。但是为什么还需要写出来呢?因为链接中的文章只是对于程序作了大致的介绍。由于flow模块非常重要,所以我补充了相关知识梳理的流程图(该流程图主要是描述了分类器的创建和添加规则的流程,以及分类器的组成,个人认为这一部分才是核心):
关于运行的结果我会在后面添加。