vserver 2.0 rc7
[linux-2.6.git] / net / ipv6 / ip6_output.c
index fce2a87..b78a535 100644 (file)
@@ -147,7 +147,7 @@ static int ip6_output2(struct sk_buff *skb)
 
 int ip6_output(struct sk_buff *skb)
 {
-       if (skb->len > dst_pmtu(skb->dst))
+       if (skb->len > dst_mtu(skb->dst) || dst_allfrag(skb->dst))
                return ip6_fragment(skb, ip6_output2);
        else
                return ip6_output2(skb);
@@ -253,6 +253,8 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
                hlimit = np->hop_limit;
        if (hlimit < 0)
                hlimit = dst_metric(dst, RTAX_HOPLIMIT);
+       if (hlimit < 0)
+               hlimit = ipv6_get_hoplimit(dst->dev);
 
        hdr->payload_len = htons(seg_len);
        hdr->nexthdr = proto;
@@ -261,7 +263,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
        ipv6_addr_copy(&hdr->saddr, &fl->fl6_src);
        ipv6_addr_copy(&hdr->daddr, first_hop);
 
-       mtu = dst_pmtu(dst);
+       mtu = dst_mtu(dst);
        if ((skb->len <= mtu) || ipfragok) {
                IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
                return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute);
@@ -395,6 +397,7 @@ int ip6_forward(struct sk_buff *skb)
                IP6_INC_STATS(IPSTATS_MIB_INDISCARDS);
                goto drop;
        }
+       dst = skb->dst;
 
        /* IPv6 specs say nothing about it, but it is clear that we cannot
           send redirects to source routed frames.
@@ -426,10 +429,10 @@ int ip6_forward(struct sk_buff *skb)
                goto error;
        }
 
-       if (skb->len > dst_pmtu(dst)) {
+       if (skb->len > dst_mtu(dst)) {
                /* Again, force OUTPUT device used as source address */
                skb->dev = dst->dev;
-               icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst_pmtu(dst), skb->dev);
+               icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst_mtu(dst), skb->dev);
                IP6_INC_STATS_BH(IPSTATS_MIB_INTOOBIGERRORS);
                IP6_INC_STATS_BH(IPSTATS_MIB_FRAGFAILS);
                kfree_skb(skb);
@@ -532,7 +535,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
        hlen = ip6_find_1stfragopt(skb, &prevhdr);
        nexthdr = *prevhdr;
 
-       mtu = dst_pmtu(&rt->u.dst) - hlen - sizeof(struct frag_hdr);
+       mtu = dst_mtu(&rt->u.dst) - hlen - sizeof(struct frag_hdr);
 
        if (skb_shinfo(skb)->frag_list) {
                int first_len = skb_pagelen(skb);
@@ -549,13 +552,17 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
                            skb_headroom(frag) < hlen)
                            goto slow_path;
 
-                       /* Correct socket ownership. */
-                       if (frag->sk == NULL)
-                               goto slow_path;
-
                        /* Partially cloned skb? */
                        if (skb_shared(frag))
                                goto slow_path;
+
+                       BUG_ON(frag->sk);
+                       if (skb->sk) {
+                               sock_hold(skb->sk);
+                               frag->sk = skb->sk;
+                               frag->destructor = sock_wfree;
+                               skb->truesize -= frag->truesize;
+                       }
                }
 
                err = 0;
@@ -847,7 +854,9 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse
                np->cork.rt = rt;
                inet->cork.fl = *fl;
                np->cork.hop_limit = hlimit;
-               inet->cork.fragsize = mtu = dst_pmtu(&rt->u.dst);
+               inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
+               if (dst_allfrag(rt->u.dst.path))
+                       inet->cork.flags |= IPCORK_ALLFRAG;
                inet->cork.length = 0;
                sk->sk_sndmsg_page = NULL;
                sk->sk_sndmsg_off = 0;
@@ -899,7 +908,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse
 
        while (length > 0) {
                /* Check if the remaining data fits into current packet. */
-               copy = mtu - skb->len;
+               copy = (inet->cork.length <= mtu && !(inet->cork.flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - skb->len;
                if (copy < length)
                        copy = maxfraglen - skb->len;
 
@@ -924,7 +933,7 @@ alloc_new_skb:
                         * we know we need more fragment(s).
                         */
                        datalen = length + fraggap;
-                       if (datalen > mtu - fragheaderlen)
+                       if (datalen > (inet->cork.length <= mtu && !(inet->cork.flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
                                datalen = maxfraglen - fragheaderlen;
 
                        fraglen = datalen + fragheaderlen;
@@ -1111,12 +1120,10 @@ int ip6_push_pending_frames(struct sock *sk)
                tail_skb = &(tmp_skb->next);
                skb->len += tmp_skb->len;
                skb->data_len += tmp_skb->len;
-#if 0 /* Logically correct, but useless work, ip_fragment() will have to undo */
                skb->truesize += tmp_skb->truesize;
                __sock_put(tmp_skb->sk);
                tmp_skb->destructor = NULL;
                tmp_skb->sk = NULL;
-#endif
        }
 
        ipv6_addr_copy(final_dst, &fl->fl6_dst);
@@ -1144,7 +1151,7 @@ int ip6_push_pending_frames(struct sock *sk)
        err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dst->dev, dst_output);
        if (err) {
                if (err > 0)
-                       err = inet->recverr ? net_xmit_errno(err) : 0;
+                       err = np->recverr ? net_xmit_errno(err) : 0;
                if (err)
                        goto error;
        }
@@ -1158,6 +1165,7 @@ out:
        if (np->cork.rt) {
                dst_release(&np->cork.rt->u.dst);
                np->cork.rt = NULL;
+               inet->cork.flags &= ~IPCORK_ALLFRAG;
        }
        memset(&inet->cork.fl, 0, sizeof(inet->cork.fl));
        return err;
@@ -1185,6 +1193,7 @@ void ip6_flush_pending_frames(struct sock *sk)
        if (np->cork.rt) {
                dst_release(&np->cork.rt->u.dst);
                np->cork.rt = NULL;
+               inet->cork.flags &= ~IPCORK_ALLFRAG;
        }
        memset(&inet->cork.fl, 0, sizeof(inet->cork.fl));
 }