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>
This commit is contained in:
Gregory Etelson 2021-10-20 18:14:57 +03:00 committed by Ferruh Yigit
parent 2566c33c71
commit 59f3a8acbc
7 changed files with 928 additions and 2 deletions

View File

@ -0,0 +1,548 @@
/* 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,
}
};

View File

@ -17861,6 +17861,8 @@ cmdline_parse_ctx_t main_ctx[] = {
(cmdline_parse_inst_t *)&cmd_show_fec_mode,
(cmdline_parse_inst_t *)&cmd_set_fec_mode,
(cmdline_parse_inst_t *)&cmd_show_capability,
(cmdline_parse_inst_t *)&cmd_set_flex_is_pattern,
(cmdline_parse_inst_t *)&cmd_set_flex_spec_pattern,
NULL,
};

View File

@ -54,6 +54,8 @@ enum index {
COMMON_PRIORITY_LEVEL,
COMMON_INDIRECT_ACTION_ID,
COMMON_POLICY_ID,
COMMON_FLEX_HANDLE,
COMMON_FLEX_TOKEN,
/* TOP-level command. */
ADD,
@ -81,6 +83,12 @@ enum index {
AGED,
ISOLATE,
TUNNEL,
FLEX,
/* Flex arguments */
FLEX_ITEM_INIT,
FLEX_ITEM_CREATE,
FLEX_ITEM_DESTROY,
/* Tunnel arguments. */
TUNNEL_CREATE,
@ -310,6 +318,9 @@ enum index {
ITEM_PORT_REPRESENTOR_PORT_ID,
ITEM_REPRESENTED_PORT,
ITEM_REPRESENTED_PORT_ETHDEV_PORT_ID,
ITEM_FLEX,
ITEM_FLEX_ITEM_HANDLE,
ITEM_FLEX_PATTERN_HANDLE,
/* Validate/create actions. */
ACTIONS,
@ -860,6 +871,11 @@ struct buffer {
struct {
uint32_t policy_id;
} policy;/**< Policy arguments. */
struct {
uint16_t token;
uintptr_t uintptr;
char filename[128];
} flex; /**< Flex arguments*/
} args; /**< Command arguments. */
};
@ -887,6 +903,13 @@ struct parse_action_priv {
.size = s, \
})
static const enum index next_flex_item[] = {
FLEX_ITEM_INIT,
FLEX_ITEM_CREATE,
FLEX_ITEM_DESTROY,
ZERO,
};
static const enum index next_ia_create_attr[] = {
INDIRECT_ACTION_CREATE_ID,
INDIRECT_ACTION_INGRESS,
@ -1018,6 +1041,7 @@ static const enum index next_item[] = {
ITEM_CONNTRACK,
ITEM_PORT_REPRESENTOR,
ITEM_REPRESENTED_PORT,
ITEM_FLEX,
END_SET,
ZERO,
};
@ -1398,6 +1422,13 @@ static const enum index item_represented_port[] = {
ZERO,
};
static const enum index item_flex[] = {
ITEM_FLEX_PATTERN_HANDLE,
ITEM_FLEX_ITEM_HANDLE,
ITEM_NEXT,
ZERO,
};
static const enum index next_action[] = {
ACTION_END,
ACTION_VOID,
@ -1768,6 +1799,9 @@ static int parse_set_sample_action(struct context *, const struct token *,
static int parse_set_init(struct context *, const struct token *,
const char *, unsigned int,
void *, unsigned int);
static int
parse_flex_handle(struct context *, const struct token *,
const char *, unsigned int, void *, unsigned int);
static int parse_init(struct context *, const struct token *,
const char *, unsigned int,
void *, unsigned int);
@ -1884,6 +1918,8 @@ static int parse_isolate(struct context *, const struct token *,
static int parse_tunnel(struct context *, const struct token *,
const char *, unsigned int,
void *, unsigned int);
static int parse_flex(struct context *, const struct token *,
const char *, unsigned int, void *, unsigned int);
static int parse_int(struct context *, const struct token *,
const char *, unsigned int,
void *, unsigned int);
@ -2084,6 +2120,20 @@ static const struct token token_list[] = {
.call = parse_int,
.comp = comp_none,
},
[COMMON_FLEX_TOKEN] = {
.name = "{flex token}",
.type = "flex token",
.help = "flex token",
.call = parse_int,
.comp = comp_none,
},
[COMMON_FLEX_HANDLE] = {
.name = "{flex handle}",
.type = "FLEX HANDLE",
.help = "fill flex item data",
.call = parse_flex_handle,
.comp = comp_none,
},
/* Top-level command. */
[FLOW] = {
.name = "flow",
@ -2100,7 +2150,8 @@ static const struct token token_list[] = {
AGED,
QUERY,
ISOLATE,
TUNNEL)),
TUNNEL,
FLEX)),
.call = parse_init,
},
/* Top-level command. */
@ -2212,6 +2263,41 @@ static const struct token token_list[] = {
ARGS_ENTRY(struct buffer, port)),
.call = parse_isolate,
},
[FLEX] = {
.name = "flex_item",
.help = "flex item API",
.next = NEXT(next_flex_item),
.call = parse_flex,
},
[FLEX_ITEM_INIT] = {
.name = "init",
.help = "flex item init",
.args = ARGS(ARGS_ENTRY(struct buffer, args.flex.token),
ARGS_ENTRY(struct buffer, port)),
.next = NEXT(NEXT_ENTRY(COMMON_FLEX_TOKEN),
NEXT_ENTRY(COMMON_PORT_ID)),
.call = parse_flex
},
[FLEX_ITEM_CREATE] = {
.name = "create",
.help = "flex item create",
.args = ARGS(ARGS_ENTRY(struct buffer, args.flex.filename),
ARGS_ENTRY(struct buffer, args.flex.token),
ARGS_ENTRY(struct buffer, port)),
.next = NEXT(NEXT_ENTRY(COMMON_FILE_PATH),
NEXT_ENTRY(COMMON_FLEX_TOKEN),
NEXT_ENTRY(COMMON_PORT_ID)),
.call = parse_flex
},
[FLEX_ITEM_DESTROY] = {
.name = "destroy",
.help = "flex item destroy",
.args = ARGS(ARGS_ENTRY(struct buffer, args.flex.token),
ARGS_ENTRY(struct buffer, port)),
.next = NEXT(NEXT_ENTRY(COMMON_FLEX_TOKEN),
NEXT_ENTRY(COMMON_PORT_ID)),
.call = parse_flex
},
[TUNNEL] = {
.name = "tunnel",
.help = "new tunnel API",
@ -3682,6 +3768,27 @@ static const struct token token_list[] = {
item_param),
.args = ARGS(ARGS_ENTRY(struct rte_flow_item_ethdev, port_id)),
},
[ITEM_FLEX] = {
.name = "flex",
.help = "match flex header",
.priv = PRIV_ITEM(FLEX, sizeof(struct rte_flow_item_flex)),
.next = NEXT(item_flex),
.call = parse_vc,
},
[ITEM_FLEX_ITEM_HANDLE] = {
.name = "item",
.help = "flex item handle",
.next = NEXT(item_flex, NEXT_ENTRY(COMMON_FLEX_HANDLE),
NEXT_ENTRY(ITEM_PARAM_IS)),
.args = ARGS(ARGS_ENTRY(struct rte_flow_item_flex, handle)),
},
[ITEM_FLEX_PATTERN_HANDLE] = {
.name = "pattern",
.help = "flex pattern handle",
.next = NEXT(item_flex, NEXT_ENTRY(COMMON_FLEX_HANDLE),
NEXT_ENTRY(ITEM_PARAM_IS)),
.args = ARGS(ARGS_ENTRY(struct rte_flow_item_flex, pattern)),
},
/* Validate/create actions. */
[ACTIONS] = {
.name = "actions",
@ -7113,6 +7220,43 @@ parse_isolate(struct context *ctx, const struct token *token,
return len;
}
static int
parse_flex(struct context *ctx, const struct token *token,
const char *str, unsigned int len,
void *buf, unsigned int size)
{
struct buffer *out = buf;
/* Token name must match. */
if (parse_default(ctx, token, str, len, NULL, 0) < 0)
return -1;
/* Nothing else to do if there is no buffer. */
if (!out)
return len;
if (out->command == ZERO) {
if (ctx->curr != FLEX)
return -1;
if (sizeof(*out) > size)
return -1;
out->command = ctx->curr;
ctx->objdata = 0;
ctx->object = out;
ctx->objmask = NULL;
} else {
switch (ctx->curr) {
default:
break;
case FLEX_ITEM_INIT:
case FLEX_ITEM_CREATE:
case FLEX_ITEM_DESTROY:
out->command = ctx->curr;
break;
}
}
return len;
}
static int
parse_tunnel(struct context *ctx, const struct token *token,
const char *str, unsigned int len,
@ -7778,6 +7922,71 @@ parse_set_init(struct context *ctx, const struct token *token,
return len;
}
/*
* Replace testpmd handles in a flex flow item with real values.
*/
static int
parse_flex_handle(struct context *ctx, const struct token *token,
const char *str, unsigned int len,
void *buf, unsigned int size)
{
struct rte_flow_item_flex *spec, *mask;
const struct rte_flow_item_flex *src_spec, *src_mask;
const struct arg *arg = pop_args(ctx);
uint32_t offset;
uint16_t handle;
int ret;
if (!arg) {
printf("Bad environment\n");
return -1;
}
offset = arg->offset;
push_args(ctx, arg);
ret = parse_int(ctx, token, str, len, buf, size);
if (ret <= 0 || !ctx->object)
return ret;
if (ctx->port >= RTE_MAX_ETHPORTS) {
printf("Bad port\n");
return -1;
}
if (offset == offsetof(struct rte_flow_item_flex, handle)) {
const struct flex_item *fp;
struct rte_flow_item_flex *item_flex = ctx->object;
handle = (uint16_t)(uintptr_t)item_flex->handle;
if (handle >= FLEX_MAX_PARSERS_NUM) {
printf("Bad flex item handle\n");
return -1;
}
fp = flex_items[ctx->port][handle];
if (!fp) {
printf("Bad flex item handle\n");
return -1;
}
item_flex->handle = fp->flex_handle;
} else if (offset == offsetof(struct rte_flow_item_flex, pattern)) {
handle = (uint16_t)(uintptr_t)
((struct rte_flow_item_flex *)ctx->object)->pattern;
if (handle >= FLEX_MAX_PATTERNS_NUM) {
printf("Bad pattern handle\n");
return -1;
}
src_spec = &flex_patterns[handle].spec;
src_mask = &flex_patterns[handle].mask;
spec = ctx->object;
mask = spec + 2; /* spec, last, mask */
/* fill flow rule spec and mask parameters */
spec->length = src_spec->length;
spec->pattern = src_spec->pattern;
mask->length = src_mask->length;
mask->pattern = src_mask->pattern;
} else {
printf("Bad arguments - unknown flex item offset\n");
return -1;
}
return ret;
}
/** No completion. */
static int
comp_none(struct context *ctx, const struct token *token,
@ -8305,6 +8514,13 @@ cmd_flow_parsed(const struct buffer *in)
port_meter_policy_add(in->port, in->args.policy.policy_id,
in->args.vc.actions);
break;
case FLEX_ITEM_CREATE:
flex_item_create(in->port, in->args.flex.token,
in->args.flex.filename);
break;
case FLEX_ITEM_DESTROY:
flex_item_destroy(in->port, in->args.flex.token);
break;
default:
break;
}
@ -8760,6 +8976,11 @@ cmd_set_raw_parsed(const struct buffer *in)
case RTE_FLOW_ITEM_TYPE_PFCP:
size = sizeof(struct rte_flow_item_pfcp);
break;
case RTE_FLOW_ITEM_TYPE_FLEX:
size = item->spec ?
((const struct rte_flow_item_flex *)
item->spec)->length : 0;
break;
default:
fprintf(stderr, "Error - Not supported item\n");
goto error;

View File

@ -10,6 +10,7 @@ sources = files(
'cmdline_flow.c',
'cmdline_mtr.c',
'cmdline_tm.c',
'cmd_flex_item.c',
'config.c',
'csumonly.c',
'flowgen.c',
@ -61,3 +62,8 @@ if dpdk_conf.has('RTE_LIB_BPF')
sources += files('bpf_cmd.c')
deps += 'bpf'
endif
jansson_dep = dependency('jansson', required: false, method: 'pkg-config')
if jansson_dep.found()
dpdk_conf.set('RTE_HAS_JANSSON', 1)
ext_deps += jansson_dep
endif

View File

@ -3118,6 +3118,7 @@ close_port(portid_t pid)
if (is_proc_primary()) {
port_flow_flush(pi);
port_flex_item_flush(pi);
rte_eth_dev_close(pi);
}
@ -4223,7 +4224,6 @@ main(int argc, char** argv)
rte_stats_bitrate_reg(bitrate_data);
}
#endif
#ifdef RTE_LIB_CMDLINE
if (strlen(cmdline_filename) != 0)
cmdline_read_from_file(cmdline_filename);

View File

@ -14,6 +14,9 @@
#include <rte_os_shim.h>
#include <cmdline.h>
#include <sys/queue.h>
#ifdef RTE_HAS_JANSSON
#include <jansson.h>
#endif
#define RTE_PORT_ALL (~(portid_t)0x0)
@ -295,6 +298,27 @@ struct fwd_engine {
packet_fwd_t packet_fwd; /**< Mandatory. */
};
#define FLEX_ITEM_MAX_SAMPLES_NUM 16
#define FLEX_ITEM_MAX_LINKS_NUM 16
#define FLEX_MAX_FLOW_PATTERN_LENGTH 64
#define FLEX_MAX_PARSERS_NUM 8
#define FLEX_MAX_PATTERNS_NUM 64
#define FLEX_PARSER_ERR ((struct flex_item *)-1)
struct flex_item {
struct rte_flow_item_flex_conf flex_conf;
struct rte_flow_item_flex_handle *flex_handle;
uint32_t flex_id;
};
struct flex_pattern {
struct rte_flow_item_flex spec, mask;
uint8_t spec_pattern[FLEX_MAX_FLOW_PATTERN_LENGTH];
uint8_t mask_pattern[FLEX_MAX_FLOW_PATTERN_LENGTH];
};
extern struct flex_item *flex_items[RTE_MAX_ETHPORTS][FLEX_MAX_PARSERS_NUM];
extern struct flex_pattern flex_patterns[FLEX_MAX_PATTERNS_NUM];
#define BURST_TX_WAIT_US 1
#define BURST_TX_RETRIES 64
@ -319,6 +343,8 @@ extern struct fwd_engine * fwd_engines[]; /**< NULL terminated array. */
extern cmdline_parse_inst_t cmd_set_raw;
extern cmdline_parse_inst_t cmd_show_set_raw;
extern cmdline_parse_inst_t cmd_show_set_raw_all;
extern cmdline_parse_inst_t cmd_set_flex_is_pattern;
extern cmdline_parse_inst_t cmd_set_flex_spec_pattern;
extern uint16_t mempool_flags;
@ -1046,6 +1072,10 @@ uint16_t tx_pkt_set_dynf(uint16_t port_id, __rte_unused uint16_t queue,
void add_tx_dynf_callback(portid_t portid);
void remove_tx_dynf_callback(portid_t portid);
int update_mtu_from_frame_size(portid_t portid, uint32_t max_rx_pktlen);
int update_jumbo_frame_offload(portid_t portid);
void flex_item_create(portid_t port_id, uint16_t flex_id, const char *filename);
void flex_item_destroy(portid_t port_id, uint16_t flex_id);
void port_flex_item_flush(portid_t port_id);
extern int flow_parse(const char *src, void *result, unsigned int size,
struct rte_flow_attr **attr,

View File

@ -5091,3 +5091,122 @@ For example to unload BPF filter from TX queue 0, port 0:
.. code-block:: console
testpmd> bpf-unload tx 0 0
Flex Item Functions
-------------------
The following sections show functions that configure and create flex item object,
create flex pattern and use it in a flow rule.
The commands will use 20 bytes IPv4 header for examples:
::
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ver | IHL | TOS | length | +0
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| identification | flg | frag. offset | +4
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TTL | protocol | checksum | +8
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| source IP address | +12
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| destination IP address | +16
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Create flex item
~~~~~~~~~~~~~~~~
Flex item object is created by PMD according to a new header configuration. The
header configuration is compiled by the testpmd and stored in
``rte_flow_item_flex_conf`` type variable.
::
# flow flex_item create <port> <flex id> <configuration file>
testpmd> flow flex_item init 0 3 ipv4_flex_config.json
port-0: created flex item #3
Flex item configuration is kept in external JSON file.
It describes the following header elements:
**New header length.**
Specify whether the new header has fixed or variable length and the basic/minimal
header length value.
If header length is not fixed, header location with a value that completes header
length calculation and scale/offset function must be added.
Scale function depends on port hardware.
**Next protocol.**
Describes location in the new header that specify following network header type.
**Flow match samples.**
Describes locations in the new header that will be used in flow rules.
Number of flow samples and sample maximal length depend of port hardware.
**Input trigger.**
Describes preceding network header configuration.
**Output trigger.**
Describes conditions that trigger transfer to following network header
.. code-block:: json
{
"next_header": { "field_mode": "FIELD_MODE_FIXED", "field_size": 20},
"next_protocol": {"field_size": 8, "field_base": 72},
"sample_data": [
{ "field_mode": "FIELD_MODE_FIXED", "field_size": 32, "field_base": 0},
{ "field_mode": "FIELD_MODE_FIXED", "field_size": 32, "field_base": 32},
{ "field_mode": "FIELD_MODE_FIXED", "field_size": 32, "field_base": 64},
{ "field_mode": "FIELD_MODE_FIXED", "field_size": 32, "field_base": 96}
],
"input_link": [
{"item": "eth type is 0x0800"},
{"item": "vlan inner_type is 0x0800"}
],
"output_link": [
{"item": "udp", "next": 17},
{"item": "tcp", "next": 6},
{"item": "icmp", "next": 1}
]
}
Flex pattern and flow rules
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Flex pattern describe parts of network header that will trigger flex flow item hit in a flow rule.
Flex pattern directly related to flex item samples configuration.
Flex pattern can be shared between ports.
**Flex pattern and flow rule to match IPv4 version and 20 bytes length**
::
# set flex_pattern <pattern_id> is <hex bytes sequence>
testpmd> flow flex_item pattern 5 is 45FF
created pattern #5
testpmd> flow create 0 ingress pattern eth / ipv4 / udp / flex item is 3 pattern is 5 / end actions mark id 1 / queue index 0 / end
Flow rule #0 created
**Flex pattern and flow rule to match packets with source address 1.2.3.4**
::
testpmd> flow flex_item pattern 2 spec 45000000000000000000000001020304 mask FF0000000000000000000000FFFFFFFF
created pattern #2
testpmd> flow create 0 ingress pattern eth / ipv4 / udp / flex item is 3 pattern is 2 / end actions mark id 1 / queue index 0 / end
Flow rule #0 created