Merge branch 'virtio-net-fix-for-virtio_net_f_guest_hdrlen'

Xuan Zhuo says:

====================
virtio-net: fix 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 path set fixes that issue.

As discussed in [0], this version checks the VIRTIO_NET_F_GUEST_HDRLEN is
negotiated.

[0]: http://lore.kernel.org/all/20251029030913.20423-1-xuanzhuo@linux.alibaba.com

v10: fix http://lore.kernel.org/all/202603122214.8Anoxrmq-lkp@intel.com
====================

Link: https://patch.msgid.link/20260320021818.111741-1-xuanzhuo@linux.alibaba.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-03-24 11:12:10 +01:00
commit 673bb63d20
3 changed files with 55 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,39 @@ 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);
}
/* This function must be called after virtio_net_hdr_from_skb(). */
static inline void __virtio_net_set_tnl_hdrlen(const struct sk_buff *skb,
struct virtio_net_hdr *hdr)
{
u16 hdr_len;
hdr_len = skb_inner_transport_offset(skb);
if (hdr->gso_type == VIRTIO_NET_HDR_GSO_UDP_L4)
hdr_len += sizeof(struct udphdr);
else
hdr_len += inner_tcp_hdrlen(skb);
hdr->hdr_len = __cpu_to_virtio16(true, 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 +418,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 +428,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)
@ -414,6 +456,9 @@ virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb,
if (ret)
return ret;
if (feature_hdrlen && hdr->hdr_len)
__virtio_net_set_tnl_hdrlen(skb, hdr);
if (skb->protocol == htons(ETH_P_IPV6))
hdr->gso_type |= VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6;
else