aboutsummaryrefslogtreecommitdiffstats
path: root/net/packet/af_packet.c
diff options
context:
space:
mode:
authorJason Wang <jasowang@redhat.com>2013-03-25 20:19:57 +0000
committerDavid S. Miller <davem@davemloft.net>2013-03-26 12:44:43 -0400
commitc1aad275b0293d2b1905ec95a945422262470684 (patch)
tree68ce7c931434840a46bffd1e9db5cb20c0feb7cd /net/packet/af_packet.c
parenttuntap: set transport header before passing it to kernel (diff)
downloadlinux-dev-c1aad275b0293d2b1905ec95a945422262470684.tar.xz
linux-dev-c1aad275b0293d2b1905ec95a945422262470684.zip
packet: set transport header before doing xmit
Set the transport header for 1) some drivers (e.g ixgbe needs l4 header to do atr) 2) precise packet length estimation (introduced in 1def9238) needs l4 header to compute header length. So this patch first tries to get l4 header for packet socket through skb_flow_dissect(), and pretend no l4 header if skb_flow_dissect() fails. Cc: Eric Dumazet <edumazet@google.com> Signed-off-by: Jason Wang <jasowang@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/packet/af_packet.c')
-rw-r--r--net/packet/af_packet.c21
1 files changed, 21 insertions, 0 deletions
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index bd0d14c97d41..83fdd0a87eb6 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -88,6 +88,7 @@
#include <linux/virtio_net.h>
#include <linux/errqueue.h>
#include <linux/net_tstamp.h>
+#include <net/flow_keys.h>
#ifdef CONFIG_INET
#include <net/inet_common.h>
@@ -1412,6 +1413,7 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock,
__be16 proto = 0;
int err;
int extra_len = 0;
+ struct flow_keys keys;
/*
* Get and verify the address.
@@ -1512,6 +1514,11 @@ retry:
if (unlikely(extra_len == 4))
skb->no_fcs = 1;
+ if (skb_flow_dissect(skb, &keys))
+ skb_set_transport_header(skb, keys.thoff);
+ else
+ skb_reset_transport_header(skb);
+
dev_queue_xmit(skb);
rcu_read_unlock();
return len;
@@ -1918,6 +1925,7 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
struct page *page;
void *data;
int err;
+ struct flow_keys keys;
ph.raw = frame;
@@ -1943,6 +1951,11 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
skb_reserve(skb, hlen);
skb_reset_network_header(skb);
+ if (skb_flow_dissect(skb, &keys))
+ skb_set_transport_header(skb, keys.thoff);
+ else
+ skb_reset_transport_header(skb);
+
if (po->tp_tx_has_off) {
int off_min, off_max, off;
off_min = po->tp_hdrlen - sizeof(struct sockaddr_ll);
@@ -2199,6 +2212,7 @@ static int packet_snd(struct socket *sock,
unsigned short gso_type = 0;
int hlen, tlen;
int extra_len = 0;
+ struct flow_keys keys;
/*
* Get and verify the address.
@@ -2351,6 +2365,13 @@ static int packet_snd(struct socket *sock,
len += vnet_hdr_len;
}
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ skb_set_transport_header(skb, skb_checksum_start_offset(skb));
+ else if (skb_flow_dissect(skb, &keys))
+ skb_set_transport_header(skb, keys.thoff);
+ else
+ skb_set_transport_header(skb, reserve);
+
if (unlikely(extra_len == 4))
skb->no_fcs = 1;