netfilter: add nf_ipv6_ops hook to fix xt_addrtype with IPv6
[linux-3.10.git] / net / ipv6 / route.c
index 2839381..ad0aa6b 100644 (file)
@@ -151,19 +151,6 @@ static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
        return neigh_create(&nd_tbl, daddr, dst->dev);
 }
 
-static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev)
-{
-       struct neighbour *n = __ipv6_neigh_lookup(dev, &rt->rt6i_gateway);
-       if (!n) {
-               n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev);
-               if (IS_ERR(n))
-                       return PTR_ERR(n);
-       }
-       rt->n = n;
-
-       return 0;
-}
-
 static struct dst_ops ip6_dst_ops_template = {
        .family                 =       AF_INET6,
        .protocol               =       cpu_to_be16(ETH_P_IPV6),
@@ -300,9 +287,7 @@ static void ip6_dst_destroy(struct dst_entry *dst)
 {
        struct rt6_info *rt = (struct rt6_info *)dst;
        struct inet6_dev *idev = rt->rt6i_idev;
-
-       if (rt->n)
-               neigh_release(rt->n);
+       struct dst_entry *from = dst->from;
 
        if (!(rt->dst.flags & DST_HOST))
                dst_destroy_metrics_generic(dst);
@@ -312,8 +297,8 @@ static void ip6_dst_destroy(struct dst_entry *dst)
                in6_dev_put(idev);
        }
 
-       if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from)
-               dst_release(dst->from);
+       dst->from = NULL;
+       dst_release(from);
 
        if (rt6_has_peer(rt)) {
                struct inet_peer *peer = rt6_peer_ptr(rt);
@@ -354,11 +339,6 @@ static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
                                in6_dev_put(idev);
                        }
                }
-               if (rt->n && rt->n->dev == dev) {
-                       rt->n->dev = loopback_dev;
-                       dev_hold(loopback_dev);
-                       dev_put(dev);
-               }
        }
 }
 
@@ -498,28 +478,34 @@ static void rt6_probe(struct rt6_info *rt)
         * Router Reachability Probe MUST be rate-limited
         * to no more than one per minute.
         */
-       neigh = rt ? rt->n : NULL;
-       if (!neigh)
-               return;
-       write_lock_bh(&neigh->lock);
-       if (neigh->nud_state & NUD_VALID) {
-               write_unlock_bh(&neigh->lock);
+       if (!rt || !(rt->rt6i_flags & RTF_GATEWAY))
                return;
+       rcu_read_lock_bh();
+       neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
+       if (neigh) {
+               write_lock(&neigh->lock);
+               if (neigh->nud_state & NUD_VALID)
+                       goto out;
        }
-       if (!(neigh->nud_state & NUD_VALID) &&
+
+       if (!neigh ||
            time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
                struct in6_addr mcaddr;
                struct in6_addr *target;
 
-               neigh->updated = jiffies;
-               write_unlock_bh(&neigh->lock);
+               if (neigh) {
+                       neigh->updated = jiffies;
+                       write_unlock(&neigh->lock);
+               }
 
-               target = (struct in6_addr *)&neigh->primary_key;
+               target = (struct in6_addr *)&rt->rt6i_gateway;
                addrconf_addr_solict_mult(target, &mcaddr);
                ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
        } else {
-               write_unlock_bh(&neigh->lock);
+out:
+               write_unlock(&neigh->lock);
        }
+       rcu_read_unlock_bh();
 }
 #else
 static inline void rt6_probe(struct rt6_info *rt)
@@ -546,20 +532,24 @@ static inline bool rt6_check_neigh(struct rt6_info *rt)
        struct neighbour *neigh;
        bool ret = false;
 
-       neigh = rt->n;
        if (rt->rt6i_flags & RTF_NONEXTHOP ||
            !(rt->rt6i_flags & RTF_GATEWAY))
-               ret = true;
-       else if (neigh) {
-               read_lock_bh(&neigh->lock);
+               return true;
+
+       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;
 #ifdef CONFIG_IPV6_ROUTER_PREF
                else if (!(neigh->nud_state & NUD_FAILED))
                        ret = true;
 #endif
-               read_unlock_bh(&neigh->lock);
+               read_unlock(&neigh->lock);
        }
+       rcu_read_unlock_bh();
+
        return ret;
 }
 
@@ -835,8 +825,6 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
        rt = ip6_rt_copy(ort, daddr);
 
        if (rt) {
-               int attempts = !in_softirq();
-
                if (!(rt->rt6i_flags & RTF_GATEWAY)) {
                        if (ort->rt6i_dst.plen != 128 &&
                            ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
@@ -852,32 +840,6 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
                        rt->rt6i_src.plen = 128;
                }
 #endif
-
-       retry:
-               if (rt6_bind_neighbour(rt, rt->dst.dev)) {
-                       struct net *net = dev_net(rt->dst.dev);
-                       int saved_rt_min_interval =
-                               net->ipv6.sysctl.ip6_rt_gc_min_interval;
-                       int saved_rt_elasticity =
-                               net->ipv6.sysctl.ip6_rt_gc_elasticity;
-
-                       if (attempts-- > 0) {
-                               net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
-                               net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
-
-                               ip6_dst_gc(&net->ipv6.ip6_dst_ops);
-
-                               net->ipv6.sysctl.ip6_rt_gc_elasticity =
-                                       saved_rt_elasticity;
-                               net->ipv6.sysctl.ip6_rt_gc_min_interval =
-                                       saved_rt_min_interval;
-                               goto retry;
-                       }
-
-                       net_warn_ratelimited("Neighbour table overflow\n");
-                       dst_free(&rt->dst);
-                       return NULL;
-               }
        }
 
        return rt;
@@ -888,10 +850,8 @@ static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
 {
        struct rt6_info *rt = ip6_rt_copy(ort, daddr);
 
-       if (rt) {
+       if (rt)
                rt->rt6i_flags |= RTF_CACHE;
-               rt->n = neigh_clone(ort->n);
-       }
        return rt;
 }
 
@@ -1051,7 +1011,6 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori
 
                rt->rt6i_gateway = ort->rt6i_gateway;
                rt->rt6i_flags = ort->rt6i_flags;
-               rt6_clean_expires(rt);
                rt->rt6i_metric = 0;
 
                memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
@@ -1244,7 +1203,6 @@ static struct dst_entry *icmp6_dst_gc_list;
 static DEFINE_SPINLOCK(icmp6_dst_lock);
 
 struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
-                                 struct neighbour *neigh,
                                  struct flowi6 *fl6)
 {
        struct dst_entry *dst;
@@ -1262,20 +1220,8 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
                goto out;
        }
 
-       if (neigh)
-               neigh_hold(neigh);
-       else {
-               neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
-               if (IS_ERR(neigh)) {
-                       in6_dev_put(idev);
-                       dst_free(&rt->dst);
-                       return ERR_CAST(neigh);
-               }
-       }
-
        rt->dst.flags |= DST_HOST;
        rt->dst.output  = ip6_output;
-       rt->n = neigh;
        atomic_set(&rt->dst.__refcnt, 1);
        rt->rt6i_dst.addr = fl6->daddr;
        rt->rt6i_dst.plen = 128;
@@ -1584,12 +1530,6 @@ int ip6_route_add(struct fib6_config *cfg)
        } else
                rt->rt6i_prefsrc.plen = 0;
 
-       if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
-               err = rt6_bind_neighbour(rt, dev);
-               if (err)
-                       goto out;
-       }
-
        rt->rt6i_flags = cfg->fc_flags;
 
 install_route:
@@ -1703,7 +1643,6 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
        struct netevent_redirect netevent;
        struct rt6_info *rt, *nrt = NULL;
        struct ndisc_options ndopts;
-       struct neighbour *old_neigh;
        struct inet6_dev *in6_dev;
        struct neighbour *neigh;
        struct rd_msg *msg;
@@ -1776,11 +1715,6 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
        if (!neigh)
                return;
 
-       /* Duplicate redirect: silently ignore. */
-       old_neigh = rt->n;
-       if (neigh == old_neigh)
-               goto out;
-
        /*
         *      We have finally decided to accept it.
         */
@@ -1801,7 +1735,6 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
                nrt->rt6i_flags &= ~RTF_GATEWAY;
 
        nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
-       nrt->n = neigh_clone(neigh);
 
        if (ip6_ins_rt(nrt))
                goto out;
@@ -1851,8 +1784,6 @@ static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
                if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
                    (RTF_DEFAULT | RTF_ADDRCONF))
                        rt6_set_from(rt, ort);
-               else
-                       rt6_clean_expires(rt);
                rt->rt6i_metric = 0;
 
 #ifdef CONFIG_IPV6_SUBTREES
@@ -1984,7 +1915,8 @@ void rt6_purge_dflt_routers(struct net *net)
 restart:
        read_lock_bh(&table->tb6_lock);
        for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
-               if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
+               if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
+                   (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) {
                        dst_hold(&rt->dst);
                        read_unlock_bh(&table->tb6_lock);
                        ip6_del_rt(rt);
@@ -2115,7 +2047,6 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
 {
        struct net *net = dev_net(idev->dev);
        struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
-       int err;
 
        if (!rt) {
                net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
@@ -2134,11 +2065,6 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
                rt->rt6i_flags |= RTF_ANYCAST;
        else
                rt->rt6i_flags |= RTF_LOCAL;
-       err = rt6_bind_neighbour(rt, rt->dst.dev);
-       if (err) {
-               dst_free(&rt->dst);
-               return ERR_PTR(err);
-       }
 
        rt->rt6i_dst.addr = *addr;
        rt->rt6i_dst.plen = 128;
@@ -2429,7 +2355,7 @@ beginning:
        return last_err;
 }
 
-static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh)
 {
        struct fib6_config cfg;
        int err;
@@ -2444,7 +2370,7 @@ static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *a
                return ip6_route_del(&cfg);
 }
 
-static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh)
 {
        struct fib6_config cfg;
        int err;
@@ -2636,7 +2562,7 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg)
                     prefix, 0, NLM_F_MULTI);
 }
 
-static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
+static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh)
 {
        struct net *net = sock_net(in_skb->sk);
        struct nlattr *tb[RTA_MAX+1];
@@ -3068,8 +2994,8 @@ static void __net_exit ip6_route_net_exit(struct net *net)
 static int __net_init ip6_route_net_init_late(struct net *net)
 {
 #ifdef CONFIG_PROC_FS
-       proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
-       proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
+       proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops);
+       proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops);
 #endif
        return 0;
 }
@@ -3077,8 +3003,8 @@ static int __net_init ip6_route_net_init_late(struct net *net)
 static void __net_exit ip6_route_net_exit_late(struct net *net)
 {
 #ifdef CONFIG_PROC_FS
-       proc_net_remove(net, "ipv6_route");
-       proc_net_remove(net, "rt6_stats");
+       remove_proc_entry("ipv6_route", net->proc_net);
+       remove_proc_entry("rt6_stats", net->proc_net);
 #endif
 }