[v2,4/4] app/testpmd: support tunnel offload API

Message ID 20200908201552.14423-5-getelson@nvidia.com (mailing list archive)
State Superseded, archived
Delegated to: Ferruh Yigit
Headers
Series Tunnel Offload API |

Checks

Context Check Description
ci/iol-testing fail Testing issues
ci/travis-robot warning Travis build: failed
ci/checkpatch warning coding style issues
ci/Intel-compilation fail Compilation issues

Commit Message

Etelson, Gregory Sept. 8, 2020, 8:15 p.m. UTC
Tunnel Offload API provides hardware independent, unified model
to offload tunneled traffic. Key model elements are:
 - apply matches to both outer and inner packet headers
   during entire offload procedure;
 - restore outer header of partially offloaded packet;
 - model is implemented as a set of helper functions.

Implementation details:

* Create application tunnel:
flow tunnel <port> type <tunnel type>
On success, the command creates application tunnel object and returns
the tunnel descriptor. Tunnel descriptor is used in subsequent flow
creation commands to reference the tunnel.

* Create tunnel steering flow rule:
tunnel_set <tunnel descriptor> parameter used with steering rule
template.

* Create tunnel matching flow rule:
tunnel_match <tunnel descriptor> used with matching rule template.

* If tunnel steering rule was offloaded, outer header of a partially
offloaded packet is restored after miss.

Example:
test packet=
<Ether  dst=24:8a:07:8d:ae:d6 src=50:6b:4b:cc:fc:e2 type=IPv4 |
<IP  version=4 ihl=5 proto=udp src=1.1.1.1 dst=1.1.1.10 |
<UDP  sport=4789 dport=4789 len=58 chksum=0x7f7b |
<VXLAN  NextProtocol=Ethernet vni=0x0 |
<Ether  dst=24:aa:aa:aa:aa:d6 src=50:bb:bb:bb:bb:e2 type=IPv4 |
<IP  version=4 ihl=5 proto=icmp src=2.2.2.2 dst=2.2.2.200 |
<ICMP  type=echo-request code=0 chksum=0xf7ff id=0x0 seq=0x0 |>>>>>>>
>>> len(packet)
92

testpmd> flow flush 0
testpmd> port 0/queue 0: received 1 packets
src=50:6B:4B:CC:FC:E2 - dst=24:8A:07:8D:AE:D6 - type=0x0800 -
length=92

testpmd> flow tunnel 0 type vxlan
port 0: flow tunnel #1 type vxlan
testpmd> flow create 0 ingress group 0 tunnel_set 1
         pattern eth /ipv4 / udp dst is 4789 / vxlan / end
         actions  jump group 0 / end
Flow rule #0 created
testpmd> port 0/queue 0: received 1 packets
tunnel restore info: - vxlan tunnel - outer header present # <--
  src=50:6B:4B:CC:FC:E2 - dst=24:8A:07:8D:AE:D6 - type=0x0800 -
length=92

testpmd> flow create 0 ingress group 0 tunnel_match 1
         pattern eth / ipv4 / udp dst is 4789 / vxlan / eth / ipv4 /
         end
         actions set_mac_dst mac_addr 02:CA:FE:CA:FA:80 /
         queue index 0 / end
Flow rule #1 created
testpmd> port 0/queue 0: received 1 packets
  src=50:BB:BB:BB:BB:E2 - dst=02:CA:FE:CA:FA:80 - type=0x0800 -
length=42

TODO: add tunnel destruction command.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
---
v2:
* introduce testpmd support for tunnel offload API
---
 app/test-pmd/cmdline_flow.c | 102 ++++++++++++++++++++++++-
 app/test-pmd/config.c       | 147 +++++++++++++++++++++++++++++++++++-
 app/test-pmd/testpmd.c      |   5 +-
 app/test-pmd/testpmd.h      |  27 ++++++-
 app/test-pmd/util.c         |  30 +++++++-
 5 files changed, 302 insertions(+), 9 deletions(-)
  

Comments

Ajit Khaparde Sept. 15, 2020, 4:47 a.m. UTC | #1
On Tue, Sep 8, 2020 at 1:17 PM Gregory Etelson <getelson@nvidia.com> wrote:

> ::::snip::::
> @@ -1520,6 +1574,75 @@ port_flow_create(portid_t port_id,
>                 }
>                 id = port->flow_list->id + 1;
>         }
> +       if (tunnel_ops->enabled) {
> +               int ret;
> +               pft = port_flow_locate_tunnel(port, tunnel_ops->id);
> +               if (!pft) {
> +                       printf("failed to locate port flow tunnel #%u\n",
> +                               tunnel_ops->id);
> +                       return -ENOENT;
> +               }
> +               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);
>
Does tunnel_ops always indicate decap?
Shouldn't there be a check for encap/decap? Or check for direction?



> +                       if (ret) {
> +                               port_flow_complain(&error);
> +                               return -EINVAL;
> +                       }
> ::::snip::::
>
>
  
Etelson, Gregory Sept. 15, 2020, 10:44 a.m. UTC | #2
::::snip::::
@@ -1520,6 +1574,75 @@ port_flow_create(portid_t port_id,
                }
                id = port->flow_list->id + 1;
        }
+       if (tunnel_ops->enabled) {
+               int ret;
+               pft = port_flow_locate_tunnel(port, tunnel_ops->id);
+               if (!pft) {
+                       printf("failed to locate port flow tunnel #%u\n",
+                               tunnel_ops->id);
+                       return -ENOENT;
+               }
+               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);
> Does tunnel_ops always indicate decap?
> Shouldn't there be a check for encap/decap? Or check for direction?

When tunnel offload API is enabled, application does not need to specify
decap  in flow rules - that action is carried out internally by PMD.
If application activated the API, it's expected that it will call rte_flow_tunnel_decap_set()
and rte_flow_tunnel_math()  and use results obtained by the functions to compile flow rule parameters.
tunnel_ops members specify 2 things
1 - tunnel offload API was activated
2 - what function, decap_set or match, should be activated and how to proceed with results
 
+                       if (ret) {
+                               port_flow_complain(&error);
+                               return -EINVAL;
+                       }
::::snip::::
  

Patch

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 6263d307ed..f0a7a4a9ea 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -69,6 +69,10 @@  enum index {
 	LIST,
 	AGED,
 	ISOLATE,
+	TUNNEL,
+
+	/* Tunnel argumens. */
+	TUNNEL_RULE,
 
 	/* Destroy arguments. */
 	DESTROY_RULE,
@@ -88,6 +92,8 @@  enum index {
 	INGRESS,
 	EGRESS,
 	TRANSFER,
+	TUNNEL_SET,
+	TUNNEL_MATCH,
 
 	/* Validate/create pattern. */
 	PATTERN,
@@ -653,6 +659,7 @@  struct buffer {
 	union {
 		struct {
 			struct rte_flow_attr attr;
+			struct tunnel_ops tunnel_ops;
 			struct rte_flow_item *pattern;
 			struct rte_flow_action *actions;
 			uint32_t pattern_n;
@@ -713,10 +720,18 @@  static const enum index next_vc_attr[] = {
 	INGRESS,
 	EGRESS,
 	TRANSFER,
+	TUNNEL_SET,
+	TUNNEL_MATCH,
 	PATTERN,
 	ZERO,
 };
 
+static const enum index next_tunnel_attr[] = {
+	TUNNEL_RULE,
+	END,
+	ZERO,
+};
+
 static const enum index next_destroy_attr[] = {
 	DESTROY_RULE,
 	END,
@@ -1516,6 +1531,9 @@  static int parse_aged(struct context *, const struct token *,
 static int parse_isolate(struct context *, const struct token *,
 			 const char *, unsigned int,
 			 void *, unsigned int);
+static int parse_tunnel(struct context *, const struct token *,
+			const char *, unsigned int,
+			void *, unsigned int);
 static int parse_int(struct context *, const struct token *,
 		     const char *, unsigned int,
 		     void *, unsigned int);
@@ -1698,7 +1716,8 @@  static const struct token token_list[] = {
 			      LIST,
 			      AGED,
 			      QUERY,
-			      ISOLATE)),
+			      ISOLATE,
+			      TUNNEL)),
 		.call = parse_init,
 	},
 	/* Sub-level commands. */
@@ -1772,6 +1791,21 @@  static const struct token token_list[] = {
 			     ARGS_ENTRY(struct buffer, port)),
 		.call = parse_isolate,
 	},
+	[TUNNEL] = {
+		.name = "tunnel",
+		.help = "new tunnel API",
+		.next = NEXT(NEXT_ENTRY(TUNNEL_RULE), NEXT_ENTRY(PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_tunnel,
+	},
+	/* Tunnel arguments. */
+	[TUNNEL_RULE]{
+		.name = "type",
+		.help = "specify tunnel type",
+		.next = NEXT(next_tunnel_attr, NEXT_ENTRY(FILE_PATH)),
+		.args = ARGS(ARGS_ENTRY(struct tunnel_ops, type)),
+		.call = parse_tunnel,
+	},
 	/* Destroy arguments. */
 	[DESTROY_RULE] = {
 		.name = "rule",
@@ -1835,6 +1869,20 @@  static const struct token token_list[] = {
 		.next = NEXT(next_vc_attr),
 		.call = parse_vc,
 	},
+	[TUNNEL_SET] = {
+		.name = "tunnel_set",
+		.help = "tunnel steer rule",
+		.next = NEXT(next_vc_attr, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct tunnel_ops, id)),
+		.call = parse_vc,
+	},
+	[TUNNEL_MATCH] = {
+		.name = "tunnel_match",
+		.help = "tunnel match rule",
+		.next = NEXT(next_vc_attr, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct tunnel_ops, id)),
+		.call = parse_vc,
+	},
 	/* Validate/create pattern. */
 	[PATTERN] = {
 		.name = "pattern",
@@ -4054,12 +4102,28 @@  parse_vc(struct context *ctx, const struct token *token,
 		return len;
 	}
 	ctx->objdata = 0;
-	ctx->object = &out->args.vc.attr;
+	switch (ctx->curr) {
+	default:
+		ctx->object = &out->args.vc.attr;
+		break;
+	case TUNNEL_SET:
+	case TUNNEL_MATCH:
+		ctx->object = &out->args.vc.tunnel_ops;
+		break;
+	}
 	ctx->objmask = NULL;
 	switch (ctx->curr) {
 	case GROUP:
 	case PRIORITY:
 		return len;
+	case TUNNEL_SET:
+		out->args.vc.tunnel_ops.enabled = 1;
+		out->args.vc.tunnel_ops.actions = 1;
+		return len;
+	case TUNNEL_MATCH:
+		out->args.vc.tunnel_ops.enabled = 1;
+		out->args.vc.tunnel_ops.items = 1;
+		return len;
 	case INGRESS:
 		out->args.vc.attr.ingress = 1;
 		return len;
@@ -5597,6 +5661,34 @@  parse_isolate(struct context *ctx, const struct token *token,
 	return len;
 }
 
+static int
+parse_tunnel(struct context *ctx, const struct token *token,
+	     const char *str, unsigned int len,
+	     void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (!out->command) {
+		if (ctx->curr != TUNNEL)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+	}
+	if (ctx->curr == TUNNEL_RULE)
+		ctx->object = &out->args.vc.tunnel_ops;
+	return len;
+}
+
 /**
  * Parse signed/unsigned integers 8 to 64-bit long.
  *
@@ -6547,7 +6639,8 @@  cmd_flow_parsed(const struct buffer *in)
 		break;
 	case CREATE:
 		port_flow_create(in->port, &in->args.vc.attr,
-				 in->args.vc.pattern, in->args.vc.actions);
+				 in->args.vc.pattern, in->args.vc.actions,
+				 &in->args.vc.tunnel_ops);
 		break;
 	case DESTROY:
 		port_flow_destroy(in->port, in->args.destroy.rule_n,
@@ -6573,6 +6666,9 @@  cmd_flow_parsed(const struct buffer *in)
 	case AGED:
 		port_flow_aged(in->port, in->args.aged.destroy);
 		break;
+	case TUNNEL:
+		port_flow_add_tunnel(in->port, &in->args.vc.tunnel_ops);
+		break;
 	default:
 		break;
 	}
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 30bee33248..b90927f451 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1337,6 +1337,58 @@  port_mtu_set(portid_t port_id, uint16_t mtu)
 
 /* Generic flow management functions. */
 
+static struct port_flow_tunnel *
+port_flow_locate_tunnel(struct rte_port *port, uint32_t port_tunnel_id)
+{
+	struct port_flow_tunnel *flow_tunnel;
+
+	LIST_FOREACH(flow_tunnel, &port->flow_tunnel_list, chain) {
+		if (flow_tunnel->id == port_tunnel_id)
+			goto out;
+	}
+	flow_tunnel = NULL;
+
+out:
+	return flow_tunnel;
+}
+
+void port_flow_release_tunnel(struct port_flow_tunnel *flow_tunnel)
+{
+	LIST_REMOVE(flow_tunnel, chain);
+	free(flow_tunnel);
+}
+
+void port_flow_add_tunnel(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 *flow_tunnel;
+
+	if (!strncmp(ops->type, "vxlan", strlen("vxlan")))
+		type = RTE_FLOW_ITEM_TYPE_VXLAN;
+	else {
+		printf("cannot offload \"%s\" tunnel type\n", ops->type);
+		return;
+	}
+	LIST_FOREACH(flow_tunnel, &port->flow_tunnel_list, chain) {
+		if (flow_tunnel->tunnel.type == type)
+			break;
+	}
+	if (!flow_tunnel) {
+		flow_tunnel = calloc(1, sizeof(*flow_tunnel));
+		if (!flow_tunnel) {
+			printf("failed to allocate port flow_tunnel object\n");
+			return;
+		}
+		flow_tunnel->tunnel.type = type;
+		flow_tunnel->id = LIST_EMPTY(&port->flow_tunnel_list) ? 1 :
+				  LIST_FIRST(&port->flow_tunnel_list)->id + 1;
+		LIST_INSERT_HEAD(&port->flow_tunnel_list, flow_tunnel, chain);
+	}
+	printf("port %d: flow tunnel #%u type %s\n",
+		port_id, flow_tunnel->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,
@@ -1503,13 +1555,15 @@  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 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;
 
 	port = &ports[port_id];
 	if (port->flow_list) {
@@ -1520,6 +1574,75 @@  port_flow_create(portid_t port_id,
 		}
 		id = port->flow_list->id + 1;
 	}
+	if (tunnel_ops->enabled) {
+		int ret;
+		pft = port_flow_locate_tunnel(port, tunnel_ops->id);
+		if (!pft) {
+			printf("failed to locate port flow tunnel #%u\n",
+				tunnel_ops->id);
+			return -ENOENT;
+		}
+		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 -EINVAL;
+			}
+			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 -ENOMEM;
+			}
+			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]));
+			actions = pft->actions;
+		}
+		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 -EINVAL;
+			}
+			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 -ENOMEM;
+			}
+			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]));
+			pattern = pft->items;
+		}
+
+	}
 	pf = port_flow_new(attr, pattern, actions, &error);
 	if (!pf)
 		return port_flow_complain(&error);
@@ -1535,6 +1658,20 @@  port_flow_create(portid_t port_id,
 	pf->id = id;
 	pf->flow = flow;
 	port->flow_list = pf;
+	if (tunnel_ops->enabled) {
+		if (tunnel_ops->actions) {
+			free(pft->actions);
+			rte_flow_tunnel_action_decap_release(
+				port_id, pft->pmd_actions,
+				pft->num_pmd_actions, &error);
+		}
+		if (tunnel_ops->items) {
+			free(pft->items);
+			rte_flow_tunnel_item_release(port_id, pft->pmd_items,
+						     pft->num_pmd_items,
+						     &error);
+		}
+	}
 	printf("Flow rule #%u created\n", pf->id);
 	return 0;
 }
@@ -1829,7 +1966,9 @@  port_flow_list(portid_t port_id, uint32_t n, const uint32_t group[n])
 		       pf->rule.attr->egress ? 'e' : '-',
 		       pf->rule.attr->transfer ? 't' : '-');
 		while (item->type != RTE_FLOW_ITEM_TYPE_END) {
-			if (rte_flow_conv(RTE_FLOW_CONV_OP_ITEM_NAME_PTR,
+			if ((uint32_t)item->type > INT_MAX)
+				name = "PMD_INTERNAL";
+			else if (rte_flow_conv(RTE_FLOW_CONV_OP_ITEM_NAME_PTR,
 					  &name, sizeof(name),
 					  (void *)(uintptr_t)item->type,
 					  NULL) <= 0)
@@ -1840,7 +1979,9 @@  port_flow_list(portid_t port_id, uint32_t n, const uint32_t group[n])
 		}
 		printf("=>");
 		while (action->type != RTE_FLOW_ACTION_TYPE_END) {
-			if (rte_flow_conv(RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
+			if ((uint32_t)action->type > INT_MAX)
+				name = "PMD_INTERNAL";
+			else if (rte_flow_conv(RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
 					  &name, sizeof(name),
 					  (void *)(uintptr_t)action->type,
 					  NULL) <= 0)
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index 7842c3b781..e2330116e1 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -3591,6 +3591,8 @@  init_port_dcb_config(portid_t pid,
 static void
 init_port(void)
 {
+	int i;
+
 	/* Configuration of Ethernet ports. */
 	ports = rte_zmalloc("testpmd: ports",
 			    sizeof(struct rte_port) * RTE_MAX_ETHPORTS,
@@ -3600,7 +3602,8 @@  init_port(void)
 				"rte_zmalloc(%d struct rte_port) failed\n",
 				RTE_MAX_ETHPORTS);
 	}
-
+	for (i = 0; i < RTE_MAX_ETHPORTS; i++)
+		LIST_INIT(&ports[i].flow_tunnel_list);
 	/* Initialize ports NUMA structures */
 	memset(port_numa, NUMA_NO_CONFIG, RTE_MAX_ETHPORTS);
 	memset(rxring_numa, NUMA_NO_CONFIG, RTE_MAX_ETHPORTS);
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 25a12b14f2..9c47533c68 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -12,6 +12,7 @@ 
 #include <rte_gro.h>
 #include <rte_gso.h>
 #include <cmdline.h>
+#include <sys/queue.h>
 
 #define RTE_PORT_ALL            (~(portid_t)0x0)
 
@@ -148,6 +149,26 @@  struct port_flow {
 	uint8_t data[]; /**< Storage for flow rule description */
 };
 
+struct port_flow_tunnel {
+	LIST_ENTRY(port_flow_tunnel) chain;
+	struct rte_flow_action *pmd_actions;
+	struct rte_flow_item   *pmd_items;
+	uint32_t id;
+	uint32_t num_pmd_actions;
+	uint32_t num_pmd_items;
+	struct rte_flow_tunnel tunnel;
+	struct rte_flow_action *actions;
+	struct rte_flow_item *items;
+};
+
+struct tunnel_ops {
+	uint32_t id;
+	char type[16];
+	uint32_t enabled:1;
+	uint32_t actions:1;
+	uint32_t items:1;
+};
+
 /**
  * The data structure associated with each port.
  */
@@ -178,6 +199,7 @@  struct rte_port {
 	uint32_t                mc_addr_nb; /**< nb. of addr. in mc_addr_pool */
 	uint8_t                 slave_flag; /**< bonding slave port */
 	struct port_flow        *flow_list; /**< Associated flows. */
+	LIST_HEAD(, port_flow_tunnel) flow_tunnel_list;
 	const struct rte_eth_rxtx_callback *rx_dump_cb[RTE_MAX_QUEUES_PER_PORT+1];
 	const struct rte_eth_rxtx_callback *tx_dump_cb[RTE_MAX_QUEUES_PER_PORT+1];
 	/**< metadata value to insert in Tx packets. */
@@ -729,7 +751,8 @@  int port_flow_validate(portid_t port_id,
 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 rte_flow_action *actions,
+		     const struct tunnel_ops *tunnel_ops);
 void update_age_action_context(const struct rte_flow_action *actions,
 		     struct port_flow *pf);
 int port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule);
@@ -739,6 +762,8 @@  int port_flow_query(portid_t port_id, uint32_t rule,
 		    const struct rte_flow_action *action);
 void port_flow_list(portid_t port_id, uint32_t n, const uint32_t *group);
 void port_flow_aged(portid_t port_id, uint8_t destroy);
+void port_flow_release_tunnel(struct port_flow_tunnel *flow_tunnel);
+void port_flow_add_tunnel(portid_t port_id, const struct tunnel_ops *ops);
 int port_flow_isolate(portid_t port_id, int set);
 
 void rx_ring_desc_display(portid_t port_id, queueid_t rxq_id, uint16_t rxd_id);
diff --git a/app/test-pmd/util.c b/app/test-pmd/util.c
index 8488fa1a8f..6757acde9a 100644
--- a/app/test-pmd/util.c
+++ b/app/test-pmd/util.c
@@ -22,6 +22,12 @@  print_ether_addr(const char *what, const struct rte_ether_addr *eth_addr)
 	printf("%s%s", what, buf);
 }
 
+static bool tunnel_missed_packet(struct rte_mbuf  *mb)
+{
+	uint64_t mask = PKT_RX_FDIR | PKT_RX_FDIR_ID;
+	return (mb->ol_flags & mask) == mask;
+}
+
 static inline void
 dump_pkt_burst(uint16_t port_id, uint16_t queue, struct rte_mbuf *pkts[],
 	      uint16_t nb_pkts, int is_rx)
@@ -51,15 +57,37 @@  dump_pkt_burst(uint16_t port_id, uint16_t queue, struct rte_mbuf *pkts[],
 		mb = pkts[i];
 		eth_hdr = rte_pktmbuf_read(mb, 0, sizeof(_eth_hdr), &_eth_hdr);
 		eth_type = RTE_BE_TO_CPU_16(eth_hdr->ether_type);
-		ol_flags = mb->ol_flags;
 		packet_type = mb->packet_type;
 		is_encapsulation = RTE_ETH_IS_TUNNEL_PKT(packet_type);
 
+		if (tunnel_missed_packet(mb)) {
+			int ret;
+			struct rte_flow_error error;
+			struct rte_flow_restore_info info = { 0, };
+
+			ret = rte_flow_tunnel_get_restore_info(port_id, mb,
+							       &info, &error);
+			if (!ret) {
+				printf("tunnel restore info:");
+				if (info.flags & RTE_FLOW_RESTORE_INFO_TUNNEL)
+					if (info.tunnel.type ==
+					    RTE_FLOW_ITEM_TYPE_VXLAN)
+						printf(" - vxlan tunnel");
+				if (info.flags &
+				    RTE_FLOW_RESTORE_INFO_ENCAPSULATED)
+					printf(" - outer header present");
+				if (info.flags & RTE_FLOW_RESTORE_INFO_GROUP_ID)
+					printf(" - miss group %u",
+						info.group_id);
+			}
+			printf("\n");
+		}
 		print_ether_addr("  src=", &eth_hdr->s_addr);
 		print_ether_addr(" - dst=", &eth_hdr->d_addr);
 		printf(" - type=0x%04x - length=%u - nb_segs=%d",
 		       eth_type, (unsigned int) mb->pkt_len,
 		       (int)mb->nb_segs);
+		ol_flags = mb->ol_flags;
 		if (ol_flags & PKT_RX_RSS_HASH) {
 			printf(" - RSS hash=0x%x", (unsigned int) mb->hash.rss);
 			printf(" - RSS queue=0x%x", (unsigned int) queue);