Message ID | 1432248256-28846-1-git-send-email-fw@strlen.de |
---|---|
State | Accepted, archived |
Delegated to: | David Miller |
Headers | show |
From: Florian Westphal <fw@strlen.de> Date: Fri, 22 May 2015 00:44:16 +0200 > since commit 6aafeef03b9d ("netfilter: push reasm skb through instead of > original frag skbs") we will end up sometimes re-fragmenting skbs > that we've reassembled. > > ipv6 defrag preserves the original skbs using the skb frag list, i.e. as long > as the skb frag list is preserved there is no problem since we keep > original geometry of fragments intact. > > However, in the rare case where the frag list is munged or skb > is linearized, we might send larger fragments than what we originally > received. > > A router in the path might then send packet-too-big errors even if > sender never sent fragments exceeding the reported mtu: > > mtu 1500 - 1500:1400 - 1400:1280 - 1280 > A R1 R2 B > > 1 - A sends to B, fragment size 1400 > 2 - R2 sends pkttoobig error for 1280 > 3 - A sends to B, fragment size 1280 > 4 - R2 sends pkttoobig error for 1280 again because it sees fragments of size 1400. > > make sure ip6_fragment always caps MTU at largest packet size seen > when defragmented skb is forwarded. > > Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org> > Signed-off-by: Florian Westphal <fw@strlen.de> Applied to net-next, thanks. -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index c217775..bf40b4b 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -564,18 +564,17 @@ int ip6_fragment(struct sock *sk, struct sk_buff *skb, /* We must not fragment if the socket is set to force MTU discovery * or if the skb it not generated by a local socket. */ - if (unlikely(!skb->ignore_df && skb->len > mtu) || - (IP6CB(skb)->frag_max_size && - IP6CB(skb)->frag_max_size > mtu)) { - if (skb->sk && dst_allfrag(skb_dst(skb))) - sk_nocaps_add(skb->sk, NETIF_F_GSO_MASK); + if (unlikely(!skb->ignore_df && skb->len > mtu)) + goto fail_toobig; - skb->dev = skb_dst(skb)->dev; - icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); - IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), - IPSTATS_MIB_FRAGFAILS); - kfree_skb(skb); - return -EMSGSIZE; + if (IP6CB(skb)->frag_max_size) { + if (IP6CB(skb)->frag_max_size > mtu) + goto fail_toobig; + + /* don't send fragments larger than what we received */ + mtu = IP6CB(skb)->frag_max_size; + if (mtu < IPV6_MIN_MTU) + mtu = IPV6_MIN_MTU; } if (np && np->frag_size < mtu) { @@ -815,6 +814,14 @@ slow_path: consume_skb(skb); return err; +fail_toobig: + if (skb->sk && dst_allfrag(skb_dst(skb))) + sk_nocaps_add(skb->sk, NETIF_F_GSO_MASK); + + skb->dev = skb_dst(skb)->dev; + icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); + err = -EMSGSIZE; + fail: IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_FRAGFAILS);