From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm0-f45.google.com (mail-wm0-f45.google.com [74.125.82.45]) by dpdk.org (Postfix) with ESMTP id B33D95681 for ; Wed, 16 Nov 2016 17:24:29 +0100 (CET) Received: by mail-wm0-f45.google.com with SMTP id a197so249922477wmd.0 for ; Wed, 16 Nov 2016 08:24:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=6wind-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=BdeR6yzyp8lgjkF1TLlcQ7ywn3llu4p6NnbR5eKh2Xw=; b=dJ3pXayarQiVC3f3cKkej51WnbMMLdz6pblgho/L9DHDdiB6xyki/HFz92G9beHUOw aUxxEOLBW/Szl9sHqMHz4KHv+jzzw4Txb+Hi4Q2V2y8DPu+AM5cmvKb07PcP0ROrWc/Y GXHVfJYjIHRdeGKcqX3y/cajkIZnMIvRtmGZTJqHWdm9yH7rXnmbBSWMpZFVA3E1xdnF Sn+YxV5mcAUMJdyf3gj+zYy8FWnmEiAACozvBCwn5gB+2wbDfRH17oFHFJ9EQcfTAWUX oxdy9F6t6FuWBJldPjHCnj2bOUj4thgkPAUMH07+l9XUx2b9KTbl6w2uxw2ggus5QazD kGIA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=BdeR6yzyp8lgjkF1TLlcQ7ywn3llu4p6NnbR5eKh2Xw=; b=HbqVWptfw4GBVa2/MuAgwV0lOSDaD9c44qZfZoOWXB2Sw78LGJ+C7kgWwgsgCV2I1z a8vBGcASpAuzVpSAJOQmn6b2jMmDfjWnrKmRMI3TNFJWr5FHA5ORVjxmz6BYVpsveTc3 Hdqz0WiMYdsXWPKfrrgdrsXO8y9E80Ep8U4mBqCrWwVJzfYSJg2/8rb5bUyCrtiE5tOK yhv84mfmv+apI1jm3FWHS56OitktdKc2OCzFPFL/qPqKBiomUEPYTNzJQhv8cDJPYW6c 4CffEE3+h3Lbzee0TKPPcr6S/i+YXwW4XWQtrhEbcQ9hyDC9yIxWkkja0+/r2pVf5xgS wq3Q== X-Gm-Message-State: ABUngvf20B521nrFuPojeGPf59GncYgrg8/wVQD0FcGl7vJT7lJNm0diKfgIzzoUI9Xw5Dqs X-Received: by 10.28.173.131 with SMTP id w125mr6727003wme.0.1479313469114; Wed, 16 Nov 2016 08:24:29 -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 e5sm10945074wma.12.2016.11.16.08.24.27 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Wed, 16 Nov 2016 08:24:28 -0800 (PST) From: Adrien Mazarguil To: dev@dpdk.org Cc: Thomas Monjalon , Pablo de Lara , Olivier Matz Date: Wed, 16 Nov 2016 17:23:36 +0100 Message-Id: X-Mailer: git-send-email 2.1.4 In-Reply-To: References: Subject: [dpdk-dev] [PATCH 10/22] app/testpmd: add flow validate/create commands X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: patches and discussions about DPDK List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 16 Nov 2016 16:24:30 -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 --- 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 20a64b6..851cc16 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 5a8980c..1874849 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, + 0, +}; + static const enum index next_destroy_attr[] = { DESTROY_RULE, END, @@ -192,9 +259,26 @@ static const enum index next_list_attr[] = { 0, }; +static const enum index next_item[] = { + ITEM_END, + ITEM_VOID, + ITEM_INVERT, + 0, +}; + +static const enum index next_action[] = { + ACTION_END, + ACTION_VOID, + ACTION_PASSTHRU, + 0, +}; + 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