Re: [PATCH net v3 8/8] selftests: net: tuntap: write UDP tunnel offsets in little endian

From
Eric Dumazet <edumazet@kernel.org>
Date
2026-11-06 08:52:36
Also in
sashiko-reviews
Reply to
sashiko-bot@kernel.org, 2026-11-06
Le ven. 6 nov. 2026 à 09:16, [off-list ref] a écrit :
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] selftests: net: tun: UDP tunnel GSO tests still fail on
  big-endian hosts
--

commit 19b0a422177cb62b0357c401d17b939feb10dca3
Author: Eric Dumazet [off-list ref]

selftests: net: tuntap: write UDP tunnel offsets in little endian

This commit converts the outer_th_offset and inner_nh_offset fields of
struct virtio_net_hdr_v1_hash_tunnel to little-endian in the tuntap
selftests while leaving the base virtio_net_hdr_v1 fields in host byte
order.
quoted
diff --git a/tools/testing/selftests/net/tuntap_helpers.h b/tools/testing/selftests/net/tuntap_helpers.h
index ecad83fae1582..dc85cd9bc0aec 100644
--- a/tools/testing/selftests/net/tuntap_helpers.h
+++ b/tools/testing/selftests/net/tuntap_helpers.h
[ ... ]
quoted
@@ -375,11 +377,14 @@ static inline int build_virtio_net_hdr_v1_hash_tunnel(uint8_t *buf, bool is_tap,
      inner_iphlen = (inner_family == AF_INET) ? sizeof(struct iphdr) :
                                                 sizeof(struct ipv6hdr);

-     vh_tunnel->outer_th_offset = eth_hlen + outer_iphlen;
-     vh_tunnel->inner_nh_offset = vh_tunnel->outer_th_offset + ETH_HLEN +
-                                  GENEVE_HLEN + sizeof(struct udphdr);
+     outer_th = eth_hlen + outer_iphlen;
+     inner_nh = outer_th + ETH_HLEN + GENEVE_HLEN + sizeof(struct udphdr);

-     vh->csum_start = vh_tunnel->inner_nh_offset + inner_iphlen;
+     /* The tunnel offsets are always little endian. */
+     vh_tunnel->outer_th_offset = htole16(outer_th);
+     vh_tunnel->inner_nh_offset = htole16(inner_nh);
+
+     vh->csum_start = inner_nh + inner_iphlen;
      vh->csum_offset = __builtin_offsetof(struct udphdr, check);
      vh->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
      vh->hdr_len = hdr_len;
[Severity: Medium]
Will the UDP tunnel GSO tests still fail on big-endian hosts because the
kernel hardcodes little_endian = true when delegating the base header fields
in virtio_net_hdr_tnl_to_skb() and virtio_net_hdr_tnl_from_skb()?

Right, the kernel treats the whole tunnel header as little endian, so
the tests need TUNSETVNETLE and little endian accesses for all fields,
not only the tunnel offsets. I will do that in the separate selftests
series.
In tools/testing/selftests/net/tun.c, tun_open() does not set TUNSETVNETLE,
so tun_vnet_is_little_endian() is false and __tun_vnet_hdr_get() reads
hdr_len, csum_start, and csum_offset in host order. However, when
tun_vnet_hdr_tnl_to_skb() calls virtio_net_hdr_tnl_to_skb() with
little_endian = false:

include/linux/virtio_net.h:virtio_net_hdr_tnl_to_skb() {
        ...
        inner_th = __virtio16_to_cpu(little_endian, hdr->csum_start);
        inner_nh = le16_to_cpu(vhdr->inner_nh_offset);
        outer_th = le16_to_cpu(vhdr->outer_th_offset);
        if (outer_th < outer_l3min ||
            inner_nh < outer_th + sizeof(struct udphdr) ||
            inner_th < inner_nh + inner_l3min)
                return -EINVAL;

        /* Let the basic parsing deal with plain GSO features. */
        ret = __virtio_net_hdr_to_skb(skb, hdr, true,
                                      hdr->gso_type & ~gso_tunnel_type);
        if (ret)
                return ret;

        if (!virtio_net_tnl_offsets_ok(skb, has_eth, outer_isv6, outer_th))
                return -EINVAL;
        ...
}

Because __virtio_net_hdr_to_skb() is called with little_endian hardcoded to
true instead of passing little_endian, it byte-swaps the host-order
csum_start, csum_offset, and gso_size on big-endian hosts. That causes
__virtio_net_hdr_to_skb() to return -EINVAL (either in pskb_may_pull() or the
skb->csum_offset check) before virtio_net_tnl_offsets_ok() is reached in
tun_vnet_udptnl_proto, tun_vnet_udptnl_proto_l3, and tun_vnet_udptnl.

Similarly, on the receive path in virtio_net_hdr_tnl_from_skb():

include/linux/virtio_net.h:virtio_net_hdr_tnl_from_skb() {
        ...
        /* Let the basic parsing deal with plain GSO features. */
        skb_shinfo(skb)->gso_type &= ~tnl_gso_type;
        ret = virtio_net_hdr_from_skb(skb, hdr, true, false, vlan_hlen);
        skb_shinfo(skb)->gso_type |= tnl_gso_type;
        ...
}

virtio_net_hdr_from_skb() is also called with little_endian hardcoded to
true, writing hdr->gso_size in little-endian while recv_gso_packet in
tools/testing/selftests/net/tun.c compares vh->gso_size against host-order
variant->gso_size.

--
Sashiko AI review · https://sashiko.dev/#/patchset/20261105115715.1466924-1-edumazet@kernel.org?part=8
Thread (27 messages) 27 messages, 4 authors, last reply 2d ago
Keyboard shortcuts
hback out one level
jnext message in thread / next series-diff position
kprevious message in thread / previous series-diff position
ldrill in
Escclose help / fold thread tree
?toggle this help