Ferruh Yigit 1bb4a528c4 ethdev: fix max Rx packet length
There is a confusion on setting max Rx packet length, this patch aims to
clarify it.

'rte_eth_dev_configure()' API accepts max Rx packet size via
'uint32_t max_rx_pkt_len' field of the config struct 'struct
rte_eth_conf'.

Also 'rte_eth_dev_set_mtu()' API can be used to set the MTU, and result
stored into '(struct rte_eth_dev)->data->mtu'.

These two APIs are related but they work in a disconnected way, they
store the set values in different variables which makes hard to figure
out which one to use, also having two different method for a related
functionality is confusing for the users.

Other issues causing confusion is:
* maximum transmission unit (MTU) is payload of the Ethernet frame. And
  'max_rx_pkt_len' is the size of the Ethernet frame. Difference is
  Ethernet frame overhead, and this overhead may be different from
  device to device based on what device supports, like VLAN and QinQ.
* 'max_rx_pkt_len' is only valid when application requested jumbo frame,
  which adds additional confusion and some APIs and PMDs already
  discards this documented behavior.
* For the jumbo frame enabled case, 'max_rx_pkt_len' is an mandatory
  field, this adds configuration complexity for application.

As solution, both APIs gets MTU as parameter, and both saves the result
in same variable '(struct rte_eth_dev)->data->mtu'. For this
'max_rx_pkt_len' updated as 'mtu', and it is always valid independent
from jumbo frame.

For 'rte_eth_dev_configure()', 'dev->data->dev_conf.rxmode.mtu' is user
request and it should be used only within configure function and result
should be stored to '(struct rte_eth_dev)->data->mtu'. After that point
both application and PMD uses MTU from this variable.

When application doesn't provide an MTU during 'rte_eth_dev_configure()'
default 'RTE_ETHER_MTU' value is used.

Additional clarification done on scattered Rx configuration, in
relation to MTU and Rx buffer size.
MTU is used to configure the device for physical Rx/Tx size limitation,
Rx buffer is where to store Rx packets, many PMDs use mbuf data buffer
size as Rx buffer size.
PMDs compare MTU against Rx buffer size to decide enabling scattered Rx
or not. If scattered Rx is not supported by device, MTU bigger than Rx
buffer size should fail.

Signed-off-by: Ferruh Yigit <ferruh.yigit@intel.com>
Acked-by: Ajit Khaparde <ajit.khaparde@broadcom.com>
Acked-by: Somnath Kotur <somnath.kotur@broadcom.com>
Acked-by: Huisong Li <lihuisong@huawei.com>
Acked-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Acked-by: Rosen Xu <rosen.xu@intel.com>
Acked-by: Hyong Youb Kim <hyonkim@cisco.com>
2021-10-18 19:20:20 +02:00

877 lines
22 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2017 Intel Corporation
*/
#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[] = ":";
/* Creation of flow classifier object. 8< */
struct flow_classifier {
struct rte_flow_classifier *cls;
};
struct flow_classifier_acl {
struct flow_classifier cls;
} __rte_cache_aligned;
/* >8 End of creation of flow classifier object. */
/* Creation of ACL table during initialization of application. 8< */
/* ACL field definitions for IPv4 5 tuple rule */
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] = {
/* first input field - always one byte long. */
{
.type = RTE_ACL_FIELD_TYPE_BITMASK,
.size = sizeof(uint8_t),
.field_index = PROTO_FIELD_IPV4,
.input_index = PROTO_INPUT_IPV4,
.offset = sizeof(struct rte_ether_hdr) +
offsetof(struct rte_ipv4_hdr, next_proto_id),
},
/* next input field (IPv4 source address) - 4 consecutive bytes. */
{
/* rte_flow uses a bit mask for IPv4 addresses */
.type = RTE_ACL_FIELD_TYPE_BITMASK,
.size = sizeof(uint32_t),
.field_index = SRC_FIELD_IPV4,
.input_index = SRC_INPUT_IPV4,
.offset = sizeof(struct rte_ether_hdr) +
offsetof(struct rte_ipv4_hdr, src_addr),
},
/* next input field (IPv4 destination address) - 4 consecutive bytes. */
{
/* rte_flow uses a bit mask for IPv4 addresses */
.type = RTE_ACL_FIELD_TYPE_BITMASK,
.size = sizeof(uint32_t),
.field_index = DST_FIELD_IPV4,
.input_index = DST_INPUT_IPV4,
.offset = sizeof(struct rte_ether_hdr) +
offsetof(struct rte_ipv4_hdr, dst_addr),
},
/*
* Next 2 fields (src & dst ports) form 4 consecutive bytes.
* They share the same input index.
*/
{
/* rte_flow uses a bit mask for protocol ports */
.type = RTE_ACL_FIELD_TYPE_BITMASK,
.size = sizeof(uint16_t),
.field_index = SRCP_FIELD_IPV4,
.input_index = SRCP_DESTP_INPUT_IPV4,
.offset = sizeof(struct rte_ether_hdr) +
sizeof(struct rte_ipv4_hdr) +
offsetof(struct rte_tcp_hdr, src_port),
},
{
/* rte_flow uses a bit mask for protocol ports */
.type = RTE_ACL_FIELD_TYPE_BITMASK,
.size = sizeof(uint16_t),
.field_index = DSTP_FIELD_IPV4,
.input_index = SRCP_DESTP_INPUT_IPV4,
.offset = sizeof(struct rte_ether_hdr) +
sizeof(struct rte_ipv4_hdr) +
offsetof(struct rte_tcp_hdr, dst_port),
},
};
/* >8 End of creation of ACL table. */
/* Flow classify data. 8< */
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
};
/* >8 End of flow classify data. */
/* parameters for rte_flow_classify_validate and
* rte_flow_classify_table_entry_add functions
*/
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 };
/* sample actions:
* "actions count / end"
*/
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];
/* sample attributes */
static struct rte_flow_attr attr;
/* flow_classify.c: * Based on DPDK skeleton forwarding example. */
/*
* Initializes a given port using global settings and with the RX buffers
* coming from the mbuf_pool passed as a parameter.
*/
/* Initializing port using global settings. 8< */
static inline int
port_init(uint8_t port, struct rte_mempool *mbuf_pool)
{
struct rte_eth_conf port_conf;
struct rte_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;
memset(&port_conf, 0, sizeof(struct rte_eth_conf));
retval = rte_eth_dev_info_get(port, &dev_info);
if (retval != 0) {
printf("Error during getting device (port %u) info: %s\n",
port, strerror(-retval));
return retval;
}
if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
port_conf.txmode.offloads |=
DEV_TX_OFFLOAD_MBUF_FAST_FREE;
/* Configure the Ethernet device. */
retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
if (retval != 0)
return retval;
/* Allocate and set up 1 RX queue per Ethernet port. */
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;
/* Allocate and set up 1 TX queue per Ethernet port. */
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;
}
/* Start the Ethernet port. 8< */
retval = rte_eth_dev_start(port);
/* >8 End of starting the Ethernet port. */
if (retval < 0)
return retval;
/* Display the port MAC address. */
retval = rte_eth_macaddr_get(port, &addr);
if (retval != 0)
return retval;
printf("Port %u MAC: %02" PRIx8 " %02" PRIx8 " %02" PRIx8
" %02" PRIx8 " %02" PRIx8 " %02" PRIx8 "\n",
port, RTE_ETHER_ADDR_BYTES(&addr));
/* Enable RX in promiscuous mode for the Ethernet device. */
retval = rte_eth_promiscuous_enable(port);
if (retval != 0)
return retval;
return 0;
}
/* >8 End of initializing a given port. */
/*
* The lcore main. This is the main thread that does the work, reading from
* an input port classifying the packets and writing to an output port.
*/
/* Classifying the packets. 8< */
static __rte_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");
/*
* Check that the port is on the same NUMA node as the polling thread
* for best performance.
*/
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");
/* Run until the application is quit or killed. 8< */
for (;;) {
/*
* Receive packets on a port, classify them and forward them
* on the paired port.
* The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc.
*/
RTE_ETH_FOREACH_DEV(port) {
/* Get burst of RX packets, from first port of pair. */
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);
}
}
}
/* Send burst of TX packets, to second port of pair. */
const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
bufs, nb_rx);
/* Free any unsent packets. */
if (unlikely(nb_tx < nb_rx)) {
uint16_t buf;
for (buf = nb_tx; buf < nb_rx; buf++)
rte_pktmbuf_free(bufs[buf]);
}
}
}
/* >8 End of main loop. */
}
/* >8 End of lcore main. */
/*
* Parse IPv4 5 tuple rules file, ipv4_rules_file.txt.
* Expected format:
* <src_ipv4_addr>'/'<masklen> <space> \
* <dst_ipv4_addr>'/'<masklen> <space> \
* <src_port> <space> ":" <src_port_mask> <space> \
* <dst_port> <space> ":" <dst_port_mask> <space> \
* <proto>'/'<proto_mask> <space> \
* <priority>
*/
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] = RTE_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;
}
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;
}
/* 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;
}
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;
}
/* set up parameters for validate and add */
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;
/* Validate and add rule */
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;
}
/* Reads file and calls the add_classify_rule function. 8< */
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) {
i++;
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;
}
/* >8 End of add_rules. */
/* display usage */
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");
}
/* 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_RULE_IPV4, 1, 0, 0},
{NULL, 0, 0, 0}
};
argvopt = argv;
while ((opt = getopt_long(argc, argvopt, "",
lgopts, &option_index)) != EOF) {
switch (opt) {
/* long options */
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; /* reset getopt lib */
return ret;
}
/*
* The main function, which does initialization and calls the lcore_main
* function.
*/
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;
/* Initialize the Environment Abstraction Layer (EAL). 8< */
ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
/* >8 End of initialization of EAL. */
argc -= ret;
argv += ret;
/* Parse application arguments (after the EAL ones). 8< */
ret = parse_args(argc, argv);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Invalid flow_classify parameters\n");
/* >8 End of parse application arguments. */
/* Check that there is an even number of ports to send/receive on. */
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");
/* Creates a new mempool in memory to hold the mbufs. 8< */
mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
/* >8 End of creation of new mempool in memory. */
if (mbuf_pool == NULL)
rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
/* Initialize all ports. 8< */
RTE_ETH_FOREACH_DEV(portid)
if (port_init(portid, mbuf_pool) != 0)
rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n",
portid);
/* >8 End of initialization of all ports. */
if (rte_lcore_count() > 1)
printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
socket_id = rte_eth_dev_socket_id(0);
/* Memory allocation. 8< */
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");
}
/* initialise ACL table params */
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));
/* initialise table create params */
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");
}
/* >8 End of initialization of table create params. */
/* read file of IPv4 5 tuple rules and initialize parameters
* for rte_flow_classify_validate and rte_flow_classify_table_entry_add
* API's.
*/
/* Read file of IPv4 tuple rules. 8< */
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");
}
/* >8 End of reading file of IPv4 5 tuple rules. */
/* Call lcore_main on the main core only. */
lcore_main(cls_app);
/* clean up the EAL */
rte_eal_cleanup();
return 0;
}