+ return type;
+}
+
+struct port_flow_tunnel *
+port_flow_locate_tunnel(uint16_t port_id, struct rte_flow_tunnel *tun)
+{
+ struct rte_port *port = &ports[port_id];
+ struct port_flow_tunnel *flow_tunnel;
+
+ LIST_FOREACH(flow_tunnel, &port->flow_tunnel_list, chain) {
+ if (!memcmp(&flow_tunnel->tunnel, tun, sizeof(*tun)))
+ goto out;
+ }
+ flow_tunnel = NULL;
+
+out:
+ return flow_tunnel;
+}
+
+void port_flow_tunnel_list(portid_t port_id)
+{
+ struct rte_port *port = &ports[port_id];
+ struct port_flow_tunnel *flt;
+
+ LIST_FOREACH(flt, &port->flow_tunnel_list, chain) {
+ printf("port %u tunnel #%u type=%s",
+ port_id, flt->id, port_flow_tunnel_type(&flt->tunnel));
+ if (flt->tunnel.tun_id)
+ printf(" id=%" PRIu64, flt->tunnel.tun_id);
+ printf("\n");
+ }
+}
+
+void port_flow_tunnel_destroy(portid_t port_id, uint32_t tunnel_id)
+{
+ struct rte_port *port = &ports[port_id];
+ struct port_flow_tunnel *flt;
+
+ LIST_FOREACH(flt, &port->flow_tunnel_list, chain) {
+ if (flt->id == tunnel_id)
+ break;
+ }
+ if (flt) {
+ LIST_REMOVE(flt, chain);
+ free(flt);
+ printf("port %u: flow tunnel #%u destroyed\n",
+ port_id, tunnel_id);
+ }
+}
+
+void port_flow_tunnel_create(portid_t port_id, const struct tunnel_ops *ops)
+{
+ struct rte_port *port = &ports[port_id];
+ enum rte_flow_item_type type;
+ struct port_flow_tunnel *flt;
+
+ if (!strcmp(ops->type, "vxlan"))
+ type = RTE_FLOW_ITEM_TYPE_VXLAN;
+ else if (!strcmp(ops->type, "gre"))
+ type = RTE_FLOW_ITEM_TYPE_GRE;
+ else if (!strcmp(ops->type, "nvgre"))
+ type = RTE_FLOW_ITEM_TYPE_NVGRE;
+ else if (!strcmp(ops->type, "geneve"))
+ type = RTE_FLOW_ITEM_TYPE_GENEVE;
+ else {
+ fprintf(stderr, "cannot offload \"%s\" tunnel type\n",
+ ops->type);
+ return;
+ }
+ LIST_FOREACH(flt, &port->flow_tunnel_list, chain) {
+ if (flt->tunnel.type == type)
+ break;
+ }
+ if (!flt) {
+ flt = calloc(1, sizeof(*flt));
+ if (!flt) {
+ fprintf(stderr, "failed to allocate port flt object\n");
+ return;
+ }
+ flt->tunnel.type = type;
+ flt->id = LIST_EMPTY(&port->flow_tunnel_list) ? 1 :
+ LIST_FIRST(&port->flow_tunnel_list)->id + 1;
+ LIST_INSERT_HEAD(&port->flow_tunnel_list, flt, chain);
+ }
+ printf("port %d: flow tunnel #%u type %s\n",
+ port_id, flt->id, ops->type);
+}
+
+/** Generate a port_flow entry from attributes/pattern/actions. */
+static struct port_flow *
+port_flow_new(const struct rte_flow_attr *attr,
+ const struct rte_flow_item *pattern,
+ const struct rte_flow_action *actions,
+ struct rte_flow_error *error)
+{
+ const struct rte_flow_conv_rule rule = {
+ .attr_ro = attr,
+ .pattern_ro = pattern,
+ .actions_ro = actions,
+ };
+ struct port_flow *pf;
+ int ret;
+
+ ret = rte_flow_conv(RTE_FLOW_CONV_OP_RULE, NULL, 0, &rule, error);
+ if (ret < 0)
+ return NULL;
+ pf = calloc(1, offsetof(struct port_flow, rule) + ret);
+ if (!pf) {
+ rte_flow_error_set
+ (error, errno, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "calloc() failed");
+ return NULL;
+ }
+ 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];
+ fprintf(stderr, "%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));
+
+ switch (error->type) {
+ case RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER:
+ fprintf(stderr, "The status suggests the use of \"transfer\" "
+ "as the possible cause of the failure. Make "
+ "sure that the flow in question and its "
+ "indirect components (if any) are managed "
+ "via \"transfer\" proxy port. Use command "
+ "\"show port (port_id) flow transfer proxy\" "
+ "to figure out the proxy port ID\n");
+ break;
+ default:
+ break;
+ }
+
+ return -err;
+}
+
+static void
+rss_config_display(struct rte_flow_action_rss *rss_conf)
+{
+ uint8_t i;
+
+ if (rss_conf == NULL) {
+ fprintf(stderr, "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;