With IFF_NAPI_FRAGS, tun_napi_alloc_frags() gets its skb from
napi_get_frags(). If GRO merged the previous frame with
GRO_MERGED_FREE, that skb was recycled by napi_reuse_skb(), which does
not reset skb->protocol set by napi_frags_skb() for the previous frame.
tun_get_user() calls tun_vnet_hdr_tnl_to_skb() before eth_type_trans(),
so the virtio header checks see the protocol of the previous frame:
the outer protocol check of UDP tunnel GSO packets rejects a valid
frame when the two ethertypes differ, and GSO packets without
VIRTIO_NET_HDR_F_NEEDS_CSUM are dissected with the wrong protocol.
This needs GRO to hold packets across writes, and IFF_NAPI_FRAGS needs
CAP_NET_ADMIN.
Clear skb->protocol in tun_napi_alloc_frags(), like skbs allocated
by tun_alloc_skb() or build_skb(). eth_type_trans() sets it later.
Fixes: 90e33d459407 ("tun: enable napi_gro_frags() for TUN/TAP driver")
Reported-by: Sashiko <[email protected]>
Closes:
https://lore.kernel.org/netdev/[email protected]/
Suggested-by: Willem de Bruijn <[email protected]>
Reviewed-by: Willem de Bruijn <[email protected]>
Signed-off-by: Eric Dumazet <[email protected]>
---
drivers/net/tun.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 3920b3dff1e8..9aca5056f7c5 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1500,6 +1500,12 @@ static struct sk_buff *tun_napi_alloc_frags(struct
tun_file *tfile,
if (!skb)
return ERR_PTR(-ENOMEM);
+ /* An skb recycled by napi_reuse_skb() keeps the protocol of a
+ * previous frame. eth_type_trans() sets it after the virtio
+ * header checks, which must not see a stale one.
+ */
+ skb->protocol = 0;
+
linear = iov_iter_single_seg_count(it);
err = __skb_grow(skb, linear);
if (err)
--
2.53.0