* [PATCH net v10 0/2] virtio-net: fix for VIRTIO_NET_F_GUEST_HDRLEN
@ 2026-03-13 7:59 Xuan Zhuo
2026-03-13 7:59 ` [PATCH net v10 1/2] virtio-net: correct hdr_len handling " Xuan Zhuo
2026-03-13 7:59 ` [PATCH net v10 2/2] virtio-net: correct hdr_len handling for tunnel gso Xuan Zhuo
0 siblings, 2 replies; 10+ messages in thread
From: Xuan Zhuo @ 2026-03-13 7:59 UTC (permalink / raw)
To: netdev
Cc: Willem de Bruijn, Jason Wang, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Michael S. Tsirkin,
Xuan Zhuo, Eugenio Pérez, Jiri Pirko, Alvaro Karsz,
virtualization
v10:
1. fix http://lore.kernel.org/all/202603122214.8Anoxrmq-lkp@intel.com
v9:
1. Introduce new helpers to set hdr len, and virtio_net_hdr_tnl_from_skb
calls them directly.
v8:
1. move changes of virtio_net_hdr_from_skb for udp tunnel to #2
2. remove change for num_sg
v7:
1. fix bug reported by robot
2. Still splitting into two commits, as the issues fixed originate from
different commits. This separation makes it easier to selectively revert to
previous versions.
v6:
1. rename to guest_hdrlen
2. introduce a function virtio_net_set_hdrlen to set the hdrlen
The commit be50da3e9d4a ("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
Xuan Zhuo (2):
virtio-net: correct hdr_len handling for VIRTIO_NET_F_GUEST_HDRLEN
virtio-net: correct hdr_len handling for tunnel gso
drivers/net/tun_vnet.h | 2 +-
drivers/net/virtio_net.c | 6 ++++-
include/linux/virtio_net.h | 51 +++++++++++++++++++++++++++++++++++---
3 files changed, 53 insertions(+), 6 deletions(-)
--
2.32.0.3.g01195cf9f
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH net v10 1/2] virtio-net: correct hdr_len handling for VIRTIO_NET_F_GUEST_HDRLEN 2026-03-13 7:59 [PATCH net v10 0/2] virtio-net: fix for VIRTIO_NET_F_GUEST_HDRLEN Xuan Zhuo @ 2026-03-13 7:59 ` Xuan Zhuo 2026-03-17 11:29 ` Paolo Abeni 2026-03-18 4:07 ` Jason Wang 2026-03-13 7:59 ` [PATCH net v10 2/2] virtio-net: correct hdr_len handling for tunnel gso Xuan Zhuo 1 sibling, 2 replies; 10+ messages in thread From: Xuan Zhuo @ 2026-03-13 7:59 UTC (permalink / raw) To: netdev Cc: Willem de Bruijn, Jason Wang, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Michael S. Tsirkin, Xuan Zhuo, Eugenio Pérez, Jiri Pirko, Alvaro Karsz, virtualization The commit be50da3e9d4a ("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: be50da3e9d4a ("net: virtio_net: implement exact header length guest feature") Signed-off-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com> --- drivers/net/tun_vnet.h | 2 +- drivers/net/virtio_net.c | 6 +++++- include/linux/virtio_net.h | 33 +++++++++++++++++++++++++++++---- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/drivers/net/tun_vnet.h b/drivers/net/tun_vnet.h index a5f93b6c4482..fa5cab9d3e55 100644 --- a/drivers/net/tun_vnet.h +++ b/drivers/net/tun_vnet.h @@ -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); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 72d6a9c6a5a2..7106333ef904 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -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) diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 75dabb763c65..48de4a16a96a 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -207,6 +207,22 @@ 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); } +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 +401,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 +411,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) -- 2.32.0.3.g01195cf9f ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH net v10 1/2] virtio-net: correct hdr_len handling for VIRTIO_NET_F_GUEST_HDRLEN 2026-03-13 7:59 ` [PATCH net v10 1/2] virtio-net: correct hdr_len handling " Xuan Zhuo @ 2026-03-17 11:29 ` Paolo Abeni 2026-03-20 1:47 ` Xuan Zhuo 2026-03-18 4:07 ` Jason Wang 1 sibling, 1 reply; 10+ messages in thread From: Paolo Abeni @ 2026-03-17 11:29 UTC (permalink / raw) To: Xuan Zhuo, netdev Cc: Willem de Bruijn, Jason Wang, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Michael S. Tsirkin, Eugenio Pérez, Jiri Pirko, Alvaro Karsz, virtualization On 3/13/26 8:59 AM, Xuan Zhuo wrote: > The commit be50da3e9d4a ("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: be50da3e9d4a ("net: virtio_net: implement exact header length guest feature") > Signed-off-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com> > --- > drivers/net/tun_vnet.h | 2 +- > drivers/net/virtio_net.c | 6 +++++- > include/linux/virtio_net.h | 33 +++++++++++++++++++++++++++++---- > 3 files changed, 35 insertions(+), 6 deletions(-) > > diff --git a/drivers/net/tun_vnet.h b/drivers/net/tun_vnet.h > index a5f93b6c4482..fa5cab9d3e55 100644 > --- a/drivers/net/tun_vnet.h > +++ b/drivers/net/tun_vnet.h > @@ -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); > > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c > index 72d6a9c6a5a2..7106333ef904 100644 > --- a/drivers/net/virtio_net.c > +++ b/drivers/net/virtio_net.c > @@ -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) > diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h > index 75dabb763c65..48de4a16a96a 100644 > --- a/include/linux/virtio_net.h > +++ b/include/linux/virtio_net.h > @@ -207,6 +207,22 @@ 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); > } > > +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); > +} The series LGTM, let's wait for Jason and or Michael. If another revision is needed, please consider factoring out a __virtio_net_set_hdrlen() helper taking an additional explicit transport_offset argument, to deduplicate a bit the code between patch 1/2 and 2.2. Not a blocked anyway for me. /P ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net v10 1/2] virtio-net: correct hdr_len handling for VIRTIO_NET_F_GUEST_HDRLEN 2026-03-17 11:29 ` Paolo Abeni @ 2026-03-20 1:47 ` Xuan Zhuo 0 siblings, 0 replies; 10+ messages in thread From: Xuan Zhuo @ 2026-03-20 1:47 UTC (permalink / raw) To: Paolo Abeni Cc: Willem de Bruijn, Jason Wang, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Michael S. Tsirkin, Eugenio Pérez, Jiri Pirko, Alvaro Karsz, virtualization, netdev On Tue, 17 Mar 2026 12:29:17 +0100, Paolo Abeni <pabeni@redhat.com> wrote: > On 3/13/26 8:59 AM, Xuan Zhuo wrote: > > The commit be50da3e9d4a ("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: be50da3e9d4a ("net: virtio_net: implement exact header length guest feature") > > Signed-off-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com> > > --- > > drivers/net/tun_vnet.h | 2 +- > > drivers/net/virtio_net.c | 6 +++++- > > include/linux/virtio_net.h | 33 +++++++++++++++++++++++++++++---- > > 3 files changed, 35 insertions(+), 6 deletions(-) > > > > diff --git a/drivers/net/tun_vnet.h b/drivers/net/tun_vnet.h > > index a5f93b6c4482..fa5cab9d3e55 100644 > > --- a/drivers/net/tun_vnet.h > > +++ b/drivers/net/tun_vnet.h > > @@ -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); > > > > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c > > index 72d6a9c6a5a2..7106333ef904 100644 > > --- a/drivers/net/virtio_net.c > > +++ b/drivers/net/virtio_net.c > > @@ -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) > > diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h > > index 75dabb763c65..48de4a16a96a 100644 > > --- a/include/linux/virtio_net.h > > +++ b/include/linux/virtio_net.h > > @@ -207,6 +207,22 @@ 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); > > } > > > > +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); > > +} > > The series LGTM, let's wait for Jason and or Michael. > > If another revision is needed, please consider factoring out a > __virtio_net_set_hdrlen() helper taking an additional explicit > transport_offset argument, to deduplicate a bit the code between patch > 1/2 and 2.2. Not a blocked anyway for me. Hi, currently, a new version seems necessary. However, I do not plan to implement it this way. This is because we require two parameters: one for transport and another for TCP offset (tcp_hdrlen or inner_tcp_hdrlen). I believe it would be inappropriate to put the TCP offset into the parameters as well, since we must first determine whether the protocol is TCP before calling tcp_hdrlen or inner_tcp_hdrlen. Therefore, I think having two separate functions is clearer. Thanks. > > /P > ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net v10 1/2] virtio-net: correct hdr_len handling for VIRTIO_NET_F_GUEST_HDRLEN 2026-03-13 7:59 ` [PATCH net v10 1/2] virtio-net: correct hdr_len handling " Xuan Zhuo 2026-03-17 11:29 ` Paolo Abeni @ 2026-03-18 4:07 ` Jason Wang 2026-03-18 4:11 ` Jason Wang 1 sibling, 1 reply; 10+ messages in thread From: Jason Wang @ 2026-03-18 4:07 UTC (permalink / raw) To: Xuan Zhuo Cc: netdev, Willem de Bruijn, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Michael S. Tsirkin, Eugenio Pérez, Jiri Pirko, Alvaro Karsz, virtualization On Fri, Mar 13, 2026 at 3:59 PM Xuan Zhuo <xuanzhuo@linux.alibaba.com> wrote: > > The commit be50da3e9d4a ("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: be50da3e9d4a ("net: virtio_net: implement exact header length guest feature") > Signed-off-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com> > --- > drivers/net/tun_vnet.h | 2 +- > drivers/net/virtio_net.c | 6 +++++- > include/linux/virtio_net.h | 33 +++++++++++++++++++++++++++++---- > 3 files changed, 35 insertions(+), 6 deletions(-) > > diff --git a/drivers/net/tun_vnet.h b/drivers/net/tun_vnet.h > index a5f93b6c4482..fa5cab9d3e55 100644 > --- a/drivers/net/tun_vnet.h > +++ b/drivers/net/tun_vnet.h > @@ -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); > > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c > index 72d6a9c6a5a2..7106333ef904 100644 > --- a/drivers/net/virtio_net.c > +++ b/drivers/net/virtio_net.c > @@ -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) > diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h > index 75dabb763c65..48de4a16a96a 100644 > --- a/include/linux/virtio_net.h > +++ b/include/linux/virtio_net.h > @@ -207,6 +207,22 @@ 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); > } > > +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); Ok, I think this depends on the logic inside virtio_net_hdr_from_skb() if (sinfo->gso_type & SKB_GSO_TCPV4) hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; else if (sinfo->gso_type & SKB_GSO_TCPV6) hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; else if (sinfo->gso_type & SKB_GSO_UDP_L4) hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP_L4; else return -EINVAL; To be more robust, I'd suggest moving it there. But I have another question, the logic above depends on the headlen is correctly set: /* This is a hint as to how much should be linear. */ hdr->hdr_len = __cpu_to_virtio16(little_endian, skb_headlen(skb)); Is the headlen guaranteed to be correct in all cases (e.g for nested setups or dodgy packets?) Thanks > + > + 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 +401,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 +411,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) > -- > 2.32.0.3.g01195cf9f > ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net v10 1/2] virtio-net: correct hdr_len handling for VIRTIO_NET_F_GUEST_HDRLEN 2026-03-18 4:07 ` Jason Wang @ 2026-03-18 4:11 ` Jason Wang 2026-03-18 5:52 ` Xuan Zhuo 2026-03-19 8:15 ` Paolo Abeni 0 siblings, 2 replies; 10+ messages in thread From: Jason Wang @ 2026-03-18 4:11 UTC (permalink / raw) To: Xuan Zhuo Cc: netdev, Willem de Bruijn, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Michael S. Tsirkin, Eugenio Pérez, Jiri Pirko, Alvaro Karsz, virtualization On Wed, Mar 18, 2026 at 12:07 PM Jason Wang <jasowang@redhat.com> wrote: > > On Fri, Mar 13, 2026 at 3:59 PM Xuan Zhuo <xuanzhuo@linux.alibaba.com> wrote: > > > > The commit be50da3e9d4a ("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: be50da3e9d4a ("net: virtio_net: implement exact header length guest feature") > > Signed-off-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com> > > --- > > drivers/net/tun_vnet.h | 2 +- > > drivers/net/virtio_net.c | 6 +++++- > > include/linux/virtio_net.h | 33 +++++++++++++++++++++++++++++---- > > 3 files changed, 35 insertions(+), 6 deletions(-) > > > > diff --git a/drivers/net/tun_vnet.h b/drivers/net/tun_vnet.h > > index a5f93b6c4482..fa5cab9d3e55 100644 > > --- a/drivers/net/tun_vnet.h > > +++ b/drivers/net/tun_vnet.h > > @@ -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); > > > > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c > > index 72d6a9c6a5a2..7106333ef904 100644 > > --- a/drivers/net/virtio_net.c > > +++ b/drivers/net/virtio_net.c > > @@ -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) > > diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h > > index 75dabb763c65..48de4a16a96a 100644 > > --- a/include/linux/virtio_net.h > > +++ b/include/linux/virtio_net.h > > @@ -207,6 +207,22 @@ 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); > > } > > > > +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); > > Ok, I think this depends on the logic inside virtio_net_hdr_from_skb() > > if (sinfo->gso_type & SKB_GSO_TCPV4) > hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; > else if (sinfo->gso_type & SKB_GSO_TCPV6) > hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; > else if (sinfo->gso_type & SKB_GSO_UDP_L4) > hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP_L4; > else > return -EINVAL; > > To be more robust, I'd suggest moving it there. > > But I have another question, the logic above depends on the headlen is > correctly set: > > /* This is a hint as to how much should be linear. */ > hdr->hdr_len = __cpu_to_virtio16(little_endian, > skb_headlen(skb)); > > Is the headlen guaranteed to be correct in all cases (e.g for nested > setups or dodgy packets?) Speak too fast, I miss hdr_len = skb_transport_offset(skb); This is probably another call to 1) Move virtio_net_set_hdrlen() inside virtio_net_hdr_from_skb() or 2) call virtio_net_set_hdrlen() inside virtio_net_hdr_from_skb(). Thanks > > Thanks > > > + > > + 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 +401,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 +411,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) > > -- > > 2.32.0.3.g01195cf9f > > ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net v10 1/2] virtio-net: correct hdr_len handling for VIRTIO_NET_F_GUEST_HDRLEN 2026-03-18 4:11 ` Jason Wang @ 2026-03-18 5:52 ` Xuan Zhuo 2026-03-19 8:15 ` Paolo Abeni 1 sibling, 0 replies; 10+ messages in thread From: Xuan Zhuo @ 2026-03-18 5:52 UTC (permalink / raw) To: Jason Wang Cc: netdev, Willem de Bruijn, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Michael S. Tsirkin, Eugenio Pérez, Jiri Pirko, Alvaro Karsz, virtualization On Wed, 18 Mar 2026 12:11:15 +0800, Jason Wang <jasowang@redhat.com> wrote: > On Wed, Mar 18, 2026 at 12:07 PM Jason Wang <jasowang@redhat.com> wrote: > > > > On Fri, Mar 13, 2026 at 3:59 PM Xuan Zhuo <xuanzhuo@linux.alibaba.com> wrote: > > > > > > The commit be50da3e9d4a ("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: be50da3e9d4a ("net: virtio_net: implement exact header length guest feature") > > > Signed-off-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com> > > > --- > > > drivers/net/tun_vnet.h | 2 +- > > > drivers/net/virtio_net.c | 6 +++++- > > > include/linux/virtio_net.h | 33 +++++++++++++++++++++++++++++---- > > > 3 files changed, 35 insertions(+), 6 deletions(-) > > > > > > diff --git a/drivers/net/tun_vnet.h b/drivers/net/tun_vnet.h > > > index a5f93b6c4482..fa5cab9d3e55 100644 > > > --- a/drivers/net/tun_vnet.h > > > +++ b/drivers/net/tun_vnet.h > > > @@ -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); > > > > > > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c > > > index 72d6a9c6a5a2..7106333ef904 100644 > > > --- a/drivers/net/virtio_net.c > > > +++ b/drivers/net/virtio_net.c > > > @@ -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) > > > diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h > > > index 75dabb763c65..48de4a16a96a 100644 > > > --- a/include/linux/virtio_net.h > > > +++ b/include/linux/virtio_net.h > > > @@ -207,6 +207,22 @@ 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); > > > } > > > > > > +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); > > > > Ok, I think this depends on the logic inside virtio_net_hdr_from_skb() > > > > if (sinfo->gso_type & SKB_GSO_TCPV4) > > hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; > > else if (sinfo->gso_type & SKB_GSO_TCPV6) > > hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; > > else if (sinfo->gso_type & SKB_GSO_UDP_L4) > > hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP_L4; > > else > > return -EINVAL; > > > > To be more robust, I'd suggest moving it there. > > > > But I have another question, the logic above depends on the headlen is > > correctly set: > > > > /* This is a hint as to how much should be linear. */ > > hdr->hdr_len = __cpu_to_virtio16(little_endian, > > skb_headlen(skb)); > > > > Is the headlen guaranteed to be correct in all cases (e.g for nested > > setups or dodgy packets?) > > Speak too fast, I miss > > hdr_len = skb_transport_offset(skb); > > This is probably another call to > > 1) Move virtio_net_set_hdrlen() inside virtio_net_hdr_from_skb() or > 2) call virtio_net_set_hdrlen() inside virtio_net_hdr_from_skb(). Do you more like v8? If so, please reivew this: http://lore.kernel.org/all/20260305031938.24518-1-xuanzhuo@linux.alibaba.com Thanks > > Thanks > > > > > Thanks > > > > > + > > > + 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 +401,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 +411,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) > > > -- > > > 2.32.0.3.g01195cf9f > > > > ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net v10 1/2] virtio-net: correct hdr_len handling for VIRTIO_NET_F_GUEST_HDRLEN 2026-03-18 4:11 ` Jason Wang 2026-03-18 5:52 ` Xuan Zhuo @ 2026-03-19 8:15 ` Paolo Abeni 2026-03-20 0:38 ` Jason Wang 1 sibling, 1 reply; 10+ messages in thread From: Paolo Abeni @ 2026-03-19 8:15 UTC (permalink / raw) To: Jason Wang, Xuan Zhuo Cc: netdev, Willem de Bruijn, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Michael S. Tsirkin, Eugenio Pérez, Jiri Pirko, Alvaro Karsz, virtualization Hi, On 3/18/26 5:11 AM, Jason Wang wrote: > On Wed, Mar 18, 2026 at 12:07 PM Jason Wang <jasowang@redhat.com> wrote: >> On Fri, Mar 13, 2026 at 3:59 PM Xuan Zhuo <xuanzhuo@linux.alibaba.com> wrote: >>> diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h >>> index 75dabb763c65..48de4a16a96a 100644 >>> --- a/include/linux/virtio_net.h >>> +++ b/include/linux/virtio_net.h >>> @@ -207,6 +207,22 @@ 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); >>> } >>> >>> +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); >> >> Ok, I think this depends on the logic inside virtio_net_hdr_from_skb() >> >> if (sinfo->gso_type & SKB_GSO_TCPV4) >> hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; >> else if (sinfo->gso_type & SKB_GSO_TCPV6) >> hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; >> else if (sinfo->gso_type & SKB_GSO_UDP_L4) >> hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP_L4; >> else >> return -EINVAL; >> >> To be more robust, I'd suggest moving it there. >> >> But I have another question, the logic above depends on the headlen is >> correctly set: >> >> /* This is a hint as to how much should be linear. */ >> hdr->hdr_len = __cpu_to_virtio16(little_endian, >> skb_headlen(skb)); >> >> Is the headlen guaranteed to be correct in all cases (e.g for nested >> setups or dodgy packets?) > > Speak too fast, I miss > > hdr_len = skb_transport_offset(skb); > > This is probably another call to > > 1) Move virtio_net_set_hdrlen() inside virtio_net_hdr_from_skb() or > 2) call virtio_net_set_hdrlen() inside virtio_net_hdr_from_skb(). It looks like that the above leads to more complex code with a bunch of additional conditionals, which possibly the compiler can't optimize out, and IMHO not nice layering violation, see the already mentioned previous iteration: https://lore.kernel.org/all/cf62a942-e1e8-4b37-837a-b0bd76297656@redhat.com/ What about instead simply rename virtio_net_set_hdrlen() to __virtio_net_set_hdrlen(), virtio_net_set_tnl_hdrlen() to __virtio_net_set_tnl_hdrlen(), and add explicitly mention in a comment that such functions must be invoked only after virtio_net_hdr_from_skb() validation? /P ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net v10 1/2] virtio-net: correct hdr_len handling for VIRTIO_NET_F_GUEST_HDRLEN 2026-03-19 8:15 ` Paolo Abeni @ 2026-03-20 0:38 ` Jason Wang 0 siblings, 0 replies; 10+ messages in thread From: Jason Wang @ 2026-03-20 0:38 UTC (permalink / raw) To: Paolo Abeni Cc: Xuan Zhuo, netdev, Willem de Bruijn, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Michael S. Tsirkin, Eugenio Pérez, Jiri Pirko, Alvaro Karsz, virtualization On Thu, Mar 19, 2026 at 4:15 PM Paolo Abeni <pabeni@redhat.com> wrote: > > Hi, > > On 3/18/26 5:11 AM, Jason Wang wrote: > > On Wed, Mar 18, 2026 at 12:07 PM Jason Wang <jasowang@redhat.com> wrote: > >> On Fri, Mar 13, 2026 at 3:59 PM Xuan Zhuo <xuanzhuo@linux.alibaba.com> wrote: > >>> diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h > >>> index 75dabb763c65..48de4a16a96a 100644 > >>> --- a/include/linux/virtio_net.h > >>> +++ b/include/linux/virtio_net.h > >>> @@ -207,6 +207,22 @@ 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); > >>> } > >>> > >>> +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); > >> > >> Ok, I think this depends on the logic inside virtio_net_hdr_from_skb() > >> > >> if (sinfo->gso_type & SKB_GSO_TCPV4) > >> hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; > >> else if (sinfo->gso_type & SKB_GSO_TCPV6) > >> hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; > >> else if (sinfo->gso_type & SKB_GSO_UDP_L4) > >> hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP_L4; > >> else > >> return -EINVAL; > >> > >> To be more robust, I'd suggest moving it there. > >> > >> But I have another question, the logic above depends on the headlen is > >> correctly set: > >> > >> /* This is a hint as to how much should be linear. */ > >> hdr->hdr_len = __cpu_to_virtio16(little_endian, > >> skb_headlen(skb)); > >> > >> Is the headlen guaranteed to be correct in all cases (e.g for nested > >> setups or dodgy packets?) > > > > Speak too fast, I miss > > > > hdr_len = skb_transport_offset(skb); > > > > This is probably another call to > > > > 1) Move virtio_net_set_hdrlen() inside virtio_net_hdr_from_skb() or > > 2) call virtio_net_set_hdrlen() inside virtio_net_hdr_from_skb(). > > It looks like that the above leads to more complex code with a bunch of > additional conditionals, which possibly the compiler can't optimize out, > and IMHO not nice layering violation, see the already mentioned previous > iteration: > > https://lore.kernel.org/all/cf62a942-e1e8-4b37-837a-b0bd76297656@redhat.com/ > > What about instead simply rename virtio_net_set_hdrlen() to > __virtio_net_set_hdrlen(), virtio_net_set_tnl_hdrlen() to > __virtio_net_set_tnl_hdrlen(), and add explicitly mention in a comment > that such functions must be invoked only after virtio_net_hdr_from_skb() > validation? Works for me. Thanks > > /P > ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH net v10 2/2] virtio-net: correct hdr_len handling for tunnel gso 2026-03-13 7:59 [PATCH net v10 0/2] virtio-net: fix for VIRTIO_NET_F_GUEST_HDRLEN Xuan Zhuo 2026-03-13 7:59 ` [PATCH net v10 1/2] virtio-net: correct hdr_len handling " Xuan Zhuo @ 2026-03-13 7:59 ` Xuan Zhuo 1 sibling, 0 replies; 10+ messages in thread From: Xuan Zhuo @ 2026-03-13 7:59 UTC (permalink / raw) To: netdev Cc: Willem de Bruijn, Jason Wang, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Michael S. Tsirkin, Xuan Zhuo, Eugenio Pérez, Jiri Pirko, Alvaro Karsz, virtualization The commit a2fb4bc4e2a6a03 ("net: implement virtio helpers to handle UDP GSO tunneling.") introduces support for the UDP GSO tunnel feature in virtio-net. The virtio spec says: If the \field{gso_type} has the VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 bit or VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6 bit set, \field{hdr_len} accounts for all the headers up to and including the inner transport. The commit did not update the hdr_len to include the inner transport. I observed that the "hdr_len" is 116 for this packet: 17:36:18.241105 52:55:00:d1:27:0a > 2e:2c:df:46:a9:e1, ethertype IPv4 (0x0800), length 2912: (tos 0x0, ttl 64, id 45197, offset 0, flags [none], proto UDP (17), length 2898) 192.168.122.100.50613 > 192.168.122.1.4789: [bad udp cksum 0x8106 -> 0x26a0!] VXLAN, flags [I] (0x08), vni 1 fa:c3:ba:82:05:ee > ce:85:0c:31:77:e5, ethertype IPv4 (0x0800), length 2862: (tos 0x0, ttl 64, id 14678, offset 0, flags [DF], proto TCP (6), length 2848) 192.168.3.1.49880 > 192.168.3.2.9898: Flags [P.], cksum 0x9266 (incorrect -> 0xaa20), seq 515667:518463, ack 1, win 64, options [nop,nop,TS val 2990048824 ecr 2798801412], length 2796 116 = 14(mac) + 20(ip) + 8(udp) + 8(vxlan) + 14(inner mac) + 20(inner ip) + 32(innner tcp) Fixes: a2fb4bc4e2a6a03 ("net: implement virtio helpers to handle UDP GSO tunneling.") Signed-off-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com> --- include/linux/virtio_net.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 48de4a16a96a..76aa017a65c2 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -223,6 +223,21 @@ static inline void virtio_net_set_hdrlen(const struct sk_buff *skb, hdr->hdr_len = __cpu_to_virtio16(little_endian, hdr_len); } +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, @@ -439,6 +454,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 -- 2.32.0.3.g01195cf9f ^ permalink raw reply related [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-03-20 1:51 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-03-13 7:59 [PATCH net v10 0/2] virtio-net: fix for VIRTIO_NET_F_GUEST_HDRLEN Xuan Zhuo 2026-03-13 7:59 ` [PATCH net v10 1/2] virtio-net: correct hdr_len handling " Xuan Zhuo 2026-03-17 11:29 ` Paolo Abeni 2026-03-20 1:47 ` Xuan Zhuo 2026-03-18 4:07 ` Jason Wang 2026-03-18 4:11 ` Jason Wang 2026-03-18 5:52 ` Xuan Zhuo 2026-03-19 8:15 ` Paolo Abeni 2026-03-20 0:38 ` Jason Wang 2026-03-13 7:59 ` [PATCH net v10 2/2] virtio-net: correct hdr_len handling for tunnel gso Xuan Zhuo
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox