]> Pileus Git - ~andy/linux/blobdiff - net/ipv6/ip6_output.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[~andy/linux] / net / ipv6 / ip6_output.c
index 4acdb63495dbe2484de9d278b810401e6d26fc6a..d1de9560c4217925b19666d811bb17378e550a39 100644 (file)
@@ -336,7 +336,8 @@ int ip6_forward(struct sk_buff *skb)
                goto drop;
 
        if (!xfrm6_policy_check(NULL, XFRM_POLICY_FWD, skb)) {
-               IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_INDISCARDS);
+               IP6_INC_STATS_BH(net, ip6_dst_idev(dst),
+                                IPSTATS_MIB_INDISCARDS);
                goto drop;
        }
 
@@ -370,8 +371,8 @@ int ip6_forward(struct sk_buff *skb)
                /* Force OUTPUT device used as source address */
                skb->dev = dst->dev;
                icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, 0);
-               IP6_INC_STATS_BH(net,
-                                ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS);
+               IP6_INC_STATS_BH(net, ip6_dst_idev(dst),
+                                IPSTATS_MIB_INHDRERRORS);
 
                kfree_skb(skb);
                return -ETIMEDOUT;
@@ -384,14 +385,15 @@ int ip6_forward(struct sk_buff *skb)
                if (proxied > 0)
                        return ip6_input(skb);
                else if (proxied < 0) {
-                       IP6_INC_STATS(net, ip6_dst_idev(dst),
-                                     IPSTATS_MIB_INDISCARDS);
+                       IP6_INC_STATS_BH(net, ip6_dst_idev(dst),
+                                        IPSTATS_MIB_INDISCARDS);
                        goto drop;
                }
        }
 
        if (!xfrm6_route_forward(skb)) {
-               IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_INDISCARDS);
+               IP6_INC_STATS_BH(net, ip6_dst_idev(dst),
+                                IPSTATS_MIB_INDISCARDS);
                goto drop;
        }
        dst = skb_dst(skb);
@@ -448,16 +450,17 @@ int ip6_forward(struct sk_buff *skb)
                /* Again, force OUTPUT device used as source address */
                skb->dev = dst->dev;
                icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
-               IP6_INC_STATS_BH(net,
-                                ip6_dst_idev(dst), IPSTATS_MIB_INTOOBIGERRORS);
-               IP6_INC_STATS_BH(net,
-                                ip6_dst_idev(dst), IPSTATS_MIB_FRAGFAILS);
+               IP6_INC_STATS_BH(net, ip6_dst_idev(dst),
+                                IPSTATS_MIB_INTOOBIGERRORS);
+               IP6_INC_STATS_BH(net, ip6_dst_idev(dst),
+                                IPSTATS_MIB_FRAGFAILS);
                kfree_skb(skb);
                return -EMSGSIZE;
        }
 
        if (skb_cow(skb, dst->dev->hard_header_len)) {
-               IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTDISCARDS);
+               IP6_INC_STATS_BH(net, ip6_dst_idev(dst),
+                                IPSTATS_MIB_OUTDISCARDS);
                goto drop;
        }
 
@@ -938,7 +941,6 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup);
  *     @sk: socket which provides route info
  *     @fl6: flow to lookup
  *     @final_dst: final destination address for ipsec lookup
- *     @can_sleep: we are in a sleepable context
  *
  *     This function performs a route lookup on the given flow.
  *
@@ -946,8 +948,7 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup);
  *     error code.
  */
 struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
-                                     const struct in6_addr *final_dst,
-                                     bool can_sleep)
+                                     const struct in6_addr *final_dst)
 {
        struct dst_entry *dst = NULL;
        int err;
@@ -957,8 +958,6 @@ struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
                return ERR_PTR(err);
        if (final_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);
 }
@@ -969,7 +968,6 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow);
  *     @sk: socket which provides the dst cache and route info
  *     @fl6: flow to lookup
  *     @final_dst: final destination address for ipsec lookup
- *     @can_sleep: we are in a sleepable context
  *
  *     This function performs a route lookup on the given flow with the
  *     possibility of using the cached route in the socket if it is valid.
@@ -980,8 +978,7 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow);
  *     error code.
  */
 struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
-                                        const struct in6_addr *final_dst,
-                                        bool can_sleep)
+                                        const struct in6_addr *final_dst)
 {
        struct dst_entry *dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie);
        int err;
@@ -993,8 +990,6 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
                return ERR_PTR(err);
        if (final_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);
 }
@@ -1162,10 +1157,10 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
                np->cork.hop_limit = hlimit;
                np->cork.tclass = tclass;
                if (rt->dst.flags & DST_XFRM_TUNNEL)
-                       mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+                       mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ?
                              rt->dst.dev->mtu : dst_mtu(&rt->dst);
                else
-                       mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+                       mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ?
                              rt->dst.dev->mtu : dst_mtu(rt->dst.path);
                if (np->frag_size < mtu) {
                        if (np->frag_size)
@@ -1193,11 +1188,35 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 
        fragheaderlen = sizeof(struct ipv6hdr) + rt->rt6i_nfheader_len +
                        (opt ? opt->opt_nflen : 0);
-       maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - sizeof(struct frag_hdr);
+       maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen -
+                    sizeof(struct frag_hdr);
 
        if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) {
-               if (cork->length + length > sizeof(struct ipv6hdr) + IPV6_MAXPLEN - fragheaderlen) {
-                       ipv6_local_error(sk, EMSGSIZE, fl6, mtu-exthdrlen);
+               unsigned int maxnonfragsize, headersize;
+
+               headersize = sizeof(struct ipv6hdr) +
+                            (opt ? opt->tot_len : 0) +
+                            (dst_allfrag(&rt->dst) ?
+                             sizeof(struct frag_hdr) : 0) +
+                            rt->rt6i_nfheader_len;
+
+               maxnonfragsize = (np->pmtudisc >= IPV6_PMTUDISC_DO) ?
+                                mtu : sizeof(struct ipv6hdr) + IPV6_MAXPLEN;
+
+               /* dontfrag active */
+               if ((cork->length + length > mtu - headersize) && dontfrag &&
+                   (sk->sk_protocol == IPPROTO_UDP ||
+                    sk->sk_protocol == IPPROTO_RAW)) {
+                       ipv6_local_rxpmtu(sk, fl6, mtu - headersize +
+                                                  sizeof(struct ipv6hdr));
+                       goto emsgsize;
+               }
+
+               if (cork->length + length > maxnonfragsize - headersize) {
+emsgsize:
+                       ipv6_local_error(sk, EMSGSIZE, fl6,
+                                        mtu - headersize +
+                                        sizeof(struct ipv6hdr));
                        return -EMSGSIZE;
                }
        }
@@ -1222,12 +1241,6 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
         * --yoshfuji
         */
 
-       if ((length > mtu) && dontfrag && (sk->sk_protocol == IPPROTO_UDP ||
-                                          sk->sk_protocol == IPPROTO_RAW)) {
-               ipv6_local_rxpmtu(sk, fl6, mtu-exthdrlen);
-               return -EMSGSIZE;
-       }
-
        skb = skb_peek_tail(&sk->sk_write_queue);
        cork->length += length;
        if (((length > mtu) ||
@@ -1267,7 +1280,7 @@ alloc_new_skb:
                        if (skb == NULL || skb_prev == NULL)
                                ip6_append_data_mtu(&mtu, &maxfraglen,
                                                    fragheaderlen, skb, rt,
-                                                   np->pmtudisc ==
+                                                   np->pmtudisc >=
                                                    IPV6_PMTUDISC_PROBE);
 
                        skb_prev = skb;