From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm0-f54.google.com (mail-wm0-f54.google.com [74.125.82.54]) by dpdk.org (Postfix) with ESMTP id 5F40810C79 for ; Wed, 21 Dec 2016 15:52:27 +0100 (CET) Received: by mail-wm0-f54.google.com with SMTP id g23so24624281wme.1 for ; Wed, 21 Dec 2016 06:52:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=6wind-com.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:in-reply-to:references; bh=Uhip9ZkRNSwWs7NyUvHIoRVm6/8viO5FkDo02b7O7Qc=; b=Y8vpFy4EyQLD/OwWNHtCRq+rJ8UPx10J8ZG6nHwqculBLEG05vTgbdo1u2gfFk/YXq j+fXc3T7i9maKcHO8YX52kJhwFGSOUHlYLSHJzNlzyhL04VwggHqRCPoe/VXlTZyksi7 93ieO1F3/SOka9OJvf/eGxQqwJVmZdjN8BlAqfRRTo3QrM2OR5aWSj8Gu3B+RafWGcWr eB6Dr5LNBCrzFsHQkxn3DCXywqgRyqhBnDGI1zh9t1fWKV1C0uQEXvoT03ZwcMilWyTL 6VHhglPsPM1N1//6jKHTLdwpY8vXzXORNnpCHXxgg0TXJq0PL91wET7eNbtftI5EYR6n f0CQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=Uhip9ZkRNSwWs7NyUvHIoRVm6/8viO5FkDo02b7O7Qc=; b=T1+YQ9RZKM9tlQhvcFE+EaZDHZsus84wJNPl/3i/fUZIO2XuNntujMVHpl3x9NP8OG Ent7q8NR35sY6u2UCwgAf1Y98T1XzQW2rbtc3aahNYNCW49lsiBuoAZDrn/L/+cW4zJz J0ltd/l9ZwEcn2Sd4esi/b/i5OQJJBxzBVS38Wqd0TkAnLNsgkbbm2T8SeBbNhT2bQJ0 KqQIgdmeKabO+emk/fOVWWwkbIE1WbWh6N2wwjTHc1fxF2ytatZbqjcM4cUK4liGQIef Bnuj/yEfGAK8apkzEcwZQwSbvccgH3KaYj9DIGl4iYQcKtNj3WqBIXj2h+6rWVFgrs5P JwSw== X-Gm-Message-State: AIkVDXJrbMCk0YfuyBWcuVVDVmOoyebkzEqmdDp0BYAjTLeoQ29hMVMYBzZWrI09V+WgT6xW X-Received: by 10.28.209.67 with SMTP id i64mr5431061wmg.48.1482331946531; Wed, 21 Dec 2016 06:52:26 -0800 (PST) Received: from 6wind.com (guy78-3-82-239-227-177.fbx.proxad.net. [82.239.227.177]) by smtp.gmail.com with ESMTPSA id v3sm31015775wjp.13.2016.12.21.06.52.24 for (version=TLS1_2 cipher=AES128-SHA bits=128/128); Wed, 21 Dec 2016 06:52:25 -0800 (PST) From: Adrien Mazarguil To: dev@dpdk.org Date: Wed, 21 Dec 2016 15:51:28 +0100 Message-Id: X-Mailer: git-send-email 2.1.4 In-Reply-To: References: Subject: [dpdk-dev] [PATCH v5 12/26] app/testpmd: add flow validate/create commands X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 21 Dec 2016 14:52:27 -0000 Syntax: flow (validate|create) {port_id} [group {group_id}] [priority {level}] [ingress] [egress] pattern {item} [/ {item} [...]] / end actions {action} [/ {action} [...]] / end Either check the validity of a flow rule or create it. Any number of pattern items and actions can be provided in any order. Completion is available for convenience. This commit only adds support for the most basic item and action types, namely: - END: terminates pattern items and actions lists. - VOID: item/action filler, no operation. - INVERT: inverted pattern matching, process packets that do not match. - PASSTHRU: action that leaves packets up for additional processing by subsequent flow rules. Signed-off-by: Adrien Mazarguil Acked-by: Olga Shern --- app/test-pmd/cmdline.c | 14 ++ app/test-pmd/cmdline_flow.c | 314 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 327 insertions(+), 1 deletion(-) diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index 80ddda2..23f4b48 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -811,6 +811,20 @@ static void cmd_help_long_parsed(void *parsed_result, " (select|add)\n" " Set the input set for FDir.\n\n" + "flow validate {port_id}" + " [group {group_id}] [priority {level}]" + " [ingress] [egress]" + " pattern {item} [/ {item} [...]] / end" + " actions {action} [/ {action} [...]] / end\n" + " Check whether a flow rule can be created.\n\n" + + "flow create {port_id}" + " [group {group_id}] [priority {level}]" + " [ingress] [egress]" + " pattern {item} [/ {item} [...]] / end" + " actions {action} [/ {action} [...]] / end\n" + " Create a flow rule.\n\n" + "flow destroy {port_id} rule {rule_id} [...]\n" " Destroy specific flow rules.\n\n" diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c index 5c45b3a..dc68685 100644 --- a/app/test-pmd/cmdline_flow.c +++ b/app/test-pmd/cmdline_flow.c @@ -59,11 +59,14 @@ enum index { RULE_ID, PORT_ID, GROUP_ID, + PRIORITY_LEVEL, /* Top-level command. */ FLOW, /* Sub-level commands. */ + VALIDATE, + CREATE, DESTROY, FLUSH, LIST, @@ -73,6 +76,26 @@ enum index { /* List arguments. */ LIST_GROUP, + + /* Validate/create arguments. */ + GROUP, + PRIORITY, + INGRESS, + EGRESS, + + /* Validate/create pattern. */ + PATTERN, + ITEM_NEXT, + ITEM_END, + ITEM_VOID, + ITEM_INVERT, + + /* Validate/create actions. */ + ACTIONS, + ACTION_NEXT, + ACTION_END, + ACTION_VOID, + ACTION_PASSTHRU, }; /** Maximum number of subsequent tokens and arguments on the stack. */ @@ -92,6 +115,7 @@ struct context { uint32_t eol:1; /**< EOL has been detected. */ uint32_t last:1; /**< No more arguments. */ uint16_t port; /**< Current port ID (for completions). */ + uint32_t objdata; /**< Object-specific data. */ void *object; /**< Address of current object for relative offsets. */ }; @@ -109,6 +133,8 @@ struct token { const char *type; /** Help displayed during completion (defaults to token name). */ const char *help; + /** Private data used by parser functions. */ + const void *priv; /** * Lists of subsequent tokens to push on the stack. Each call to the * parser consumes the last entry of that stack. @@ -170,6 +196,14 @@ struct buffer { uint16_t port; /**< Affected port ID. */ union { struct { + struct rte_flow_attr attr; + struct rte_flow_item *pattern; + struct rte_flow_action *actions; + uint32_t pattern_n; + uint32_t actions_n; + uint8_t *data; + } vc; /**< Validate/create arguments. */ + struct { uint32_t *rule; uint32_t rule_n; } destroy; /**< Destroy arguments. */ @@ -180,6 +214,39 @@ struct buffer { } args; /**< Command arguments. */ }; +/** Private data for pattern items. */ +struct parse_item_priv { + enum rte_flow_item_type type; /**< Item type. */ + uint32_t size; /**< Size of item specification structure. */ +}; + +#define PRIV_ITEM(t, s) \ + (&(const struct parse_item_priv){ \ + .type = RTE_FLOW_ITEM_TYPE_ ## t, \ + .size = s, \ + }) + +/** Private data for actions. */ +struct parse_action_priv { + enum rte_flow_action_type type; /**< Action type. */ + uint32_t size; /**< Size of action configuration structure. */ +}; + +#define PRIV_ACTION(t, s) \ + (&(const struct parse_action_priv){ \ + .type = RTE_FLOW_ACTION_TYPE_ ## t, \ + .size = s, \ + }) + +static const enum index next_vc_attr[] = { + GROUP, + PRIORITY, + INGRESS, + EGRESS, + PATTERN, + ZERO, +}; + static const enum index next_destroy_attr[] = { DESTROY_RULE, END, @@ -192,9 +259,26 @@ static const enum index next_list_attr[] = { ZERO, }; +static const enum index next_item[] = { + ITEM_END, + ITEM_VOID, + ITEM_INVERT, + ZERO, +}; + +static const enum index next_action[] = { + ACTION_END, + ACTION_VOID, + ACTION_PASSTHRU, + ZERO, +}; + static int parse_init(struct context *, const struct token *, const char *, unsigned int, void *, unsigned int); +static int parse_vc(struct context *, const struct token *, + const char *, unsigned int, + void *, unsigned int); static int parse_destroy(struct context *, const struct token *, const char *, unsigned int, void *, unsigned int); @@ -266,18 +350,41 @@ static const struct token token_list[] = { .call = parse_int, .comp = comp_none, }, + [PRIORITY_LEVEL] = { + .name = "{level}", + .type = "PRIORITY", + .help = "priority level", + .call = parse_int, + .comp = comp_none, + }, /* Top-level command. */ [FLOW] = { .name = "flow", .type = "{command} {port_id} [{arg} [...]]", .help = "manage ingress/egress flow rules", .next = NEXT(NEXT_ENTRY - (DESTROY, + (VALIDATE, + CREATE, + DESTROY, FLUSH, LIST)), .call = parse_init, }, /* Sub-level commands. */ + [VALIDATE] = { + .name = "validate", + .help = "check whether a flow rule can be created", + .next = NEXT(next_vc_attr, NEXT_ENTRY(PORT_ID)), + .args = ARGS(ARGS_ENTRY(struct buffer, port)), + .call = parse_vc, + }, + [CREATE] = { + .name = "create", + .help = "create a flow rule", + .next = NEXT(next_vc_attr, NEXT_ENTRY(PORT_ID)), + .args = ARGS(ARGS_ENTRY(struct buffer, port)), + .call = parse_vc, + }, [DESTROY] = { .name = "destroy", .help = "destroy specific flow rules", @@ -315,6 +422,98 @@ static const struct token token_list[] = { .args = ARGS(ARGS_ENTRY_PTR(struct buffer, args.list.group)), .call = parse_list, }, + /* Validate/create attributes. */ + [GROUP] = { + .name = "group", + .help = "specify a group", + .next = NEXT(next_vc_attr, NEXT_ENTRY(GROUP_ID)), + .args = ARGS(ARGS_ENTRY(struct rte_flow_attr, group)), + .call = parse_vc, + }, + [PRIORITY] = { + .name = "priority", + .help = "specify a priority level", + .next = NEXT(next_vc_attr, NEXT_ENTRY(PRIORITY_LEVEL)), + .args = ARGS(ARGS_ENTRY(struct rte_flow_attr, priority)), + .call = parse_vc, + }, + [INGRESS] = { + .name = "ingress", + .help = "affect rule to ingress", + .next = NEXT(next_vc_attr), + .call = parse_vc, + }, + [EGRESS] = { + .name = "egress", + .help = "affect rule to egress", + .next = NEXT(next_vc_attr), + .call = parse_vc, + }, + /* Validate/create pattern. */ + [PATTERN] = { + .name = "pattern", + .help = "submit a list of pattern items", + .next = NEXT(next_item), + .call = parse_vc, + }, + [ITEM_NEXT] = { + .name = "/", + .help = "specify next pattern item", + .next = NEXT(next_item), + }, + [ITEM_END] = { + .name = "end", + .help = "end list of pattern items", + .priv = PRIV_ITEM(END, 0), + .next = NEXT(NEXT_ENTRY(ACTIONS)), + .call = parse_vc, + }, + [ITEM_VOID] = { + .name = "void", + .help = "no-op pattern item", + .priv = PRIV_ITEM(VOID, 0), + .next = NEXT(NEXT_ENTRY(ITEM_NEXT)), + .call = parse_vc, + }, + [ITEM_INVERT] = { + .name = "invert", + .help = "perform actions when pattern does not match", + .priv = PRIV_ITEM(INVERT, 0), + .next = NEXT(NEXT_ENTRY(ITEM_NEXT)), + .call = parse_vc, + }, + /* Validate/create actions. */ + [ACTIONS] = { + .name = "actions", + .help = "submit a list of associated actions", + .next = NEXT(next_action), + .call = parse_vc, + }, + [ACTION_NEXT] = { + .name = "/", + .help = "specify next action", + .next = NEXT(next_action), + }, + [ACTION_END] = { + .name = "end", + .help = "end list of actions", + .priv = PRIV_ACTION(END, 0), + .call = parse_vc, + }, + [ACTION_VOID] = { + .name = "void", + .help = "no-op action", + .priv = PRIV_ACTION(VOID, 0), + .next = NEXT(NEXT_ENTRY(ACTION_NEXT)), + .call = parse_vc, + }, + [ACTION_PASSTHRU] = { + .name = "passthru", + .help = "let subsequent rule process matched packets", + .priv = PRIV_ACTION(PASSTHRU, 0), + .next = NEXT(NEXT_ENTRY(ACTION_NEXT)), + .call = parse_vc, + }, }; /** Remove and return last entry from argument stack. */ @@ -368,10 +567,108 @@ parse_init(struct context *ctx, const struct token *token, /* Initialize buffer. */ memset(out, 0x00, sizeof(*out)); memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out)); + ctx->objdata = 0; ctx->object = out; return len; } +/** Parse tokens for validate/create commands. */ +static int +parse_vc(struct context *ctx, const struct token *token, + const char *str, unsigned int len, + void *buf, unsigned int size) +{ + struct buffer *out = buf; + uint8_t *data; + uint32_t data_size; + + /* 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) { + if (ctx->curr != VALIDATE && ctx->curr != CREATE) + return -1; + if (sizeof(*out) > size) + return -1; + out->command = ctx->curr; + ctx->objdata = 0; + ctx->object = out; + out->args.vc.data = (uint8_t *)out + size; + return len; + } + ctx->objdata = 0; + ctx->object = &out->args.vc.attr; + switch (ctx->curr) { + case GROUP: + case PRIORITY: + return len; + case INGRESS: + out->args.vc.attr.ingress = 1; + return len; + case EGRESS: + out->args.vc.attr.egress = 1; + return len; + case PATTERN: + out->args.vc.pattern = + (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1), + sizeof(double)); + ctx->object = out->args.vc.pattern; + return len; + case ACTIONS: + out->args.vc.actions = + (void *)RTE_ALIGN_CEIL((uintptr_t) + (out->args.vc.pattern + + out->args.vc.pattern_n), + sizeof(double)); + ctx->object = out->args.vc.actions; + return len; + default: + if (!token->priv) + return -1; + break; + } + if (!out->args.vc.actions) { + const struct parse_item_priv *priv = token->priv; + struct rte_flow_item *item = + out->args.vc.pattern + out->args.vc.pattern_n; + + data_size = priv->size * 3; /* spec, last, mask */ + data = (void *)RTE_ALIGN_FLOOR((uintptr_t) + (out->args.vc.data - data_size), + sizeof(double)); + if ((uint8_t *)item + sizeof(*item) > data) + return -1; + *item = (struct rte_flow_item){ + .type = priv->type, + }; + ++out->args.vc.pattern_n; + ctx->object = item; + } else { + const struct parse_action_priv *priv = token->priv; + struct rte_flow_action *action = + out->args.vc.actions + out->args.vc.actions_n; + + data_size = priv->size; /* configuration */ + data = (void *)RTE_ALIGN_FLOOR((uintptr_t) + (out->args.vc.data - data_size), + sizeof(double)); + if ((uint8_t *)action + sizeof(*action) > data) + return -1; + *action = (struct rte_flow_action){ + .type = priv->type, + }; + ++out->args.vc.actions_n; + ctx->object = action; + } + memset(data, 0, data_size); + out->args.vc.data = data; + ctx->objdata = data_size; + return len; +} + /** Parse tokens for destroy command. */ static int parse_destroy(struct context *ctx, const struct token *token, @@ -392,6 +689,7 @@ parse_destroy(struct context *ctx, const struct token *token, if (sizeof(*out) > size) return -1; out->command = ctx->curr; + ctx->objdata = 0; ctx->object = out; out->args.destroy.rule = (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1), @@ -401,6 +699,7 @@ parse_destroy(struct context *ctx, const struct token *token, if (((uint8_t *)(out->args.destroy.rule + out->args.destroy.rule_n) + sizeof(*out->args.destroy.rule)) > (uint8_t *)out + size) return -1; + ctx->objdata = 0; ctx->object = out->args.destroy.rule + out->args.destroy.rule_n++; return len; } @@ -425,6 +724,7 @@ parse_flush(struct context *ctx, const struct token *token, if (sizeof(*out) > size) return -1; out->command = ctx->curr; + ctx->objdata = 0; ctx->object = out; } return len; @@ -450,6 +750,7 @@ parse_list(struct context *ctx, const struct token *token, if (sizeof(*out) > size) return -1; out->command = ctx->curr; + ctx->objdata = 0; ctx->object = out; out->args.list.group = (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1), @@ -459,6 +760,7 @@ parse_list(struct context *ctx, const struct token *token, if (((uint8_t *)(out->args.list.group + out->args.list.group_n) + sizeof(*out->args.list.group)) > (uint8_t *)out + size) return -1; + ctx->objdata = 0; ctx->object = out->args.list.group + out->args.list.group_n++; return len; } @@ -526,6 +828,7 @@ parse_port(struct context *ctx, const struct token *token, if (buf) out = buf; else { + ctx->objdata = 0; ctx->object = out; size = sizeof(*out); } @@ -613,6 +916,7 @@ cmd_flow_context_init(struct context *ctx) ctx->eol = 0; ctx->last = 0; ctx->port = 0; + ctx->objdata = 0; ctx->object = NULL; } @@ -836,6 +1140,14 @@ static void cmd_flow_parsed(const struct buffer *in) { switch (in->command) { + case VALIDATE: + port_flow_validate(in->port, &in->args.vc.attr, + in->args.vc.pattern, in->args.vc.actions); + break; + case CREATE: + port_flow_create(in->port, &in->args.vc.attr, + in->args.vc.pattern, in->args.vc.actions); + break; case DESTROY: port_flow_destroy(in->port, in->args.destroy.rule_n, in->args.destroy.rule); -- 2.1.4