+ if (rte_flow_conv(RTE_FLOW_CONV_OP_RULE, &pf->rule, ret, &rule,
+ error) >= 0)
+ return pf;
+ free(pf);
+ return NULL;
+}
+
+/** Print a message out of a flow error. */
+static int
+port_flow_complain(struct rte_flow_error *error)
+{
+ static const char *const errstrlist[] = {
+ [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",
+ };
+ const char *errstr;
+ char buf[32];
+ int err = rte_errno;
+
+ if ((unsigned int)error->type >= RTE_DIM(errstrlist) ||
+ !errstrlist[error->type])
+ errstr = "unknown type";
+ else
+ errstr = errstrlist[error->type];
+ printf("%s(): Caught PMD error type %d (%s): %s%s: %s\n", __func__,
+ error->type, errstr,
+ error->cause ? (snprintf(buf, sizeof(buf), "cause: %p, ",
+ error->cause), buf) : "",
+ error->message ? error->message : "(no stated reason)",
+ rte_strerror(err));
+ return -err;
+}
+
+static void
+rss_config_display(struct rte_flow_action_rss *rss_conf)
+{
+ uint8_t i;
+
+ if (rss_conf == NULL) {
+ printf("Invalid rule\n");
+ return;
+ }
+
+ printf("RSS:\n"
+ " queues:");
+ if (rss_conf->queue_num == 0)
+ printf(" none");
+ for (i = 0; i < rss_conf->queue_num; i++)
+ printf(" %d", rss_conf->queue[i]);
+ printf("\n");
+
+ printf(" function: ");
+ switch (rss_conf->func) {
+ case RTE_ETH_HASH_FUNCTION_DEFAULT:
+ printf("default\n");
+ break;
+ case RTE_ETH_HASH_FUNCTION_TOEPLITZ:
+ printf("toeplitz\n");
+ break;
+ case RTE_ETH_HASH_FUNCTION_SIMPLE_XOR:
+ printf("simple_xor\n");
+ break;
+ case RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ:
+ printf("symmetric_toeplitz\n");
+ break;
+ default:
+ printf("Unknown function\n");
+ return;
+ }
+
+ printf(" types:\n");
+ if (rss_conf->types == 0) {
+ printf(" none\n");
+ return;
+ }
+ for (i = 0; rss_type_table[i].str; i++) {
+ if ((rss_conf->types &
+ rss_type_table[i].rss_type) ==
+ rss_type_table[i].rss_type &&
+ rss_type_table[i].rss_type != 0)
+ printf(" %s\n", rss_type_table[i].str);
+ }
+}
+
+static struct port_indirect_action *
+action_get_by_id(portid_t port_id, uint32_t id)
+{
+ struct rte_port *port;
+ struct port_indirect_action **ppia;
+ struct port_indirect_action *pia = NULL;
+
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return NULL;
+ port = &ports[port_id];
+ ppia = &port->actions_list;
+ while (*ppia) {
+ if ((*ppia)->id == id) {
+ pia = *ppia;
+ break;
+ }
+ ppia = &(*ppia)->next;
+ }
+ if (!pia)
+ printf("Failed to find indirect action #%u on port %u\n",
+ id, port_id);
+ return pia;
+}
+
+static int
+action_alloc(portid_t port_id, uint32_t id,
+ struct port_indirect_action **action)
+{
+ struct rte_port *port;
+ struct port_indirect_action **ppia;
+ struct port_indirect_action *pia = NULL;
+
+ *action = NULL;
+ if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+ port_id == (portid_t)RTE_PORT_ALL)
+ return -EINVAL;
+ port = &ports[port_id];
+ if (id == UINT32_MAX) {
+ /* taking first available ID */
+ if (port->actions_list) {
+ if (port->actions_list->id == UINT32_MAX - 1) {
+ printf("Highest indirect action ID is already"
+ " assigned, delete it first\n");
+ return -ENOMEM;
+ }
+ id = port->actions_list->id + 1;
+ } else {
+ id = 0;
+ }
+ }
+ pia = calloc(1, sizeof(*pia));
+ if (!pia) {
+ printf("Allocation of port %u indirect action failed\n",
+ port_id);
+ return -ENOMEM;
+ }
+ ppia = &port->actions_list;
+ while (*ppia && (*ppia)->id > id)
+ ppia = &(*ppia)->next;
+ if (*ppia && (*ppia)->id == id) {
+ printf("Indirect action #%u is already assigned,"
+ " delete it first\n", id);
+ free(pia);
+ return -EINVAL;
+ }
+ pia->next = *ppia;
+ pia->id = id;
+ *ppia = pia;
+ *action = pia;
+ return 0;
+}
+
+/** Create indirect action */
+int
+port_action_handle_create(portid_t port_id, uint32_t id,
+ const struct rte_flow_indir_action_conf *conf,
+ const struct rte_flow_action *action)
+{
+ struct port_indirect_action *pia;
+ int ret;
+ struct rte_flow_error error;
+
+ ret = action_alloc(port_id, id, &pia);
+ if (ret)
+ return ret;
+ if (action->type == RTE_FLOW_ACTION_TYPE_AGE) {
+ struct rte_flow_action_age *age =
+ (struct rte_flow_action_age *)(uintptr_t)(action->conf);
+
+ pia->age_type = ACTION_AGE_CONTEXT_TYPE_INDIRECT_ACTION;
+ age->context = &pia->age_type;
+ } else if (action->type == RTE_FLOW_ACTION_TYPE_CONNTRACK) {
+ struct rte_flow_action_conntrack *ct =
+ (struct rte_flow_action_conntrack *)(uintptr_t)(action->conf);
+
+ memcpy(ct, &conntrack_context, sizeof(*ct));
+ }
+ /* Poisoning to make sure PMDs update it in case of error. */
+ memset(&error, 0x22, sizeof(error));
+ pia->handle = rte_flow_action_handle_create(port_id, conf, action,
+ &error);
+ if (!pia->handle) {
+ uint32_t destroy_id = pia->id;
+ port_action_handle_destroy(port_id, 1, &destroy_id);
+ return port_flow_complain(&error);
+ }
+ pia->type = action->type;
+ printf("Indirect action #%u created\n", pia->id);
+ return 0;
+}
+
+/** Destroy indirect action */
+int
+port_action_handle_destroy(portid_t port_id,
+ uint32_t n,
+ const uint32_t *actions)
+{
+ struct rte_port *port;
+ struct port_indirect_action **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->actions_list;
+ while (*tmp) {
+ uint32_t i;
+
+ for (i = 0; i != n; ++i) {
+ struct rte_flow_error error;
+ struct port_indirect_action *pia = *tmp;
+
+ if (actions[i] != pia->id)
+ continue;
+ /*
+ * Poisoning to make sure PMDs update it in case
+ * of error.
+ */
+ memset(&error, 0x33, sizeof(error));
+
+ if (pia->handle && rte_flow_action_handle_destroy(
+ port_id, pia->handle, &error)) {
+ ret = port_flow_complain(&error);
+ continue;
+ }
+ *tmp = pia->next;
+ printf("Indirect action #%u destroyed\n", pia->id);
+ free(pia);
+ break;
+ }
+ if (i == n)
+ tmp = &(*tmp)->next;
+ ++c;
+ }
+ return ret;
+}
+
+
+/** Get indirect action by port + id */
+struct rte_flow_action_handle *
+port_action_handle_get_by_id(portid_t port_id, uint32_t id)
+{
+
+ struct port_indirect_action *pia = action_get_by_id(port_id, id);
+
+ return (pia) ? pia->handle : NULL;
+}
+
+/** Update indirect action */
+int
+port_action_handle_update(portid_t port_id, uint32_t id,
+ const struct rte_flow_action *action)
+{
+ struct rte_flow_error error;
+ struct rte_flow_action_handle *action_handle;
+ struct port_indirect_action *pia;
+ const void *update;
+
+ action_handle = port_action_handle_get_by_id(port_id, id);
+ if (!action_handle)
+ return -EINVAL;
+ pia = action_get_by_id(port_id, id);
+ if (!pia)
+ return -EINVAL;
+ switch (pia->type) {
+ case RTE_FLOW_ACTION_TYPE_CONNTRACK:
+ update = action->conf;
+ break;
+ default:
+ update = action;
+ break;
+ }
+ if (rte_flow_action_handle_update(port_id, action_handle, update,
+ &error)) {
+ return port_flow_complain(&error);
+ }
+ printf("Indirect action #%u updated\n", id);
+ return 0;
+}
+
+int
+port_action_handle_query(portid_t port_id, uint32_t id)
+{
+ struct rte_flow_error error;
+ struct port_indirect_action *pia;
+ union {
+ struct rte_flow_query_count count;
+ struct rte_flow_query_age age;
+ struct rte_flow_action_conntrack ct;
+ } query;
+
+ pia = action_get_by_id(port_id, id);
+ if (!pia)
+ return -EINVAL;
+ switch (pia->type) {
+ case RTE_FLOW_ACTION_TYPE_AGE:
+ case RTE_FLOW_ACTION_TYPE_COUNT:
+ break;
+ default:
+ printf("Indirect action %u (type: %d) on port %u doesn't support query\n",
+ id, pia->type, port_id);
+ 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_action_handle_query(port_id, pia->handle, &query, &error))
+ return port_flow_complain(&error);
+ switch (pia->type) {
+ case RTE_FLOW_ACTION_TYPE_AGE:
+ printf("Indirect AGE action:\n"
+ " aged: %u\n"
+ " sec_since_last_hit_valid: %u\n"
+ " sec_since_last_hit: %" PRIu32 "\n",
+ query.age.aged,
+ query.age.sec_since_last_hit_valid,
+ query.age.sec_since_last_hit);
+ break;
+ case RTE_FLOW_ACTION_TYPE_COUNT:
+ printf("Indirect COUNT action:\n"
+ " hits_set: %u\n"
+ " bytes_set: %u\n"
+ " hits: %" PRIu64 "\n"
+ " bytes: %" PRIu64 "\n",
+ query.count.hits_set,
+ query.count.bytes_set,
+ query.count.hits,
+ query.count.bytes);
+ break;
+ case RTE_FLOW_ACTION_TYPE_CONNTRACK:
+ printf("Conntrack Context:\n"
+ " Peer: %u, Flow dir: %s, Enable: %u\n"
+ " Live: %u, SACK: %u, CACK: %u\n"
+ " Packet dir: %s, Liberal: %u, State: %u\n"
+ " Factor: %u, Retrans: %u, TCP flags: %u\n"
+ " Last Seq: %u, Last ACK: %u\n"
+ " Last Win: %u, Last End: %u\n",
+ query.ct.peer_port,
+ query.ct.is_original_dir ? "Original" : "Reply",
+ query.ct.enable, query.ct.live_connection,
+ query.ct.selective_ack, query.ct.challenge_ack_passed,
+ query.ct.last_direction ? "Original" : "Reply",
+ query.ct.liberal_mode, query.ct.state,
+ query.ct.max_ack_window, query.ct.retransmission_limit,
+ query.ct.last_index, query.ct.last_seq,
+ query.ct.last_ack, query.ct.last_window,
+ query.ct.last_end);
+ printf(" Original Dir:\n"
+ " scale: %u, fin: %u, ack seen: %u\n"
+ " unacked data: %u\n Sent end: %u,"
+ " Reply end: %u, Max win: %u, Max ACK: %u\n",
+ query.ct.original_dir.scale,
+ query.ct.original_dir.close_initiated,
+ query.ct.original_dir.last_ack_seen,
+ query.ct.original_dir.data_unacked,
+ query.ct.original_dir.sent_end,
+ query.ct.original_dir.reply_end,
+ query.ct.original_dir.max_win,
+ query.ct.original_dir.max_ack);
+ printf(" Reply Dir:\n"
+ " scale: %u, fin: %u, ack seen: %u\n"
+ " unacked data: %u\n Sent end: %u,"
+ " Reply end: %u, Max win: %u, Max ACK: %u\n",
+ query.ct.reply_dir.scale,
+ query.ct.reply_dir.close_initiated,
+ query.ct.reply_dir.last_ack_seen,
+ query.ct.reply_dir.data_unacked,
+ query.ct.reply_dir.sent_end,
+ query.ct.reply_dir.reply_end,
+ query.ct.reply_dir.max_win,
+ query.ct.reply_dir.max_ack);
+ break;
+ default:
+ printf("Indirect action %u (type: %d) on port %u doesn't support query\n",
+ id, pia->type, port_id);
+ break;
+ }
+ return 0;
+}
+
+static struct port_flow_tunnel *
+port_flow_tunnel_offload_cmd_prep(portid_t port_id,
+ const struct rte_flow_item *pattern,
+ const struct rte_flow_action *actions,
+ const struct tunnel_ops *tunnel_ops)
+{
+ int ret;
+ struct rte_port *port;
+ struct port_flow_tunnel *pft;
+ struct rte_flow_error error;
+
+ port = &ports[port_id];
+ 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;
+ }