virtio-net: correct hdr_len handling for VIRTIO_NET_F_GUEST_HDRLEN

The commit be50da3e9d ("net: virtio_net: implement exact header length
guest feature") introduces support for the VIRTIO_NET_F_GUEST_HDRLEN
feature in virtio-net.

This feature requires virtio-net to set hdr_len to the actual header
length of the packet when transmitting, the number of
bytes from the start of the packet to the beginning of the
transport-layer payload.

However, in practice, hdr_len was being set using skb_headlen(skb),
which is clearly incorrect. This commit fixes that issue.

Fixes: be50da3e9d ("net: virtio_net: implement exact header length guest feature")
Signed-off-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
Link: https://patch.msgid.link/20260320021818.111741-2-xuanzhuo@linux.alibaba.com
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Xuan Zhuo 2026-03-20 10:18:17 +08:00 committed by Paolo Abeni
parent 70b439bf06
commit 38ec410b99
3 changed files with 36 additions and 6 deletions

View File

@ -244,7 +244,7 @@ tun_vnet_hdr_tnl_from_skb(unsigned int flags,
if (virtio_net_hdr_tnl_from_skb(skb, tnl_hdr, has_tnl_offload,
tun_vnet_is_little_endian(flags),
vlan_hlen, true)) {
vlan_hlen, true, false)) {
struct virtio_net_hdr_v1 *hdr = &tnl_hdr->hash_hdr.hdr;
struct skb_shared_info *sinfo = skb_shinfo(skb);

View File

@ -3267,8 +3267,12 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb, bool orphan)
struct virtio_net_hdr_v1_hash_tunnel *hdr;
int num_sg;
unsigned hdr_len = vi->hdr_len;
bool feature_hdrlen;
bool can_push;
feature_hdrlen = virtio_has_feature(vi->vdev,
VIRTIO_NET_F_GUEST_HDRLEN);
pr_debug("%s: xmit %p %pM\n", vi->dev->name, skb, dest);
/* Make sure it's safe to cast between formats */
@ -3288,7 +3292,7 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb, bool orphan)
if (virtio_net_hdr_tnl_from_skb(skb, hdr, vi->tx_tnl,
virtio_is_little_endian(vi->vdev), 0,
false))
false, feature_hdrlen))
return -EPROTO;
if (vi->mergeable_rx_bufs)

View File

@ -207,6 +207,23 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
return __virtio_net_hdr_to_skb(skb, hdr, little_endian, hdr->gso_type);
}
/* This function must be called after virtio_net_hdr_from_skb(). */
static inline void __virtio_net_set_hdrlen(const struct sk_buff *skb,
struct virtio_net_hdr *hdr,
bool little_endian)
{
u16 hdr_len;
hdr_len = skb_transport_offset(skb);
if (hdr->gso_type == VIRTIO_NET_HDR_GSO_UDP_L4)
hdr_len += sizeof(struct udphdr);
else
hdr_len += tcp_hdrlen(skb);
hdr->hdr_len = __cpu_to_virtio16(little_endian, hdr_len);
}
static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,
struct virtio_net_hdr *hdr,
bool little_endian,
@ -385,7 +402,8 @@ virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb,
bool tnl_hdr_negotiated,
bool little_endian,
int vlan_hlen,
bool has_data_valid)
bool has_data_valid,
bool feature_hdrlen)
{
struct virtio_net_hdr *hdr = (struct virtio_net_hdr *)vhdr;
unsigned int inner_nh, outer_th;
@ -394,9 +412,17 @@ virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb,
tnl_gso_type = skb_shinfo(skb)->gso_type & (SKB_GSO_UDP_TUNNEL |
SKB_GSO_UDP_TUNNEL_CSUM);
if (!tnl_gso_type)
return virtio_net_hdr_from_skb(skb, hdr, little_endian,
has_data_valid, vlan_hlen);
if (!tnl_gso_type) {
ret = virtio_net_hdr_from_skb(skb, hdr, little_endian,
has_data_valid, vlan_hlen);
if (ret)
return ret;
if (feature_hdrlen && hdr->hdr_len)
__virtio_net_set_hdrlen(skb, hdr, little_endian);
return ret;
}
/* Tunnel support not negotiated but skb ask for it. */
if (!tnl_hdr_negotiated)