Commit 8d39b4a6 authored by Willem de Bruijn's avatar Willem de Bruijn Committed by David S. Miller

packet: parse tpacket header before skb alloc

GSO packet headers must be stored in the linear skb segment.
Move tpacket header parsing before sock_alloc_send_skb. The GSO
follow-on patch will later increase the skb linear argument to
sock_alloc_send_skb if needed for large packets.

The header parsing code does not require an allocated skb, so is
safe to move. Later pass to tpacket_fill_skb the computed data
start and length.
Signed-off-by: default avatarWillem de Bruijn <willemb@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 58d19b19
...@@ -2494,14 +2494,13 @@ static int packet_snd_vnet_gso(struct sk_buff *skb, ...@@ -2494,14 +2494,13 @@ static int packet_snd_vnet_gso(struct sk_buff *skb,
} }
static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
void *frame, struct net_device *dev, int size_max, void *frame, struct net_device *dev, void *data, int tp_len,
__be16 proto, unsigned char *addr, int hlen) __be16 proto, unsigned char *addr, int hlen)
{ {
union tpacket_uhdr ph; union tpacket_uhdr ph;
int to_write, offset, len, tp_len, nr_frags, len_max; int to_write, offset, len, nr_frags, len_max;
struct socket *sock = po->sk.sk_socket; struct socket *sock = po->sk.sk_socket;
struct page *page; struct page *page;
void *data;
int err; int err;
ph.raw = frame; ph.raw = frame;
...@@ -2513,51 +2512,9 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, ...@@ -2513,51 +2512,9 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
sock_tx_timestamp(&po->sk, &skb_shinfo(skb)->tx_flags); sock_tx_timestamp(&po->sk, &skb_shinfo(skb)->tx_flags);
skb_shinfo(skb)->destructor_arg = ph.raw; skb_shinfo(skb)->destructor_arg = ph.raw;
switch (po->tp_version) {
case TPACKET_V2:
tp_len = ph.h2->tp_len;
break;
default:
tp_len = ph.h1->tp_len;
break;
}
if (unlikely(tp_len > size_max)) {
pr_err("packet size is too long (%d > %d)\n", tp_len, size_max);
return -EMSGSIZE;
}
skb_reserve(skb, hlen); skb_reserve(skb, hlen);
skb_reset_network_header(skb); skb_reset_network_header(skb);
if (unlikely(po->tp_tx_has_off)) {
int off_min, off_max, off;
off_min = po->tp_hdrlen - sizeof(struct sockaddr_ll);
off_max = po->tx_ring.frame_size - tp_len;
if (sock->type == SOCK_DGRAM) {
switch (po->tp_version) {
case TPACKET_V2:
off = ph.h2->tp_net;
break;
default:
off = ph.h1->tp_net;
break;
}
} else {
switch (po->tp_version) {
case TPACKET_V2:
off = ph.h2->tp_mac;
break;
default:
off = ph.h1->tp_mac;
break;
}
}
if (unlikely((off < off_min) || (off_max < off)))
return -EINVAL;
data = ph.raw + off;
} else {
data = ph.raw + po->tp_hdrlen - sizeof(struct sockaddr_ll);
}
to_write = tp_len; to_write = tp_len;
if (sock->type == SOCK_DGRAM) { if (sock->type == SOCK_DGRAM) {
...@@ -2615,6 +2572,61 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, ...@@ -2615,6 +2572,61 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
return tp_len; return tp_len;
} }
static int tpacket_parse_header(struct packet_sock *po, void *frame,
int size_max, void **data)
{
union tpacket_uhdr ph;
int tp_len, off;
ph.raw = frame;
switch (po->tp_version) {
case TPACKET_V2:
tp_len = ph.h2->tp_len;
break;
default:
tp_len = ph.h1->tp_len;
break;
}
if (unlikely(tp_len > size_max)) {
pr_err("packet size is too long (%d > %d)\n", tp_len, size_max);
return -EMSGSIZE;
}
if (unlikely(po->tp_tx_has_off)) {
int off_min, off_max;
off_min = po->tp_hdrlen - sizeof(struct sockaddr_ll);
off_max = po->tx_ring.frame_size - tp_len;
if (po->sk.sk_type == SOCK_DGRAM) {
switch (po->tp_version) {
case TPACKET_V2:
off = ph.h2->tp_net;
break;
default:
off = ph.h1->tp_net;
break;
}
} else {
switch (po->tp_version) {
case TPACKET_V2:
off = ph.h2->tp_mac;
break;
default:
off = ph.h1->tp_mac;
break;
}
}
if (unlikely((off < off_min) || (off_max < off)))
return -EINVAL;
} else {
off = po->tp_hdrlen - sizeof(struct sockaddr_ll);
}
*data = frame + off;
return tp_len;
}
static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
{ {
struct sk_buff *skb; struct sk_buff *skb;
...@@ -2626,6 +2638,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) ...@@ -2626,6 +2638,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
bool need_wait = !(msg->msg_flags & MSG_DONTWAIT); bool need_wait = !(msg->msg_flags & MSG_DONTWAIT);
int tp_len, size_max; int tp_len, size_max;
unsigned char *addr; unsigned char *addr;
void *data;
int len_sum = 0; int len_sum = 0;
int status = TP_STATUS_AVAILABLE; int status = TP_STATUS_AVAILABLE;
int hlen, tlen; int hlen, tlen;
...@@ -2673,6 +2686,11 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) ...@@ -2673,6 +2686,11 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
continue; continue;
} }
skb = NULL;
tp_len = tpacket_parse_header(po, ph, size_max, &data);
if (tp_len < 0)
goto tpacket_error;
status = TP_STATUS_SEND_REQUEST; status = TP_STATUS_SEND_REQUEST;
hlen = LL_RESERVED_SPACE(dev); hlen = LL_RESERVED_SPACE(dev);
tlen = dev->needed_tailroom; tlen = dev->needed_tailroom;
...@@ -2686,7 +2704,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) ...@@ -2686,7 +2704,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
err = len_sum; err = len_sum;
goto out_status; goto out_status;
} }
tp_len = tpacket_fill_skb(po, skb, ph, dev, size_max, proto, tp_len = tpacket_fill_skb(po, skb, ph, dev, data, tp_len, proto,
addr, hlen); addr, hlen);
if (likely(tp_len >= 0) && if (likely(tp_len >= 0) &&
tp_len > dev->mtu + reserve && tp_len > dev->mtu + reserve &&
...@@ -2694,6 +2712,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) ...@@ -2694,6 +2712,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
tp_len = -EMSGSIZE; tp_len = -EMSGSIZE;
if (unlikely(tp_len < 0)) { if (unlikely(tp_len < 0)) {
tpacket_error:
if (po->tp_loss) { if (po->tp_loss) {
__packet_set_status(po, ph, __packet_set_status(po, ph,
TP_STATUS_AVAILABLE); TP_STATUS_AVAILABLE);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment