+ pft = port_flow_locate_tunnel_id(port, tunnel_ops->id);
+ if (!pft) {
+ printf("failed to locate port flow tunnel #%u\n",
+ tunnel_ops->id);
+ return NULL;
+ }
+ if (tunnel_ops->actions) {
+ uint32_t num_actions;
+ const struct rte_flow_action *aptr;
+
+ ret = rte_flow_tunnel_decap_set(port_id, &pft->tunnel,
+ &pft->pmd_actions,
+ &pft->num_pmd_actions,
+ &error);
+ if (ret) {
+ port_flow_complain(&error);
+ return NULL;
+ }
+ for (aptr = actions, num_actions = 1;
+ aptr->type != RTE_FLOW_ACTION_TYPE_END;
+ aptr++, num_actions++);
+ pft->actions = malloc(
+ (num_actions + pft->num_pmd_actions) *
+ sizeof(actions[0]));
+ if (!pft->actions) {
+ rte_flow_tunnel_action_decap_release(
+ port_id, pft->actions,
+ pft->num_pmd_actions, &error);
+ return NULL;
+ }
+ rte_memcpy(pft->actions, pft->pmd_actions,
+ pft->num_pmd_actions * sizeof(actions[0]));
+ rte_memcpy(pft->actions + pft->num_pmd_actions, actions,
+ num_actions * sizeof(actions[0]));
+ }
+ if (tunnel_ops->items) {
+ uint32_t num_items;
+ const struct rte_flow_item *iptr;
+
+ ret = rte_flow_tunnel_match(port_id, &pft->tunnel,
+ &pft->pmd_items,
+ &pft->num_pmd_items,
+ &error);
+ if (ret) {
+ port_flow_complain(&error);
+ return NULL;
+ }
+ for (iptr = pattern, num_items = 1;
+ iptr->type != RTE_FLOW_ITEM_TYPE_END;
+ iptr++, num_items++);
+ pft->items = malloc((num_items + pft->num_pmd_items) *
+ sizeof(pattern[0]));
+ if (!pft->items) {
+ rte_flow_tunnel_item_release(
+ port_id, pft->pmd_items,
+ pft->num_pmd_items, &error);
+ return NULL;
+ }
+ rte_memcpy(pft->items, pft->pmd_items,
+ pft->num_pmd_items * sizeof(pattern[0]));
+ rte_memcpy(pft->items + pft->num_pmd_items, pattern,
+ num_items * sizeof(pattern[0]));
+ }
+
+ return pft;
+}
+
+static void
+port_flow_tunnel_offload_cmd_release(portid_t port_id,
+ const struct tunnel_ops *tunnel_ops,
+ struct port_flow_tunnel *pft)
+{
+ struct rte_flow_error error;
+
+ if (tunnel_ops->actions) {
+ free(pft->actions);
+ rte_flow_tunnel_action_decap_release(
+ port_id, pft->pmd_actions,
+ pft->num_pmd_actions, &error);
+ pft->actions = NULL;
+ pft->pmd_actions = NULL;
+ }
+ if (tunnel_ops->items) {
+ free(pft->items);
+ rte_flow_tunnel_item_release(port_id, pft->pmd_items,
+ pft->num_pmd_items,
+ &error);
+ pft->items = NULL;
+ pft->pmd_items = NULL;
+ }
+}
+
+/** Add port meter policy */
+int
+port_meter_policy_add(portid_t port_id, uint32_t policy_id,
+ const struct rte_flow_action *actions)
+{
+ struct rte_mtr_error error;
+ const struct rte_flow_action *act = actions;
+ const struct rte_flow_action *start;
+ struct rte_mtr_meter_policy_params policy;
+ uint32_t i = 0, act_n;
+ int ret;
+
+ for (i = 0; i < RTE_COLORS; i++) {
+ for (act_n = 0, start = act;
+ act->type != RTE_FLOW_ACTION_TYPE_END; act++)
+ act_n++;
+ if (act_n && act->type == RTE_FLOW_ACTION_TYPE_END)
+ policy.actions[i] = start;
+ else
+ policy.actions[i] = NULL;
+ act++;
+ }
+ ret = rte_mtr_meter_policy_add(port_id,
+ policy_id,
+ &policy, &error);
+ if (ret)
+ print_mtr_err_msg(&error);
+ return ret;
+}
+
+/** Validate flow rule. */
+int
+port_flow_validate(portid_t port_id,
+ const struct rte_flow_attr *attr,
+ const struct rte_flow_item *pattern,
+ const struct rte_flow_action *actions,
+ const struct tunnel_ops *tunnel_ops)
+{
+ struct rte_flow_error error;
+ struct port_flow_tunnel *pft = NULL;
+
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x11, sizeof(error));
+ if (tunnel_ops->enabled) {
+ pft = port_flow_tunnel_offload_cmd_prep(port_id, pattern,
+ actions, tunnel_ops);
+ if (!pft)
+ return -ENOENT;
+ if (pft->items)
+ pattern = pft->items;
+ if (pft->actions)
+ actions = pft->actions;
+ }
+ if (rte_flow_validate(port_id, attr, pattern, actions, &error))
+ return port_flow_complain(&error);
+ if (tunnel_ops->enabled)
+ port_flow_tunnel_offload_cmd_release(port_id, tunnel_ops, pft);
+ printf("Flow rule validated\n");
+ return 0;
+}
+
+/** Return age action structure if exists, otherwise NULL. */
+static struct rte_flow_action_age *
+age_action_get(const struct rte_flow_action *actions)
+{
+ for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+ switch (actions->type) {
+ case RTE_FLOW_ACTION_TYPE_AGE:
+ return (struct rte_flow_action_age *)
+ (uintptr_t)actions->conf;
+ default:
+ break;
+ }
+ }
+ return NULL;
+}
+
+/** Create flow rule. */
+int
+port_flow_create(portid_t port_id,
+ const struct rte_flow_attr *attr,
+ const struct rte_flow_item *pattern,
+ const struct rte_flow_action *actions,
+ const struct tunnel_ops *tunnel_ops)
+{
+ struct rte_flow *flow;
+ struct rte_port *port;
+ struct port_flow *pf;
+ uint32_t id = 0;
+ struct rte_flow_error error;
+ struct port_flow_tunnel *pft = NULL;
+ struct rte_flow_action_age *age = age_action_get(actions);
+
+ port = &ports[port_id];
+ if (port->flow_list) {
+ if (port->flow_list->id == UINT32_MAX) {
+ printf("Highest rule ID is already assigned, delete"
+ " it first");
+ return -ENOMEM;
+ }
+ id = port->flow_list->id + 1;
+ }
+ if (tunnel_ops->enabled) {
+ pft = port_flow_tunnel_offload_cmd_prep(port_id, pattern,
+ actions, tunnel_ops);
+ if (!pft)
+ return -ENOENT;
+ if (pft->items)
+ pattern = pft->items;
+ if (pft->actions)
+ actions = pft->actions;
+ }
+ pf = port_flow_new(attr, pattern, actions, &error);
+ if (!pf)
+ return port_flow_complain(&error);
+ if (age) {
+ pf->age_type = ACTION_AGE_CONTEXT_TYPE_FLOW;
+ age->context = &pf->age_type;
+ }
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x22, sizeof(error));
+ flow = rte_flow_create(port_id, attr, pattern, actions, &error);
+ if (!flow) {
+ if (tunnel_ops->enabled)
+ port_flow_tunnel_offload_cmd_release(port_id,
+ tunnel_ops, pft);
+ free(pf);
+ return port_flow_complain(&error);
+ }
+ pf->next = port->flow_list;
+ pf->id = id;
+ pf->flow = flow;
+ port->flow_list = pf;
+ if (tunnel_ops->enabled)
+ port_flow_tunnel_offload_cmd_release(port_id, tunnel_ops, pft);
+ printf("Flow rule #%u created\n", pf->id);
+ return 0;
+}
+
+/** Destroy a number of flow rules. */
+int
+port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule)
+{
+ struct rte_port *port;
+ struct port_flow **tmp;
+ uint32_t c = 0;
+ int ret = 0;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+ tmp = &port->flow_list;
+ while (*tmp) {
+ uint32_t i;
+
+ for (i = 0; i != n; ++i) {
+ struct rte_flow_error error;
+ struct port_flow *pf = *tmp;
+
+ if (rule[i] != pf->id)
+ continue;
+ /*
+ * Poisoning to make sure PMDs update it in case
+ * of error.
+ */
+ memset(&error, 0x33, sizeof(error));
+ if (rte_flow_destroy(port_id, pf->flow, &error)) {
+ ret = port_flow_complain(&error);
+ continue;
+ }
+ printf("Flow rule #%u destroyed\n", pf->id);
+ *tmp = pf->next;
+ free(pf);
+ break;
+ }
+ if (i == n)
+ tmp = &(*tmp)->next;
+ ++c;
+ }
+ return ret;
+}
+
+/** Remove all flow rules. */
+int
+port_flow_flush(portid_t port_id)
+{
+ struct rte_flow_error error;
+ struct rte_port *port;
+ int ret = 0;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+
+ port = &ports[port_id];
+
+ if (port->flow_list == NULL)
+ return ret;
+
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x44, sizeof(error));
+ if (rte_flow_flush(port_id, &error)) {
+ port_flow_complain(&error);
+ }
+
+ while (port->flow_list) {
+ struct port_flow *pf = port->flow_list->next;
+
+ free(port->flow_list);
+ port->flow_list = pf;
+ }
+ return ret;
+}
+
+/** Dump flow rules. */
+int
+port_flow_dump(portid_t port_id, bool dump_all, uint32_t rule_id,
+ const char *file_name)
+{
+ int ret = 0;
+ FILE *file = stdout;
+ struct rte_flow_error error;
+ struct rte_port *port;
+ struct port_flow *pflow;
+ struct rte_flow *tmpFlow = NULL;
+ bool found = false;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+
+ if (!dump_all) {
+ port = &ports[port_id];
+ pflow = port->flow_list;
+ while (pflow) {
+ if (rule_id != pflow->id) {
+ pflow = pflow->next;
+ } else {
+ tmpFlow = pflow->flow;
+ if (tmpFlow)
+ found = true;
+ break;
+ }
+ }
+ if (found == false) {
+ printf("Failed to dump to flow %d\n", rule_id);
+ return -EINVAL;
+ }
+ }
+
+ if (file_name && strlen(file_name)) {
+ file = fopen(file_name, "w");
+ if (!file) {
+ printf("Failed to create file %s: %s\n", file_name,
+ strerror(errno));
+ return -errno;
+ }
+ }
+
+ if (!dump_all)
+ ret = rte_flow_dev_dump(port_id, tmpFlow, file, &error);
+ else
+ ret = rte_flow_dev_dump(port_id, NULL, file, &error);
+ if (ret) {
+ port_flow_complain(&error);
+ printf("Failed to dump flow: %s\n", strerror(-ret));
+ } else
+ printf("Flow dump finished\n");
+ if (file_name && strlen(file_name))
+ fclose(file);
+ return ret;
+}
+
+/** Query a flow rule. */
+int
+port_flow_query(portid_t port_id, uint32_t rule,
+ const struct rte_flow_action *action)
+{
+ struct rte_flow_error error;
+ struct rte_port *port;
+ struct port_flow *pf;
+ const char *name;
+ union {
+ struct rte_flow_query_count count;
+ struct rte_flow_action_rss rss_conf;
+ struct rte_flow_query_age age;
+ } query;
+ int ret;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+ for (pf = port->flow_list; pf; pf = pf->next)
+ if (pf->id == rule)
+ break;
+ if (!pf) {
+ printf("Flow rule #%u not found\n", rule);
+ return -ENOENT;
+ }
+ ret = rte_flow_conv(RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
+ &name, sizeof(name),
+ (void *)(uintptr_t)action->type, &error);
+ if (ret < 0)
+ return port_flow_complain(&error);
+ switch (action->type) {
+ case RTE_FLOW_ACTION_TYPE_COUNT:
+ case RTE_FLOW_ACTION_TYPE_RSS:
+ case RTE_FLOW_ACTION_TYPE_AGE:
+ break;
+ default:
+ printf("Cannot query action type %d (%s)\n",
+ action->type, name);
+ return -ENOTSUP;
+ }
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x55, sizeof(error));
+ memset(&query, 0, sizeof(query));
+ if (rte_flow_query(port_id, pf->flow, action, &query, &error))
+ return port_flow_complain(&error);
+ switch (action->type) {
+ case RTE_FLOW_ACTION_TYPE_COUNT:
+ printf("%s:\n"
+ " hits_set: %u\n"
+ " bytes_set: %u\n"
+ " hits: %" PRIu64 "\n"
+ " bytes: %" PRIu64 "\n",
+ name,
+ query.count.hits_set,
+ query.count.bytes_set,
+ query.count.hits,
+ query.count.bytes);
+ break;
+ case RTE_FLOW_ACTION_TYPE_RSS:
+ rss_config_display(&query.rss_conf);
+ break;
+ case RTE_FLOW_ACTION_TYPE_AGE:
+ printf("%s:\n"
+ " aged: %u\n"
+ " sec_since_last_hit_valid: %u\n"
+ " sec_since_last_hit: %" PRIu32 "\n",
+ name,
+ query.age.aged,
+ query.age.sec_since_last_hit_valid,
+ query.age.sec_since_last_hit);
+ break;
+ default:
+ printf("Cannot display result for action type %d (%s)\n",
+ action->type, name);
+ break;
+ }
+ return 0;
+}
+
+/** List simply and destroy all aged flows. */
+void
+port_flow_aged(portid_t port_id, uint8_t destroy)
+{
+ void **contexts;
+ int nb_context, total = 0, idx;
+ struct rte_flow_error error;
+ enum age_action_context_type *type;
+ union {
+ struct port_flow *pf;
+ struct port_indirect_action *pia;
+ } ctx;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return;
+ total = rte_flow_get_aged_flows(port_id, NULL, 0, &error);
+ printf("Port %u total aged flows: %d\n", port_id, total);
+ if (total < 0) {
+ port_flow_complain(&error);
+ return;
+ }
+ if (total == 0)
+ return;
+ contexts = malloc(sizeof(void *) * total);
+ if (contexts == NULL) {
+ printf("Cannot allocate contexts for aged flow\n");
+ return;
+ }
+ printf("%-20s\tID\tGroup\tPrio\tAttr\n", "Type");
+ nb_context = rte_flow_get_aged_flows(port_id, contexts, total, &error);
+ if (nb_context != total) {
+ printf("Port:%d get aged flows count(%d) != total(%d)\n",
+ port_id, nb_context, total);
+ free(contexts);
+ return;
+ }
+ total = 0;
+ for (idx = 0; idx < nb_context; idx++) {
+ if (!contexts[idx]) {
+ printf("Error: get Null context in port %u\n", port_id);
+ continue;
+ }
+ type = (enum age_action_context_type *)contexts[idx];
+ switch (*type) {
+ case ACTION_AGE_CONTEXT_TYPE_FLOW:
+ ctx.pf = container_of(type, struct port_flow, age_type);
+ printf("%-20s\t%" PRIu32 "\t%" PRIu32 "\t%" PRIu32
+ "\t%c%c%c\t\n",
+ "Flow",
+ ctx.pf->id,
+ ctx.pf->rule.attr->group,
+ ctx.pf->rule.attr->priority,
+ ctx.pf->rule.attr->ingress ? 'i' : '-',
+ ctx.pf->rule.attr->egress ? 'e' : '-',
+ ctx.pf->rule.attr->transfer ? 't' : '-');
+ if (destroy && !port_flow_destroy(port_id, 1,
+ &ctx.pf->id))
+ total++;
+ break;
+ case ACTION_AGE_CONTEXT_TYPE_INDIRECT_ACTION:
+ ctx.pia = container_of(type,
+ struct port_indirect_action, age_type);
+ printf("%-20s\t%" PRIu32 "\n", "Indirect action",
+ ctx.pia->id);
+ break;
+ default:
+ printf("Error: invalid context type %u\n", port_id);
+ break;
+ }
+ }
+ printf("\n%d flows destroyed\n", total);
+ free(contexts);
+}
+
+/** List flow rules. */
+void
+port_flow_list(portid_t port_id, uint32_t n, const uint32_t *group)
+{
+ struct rte_port *port;
+ struct port_flow *pf;
+ struct port_flow *list = NULL;
+ uint32_t i;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return;
+ port = &ports[port_id];
+ if (!port->flow_list)
+ return;
+ /* Sort flows by group, priority and ID. */
+ for (pf = port->flow_list; pf != NULL; pf = pf->next) {
+ struct port_flow **tmp;
+ const struct rte_flow_attr *curr = pf->rule.attr;
+
+ if (n) {
+ /* Filter out unwanted groups. */
+ for (i = 0; i != n; ++i)
+ if (curr->group == group[i])
+ break;
+ if (i == n)
+ continue;
+ }
+ for (tmp = &list; *tmp; tmp = &(*tmp)->tmp) {
+ const struct rte_flow_attr *comp = (*tmp)->rule.attr;
+
+ if (curr->group > comp->group ||
+ (curr->group == comp->group &&
+ curr->priority > comp->priority) ||
+ (curr->group == comp->group &&
+ curr->priority == comp->priority &&
+ pf->id > (*tmp)->id))
+ continue;
+ break;