diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 1e8a37c..5dcb862 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -3724,7 +3724,6 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
     struct dp_netdev *dp = pmd->dp;
     int type = nl_attr_type(a);
     struct dp_netdev_port *p;
-    int i;
 
     switch ((enum ovs_action_attr)type) {
     case OVS_ACTION_ATTR_OUTPUT:
@@ -3775,8 +3774,12 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
                    packets = tnl_pkt;
                 }
 
-                err = netdev_pop_header(p->netdev, packets, cnt);
+                err = netdev_pop_header(p->netdev, packets, &cnt);
+                if (!cnt) {
+                    return;
+                }
                 if (!err) {
+                    int i;
 
                     for (i = 0; i < cnt; i++) {
                         packets[i]->md.in_port.odp_port = portno;
@@ -3799,6 +3802,7 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
             struct ofpbuf actions;
             struct flow flow;
             ovs_u128 ufid;
+            int i;
 
             userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
             ofpbuf_init(&actions, 0);
@@ -3830,6 +3834,7 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
     case OVS_ACTION_ATTR_RECIRC:
         if (*depth < MAX_RECIRC_DEPTH) {
             struct dp_packet *recirc_pkts[NETDEV_MAX_BURST];
+            int i;
 
             if (!may_steal) {
                dp_netdev_clone_pkt_batch(recirc_pkts, packets, cnt);
diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
index b52b068..d28cfbf 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -353,7 +353,7 @@ parse_gre_header(struct dp_packet *packet,
     return hlen;
 }
 
-int
+struct dp_packet *
 netdev_gre_pop_header(struct dp_packet *packet)
 {
     struct pkt_metadata *md = &packet->md;
@@ -365,17 +365,20 @@ netdev_gre_pop_header(struct dp_packet *packet)
 
     pkt_metadata_init_tnl(md);
     if (hlen > dp_packet_size(packet)) {
-        return EINVAL;
+        goto err;
     }
 
     hlen = parse_gre_header(packet, tnl);
     if (hlen < 0) {
-        return -hlen;
+        goto err;
     }
 
     dp_packet_reset_packet(packet, hlen);
 
-    return 0;
+    return packet;
+err:
+    dp_packet_delete(packet);
+    return NULL;
 }
 
 void
@@ -450,7 +453,7 @@ netdev_gre_build_header(const struct netdev *netdev,
     return 0;
 }
 
-int
+struct dp_packet *
 netdev_vxlan_pop_header(struct dp_packet *packet)
 {
     struct pkt_metadata *md = &packet->md;
@@ -460,12 +463,12 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
 
     pkt_metadata_init_tnl(md);
     if (VXLAN_HLEN > dp_packet_l4_size(packet)) {
-        return EINVAL;
+        goto err;
     }
 
     vxh = udp_extract_tnl_md(packet, tnl, &hlen);
     if (!vxh) {
-        return EINVAL;
+        goto err;
     }
 
     if (get_16aligned_be32(&vxh->vx_flags) != htonl(VXLAN_FLAGS) ||
@@ -473,14 +476,17 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
         VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n",
                      ntohl(get_16aligned_be32(&vxh->vx_flags)),
                      ntohl(get_16aligned_be32(&vxh->vx_vni)));
-        return EINVAL;
+        goto err;
     }
     tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
     tnl->flags |= FLOW_TNL_F_KEY;
 
     dp_packet_reset_packet(packet, hlen + VXLAN_HLEN);
 
-    return 0;
+    return packet;
+err:
+    dp_packet_delete(packet);
+    return NULL;
 }
 
 int
@@ -508,7 +514,7 @@ netdev_vxlan_build_header(const struct netdev *netdev,
     return 0;
 }
 
-int
+struct dp_packet *
 netdev_geneve_pop_header(struct dp_packet *packet)
 {
     struct pkt_metadata *md = &packet->md;
@@ -520,12 +526,12 @@ netdev_geneve_pop_header(struct dp_packet *packet)
     if (GENEVE_BASE_HLEN > dp_packet_l4_size(packet)) {
         VLOG_WARN_RL(&err_rl, "geneve packet too small: min header=%u packet size=%"PRIuSIZE"\n",
                      (unsigned int)GENEVE_BASE_HLEN, dp_packet_l4_size(packet));
-        return EINVAL;
+        goto err;
     }
 
     gnh = udp_extract_tnl_md(packet, tnl, &ulen);
     if (!gnh) {
-        return EINVAL;
+        goto err;
     }
 
     opts_len = gnh->opt_len * 4;
@@ -533,18 +539,18 @@ netdev_geneve_pop_header(struct dp_packet *packet)
     if (hlen > dp_packet_size(packet)) {
         VLOG_WARN_RL(&err_rl, "geneve packet too small: header len=%u packet size=%u\n",
                      hlen, dp_packet_size(packet));
-        return EINVAL;
+        goto err;
     }
 
     if (gnh->ver != 0) {
         VLOG_WARN_RL(&err_rl, "unknown geneve version: %"PRIu8"\n", gnh->ver);
-        return EINVAL;
+        goto err;
     }
 
     if (gnh->proto_type != htons(ETH_TYPE_TEB)) {
         VLOG_WARN_RL(&err_rl, "unknown geneve encapsulated protocol: %#x\n",
                      ntohs(gnh->proto_type));
-        return EINVAL;
+        goto err;
     }
 
     tnl->flags |= gnh->oam ? FLOW_TNL_F_OAM : 0;
@@ -557,7 +563,10 @@ netdev_geneve_pop_header(struct dp_packet *packet)
 
     dp_packet_reset_packet(packet, hlen);
 
-    return 0;
+    return packet;
+err:
+    dp_packet_delete(packet);
+    return NULL;
 }
 
 int
diff --git a/lib/netdev-native-tnl.h b/lib/netdev-native-tnl.h
index 5173b10..dbe6bd0 100644
--- a/lib/netdev-native-tnl.h
+++ b/lib/netdev-native-tnl.h
@@ -29,7 +29,7 @@ netdev_gre_build_header(const struct netdev *netdev,
 void
 netdev_gre_push_header(struct dp_packet *packet,
                        const struct ovs_action_push_tnl *data);
-int
+struct dp_packet *
 netdev_gre_pop_header(struct dp_packet *packet);
 
 void
@@ -39,14 +39,14 @@ int
 netdev_geneve_build_header(const struct netdev *netdev,
                            struct ovs_action_push_tnl *data,
                            const struct flow *tnl_flow);
-int
+struct dp_packet *
 netdev_geneve_pop_header(struct dp_packet *packet);
 
 int
 netdev_vxlan_build_header(const struct netdev *netdev,
                           struct ovs_action_push_tnl *data,
                           const struct flow *tnl_flow);
-int
+struct dp_packet *
 netdev_vxlan_pop_header(struct dp_packet *packet);
 
 void
diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
index c2ddbf1..6af0708 100644
--- a/lib/netdev-provider.h
+++ b/lib/netdev-provider.h
@@ -275,8 +275,10 @@ struct netdev_class {
                         const struct ovs_action_push_tnl *data);
 
     /* Pop tunnel header from packet, build tunnel metadata and resize packet
-     * for further processing. */
-    int (*pop_header)(struct dp_packet *packet);
+     * for further processing.
+     * Returns NULL in case of error or tunnel implementation queued packet for further
+     * processing. */
+    struct dp_packet * (*pop_header)(struct dp_packet *packet);
 
     /* Returns the id of the numa node the 'netdev' is on.  If there is no
      * such info, returns NETDEV_NUMA_UNSPEC. */
diff --git a/lib/netdev.c b/lib/netdev.c
index 3e50694..cac91ea 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -767,23 +767,21 @@ netdev_send(struct netdev *netdev, int qid, struct dp_packet **buffers,
 }
 
 int
-netdev_pop_header(struct netdev *netdev, struct dp_packet **buffers, int cnt)
+netdev_pop_header(struct netdev *netdev, struct dp_packet **buffers, int *pcnt)
 {
-    int i;
+    int i, cnt = *pcnt, n_cnt = 0;
 
     if (!netdev->netdev_class->pop_header) {
         return EOPNOTSUPP;
     }
 
     for (i = 0; i < cnt; i++) {
-        int err;
-
-        err = netdev->netdev_class->pop_header(buffers[i]);
-        if (err) {
-            dp_packet_clear(buffers[i]);
+        buffers[i] = netdev->netdev_class->pop_header(buffers[i]);
+        if (buffers[i]) {
+            buffers[n_cnt++] = buffers[i];
         }
     }
-
+    *pcnt = n_cnt;
     return 0;
 }
 
diff --git a/lib/netdev.h b/lib/netdev.h
index 6dabc92..f31b565 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -159,7 +159,7 @@ int netdev_push_header(const struct netdev *netdev,
                        struct dp_packet **buffers, int cnt,
                        const struct ovs_action_push_tnl *data);
 int netdev_pop_header(struct netdev *netdev, struct dp_packet **buffers,
-                      int cnt);
+                      int *pcnt);
 
 /* Hardware address. */
 int netdev_set_etheraddr(struct netdev *, const struct eth_addr mac);
