8e693616fc
RTE_FLOW API allows hardware parsing and steering of packets to specific queues which helps in distributing ingress traffic across various cores. Adding 'flow' rules allows user to specify the distribution required. Signed-off-by: Anoob Joseph <anoobj@marvell.com> Acked-by: Akhil Goyal <akhil.goyal@nxp.com>
286 lines
6.7 KiB
C
286 lines
6.7 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright (C) 2020 Marvell International Ltd.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <rte_common.h>
|
|
#include <rte_flow.h>
|
|
#include <rte_ip.h>
|
|
|
|
#include "flow.h"
|
|
#include "ipsec-secgw.h"
|
|
#include "parser.h"
|
|
|
|
#define FLOW_RULES_MAX 128
|
|
|
|
struct flow_rule_entry {
|
|
uint8_t is_ipv4;
|
|
RTE_STD_C11
|
|
union {
|
|
struct {
|
|
struct rte_flow_item_ipv4 spec;
|
|
struct rte_flow_item_ipv4 mask;
|
|
} ipv4;
|
|
struct {
|
|
struct rte_flow_item_ipv6 spec;
|
|
struct rte_flow_item_ipv6 mask;
|
|
} ipv6;
|
|
};
|
|
uint16_t port;
|
|
uint16_t queue;
|
|
struct rte_flow *flow;
|
|
} flow_rule_tbl[FLOW_RULES_MAX];
|
|
|
|
int nb_flow_rule;
|
|
|
|
static void
|
|
ipv4_hdr_print(struct rte_ipv4_hdr *hdr)
|
|
{
|
|
char a, b, c, d;
|
|
|
|
uint32_t_to_char(rte_bswap32(hdr->src_addr), &a, &b, &c, &d);
|
|
printf("src: %3hhu.%3hhu.%3hhu.%3hhu \t", a, b, c, d);
|
|
|
|
uint32_t_to_char(rte_bswap32(hdr->dst_addr), &a, &b, &c, &d);
|
|
printf("dst: %3hhu.%3hhu.%3hhu.%3hhu", a, b, c, d);
|
|
}
|
|
|
|
static int
|
|
ipv4_addr_cpy(rte_be32_t *spec, rte_be32_t *mask, char *token,
|
|
struct parse_status *status)
|
|
{
|
|
struct in_addr ip;
|
|
uint32_t depth;
|
|
|
|
APP_CHECK(parse_ipv4_addr(token, &ip, &depth) == 0, status,
|
|
"unrecognized input \"%s\", expect valid ipv4 addr", token);
|
|
if (status->status < 0)
|
|
return -1;
|
|
|
|
if (depth > 32)
|
|
return -1;
|
|
|
|
memcpy(mask, &rte_flow_item_ipv4_mask.hdr.src_addr, sizeof(ip));
|
|
|
|
*spec = ip.s_addr;
|
|
if (depth < 32)
|
|
*mask = *mask << (32-depth);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
ipv6_hdr_print(struct rte_ipv6_hdr *hdr)
|
|
{
|
|
uint8_t *addr;
|
|
|
|
addr = hdr->src_addr;
|
|
printf("src: %4hx:%4hx:%4hx:%4hx:%4hx:%4hx:%4hx:%4hx \t",
|
|
(uint16_t)((addr[0] << 8) | addr[1]),
|
|
(uint16_t)((addr[2] << 8) | addr[3]),
|
|
(uint16_t)((addr[4] << 8) | addr[5]),
|
|
(uint16_t)((addr[6] << 8) | addr[7]),
|
|
(uint16_t)((addr[8] << 8) | addr[9]),
|
|
(uint16_t)((addr[10] << 8) | addr[11]),
|
|
(uint16_t)((addr[12] << 8) | addr[13]),
|
|
(uint16_t)((addr[14] << 8) | addr[15]));
|
|
|
|
addr = hdr->dst_addr;
|
|
printf("dst: %4hx:%4hx:%4hx:%4hx:%4hx:%4hx:%4hx:%4hx",
|
|
(uint16_t)((addr[0] << 8) | addr[1]),
|
|
(uint16_t)((addr[2] << 8) | addr[3]),
|
|
(uint16_t)((addr[4] << 8) | addr[5]),
|
|
(uint16_t)((addr[6] << 8) | addr[7]),
|
|
(uint16_t)((addr[8] << 8) | addr[9]),
|
|
(uint16_t)((addr[10] << 8) | addr[11]),
|
|
(uint16_t)((addr[12] << 8) | addr[13]),
|
|
(uint16_t)((addr[14] << 8) | addr[15]));
|
|
}
|
|
|
|
static int
|
|
ipv6_addr_cpy(uint8_t *spec, uint8_t *mask, char *token,
|
|
struct parse_status *status)
|
|
{
|
|
struct in6_addr ip;
|
|
uint32_t depth, i;
|
|
|
|
APP_CHECK(parse_ipv6_addr(token, &ip, &depth) == 0, status,
|
|
"unrecognized input \"%s\", expect valid ipv6 address", token);
|
|
if (status->status < 0)
|
|
return -1;
|
|
|
|
memcpy(mask, &rte_flow_item_ipv6_mask.hdr.src_addr, sizeof(ip));
|
|
memcpy(spec, ip.s6_addr, sizeof(struct in6_addr));
|
|
|
|
for (i = 0; i < depth && (i%8 <= sizeof(struct in6_addr)); i++)
|
|
mask[i/8] &= ~(1 << (7-i%8));
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
parse_flow_tokens(char **tokens, uint32_t n_tokens,
|
|
struct parse_status *status)
|
|
{
|
|
struct flow_rule_entry *rule;
|
|
uint32_t ti;
|
|
|
|
if (nb_flow_rule >= FLOW_RULES_MAX) {
|
|
printf("Too many flow rules\n");
|
|
return;
|
|
}
|
|
|
|
rule = &flow_rule_tbl[nb_flow_rule];
|
|
memset(rule, 0, sizeof(*rule));
|
|
|
|
if (strcmp(tokens[0], "ipv4") == 0) {
|
|
rule->is_ipv4 = 1;
|
|
} else if (strcmp(tokens[0], "ipv6") == 0) {
|
|
rule->is_ipv4 = 0;
|
|
} else {
|
|
APP_CHECK(0, status, "unrecognized input \"%s\"", tokens[0]);
|
|
return;
|
|
}
|
|
|
|
for (ti = 1; ti < n_tokens; ti++) {
|
|
if (strcmp(tokens[ti], "src") == 0) {
|
|
INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
|
|
if (status->status < 0)
|
|
return;
|
|
|
|
if (rule->is_ipv4) {
|
|
if (ipv4_addr_cpy(&rule->ipv4.spec.hdr.src_addr,
|
|
&rule->ipv4.mask.hdr.src_addr,
|
|
tokens[ti], status))
|
|
return;
|
|
} else {
|
|
if (ipv6_addr_cpy(rule->ipv6.spec.hdr.src_addr,
|
|
rule->ipv6.mask.hdr.src_addr,
|
|
tokens[ti], status))
|
|
return;
|
|
}
|
|
}
|
|
if (strcmp(tokens[ti], "dst") == 0) {
|
|
INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
|
|
if (status->status < 0)
|
|
return;
|
|
|
|
if (rule->is_ipv4) {
|
|
if (ipv4_addr_cpy(&rule->ipv4.spec.hdr.dst_addr,
|
|
&rule->ipv4.mask.hdr.dst_addr,
|
|
tokens[ti], status))
|
|
return;
|
|
} else {
|
|
if (ipv6_addr_cpy(rule->ipv6.spec.hdr.dst_addr,
|
|
rule->ipv6.mask.hdr.dst_addr,
|
|
tokens[ti], status))
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (strcmp(tokens[ti], "port") == 0) {
|
|
INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
|
|
if (status->status < 0)
|
|
return;
|
|
APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
|
|
if (status->status < 0)
|
|
return;
|
|
|
|
rule->port = atoi(tokens[ti]);
|
|
|
|
INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
|
|
if (status->status < 0)
|
|
return;
|
|
APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
|
|
if (status->status < 0)
|
|
return;
|
|
|
|
rule->queue = atoi(tokens[ti]);
|
|
}
|
|
}
|
|
|
|
nb_flow_rule++;
|
|
}
|
|
|
|
#define MAX_RTE_FLOW_PATTERN (3)
|
|
#define MAX_RTE_FLOW_ACTIONS (2)
|
|
|
|
static void
|
|
flow_init_single(struct flow_rule_entry *rule)
|
|
{
|
|
struct rte_flow_item pattern[MAX_RTE_FLOW_PATTERN] = {};
|
|
struct rte_flow_action action[MAX_RTE_FLOW_ACTIONS] = {};
|
|
struct rte_flow_attr attr = {};
|
|
struct rte_flow_error err;
|
|
int ret;
|
|
|
|
attr.egress = 0;
|
|
attr.ingress = 1;
|
|
|
|
action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
|
|
action[0].conf = &(struct rte_flow_action_queue) {
|
|
.index = rule->queue,
|
|
};
|
|
action[1].type = RTE_FLOW_ACTION_TYPE_END;
|
|
|
|
pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
|
|
|
|
if (rule->is_ipv4) {
|
|
pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
|
|
pattern[1].spec = &rule->ipv4.spec;
|
|
pattern[1].mask = &rule->ipv4.mask;
|
|
} else {
|
|
pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6;
|
|
pattern[1].spec = &rule->ipv6.spec;
|
|
pattern[1].mask = &rule->ipv6.mask;
|
|
}
|
|
|
|
pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
|
|
|
|
ret = rte_flow_validate(rule->port, &attr, pattern, action, &err);
|
|
if (ret < 0) {
|
|
RTE_LOG(ERR, IPSEC, "Flow validation failed %s\n", err.message);
|
|
return;
|
|
}
|
|
|
|
rule->flow = rte_flow_create(rule->port, &attr, pattern, action, &err);
|
|
if (rule->flow == NULL)
|
|
RTE_LOG(ERR, IPSEC, "Flow creation return %s\n", err.message);
|
|
}
|
|
|
|
void
|
|
flow_init(void)
|
|
{
|
|
struct flow_rule_entry *rule;
|
|
int i;
|
|
|
|
for (i = 0; i < nb_flow_rule; i++) {
|
|
rule = &flow_rule_tbl[i];
|
|
flow_init_single(rule);
|
|
}
|
|
|
|
for (i = 0; i < nb_flow_rule; i++) {
|
|
rule = &flow_rule_tbl[i];
|
|
if (rule->is_ipv4) {
|
|
printf("Flow #%3d: spec ipv4 ", i);
|
|
ipv4_hdr_print(&rule->ipv4.spec.hdr);
|
|
printf("\n");
|
|
printf(" mask ipv4 ");
|
|
ipv4_hdr_print(&rule->ipv4.mask.hdr);
|
|
} else {
|
|
printf("Flow #%3d: spec ipv6 ", i);
|
|
ipv6_hdr_print(&rule->ipv6.spec.hdr);
|
|
printf("\n");
|
|
printf(" mask ipv6 ");
|
|
ipv6_hdr_print(&rule->ipv6.mask.hdr);
|
|
}
|
|
|
|
printf("\tPort: %d, Queue: %d", rule->port, rule->queue);
|
|
|
|
if (rule->flow == NULL)
|
|
printf(" [UNSUPPORTED]");
|
|
printf("\n");
|
|
}
|
|
}
|