]> err.no Git - linux-2.6/blobdiff - net/ipv6/route.c
[NETFILTER]: annotate {arp,ip,ip6,x}tables with const
[linux-2.6] / net / ipv6 / route.c
index 7ff66cebe77cff96777775a6e0bed97cd0be3f63..6293cb91ed1d49aca560c255c04ea545a1247026 100644 (file)
@@ -36,6 +36,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>
@@ -97,7 +98,7 @@ static struct rt6_info *rt6_get_route_info(struct net *net,
                                           struct in6_addr *gwaddr, int ifindex);
 #endif
 
-static struct dst_ops ip6_dst_ops = {
+static struct dst_ops ip6_dst_ops_template = {
        .family                 =       AF_INET6,
        .protocol               =       __constant_htons(ETH_P_IPV6),
        .gc                     =       ip6_dst_gc,
@@ -137,7 +138,6 @@ static struct rt6_info ip6_null_entry_template = {
                        .metrics        = { [RTAX_HOPLIMIT - 1] = 255, },
                        .input          = ip6_pkt_discard,
                        .output         = ip6_pkt_discard_out,
-                       .ops            = &ip6_dst_ops,
                }
        },
        .rt6i_flags     = (RTF_REJECT | RTF_NONEXTHOP),
@@ -160,7 +160,6 @@ struct rt6_info ip6_prohibit_entry_template = {
                        .metrics        = { [RTAX_HOPLIMIT - 1] = 255, },
                        .input          = ip6_pkt_prohibit,
                        .output         = ip6_pkt_prohibit_out,
-                       .ops            = &ip6_dst_ops,
                }
        },
        .rt6i_flags     = (RTF_REJECT | RTF_NONEXTHOP),
@@ -178,7 +177,6 @@ static struct rt6_info ip6_blk_hole_entry_template = {
                        .metrics        = { [RTAX_HOPLIMIT - 1] = 255, },
                        .input          = dst_discard,
                        .output         = dst_discard,
-                       .ops            = &ip6_dst_ops,
                }
        },
        .rt6i_flags     = (RTF_REJECT | RTF_NONEXTHOP),
@@ -189,9 +187,9 @@ static struct rt6_info ip6_blk_hole_entry_template = {
 #endif
 
 /* allocate dst with ip6_dst_ops */
-static __inline__ struct rt6_info *ip6_dst_alloc(void)
+static inline struct rt6_info *ip6_dst_alloc(struct dst_ops *ops)
 {
-       return (struct rt6_info *)dst_alloc(&ip6_dst_ops);
+       return (struct rt6_info *)dst_alloc(ops);
 }
 
 static void ip6_dst_destroy(struct dst_entry *dst)
@@ -211,7 +209,7 @@ static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
        struct rt6_info *rt = (struct rt6_info *)dst;
        struct inet6_dev *idev = rt->rt6i_idev;
        struct net_device *loopback_dev =
-               dev->nd_net->loopback_dev;
+               dev_net(dev)->loopback_dev;
 
        if (dev != loopback_dev && idev != NULL && idev->dev == dev) {
                struct inet6_dev *loopback_idev =
@@ -413,7 +411,7 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
        struct net *net;
 
        RT6_TRACE("%s(fn->leaf=%p, oif=%d)\n",
-                 __FUNCTION__, fn->leaf, oif);
+                 __func__, fn->leaf, oif);
 
        rt0 = fn->rr_ptr;
        if (!rt0)
@@ -434,9 +432,9 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
        }
 
        RT6_TRACE("%s() => %p\n",
-                 __FUNCTION__, match);
+                 __func__, match);
 
-       net = rt0->rt6i_dev->nd_net;
+       net = dev_net(rt0->rt6i_dev);
        return (match ? match : net->ipv6.ip6_null_entry);
 }
 
@@ -444,7 +442,7 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
 int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
                  struct in6_addr *gwaddr)
 {
-       struct net *net = dev->nd_net;
+       struct net *net = dev_net(dev);
        struct route_info *rinfo = (struct route_info *) opt;
        struct in6_addr prefix_buf, *prefix;
        unsigned int pref;
@@ -558,8 +556,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,
@@ -610,7 +608,7 @@ static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
 int ip6_ins_rt(struct rt6_info *rt)
 {
        struct nl_info info = {
-               .nl_net = rt->rt6i_dev->nd_net,
+               .nl_net = dev_net(rt->rt6i_dev),
        };
        return __ip6_ins_rt(rt, &info);
 }
@@ -748,7 +746,7 @@ static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *
 void ip6_route_input(struct sk_buff *skb)
 {
        struct ipv6hdr *iph = ipv6_hdr(skb);
-       struct net *net = skb->dev->nd_net;
+       struct net *net = dev_net(skb->dev);
        int flags = RT6_LOOKUP_F_HAS_SADDR;
        struct flowi fl = {
                .iif = skb->dev->ifindex,
@@ -775,7 +773,8 @@ static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table
        return ip6_pol_route(net, table, fl->oif, fl, flags);
 }
 
-struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl)
+struct dst_entry * ip6_route_output(struct net *net, struct sock *sk,
+                                   struct flowi *fl)
 {
        int flags = 0;
 
@@ -784,8 +783,17 @@ struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl)
 
        if (!ipv6_addr_any(&fl->fl6_src))
                flags |= RT6_LOOKUP_F_HAS_SADDR;
+       else if (sk) {
+               unsigned int prefs = inet6_sk(sk)->srcprefs;
+               if (prefs & IPV6_PREFER_SRC_TMP)
+                       flags |= RT6_LOOKUP_F_SRCPREF_TMP;
+               if (prefs & IPV6_PREFER_SRC_PUBLIC)
+                       flags |= RT6_LOOKUP_F_SRCPREF_PUBLIC;
+               if (prefs & IPV6_PREFER_SRC_COA)
+                       flags |= RT6_LOOKUP_F_SRCPREF_COA;
+       }
 
-       return fib6_rule_lookup(&init_net, fl, flags, ip6_pol_route_output);
+       return fib6_rule_lookup(net, fl, flags, ip6_pol_route_output);
 }
 
 EXPORT_SYMBOL(ip6_route_output);
@@ -917,16 +925,16 @@ 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);
-       struct net *net = dev->nd_net;
+       struct net *net = dev_net(dev);
 
        if (unlikely(idev == NULL))
                return NULL;
 
-       rt = ip6_dst_alloc();
+       rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
        if (unlikely(rt == NULL)) {
                in6_dev_put(idev);
                goto out;
@@ -995,23 +1003,26 @@ int icmp6_dst_gc(int *more)
 
 static int ip6_dst_gc(struct dst_ops *ops)
 {
-       static unsigned expire = 30*HZ;
-       static unsigned long last_gc;
        unsigned long now = jiffies;
-
-       if (time_after(last_gc + init_net.ipv6.sysctl.ip6_rt_gc_min_interval, now) &&
-           atomic_read(&ip6_dst_ops.entries) <= init_net.ipv6.sysctl.ip6_rt_max_size)
+       struct net *net = ops->dst_net;
+       int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
+       int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
+       int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
+       int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
+       unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
+
+       if (time_after(rt_last_gc + rt_min_interval, now) &&
+           atomic_read(&ops->entries) <= rt_max_size)
                goto out;
 
-       expire++;
-       fib6_run_gc(expire, &init_net);
-       last_gc = now;
-       if (atomic_read(&ip6_dst_ops.entries) < ip6_dst_ops.gc_thresh)
-               expire = init_net.ipv6.sysctl.ip6_rt_gc_timeout>>1;
-
+       net->ipv6.ip6_rt_gc_expire++;
+       fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
+       net->ipv6.ip6_rt_last_gc = now;
+       if (atomic_read(&ops->entries) < ops->gc_thresh)
+               net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
 out:
-       expire -= expire>>init_net.ipv6.sysctl.ip6_rt_gc_elasticity;
-       return (atomic_read(&ip6_dst_ops.entries) > init_net.ipv6.sysctl.ip6_rt_max_size);
+       net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
+       return (atomic_read(&ops->entries) > rt_max_size);
 }
 
 /* Clean host part of a prefix. Not necessary in radix tree,
@@ -1033,15 +1044,17 @@ static int ipv6_get_mtu(struct net_device *dev)
        return mtu;
 }
 
-int ipv6_get_hoplimit(struct net_device *dev)
+int ip6_dst_hoplimit(struct dst_entry *dst)
 {
-       int hoplimit = ipv6_devconf.hop_limit;
-       struct inet6_dev *idev;
-
-       idev = in6_dev_get(dev);
-       if (idev) {
-               hoplimit = idev->cnf.hop_limit;
-               in6_dev_put(idev);
+       int hoplimit = dst_metric(dst, RTAX_HOPLIMIT);
+       if (hoplimit < 0) {
+               struct net_device *dev = dst->dev;
+               struct inet6_dev *idev = in6_dev_get(dev);
+               if (idev) {
+                       hoplimit = idev->cnf.hop_limit;
+                       in6_dev_put(idev);
+               } else
+                       hoplimit = ipv6_devconf.hop_limit;
        }
        return hoplimit;
 }
@@ -1085,7 +1098,7 @@ int ip6_route_add(struct fib6_config *cfg)
                goto out;
        }
 
-       rt = ip6_dst_alloc();
+       rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
 
        if (rt == NULL) {
                err = -ENOMEM;
@@ -1240,7 +1253,7 @@ install_route:
        rt->rt6i_idev = idev;
        rt->rt6i_table = table;
 
-       cfg->fc_nlinfo.nl_net = dev->nd_net;
+       cfg->fc_nlinfo.nl_net = dev_net(dev);
 
        return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
 
@@ -1258,7 +1271,7 @@ static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
 {
        int err;
        struct fib6_table *table;
-       struct net *net = rt->rt6i_dev->nd_net;
+       struct net *net = dev_net(rt->rt6i_dev);
 
        if (rt == net->ipv6.ip6_null_entry)
                return -ENOENT;
@@ -1277,7 +1290,7 @@ static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
 int ip6_del_rt(struct rt6_info *rt)
 {
        struct nl_info info = {
-               .nl_net = rt->rt6i_dev->nd_net,
+               .nl_net = dev_net(rt->rt6i_dev),
        };
        return __ip6_del_rt(rt, &info);
 }
@@ -1389,7 +1402,7 @@ static struct rt6_info *ip6_route_redirect(struct in6_addr *dest,
                                           struct net_device *dev)
 {
        int flags = RT6_LOOKUP_F_HAS_SADDR;
-       struct net *net = dev->nd_net;
+       struct net *net = dev_net(dev);
        struct ip6rd_flowi rdfl = {
                .fl = {
                        .oif = dev->ifindex,
@@ -1416,7 +1429,7 @@ void rt6_redirect(struct in6_addr *dest, struct in6_addr *src,
 {
        struct rt6_info *rt, *nrt = NULL;
        struct netevent_redirect netevent;
-       struct net *net = neigh->dev->nd_net;
+       struct net *net = dev_net(neigh->dev);
 
        rt = ip6_route_redirect(dest, src, saddr, neigh->dev);
 
@@ -1465,7 +1478,7 @@ void rt6_redirect(struct in6_addr *dest, struct in6_addr *src,
        nrt->rt6i_nexthop = neigh_clone(neigh);
        /* Reset pmtu, it may be better */
        nrt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(neigh->dev);
-       nrt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(neigh->dev->nd_net,
+       nrt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(dev_net(neigh->dev),
                                                        dst_mtu(&nrt->u.dst));
 
        if (ip6_ins_rt(nrt))
@@ -1494,7 +1507,7 @@ void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr,
                        struct net_device *dev, u32 pmtu)
 {
        struct rt6_info *rt, *nrt;
-       struct net *net = dev->nd_net;
+       struct net *net = dev_net(dev);
        int allfrag = 0;
 
        rt = rt6_lookup(net, daddr, saddr, dev->ifindex, 0);
@@ -1571,7 +1584,8 @@ out:
 
 static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
 {
-       struct rt6_info *rt = ip6_dst_alloc();
+       struct net *net = dev_net(ort->rt6i_dev);
+       struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
 
        if (rt) {
                rt->u.dst.input = ort->u.dst.input;
@@ -1669,7 +1683,7 @@ struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *d
        struct rt6_info *rt;
        struct fib6_table *table;
 
-       table = fib6_get_table(dev->nd_net, RT6_TABLE_DFLT);
+       table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
        if (table == NULL)
                return NULL;
 
@@ -1686,8 +1700,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)
@@ -1700,7 +1712,7 @@ struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr,
                                  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
                .fc_nlinfo.pid = 0,
                .fc_nlinfo.nlh = NULL,
-               .fc_nlinfo.nl_net = dev->nd_net,
+               .fc_nlinfo.nl_net = dev_net(dev),
        };
 
        ipv6_addr_copy(&cfg.fc_gateway, gwaddr);
@@ -1849,8 +1861,8 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
                                    const struct in6_addr *addr,
                                    int anycast)
 {
-       struct net *net = idev->dev->nd_net;
-       struct rt6_info *rt = ip6_dst_alloc();
+       struct net *net = dev_net(idev->dev);
+       struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
 
        if (rt == NULL)
                return ERR_PTR(-ENOMEM);
@@ -1926,7 +1938,7 @@ static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
 {
        struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
        struct inet6_dev *idev;
-       struct net *net = arg->dev->nd_net;
+       struct net *net = dev_net(arg->dev);
 
        /* In IPv6 pmtu discovery is not optional,
           so that RTAX_MTU lock cannot disable it.
@@ -1970,7 +1982,7 @@ void rt6_mtu_change(struct net_device *dev, unsigned mtu)
                .mtu = mtu,
        };
 
-       fib6_clean_all(dev->nd_net, rt6_mtu_change_route, 0, &arg);
+       fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
 }
 
 static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
@@ -2007,7 +2019,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);
@@ -2093,7 +2105,7 @@ static inline size_t rt6_nlmsg_size(void)
 static int rt6_fill_node(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;
@@ -2153,12 +2165,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(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 in6_addr saddr_buf;
                if (ipv6_dev_get_saddr(ip6_dst_idev(&rt->u.dst)->dev,
-                                      dst, &saddr_buf) == 0)
+                                      dst, 0, &saddr_buf) == 0)
                        NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
        }
 
@@ -2198,12 +2225,12 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg)
 
        return rt6_fill_node(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;
@@ -2259,12 +2286,12 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void
        skb_reset_mac_header(skb);
        skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
 
-       rt = (struct rt6_info*) ip6_route_output(NULL, &fl);
+       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,
                            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;
@@ -2290,7 +2317,7 @@ void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
                goto errout;
 
        err = rt6_fill_node(skb, rt, NULL, NULL, 0,
-                               event, info->pid, seq, 0, 0);
+                               event, info->pid, seq, 0, 0, 0);
        if (err < 0) {
                /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
                WARN_ON(err == -EMSGSIZE);
@@ -2308,7 +2335,7 @@ static int ip6_route_dev_notify(struct notifier_block *this,
                                unsigned long event, void *data)
 {
        struct net_device *dev = (struct net_device *)data;
-       struct net *net = dev->nd_net;
+       struct net *net = dev_net(dev);
 
        if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
                net->ipv6.ip6_null_entry->u.dst.dev = dev;
@@ -2377,10 +2404,18 @@ static int ipv6_route_show(struct seq_file *m, void *v)
 
 static int ipv6_route_open(struct inode *inode, struct file *file)
 {
+       int err;
        struct net *net = get_proc_net(inode);
        if (!net)
                return -ENXIO;
-       return single_open(file, ipv6_route_show, net);
+
+       err = single_open(file, ipv6_route_show, net);
+       if (err < 0) {
+               put_net(net);
+               return err;
+       }
+
+       return 0;
 }
 
 static int ipv6_route_release(struct inode *inode, struct file *file)
@@ -2408,7 +2443,7 @@ static int rt6_stats_seq_show(struct seq_file *seq, void *v)
                   net->ipv6.rt6_stats->fib_rt_alloc,
                   net->ipv6.rt6_stats->fib_rt_entries,
                   net->ipv6.rt6_stats->fib_rt_cache,
-                  atomic_read(&ip6_dst_ops.entries),
+                  atomic_read(&net->ipv6.ip6_dst_ops->entries),
                   net->ipv6.rt6_stats->fib_discarded_routes);
 
        return 0;
@@ -2416,8 +2451,18 @@ static int rt6_stats_seq_show(struct seq_file *seq, void *v)
 
 static int rt6_stats_seq_open(struct inode *inode, struct file *file)
 {
+       int err;
        struct net *net = get_proc_net(inode);
-       return single_open(file, rt6_stats_seq_show, net);
+       if (!net)
+               return -ENXIO;
+
+       err = single_open(file, rt6_stats_seq_show, net);
+       if (err < 0) {
+               put_net(net);
+               return err;
+       }
+
+       return 0;
 }
 
 static int rt6_stats_seq_release(struct inode *inode, struct file *file)
@@ -2464,7 +2509,7 @@ ctl_table ipv6_route_table_template[] = {
        {
                .ctl_name       =       NET_IPV6_ROUTE_GC_THRESH,
                .procname       =       "gc_thresh",
-               .data           =       &ip6_dst_ops.gc_thresh,
+               .data           =       &ip6_dst_ops_template.gc_thresh,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
                .proc_handler   =       &proc_dointvec,
@@ -2553,8 +2598,7 @@ struct ctl_table *ipv6_route_sysctl_init(struct net *net)
 
        if (table) {
                table[0].data = &net->ipv6.sysctl.flush_delay;
-               /* table[1].data will be handled when we have
-                  routes per namespace */
+               table[1].data = &net->ipv6.ip6_dst_ops->gc_thresh;
                table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
                table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
                table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
@@ -2573,13 +2617,21 @@ static int ip6_route_net_init(struct net *net)
        int ret = 0;
 
        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_null_entry = kmemdup(&ip6_null_entry_template,
                                           sizeof(*net->ipv6.ip6_null_entry),
                                           GFP_KERNEL);
        if (!net->ipv6.ip6_null_entry)
-               goto out;
+               goto out_ip6_dst_ops;
        net->ipv6.ip6_null_entry->u.dst.path =
                (struct dst_entry *)net->ipv6.ip6_null_entry;
+       net->ipv6.ip6_null_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
 
 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
        net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
@@ -2591,6 +2643,7 @@ static int ip6_route_net_init(struct net *net)
        }
        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;
 
        net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
                                               sizeof(*net->ipv6.ip6_blk_hole_entry),
@@ -2602,15 +2655,22 @@ static int ip6_route_net_init(struct net *net)
        }
        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
 
 #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);
 #endif
+       net->ipv6.ip6_rt_gc_expire = 30*HZ;
+
        ret = 0;
 out:
        return ret;
+
+out_ip6_dst_ops:
+       kfree(net->ipv6.ip6_dst_ops);
+       goto out;
 }
 
 static void ip6_route_net_exit(struct net *net)
@@ -2624,6 +2684,7 @@ static void ip6_route_net_exit(struct net *net)
        kfree(net->ipv6.ip6_prohibit_entry);
        kfree(net->ipv6.ip6_blk_hole_entry);
 #endif
+       kfree(net->ipv6.ip6_dst_ops);
 }
 
 static struct pernet_operations ip6_route_net_ops = {
@@ -2640,13 +2701,12 @@ int __init ip6_route_init(void)
 {
        int ret;
 
-       ip6_dst_ops.kmem_cachep =
+       ret = -ENOMEM;
+       ip6_dst_ops_template.kmem_cachep =
                kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
                                  SLAB_HWCACHE_ALIGN, NULL);
-       if (!ip6_dst_ops.kmem_cachep)
-               return -ENOMEM;
-
-       ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops.kmem_cachep;
+       if (!ip6_dst_ops_template.kmem_cachep)
+               goto out;;
 
        ret = register_pernet_subsys(&ip6_route_net_ops);
        if (ret)
@@ -2697,7 +2757,7 @@ out_fib6_init:
 out_register_subsys:
        unregister_pernet_subsys(&ip6_route_net_ops);
 out_kmem_cache:
-       kmem_cache_destroy(ip6_dst_ops.kmem_cachep);
+       kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
        goto out;
 }
 
@@ -2708,5 +2768,5 @@ void ip6_route_cleanup(void)
        xfrm6_fini();
        fib6_gc_cleanup();
        unregister_pernet_subsys(&ip6_route_net_ops);
-       kmem_cache_destroy(ip6_dst_ops.kmem_cachep);
+       kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
 }