Skip to content

Commit 1d036d2

Browse files
wdebruijdavem330
authored andcommitted
packet: tpacket_snd gso and checksum offload
Support socket option PACKET_VNET_HDR together with PACKET_TX_RING. When enabled, a struct virtio_net_hdr is expected to precede the data in the ring. The vnet option must be set before the ring is created. The implementation reuses the existing skb_copy_bits code that is used when dev->hard_header_len is non-zero. Move this ll_header check to before the skb alloc and combine it with a test for vnet_hdr->hdr_len. Allocate and copy the max of the two. Verified with test program at github.com/wdebruij/kerneltools/blob/master/tests/psock_txring_vnet.c Signed-off-by: Willem de Bruijn <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 8d39b4a commit 1d036d2

File tree

1 file changed

+37
-16
lines changed

1 file changed

+37
-16
lines changed

net/packet/af_packet.c

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2495,7 +2495,7 @@ static int packet_snd_vnet_gso(struct sk_buff *skb,
24952495

24962496
static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
24972497
void *frame, struct net_device *dev, void *data, int tp_len,
2498-
__be16 proto, unsigned char *addr, int hlen)
2498+
__be16 proto, unsigned char *addr, int hlen, int copylen)
24992499
{
25002500
union tpacket_uhdr ph;
25012501
int to_write, offset, len, nr_frags, len_max;
@@ -2522,20 +2522,17 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
25222522
NULL, tp_len);
25232523
if (unlikely(err < 0))
25242524
return -EINVAL;
2525-
} else if (dev->hard_header_len) {
2526-
if (ll_header_truncated(dev, tp_len))
2527-
return -EINVAL;
2528-
2525+
} else if (copylen) {
25292526
skb_push(skb, dev->hard_header_len);
2530-
err = skb_store_bits(skb, 0, data,
2531-
dev->hard_header_len);
2527+
skb_put(skb, copylen - dev->hard_header_len);
2528+
err = skb_store_bits(skb, 0, data, copylen);
25322529
if (unlikely(err))
25332530
return err;
25342531
if (!skb->protocol)
25352532
tpacket_set_protocol(dev, skb);
25362533

2537-
data += dev->hard_header_len;
2538-
to_write -= dev->hard_header_len;
2534+
data += copylen;
2535+
to_write -= copylen;
25392536
}
25402537

25412538
offset = offset_in_page(data);
@@ -2631,6 +2628,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
26312628
{
26322629
struct sk_buff *skb;
26332630
struct net_device *dev;
2631+
struct virtio_net_hdr *vnet_hdr = NULL;
26342632
__be16 proto;
26352633
int err, reserve = 0;
26362634
void *ph;
@@ -2641,7 +2639,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
26412639
void *data;
26422640
int len_sum = 0;
26432641
int status = TP_STATUS_AVAILABLE;
2644-
int hlen, tlen;
2642+
int hlen, tlen, copylen = 0;
26452643

26462644
mutex_lock(&po->pg_vec_lock);
26472645

@@ -2674,7 +2672,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
26742672
size_max = po->tx_ring.frame_size
26752673
- (po->tp_hdrlen - sizeof(struct sockaddr_ll));
26762674

2677-
if (size_max > dev->mtu + reserve + VLAN_HLEN)
2675+
if ((size_max > dev->mtu + reserve + VLAN_HLEN) && !po->has_vnet_hdr)
26782676
size_max = dev->mtu + reserve + VLAN_HLEN;
26792677

26802678
do {
@@ -2694,8 +2692,28 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
26942692
status = TP_STATUS_SEND_REQUEST;
26952693
hlen = LL_RESERVED_SPACE(dev);
26962694
tlen = dev->needed_tailroom;
2695+
if (po->has_vnet_hdr) {
2696+
vnet_hdr = data;
2697+
data += sizeof(*vnet_hdr);
2698+
tp_len -= sizeof(*vnet_hdr);
2699+
if (tp_len < 0 ||
2700+
__packet_snd_vnet_parse(vnet_hdr, tp_len)) {
2701+
tp_len = -EINVAL;
2702+
goto tpacket_error;
2703+
}
2704+
copylen = __virtio16_to_cpu(vio_le(),
2705+
vnet_hdr->hdr_len);
2706+
}
2707+
if (dev->hard_header_len) {
2708+
if (ll_header_truncated(dev, tp_len)) {
2709+
tp_len = -EINVAL;
2710+
goto tpacket_error;
2711+
}
2712+
copylen = max_t(int, copylen, dev->hard_header_len);
2713+
}
26972714
skb = sock_alloc_send_skb(&po->sk,
2698-
hlen + tlen + sizeof(struct sockaddr_ll),
2715+
hlen + tlen + sizeof(struct sockaddr_ll) +
2716+
(copylen - dev->hard_header_len),
26992717
!need_wait, &err);
27002718

27012719
if (unlikely(skb == NULL)) {
@@ -2705,9 +2723,10 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
27052723
goto out_status;
27062724
}
27072725
tp_len = tpacket_fill_skb(po, skb, ph, dev, data, tp_len, proto,
2708-
addr, hlen);
2726+
addr, hlen, copylen);
27092727
if (likely(tp_len >= 0) &&
27102728
tp_len > dev->mtu + reserve &&
2729+
!po->has_vnet_hdr &&
27112730
!packet_extra_vlan_len_allowed(dev, skb))
27122731
tp_len = -EMSGSIZE;
27132732

@@ -2726,6 +2745,11 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
27262745
}
27272746
}
27282747

2748+
if (po->has_vnet_hdr && packet_snd_vnet_gso(skb, vnet_hdr)) {
2749+
tp_len = -EINVAL;
2750+
goto tpacket_error;
2751+
}
2752+
27292753
packet_pick_tx_queue(dev, skb);
27302754

27312755
skb->destructor = tpacket_destruct_skb;
@@ -3616,9 +3640,6 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv
36163640
}
36173641
if (optlen < len)
36183642
return -EINVAL;
3619-
if (pkt_sk(sk)->has_vnet_hdr &&
3620-
optname == PACKET_TX_RING)
3621-
return -EINVAL;
36223643
if (copy_from_user(&req_u.req, optval, len))
36233644
return -EFAULT;
36243645
return packet_set_ring(sk, &req_u, 0,

0 commit comments

Comments
 (0)