From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 92DB4A0555 for ; Wed, 19 Feb 2020 03:23:54 +0100 (CET) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 48BE21C06D; Wed, 19 Feb 2020 03:23:54 +0100 (CET) Received: from mail-pl1-f193.google.com (mail-pl1-f193.google.com [209.85.214.193]) by dpdk.org (Postfix) with ESMTP id AC0741C06D for ; Wed, 19 Feb 2020 03:23:51 +0100 (CET) Received: by mail-pl1-f193.google.com with SMTP id t6so8889035plj.5 for ; Tue, 18 Feb 2020 18:23:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=subject:to:cc:references:from:message-id:date:user-agent :mime-version:in-reply-to:content-language:content-transfer-encoding; bh=qwdHa8m7MHmKKbxpY5QUlOcx93YqPMAO5BtoguEIjEY=; b=Knlr6knuqlVJtZoZjWJ6m146+eqGp/GwqorbWnqeZAH63k5+SxK49lPbPTZQjo3dXb M230gvJBcaXTJ4000ZLfvT1addw0IZP0lBKz6FxxsFt83NiPsSLIKWlzQErzBJn1X7nR tg54BPBVyKQkbLngDxWl0Mz+JU0r3jtAl80BFNPkptG+xPAY7O0d1ZXE5NJgPXG20FLS 7xnS2egHYcTBE8LFAOD27duZGRgNq7PaYEh6nnXqgtQz3jb/7BanpPNxinNqppdFVyLu of3OEEb4AMYAIVJoV8oI+2O/BuJfflbwS8nXGvocXOw8Us3GBOYRrKlHlmawPpRlvVUZ t3Tg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:cc:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=qwdHa8m7MHmKKbxpY5QUlOcx93YqPMAO5BtoguEIjEY=; b=lkeMiDWWxKcp82ajb04tGIUp63H6fkdhU2u12CaCxE8TVCpXa8L/PdNN69yk1jFoom +rSouFcR61dzUJY2huDLTvYQTMHeOO7t+HLnEZm2CnnsFF2LBf3VPJlr1wXnJJPvKZZ3 lTaru3GiTrCcjG0gRPujdFinA9ZJwFL+ni0CXotwAp4ZUSy5vjf5ugXELo82HrxT+Gtm Xa63qGgk9R7JDGfaiDixyJxDu4df2teIoQXwHlBz3uCZTJWjGTiw/SaXSt9ZwLH/ks+f BJvDdSjIgEvn7aOckIDYE8z5mmCVsgiJpJll6+AQU/ABHNHQb+yxQTAxPIuWxXCe5foE JoQg== X-Gm-Message-State: APjAAAU25NRzSZ2y9+SuQm2wYzUntZDKbNKVjs9vBfdjmrgu0Coj8EA/ b2Kpfgxyx/dFBW0s64E5ZZEan4UV X-Google-Smtp-Source: APXvYqxhWWZjQ7el4yJfhW7O2otv7imflyUyoQw8uIUoORmb0j3/ELwQ8/QEjLPRsGlUhSPoEYgWXw== X-Received: by 2002:a17:902:b612:: with SMTP id b18mr22437940pls.318.1582079029894; Tue, 18 Feb 2020 18:23:49 -0800 (PST) Received: from [172.30.202.27] ([192.47.164.146]) by smtp.gmail.com with ESMTPSA id gc1sm249469pjb.20.2020.02.18.18.23.47 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 18 Feb 2020 18:23:49 -0800 (PST) To: x-fn-spp-ml@ntt-tx.co.jp Cc: ferruh.yigit@intel.com, spp@dpdk.org References: <20200218063720.6597-1-x-fn-spp-ml@ntt-tx.co.jp> <20200218063720.6597-7-x-fn-spp-ml@ntt-tx.co.jp> From: Yasufumi Ogawa Message-ID: Date: Wed, 19 Feb 2020 11:24:35 +0900 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.4.1 MIME-Version: 1.0 In-Reply-To: <20200218063720.6597-7-x-fn-spp-ml@ntt-tx.co.jp> Content-Type: text/plain; charset=utf-8; format=flowed Content-Language: en-US Content-Transfer-Encoding: 7bit Subject: Re: [spp] [PATCH 06/17] spp_primary: add support of rte_flow X-BeenThere: spp@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Soft Patch Panel List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: spp-bounces@dpdk.org Sender: "spp" > From: Hideyuki Yamashita > > To support rte_flow in SPP, this patch provides main functions which > defines validate, create, delete and flush flow This patch has several issues from checkpatches.sh. Please fix bellow. WARNING:RETURN_VOID: void function return statements are not generally useful #604: FILE: src/primary/flow/flow.c:581: + return; +} ERROR:SWITCH_CASE_INDENT_LEVEL: switch and case should be at the same indent #700: FILE: src/primary/flow/flow.c:677: + switch(input->command) { + case VALIDATE: [...] + case CREATE: [...] + case DESTROY: [...] + case FLUSH: ERROR:SPACING: space required before the open parenthesis '(' #700: FILE: src/primary/flow/flow.c:677: + switch(input->command) { WARNING:RETURN_VOID: void function return statements are not generally useful #729: FILE: src/primary/flow/flow.c:706: + return; +} Regards, Yasufumi > > Signed-off-by: Hideyuki Yamashita > Signed-off-by: Yasufumi Ogawa > --- > src/primary/flow/flow.c | 1048 +++++++++++++++++++++++++++++++++++++++ > src/primary/flow/flow.h | 94 ++++ > 2 files changed, 1142 insertions(+) > create mode 100644 src/primary/flow/flow.c > create mode 100644 src/primary/flow/flow.h > > diff --git a/src/primary/flow/flow.c b/src/primary/flow/flow.c > new file mode 100644 > index 0000000..52a3e59 > --- /dev/null > +++ b/src/primary/flow/flow.c > @@ -0,0 +1,1048 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright(c) 2019 Nippon Telegraph and Telephone Corporation > + */ > + > +#include > +#include > +#include > +#include > + > +#include "shared/common.h" > +#include "shared/secondary/utils.h" > +#include "shared/secondary/spp_worker_th/data_types.h" > +#include "primary/primary.h" > +#include "flow.h" > +#include "attr.h" > +#include "common.h" > + > +#include "primary/flow/pattern/eth.h" > +#include "primary/flow/pattern/vlan.h" > + > +#include "primary/flow/action/jump.h" > +#include "primary/flow/action/queue.h" > +#include "primary/flow/action/of_push_vlan.h" > +#include "primary/flow/action/of_set_vlan_vid.h" > +#include "primary/flow/action/of_set_vlan_pcp.h" > + > + > +/* Flow list for each port */ > +static struct port_flow port_list[RTE_MAX_ETHPORTS] = { 0 }; > + > +/* Define item operations */ > +static struct flow_item_ops flow_item_ops_list[] = { > + { > + .str_type = "end", > + .type = RTE_FLOW_ITEM_TYPE_END, > + .parse = NULL, > + .detail_list = NULL > + }, > + { > + .str_type = "eth", > + .type = RTE_FLOW_ITEM_TYPE_ETH, > + .size = sizeof(struct rte_flow_item_eth), > + .parse = parse_item_common, > + .detail_list = eth_ops_list, > + .status = append_item_eth_json, > + }, > + { > + .str_type = "vlan", > + .type = RTE_FLOW_ITEM_TYPE_VLAN, > + .size = sizeof(struct rte_flow_item_vlan), > + .parse = parse_item_common, > + .detail_list = vlan_ops_list, > + .status = append_item_vlan_json, > + }, > +}; > + > +/* Define action operations */ > +static struct flow_action_ops flow_action_ops_list[] = { > + { > + .str_type = "end", > + .type = RTE_FLOW_ACTION_TYPE_END, > + .size = 0, > + .parse = NULL, > + .detail_list = NULL, > + .status = NULL, > + }, > + { > + .str_type = "jump", > + .type = RTE_FLOW_ACTION_TYPE_JUMP, > + .size = sizeof(struct rte_flow_action_jump), > + .parse = parse_action_common, > + .detail_list = jump_ops_list, > + .status = append_action_jump_json, > + }, > + { > + .str_type = "queue", > + .type = RTE_FLOW_ACTION_TYPE_QUEUE, > + .size = sizeof(struct rte_flow_action_queue), > + .parse = parse_action_common, > + .detail_list = queue_ops_list, > + .status = append_action_queue_json, > + }, > + { > + .str_type = "of_pop_vlan", > + .type = RTE_FLOW_ACTION_TYPE_OF_POP_VLAN, > + .size = 0, > + .parse = NULL, > + .detail_list = NULL, > + .status = append_action_null_json, > + }, > + { > + .str_type = "of_push_vlan", > + .type = RTE_FLOW_ACTION_TYPE_OF_PUSH_VLAN, > + .size = sizeof(struct rte_flow_action_of_push_vlan), > + .parse = parse_action_common, > + .detail_list = of_push_vlan_ops_list, > + .status = append_action_of_push_vlan_json, > + }, > + { > + .str_type = "of_set_vlan_vid", > + .type = RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_VID, > + .size = sizeof(struct rte_flow_action_of_set_vlan_vid), > + .parse = parse_action_common, > + .detail_list = of_set_vlan_vid_ops_list, > + .status = append_action_of_set_vlan_vid_json, > + }, > + { > + .str_type = "of_set_vlan_pcp", > + .type = RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_PCP, > + .size = sizeof(struct rte_flow_action_of_set_vlan_pcp), > + .parse = parse_action_common, > + .detail_list = of_set_vlan_pcp_ops_list, > + .status = append_action_of_set_vlan_pcp_json, > + }, > +}; > + > +/* Free memory of "flow_args". */ > +static void > +free_flow_args(struct flow_args *input) > +{ > + int i; > + struct rte_flow_item *pattern; > + struct rte_flow_action *actions; > + char **target; > + > + if ((input->command != VALIDATE) && > + (input->command != CREATE)) > + return; > + > + pattern = input->args.rule.pattern; > + if (pattern != NULL) { > + for (i = 0; pattern[i].type != RTE_FLOW_ITEM_TYPE_END; i++) { > + target = (char **)((char *)(&pattern[i]) + > + offsetof(struct rte_flow_item, spec)); > + if (*target != NULL) > + free(*target); > + > + target = (char **)((char *)(&pattern[i]) + > + offsetof(struct rte_flow_item, last)); > + if (*target != NULL) > + free(*target); > + > + target = (char **)((char *)(&pattern[i]) + > + offsetof(struct rte_flow_item, mask)); > + if (*target != NULL) > + free(*target); > + } > + > + free(pattern); > + } > + > + actions = input->args.rule.actions; > + if (actions != NULL) { > + for (i = 0; actions[i].type != RTE_FLOW_ACTION_TYPE_END; i++) { > + target = (char **)((char *)(&actions[i]) + > + offsetof(struct rte_flow_action, conf)); > + if (*target != NULL) > + free(*target); > + } > + > + free(actions); > + } > +} > + > +/* > + * Create response in JSON format. > + * `rule_id` must be empty if flow create is failed. > + */ > +static void > +make_response(char *response, const char *result, const char *message, > + char *rule_id) > +{ > + if (rule_id == NULL) > + snprintf(response, MSG_SIZE, > + "{\"result\": \"%s\", \"message\": \"%s\"}", > + result, message); > + else > + snprintf(response, MSG_SIZE, > + "{\"result\": \"%s\", \"message\": \"%s\", " > + "\"rule_id\": \"%s\"}", > + result, message, rule_id); > +} > + > +/* Create error response from rte_flow_error */ > +static void > +make_error_response(char *response, const char *message, > + struct rte_flow_error error, char *rule_id) > +{ > + /* Define description for each error type */ > + static const char *const errstr_list[] = { > + [RTE_FLOW_ERROR_TYPE_NONE] = "No error", > + [RTE_FLOW_ERROR_TYPE_UNSPECIFIED] = "Cause unspecified", > + [RTE_FLOW_ERROR_TYPE_HANDLE] = "Flow rule (handle)", > + [RTE_FLOW_ERROR_TYPE_ATTR_GROUP] = "Group field", > + [RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY] = "Priority field", > + [RTE_FLOW_ERROR_TYPE_ATTR_INGRESS] = "Ingress field", > + [RTE_FLOW_ERROR_TYPE_ATTR_EGRESS] = "Egress field", > + [RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER] = "Transfer field", > + [RTE_FLOW_ERROR_TYPE_ATTR] = "Attributes structure", > + [RTE_FLOW_ERROR_TYPE_ITEM_NUM] = "Pattern length", > + [RTE_FLOW_ERROR_TYPE_ITEM_SPEC] = "Item specification", > + [RTE_FLOW_ERROR_TYPE_ITEM_LAST] = "Item specification range", > + [RTE_FLOW_ERROR_TYPE_ITEM_MASK] = "Item specification mask", > + [RTE_FLOW_ERROR_TYPE_ITEM] = "Specific pattern item", > + [RTE_FLOW_ERROR_TYPE_ACTION_NUM] = "Number of actions", > + [RTE_FLOW_ERROR_TYPE_ACTION_CONF] = "Action configuration", > + [RTE_FLOW_ERROR_TYPE_ACTION] = "Specific action", > + }; > + int err = rte_errno; > + char msg[512] = ""; > + char cause[32] = ""; > + const char *errstr; > + > + if ((unsigned int)error.type >= RTE_DIM(errstr_list) || > + !errstr_list[error.type]) > + errstr = "Unknown type"; > + else > + errstr = errstr_list[error.type]; > + > + > + if (error.cause != NULL) > + snprintf(cause, sizeof(cause), "cause: %p\\n", error.cause); > + > + snprintf(msg, sizeof(msg), > + "%s\\nerror type: %d (%s)\\n" > + "%serror message: %s\\nrte_errno: %s", > + message, error.type, errstr, cause, > + error.message ? error.message : "(no stated reason)", > + rte_strerror(err)); > + make_response(response, "error", msg, rule_id); > +} > + > +/* Add to array, redeclare memory. */ > +static int > +append_object_list(void **list, void *add, size_t obj_size, int num) > +{ > + char *new_list; > + > + new_list = malloc(obj_size * num); > + if (new_list == NULL) > + return -1; > + > + /* Copy original list*/ > + if (*list != NULL) { > + memcpy(new_list, *list, obj_size * (num - 1)); > + free(*list); > + } > + > + /* Add to list */ > + memcpy(new_list + (obj_size * (num - 1)), add, obj_size); > + > + *list = (void *)new_list; > + return 0; > +} > + > +static int > +parse_flow_actions(char *token_list[], int *index, > + struct rte_flow_action **actions) > +{ > + int ret; > + int action_count = 0; > + uint16_t i; > + char *token; > + struct flow_action_ops *ops; > + struct rte_flow_action action; > + > + if (strcmp(token_list[*index], "actions")) { > + RTE_LOG(ERR, SPP_FLOW, > + "Invalid parameter is %s(%s:%d)\n", > + token_list[*index], __func__, __LINE__); > + return -1; > + } > + > + /* Next to word */ > + (*index)++; > + > + while (token_list[*index] != NULL) { > + token = token_list[*index]; > + > + for (i = 0; i < RTE_DIM(flow_action_ops_list); i++) { > + ops = &flow_action_ops_list[i]; > + if (strcmp(token, ops->str_type)) > + continue; > + > + memset(&action, 0, sizeof(struct rte_flow_action)); > + action.type = ops->type; > + if (ops->parse != NULL) { > + ret = ops->parse(token_list, index, &action, > + ops); > + if (ret < 0) > + return -1; > + } else { > + (*index)++; > + } > + break; > + } > + > + /* > + * Error occurs if a action string that is not defined in > + * str_type of flow_action_ops_list is specified > + */ > + if (i == RTE_DIM(flow_action_ops_list)) { > + RTE_LOG(ERR, SPP_FLOW, > + "Invalid parameter " > + "is %s action(%s:%d)\n", > + token, __func__, __LINE__); > + return -1; > + } > + > + /* Add to "actions" list */ > + action_count++; > + ret = append_object_list((void **)actions, &action, > + sizeof(struct rte_flow_action), action_count); > + > + if (!strcmp(token, "end")) > + break; > + > + (*index)++; > + } > + > + return 0; > +} > + > +static int > +parse_flow_pattern(char *token_list[], int *index, > + struct rte_flow_item **pattern) > +{ > + int ret; > + int item_count = 0; > + uint32_t i; > + char *token; > + struct flow_item_ops *ops; > + struct rte_flow_item item; > + > + while (token_list[*index] != NULL) { > + token = token_list[*index]; > + > + for (i = 0; i < RTE_DIM(flow_item_ops_list); i++) { > + ops = &flow_item_ops_list[i]; > + if (strcmp(token, ops->str_type)) > + continue; > + > + memset(&item, 0, sizeof(struct rte_flow_item)); > + item.type = ops->type; > + if (ops->parse != NULL) { > + ret = ops->parse(token_list, index, &item, > + ops); > + if (ret < 0) > + return -1; > + } > + break; > + } > + > + /* > + * Error occurs if a pattern string that is not defined in > + * str_type of flow_item_ops_list is specified > + */ > + if (i == RTE_DIM(flow_item_ops_list)) { > + RTE_LOG(ERR, SPP_FLOW, > + "Invalid parameter " > + "is %s pattern(%s:%d)\n", > + token, __func__, __LINE__); > + return -1; > + } > + > + /* Add to "pattern" list */ > + item_count++; > + ret = append_object_list((void **)pattern, &item, > + sizeof(struct rte_flow_item), item_count); > + > + if (!strcmp(token, "end")) > + break; > + > + (*index)++; > + } > + > + return 0; > +} > + > +static int > +parse_flow_rule(char *token_list[], struct flow_args *input) > +{ > + int ret = 0; > + int index; > + > + ret = parse_phy_port_id(token_list[2], &input->port_id); > + if (ret < 0) > + return -1; > + > + /* The next index of the port */ > + index = 3; > + > + /* Attribute parse */ > + ret = parse_flow_attr(token_list, &index, &input->args.rule.attr); > + if (ret < 0) { > + RTE_LOG(ERR, SPP_FLOW, > + "Failed to parse Attribute(%s:%d)\n", > + __func__, __LINE__); > + return -1; > + } > + > + /* The next index of the pattern */ > + index++; > + > + /* Pattern parse */ > + ret = parse_flow_pattern(token_list, &index, > + &input->args.rule.pattern); > + if (ret < 0) { > + RTE_LOG(ERR, SPP_FLOW, > + "Failed to parse Pattern(%s:%d)\n", > + __func__, __LINE__); > + return -1; > + } > + > + /* The next index of the actions */ > + index++; > + > + /* Actions parse */ > + ret = parse_flow_actions(token_list, &index, > + &input->args.rule.actions); > + if (ret < 0) { > + RTE_LOG(ERR, SPP_FLOW, > + "Failed to parse Actions(%s:%d)\n", > + __func__, __LINE__); > + return -1; > + } > + > + return 0; > +} > + > +static int > +parse_flow_destroy(char *token_list[], struct flow_args *input) > +{ > + int ret; > + char *end; > + > + ret = parse_phy_port_id(token_list[2], &input->port_id); > + if (ret < 0) > + return -1; > + > + if (token_list[3] == NULL) { > + RTE_LOG(ERR, SPP_FLOW, > + "rule_id is not specified(%s:%d)\n", > + __func__, __LINE__); > + return -1; > + } > + > + if (!strcmp(token_list[3], "ALL")) { > + input->command = FLUSH; > + > + } else { > + input->command = DESTROY; > + input->args.destroy.rule_id = strtoul(token_list[3], > + &end, 10); > + } > + > + return 0; > +} > + > +/** Generate a flow_rule entry from attributes/pattern/actions. */ > +static struct flow_rule * > +create_flow_rule(struct rte_flow_attr *attr, > + struct rte_flow_item *pattern, > + struct rte_flow_action *actions, > + struct rte_flow_error *error) > +{ > + const struct rte_flow_conv_rule conv_rule = { > + .attr_ro = attr, > + .pattern_ro = pattern, > + .actions_ro = actions, > + }; > + struct flow_rule *rule; > + int ret; > + > + ret = rte_flow_conv(RTE_FLOW_CONV_OP_RULE, NULL, 0, &conv_rule, > + error); > + if (ret < 0) > + return NULL; > + > + rule = calloc(1, offsetof(struct flow_rule, rule) + ret); > + if (!rule) { > + rte_flow_error_set > + (error, errno, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, > + "calloc() failed"); > + return NULL; > + } > + > + ret = rte_flow_conv(RTE_FLOW_CONV_OP_RULE, &rule->rule, ret, &conv_rule, > + error); > + if (ret >= 0) > + return rule; > + > + free(rule); > + return NULL; > +} > + > +/* Execute rte_flow_validate().*/ > +static void > +exec_flow_validate(int port_id, > + struct rte_flow_attr *attr, > + struct rte_flow_item *pattern, > + struct rte_flow_action *actions, > + char *response) > +{ > + int ret; > + struct rte_flow_error error; > + > + memset(&error, 0, sizeof(error)); > + > + ret = rte_flow_validate(port_id, attr, pattern, actions, &error); > + if (ret != 0) > + make_error_response(response, "Flow validate error", error, > + NULL); > + else > + make_response(response, "success", "Flow rule validated", > + NULL); > +} > + > +/* Execute rte_flow_create(). Save flow rules globally */ > +static void > +exec_flow_create(int port_id, > + struct rte_flow_attr *attr, > + struct rte_flow_item *pattern, > + struct rte_flow_action *actions, > + char *response) > +{ > + uint32_t rule_id; > + char mes[32]; > + char rule_id_str[11] = {0}; > + struct rte_flow_error error; > + struct rte_flow *flow; > + struct flow_rule *rule; > + struct port_flow *port; > + > + memset(&error, 0, sizeof(error)); > + > + flow = rte_flow_create(port_id, attr, pattern, actions, &error); > + if (flow == NULL) { > + make_error_response(response, "Flow create error", error, > + rule_id_str); > + return; > + } > + > + port = &port_list[port_id]; > + if (port->flow_list != NULL) { > + if (port->flow_list->rule_id >= UINT32_MAX) { > + make_response(response, "error", > + "Rule ID must be less than %"PRIu32, > + rule_id_str); > + rte_flow_destroy(port_id, flow, NULL); > + return; > + } > + rule_id = port->flow_list->rule_id + 1; > + } else { > + rule_id = 0; > + } > + > + rule = create_flow_rule(attr, pattern, actions, &error); > + if (rule == NULL) { > + rte_flow_destroy(port_id, flow, NULL); > + make_error_response(response, "Flow create error", error, > + rule_id_str); > + return; > + } > + > + /* Keep it globally as a list */ > + rule->rule_id = rule_id; > + rule->flow_handle = flow; > + > + if (port->flow_list == NULL) > + rule->prev = NULL; > + else > + rule->prev = port->flow_list; > + > + port->flow_list = rule; > + > + sprintf(mes, "Flow rule #%d created", rule_id); > + sprintf(rule_id_str, "%d", rule_id); > + make_response(response, "success", mes, rule_id_str); > + return; > +} > + > +/* Execute rte_flow_destroy(). Destroying a globally saved flow rule */ > +static void > +exec_flow_destroy(int port_id, uint32_t rule_id, char *response) > +{ > + int ret; > + int found_flg = 0; > + char mes[64]; > + struct flow_rule *rule, **next_ptr; > + struct rte_flow_error error; > + > + memset(&error, 0, sizeof(error)); > + > + ret = is_portid_used(port_id); > + if (ret != 0) { > + sprintf(mes, "Invalid port %d", port_id); > + make_response(response, "error", mes, NULL); > + return; > + } > + > + next_ptr = &(port_list[port_id].flow_list); > + rule = port_list[port_id].flow_list; > + > + while (rule != NULL) { > + if (rule->rule_id != rule_id) { > + next_ptr = &(rule->prev); > + rule = rule->prev; > + continue; > + } > + > + ret = rte_flow_destroy(port_id, rule->flow_handle, &error); > + if (ret != 0) { > + make_error_response(response, "Flow destroy error", > + error, NULL); > + return; > + } > + > + /* Remove flow from global list */ > + *next_ptr = rule->prev; > + free(rule); > + found_flg = 1; > + > + sprintf(mes, "Flow rule #%d destroyed", rule_id); > + make_response(response, "success", mes, NULL); > + break; > + } > + > + /* Rule_id not found */ > + if (found_flg == 0) { > + sprintf(mes, "Flow rule #%d not found", rule_id); > + make_response(response, "error", mes, NULL); > + } > +} > + > +/* Delete all globally saved flow rules */ > +static void > +exec_flow_flush(int port_id, char *response) > +{ > + int ret; > + char mes[64]; > + struct flow_rule *rule; > + struct rte_flow_error error; > + > + memset(&error, 0, sizeof(error)); > + > + ret = is_portid_used(port_id); > + if (ret != 0) { > + sprintf(mes, "Invalid port %d", port_id); > + make_response(response, "error", mes, NULL); > + return; > + } > + > + ret = rte_flow_flush(port_id, &error); > + if (ret != 0) > + make_error_response(response, "Flow destroy error", > + error, NULL); > + else > + make_response(response, "success", "Flow rule all destroyed", > + NULL); > + > + /* > + * Even if a failure occurs, flow handle is invalidated, > + * so delete flow_list. > + */ > + > + while (port_list[port_id].flow_list != NULL) { > + rule = port_list[port_id].flow_list->prev; > + free(port_list[port_id].flow_list); > + port_list[port_id].flow_list = rule; > + } > +} > + > +static void > +exec_flow(struct flow_args *input, char *response) > +{ > + switch(input->command) { > + case VALIDATE: > + exec_flow_validate(input->port_id, > + &input->args.rule.attr, > + input->args.rule.pattern, > + input->args.rule.actions, > + response); > + break; > + case CREATE: > + exec_flow_create(input->port_id, > + &input->args.rule.attr, > + input->args.rule.pattern, > + input->args.rule.actions, > + response); > + break; > + case DESTROY: > + exec_flow_destroy(input->port_id, > + input->args.destroy.rule_id, > + response); > + break; > + case FLUSH: > + exec_flow_flush(input->port_id, response); > + break; > + } > + > + /* Argument data is no longer needed and freed */ > + free_flow_args(input); > + > + return; > +} > + > +int > +parse_flow(char *token_list[], char *response) > +{ > + int ret = 0; > + struct flow_args input = { 0 }; > + > + if (token_list[1] == NULL) { > + ret = -1; > + } else if (!strcmp(token_list[1], "validate")) { > + input.command = VALIDATE; > + ret = parse_flow_rule(token_list, &input); > + > + } else if (!strcmp(token_list[1], "create")) { > + input.command = CREATE; > + ret = parse_flow_rule(token_list, &input); > + > + } else if (!strcmp(token_list[1], "destroy")) { > + ret = parse_flow_destroy(token_list, &input); > + > + } else { > + ret = -1; > + } > + > + if (ret != 0) { > + free_flow_args(&input); > + make_response(response, "error", > + "Flow command invalid argument", NULL); > + return 0; > + } > + > + exec_flow(&input, response); > + > + return 0; > +} > + > +static int > +append_flow_pattern_json(const struct rte_flow_item *pattern, int buf_size, > + char *pattern_str) > +{ > + uint32_t i, j; > + uint32_t nof_elems = 3; > + int ret = 0; > + char *tmp_str; > + const char element_str[][5] = { "spec", "last", "mask" }; > + const struct rte_flow_item *ptn = pattern; > + struct flow_item_ops *ops; > + const void *tmp_ptr[nof_elems]; > + > + tmp_str = malloc(buf_size); > + if (tmp_str == NULL) { > + RTE_LOG(ERR, SPP_FLOW, > + "Memory allocation failure(%s:%d)\n", > + __func__, __LINE__); > + return -1; > + } > + > + while (ptn->type != RTE_FLOW_ITEM_TYPE_END) { > + memset(tmp_str, 0, buf_size); > + > + tmp_ptr[0] = ptn->spec; > + tmp_ptr[1] = ptn->last; > + tmp_ptr[2] = ptn->mask; > + > + for (i = 0; i < RTE_DIM(flow_item_ops_list); i++) { > + ops = &flow_item_ops_list[i]; > + if (ptn->type != ops->type) > + continue; > + > + snprintf(tmp_str, buf_size, > + "{\"type\":\"%s\",", > + ops->str_type); > + > + for (j = 0; j < nof_elems; j++) { > + snprintf(tmp_str + strlen(tmp_str), buf_size, > + "\"%s\":", > + element_str[j]); > + > + if (tmp_ptr[j] != NULL) > + ret = ops->status(tmp_ptr[j], > + buf_size - (int)strlen(tmp_str), > + tmp_str + strlen(tmp_str)); > + else > + snprintf(tmp_str + strlen(tmp_str), > + buf_size, > + "null"); > + > + if (ret != 0) > + break; > + > + if (j < nof_elems - 1) > + tmp_str[strlen(tmp_str)] = ','; > + } > + > + tmp_str[strlen(tmp_str)] = '}'; > + > + break; > + } > + > + if (ret != 0) > + break; > + > + if ((int)strlen(pattern_str) + (int)strlen(tmp_str) > + > buf_size - 1) { > + ret = -1; > + break; > + } > + strncat(pattern_str, tmp_str, strlen(tmp_str)); > + > + /* > + * If there is the following pattern, add ',' to > + * pattern_str > + */ > + ptn++; > + if (ptn->type != RTE_FLOW_ITEM_TYPE_END) { > + if ((int)strlen(pattern_str) + 1 > buf_size - 1) { > + ret = -1; > + break; > + } > + pattern_str[strlen(pattern_str)] = ','; > + } > + } > + > + if (tmp_str != NULL) > + free(tmp_str); > + > + return ret; > +} > + > +static int > +append_flow_action_json(const struct rte_flow_action *actions, int buf_size, > + char *actions_str) > +{ > + uint32_t i; > + int ret = 0; > + char *tmp_str; > + const struct rte_flow_action *act = actions; > + struct flow_action_ops *ops; > + > + tmp_str = malloc(buf_size); > + if (tmp_str == NULL) { > + RTE_LOG(ERR, SPP_FLOW, > + "Memory allocation failure(%s:%d)\n", > + __func__, __LINE__); > + return -1; > + } > + > + while (act->type != RTE_FLOW_ACTION_TYPE_END) { > + memset(tmp_str, 0, buf_size); > + > + for (i = 0; i < RTE_DIM(flow_action_ops_list); i++) { > + ops = &flow_action_ops_list[i]; > + if (act->type != ops->type) > + continue; > + > + snprintf(tmp_str, buf_size, > + "{\"type\":\"%s\",\"conf\":", > + ops->str_type); > + > + ret = ops->status(act->conf, > + buf_size - (int)strlen(tmp_str), > + tmp_str + strlen(tmp_str)); > + tmp_str[strlen(tmp_str)] = '}'; > + break; > + } > + > + if (ret != 0) > + break; > + > + if ((int)strlen(actions_str) + (int)strlen(tmp_str) > + > buf_size - 1) { > + ret = -1; > + break; > + } > + strncat(actions_str, tmp_str, strlen(tmp_str)); > + > + /* > + * If there is the following pattern, add ',' to > + * actions_str > + */ > + act++; > + if (act->type != RTE_FLOW_ACTION_TYPE_END) { > + if ((int)strlen(actions_str) + 1 > buf_size - 1) { > + ret = -1; > + break; > + } > + actions_str[strlen(actions_str)] = ','; > + } > + } > + > + if (tmp_str != NULL) > + free(tmp_str); > + > + return ret; > +} > + > +static int > +append_flow_rule_json(struct flow_rule *flow, int buf_size, char *flow_str) > +{ > + int ret = 0; > + struct rte_flow_conv_rule rule; > + char *tmp_str, *attr_str, *pattern_str, *actions_str; > + > + while (1) { > + tmp_str = malloc(buf_size); > + attr_str = malloc(buf_size); > + pattern_str = malloc(buf_size); > + actions_str = malloc(buf_size); > + if (tmp_str == NULL || attr_str == NULL > + || pattern_str == NULL || actions_str == NULL) { > + RTE_LOG(ERR, SPP_FLOW, > + "Memory allocation failure(%s:%d)\n", > + __func__, __LINE__); > + ret = -1; > + break; > + } > + memset(tmp_str, 0, buf_size); > + memset(attr_str, 0, buf_size); > + memset(pattern_str, 0, buf_size); > + memset(actions_str, 0, buf_size); > + > + rule = flow->rule; > + > + ret = append_flow_attr_json(rule.attr_ro, buf_size, attr_str); > + if (ret != 0) > + break; > + > + ret = append_flow_pattern_json(rule.pattern_ro, buf_size, > + pattern_str); > + if (ret != 0) > + break; > + > + ret = append_flow_action_json(rule.actions_ro, buf_size, > + actions_str); > + if (ret != 0) > + break; > + > + snprintf(tmp_str, buf_size, > + "{\"rule_id\":%d," > + "\"attr\":%s," > + "\"patterns\":[%s]," > + "\"actions\":[%s]}", > + flow->rule_id, attr_str, pattern_str, actions_str); > + > + if ((int)strlen(tmp_str) > buf_size - 1) { > + ret = -1; > + break; > + } > + > + snprintf(flow_str, buf_size, "%s", tmp_str); > + break; > + } > + > + if (tmp_str != NULL) > + free(tmp_str); > + if (attr_str != NULL) > + free(attr_str); > + if (pattern_str != NULL) > + free(pattern_str); > + if (actions_str != NULL) > + free(actions_str); > + > + return ret; > +} > + > +int > +append_flow_json(int port_id, int buf_size, char *output) > +{ > + int ret = 0; > + int str_size = 0; > + char *flow_str, *tmp_str; > + struct flow_rule *flow; > + > + flow_str = malloc(buf_size); > + tmp_str = malloc(buf_size); > + if (flow_str == NULL || tmp_str == NULL) { > + RTE_LOG(ERR, SPP_FLOW, > + "Memory allocation failure(%s:%d)\n", > + __func__, __LINE__); > + return -1; > + } > + > + flow = port_list[port_id].flow_list; > + > + while (flow != NULL) { > + memset(flow_str, 0, buf_size); > + > + ret = append_flow_rule_json(flow, buf_size, flow_str); > + if (ret != 0) > + break; > + > + if (str_size == 0) { > + snprintf(output, buf_size, "%s", flow_str); > + str_size += (int)strlen(flow_str); > + > + } else { > + str_size += ((int)strlen(flow_str) + 1); > + if (str_size > buf_size - 1) { > + ret = -1; > + break; > + } > + > + /* > + * Since flow_list is in descending order, > + * concatenate the strings in front. > + */ > + memset(tmp_str, 0, buf_size); > + strncpy(tmp_str, output, buf_size); > + memset(output, 0, buf_size); > + > + snprintf(output, buf_size, "%s,%s", > + flow_str, tmp_str); > + } > + > + flow = flow->prev; > + } > + > + if (ret == 0) { > + if ((int)strlen("[]") + (int)strlen(flow_str) > + > buf_size - 1) > + ret = -1; > + else { > + memset(tmp_str, 0, buf_size); > + strncpy(tmp_str, output, buf_size); > + memset(output, 0, buf_size); > + > + snprintf(output, buf_size, "[%s]", tmp_str); > + } > + } > + > + if (ret != 0) > + RTE_LOG(ERR, SPP_FLOW, > + "Cannot send all of flow stats(%s:%d)\n", > + __func__, __LINE__); > + > + if (flow_str != NULL) > + free(flow_str); > + if (tmp_str != NULL) > + free(tmp_str); > + > + return ret; > +} > diff --git a/src/primary/flow/flow.h b/src/primary/flow/flow.h > new file mode 100644 > index 0000000..ecd4eb3 > --- /dev/null > +++ b/src/primary/flow/flow.h > @@ -0,0 +1,94 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright(c) 2019 Nippon Telegraph and Telephone Corporation > + */ > + > +#ifndef _PRIMARY_FLOW_H_ > +#define _PRIMARY_FLOW_H_ > + > +#include > + > +#define RTE_LOGTYPE_SPP_FLOW RTE_LOGTYPE_USER1 > + > +enum flow_command { > + VALIDATE = 0, > + CREATE, > + DESTROY, > + FLUSH > +}; > + > +/* Parser result of flow command arguments */ > +struct flow_args { > + enum flow_command command; > + int port_id; > + union { > + struct { > + struct rte_flow_attr attr; > + struct rte_flow_item *pattern; > + struct rte_flow_action *actions; > + } rule; /* validate or create arguments. */ > + struct { > + uint32_t rule_id; > + } destroy; /* destroy arguments. */ > + } args; > +}; > + > +/* Descriptor for a single flow. */ > +struct flow_rule { > + /* Flow rule ID */ > + uint32_t rule_id; > + > + /* Previous flow in list. */ > + struct flow_rule *prev; > + > + /* Opaque flow object returned by PMD. */ > + struct rte_flow *flow_handle; > + > + /* Saved flow rule description. */ > + struct rte_flow_conv_rule rule; > +}; > + > +/* Flow rule list of the port */ > +struct port_flow { > + /* Associated flows */ > + struct flow_rule *flow_list; > +}; > + > +/* Detail parse operation for a specific item or action */ > +struct flow_detail_ops { > + const char *token; > + const size_t offset; > + const size_t size; > + int flg_value; > + int (*parse_detail)(char *str, void *output); > +}; > + > +/* Operation for each item type */ > +struct flow_item_ops { > + const char *str_type; > + enum rte_flow_item_type type; > + size_t size; > + int (*parse)(char *token_list[], int *index, > + struct rte_flow_item *pattern, > + struct flow_item_ops *ops); > + struct flow_detail_ops *detail_list; > + int (*status)(const void *element, > + int buf_size, char *pattern_str); > +}; > + > +/* Operation for each action type */ > +struct flow_action_ops { > + const char *str_type; > + enum rte_flow_action_type type; > + size_t size; > + int (*parse)(char *token_list[], int *index, > + struct rte_flow_action *action, > + struct flow_action_ops *ops); > + struct flow_detail_ops *detail_list; > + int (*status)(const void *conf, > + int buf_size, char *action_str); > +}; > + > +int parse_flow(char *token_list[], char *response); > +int append_flow_json(int port_id, int buf_size, char *output); > + > +#endif >