Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[linux-2.6.git] / net / ipv6 / ip6_output.c
index ac16f3b..d97e071 100644 (file)
@@ -100,6 +100,7 @@ static int ip6_finish_output2(struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct net_device *dev = dst->dev;
+       struct neighbour *neigh;
 
        skb->protocol = htons(ETH_P_IPV6);
        skb->dev = dev;
@@ -134,11 +135,15 @@ static int ip6_finish_output2(struct sk_buff *skb)
                                skb->len);
        }
 
-       if (dst->hh)
-               return neigh_hh_output(dst->hh, skb);
-       else if (dst->neighbour)
-               return dst->neighbour->output(skb);
+       rcu_read_lock();
+       neigh = dst_get_neighbour_noref(dst);
+       if (neigh) {
+               int res = neigh_output(neigh, skb);
 
+               rcu_read_unlock();
+               return res;
+       }
+       rcu_read_unlock();
        IP6_INC_STATS_BH(dev_net(dst->dev),
                         ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
        kfree_skb(skb);
@@ -174,18 +179,17 @@ int ip6_output(struct sk_buff *skb)
  *     xmit an sk_buff (used by TCP, SCTP and DCCP)
  */
 
-int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
-            struct ipv6_txoptions *opt)
+int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
+            struct ipv6_txoptions *opt, int tclass)
 {
        struct net *net = sock_net(sk);
        struct ipv6_pinfo *np = inet6_sk(sk);
-       struct in6_addr *first_hop = &fl->fl6_dst;
+       struct in6_addr *first_hop = &fl6->daddr;
        struct dst_entry *dst = skb_dst(skb);
        struct ipv6hdr *hdr;
-       u8  proto = fl->proto;
+       u8  proto = fl6->flowi6_proto;
        int seg_len = skb->len;
        int hlimit = -1;
-       int tclass = 0;
        u32 mtu;
 
        if (opt) {
@@ -223,21 +227,19 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
        /*
         *      Fill in the IPv6 header
         */
-       if (np) {
-               tclass = np->tclass;
+       if (np)
                hlimit = np->hop_limit;
-       }
        if (hlimit < 0)
                hlimit = ip6_dst_hoplimit(dst);
 
-       *(__be32 *)hdr = htonl(0x60000000 | (tclass << 20)) | fl->fl6_flowlabel;
+       *(__be32 *)hdr = htonl(0x60000000 | (tclass << 20)) | fl6->flowlabel;
 
        hdr->payload_len = htons(seg_len);
        hdr->nexthdr = proto;
        hdr->hop_limit = hlimit;
 
-       ipv6_addr_copy(&hdr->saddr, &fl->fl6_src);
-       ipv6_addr_copy(&hdr->daddr, first_hop);
+       hdr->saddr = fl6->saddr;
+       hdr->daddr = *first_hop;
 
        skb->priority = sk->sk_priority;
        skb->mark = sk->sk_mark;
@@ -288,8 +290,8 @@ int ip6_nd_hdr(struct sock *sk, struct sk_buff *skb, struct net_device *dev,
        hdr->nexthdr = proto;
        hdr->hop_limit = np->hop_limit;
 
-       ipv6_addr_copy(&hdr->saddr, saddr);
-       ipv6_addr_copy(&hdr->daddr, daddr);
+       hdr->saddr = *saddr;
+       hdr->daddr = *daddr;
 
        return 0;
 }
@@ -327,10 +329,11 @@ static int ip6_forward_proxy_check(struct sk_buff *skb)
 {
        struct ipv6hdr *hdr = ipv6_hdr(skb);
        u8 nexthdr = hdr->nexthdr;
+       __be16 frag_off;
        int offset;
 
        if (ipv6_ext_hdr(nexthdr)) {
-               offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr);
+               offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr, &frag_off);
                if (offset < 0)
                        return 0;
        } else
@@ -385,6 +388,7 @@ int ip6_forward(struct sk_buff *skb)
        struct ipv6hdr *hdr = ipv6_hdr(skb);
        struct inet6_skb_parm *opt = IP6CB(skb);
        struct net *net = dev_net(dst->dev);
+       struct neighbour *n;
        u32 mtu;
 
        if (net->ipv6.devconf_all->forwarding == 0)
@@ -459,11 +463,10 @@ int ip6_forward(struct sk_buff *skb)
           send redirects to source routed frames.
           We don't send redirects to frames decapsulated from IPsec.
         */
-       if (skb->dev == dst->dev && dst->neighbour && opt->srcrt == 0 &&
-           !skb_sec_path(skb)) {
+       n = dst_get_neighbour_noref(dst);
+       if (skb->dev == dst->dev && n && opt->srcrt == 0 && !skb_sec_path(skb)) {
                struct in6_addr *target = NULL;
                struct rt6_info *rt;
-               struct neighbour *n = dst->neighbour;
 
                /*
                 *      incoming and outgoing devices are the same
@@ -596,6 +599,31 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
        return offset;
 }
 
+void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
+{
+       static atomic_t ipv6_fragmentation_id;
+       int old, new;
+
+       if (rt && !(rt->dst.flags & DST_NOPEER)) {
+               struct inet_peer *peer;
+
+               if (!rt->rt6i_peer)
+                       rt6_bind_peer(rt, 1);
+               peer = rt->rt6i_peer;
+               if (peer) {
+                       fhdr->identification = htonl(inet_getid(peer, 0));
+                       return;
+               }
+       }
+       do {
+               old = atomic_read(&ipv6_fragmentation_id);
+               new = old + 1;
+               if (!new)
+                       new = 1;
+       } while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old);
+       fhdr->identification = htonl(new);
+}
+
 int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 {
        struct sk_buff *frag;
@@ -604,6 +632,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
        struct ipv6hdr *tmp_hdr;
        struct frag_hdr *fh;
        unsigned int mtu, hlen, left, len;
+       int hroom, troom;
        __be32 frag_id = 0;
        int ptr, offset = 0, err=0;
        u8 *prevhdr, nexthdr = 0;
@@ -680,7 +709,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
                skb_reset_network_header(skb);
                memcpy(skb_network_header(skb), tmp_hdr, hlen);
 
-               ipv6_select_ident(fh);
+               ipv6_select_ident(fh, rt);
                fh->nexthdr = nexthdr;
                fh->reserved = 0;
                fh->frag_off = htons(IP6_MF);
@@ -770,6 +799,8 @@ slow_path:
         */
 
        *prevhdr = NEXTHDR_FRAGMENT;
+       hroom = LL_RESERVED_SPACE(rt->dst.dev);
+       troom = rt->dst.dev->needed_tailroom;
 
        /*
         *      Keep copying data until we run out.
@@ -779,7 +810,7 @@ slow_path:
                /* IF: it doesn't fit, use 'mtu' - the data space left */
                if (len > mtu)
                        len = mtu;
-               /* IF: we are not sending upto and including the packet end
+               /* IF: we are not sending up to and including the packet end
                   then align the next start on an eight byte boundary */
                if (len < left) {
                        len &= ~7;
@@ -788,7 +819,8 @@ slow_path:
                 *      Allocate buffer.
                 */
 
-               if ((frag = alloc_skb(len+hlen+sizeof(struct frag_hdr)+LL_ALLOCATED_SPACE(rt->dst.dev), GFP_ATOMIC)) == NULL) {
+               if ((frag = alloc_skb(len + hlen + sizeof(struct frag_hdr) +
+                                     hroom + troom, GFP_ATOMIC)) == NULL) {
                        NETDEBUG(KERN_INFO "IPv6: frag: no memory for new fragment!\n");
                        IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
                                      IPSTATS_MIB_FRAGFAILS);
@@ -801,7 +833,7 @@ slow_path:
                 */
 
                ip6_copy_metadata(frag, skb);
-               skb_reserve(frag, LL_RESERVED_SPACE(rt->dst.dev));
+               skb_reserve(frag, hroom);
                skb_put(frag, len + hlen + sizeof(struct frag_hdr));
                skb_reset_network_header(frag);
                fh = (struct frag_hdr *)(skb_network_header(frag) + hlen);
@@ -826,7 +858,7 @@ slow_path:
                fh->nexthdr = nexthdr;
                fh->reserved = 0;
                if (!frag_id) {
-                       ipv6_select_ident(fh);
+                       ipv6_select_ident(fh, rt);
                        frag_id = fh->identification;
                } else
                        fh->identification = frag_id;
@@ -869,9 +901,9 @@ fail:
        return err;
 }
 
-static inline int ip6_rt_check(struct rt6key *rt_key,
-                              struct in6_addr *fl_addr,
-                              struct in6_addr *addr_cache)
+static inline int ip6_rt_check(const struct rt6key *rt_key,
+                              const struct in6_addr *fl_addr,
+                              const struct in6_addr *addr_cache)
 {
        return (rt_key->plen != 128 || !ipv6_addr_equal(fl_addr, &rt_key->addr)) &&
                (addr_cache == NULL || !ipv6_addr_equal(fl_addr, addr_cache));
@@ -879,7 +911,7 @@ static inline int ip6_rt_check(struct rt6key *rt_key,
 
 static struct dst_entry *ip6_sk_dst_check(struct sock *sk,
                                          struct dst_entry *dst,
-                                         struct flowi *fl)
+                                         const struct flowi6 *fl6)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct rt6_info *rt = (struct rt6_info *)dst;
@@ -904,11 +936,11 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk,
         *    sockets.
         * 2. oif also should be the same.
         */
-       if (ip6_rt_check(&rt->rt6i_dst, &fl->fl6_dst, np->daddr_cache) ||
+       if (ip6_rt_check(&rt->rt6i_dst, &fl6->daddr, np->daddr_cache) ||
 #ifdef CONFIG_IPV6_SUBTREES
-           ip6_rt_check(&rt->rt6i_src, &fl->fl6_src, np->saddr_cache) ||
+           ip6_rt_check(&rt->rt6i_src, &fl6->saddr, np->saddr_cache) ||
 #endif
-           (fl->oif && fl->oif != dst->dev->ifindex)) {
+           (fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex)) {
                dst_release(dst);
                dst = NULL;
        }
@@ -918,22 +950,25 @@ out:
 }
 
 static int ip6_dst_lookup_tail(struct sock *sk,
-                              struct dst_entry **dst, struct flowi *fl)
+                              struct dst_entry **dst, struct flowi6 *fl6)
 {
-       int err;
        struct net *net = sock_net(sk);
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+       struct neighbour *n;
+#endif
+       int err;
 
        if (*dst == NULL)
-               *dst = ip6_route_output(net, sk, fl);
+               *dst = ip6_route_output(net, sk, fl6);
 
        if ((err = (*dst)->error))
                goto out_err_release;
 
-       if (ipv6_addr_any(&fl->fl6_src)) {
-               err = ipv6_dev_get_saddr(net, ip6_dst_idev(*dst)->dev,
-                                        &fl->fl6_dst,
-                                        sk ? inet6_sk(sk)->srcprefs : 0,
-                                        &fl->fl6_src);
+       if (ipv6_addr_any(&fl6->saddr)) {
+               struct rt6_info *rt = (struct rt6_info *) *dst;
+               err = ip6_route_get_saddr(net, rt, &fl6->daddr,
+                                         sk ? inet6_sk(sk)->srcprefs : 0,
+                                         &fl6->saddr);
                if (err)
                        goto out_err_release;
        }
@@ -947,12 +982,15 @@ static int ip6_dst_lookup_tail(struct sock *sk,
         * dst entry and replace it instead with the
         * dst entry of the nexthop router
         */
-       if ((*dst)->neighbour && !((*dst)->neighbour->nud_state & NUD_VALID)) {
+       rcu_read_lock();
+       n = dst_get_neighbour_noref(*dst);
+       if (n && !(n->nud_state & NUD_VALID)) {
                struct inet6_ifaddr *ifp;
-               struct flowi fl_gw;
+               struct flowi6 fl_gw6;
                int redirect;
 
-               ifp = ipv6_get_ifaddr(net, &fl->fl6_src,
+               rcu_read_unlock();
+               ifp = ipv6_get_ifaddr(net, &fl6->saddr,
                                      (*dst)->dev, 1);
 
                redirect = (ifp && ifp->flags & IFA_F_OPTIMISTIC);
@@ -965,12 +1003,14 @@ static int ip6_dst_lookup_tail(struct sock *sk,
                         * default router instead
                         */
                        dst_release(*dst);
-                       memcpy(&fl_gw, fl, sizeof(struct flowi));
-                       memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
-                       *dst = ip6_route_output(net, sk, &fl_gw);
+                       memcpy(&fl_gw6, fl6, sizeof(struct flowi6));
+                       memset(&fl_gw6.daddr, 0, sizeof(struct in6_addr));
+                       *dst = ip6_route_output(net, sk, &fl_gw6);
                        if ((err = (*dst)->error))
                                goto out_err_release;
                }
+       } else {
+               rcu_read_unlock();
        }
 #endif
 
@@ -988,23 +1028,23 @@ out_err_release:
  *     ip6_dst_lookup - perform route lookup on flow
  *     @sk: socket which provides route info
  *     @dst: pointer to dst_entry * for result
- *     @fl: flow to lookup
+ *     @fl6: flow to lookup
  *
  *     This function performs a route lookup on the given flow.
  *
  *     It returns zero on success, or a standard errno code on error.
  */
-int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl)
+int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi6 *fl6)
 {
        *dst = NULL;
-       return ip6_dst_lookup_tail(sk, dst, fl);
+       return ip6_dst_lookup_tail(sk, dst, fl6);
 }
 EXPORT_SYMBOL_GPL(ip6_dst_lookup);
 
 /**
  *     ip6_dst_lookup_flow - perform route lookup on flow with ipsec
  *     @sk: socket which provides route info
- *     @fl: flow to lookup
+ *     @fl6: flow to lookup
  *     @final_dst: final destination address for ipsec lookup
  *     @can_sleep: we are in a sleepable context
  *
@@ -1013,38 +1053,29 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup);
  *     It returns a valid dst pointer on success, or a pointer encoded
  *     error code.
  */
-struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi *fl,
+struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
                                      const struct in6_addr *final_dst,
                                      bool can_sleep)
 {
        struct dst_entry *dst = NULL;
        int err;
 
-       err = ip6_dst_lookup_tail(sk, &dst, fl);
+       err = ip6_dst_lookup_tail(sk, &dst, fl6);
        if (err)
                return ERR_PTR(err);
        if (final_dst)
-               ipv6_addr_copy(&fl->fl6_dst, final_dst);
-       if (can_sleep) {
-               fl->flags |= FLOWI_FLAG_CAN_SLEEP;
-               err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, 0);
-               if (err == -EREMOTE)
-                       return ip6_dst_blackhole(sock_net(sk), dst);
-               if (err)
-                       return ERR_PTR(err);
-       } else {
-               err = xfrm_lookup(sock_net(sk), &dst, fl, sk, 0);
-               if (err)
-                       return ERR_PTR(err);
-       }
-       return dst;
+               fl6->daddr = *final_dst;
+       if (can_sleep)
+               fl6->flowi6_flags |= FLOWI_FLAG_CAN_SLEEP;
+
+       return xfrm_lookup(sock_net(sk), dst, flowi6_to_flowi(fl6), sk, 0);
 }
 EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow);
 
 /**
  *     ip6_sk_dst_lookup_flow - perform socket cached route lookup on flow
  *     @sk: socket which provides the dst cache and route info
- *     @fl: flow to lookup
+ *     @fl6: flow to lookup
  *     @final_dst: final destination address for ipsec lookup
  *     @can_sleep: we are in a sleepable context
  *
@@ -1056,33 +1087,24 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow);
  *     It returns a valid dst pointer on success, or a pointer encoded
  *     error code.
  */
-struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi *fl,
+struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
                                         const struct in6_addr *final_dst,
                                         bool can_sleep)
 {
        struct dst_entry *dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie);
        int err;
 
-       dst = ip6_sk_dst_check(sk, dst, fl);
+       dst = ip6_sk_dst_check(sk, dst, fl6);
 
-       err = ip6_dst_lookup_tail(sk, &dst, fl);
+       err = ip6_dst_lookup_tail(sk, &dst, fl6);
        if (err)
                return ERR_PTR(err);
        if (final_dst)
-               ipv6_addr_copy(&fl->fl6_dst, final_dst);
-       if (can_sleep) {
-               fl->flags |= FLOWI_FLAG_CAN_SLEEP;
-               err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, 0);
-               if (err == -EREMOTE)
-                       return ip6_dst_blackhole(sock_net(sk), dst);
-               if (err)
-                       return ERR_PTR(err);
-       } else {
-               err = xfrm_lookup(sock_net(sk), &dst, fl, sk, 0);
-               if (err)
-                       return ERR_PTR(err);
-       }
-       return dst;
+               fl6->daddr = *final_dst;
+       if (can_sleep)
+               fl6->flowi6_flags |= FLOWI_FLAG_CAN_SLEEP;
+
+       return xfrm_lookup(sock_net(sk), dst, flowi6_to_flowi(fl6), sk, 0);
 }
 EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow);
 
@@ -1090,7 +1112,8 @@ static inline int ip6_ufo_append_data(struct sock *sk,
                        int getfrag(void *from, char *to, int offset, int len,
                        int odd, struct sk_buff *skb),
                        void *from, int length, int hh_len, int fragheaderlen,
-                       int transhdrlen, int mtu,unsigned int flags)
+                       int transhdrlen, int mtu,unsigned int flags,
+                       struct rt6_info *rt)
 
 {
        struct sk_buff *skb;
@@ -1105,7 +1128,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
                        hh_len + fragheaderlen + transhdrlen + 20,
                        (flags & MSG_DONTWAIT), &err);
                if (skb == NULL)
-                       return -ENOMEM;
+                       return err;
 
                /* reserve space for Hardware header */
                skb_reserve(skb, hh_len);
@@ -1134,7 +1157,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
                skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
                                             sizeof(struct frag_hdr)) & ~7;
                skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
-               ipv6_select_ident(&fhdr);
+               ipv6_select_ident(&fhdr, rt);
                skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
                __skb_queue_tail(&sk->sk_write_queue, skb);
 
@@ -1163,14 +1186,16 @@ static inline struct ipv6_rt_hdr *ip6_rthdr_dup(struct ipv6_rt_hdr *src,
 int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
        int offset, int len, int odd, struct sk_buff *skb),
        void *from, int length, int transhdrlen,
-       int hlimit, int tclass, struct ipv6_txoptions *opt, struct flowi *fl,
+       int hlimit, int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6,
        struct rt6_info *rt, unsigned int flags, int dontfrag)
 {
        struct inet_sock *inet = inet_sk(sk);
        struct ipv6_pinfo *np = inet6_sk(sk);
+       struct inet_cork *cork;
        struct sk_buff *skb;
        unsigned int maxfraglen, fragheaderlen;
        int exthdrlen;
+       int dst_exthdrlen;
        int hh_len;
        int mtu;
        int copy;
@@ -1181,6 +1206,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 
        if (flags&MSG_PROBE)
                return 0;
+       cork = &inet->cork.base;
        if (skb_queue_empty(&sk->sk_write_queue)) {
                /*
                 * setup for corking
@@ -1220,33 +1246,34 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
                        /* need source address above miyazawa*/
                }
                dst_hold(&rt->dst);
-               inet->cork.dst = &rt->dst;
-               inet->cork.fl = *fl;
+               cork->dst = &rt->dst;
+               inet->cork.fl.u.ip6 = *fl6;
                np->cork.hop_limit = hlimit;
                np->cork.tclass = tclass;
                mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
-                     rt->dst.dev->mtu : dst_mtu(rt->dst.path);
+                     rt->dst.dev->mtu : dst_mtu(&rt->dst);
                if (np->frag_size < mtu) {
                        if (np->frag_size)
                                mtu = np->frag_size;
                }
-               inet->cork.fragsize = mtu;
+               cork->fragsize = mtu;
                if (dst_allfrag(rt->dst.path))
-                       inet->cork.flags |= IPCORK_ALLFRAG;
-               inet->cork.length = 0;
+                       cork->flags |= IPCORK_ALLFRAG;
+               cork->length = 0;
                sk->sk_sndmsg_page = NULL;
                sk->sk_sndmsg_off = 0;
-               exthdrlen = rt->dst.header_len + (opt ? opt->opt_flen : 0) -
-                           rt->rt6i_nfheader_len;
+               exthdrlen = (opt ? opt->opt_flen : 0) - rt->rt6i_nfheader_len;
                length += exthdrlen;
                transhdrlen += exthdrlen;
+               dst_exthdrlen = rt->dst.header_len;
        } else {
-               rt = (struct rt6_info *)inet->cork.dst;
-               fl = &inet->cork.fl;
+               rt = (struct rt6_info *)cork->dst;
+               fl6 = &inet->cork.fl.u.ip6;
                opt = np->cork.opt;
                transhdrlen = 0;
                exthdrlen = 0;
-               mtu = inet->cork.fragsize;
+               dst_exthdrlen = 0;
+               mtu = cork->fragsize;
        }
 
        hh_len = LL_RESERVED_SPACE(rt->dst.dev);
@@ -1256,8 +1283,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
        maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - sizeof(struct frag_hdr);
 
        if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) {
-               if (inet->cork.length + length > sizeof(struct ipv6hdr) + IPV6_MAXPLEN - fragheaderlen) {
-                       ipv6_local_error(sk, EMSGSIZE, fl, mtu-exthdrlen);
+               if (cork->length + length > sizeof(struct ipv6hdr) + IPV6_MAXPLEN - fragheaderlen) {
+                       ipv6_local_error(sk, EMSGSIZE, fl6, mtu-exthdrlen);
                        return -EMSGSIZE;
                }
        }
@@ -1285,11 +1312,11 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
         * --yoshfuji
         */
 
-       inet->cork.length += length;
+       cork->length += length;
        if (length > mtu) {
                int proto = sk->sk_protocol;
                if (dontfrag && (proto == IPPROTO_UDP || proto == IPPROTO_RAW)){
-                       ipv6_local_rxpmtu(sk, fl, mtu-exthdrlen);
+                       ipv6_local_rxpmtu(sk, fl6, mtu-exthdrlen);
                        return -EMSGSIZE;
                }
 
@@ -1298,7 +1325,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 
                        err = ip6_ufo_append_data(sk, getfrag, from, length,
                                                  hh_len, fragheaderlen,
-                                                 transhdrlen, mtu, flags);
+                                                 transhdrlen, mtu, flags, rt);
                        if (err)
                                goto error;
                        return 0;
@@ -1310,7 +1337,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 
        while (length > 0) {
                /* Check if the remaining data fits into current packet. */
-               copy = (inet->cork.length <= mtu && !(inet->cork.flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - skb->len;
+               copy = (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - skb->len;
                if (copy < length)
                        copy = maxfraglen - skb->len;
 
@@ -1335,7 +1362,7 @@ alloc_new_skb:
                         * we know we need more fragment(s).
                         */
                        datalen = length + fraggap;
-                       if (datalen > (inet->cork.length <= mtu && !(inet->cork.flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
+                       if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
                                datalen = maxfraglen - fragheaderlen;
 
                        fraglen = datalen + fragheaderlen;
@@ -1345,6 +1372,8 @@ alloc_new_skb:
                        else
                                alloclen = datalen + fragheaderlen;
 
+                       alloclen += dst_exthdrlen;
+
                        /*
                         * The last fragment gets additional space at tail.
                         * Note: we overallocate on fragments with MSG_MODE
@@ -1396,9 +1425,9 @@ alloc_new_skb:
                        /*
                         *      Find where to start putting bytes
                         */
-                       data = skb_put(skb, fraglen);
-                       skb_set_network_header(skb, exthdrlen);
-                       data += fragheaderlen;
+                       data = skb_put(skb, fraglen + dst_exthdrlen);
+                       skb_set_network_header(skb, exthdrlen + dst_exthdrlen);
+                       data += fragheaderlen + dst_exthdrlen;
                        skb->transport_header = (skb->network_header +
                                                 fragheaderlen);
                        if (fraggap) {
@@ -1411,6 +1440,7 @@ alloc_new_skb:
                                pskb_trim_unique(skb_prev, maxfraglen);
                        }
                        copy = datalen - transhdrlen - fraggap;
+
                        if (copy < 0) {
                                err = -EINVAL;
                                kfree_skb(skb);
@@ -1425,6 +1455,7 @@ alloc_new_skb:
                        length -= datalen - fraggap;
                        transhdrlen = 0;
                        exthdrlen = 0;
+                       dst_exthdrlen = 0;
                        csummode = CHECKSUM_NONE;
 
                        /*
@@ -1457,13 +1488,13 @@ alloc_new_skb:
                        if (page && (left = PAGE_SIZE - off) > 0) {
                                if (copy >= left)
                                        copy = left;
-                               if (page != frag->page) {
+                               if (page != skb_frag_page(frag)) {
                                        if (i == MAX_SKB_FRAGS) {
                                                err = -EMSGSIZE;
                                                goto error;
                                        }
-                                       get_page(page);
                                        skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
+                                       skb_frag_ref(skb, i);
                                        frag = &skb_shinfo(skb)->frags[i];
                                }
                        } else if(i < MAX_SKB_FRAGS) {
@@ -1483,12 +1514,14 @@ alloc_new_skb:
                                err = -EMSGSIZE;
                                goto error;
                        }
-                       if (getfrag(from, page_address(frag->page)+frag->page_offset+frag->size, offset, copy, skb->len, skb) < 0) {
+                       if (getfrag(from,
+                                   skb_frag_address(frag) + skb_frag_size(frag),
+                                   offset, copy, skb->len, skb) < 0) {
                                err = -EFAULT;
                                goto error;
                        }
                        sk->sk_sndmsg_off += copy;
-                       frag->size += copy;
+                       skb_frag_size_add(frag, copy);
                        skb->len += copy;
                        skb->data_len += copy;
                        skb->truesize += copy;
@@ -1499,7 +1532,7 @@ alloc_new_skb:
        }
        return 0;
 error:
-       inet->cork.length -= length;
+       cork->length -= length;
        IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
        return err;
 }
@@ -1515,10 +1548,10 @@ static void ip6_cork_release(struct inet_sock *inet, struct ipv6_pinfo *np)
                np->cork.opt = NULL;
        }
 
-       if (inet->cork.dst) {
-               dst_release(inet->cork.dst);
-               inet->cork.dst = NULL;
-               inet->cork.flags &= ~IPCORK_ALLFRAG;
+       if (inet->cork.base.dst) {
+               dst_release(inet->cork.base.dst);
+               inet->cork.base.dst = NULL;
+               inet->cork.base.flags &= ~IPCORK_ALLFRAG;
        }
        memset(&inet->cork.fl, 0, sizeof(inet->cork.fl));
 }
@@ -1533,9 +1566,9 @@ int ip6_push_pending_frames(struct sock *sk)
        struct net *net = sock_net(sk);
        struct ipv6hdr *hdr;
        struct ipv6_txoptions *opt = np->cork.opt;
-       struct rt6_info *rt = (struct rt6_info *)inet->cork.dst;
-       struct flowi *fl = &inet->cork.fl;
-       unsigned char proto = fl->proto;
+       struct rt6_info *rt = (struct rt6_info *)inet->cork.base.dst;
+       struct flowi6 *fl6 = &inet->cork.fl.u.ip6;
+       unsigned char proto = fl6->flowi6_proto;
        int err = 0;
 
        if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL)
@@ -1560,7 +1593,7 @@ int ip6_push_pending_frames(struct sock *sk)
        if (np->pmtudisc < IPV6_PMTUDISC_DO)
                skb->local_df = 1;
 
-       ipv6_addr_copy(final_dst, &fl->fl6_dst);
+       *final_dst = fl6->daddr;
        __skb_pull(skb, skb_network_header_len(skb));
        if (opt && opt->opt_flen)
                ipv6_push_frag_opts(skb, opt, &proto);
@@ -1571,13 +1604,13 @@ int ip6_push_pending_frames(struct sock *sk)
        skb_reset_network_header(skb);
        hdr = ipv6_hdr(skb);
 
-       *(__be32*)hdr = fl->fl6_flowlabel |
+       *(__be32*)hdr = fl6->flowlabel |
                     htonl(0x60000000 | ((int)np->cork.tclass << 20));
 
        hdr->hop_limit = np->cork.hop_limit;
        hdr->nexthdr = proto;
-       ipv6_addr_copy(&hdr->saddr, &fl->fl6_src);
-       ipv6_addr_copy(&hdr->daddr, final_dst);
+       hdr->saddr = fl6->saddr;
+       hdr->daddr = *final_dst;
 
        skb->priority = sk->sk_priority;
        skb->mark = sk->sk_mark;