[spp] [PATCH v2 06/17] spp_primary: add support of rte_flow
x-fn-spp-ml at ntt-tx.co.jp
x-fn-spp-ml at ntt-tx.co.jp
Wed Feb 19 12:49:36 CET 2020
From: Hideyuki Yamashita <yamashita.hideyuki at ntt-tx.co.jp>
To support rte_flow in SPP, this patch provides main functions which
defines validate, create, delete and flush flow
Signed-off-by: Hideyuki Yamashita <yamashita.hideyuki at ntt-tx.co.jp>
Signed-off-by: Yasufumi Ogawa <yasufum.o at gmail.com>
---
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 <rte_flow.h>
+#include <rte_common.h>
+#include <rte_ether.h>
+#include <rte_byteorder.h>
+
+#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 <rte_log.h>
+
+#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
More information about the spp
mailing list