gro: Fix frag_list merging on imprecisely split packets
authorHerbert Xu <herbert@gondor.apana.org.au>
Fri, 6 Feb 2009 05:26:52 +0000 (21:26 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 6 Feb 2009 05:26:52 +0000 (21:26 -0800)
The previous fix ad0f9904444de1309dedd2b9e365cae8af77d9b1 (gro:
Fix handling of imprecisely split packets) only fixed the case
of frags merging, frag_list merging in the same circumstances
were still broken.

In particular, the packet headers end up in the data stream.

This patch fixes this plus another issue where an imprecisely
split packet header may be read incorrectly (this is mostly
harmless since it'll simply cause the packet to not match and
be rejected for GRO).

Thanks to Emil Tantilov and Jeff Kirsher for helping to track
this down.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/core/dev.c
net/core/skbuff.c

index 3337cf98f2311b8811c57c9e14f701fa90dce093..247f1614a796a25ebf3a2b4bc96beea03f33016e 100644 (file)
@@ -2391,7 +2391,8 @@ void *skb_gro_header(struct sk_buff *skb, unsigned int hlen)
                return pskb_may_pull(skb, hlen) ? skb->data + offset : NULL;
 
        return page_address(skb_shinfo(skb)->frags[0].page) +
-              skb_shinfo(skb)->frags[0].page_offset + offset;
+              skb_shinfo(skb)->frags[0].page_offset +
+              offset - skb_headlen(skb);
 }
 EXPORT_SYMBOL(skb_gro_header);
 
index e55d1ef5690dc4d024454bc2142b53a1c357a148..67f2a2f85827ac687673571f74c9ab7068106e8c 100644 (file)
@@ -2678,6 +2678,17 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
        p = nskb;
 
 merge:
+       if (skb_gro_offset(skb) > skb_headlen(skb)) {
+               skb_shinfo(skb)->frags[0].page_offset +=
+                       skb_gro_offset(skb) - skb_headlen(skb);
+               skb_shinfo(skb)->frags[0].size -=
+                       skb_gro_offset(skb) - skb_headlen(skb);
+               skb_gro_reset_offset(skb);
+               skb_gro_pull(skb, skb_headlen(skb));
+       }
+
+       __skb_pull(skb, skb_gro_offset(skb));
+
        p->prev->next = skb;
        p->prev = skb;
        skb_header_release(skb);