Commit 1e2bd517 authored by Pravin B Shelar's avatar Pravin B Shelar Committed by David S. Miller

udp6: Fix udp fragmentation for tunnel traffic.

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 68c33163 (GRE: Add TCP
segmentation offload for GRE).
Reported-by: default avatarDmitry Kravkov <>
Signed-off-by: default avatarPravin B Shelar <>
Tested-by: default avatarDmitry Kravkov <>
Signed-off-by: default avatarDavid S. Miller <>
parent b190a508
......@@ -2852,6 +2852,21 @@ static inline int skb_tnl_header_len(const struct sk_buff *inner_skb)
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;
......@@ -46,11 +46,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))
......@@ -83,9 +84,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.
......@@ -93,11 +96,12 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
nexthdr = *prevhdr;
unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
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;
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