]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - net/ipv6/route.c
ipv6: move ip6_dst_hoplimit() into core kernel
[karo-tx-linux.git] / net / ipv6 / route.c
index bd5fd7054031d677f9ac111ef7d78c74b3d68b24..b770085ae36d167af19dffcafe8e504c13031659 100644 (file)
 #include <linux/sysctl.h>
 #endif
 
+enum rt6_nud_state {
+       RT6_NUD_FAIL_HARD = -2,
+       RT6_NUD_FAIL_SOFT = -1,
+       RT6_NUD_SUCCEED = 1
+};
+
 static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
                                    const struct in6_addr *dest);
 static struct dst_entry        *ip6_dst_check(struct dst_entry *dst, u32 cookie);
@@ -277,9 +283,8 @@ static inline struct rt6_info *ip6_dst_alloc(struct net *net,
 
                memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
                rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
-               rt->rt6i_genid = rt_genid(net);
+               rt->rt6i_genid = rt_genid_ipv6(net);
                INIT_LIST_HEAD(&rt->rt6i_siblings);
-               rt->rt6i_nsiblings = 0;
        }
        return rt;
 }
@@ -531,28 +536,29 @@ static inline int rt6_check_dev(struct rt6_info *rt, int oif)
        return 0;
 }
 
-static inline bool rt6_check_neigh(struct rt6_info *rt)
+static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
 {
        struct neighbour *neigh;
-       bool ret = false;
+       enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
 
        if (rt->rt6i_flags & RTF_NONEXTHOP ||
            !(rt->rt6i_flags & RTF_GATEWAY))
-               return true;
+               return RT6_NUD_SUCCEED;
 
        rcu_read_lock_bh();
        neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
        if (neigh) {
                read_lock(&neigh->lock);
                if (neigh->nud_state & NUD_VALID)
-                       ret = true;
+                       ret = RT6_NUD_SUCCEED;
 #ifdef CONFIG_IPV6_ROUTER_PREF
                else if (!(neigh->nud_state & NUD_FAILED))
-                       ret = true;
+                       ret = RT6_NUD_SUCCEED;
 #endif
                read_unlock(&neigh->lock);
-       } else if (IS_ENABLED(CONFIG_IPV6_ROUTER_PREF)) {
-               ret = true;
+       } else {
+               ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
+                     RT6_NUD_SUCCEED : RT6_NUD_FAIL_SOFT;
        }
        rcu_read_unlock_bh();
 
@@ -566,43 +572,52 @@ static int rt6_score_route(struct rt6_info *rt, int oif,
 
        m = rt6_check_dev(rt, oif);
        if (!m && (strict & RT6_LOOKUP_F_IFACE))
-               return -1;
+               return RT6_NUD_FAIL_HARD;
 #ifdef CONFIG_IPV6_ROUTER_PREF
        m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
 #endif
-       if (!rt6_check_neigh(rt) && (strict & RT6_LOOKUP_F_REACHABLE))
-               return -1;
+       if (strict & RT6_LOOKUP_F_REACHABLE) {
+               int n = rt6_check_neigh(rt);
+               if (n < 0)
+                       return n;
+       }
        return m;
 }
 
 static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
-                                  int *mpri, struct rt6_info *match)
+                                  int *mpri, struct rt6_info *match,
+                                  bool *do_rr)
 {
        int m;
+       bool match_do_rr = false;
 
        if (rt6_check_expired(rt))
                goto out;
 
        m = rt6_score_route(rt, oif, strict);
-       if (m < 0)
+       if (m == RT6_NUD_FAIL_SOFT && !IS_ENABLED(CONFIG_IPV6_ROUTER_PREF)) {
+               match_do_rr = true;
+               m = 0; /* lowest valid score */
+       } else if (m < 0) {
                goto out;
+       }
+
+       if (strict & RT6_LOOKUP_F_REACHABLE)
+               rt6_probe(rt);
 
        if (m > *mpri) {
-               if (strict & RT6_LOOKUP_F_REACHABLE)
-                       rt6_probe(match);
+               *do_rr = match_do_rr;
                *mpri = m;
                match = rt;
-       } else if (strict & RT6_LOOKUP_F_REACHABLE) {
-               rt6_probe(rt);
        }
-
 out:
        return match;
 }
 
 static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
                                     struct rt6_info *rr_head,
-                                    u32 metric, int oif, int strict)
+                                    u32 metric, int oif, int strict,
+                                    bool *do_rr)
 {
        struct rt6_info *rt, *match;
        int mpri = -1;
@@ -610,10 +625,10 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
        match = NULL;
        for (rt = rr_head; rt && rt->rt6i_metric == metric;
             rt = rt->dst.rt6_next)
-               match = find_match(rt, oif, strict, &mpri, match);
+               match = find_match(rt, oif, strict, &mpri, match, do_rr);
        for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
             rt = rt->dst.rt6_next)
-               match = find_match(rt, oif, strict, &mpri, match);
+               match = find_match(rt, oif, strict, &mpri, match, do_rr);
 
        return match;
 }
@@ -622,15 +637,16 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
 {
        struct rt6_info *match, *rt0;
        struct net *net;
+       bool do_rr = false;
 
        rt0 = fn->rr_ptr;
        if (!rt0)
                fn->rr_ptr = rt0 = fn->leaf;
 
-       match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
+       match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
+                            &do_rr);
 
-       if (!match &&
-           (strict & RT6_LOOKUP_F_REACHABLE)) {
+       if (do_rr) {
                struct rt6_info *next = rt0->dst.rt6_next;
 
                /* no entries matched; do round-robin */
@@ -1045,7 +1061,7 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
         * DST_OBSOLETE_FORCE_CHK which forces validation calls down
         * into this function always.
         */
-       if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev)))
+       if (rt->rt6i_genid != rt_genid_ipv6(dev_net(rt->dst.dev)))
                return NULL;
 
        if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
@@ -1080,10 +1096,13 @@ static void ip6_link_failure(struct sk_buff *skb)
 
        rt = (struct rt6_info *) skb_dst(skb);
        if (rt) {
-               if (rt->rt6i_flags & RTF_CACHE)
-                       rt6_update_expires(rt, 0);
-               else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
+               if (rt->rt6i_flags & RTF_CACHE) {
+                       dst_hold(&rt->dst);
+                       if (ip6_del_rt(rt))
+                               dst_free(&rt->dst);
+               } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) {
                        rt->rt6i_node->fn_sernum = -1;
+               }
        }
 }
 
@@ -1158,6 +1177,27 @@ void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
 }
 EXPORT_SYMBOL_GPL(ip6_redirect);
 
+void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
+                           u32 mark)
+{
+       const struct ipv6hdr *iph = ipv6_hdr(skb);
+       const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
+       struct dst_entry *dst;
+       struct flowi6 fl6;
+
+       memset(&fl6, 0, sizeof(fl6));
+       fl6.flowi6_oif = oif;
+       fl6.flowi6_mark = mark;
+       fl6.flowi6_flags = 0;
+       fl6.daddr = msg->dest;
+       fl6.saddr = iph->daddr;
+
+       dst = ip6_route_output(net, NULL, &fl6);
+       if (!dst->error)
+               rt6_do_redirect(dst, NULL, skb);
+       dst_release(dst);
+}
+
 void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
 {
        ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
@@ -1291,7 +1331,6 @@ static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
 
 static int ip6_dst_gc(struct dst_ops *ops)
 {
-       unsigned long now = jiffies;
        struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
        int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
        int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
@@ -1301,13 +1340,12 @@ static int ip6_dst_gc(struct dst_ops *ops)
        int entries;
 
        entries = dst_entries_get_fast(ops);
-       if (time_after(rt_last_gc + rt_min_interval, now) &&
+       if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
            entries <= rt_max_size)
                goto out;
 
        net->ipv6.ip6_rt_gc_expire++;
-       fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
-       net->ipv6.ip6_rt_last_gc = now;
+       fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, entries > rt_max_size);
        entries = dst_entries_get_slow(ops);
        if (entries < ops->gc_thresh)
                net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
@@ -1316,25 +1354,6 @@ out:
        return entries > rt_max_size;
 }
 
-int ip6_dst_hoplimit(struct dst_entry *dst)
-{
-       int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
-       if (hoplimit == 0) {
-               struct net_device *dev = dst->dev;
-               struct inet6_dev *idev;
-
-               rcu_read_lock();
-               idev = __in6_dev_get(dev);
-               if (idev)
-                       hoplimit = idev->cnf.hop_limit;
-               else
-                       hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
-               rcu_read_unlock();
-       }
-       return hoplimit;
-}
-EXPORT_SYMBOL(ip6_dst_hoplimit);
-
 /*
  *
  */
@@ -2807,7 +2826,7 @@ int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
        net = (struct net *)ctl->extra1;
        delay = net->ipv6.sysctl.flush_delay;
        proc_dointvec(ctl, write, buffer, lenp, ppos);
-       fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
+       fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
        return 0;
 }