DPDK  24.07.0
examples/l3fwd/l3fwd_acl.c
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2022 Intel Corporation
*/
#include "l3fwd.h"
#include "l3fwd_route.h"
/*
* Rule and trace formats definitions.
*/
enum {
PROTO_FIELD_IPV4,
SRC_FIELD_IPV4,
DST_FIELD_IPV4,
SRCP_FIELD_IPV4,
DSTP_FIELD_IPV4,
NUM_FIELDS_IPV4
};
/*
* That effectively defines order of IPV4VLAN classifications:
* - PROTO
* - VLAN (TAG and DOMAIN)
* - SRC IP ADDRESS
* - DST IP ADDRESS
* - PORTS (SRC and DST)
*/
enum {
RTE_ACL_IPV4VLAN_PROTO,
RTE_ACL_IPV4VLAN_VLAN,
RTE_ACL_IPV4VLAN_SRC,
RTE_ACL_IPV4VLAN_DST,
RTE_ACL_IPV4VLAN_PORTS,
RTE_ACL_IPV4VLAN_NUM
};
struct acl_algorithms acl_alg[] = {
{
.name = "scalar",
},
{
.name = "sse",
},
{
.name = "avx2",
},
{
.name = "neon",
},
{
.name = "altivec",
},
{
.name = "avx512x16",
},
{
.name = "avx512x32",
},
};
struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
{
.type = RTE_ACL_FIELD_TYPE_BITMASK,
.size = sizeof(uint8_t),
.field_index = PROTO_FIELD_IPV4,
.input_index = RTE_ACL_IPV4VLAN_PROTO,
.offset = 0,
},
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof(uint32_t),
.field_index = SRC_FIELD_IPV4,
.input_index = RTE_ACL_IPV4VLAN_SRC,
.offset = offsetof(struct rte_ipv4_hdr, src_addr) -
offsetof(struct rte_ipv4_hdr, next_proto_id),
},
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof(uint32_t),
.field_index = DST_FIELD_IPV4,
.input_index = RTE_ACL_IPV4VLAN_DST,
.offset = offsetof(struct rte_ipv4_hdr, dst_addr) -
offsetof(struct rte_ipv4_hdr, next_proto_id),
},
{
.type = RTE_ACL_FIELD_TYPE_RANGE,
.size = sizeof(uint16_t),
.field_index = SRCP_FIELD_IPV4,
.input_index = RTE_ACL_IPV4VLAN_PORTS,
.offset = sizeof(struct rte_ipv4_hdr) -
offsetof(struct rte_ipv4_hdr, next_proto_id),
},
{
.type = RTE_ACL_FIELD_TYPE_RANGE,
.size = sizeof(uint16_t),
.field_index = DSTP_FIELD_IPV4,
.input_index = RTE_ACL_IPV4VLAN_PORTS,
.offset = sizeof(struct rte_ipv4_hdr) -
offsetof(struct rte_ipv4_hdr, next_proto_id) +
sizeof(uint16_t),
},
};
enum {
PROTO_FIELD_IPV6,
SRC1_FIELD_IPV6,
SRC2_FIELD_IPV6,
SRC3_FIELD_IPV6,
SRC4_FIELD_IPV6,
DST1_FIELD_IPV6,
DST2_FIELD_IPV6,
DST3_FIELD_IPV6,
DST4_FIELD_IPV6,
SRCP_FIELD_IPV6,
DSTP_FIELD_IPV6,
NUM_FIELDS_IPV6
};
struct rte_acl_field_def ipv6_defs[NUM_FIELDS_IPV6] = {
{
.type = RTE_ACL_FIELD_TYPE_BITMASK,
.size = sizeof(uint8_t),
.field_index = PROTO_FIELD_IPV6,
.input_index = PROTO_FIELD_IPV6,
.offset = 0,
},
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof(uint32_t),
.field_index = SRC1_FIELD_IPV6,
.input_index = SRC1_FIELD_IPV6,
.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
offsetof(struct rte_ipv6_hdr, proto),
},
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof(uint32_t),
.field_index = SRC2_FIELD_IPV6,
.input_index = SRC2_FIELD_IPV6,
.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint32_t),
},
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof(uint32_t),
.field_index = SRC3_FIELD_IPV6,
.input_index = SRC3_FIELD_IPV6,
.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
offsetof(struct rte_ipv6_hdr, proto) +
2 * sizeof(uint32_t),
},
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof(uint32_t),
.field_index = SRC4_FIELD_IPV6,
.input_index = SRC4_FIELD_IPV6,
.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
offsetof(struct rte_ipv6_hdr, proto) +
3 * sizeof(uint32_t),
},
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof(uint32_t),
.field_index = DST1_FIELD_IPV6,
.input_index = DST1_FIELD_IPV6,
.offset = offsetof(struct rte_ipv6_hdr, dst_addr)
- offsetof(struct rte_ipv6_hdr, proto),
},
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof(uint32_t),
.field_index = DST2_FIELD_IPV6,
.input_index = DST2_FIELD_IPV6,
.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint32_t),
},
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof(uint32_t),
.field_index = DST3_FIELD_IPV6,
.input_index = DST3_FIELD_IPV6,
.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
offsetof(struct rte_ipv6_hdr, proto) +
2 * sizeof(uint32_t),
},
{
.type = RTE_ACL_FIELD_TYPE_MASK,
.size = sizeof(uint32_t),
.field_index = DST4_FIELD_IPV6,
.input_index = DST4_FIELD_IPV6,
.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
offsetof(struct rte_ipv6_hdr, proto) +
3 * sizeof(uint32_t),
},
{
.type = RTE_ACL_FIELD_TYPE_RANGE,
.size = sizeof(uint16_t),
.field_index = SRCP_FIELD_IPV6,
.input_index = SRCP_FIELD_IPV6,
.offset = sizeof(struct rte_ipv6_hdr) -
offsetof(struct rte_ipv6_hdr, proto),
},
{
.type = RTE_ACL_FIELD_TYPE_RANGE,
.size = sizeof(uint16_t),
.field_index = DSTP_FIELD_IPV6,
.input_index = SRCP_FIELD_IPV6,
.offset = sizeof(struct rte_ipv6_hdr) -
offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint16_t),
},
};
enum {
CB_FLD_SRC_ADDR,
CB_FLD_DST_ADDR,
CB_FLD_SRC_PORT_LOW,
CB_FLD_SRC_PORT_DLM,
CB_FLD_SRC_PORT_HIGH,
CB_FLD_DST_PORT_LOW,
CB_FLD_DST_PORT_DLM,
CB_FLD_DST_PORT_HIGH,
CB_FLD_PROTO,
CB_FLD_USERDATA,
CB_FLD_NUM,
};
RTE_ACL_RULE_DEF(acl4_rule, RTE_DIM(ipv4_defs));
RTE_ACL_RULE_DEF(acl6_rule, RTE_DIM(ipv6_defs));
static struct {
struct rte_acl_ctx *acx_ipv4[NB_SOCKETS];
struct rte_acl_ctx *acx_ipv6[NB_SOCKETS];
#ifdef L3FWDACL_DEBUG
struct acl4_rule *rule_ipv4;
struct acl6_rule *rule_ipv6;
#endif
} acl_config;
static const char cb_port_delim[] = ":";
static struct rte_acl_rule *acl_base_ipv4, *route_base_ipv4,
*acl_base_ipv6, *route_base_ipv6;
static unsigned int acl_num_ipv4, route_num_ipv4,
acl_num_ipv6, route_num_ipv6;
#include "l3fwd_acl.h"
#include "l3fwd_acl_scalar.h"
/*
* Parse IPV6 address, expects the following format:
* XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X is a hexadecimal digit).
*/
static int
parse_ipv6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
char dlm)
{
uint32_t addr[IPV6_ADDR_U16];
GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
*end = in;
v[0] = (addr[0] << 16) + addr[1];
v[1] = (addr[2] << 16) + addr[3];
v[2] = (addr[4] << 16) + addr[5];
v[3] = (addr[6] << 16) + addr[7];
return 0;
}
static int
parse_ipv6_net(const char *in, struct rte_acl_field field[4])
{
int32_t rc;
const char *mp;
uint32_t i, m, v[4];
const uint32_t nbu32 = sizeof(uint32_t) * CHAR_BIT;
/* get address. */
rc = parse_ipv6_addr(in, &mp, v, '/');
if (rc != 0)
return rc;
/* get mask. */
GET_CB_FIELD(mp, m, 0, CHAR_BIT * sizeof(v), 0);
/* put all together. */
for (i = 0; i != RTE_DIM(v); i++) {
if (m >= (i + 1) * nbu32)
field[i].mask_range.u32 = nbu32;
else
field[i].mask_range.u32 = m > (i * nbu32) ?
m - (i * 32) : 0;
field[i].value.u32 = v[i];
}
return 0;
}
static int
parse_cb_ipv6_rule(char *str, struct rte_acl_rule *v, int has_userdata)
{
int i, rc;
char *s, *sp, *in[CB_FLD_NUM];
static const char *dlm = " \t\n";
int dim = has_userdata ? CB_FLD_NUM : CB_FLD_USERDATA;
s = str;
for (i = 0; i != dim; i++, s = NULL) {
in[i] = strtok_r(s, dlm, &sp);
if (in[i] == NULL)
return -EINVAL;
}
rc = parse_ipv6_net(in[CB_FLD_SRC_ADDR], v->field + SRC1_FIELD_IPV6);
if (rc != 0) {
acl_log("failed to read source address/mask: %s\n",
in[CB_FLD_SRC_ADDR]);
return rc;
}
rc = parse_ipv6_net(in[CB_FLD_DST_ADDR], v->field + DST1_FIELD_IPV6);
if (rc != 0) {
acl_log("failed to read destination address/mask: %s\n",
in[CB_FLD_DST_ADDR]);
return rc;
}
/* source port. */
GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW],
v->field[SRCP_FIELD_IPV6].value.u16,
0, UINT16_MAX, 0);
GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH],
v->field[SRCP_FIELD_IPV6].mask_range.u16,
0, UINT16_MAX, 0);
if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
sizeof(cb_port_delim)) != 0)
return -EINVAL;
/* destination port. */
GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW],
v->field[DSTP_FIELD_IPV6].value.u16,
0, UINT16_MAX, 0);
GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH],
v->field[DSTP_FIELD_IPV6].mask_range.u16,
0, UINT16_MAX, 0);
if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
sizeof(cb_port_delim)) != 0)
return -EINVAL;
if (v->field[SRCP_FIELD_IPV6].mask_range.u16
< v->field[SRCP_FIELD_IPV6].value.u16
|| v->field[DSTP_FIELD_IPV6].mask_range.u16
< v->field[DSTP_FIELD_IPV6].value.u16)
return -EINVAL;
GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV6].value.u8,
0, UINT8_MAX, '/');
GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV6].mask_range.u8,
0, UINT8_MAX, 0);
if (has_userdata)
GET_CB_FIELD(in[CB_FLD_USERDATA], v->data.userdata,
0, UINT32_MAX, 0);
return 0;
}
/*
* Parse ClassBench rules file.
* Expected format:
* '@'<src_ipv4_addr>'/'<masklen> <space> \
* <dst_ipv4_addr>'/'<masklen> <space> \
* <src_port_low> <space> ":" <src_port_high> <space> \
* <dst_port_low> <space> ":" <dst_port_high> <space> \
* <proto>'/'<mask>
*/
static int
parse_ipv4_net(char *in, uint32_t *addr, uint32_t *mask_len)
{
char *sa, *sm, *sv;
const char *dlm = "/";
sv = NULL;
sa = strtok_r(in, dlm, &sv);
if (sa == NULL)
return -EINVAL;
sm = strtok_r(NULL, dlm, &sv);
if (sm == NULL)
return -EINVAL;
if (inet_pton(AF_INET, sa, addr) != 1)
return -EINVAL;
GET_CB_FIELD(sm, *mask_len, 0, 32, 0);
*addr = ntohl(*addr);
return 0;
}
static int
parse_cb_ipv4vlan_rule(char *str, struct rte_acl_rule *v, int has_userdata)
{
int i, rc;
char *s, *sp, *in[CB_FLD_NUM];
static const char *dlm = " \t\n";
int dim = has_userdata ? CB_FLD_NUM : CB_FLD_USERDATA;
s = str;
for (i = 0; i != dim; i++, s = NULL) {
in[i] = strtok_r(s, dlm, &sp);
if (in[i] == NULL)
return -EINVAL;
}
rc = parse_ipv4_net(in[CB_FLD_SRC_ADDR],
&v->field[SRC_FIELD_IPV4].value.u32,
&v->field[SRC_FIELD_IPV4].mask_range.u32);
if (rc != 0) {
acl_log("failed to read source address/mask: %s\n",
in[CB_FLD_SRC_ADDR]);
return rc;
}
rc = parse_ipv4_net(in[CB_FLD_DST_ADDR],
&v->field[DST_FIELD_IPV4].value.u32,
&v->field[DST_FIELD_IPV4].mask_range.u32);
if (rc != 0) {
acl_log("failed to read destination address/mask: %s\n",
in[CB_FLD_DST_ADDR]);
return rc;
}
GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW],
v->field[SRCP_FIELD_IPV4].value.u16,
0, UINT16_MAX, 0);
GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH],
v->field[SRCP_FIELD_IPV4].mask_range.u16,
0, UINT16_MAX, 0);
if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
sizeof(cb_port_delim)) != 0) {
return -EINVAL;
}
GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW],
v->field[DSTP_FIELD_IPV4].value.u16,
0, UINT16_MAX, 0);
GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH],
v->field[DSTP_FIELD_IPV4].mask_range.u16,
0, UINT16_MAX, 0);
if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
sizeof(cb_port_delim)) != 0) {
return -EINVAL;
}
if (v->field[SRCP_FIELD_IPV4].mask_range.u16
< v->field[SRCP_FIELD_IPV4].value.u16
|| v->field[DSTP_FIELD_IPV4].mask_range.u16
< v->field[DSTP_FIELD_IPV4].value.u16) {
return -EINVAL;
}
GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].value.u8,
0, UINT8_MAX, '/');
GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].mask_range.u8,
0, UINT8_MAX, 0);
if (has_userdata)
GET_CB_FIELD(in[CB_FLD_USERDATA], v->data.userdata, 0,
UINT32_MAX, 0);
return 0;
}
static int
acl_add_rules(const char *rule_path,
struct rte_acl_rule **proute_base,
unsigned int *proute_num,
struct rte_acl_rule **pacl_base,
unsigned int *pacl_num, uint32_t rule_size,
int (*parser)(char *, struct rte_acl_rule*, int))
{
uint8_t *acl_rules, *route_rules;
struct rte_acl_rule *next;
unsigned int acl_num = 0, route_num = 0, total_num = 0;
unsigned int acl_cnt = 0, route_cnt = 0;
char buff[LINE_MAX];
FILE *fh = fopen(rule_path, "rb");
unsigned int i = 0;
int val;
if (fh == NULL)
rte_exit(EXIT_FAILURE, "%s: Open %s failed\n", __func__,
rule_path);
while ((fgets(buff, LINE_MAX, fh) != NULL)) {
if (buff[0] == ROUTE_LEAD_CHAR)
route_num++;
else if (buff[0] == ACL_LEAD_CHAR)
acl_num++;
}
if (route_num == 0)
rte_exit(EXIT_FAILURE, "Not find any route entries in %s!\n",
rule_path);
val = fseek(fh, 0, SEEK_SET);
if (val < 0) {
rte_exit(EXIT_FAILURE, "%s: File seek operation failed\n",
__func__);
}
acl_rules = calloc(acl_num, rule_size);
if (acl_rules == NULL)
rte_exit(EXIT_FAILURE, "%s: failed to malloc memory\n",
__func__);
route_rules = calloc(route_num, rule_size);
if (route_rules == NULL)
rte_exit(EXIT_FAILURE, "%s: failed to malloc memory\n",
__func__);
i = 0;
while (fgets(buff, LINE_MAX, fh) != NULL) {
i++;
if (is_bypass_line(buff))
continue;
char s = buff[0];
/* Route entry */
if (s == ROUTE_LEAD_CHAR)
next = (struct rte_acl_rule *)(route_rules +
route_cnt * rule_size);
/* ACL entry */
else if (s == ACL_LEAD_CHAR)
next = (struct rte_acl_rule *)(acl_rules +
acl_cnt * rule_size);
/* Illegal line */
else
rte_exit(EXIT_FAILURE,
"%s Line %u: should start with leading "
"char %c or %c\n",
rule_path, i, ROUTE_LEAD_CHAR, ACL_LEAD_CHAR);
if (parser(buff + 1, next, s == ROUTE_LEAD_CHAR) != 0)
rte_exit(EXIT_FAILURE,
"%s Line %u: parse rules error\n",
rule_path, i);
if (s == ROUTE_LEAD_CHAR) {
/* Check the forwarding port number */
if ((enabled_port_mask & (1 << next->data.userdata)) ==
0)
rte_exit(EXIT_FAILURE,
"%s Line %u: fwd number illegal:%u\n",
rule_path, i, next->data.userdata);
next->data.userdata += FWD_PORT_SHIFT;
route_cnt++;
} else {
next->data.userdata = ACL_DENY_SIGNATURE + acl_cnt;
acl_cnt++;
}
next->data.priority = RTE_ACL_MAX_PRIORITY - total_num;
next->data.category_mask = -1;
total_num++;
}
fclose(fh);
*pacl_base = (struct rte_acl_rule *)acl_rules;
*pacl_num = acl_num;
*proute_base = (struct rte_acl_rule *)route_rules;
*proute_num = route_cnt;
return 0;
}
parse_acl_alg(const char *alg)
{
uint32_t i;
for (i = 0; i != RTE_DIM(acl_alg); i++) {
if (strcmp(alg, acl_alg[i].name) == 0)
return acl_alg[i].alg;
}
return RTE_ACL_CLASSIFY_DEFAULT;
}
int
usage_acl_alg(char *buf, size_t sz)
{
uint32_t i, n, rc, tn;
n = 0;
tn = 0;
for (i = 0; i < RTE_DIM(acl_alg); i++) {
rc = snprintf(buf + n, sz - n,
i == RTE_DIM(acl_alg) - 1 ? "%s" : "%s|",
acl_alg[i].name);
tn += rc;
if (rc < sz - n)
n += rc;
}
return tn;
}
static const char *
str_acl_alg(enum rte_acl_classify_alg alg)
{
uint32_t i;
for (i = 0; i != RTE_DIM(acl_alg); i++) {
if (alg == acl_alg[i].alg)
return acl_alg[i].name;
}
return "default";
}
static void
dump_acl_config(void)
{
printf("ACL options are:\n");
printf("rule_ipv4: %s\n", parm_config.rule_ipv4_name);
printf("rule_ipv6: %s\n", parm_config.rule_ipv6_name);
printf("alg: %s\n", str_acl_alg(parm_config.alg));
}
static int
check_acl_config(void)
{
if (parm_config.rule_ipv4_name == NULL) {
acl_log("ACL IPv4 rule file not specified\n");
return -1;
} else if (parm_config.rule_ipv6_name == NULL) {
acl_log("ACL IPv6 rule file not specified\n");
return -1;
}
return 0;
}
/* Setup ACL context. 8< */
static struct rte_acl_ctx*
app_acl_init(struct rte_acl_rule *route_base,
struct rte_acl_rule *acl_base, unsigned int route_num,
unsigned int acl_num, int ipv6, int socketid)
{
char name[PATH_MAX];
struct rte_acl_param acl_param;
struct rte_acl_config acl_build_param;
struct rte_acl_ctx *context;
int dim = ipv6 ? RTE_DIM(ipv6_defs) : RTE_DIM(ipv4_defs);
/* Create ACL contexts */
snprintf(name, sizeof(name), "%s%d",
ipv6 ? L3FWD_ACL_IPV6_NAME : L3FWD_ACL_IPV4_NAME,
socketid);
acl_param.name = name;
acl_param.socket_id = socketid;
acl_param.rule_size = RTE_ACL_RULE_SZ(dim);
acl_param.max_rule_num = MAX_ACL_RULE_NUM;
context = rte_acl_create(&acl_param);
if (context == NULL)
rte_exit(EXIT_FAILURE, "Failed to create ACL context\n");
if (parm_config.alg != RTE_ACL_CLASSIFY_DEFAULT &&
rte_acl_set_ctx_classify(context, parm_config.alg) != 0)
rte_exit(EXIT_FAILURE,
"Failed to setup classify method for ACL context\n");
if (rte_acl_add_rules(context, route_base, route_num) < 0)
rte_exit(EXIT_FAILURE, "add rules failed\n");
if (rte_acl_add_rules(context, acl_base, acl_num) < 0)
rte_exit(EXIT_FAILURE, "add rules failed\n");
/* Perform builds */
memset(&acl_build_param, 0, sizeof(acl_build_param));
acl_build_param.num_categories = DEFAULT_MAX_CATEGORIES;
acl_build_param.num_fields = dim;
memcpy(&acl_build_param.defs, ipv6 ? ipv6_defs : ipv4_defs,
ipv6 ? sizeof(ipv6_defs) : sizeof(ipv4_defs));
if (rte_acl_build(context, &acl_build_param) != 0)
rte_exit(EXIT_FAILURE, "Failed to build ACL trie\n");
rte_acl_dump(context);
return context;
}
/* >8 End of ACL context setup. */
void
acl_free_routes(void)
{
free(route_base_ipv4);
free(route_base_ipv6);
route_base_ipv4 = NULL;
route_base_ipv6 = NULL;
route_num_ipv4 = 0;
route_num_ipv6 = 0;
free(acl_base_ipv4);
free(acl_base_ipv6);
acl_base_ipv4 = NULL;
acl_base_ipv6 = NULL;
acl_num_ipv4 = 0;
acl_num_ipv6 = 0;
}
/* Load rules from the input file */
void
read_config_files_acl(void)
{
/* ipv4 check */
if (parm_config.rule_ipv4_name != NULL) {
if (acl_add_rules(parm_config.rule_ipv4_name, &route_base_ipv4,
&route_num_ipv4, &acl_base_ipv4, &acl_num_ipv4,
sizeof(struct acl4_rule), &parse_cb_ipv4vlan_rule) < 0) {
acl_free_routes();
rte_exit(EXIT_FAILURE, "Failed to add IPv4 rules\n");
}
} else {
RTE_LOG(ERR, L3FWD, "IPv4 rule file not specified\n");
rte_exit(EXIT_FAILURE, "Failed to get valid route options\n");
}
/* ipv6 check */
if (parm_config.rule_ipv6_name != NULL) {
if (acl_add_rules(parm_config.rule_ipv6_name, &route_base_ipv6,
&route_num_ipv6,
&acl_base_ipv6, &acl_num_ipv6,
sizeof(struct acl6_rule), &parse_cb_ipv6_rule) < 0) {
acl_free_routes();
rte_exit(EXIT_FAILURE, "Failed to add IPv6 rules\n");
}
} else {
RTE_LOG(ERR, L3FWD, "IPv6 rule file not specified\n");
rte_exit(EXIT_FAILURE, "Failed to get valid route options\n");
}
}
void
print_one_ipv4_rule(struct acl4_rule *rule, int extra)
{
char abuf[INET6_ADDRSTRLEN];
uint32_t ipv4_addr;
ipv4_addr = ntohl(rule->field[SRC_FIELD_IPV4].value.u32);
printf("%s/%u ", inet_ntop(AF_INET,
&(ipv4_addr), abuf,
sizeof(abuf)), rule->field[SRC_FIELD_IPV4].mask_range.u32);
ipv4_addr = ntohl(rule->field[DST_FIELD_IPV4].value.u32);
printf("%s/%u ", inet_ntop(AF_INET,
&(ipv4_addr), abuf,
sizeof(abuf)), rule->field[DST_FIELD_IPV4].mask_range.u32);
printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
rule->field[SRCP_FIELD_IPV4].value.u16,
rule->field[SRCP_FIELD_IPV4].mask_range.u16,
rule->field[DSTP_FIELD_IPV4].value.u16,
rule->field[DSTP_FIELD_IPV4].mask_range.u16,
rule->field[PROTO_FIELD_IPV4].value.u8,
rule->field[PROTO_FIELD_IPV4].mask_range.u8);
if (extra)
printf("0x%x-0x%x-0x%x ",
rule->data.category_mask,
rule->data.priority,
rule->data.userdata);
}
void
print_one_ipv6_rule(struct acl6_rule *rule, int extra)
{
unsigned char a, b, c, d;
uint32_t_to_char(rule->field[SRC1_FIELD_IPV6].value.u32,
&a, &b, &c, &d);
printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
uint32_t_to_char(rule->field[SRC2_FIELD_IPV6].value.u32,
&a, &b, &c, &d);
printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
uint32_t_to_char(rule->field[SRC3_FIELD_IPV6].value.u32,
&a, &b, &c, &d);
printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
uint32_t_to_char(rule->field[SRC4_FIELD_IPV6].value.u32,
&a, &b, &c, &d);
printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
rule->field[SRC1_FIELD_IPV6].mask_range.u32
+ rule->field[SRC2_FIELD_IPV6].mask_range.u32
+ rule->field[SRC3_FIELD_IPV6].mask_range.u32
+ rule->field[SRC4_FIELD_IPV6].mask_range.u32);
uint32_t_to_char(rule->field[DST1_FIELD_IPV6].value.u32,
&a, &b, &c, &d);
printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
uint32_t_to_char(rule->field[DST2_FIELD_IPV6].value.u32,
&a, &b, &c, &d);
printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
uint32_t_to_char(rule->field[DST3_FIELD_IPV6].value.u32,
&a, &b, &c, &d);
printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
uint32_t_to_char(rule->field[DST4_FIELD_IPV6].value.u32,
&a, &b, &c, &d);
printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
rule->field[DST1_FIELD_IPV6].mask_range.u32
+ rule->field[DST2_FIELD_IPV6].mask_range.u32
+ rule->field[DST3_FIELD_IPV6].mask_range.u32
+ rule->field[DST4_FIELD_IPV6].mask_range.u32);
printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
rule->field[SRCP_FIELD_IPV6].value.u16,
rule->field[SRCP_FIELD_IPV6].mask_range.u16,
rule->field[DSTP_FIELD_IPV6].value.u16,
rule->field[DSTP_FIELD_IPV6].mask_range.u16,
rule->field[PROTO_FIELD_IPV6].value.u8,
rule->field[PROTO_FIELD_IPV6].mask_range.u8);
if (extra)
printf("0x%x-0x%x-0x%x ",
rule->data.category_mask,
rule->data.priority,
rule->data.userdata);
}
#ifdef L3FWDACL_DEBUG
static inline void
dump_acl4_rule(struct rte_mbuf *m, uint32_t sig)
{
char abuf[INET6_ADDRSTRLEN];
uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
struct rte_ipv4_hdr *ipv4_hdr =
sizeof(struct rte_ether_hdr));
printf("Packet Src:%s ", inet_ntop(AF_INET, ipv4_hdr->src_addr,
abuf, sizeof(abuf)));
printf("Dst:%s ", inet_ntop(AF_INET, ipv4_hdr->dst_addr,
abuf, sizeof(abuf)));
printf("Src port:%hu,Dst port:%hu ",
rte_bswap16(*(uint16_t *)(ipv4_hdr + 1)),
rte_bswap16(*((uint16_t *)(ipv4_hdr + 1) + 1)));
printf("hit ACL %d - ", offset);
print_one_ipv4_rule(acl_config.rule_ipv4 + offset, 1);
printf("\n\n");
}
static inline void
dump_acl6_rule(struct rte_mbuf *m, uint32_t sig)
{
char abuf[INET6_ADDRSTRLEN];
uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
struct rte_ipv6_hdr *ipv6_hdr =
sizeof(struct rte_ether_hdr));
printf("Packet Src");
printf("%s", inet_ntop(AF_INET6, ipv6_hdr->src_addr,
abuf, sizeof(abuf)));
printf("\nDst");
printf("%s", inet_ntop(AF_INET6, ipv6_hdr->dst_addr,
abuf, sizeof(abuf)));
printf("\nSrc port:%hu,Dst port:%hu ",
rte_bswap16(*(uint16_t *)(ipv6_hdr + 1)),
rte_bswap16(*((uint16_t *)(ipv6_hdr + 1) + 1)));
printf("hit ACL %d - ", offset);
print_one_ipv6_rule(acl_config.rule_ipv6 + offset, 1);
printf("\n\n");
}
#endif /* L3FWDACL_DEBUG */
static inline void
dump_ipv4_rules(struct acl4_rule *rule, int num, int extra)
{
int i;
for (i = 0; i < num; i++, rule++) {
printf("\t%d:", i + 1);
print_one_ipv4_rule(rule, extra);
printf("\n");
}
}
static inline void
dump_ipv6_rules(struct acl6_rule *rule, int num, int extra)
{
int i;
for (i = 0; i < num; i++, rule++) {
printf("\t%d:", i + 1);
print_one_ipv6_rule(rule, extra);
printf("\n");
}
}
/* Function to setup acl. */
void
setup_acl(const int socket_id)
{
if (check_acl_config() != 0)
rte_exit(EXIT_FAILURE, "Failed to get valid ACL options\n");
dump_acl_config();
acl_log("IPv4 Route entries %u:\n", route_num_ipv4);
dump_ipv4_rules((struct acl4_rule *)route_base_ipv4, route_num_ipv4, 1);
acl_log("IPv4 ACL entries %u:\n", acl_num_ipv4);
dump_ipv4_rules((struct acl4_rule *)acl_base_ipv4, acl_num_ipv4, 1);
acl_log("IPv6 Route entries %u:\n", route_num_ipv6);
dump_ipv6_rules((struct acl6_rule *)route_base_ipv6, route_num_ipv6, 1);
acl_log("IPv6 ACL entries %u:\n", acl_num_ipv6);
dump_ipv6_rules((struct acl6_rule *)acl_base_ipv6, acl_num_ipv6, 1);
/* Check sockets a context should be created on */
if (socket_id >= NB_SOCKETS) {
acl_log("Socket %d is out "
"of range %d\n",
socket_id, NB_SOCKETS);
acl_free_routes();
return;
}
rte_acl_free(acl_config.acx_ipv4[socket_id]);
rte_acl_free(acl_config.acx_ipv6[socket_id]);
acl_config.acx_ipv4[socket_id] = app_acl_init(route_base_ipv4,
acl_base_ipv4, route_num_ipv4, acl_num_ipv4,
0, socket_id);
acl_config.acx_ipv6[socket_id] = app_acl_init(route_base_ipv6,
acl_base_ipv6, route_num_ipv6, acl_num_ipv6,
1, socket_id);
#ifdef L3FWDACL_DEBUG
acl_config.rule_ipv4 = (struct acl4_rule *)acl_base_ipv4;
acl_config.rule_ipv6 = (struct acl6_rule *)acl_base_ipv6;
#endif
}
static inline void
dump_denied_pkt(const struct rte_mbuf *pkt, uint32_t res)
{
#ifdef L3FWDACL_DEBUG
if ((res & ACL_DENY_SIGNATURE) != 0) {
dump_acl4_rule(pkt, res);
else if (RTE_ETH_IS_IPV6_HDR(pkt[i]->packet_type))
dump_acl6_rule(pkt[i], res[i]);
}
#else
#endif
}
static inline void
acl_process_pkts(struct rte_mbuf *pkts[MAX_PKT_BURST],
uint16_t hops[MAX_PKT_BURST], uint32_t num, int32_t socketid)
{
uint32_t i, n4, n6, res;
struct acl_search_t acl_search;
/* split packets burst depending on packet type (IPv4/IPv6) */
l3fwd_acl_prepare_acl_parameter(pkts, &acl_search, num);
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);
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);
/* combine lookup results back, into one array of next hops */
n4 = 0;
n6 = 0;
for (i = 0; i != num; i++) {
switch (acl_search.types[i]) {
case TYPE_IPV4:
res = acl_search.res_ipv4[n4++];
break;
case TYPE_IPV6:
res = acl_search.res_ipv6[n6++];
break;
default:
res = 0;
}
if (likely((res & ACL_DENY_SIGNATURE) == 0 && res != 0))
hops[i] = res - FWD_PORT_SHIFT;
else {
hops[i] = BAD_PORT;
dump_denied_pkt(pkts[i], res);
}
}
}
static inline void
acl_send_packets(struct lcore_conf *qconf, struct rte_mbuf *pkts[],
uint16_t hops[], uint32_t num)
{
#if defined ACL_SEND_MULTI
send_packets_multi(qconf, pkts, hops, num);
#else
send_packets_single(qconf, pkts, hops, num);
#endif
}
/* main processing loop */
int
acl_main_loop(__rte_unused void *dummy)
{
struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
uint16_t hops[MAX_PKT_BURST];
unsigned int lcore_id;
uint64_t prev_tsc, diff_tsc, cur_tsc;
int i, nb_rx;
uint16_t portid;
uint16_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=%" PRIu16 "\n",
lcore_id, portid, queueid);
}
while (!force_quit) {
cur_tsc = rte_rdtsc();
/*
* TX burst queue drain
*/
diff_tsc = cur_tsc - prev_tsc;
if (unlikely(diff_tsc > drain_tsc)) {
for (i = 0; i < qconf->n_tx_port; ++i) {
portid = qconf->tx_port_id[i];
if (qconf->tx_mbufs[portid].len == 0)
continue;
send_burst(qconf,
qconf->tx_mbufs[portid].len,
portid);
qconf->tx_mbufs[portid].len = 0;
}
prev_tsc = cur_tsc;
}
/*
* Read packet from RX queues and process them
*/
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) {
acl_process_pkts(pkts_burst, hops, nb_rx,
socketid);
acl_send_packets(qconf, pkts_burst, hops,
nb_rx);
}
}
}
return 0;
}
/* Not used by L3fwd ACL. */
void *
acl_get_ipv4_l3fwd_lookup_struct(__rte_unused const int socketid)
{
return NULL;
}
void *
acl_get_ipv6_l3fwd_lookup_struct(__rte_unused const int socketid)
{
return NULL;
}