]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
IPV6: Fix IPsec datagram fragmentation
authorHerbert Xu <herbert@gondor.apana.org.au>
Wed, 5 Mar 2008 22:46:35 +0000 (14:46 -0800)
committerChris Wright <chrisw@sous-sol.org>
Mon, 24 Mar 2008 18:47:06 +0000 (11:47 -0700)
Upstream commits: 28a89453b1e8de8d777ad96fa1eef27b5d1ce074
                  b5c15fc004ac83b7ad280acbe0fd4bbed7e2c8d4

This is a long-standing bug in the IPsec IPv6 code that breaks
when we emit a IPsec tunnel-mode datagram packet.  The problem
is that the code the emits the packet assumes the IPv6 stack
will fragment it later, but the IPv6 stack assumes that whoever
is emitting the packet is going to pre-fragment the packet.

In the long term we need to fix both sides, e.g., to get the
datagram code to pre-fragment as well as to get the IPv6 stack
to fragment locally generated tunnel-mode packet.

For now this patch does the second part which should make it
work for the IPsec host case.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Chris Wright <chrisw@sous-sol.org>
net/ipv6/ip6_output.c
net/ipv6/xfrm6_output.c

index 2f59baa53c6f9ff1315a44f4435a42fcd3d79c17..5b4095b30c4eda954931563c63c39d4004a972a1 100644 (file)
@@ -593,7 +593,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
         * or if the skb it not generated by a local socket.  (This last
         * check should be redundant, but it's free.)
         */
-       if (!np || np->pmtudisc >= IPV6_PMTUDISC_DO) {
+       if (!skb->local_df) {
                skb->dev = skb->dst->dev;
                icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
                IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS);
@@ -1389,6 +1389,10 @@ int ip6_push_pending_frames(struct sock *sk)
                tmp_skb->sk = NULL;
        }
 
+       /* Allow local fragmentation. */
+       if (np->pmtudisc < IPV6_PMTUDISC_DO)
+               skb->local_df = 1;
+
        ipv6_addr_copy(final_dst, &fl->fl6_dst);
        __skb_pull(skb, skb_network_header_len(skb));
        if (opt && opt->opt_flen)
index 656976760ad47175afa8bc6ac7d2152d5e7f95b4..dc2290955743578528a9ca6248964ce0a1ab0ef8 100644 (file)
@@ -34,7 +34,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
        if (mtu < IPV6_MIN_MTU)
                mtu = IPV6_MIN_MTU;
 
-       if (skb->len > mtu) {
+       if (!skb->local_df && skb->len > mtu) {
                skb->dev = dst->dev;
                icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
                ret = -EMSGSIZE;