Skip to content

Commit 38ec410

Browse files
fengidriPaolo Abeni
authored andcommitted
virtio-net: correct hdr_len handling for VIRTIO_NET_F_GUEST_HDRLEN
The commit be50da3 ("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: be50da3 ("net: virtio_net: implement exact header length guest feature") Signed-off-by: Xuan Zhuo <[email protected]> Link: https://patch.msgid.link/[email protected] Acked-by: Michael S. Tsirkin <[email protected]> Signed-off-by: Paolo Abeni <[email protected]>
1 parent 70b439b commit 38ec410

3 files changed

Lines changed: 36 additions & 6 deletions

File tree

drivers/net/tun_vnet.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ tun_vnet_hdr_tnl_from_skb(unsigned int flags,
244244

245245
if (virtio_net_hdr_tnl_from_skb(skb, tnl_hdr, has_tnl_offload,
246246
tun_vnet_is_little_endian(flags),
247-
vlan_hlen, true)) {
247+
vlan_hlen, true, false)) {
248248
struct virtio_net_hdr_v1 *hdr = &tnl_hdr->hash_hdr.hdr;
249249
struct skb_shared_info *sinfo = skb_shinfo(skb);
250250

drivers/net/virtio_net.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3267,8 +3267,12 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb, bool orphan)
32673267
struct virtio_net_hdr_v1_hash_tunnel *hdr;
32683268
int num_sg;
32693269
unsigned hdr_len = vi->hdr_len;
3270+
bool feature_hdrlen;
32703271
bool can_push;
32713272

3273+
feature_hdrlen = virtio_has_feature(vi->vdev,
3274+
VIRTIO_NET_F_GUEST_HDRLEN);
3275+
32723276
pr_debug("%s: xmit %p %pM\n", vi->dev->name, skb, dest);
32733277

32743278
/* 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)
32883292

32893293
if (virtio_net_hdr_tnl_from_skb(skb, hdr, vi->tx_tnl,
32903294
virtio_is_little_endian(vi->vdev), 0,
3291-
false))
3295+
false, feature_hdrlen))
32923296
return -EPROTO;
32933297

32943298
if (vi->mergeable_rx_bufs)

include/linux/virtio_net.h

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,23 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
207207
return __virtio_net_hdr_to_skb(skb, hdr, little_endian, hdr->gso_type);
208208
}
209209

210+
/* This function must be called after virtio_net_hdr_from_skb(). */
211+
static inline void __virtio_net_set_hdrlen(const struct sk_buff *skb,
212+
struct virtio_net_hdr *hdr,
213+
bool little_endian)
214+
{
215+
u16 hdr_len;
216+
217+
hdr_len = skb_transport_offset(skb);
218+
219+
if (hdr->gso_type == VIRTIO_NET_HDR_GSO_UDP_L4)
220+
hdr_len += sizeof(struct udphdr);
221+
else
222+
hdr_len += tcp_hdrlen(skb);
223+
224+
hdr->hdr_len = __cpu_to_virtio16(little_endian, hdr_len);
225+
}
226+
210227
static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,
211228
struct virtio_net_hdr *hdr,
212229
bool little_endian,
@@ -385,7 +402,8 @@ virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb,
385402
bool tnl_hdr_negotiated,
386403
bool little_endian,
387404
int vlan_hlen,
388-
bool has_data_valid)
405+
bool has_data_valid,
406+
bool feature_hdrlen)
389407
{
390408
struct virtio_net_hdr *hdr = (struct virtio_net_hdr *)vhdr;
391409
unsigned int inner_nh, outer_th;
@@ -394,9 +412,17 @@ virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb,
394412

395413
tnl_gso_type = skb_shinfo(skb)->gso_type & (SKB_GSO_UDP_TUNNEL |
396414
SKB_GSO_UDP_TUNNEL_CSUM);
397-
if (!tnl_gso_type)
398-
return virtio_net_hdr_from_skb(skb, hdr, little_endian,
399-
has_data_valid, vlan_hlen);
415+
if (!tnl_gso_type) {
416+
ret = virtio_net_hdr_from_skb(skb, hdr, little_endian,
417+
has_data_valid, vlan_hlen);
418+
if (ret)
419+
return ret;
420+
421+
if (feature_hdrlen && hdr->hdr_len)
422+
__virtio_net_set_hdrlen(skb, hdr, little_endian);
423+
424+
return ret;
425+
}
400426

401427
/* Tunnel support not negotiated but skb ask for it. */
402428
if (!tnl_hdr_negotiated)

0 commit comments

Comments
 (0)