]> 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 32e8c3f73c797b96a651924ae9025295cc346d12..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
@@ -378,7 +386,7 @@ int ip6_forward(struct sk_buff *skb)
                goto drop;
        }
 
-       skb->ip_summed = CHECKSUM_NONE;
+       skb_forward_csum(skb);
 
        /*
         *      We DO NOT make any processing on
@@ -514,7 +522,7 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
        u16 offset = sizeof(struct ipv6hdr);
        struct ipv6_opt_hdr *exthdr =
                                (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
-       unsigned int packet_len = skb->tail - skb_network_header(skb);
+       unsigned int packet_len = skb->tail - skb->network_header;
        int found_rhdr = 0;
        *nexthdr = &ipv6_hdr(skb)->nexthdr;
 
@@ -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;
@@ -733,7 +754,8 @@ slow_path:
                skb_put(frag, len + hlen + sizeof(struct frag_hdr));
                skb_reset_network_header(frag);
                fh = (struct frag_hdr *)(skb_network_header(frag) + hlen);
-               frag->h.raw = frag->nh.raw + hlen + sizeof(struct frag_hdr);
+               frag->transport_header = (frag->network_header + hlen +
+                                         sizeof(struct frag_hdr));
 
                /*
                 *      Charge the memory for the fragment to any owner
@@ -745,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.
@@ -761,7 +783,7 @@ slow_path:
                /*
                 *      Copy a block of the IP datagram.
                 */
-               if (skb_copy_bits(skb, ptr, frag->h.raw, len))
+               if (skb_copy_bits(skb, ptr, skb_transport_header(skb), len))
                        BUG();
                left -= len;
 
@@ -976,7 +998,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
                skb_reset_network_header(skb);
 
                /* initialize protocol header pointer */
-               skb->h.raw = skb->nh.raw + fragheaderlen;
+               skb->transport_header = skb->network_header + fragheaderlen;
 
                skb->ip_summed = CHECKSUM_PARTIAL;
                skb->csum = 0;
@@ -1049,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;
@@ -1198,8 +1221,8 @@ alloc_new_skb:
                        data = skb_put(skb, fraglen);
                        skb_set_network_header(skb, exthdrlen);
                        data += fragheaderlen;
-                       skb->h.raw = skb->nh.raw + fragheaderlen;
-
+                       skb->transport_header = (skb->network_header +
+                                                fragheaderlen);
                        if (fraggap) {
                                skb->csum = skb_copy_and_csum_bits(
                                        skb_prev, maxfraglen,
@@ -1325,7 +1348,7 @@ int ip6_push_pending_frames(struct sock *sk)
        if (skb->data < skb_network_header(skb))
                __skb_pull(skb, skb_network_offset(skb));
        while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {
-               __skb_pull(tmp_skb, skb->h.raw - skb->nh.raw);
+               __skb_pull(tmp_skb, skb_network_header_len(skb));
                *tail_skb = tmp_skb;
                tail_skb = &(tmp_skb->next);
                skb->len += tmp_skb->len;
@@ -1337,7 +1360,7 @@ int ip6_push_pending_frames(struct sock *sk)
        }
 
        ipv6_addr_copy(final_dst, &fl->fl6_dst);
-       __skb_pull(skb, skb->h.raw - skb->nh.raw);
+       __skb_pull(skb, skb_network_header_len(skb));
        if (opt && opt->opt_flen)
                ipv6_push_frag_opts(skb, opt, &proto);
        if (opt && opt->opt_nflen)