--- /dev/null
+/* 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,
+ }
+};
COMMON_PRIORITY_LEVEL,
COMMON_INDIRECT_ACTION_ID,
COMMON_POLICY_ID,
+ COMMON_FLEX_HANDLE,
+ COMMON_FLEX_TOKEN,
/* TOP-level command. */
ADD,
AGED,
ISOLATE,
TUNNEL,
+ FLEX,
+
+ /* Flex arguments */
+ FLEX_ITEM_INIT,
+ FLEX_ITEM_CREATE,
+ FLEX_ITEM_DESTROY,
/* Tunnel arguments. */
TUNNEL_CREATE,
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,
struct {
uint32_t policy_id;
} policy;/**< Policy arguments. */
+ struct {
+ uint16_t token;
+ uintptr_t uintptr;
+ char filename[128];
+ } flex; /**< Flex arguments*/
} args; /**< Command arguments. */
};
.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,
ITEM_CONNTRACK,
ITEM_PORT_REPRESENTOR,
ITEM_REPRESENTED_PORT,
+ ITEM_FLEX,
END_SET,
ZERO,
};
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,
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);
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);
.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",
AGED,
QUERY,
ISOLATE,
- TUNNEL)),
+ TUNNEL,
+ FLEX)),
.call = parse_init,
},
/* Top-level command. */
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",
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",
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,
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,
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;
}
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;
.. 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