Skip to content

Commit 673bb63

Browse files
author
Paolo Abeni
committed
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 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 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/[email protected] v10: fix http://lore.kernel.org/all/[email protected] ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
2 parents 70b439b + 6c860dc commit 673bb63

3 files changed

Lines changed: 55 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: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,39 @@ 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+
227+
/* This function must be called after virtio_net_hdr_from_skb(). */
228+
static inline void __virtio_net_set_tnl_hdrlen(const struct sk_buff *skb,
229+
struct virtio_net_hdr *hdr)
230+
{
231+
u16 hdr_len;
232+
233+
hdr_len = skb_inner_transport_offset(skb);
234+
235+
if (hdr->gso_type == VIRTIO_NET_HDR_GSO_UDP_L4)
236+
hdr_len += sizeof(struct udphdr);
237+
else
238+
hdr_len += inner_tcp_hdrlen(skb);
239+
240+
hdr->hdr_len = __cpu_to_virtio16(true, hdr_len);
241+
}
242+
210243
static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,
211244
struct virtio_net_hdr *hdr,
212245
bool little_endian,
@@ -385,7 +418,8 @@ virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb,
385418
bool tnl_hdr_negotiated,
386419
bool little_endian,
387420
int vlan_hlen,
388-
bool has_data_valid)
421+
bool has_data_valid,
422+
bool feature_hdrlen)
389423
{
390424
struct virtio_net_hdr *hdr = (struct virtio_net_hdr *)vhdr;
391425
unsigned int inner_nh, outer_th;
@@ -394,9 +428,17 @@ virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb,
394428

395429
tnl_gso_type = skb_shinfo(skb)->gso_type & (SKB_GSO_UDP_TUNNEL |
396430
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);
431+
if (!tnl_gso_type) {
432+
ret = virtio_net_hdr_from_skb(skb, hdr, little_endian,
433+
has_data_valid, vlan_hlen);
434+
if (ret)
435+
return ret;
436+
437+
if (feature_hdrlen && hdr->hdr_len)
438+
__virtio_net_set_hdrlen(skb, hdr, little_endian);
439+
440+
return ret;
441+
}
400442

401443
/* Tunnel support not negotiated but skb ask for it. */
402444
if (!tnl_hdr_negotiated)
@@ -414,6 +456,9 @@ virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb,
414456
if (ret)
415457
return ret;
416458

459+
if (feature_hdrlen && hdr->hdr_len)
460+
__virtio_net_set_tnl_hdrlen(skb, hdr);
461+
417462
if (skb->protocol == htons(ETH_P_IPV6))
418463
hdr->gso_type |= VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6;
419464
else

0 commit comments

Comments
 (0)