ipv6: Only create RTF_CACHE routes after encountering pmtu exception
[firefly-linux-kernel-4.4.55.git] / net / ipv6 / route.c
index f199d6357b31a7e547222037fc2aa75bbadc5731..e7ae2430dfed805985e9390069b64c093a486cbb 100644 (file)
@@ -873,16 +873,13 @@ static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
                                      struct flowi6 *fl6, int flags)
 {
        struct fib6_node *fn, *saved_fn;
-       struct rt6_info *rt, *nrt;
+       struct rt6_info *rt;
        int strict = 0;
-       int attempts = 3;
-       int err;
 
        strict |= flags & RT6_LOOKUP_F_IFACE;
        if (net->ipv6.devconf_all->forwarding == 0)
                strict |= RT6_LOOKUP_F_REACHABLE;
 
-redo_fib6_lookup_lock:
        read_lock_bh(&table->tb6_lock);
 
        fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
@@ -901,46 +898,12 @@ redo_rt6_select:
                        strict &= ~RT6_LOOKUP_F_REACHABLE;
                        fn = saved_fn;
                        goto redo_rt6_select;
-               } else {
-                       dst_hold(&rt->dst);
-                       read_unlock_bh(&table->tb6_lock);
-                       goto out2;
                }
        }
 
        dst_hold(&rt->dst);
        read_unlock_bh(&table->tb6_lock);
 
-       if (rt->rt6i_flags & RTF_CACHE)
-               goto out2;
-
-       if (!rt6_is_gw_or_nonexthop(rt) ||
-           !(rt->dst.flags & DST_HOST) || !(rt->rt6i_flags & RTF_LOCAL))
-               nrt = ip6_rt_cache_alloc(rt, &fl6->daddr, &fl6->saddr);
-       else
-               goto out2;
-
-       ip6_rt_put(rt);
-       rt = nrt ? : net->ipv6.ip6_null_entry;
-
-       dst_hold(&rt->dst);
-       if (nrt) {
-               err = ip6_ins_rt(nrt);
-               if (!err)
-                       goto out2;
-       }
-
-       if (--attempts <= 0)
-               goto out2;
-
-       /*
-        * Race condition! In the gap, when table->tb6_lock was
-        * released someone could insert this route.  Relookup.
-        */
-       ip6_rt_put(rt);
-       goto redo_fib6_lookup_lock;
-
-out2:
        rt6_dst_from_metrics_check(rt);
        rt->dst.lastuse = jiffies;
        rt->dst.__use++;
@@ -1113,24 +1076,63 @@ static void ip6_link_failure(struct sk_buff *skb)
        }
 }
 
-static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
-                              struct sk_buff *skb, u32 mtu)
+static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
+{
+       struct net *net = dev_net(rt->dst.dev);
+
+       rt->rt6i_flags |= RTF_MODIFIED;
+       rt->rt6i_pmtu = mtu;
+       rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
+}
+
+static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
+                                const struct ipv6hdr *iph, u32 mtu)
 {
        struct rt6_info *rt6 = (struct rt6_info *)dst;
 
-       dst_confirm(dst);
-       if (mtu < dst_mtu(dst) && (rt6->rt6i_flags & RTF_CACHE)) {
-               struct net *net = dev_net(dst->dev);
+       if (rt6->rt6i_flags & RTF_LOCAL)
+               return;
 
-               rt6->rt6i_flags |= RTF_MODIFIED;
-               if (mtu < IPV6_MIN_MTU)
-                       mtu = IPV6_MIN_MTU;
+       dst_confirm(dst);
+       mtu = max_t(u32, mtu, IPV6_MIN_MTU);
+       if (mtu >= dst_mtu(dst))
+               return;
 
-               rt6->rt6i_pmtu = mtu;
-               rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
+       if (rt6->rt6i_flags & RTF_CACHE) {
+               rt6_do_update_pmtu(rt6, mtu);
+       } else {
+               const struct in6_addr *daddr, *saddr;
+               struct rt6_info *nrt6;
+
+               if (iph) {
+                       daddr = &iph->daddr;
+                       saddr = &iph->saddr;
+               } else if (sk) {
+                       daddr = &sk->sk_v6_daddr;
+                       saddr = &inet6_sk(sk)->saddr;
+               } else {
+                       return;
+               }
+               nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr);
+               if (nrt6) {
+                       rt6_do_update_pmtu(nrt6, mtu);
+
+                       /* ip6_ins_rt(nrt6) will bump the
+                        * rt6->rt6i_node->fn_sernum
+                        * which will fail the next rt6_check() and
+                        * invalidate the sk->sk_dst_cache.
+                        */
+                       ip6_ins_rt(nrt6);
+               }
        }
 }
 
+static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
+                              struct sk_buff *skb, u32 mtu)
+{
+       __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
+}
+
 void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
                     int oif, u32 mark)
 {
@@ -1147,7 +1149,7 @@ void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
 
        dst = ip6_route_output(net, NULL, &fl6);
        if (!dst->error)
-               ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu));
+               __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
        dst_release(dst);
 }
 EXPORT_SYMBOL_GPL(ip6_update_pmtu);