]> git.hungrycats.org Git - linux/commitdiff
udp6: Fix udp fragmentation for tunnel traffic.
authorPravin B Shelar <pshelar@nicira.com>
Thu, 30 May 2013 06:45:27 +0000 (06:45 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 27 Jun 2013 17:39:02 +0000 (10:39 -0700)
[ Upstream commit 1e2bd517c108816220f262d7954b697af03b5f9c ]

udp6 over GRE tunnel does not work after to GRE tso changes. GRE
tso handler passes inner packet but keeps track of outer header
start in SKB_GSO_CB(skb)->mac_offset.  udp6 fragment need to
take care of outer header, which start at the mac_offset, while
adding fragment header.
This bug is introduced by commit 68c3316311 (GRE: Add TCP
segmentation offload for GRE).

Reported-by: Dmitry Kravkov <dkravkov@gmail.com>
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Tested-by: Dmitry Kravkov <dmitry@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/linux/skbuff.h
net/ipv6/udp_offload.c

index b8292d8cc9fa6238ed0691194f714f827cabd058..1f2803c18d0b3b06b56a07ac264989b143508ef7 100644 (file)
@@ -2761,6 +2761,21 @@ static inline int skb_tnl_header_len(const struct sk_buff *inner_skb)
                SKB_GSO_CB(inner_skb)->mac_offset;
 }
 
+static inline int gso_pskb_expand_head(struct sk_buff *skb, int extra)
+{
+       int new_headroom, headroom;
+       int ret;
+
+       headroom = skb_headroom(skb);
+       ret = pskb_expand_head(skb, extra, 0, GFP_ATOMIC);
+       if (ret)
+               return ret;
+
+       new_headroom = skb_headroom(skb);
+       SKB_GSO_CB(skb)->mac_offset += (new_headroom - headroom);
+       return 0;
+}
+
 static inline bool skb_is_gso(const struct sk_buff *skb)
 {
        return skb_shinfo(skb)->gso_size;
index cf05cf073c517d2e0fae4c967acffffa77befcbc..0da1bd272e26b51add9ea699e8ffbae60cae7e3b 100644 (file)
@@ -42,11 +42,12 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
        unsigned int mss;
        unsigned int unfrag_ip6hlen, unfrag_len;
        struct frag_hdr *fptr;
-       u8 *mac_start, *prevhdr;
+       u8 *packet_start, *prevhdr;
        u8 nexthdr;
        u8 frag_hdr_sz = sizeof(struct frag_hdr);
        int offset;
        __wsum csum;
+       int tnl_hlen;
 
        mss = skb_shinfo(skb)->gso_size;
        if (unlikely(skb->len <= mss))
@@ -77,9 +78,11 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
        skb->ip_summed = CHECKSUM_NONE;
 
        /* Check if there is enough headroom to insert fragment header. */
-       if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
-           pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
-               goto out;
+       tnl_hlen = skb_tnl_header_len(skb);
+       if (skb_headroom(skb) < (tnl_hlen + frag_hdr_sz)) {
+               if (gso_pskb_expand_head(skb, tnl_hlen + frag_hdr_sz))
+                       goto out;
+       }
 
        /* Find the unfragmentable header and shift it left by frag_hdr_sz
         * bytes to insert fragment header.
@@ -87,11 +90,12 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
        unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
        nexthdr = *prevhdr;
        *prevhdr = NEXTHDR_FRAGMENT;
-       unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
-                    unfrag_ip6hlen;
-       mac_start = skb_mac_header(skb);
-       memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
+       unfrag_len = (skb_network_header(skb) - skb_mac_header(skb)) +
+                    unfrag_ip6hlen + tnl_hlen;
+       packet_start = (u8 *) skb->head + SKB_GSO_CB(skb)->mac_offset;
+       memmove(packet_start-frag_hdr_sz, packet_start, unfrag_len);
 
+       SKB_GSO_CB(skb)->mac_offset -= frag_hdr_sz;
        skb->mac_header -= frag_hdr_sz;
        skb->network_header -= frag_hdr_sz;