remove lots of double-semicolons
[safe/jmp/linux-2.6] / net / ipv6 / route.c
index 65053fb..c4a5982 100644 (file)
@@ -5,8 +5,6 @@
  *     Authors:
  *     Pedro Roque             <roque@di.fc.ul.pt>
  *
- *     $Id: route.c,v 1.56 2001/10/31 21:55:55 davem Exp $
- *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
  *      as published by the Free Software Foundation; either version
@@ -36,6 +34,7 @@
 #include <linux/route.h>
 #include <linux/netdevice.h>
 #include <linux/in6.h>
+#include <linux/mroute6.h>
 #include <linux/init.h>
 #include <linux/if_arp.h>
 #include <linux/proc_fs.h>
@@ -108,8 +107,7 @@ static struct dst_ops ip6_dst_ops_template = {
        .negative_advice        =       ip6_negative_advice,
        .link_failure           =       ip6_link_failure,
        .update_pmtu            =       ip6_rt_update_pmtu,
-       .local_out              =       ip6_local_out,
-       .entry_size             =       sizeof(struct rt6_info),
+       .local_out              =       __ip6_local_out,
        .entries                =       ATOMIC_INIT(0),
 };
 
@@ -123,7 +121,6 @@ static struct dst_ops ip6_dst_blackhole_ops = {
        .destroy                =       ip6_dst_destroy,
        .check                  =       ip6_dst_check,
        .update_pmtu            =       ip6_rt_blackhole_update_pmtu,
-       .entry_size             =       sizeof(struct rt6_info),
        .entries                =       ATOMIC_INIT(0),
 };
 
@@ -149,7 +146,7 @@ static struct rt6_info ip6_null_entry_template = {
 static int ip6_pkt_prohibit(struct sk_buff *skb);
 static int ip6_pkt_prohibit_out(struct sk_buff *skb);
 
-struct rt6_info ip6_prohibit_entry_template = {
+static struct rt6_info ip6_prohibit_entry_template = {
        .u = {
                .dst = {
                        .__refcnt       = ATOMIC_INIT(1),
@@ -229,7 +226,7 @@ static __inline__ int rt6_check_expired(const struct rt6_info *rt)
 static inline int rt6_need_strict(struct in6_addr *daddr)
 {
        return (ipv6_addr_type(daddr) &
-               (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL));
+               (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK));
 }
 
 /*
@@ -238,21 +235,26 @@ static inline int rt6_need_strict(struct in6_addr *daddr)
 
 static inline struct rt6_info *rt6_device_match(struct net *net,
                                                    struct rt6_info *rt,
+                                                   struct in6_addr *saddr,
                                                    int oif,
-                                                   int strict)
+                                                   int flags)
 {
        struct rt6_info *local = NULL;
        struct rt6_info *sprt;
 
-       if (oif) {
-               for (sprt = rt; sprt; sprt = sprt->u.dst.rt6_next) {
-                       struct net_device *dev = sprt->rt6i_dev;
+       if (!oif && ipv6_addr_any(saddr))
+               goto out;
+
+       for (sprt = rt; sprt; sprt = sprt->u.dst.rt6_next) {
+               struct net_device *dev = sprt->rt6i_dev;
+
+               if (oif) {
                        if (dev->ifindex == oif)
                                return sprt;
                        if (dev->flags & IFF_LOOPBACK) {
                                if (sprt->rt6i_idev == NULL ||
                                    sprt->rt6i_idev->dev->ifindex != oif) {
-                                       if (strict && oif)
+                                       if (flags & RT6_LOOKUP_F_IFACE && oif)
                                                continue;
                                        if (local && (!oif ||
                                                      local->rt6i_idev->dev->ifindex == oif))
@@ -260,14 +262,21 @@ static inline struct rt6_info *rt6_device_match(struct net *net,
                                }
                                local = sprt;
                        }
+               } else {
+                       if (ipv6_chk_addr(net, saddr, dev,
+                                         flags & RT6_LOOKUP_F_IFACE))
+                               return sprt;
                }
+       }
 
+       if (oif) {
                if (local)
                        return local;
 
-               if (strict)
+               if (flags & RT6_LOOKUP_F_IFACE)
                        return net->ipv6.ip6_null_entry;
        }
+out:
        return rt;
 }
 
@@ -445,7 +454,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
        struct route_info *rinfo = (struct route_info *) opt;
        struct in6_addr prefix_buf, *prefix;
        unsigned int pref;
-       u32 lifetime;
+       unsigned long lifetime;
        struct rt6_info *rt;
 
        if (len < sizeof(struct route_info)) {
@@ -471,13 +480,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
        if (pref == ICMPV6_ROUTER_PREF_INVALID)
                pref = ICMPV6_ROUTER_PREF_MEDIUM;
 
-       lifetime = ntohl(rinfo->lifetime);
-       if (lifetime == 0xffffffff) {
-               /* infinity */
-       } else if (lifetime > 0x7fffffff/HZ) {
-               /* Avoid arithmetic overflow */
-               lifetime = 0x7fffffff/HZ - 1;
-       }
+       lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
 
        if (rinfo->length == 3)
                prefix = (struct in6_addr *)rinfo->prefix;
@@ -505,7 +508,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
                                 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
 
        if (rt) {
-               if (lifetime == 0xffffffff) {
+               if (!addrconf_finite_timeout(lifetime)) {
                        rt->rt6i_flags &= ~RTF_EXPIRES;
                } else {
                        rt->rt6i_expires = jiffies + HZ * lifetime;
@@ -546,7 +549,7 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net,
        fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
 restart:
        rt = fn->leaf;
-       rt = rt6_device_match(net, rt, fl->oif, flags);
+       rt = rt6_device_match(net, rt, &fl->fl6_src, fl->oif, flags);
        BACKTRACK(net, &fl->fl6_src);
 out:
        dst_use(&rt->u.dst, jiffies);
@@ -555,8 +558,8 @@ out:
 
 }
 
-struct rt6_info *rt6_lookup(struct net *net, struct in6_addr *daddr,
-                           struct in6_addr *saddr, int oif, int strict)
+struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
+                           const struct in6_addr *saddr, int oif, int strict)
 {
        struct flowi fl = {
                .oif = oif,
@@ -624,6 +627,9 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad
        rt = ip6_rt_copy(ort);
 
        if (rt) {
+               struct neighbour *neigh;
+               int attempts = !in_softirq();
+
                if (!(rt->rt6i_flags&RTF_GATEWAY)) {
                        if (rt->rt6i_dst.plen != 128 &&
                            ipv6_addr_equal(&rt->rt6i_dst.addr, daddr))
@@ -643,7 +649,35 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad
                }
 #endif
 
-               rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+       retry:
+               neigh = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+               if (IS_ERR(neigh)) {
+                       struct net *net = dev_net(rt->rt6i_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;
+                       }
+
+                       if (net_ratelimit())
+                               printk(KERN_WARNING
+                                      "Neighbour table overflow.\n");
+                       dst_free(&rt->u.dst);
+                       return NULL;
+               }
+               rt->rt6i_nexthop = neigh;
 
        }
 
@@ -671,7 +705,7 @@ static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
        int strict = 0;
        int attempts = 3;
        int err;
-       int reachable = ipv6_devconf.forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
+       int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
 
        strict |= flags & RT6_LOOKUP_F_IFACE;
 
@@ -924,7 +958,7 @@ static DEFINE_SPINLOCK(icmp6_dst_lock);
 
 struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
                                  struct neighbour *neigh,
-                                 struct in6_addr *addr)
+                                 const struct in6_addr *addr)
 {
        struct rt6_info *rt;
        struct inet6_dev *idev = in6_dev_get(dev);
@@ -942,8 +976,11 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
        dev_hold(dev);
        if (neigh)
                neigh_hold(neigh);
-       else
+       else {
                neigh = ndisc_get_neigh(dev, addr);
+               if (IS_ERR(neigh))
+                       neigh = NULL;
+       }
 
        rt->rt6i_dev      = dev;
        rt->rt6i_idev     = idev;
@@ -973,13 +1010,12 @@ out:
        return &rt->u.dst;
 }
 
-int icmp6_dst_gc(int *more)
+int icmp6_dst_gc(void)
 {
        struct dst_entry *dst, *next, **pprev;
-       int freed;
+       int more = 0;
 
        next = NULL;
-       freed = 0;
 
        spin_lock_bh(&icmp6_dst_lock);
        pprev = &icmp6_dst_gc_list;
@@ -988,16 +1024,34 @@ int icmp6_dst_gc(int *more)
                if (!atomic_read(&dst->__refcnt)) {
                        *pprev = dst->next;
                        dst_free(dst);
-                       freed++;
                } else {
                        pprev = &dst->next;
-                       (*more)++;
+                       ++more;
                }
        }
 
        spin_unlock_bh(&icmp6_dst_lock);
 
-       return freed;
+       return more;
+}
+
+static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
+                           void *arg)
+{
+       struct dst_entry *dst, **pprev;
+
+       spin_lock_bh(&icmp6_dst_lock);
+       pprev = &icmp6_dst_gc_list;
+       while ((dst = *pprev) != NULL) {
+               struct rt6_info *rt = (struct rt6_info *) dst;
+               if (func(rt, arg)) {
+                       *pprev = dst->next;
+                       dst_free(dst);
+               } else {
+                       pprev = &dst->next;
+               }
+       }
+       spin_unlock_bh(&icmp6_dst_lock);
 }
 
 static int ip6_dst_gc(struct dst_ops *ops)
@@ -1053,7 +1107,7 @@ int ip6_dst_hoplimit(struct dst_entry *dst)
                        hoplimit = idev->cnf.hop_limit;
                        in6_dev_put(idev);
                } else
-                       hoplimit = ipv6_devconf.hop_limit;
+                       hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
        }
        return hoplimit;
 }
@@ -1105,7 +1159,9 @@ int ip6_route_add(struct fib6_config *cfg)
        }
 
        rt->u.dst.obsolete = -1;
-       rt->rt6i_expires = jiffies + clock_t_to_jiffies(cfg->fc_expires);
+       rt->rt6i_expires = (cfg->fc_flags & RTF_EXPIRES) ?
+                               jiffies + clock_t_to_jiffies(cfg->fc_expires) :
+                               0;
 
        if (cfg->fc_protocol == RTPROT_UNSPEC)
                cfg->fc_protocol = RTPROT_BOOT;
@@ -1242,11 +1298,11 @@ install_route:
                }
        }
 
-       if (rt->u.dst.metrics[RTAX_HOPLIMIT-1] == 0)
+       if (dst_metric(&rt->u.dst, RTAX_HOPLIMIT) == 0)
                rt->u.dst.metrics[RTAX_HOPLIMIT-1] = -1;
-       if (!rt->u.dst.metrics[RTAX_MTU-1])
+       if (!dst_mtu(&rt->u.dst))
                rt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(dev);
-       if (!rt->u.dst.metrics[RTAX_ADVMSS-1])
+       if (!dst_metric(&rt->u.dst, RTAX_ADVMSS))
                rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(net, dst_mtu(&rt->u.dst));
        rt->u.dst.dev = dev;
        rt->rt6i_idev = idev;
@@ -1699,8 +1755,6 @@ struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *d
        return rt;
 }
 
-EXPORT_SYMBOL(rt6_get_dflt_router);
-
 struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr,
                                     struct net_device *dev,
                                     unsigned int pref)
@@ -1811,16 +1865,19 @@ int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 static int ip6_pkt_drop(struct sk_buff *skb, int code, int ipstats_mib_noroutes)
 {
        int type;
+       struct dst_entry *dst = skb->dst;
        switch (ipstats_mib_noroutes) {
        case IPSTATS_MIB_INNOROUTES:
                type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
                if (type == IPV6_ADDR_ANY || type == IPV6_ADDR_RESERVED) {
-                       IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INADDRERRORS);
+                       IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
+                                     IPSTATS_MIB_INADDRERRORS);
                        break;
                }
                /* FALLTHROUGH */
        case IPSTATS_MIB_OUTNOROUTES:
-               IP6_INC_STATS(ip6_dst_idev(skb->dst), ipstats_mib_noroutes);
+               IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
+                             ipstats_mib_noroutes);
                break;
        }
        icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0, skb->dev);
@@ -1864,6 +1921,7 @@ 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->ipv6.ip6_dst_ops);
+       struct neighbour *neigh;
 
        if (rt == NULL)
                return ERR_PTR(-ENOMEM);
@@ -1886,11 +1944,18 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
                rt->rt6i_flags |= RTF_ANYCAST;
        else
                rt->rt6i_flags |= RTF_LOCAL;
-       rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
-       if (rt->rt6i_nexthop == NULL) {
+       neigh = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+       if (IS_ERR(neigh)) {
                dst_free(&rt->u.dst);
-               return ERR_PTR(-ENOMEM);
+
+               /* We are casting this because that is the return
+                * value type.  But an errno encoded pointer is the
+                * same regardless of the underlying pointer type,
+                * and that's what we are returning.  So this is OK.
+                */
+               return (struct rt6_info *) neigh;
        }
+       rt->rt6i_nexthop = neigh;
 
        ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
        rt->rt6i_dst.plen = 128;
@@ -1927,6 +1992,7 @@ void rt6_ifdown(struct net *net, struct net_device *dev)
        };
 
        fib6_clean_all(net, fib6_ifdown, 0, &adn);
+       icmp6_clean_all(fib6_ifdown, &adn);
 }
 
 struct rt6_mtu_change_arg
@@ -2020,7 +2086,7 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
 
        cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
        cfg->fc_nlinfo.nlh = nlh;
-       cfg->fc_nlinfo.nl_net = skb->sk->sk_net;
+       cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
 
        if (tb[RTA_GATEWAY]) {
                nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
@@ -2103,10 +2169,11 @@ static inline size_t rt6_nlmsg_size(void)
               + nla_total_size(sizeof(struct rta_cacheinfo));
 }
 
-static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
+static int rt6_fill_node(struct net *net,
+                        struct sk_buff *skb, struct rt6_info *rt,
                         struct in6_addr *dst, struct in6_addr *src,
                         int iif, int type, u32 pid, u32 seq,
-                        int prefix, unsigned int flags)
+                        int prefix, int nowait, unsigned int flags)
 {
        struct rtmsg *rtm;
        struct nlmsghdr *nlh;
@@ -2166,11 +2233,27 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
        } else if (rtm->rtm_src_len)
                NLA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
 #endif
-       if (iif)
-               NLA_PUT_U32(skb, RTA_IIF, iif);
-       else if (dst) {
+       if (iif) {
+#ifdef CONFIG_IPV6_MROUTE
+               if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
+                       int err = ip6mr_get_route(net, skb, rtm, nowait);
+                       if (err <= 0) {
+                               if (!nowait) {
+                                       if (err == 0)
+                                               return 0;
+                                       goto nla_put_failure;
+                               } else {
+                                       if (err == -EMSGSIZE)
+                                               goto nla_put_failure;
+                               }
+                       }
+               } else
+#endif
+                       NLA_PUT_U32(skb, RTA_IIF, iif);
+       } else if (dst) {
+               struct inet6_dev *idev = ip6_dst_idev(&rt->u.dst);
                struct in6_addr saddr_buf;
-               if (ipv6_dev_get_saddr(ip6_dst_idev(&rt->u.dst)->dev,
+               if (ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
                                       dst, 0, &saddr_buf) == 0)
                        NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
        }
@@ -2186,7 +2269,13 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
 
        NLA_PUT_U32(skb, RTA_PRIORITY, rt->rt6i_metric);
 
-       expires = rt->rt6i_expires ? rt->rt6i_expires - jiffies : 0;
+       if (!(rt->rt6i_flags & RTF_EXPIRES))
+               expires = 0;
+       else if (rt->rt6i_expires - jiffies < INT_MAX)
+               expires = rt->rt6i_expires - jiffies;
+       else
+               expires = INT_MAX;
+
        if (rtnl_put_cacheinfo(skb, &rt->u.dst, 0, 0, 0,
                               expires, rt->u.dst.error) < 0)
                goto nla_put_failure;
@@ -2209,14 +2298,15 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg)
        } else
                prefix = 0;
 
-       return rt6_fill_node(arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
+       return rt6_fill_node(arg->net,
+                    arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
                     NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
-                    prefix, NLM_F_MULTI);
+                    prefix, 0, NLM_F_MULTI);
 }
 
 static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
 {
-       struct net *net = in_skb->sk->sk_net;
+       struct net *net = sock_net(in_skb->sk);
        struct nlattr *tb[RTA_MAX+1];
        struct rt6_info *rt;
        struct sk_buff *skb;
@@ -2275,9 +2365,9 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void
        rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl);
        skb->dst = &rt->u.dst;
 
-       err = rt6_fill_node(skb, rt, &fl.fl6_dst, &fl.fl6_src, iif,
+       err = rt6_fill_node(net, skb, rt, &fl.fl6_dst, &fl.fl6_src, iif,
                            RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
-                           nlh->nlmsg_seq, 0, 0);
+                           nlh->nlmsg_seq, 0, 0, 0);
        if (err < 0) {
                kfree_skb(skb);
                goto errout;
@@ -2302,8 +2392,8 @@ void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
        if (skb == NULL)
                goto errout;
 
-       err = rt6_fill_node(skb, rt, NULL, NULL, 0,
-                               event, info->pid, seq, 0, 0);
+       err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
+                               event, info->pid, seq, 0, 0, 0);
        if (err < 0) {
                /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
                WARN_ON(err == -EMSGSIZE);
@@ -2358,19 +2448,16 @@ static int rt6_info_route(struct rt6_info *rt, void *p_arg)
 {
        struct seq_file *m = p_arg;
 
-       seq_printf(m, NIP6_SEQFMT " %02x ", NIP6(rt->rt6i_dst.addr),
-                  rt->rt6i_dst.plen);
+       seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
 
 #ifdef CONFIG_IPV6_SUBTREES
-       seq_printf(m, NIP6_SEQFMT " %02x ", NIP6(rt->rt6i_src.addr),
-                  rt->rt6i_src.plen);
+       seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
 #else
        seq_puts(m, "00000000000000000000000000000000 00 ");
 #endif
 
        if (rt->rt6i_nexthop) {
-               seq_printf(m, NIP6_SEQFMT,
-                          NIP6(*((struct in6_addr *)rt->rt6i_nexthop->primary_key)));
+               seq_printf(m, "%pi6", rt->rt6i_nexthop->primary_key);
        } else {
                seq_puts(m, "00000000000000000000000000000000");
        }
@@ -2390,18 +2477,7 @@ static int ipv6_route_show(struct seq_file *m, void *v)
 
 static int ipv6_route_open(struct inode *inode, struct file *file)
 {
-       struct net *net = get_proc_net(inode);
-       if (!net)
-               return -ENXIO;
-       return single_open(file, ipv6_route_show, net);
-}
-
-static int ipv6_route_release(struct inode *inode, struct file *file)
-{
-       struct seq_file *seq = file->private_data;
-       struct net *net = seq->private;
-       put_net(net);
-       return single_release(inode, file);
+       return single_open_net(inode, file, ipv6_route_show);
 }
 
 static const struct file_operations ipv6_route_proc_fops = {
@@ -2409,7 +2485,7 @@ static const struct file_operations ipv6_route_proc_fops = {
        .open           = ipv6_route_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
-       .release        = ipv6_route_release,
+       .release        = single_release_net,
 };
 
 static int rt6_stats_seq_show(struct seq_file *seq, void *v)
@@ -2429,16 +2505,7 @@ static int rt6_stats_seq_show(struct seq_file *seq, void *v)
 
 static int rt6_stats_seq_open(struct inode *inode, struct file *file)
 {
-       struct net *net = get_proc_net(inode);
-       return single_open(file, rt6_stats_seq_show, net);
-}
-
-static int rt6_stats_seq_release(struct inode *inode, struct file *file)
-{
-       struct seq_file *seq = file->private_data;
-       struct net *net = (struct net *)seq->private;
-       put_net(net);
-       return single_release(inode, file);
+       return single_open_net(inode, file, rt6_stats_seq_show);
 }
 
 static const struct file_operations rt6_stats_seq_fops = {
@@ -2446,7 +2513,7 @@ static const struct file_operations rt6_stats_seq_fops = {
        .open    = rt6_stats_seq_open,
        .read    = seq_read,
        .llseek  = seq_lseek,
-       .release = rt6_stats_seq_release,
+       .release = single_release_net,
 };
 #endif /* CONFIG_PROC_FS */
 
@@ -2472,7 +2539,7 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.flush_delay,
                .maxlen         =       sizeof(int),
                .mode           =       0200,
-               .proc_handler   =       &ipv6_sysctl_rtcache_flush
+               .proc_handler   =       ipv6_sysctl_rtcache_flush
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_GC_THRESH,
@@ -2480,7 +2547,7 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &ip6_dst_ops_template.gc_thresh,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec,
+               .proc_handler   =       proc_dointvec,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_MAX_SIZE,
@@ -2488,7 +2555,7 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_max_size,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec,
+               .proc_handler   =       proc_dointvec,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_GC_MIN_INTERVAL,
@@ -2496,8 +2563,8 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec_jiffies,
-               .strategy       =       &sysctl_jiffies,
+               .proc_handler   =       proc_dointvec_jiffies,
+               .strategy       =       sysctl_jiffies,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_GC_TIMEOUT,
@@ -2505,8 +2572,8 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec_jiffies,
-               .strategy       =       &sysctl_jiffies,
+               .proc_handler   =       proc_dointvec_jiffies,
+               .strategy       =       sysctl_jiffies,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_GC_INTERVAL,
@@ -2514,8 +2581,8 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_gc_interval,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec_jiffies,
-               .strategy       =       &sysctl_jiffies,
+               .proc_handler   =       proc_dointvec_jiffies,
+               .strategy       =       sysctl_jiffies,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_GC_ELASTICITY,
@@ -2523,8 +2590,8 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec_jiffies,
-               .strategy       =       &sysctl_jiffies,
+               .proc_handler   =       proc_dointvec_jiffies,
+               .strategy       =       sysctl_jiffies,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_MTU_EXPIRES,
@@ -2532,8 +2599,8 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec_jiffies,
-               .strategy       =       &sysctl_jiffies,
+               .proc_handler   =       proc_dointvec_jiffies,
+               .strategy       =       sysctl_jiffies,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_MIN_ADVMSS,
@@ -2541,8 +2608,8 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_min_advmss,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec_jiffies,
-               .strategy       =       &sysctl_jiffies,
+               .proc_handler   =       proc_dointvec_jiffies,
+               .strategy       =       sysctl_jiffies,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_GC_MIN_INTERVAL_MS,
@@ -2550,8 +2617,8 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec_ms_jiffies,
-               .strategy       =       &sysctl_ms_jiffies,
+               .proc_handler   =       proc_dointvec_ms_jiffies,
+               .strategy       =       sysctl_ms_jiffies,
        },
        { .ctl_name = 0 }
 };
@@ -2582,15 +2649,14 @@ struct ctl_table *ipv6_route_sysctl_init(struct net *net)
 
 static int ip6_route_net_init(struct net *net)
 {
-       int ret = 0;
+       int ret = -ENOMEM;
 
-       ret = -ENOMEM;
        net->ipv6.ip6_dst_ops = kmemdup(&ip6_dst_ops_template,
                                        sizeof(*net->ipv6.ip6_dst_ops),
                                        GFP_KERNEL);
        if (!net->ipv6.ip6_dst_ops)
                goto out;
-       net->ipv6.ip6_dst_ops->dst_net = net;
+       net->ipv6.ip6_dst_ops->dst_net = hold_net(net);
 
        net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
                                           sizeof(*net->ipv6.ip6_null_entry),
@@ -2605,10 +2671,8 @@ static int ip6_route_net_init(struct net *net)
        net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
                                               sizeof(*net->ipv6.ip6_prohibit_entry),
                                               GFP_KERNEL);
-       if (!net->ipv6.ip6_prohibit_entry) {
-               kfree(net->ipv6.ip6_null_entry);
-               goto out;
-       }
+       if (!net->ipv6.ip6_prohibit_entry)
+               goto out_ip6_null_entry;
        net->ipv6.ip6_prohibit_entry->u.dst.path =
                (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
        net->ipv6.ip6_prohibit_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
@@ -2616,16 +2680,22 @@ static int ip6_route_net_init(struct net *net)
        net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
                                               sizeof(*net->ipv6.ip6_blk_hole_entry),
                                               GFP_KERNEL);
-       if (!net->ipv6.ip6_blk_hole_entry) {
-               kfree(net->ipv6.ip6_null_entry);
-               kfree(net->ipv6.ip6_prohibit_entry);
-               goto out;
-       }
+       if (!net->ipv6.ip6_blk_hole_entry)
+               goto out_ip6_prohibit_entry;
        net->ipv6.ip6_blk_hole_entry->u.dst.path =
                (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
        net->ipv6.ip6_blk_hole_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
 #endif
 
+       net->ipv6.sysctl.flush_delay = 0;
+       net->ipv6.sysctl.ip6_rt_max_size = 4096;
+       net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
+       net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
+       net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
+       net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
+       net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
+       net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
+
 #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);
@@ -2636,7 +2706,14 @@ static int ip6_route_net_init(struct net *net)
 out:
        return ret;
 
+#ifdef CONFIG_IPV6_MULTIPLE_TABLES
+out_ip6_prohibit_entry:
+       kfree(net->ipv6.ip6_prohibit_entry);
+out_ip6_null_entry:
+       kfree(net->ipv6.ip6_null_entry);
+#endif
 out_ip6_dst_ops:
+       release_net(net->ipv6.ip6_dst_ops->dst_net);
        kfree(net->ipv6.ip6_dst_ops);
        goto out;
 }
@@ -2652,6 +2729,7 @@ static void ip6_route_net_exit(struct net *net)
        kfree(net->ipv6.ip6_prohibit_entry);
        kfree(net->ipv6.ip6_blk_hole_entry);
 #endif
+       release_net(net->ipv6.ip6_dst_ops->dst_net);
        kfree(net->ipv6.ip6_dst_ops);
 }
 
@@ -2674,12 +2752,14 @@ int __init ip6_route_init(void)
                kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
                                  SLAB_HWCACHE_ALIGN, NULL);
        if (!ip6_dst_ops_template.kmem_cachep)
-               goto out;;
+               goto out;
 
        ret = register_pernet_subsys(&ip6_route_net_ops);
        if (ret)
                goto out_kmem_cache;
 
+       ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
+
        /* Registering of the loopback is done before this portion of code,
         * the loopback reference in rt6_info will not be taken, do it
         * manually for init_net */