]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/ipv6/ip6_output.c
[IPV6]: Fix slab corruption running ip6sic
[linux-2.6-omap-h63xx.git] / net / ipv6 / ip6_output.c
index be3f082a87ed410723edef3a938962c43274b1c4..f508171bab735b5e1901caa51bfff0248129e2c7 100644 (file)
@@ -137,9 +137,17 @@ static int ip6_output2(struct sk_buff *skb)
        return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, skb->dev,ip6_output_finish);
 }
 
+static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
+{
+       struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL;
+
+       return (np && np->pmtudisc == IPV6_PMTUDISC_PROBE) ?
+              skb->dst->dev->mtu : dst_mtu(skb->dst);
+}
+
 int ip6_output(struct sk_buff *skb)
 {
-       if ((skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) ||
+       if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
                                dst_allfrag(skb->dst))
                return ip6_fragment(skb, ip6_output2);
        else
@@ -566,7 +574,20 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
        hlen = ip6_find_1stfragopt(skb, &prevhdr);
        nexthdr = *prevhdr;
 
-       mtu = dst_mtu(&rt->u.dst);
+       mtu = ip6_skb_dst_mtu(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.  (This last
+        * check should be redundant, but it's free.)
+        */
+       if (!np || np->pmtudisc >= IPV6_PMTUDISC_DO) {
+               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);
+               kfree_skb(skb);
+               return -EMSGSIZE;
+       }
+
        if (np && np->frag_size < mtu) {
                if (np->frag_size)
                        mtu = np->frag_size;
@@ -746,7 +767,7 @@ slow_path:
                /*
                 *      Copy the packet header into the new buffer.
                 */
-               memcpy(skb_network_header(frag), skb->data, hlen);
+               skb_copy_from_linear_data(skb, skb_network_header(frag), hlen);
 
                /*
                 *      Build fragment header.
@@ -1050,7 +1071,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
                inet->cork.fl = *fl;
                np->cork.hop_limit = hlimit;
                np->cork.tclass = tclass;
-               mtu = dst_mtu(rt->u.dst.path);
+               mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+                     rt->u.dst.dev->mtu : dst_mtu(rt->u.dst.path);
                if (np->frag_size < mtu) {
                        if (np->frag_size)
                                mtu = np->frag_size;