Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[linux-drm-fsl-dcu.git] / net / ipv4 / af_inet.c
index 70011e029ac13718d72d7172776ac8cb76798ced..ef4f9df6d698984b9ef4872afde9451bc014fc0a 100644 (file)
@@ -1377,8 +1377,12 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
                if (!NAPI_GRO_CB(p)->same_flow)
                        continue;
 
-               iph2 = ip_hdr(p);
-
+               iph2 = (struct iphdr *)(p->data + off);
+               /* The above works because, with the exception of the top
+                * (inner most) layer, we only aggregate pkts with the same
+                * hdr length so all the hdrs we'll need to verify will start
+                * at the same offset.
+                */
                if ((iph->protocol ^ iph2->protocol) |
                    ((__force u32)iph->saddr ^ (__force u32)iph2->saddr) |
                    ((__force u32)iph->daddr ^ (__force u32)iph2->daddr)) {
@@ -1397,6 +1401,11 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
        }
 
        NAPI_GRO_CB(skb)->flush |= flush;
+       skb_set_network_header(skb, off);
+       /* The above will be needed by the transport layer if there is one
+        * immediately following this IP hdr.
+        */
+
        skb_gro_pull(skb, sizeof(*iph));
        skb_set_transport_header(skb, skb_gro_offset(skb));
 
@@ -1411,10 +1420,10 @@ out:
        return pp;
 }
 
-static int inet_gro_complete(struct sk_buff *skb)
+static int inet_gro_complete(struct sk_buff *skb, int nhoff)
 {
-       __be16 newlen = htons(skb->len - skb_network_offset(skb));
-       struct iphdr *iph = ip_hdr(skb);
+       __be16 newlen = htons(skb->len - nhoff);
+       struct iphdr *iph = (struct iphdr *)(skb->data + nhoff);
        const struct net_offload *ops;
        int proto = iph->protocol;
        int err = -ENOSYS;
@@ -1427,7 +1436,11 @@ static int inet_gro_complete(struct sk_buff *skb)
        if (WARN_ON(!ops || !ops->callbacks.gro_complete))
                goto out_unlock;
 
-       err = ops->callbacks.gro_complete(skb);
+       /* Only need to add sizeof(*iph) to get to the next hdr below
+        * because any hdr with option will have been flushed in
+        * inet_gro_receive().
+        */
+       err = ops->callbacks.gro_complete(skb, nhoff + sizeof(*iph));
 
 out_unlock:
        rcu_read_unlock();