numam-dpdk/app/test-pmd/cmd_flex_item.c

549 lines
14 KiB
C
Raw Normal View History

app/testpmd: add flex item commands Network port hardware is shipped with fixed number of supported network protocols. If application must work with a protocol that is not included in the port hardware by default, it can try to add the new protocol to port hardware. Flex item or flex parser is port infrastructure that allows application to add support for a custom network header and offload flows to match the header elements. Application must complete the following tasks to create a flow rule that matches custom header: 1. Create flow item object in port hardware. Application must provide custom header configuration to PMD. PMD will use that configuration to create flex item object in port hardware. 2. Create flex patterns to match. Flex pattern has a spec and a mask components, like a regular flow item. Combined together, spec and mask can target unique data sequence or a number of data sequences in the custom header. Flex patterns of the same flex item can have different lengths. Flex pattern is identified by unique handler value. 3. Create a flow rule with a flex flow item that references flow pattern. Testpmd flex CLI commands are: testpmd> flow flex_item create <port> <flex_id> <filename> testpmd> set flex_pattern <pattern_id> \ spec <spec data> mask <mask data> testpmd> set flex_pattern <pattern_id> is <spec_data> testpmd> flow create <port> ... \ / flex item is <flex_id> pattern is <pattern_id> / ... The patch works with the jansson library API. A new optional dependency on jansson library is added for testpmd. If jansson not detected the flex item functionality is disabled. Jansson development files must be present: jansson.pc, jansson.h libjansson.[a,so] Signed-off-by: Gregory Etelson <getelson@nvidia.com> Reviewed-by: Viacheslav Ovsiienko <viacheslavo@nvidia.com>
2021-10-20 18:14:57 +03:00
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) 2021 NVIDIA Corporation & Affiliates
*/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <rte_common.h>
#include <rte_ethdev.h>
#include <cmdline_parse.h>
#include <cmdline_parse_string.h>
#include <cmdline_parse_num.h>
#include <rte_flow.h>
#include "testpmd.h"
struct flex_item *flex_items[RTE_MAX_ETHPORTS][FLEX_MAX_PARSERS_NUM];
struct flex_pattern flex_patterns[FLEX_MAX_PATTERNS_NUM];
#ifdef RTE_HAS_JANSSON
static __rte_always_inline bool
match_strkey(const char *key, const char *pattern)
{
return strncmp(key, pattern, strlen(key)) == 0;
}
static struct flex_item *
flex_parser_fetch(uint16_t port_id, uint16_t flex_id)
{
if (port_id >= RTE_MAX_ETHPORTS) {
printf("Invalid port_id: %u\n", port_id);
return FLEX_PARSER_ERR;
}
if (flex_id >= FLEX_MAX_PARSERS_NUM) {
printf("Invalid flex item flex_id: %u\n", flex_id);
return FLEX_PARSER_ERR;
}
return flex_items[port_id][flex_id];
}
void
flex_item_destroy(portid_t port_id, uint16_t flex_id)
{
int ret;
struct rte_flow_error error;
struct flex_item *fp = flex_parser_fetch(port_id, flex_id);
if (fp == FLEX_PARSER_ERR) {
printf("Bad parameters: port_id=%u flex_id=%u\n",
port_id, flex_id);
return;
}
if (!fp)
return;
ret = rte_flow_flex_item_release(port_id, fp->flex_handle, &error);
if (!ret) {
free(fp);
flex_items[port_id][flex_id] = NULL;
printf("port-%u: released flex item #%u\n",
port_id, flex_id);
} else {
printf("port-%u: cannot release flex item #%u: %s\n",
port_id, flex_id, error.message);
}
}
static int
flex_tunnel_parse(json_t *jtun, enum rte_flow_item_flex_tunnel_mode *tunnel)
{
int tun = -1;
if (json_is_integer(jtun))
tun = (int)json_integer_value(jtun);
else if (json_is_real(jtun))
tun = (int)json_real_value(jtun);
else if (json_is_string(jtun)) {
const char *mode = json_string_value(jtun);
if (match_strkey(mode, "FLEX_TUNNEL_MODE_SINGLE"))
tun = FLEX_TUNNEL_MODE_SINGLE;
else if (match_strkey(mode, "FLEX_TUNNEL_MODE_OUTER"))
tun = FLEX_TUNNEL_MODE_OUTER;
else if (match_strkey(mode, "FLEX_TUNNEL_MODE_INNER"))
tun = FLEX_TUNNEL_MODE_INNER;
else if (match_strkey(mode, "FLEX_TUNNEL_MODE_MULTI"))
tun = FLEX_TUNNEL_MODE_MULTI;
else if (match_strkey(mode, "FLEX_TUNNEL_MODE_TUNNEL"))
tun = FLEX_TUNNEL_MODE_TUNNEL;
else
return -EINVAL;
} else
return -EINVAL;
*tunnel = (enum rte_flow_item_flex_tunnel_mode)tun;
return 0;
}
static int
flex_field_parse(json_t *jfld, struct rte_flow_item_flex_field *fld)
{
const char *key;
json_t *je;
#define FLEX_FIELD_GET(fm, t) \
do { \
if (!strncmp(key, # fm, strlen(# fm))) { \
if (json_is_real(je)) \
fld->fm = (t) json_real_value(je); \
else if (json_is_integer(je)) \
fld->fm = (t) json_integer_value(je); \
else \
return -EINVAL; \
} \
} while (0)
json_object_foreach(jfld, key, je) {
FLEX_FIELD_GET(field_size, uint32_t);
FLEX_FIELD_GET(field_base, int32_t);
FLEX_FIELD_GET(offset_base, uint32_t);
FLEX_FIELD_GET(offset_mask, uint32_t);
FLEX_FIELD_GET(offset_shift, int32_t);
FLEX_FIELD_GET(field_id, uint16_t);
if (match_strkey(key, "field_mode")) {
const char *mode;
if (!json_is_string(je))
return -EINVAL;
mode = json_string_value(je);
if (match_strkey(mode, "FIELD_MODE_DUMMY"))
fld->field_mode = FIELD_MODE_DUMMY;
else if (match_strkey(mode, "FIELD_MODE_FIXED"))
fld->field_mode = FIELD_MODE_FIXED;
else if (match_strkey(mode, "FIELD_MODE_OFFSET"))
fld->field_mode = FIELD_MODE_OFFSET;
else if (match_strkey(mode, "FIELD_MODE_BITMASK"))
fld->field_mode = FIELD_MODE_BITMASK;
else
return -EINVAL;
}
}
return 0;
}
enum flex_link_type {
FLEX_LINK_IN = 0,
FLEX_LINK_OUT = 1
};
static int
flex_link_item_parse(const char *src, struct rte_flow_item *item)
{
#define FLEX_PARSE_DATA_SIZE 1024
int ret;
uint8_t *ptr, data[FLEX_PARSE_DATA_SIZE] = {0,};
char flow_rule[256];
struct rte_flow_attr *attr;
struct rte_flow_item *pattern;
struct rte_flow_action *actions;
sprintf(flow_rule, "flow create 0 pattern %s / end", src);
src = flow_rule;
ret = flow_parse(src, (void *)data, sizeof(data),
&attr, &pattern, &actions);
if (ret)
return ret;
item->type = pattern->type;
if (pattern->spec) {
ptr = (void *)(uintptr_t)item->spec;
memcpy(ptr, pattern->spec, FLEX_MAX_FLOW_PATTERN_LENGTH);
} else {
item->spec = NULL;
}
if (pattern->mask) {
ptr = (void *)(uintptr_t)item->mask;
memcpy(ptr, pattern->mask, FLEX_MAX_FLOW_PATTERN_LENGTH);
} else {
item->mask = NULL;
}
if (pattern->last) {
ptr = (void *)(uintptr_t)item->last;
memcpy(ptr, pattern->last, FLEX_MAX_FLOW_PATTERN_LENGTH);
} else {
item->last = NULL;
}
return 0;
}
static int
flex_link_parse(json_t *jobj, struct rte_flow_item_flex_link *link,
enum flex_link_type link_type)
{
const char *key;
json_t *je;
int ret;
json_object_foreach(jobj, key, je) {
if (match_strkey(key, "item")) {
if (!json_is_string(je))
return -EINVAL;
ret = flex_link_item_parse(json_string_value(je),
&link->item);
if (ret)
return -EINVAL;
if (link_type == FLEX_LINK_IN) {
if (!link->item.spec || !link->item.mask)
return -EINVAL;
if (link->item.last)
return -EINVAL;
}
}
if (match_strkey(key, "next")) {
if (json_is_integer(je))
link->next = (typeof(link->next))
json_integer_value(je);
else if (json_is_real(je))
link->next = (typeof(link->next))
json_real_value(je);
else
return -EINVAL;
}
}
return 0;
}
static int flex_item_config(json_t *jroot,
struct rte_flow_item_flex_conf *flex_conf)
{
const char *key;
json_t *jobj = NULL;
int ret = 0;
json_object_foreach(jroot, key, jobj) {
if (match_strkey(key, "tunnel")) {
ret = flex_tunnel_parse(jobj, &flex_conf->tunnel);
if (ret) {
printf("Can't parse tunnel value\n");
goto out;
}
} else if (match_strkey(key, "next_header")) {
ret = flex_field_parse(jobj, &flex_conf->next_header);
if (ret) {
printf("Can't parse next_header field\n");
goto out;
}
} else if (match_strkey(key, "next_protocol")) {
ret = flex_field_parse(jobj,
&flex_conf->next_protocol);
if (ret) {
printf("Can't parse next_protocol field\n");
goto out;
}
} else if (match_strkey(key, "sample_data")) {
json_t *ji;
uint32_t i, size = json_array_size(jobj);
for (i = 0; i < size; i++) {
ji = json_array_get(jobj, i);
ret = flex_field_parse
(ji, flex_conf->sample_data + i);
if (ret) {
printf("Can't parse sample_data field(s)\n");
goto out;
}
}
flex_conf->nb_samples = size;
} else if (match_strkey(key, "input_link")) {
json_t *ji;
uint32_t i, size = json_array_size(jobj);
for (i = 0; i < size; i++) {
ji = json_array_get(jobj, i);
ret = flex_link_parse(ji,
flex_conf->input_link + i,
FLEX_LINK_IN);
if (ret) {
printf("Can't parse input_link(s)\n");
goto out;
}
}
flex_conf->nb_inputs = size;
} else if (match_strkey(key, "output_link")) {
json_t *ji;
uint32_t i, size = json_array_size(jobj);
for (i = 0; i < size; i++) {
ji = json_array_get(jobj, i);
ret = flex_link_parse
(ji, flex_conf->output_link + i,
FLEX_LINK_OUT);
if (ret) {
printf("Can't parse output_link(s)\n");
goto out;
}
}
flex_conf->nb_outputs = size;
}
}
out:
return ret;
}
static struct flex_item *
flex_item_init(void)
{
size_t base_size, samples_size, links_size, spec_size;
struct rte_flow_item_flex_conf *conf;
struct flex_item *fp;
uint8_t (*pattern)[FLEX_MAX_FLOW_PATTERN_LENGTH];
int i;
base_size = RTE_ALIGN(sizeof(*conf), sizeof(uintptr_t));
samples_size = RTE_ALIGN(FLEX_ITEM_MAX_SAMPLES_NUM *
sizeof(conf->sample_data[0]),
sizeof(uintptr_t));
links_size = RTE_ALIGN(FLEX_ITEM_MAX_LINKS_NUM *
sizeof(conf->input_link[0]),
sizeof(uintptr_t));
/* spec & mask for all input links */
spec_size = 2 * FLEX_MAX_FLOW_PATTERN_LENGTH * FLEX_ITEM_MAX_LINKS_NUM;
fp = calloc(1, base_size + samples_size + 2 * links_size + spec_size);
if (fp == NULL) {
printf("Can't allocate memory for flex item\n");
return NULL;
}
conf = &fp->flex_conf;
conf->sample_data = (typeof(conf->sample_data))
((uint8_t *)fp + base_size);
conf->input_link = (typeof(conf->input_link))
((uint8_t *)conf->sample_data + samples_size);
conf->output_link = (typeof(conf->output_link))
((uint8_t *)conf->input_link + links_size);
pattern = (typeof(pattern))((uint8_t *)conf->output_link + links_size);
for (i = 0; i < FLEX_ITEM_MAX_LINKS_NUM; i++) {
struct rte_flow_item_flex_link *in = conf->input_link + i;
in->item.spec = pattern++;
in->item.mask = pattern++;
}
return fp;
}
static int
flex_item_build_config(struct flex_item *fp, const char *filename)
{
int ret;
json_error_t json_error;
json_t *jroot = json_load_file(filename, 0, &json_error);
if (!jroot) {
printf("Bad JSON file \"%s\": %s\n", filename, json_error.text);
return -1;
}
ret = flex_item_config(jroot, &fp->flex_conf);
json_decref(jroot);
return ret;
}
void
flex_item_create(portid_t port_id, uint16_t flex_id, const char *filename)
{
struct rte_flow_error flow_error;
struct flex_item *fp = flex_parser_fetch(port_id, flex_id);
int ret;
if (fp == FLEX_PARSER_ERR) {
printf("Bad parameters: port_id=%u flex_id=%u\n",
port_id, flex_id);
return;
}
if (fp) {
printf("port-%u: flex item #%u is already in use\n",
port_id, flex_id);
return;
}
fp = flex_item_init();
if (!fp) {
printf("Could not allocate flex item\n");
goto out;
}
ret = flex_item_build_config(fp, filename);
if (ret)
goto out;
fp->flex_handle = rte_flow_flex_item_create(port_id,
&fp->flex_conf,
&flow_error);
if (fp->flex_handle) {
flex_items[port_id][flex_id] = fp;
printf("port-%u: created flex item #%u\n", port_id, flex_id);
fp = NULL;
} else {
printf("port-%u: flex item #%u creation failed: %s\n",
port_id, flex_id,
flow_error.message ? flow_error.message : "");
}
out:
if (fp)
free(fp);
}
#else /* RTE_HAS_JANSSON */
void flex_item_create(__rte_unused portid_t port_id,
__rte_unused uint16_t flex_id,
__rte_unused const char *filename)
{
printf("no JSON library\n");
}
void flex_item_destroy(__rte_unused portid_t port_id,
__rte_unused uint16_t flex_id)
{
printf("no JSON library\n");
}
#endif /* RTE_HAS_JANSSON */
void
port_flex_item_flush(portid_t port_id)
{
uint16_t i;
for (i = 0; i < FLEX_MAX_PARSERS_NUM; i++) {
flex_item_destroy(port_id, i);
flex_items[port_id][i] = NULL;
}
}
struct flex_pattern_set {
cmdline_fixed_string_t set, flex_pattern;
cmdline_fixed_string_t is_spec, mask;
cmdline_fixed_string_t spec_data, mask_data;
uint16_t id;
};
static cmdline_parse_token_string_t flex_pattern_set_token =
TOKEN_STRING_INITIALIZER(struct flex_pattern_set, set, "set");
static cmdline_parse_token_string_t flex_pattern_token =
TOKEN_STRING_INITIALIZER(struct flex_pattern_set,
flex_pattern, "flex_pattern");
static cmdline_parse_token_string_t flex_pattern_is_token =
TOKEN_STRING_INITIALIZER(struct flex_pattern_set,
is_spec, "is");
static cmdline_parse_token_string_t flex_pattern_spec_token =
TOKEN_STRING_INITIALIZER(struct flex_pattern_set,
is_spec, "spec");
static cmdline_parse_token_string_t flex_pattern_mask_token =
TOKEN_STRING_INITIALIZER(struct flex_pattern_set, mask, "mask");
static cmdline_parse_token_string_t flex_pattern_spec_data_token =
TOKEN_STRING_INITIALIZER(struct flex_pattern_set, spec_data, NULL);
static cmdline_parse_token_string_t flex_pattern_mask_data_token =
TOKEN_STRING_INITIALIZER(struct flex_pattern_set, mask_data, NULL);
static cmdline_parse_token_num_t flex_pattern_id_token =
TOKEN_NUM_INITIALIZER(struct flex_pattern_set, id, RTE_UINT16);
/*
* flex pattern data - spec or mask is a string representation of byte array
* in hexadecimal format. Each byte in data string must have 2 characters:
* 0x15 - "15"
* 0x1 - "01"
* Bytes in data array are in network order.
*/
static uint32_t
flex_pattern_data(const char *str, uint8_t *data)
{
uint32_t i, len = strlen(str);
char b[3], *endptr;
if (len & 01)
return 0;
len /= 2;
if (len >= FLEX_MAX_FLOW_PATTERN_LENGTH)
return 0;
for (i = 0, b[2] = '\0'; i < len; i++) {
b[0] = str[2 * i];
b[1] = str[2 * i + 1];
data[i] = strtoul(b, &endptr, 16);
if (endptr != &b[2])
return 0;
}
return len;
}
static void
flex_pattern_parsed_fn(void *parsed_result,
__rte_unused struct cmdline *cl,
__rte_unused void *data)
{
struct flex_pattern_set *res = parsed_result;
struct flex_pattern *fp;
bool full_spec;
if (res->id >= FLEX_MAX_PATTERNS_NUM) {
printf("Bad flex pattern id\n");
return;
}
fp = flex_patterns + res->id;
memset(fp->spec_pattern, 0, sizeof(fp->spec_pattern));
memset(fp->mask_pattern, 0, sizeof(fp->mask_pattern));
fp->spec.length = flex_pattern_data(res->spec_data, fp->spec_pattern);
if (!fp->spec.length) {
printf("Bad flex pattern spec\n");
return;
}
full_spec = strncmp(res->is_spec, "spec", strlen("spec")) == 0;
if (full_spec) {
fp->mask.length = flex_pattern_data(res->mask_data,
fp->mask_pattern);
if (!fp->mask.length) {
printf("Bad flex pattern mask\n");
return;
}
} else {
memset(fp->mask_pattern, 0xFF, fp->spec.length);
fp->mask.length = fp->spec.length;
}
if (fp->mask.length != fp->spec.length) {
printf("Spec length do not match mask length\n");
return;
}
fp->spec.pattern = fp->spec_pattern;
fp->mask.pattern = fp->mask_pattern;
printf("created pattern #%u\n", res->id);
}
cmdline_parse_inst_t cmd_set_flex_is_pattern = {
.f = flex_pattern_parsed_fn,
.data = NULL,
.help_str = "set flex_pattern <id> is <spec_data>",
.tokens = {
(void *)&flex_pattern_set_token,
(void *)&flex_pattern_token,
(void *)&flex_pattern_id_token,
(void *)&flex_pattern_is_token,
(void *)&flex_pattern_spec_data_token,
NULL,
}
};
cmdline_parse_inst_t cmd_set_flex_spec_pattern = {
.f = flex_pattern_parsed_fn,
.data = NULL,
.help_str = "set flex_pattern <id> spec <spec_data> mask <mask_data>",
.tokens = {
(void *)&flex_pattern_set_token,
(void *)&flex_pattern_token,
(void *)&flex_pattern_id_token,
(void *)&flex_pattern_spec_token,
(void *)&flex_pattern_spec_data_token,
(void *)&flex_pattern_mask_token,
(void *)&flex_pattern_mask_data_token,
NULL,
}
};