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 68F30A0555 for ; Wed, 19 Feb 2020 12:49:58 +0100 (CET) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 6005D1BF9C; Wed, 19 Feb 2020 12:49:58 +0100 (CET) Received: from dish-sg.nttdocomo.co.jp (dish-sg.nttdocomo.co.jp [202.19.227.74]) by dpdk.org (Postfix) with ESMTP id 1D1811BF8E for ; Wed, 19 Feb 2020 12:49:56 +0100 (CET) X-dD-Source: Outbound Received: from zssg-mailmd105.ddreams.local (zssg-mailmd900.ddreams.local [10.160.172.63]) by zssg-mailou103.ddreams.local (Postfix) with ESMTP id 5A4BA1200DE; Wed, 19 Feb 2020 20:49:55 +0900 (JST) Received: from zssg-mailmf101.ddreams.local (zssg-mailmf900.ddreams.local [10.160.172.84]) by zssg-mailmd105.ddreams.local (dDREAMS) with ESMTP id <0Q5Y01DUI677TH50@dDREAMS>; Wed, 19 Feb 2020 20:49:55 +0900 (JST) Received: from zssg-mailmf101.ddreams.local (unknown [127.0.0.1]) by zssg-mailmf101.ddreams.local (Postfix) with ESMTP id 286087E6036; Wed, 19 Feb 2020 20:49:55 +0900 (JST) Received: from zssg-mailmf101.ddreams.local (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 272238E6054; Wed, 19 Feb 2020 20:49:55 +0900 (JST) Received: from localhost (unknown [127.0.0.1]) by IMSVA (Postfix) with SMTP id 1C6FB8E6055; Wed, 19 Feb 2020 20:49:55 +0900 (JST) X-IMSS-HAND-OFF-DIRECTIVE: localhost:10026 Received: from zssg-mailmf101.ddreams.local (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id CAD4C8E605C; Wed, 19 Feb 2020 20:49:54 +0900 (JST) Received: from davinci.ntt-tx.co.jp (unknown [10.160.183.139]) by zssg-mailmf101.ddreams.local (Postfix) with ESMTP; Wed, 19 Feb 2020 20:49:54 +0900 (JST) From: x-fn-spp-ml@ntt-tx.co.jp To: spp@dpdk.org, ferruh.yigit@intel.com, yasufum.o@gmail.com Date: Wed, 19 Feb 2020 20:49:36 +0900 Message-id: <20200219114947.14134-7-x-fn-spp-ml@ntt-tx.co.jp> X-Mailer: git-send-email 2.18.0 In-reply-to: <20200218063720.6597-1-x-fn-spp-ml@ntt-tx.co.jp> References: <20200218063720.6597-1-x-fn-spp-ml@ntt-tx.co.jp> X-TM-AS-GCONF: 00 Subject: [spp] [PATCH v2 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 Signed-off-by: Hideyuki Yamashita Signed-off-by: Yasufumi Ogawa --- src/primary/flow/flow.c | 1045 +++++++++++++++++++++++++++++++++++++++ src/primary/flow/flow.h | 94 ++++ 2 files changed, 1139 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..ba28012 --- /dev/null +++ b/src/primary/flow/flow.c @@ -0,0 +1,1045 @@ +/* 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); +} + +/* 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); +} + +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 -- 2.17.1