From 59f3a8acbcdbafeebe816a26d76dfb06e6450f31 Mon Sep 17 00:00:00 2001 From: Gregory Etelson Date: Wed, 20 Oct 2021 18:14:57 +0300 Subject: [PATCH] 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 testpmd> set flex_pattern \ spec mask testpmd> set flex_pattern is testpmd> flow create ... \ / flex item is pattern is / ... 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 Reviewed-by: Viacheslav Ovsiienko --- app/test-pmd/cmd_flex_item.c | 548 ++++++++++++++++++++ app/test-pmd/cmdline.c | 2 + app/test-pmd/cmdline_flow.c | 223 +++++++- app/test-pmd/meson.build | 6 + app/test-pmd/testpmd.c | 2 +- app/test-pmd/testpmd.h | 30 ++ doc/guides/testpmd_app_ug/testpmd_funcs.rst | 119 +++++ 7 files changed, 928 insertions(+), 2 deletions(-) create mode 100644 app/test-pmd/cmd_flex_item.c diff --git a/app/test-pmd/cmd_flex_item.c b/app/test-pmd/cmd_flex_item.c new file mode 100644 index 0000000000..45103e45a8 --- /dev/null +++ b/app/test-pmd/cmd_flex_item.c @@ -0,0 +1,548 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2021 NVIDIA Corporation & Affiliates + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 is ", + .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 spec mask ", + .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, + } +}; diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index 88354ccab9..3221f6e1aa 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -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, }; diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c index cd640b9b7a..5437975837 100644 --- a/app/test-pmd/cmdline_flow.c +++ b/app/test-pmd/cmdline_flow.c @@ -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; diff --git a/app/test-pmd/meson.build b/app/test-pmd/meson.build index 98f3289bdf..1ad54caef2 100644 --- a/app/test-pmd/meson.build +++ b/app/test-pmd/meson.build @@ -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 diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index de7a8c2955..af0e79fe6d 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -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); diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index 81be754605..e3995d24ab 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -14,6 +14,9 @@ #include #include #include +#ifdef RTE_HAS_JANSSON +#include +#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, diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst index 22ba8f0516..6d127d9a7b 100644 --- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst +++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst @@ -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 + 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 is + 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