Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
[cascardo/linux.git] / net / ipv6 / ip6_output.c
index bc09cb9..d5f7716 100644 (file)
@@ -105,7 +105,7 @@ static int ip6_finish_output2(struct sock *sk, struct sk_buff *skb)
        }
 
        rcu_read_lock_bh();
-       nexthop = rt6_nexthop((struct rt6_info *)dst);
+       nexthop = rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr);
        neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
        if (unlikely(!neigh))
                neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false);
@@ -459,7 +459,7 @@ int ip6_forward(struct sk_buff *skb)
                else
                        target = &hdr->daddr;
 
-               peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
+               peer = inet_getpeer_v6(net->ipv6.peers, &hdr->daddr, 1);
 
                /* Limit redirects both by destination (here)
                   and by source (inside ndisc_send_redirect)
@@ -551,7 +551,7 @@ int ip6_fragment(struct sock *sk, struct sk_buff *skb,
        struct frag_hdr *fh;
        unsigned int mtu, hlen, left, len;
        int hroom, troom;
-       __be32 frag_id = 0;
+       __be32 frag_id;
        int ptr, offset = 0, err = 0;
        u8 *prevhdr, nexthdr = 0;
        struct net *net = dev_net(skb_dst(skb)->dev);
@@ -564,18 +564,17 @@ int ip6_fragment(struct sock *sk, struct sk_buff *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.
         */
-       if (unlikely(!skb->ignore_df && skb->len > mtu) ||
-                    (IP6CB(skb)->frag_max_size &&
-                     IP6CB(skb)->frag_max_size > mtu)) {
-               if (skb->sk && dst_allfrag(skb_dst(skb)))
-                       sk_nocaps_add(skb->sk, NETIF_F_GSO_MASK);
+       if (unlikely(!skb->ignore_df && skb->len > mtu))
+               goto fail_toobig;
 
-               skb->dev = skb_dst(skb)->dev;
-               icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
-               IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
-                             IPSTATS_MIB_FRAGFAILS);
-               kfree_skb(skb);
-               return -EMSGSIZE;
+       if (IP6CB(skb)->frag_max_size) {
+               if (IP6CB(skb)->frag_max_size > mtu)
+                       goto fail_toobig;
+
+               /* don't send fragments larger than what we received */
+               mtu = IP6CB(skb)->frag_max_size;
+               if (mtu < IPV6_MIN_MTU)
+                       mtu = IPV6_MIN_MTU;
        }
 
        if (np && np->frag_size < mtu) {
@@ -584,6 +583,9 @@ int ip6_fragment(struct sock *sk, struct sk_buff *skb,
        }
        mtu -= hlen + sizeof(struct frag_hdr);
 
+       frag_id = ipv6_select_ident(net, &ipv6_hdr(skb)->daddr,
+                                   &ipv6_hdr(skb)->saddr);
+
        if (skb_has_frag_list(skb)) {
                int first_len = skb_pagelen(skb);
                struct sk_buff *frag2;
@@ -632,11 +634,10 @@ int ip6_fragment(struct sock *sk, struct sk_buff *skb,
                skb_reset_network_header(skb);
                memcpy(skb_network_header(skb), tmp_hdr, hlen);
 
-               ipv6_select_ident(net, fh, rt);
                fh->nexthdr = nexthdr;
                fh->reserved = 0;
                fh->frag_off = htons(IP6_MF);
-               frag_id = fh->identification;
+               fh->identification = frag_id;
 
                first_len = skb_pagelen(skb);
                skb->data_len = first_len - skb_headlen(skb);
@@ -778,11 +779,7 @@ slow_path:
                 */
                fh->nexthdr = nexthdr;
                fh->reserved = 0;
-               if (!frag_id) {
-                       ipv6_select_ident(net, fh, rt);
-                       frag_id = fh->identification;
-               } else
-                       fh->identification = frag_id;
+               fh->identification = frag_id;
 
                /*
                 *      Copy a block of the IP datagram.
@@ -815,6 +812,14 @@ slow_path:
        consume_skb(skb);
        return err;
 
+fail_toobig:
+       if (skb->sk && dst_allfrag(skb_dst(skb)))
+               sk_nocaps_add(skb->sk, NETIF_F_GSO_MASK);
+
+       skb->dev = skb_dst(skb)->dev;
+       icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+       err = -EMSGSIZE;
+
 fail:
        IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
                      IPSTATS_MIB_FRAGFAILS);
@@ -936,7 +941,8 @@ static int ip6_dst_lookup_tail(struct sock *sk,
         */
        rt = (struct rt6_info *) *dst;
        rcu_read_lock_bh();
-       n = __ipv6_neigh_lookup_noref(rt->dst.dev, rt6_nexthop(rt));
+       n = __ipv6_neigh_lookup_noref(rt->dst.dev,
+                                     rt6_nexthop(rt, &fl6->daddr));
        err = n && !(n->nud_state & NUD_VALID) ? -EINVAL : 0;
        rcu_read_unlock_bh();
 
@@ -1060,11 +1066,10 @@ static inline int ip6_ufo_append_data(struct sock *sk,
                        int odd, struct sk_buff *skb),
                        void *from, int length, int hh_len, int fragheaderlen,
                        int transhdrlen, int mtu, unsigned int flags,
-                       struct rt6_info *rt)
+                       const struct flowi6 *fl6)
 
 {
        struct sk_buff *skb;
-       struct frag_hdr fhdr;
        int err;
 
        /* There is support for UDP large send offload by network
@@ -1106,8 +1111,9 @@ 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(sock_net(sk), &fhdr, rt);
-       skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
+       skb_shinfo(skb)->ip6_frag_id = ipv6_select_ident(sock_net(sk),
+                                                        &fl6->daddr,
+                                                        &fl6->saddr);
 
 append:
        return skb_append_datato_frags(sk, skb, getfrag, from,
@@ -1332,7 +1338,7 @@ emsgsize:
            (sk->sk_type == SOCK_DGRAM)) {
                err = ip6_ufo_append_data(sk, queue, getfrag, from, length,
                                          hh_len, fragheaderlen,
-                                         transhdrlen, mtu, flags, rt);
+                                         transhdrlen, mtu, flags, fl6);
                if (err)
                        goto error;
                return 0;