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,
.entries = ATOMIC_INIT(0),
};
-struct rt6_info ip6_null_entry = {
+static struct rt6_info ip6_null_entry_template = {
.u = {
.dst = {
.__refcnt = ATOMIC_INIT(1),
.metrics = { [RTAX_HOPLIMIT - 1] = 255, },
.input = ip6_pkt_discard,
.output = ip6_pkt_discard_out,
- .ops = &ip6_dst_ops,
- .path = (struct dst_entry*)&ip6_null_entry,
}
},
.rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
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 = {
+struct rt6_info ip6_prohibit_entry_template = {
.u = {
.dst = {
.__refcnt = ATOMIC_INIT(1),
.metrics = { [RTAX_HOPLIMIT - 1] = 255, },
.input = ip6_pkt_prohibit,
.output = ip6_pkt_prohibit_out,
- .ops = &ip6_dst_ops,
- .path = (struct dst_entry*)&ip6_prohibit_entry,
}
},
.rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
.rt6i_ref = ATOMIC_INIT(1),
};
-struct rt6_info ip6_blk_hole_entry = {
+static struct rt6_info ip6_blk_hole_entry_template = {
.u = {
.dst = {
.__refcnt = ATOMIC_INIT(1),
.metrics = { [RTAX_HOPLIMIT - 1] = 255, },
.input = dst_discard,
.output = dst_discard,
- .ops = &ip6_dst_ops,
- .path = (struct dst_entry*)&ip6_blk_hole_entry,
}
},
.rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
#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)
* Route lookup. Any table->tb6_lock is implied.
*/
-static __inline__ struct rt6_info *rt6_device_match(struct rt6_info *rt,
+static inline struct rt6_info *rt6_device_match(struct net *net,
+ struct rt6_info *rt,
int oif,
int strict)
{
return local;
if (strict)
- return &ip6_null_entry;
+ return net->ipv6.ip6_null_entry;
}
return rt;
}
static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
{
struct rt6_info *match, *rt0;
+ 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)
}
RT6_TRACE("%s() => %p\n",
- __FUNCTION__, match);
+ __func__, match);
- return (match ? match : &ip6_null_entry);
+ net = rt0->rt6i_dev->nd_net;
+ return (match ? match : net->ipv6.ip6_null_entry);
}
#ifdef CONFIG_IPV6_ROUTE_INFO
}
#endif
-#define BACKTRACK(saddr) \
+#define BACKTRACK(__net, saddr) \
do { \
- if (rt == &ip6_null_entry) { \
+ if (rt == __net->ipv6.ip6_null_entry) { \
struct fib6_node *pn; \
while (1) { \
if (fn->fn_flags & RTN_TL_ROOT) \
} \
} while(0)
-static struct rt6_info *ip6_pol_route_lookup(struct fib6_table *table,
+static struct rt6_info *ip6_pol_route_lookup(struct net *net,
+ struct fib6_table *table,
struct flowi *fl, int flags)
{
struct fib6_node *fn;
fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
restart:
rt = fn->leaf;
- rt = rt6_device_match(rt, fl->oif, flags);
- BACKTRACK(&fl->fl6_src);
+ rt = rt6_device_match(net, rt, fl->oif, flags);
+ BACKTRACK(net, &fl->fl6_src);
out:
dst_use(&rt->u.dst, jiffies);
read_unlock_bh(&table->tb6_lock);
return rt;
}
-static struct rt6_info *ip6_pol_route(struct fib6_table *table, int oif,
- struct flowi *fl, int flags)
+static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
+ struct flowi *fl, int flags)
{
struct fib6_node *fn;
struct rt6_info *rt, *nrt;
restart:
rt = rt6_select(fn, oif, strict | reachable);
- BACKTRACK(&fl->fl6_src);
- if (rt == &ip6_null_entry ||
+
+ BACKTRACK(net, &fl->fl6_src);
+ if (rt == net->ipv6.ip6_null_entry ||
rt->rt6i_flags & RTF_CACHE)
goto out;
}
dst_release(&rt->u.dst);
- rt = nrt ? : &ip6_null_entry;
+ rt = nrt ? : net->ipv6.ip6_null_entry;
dst_hold(&rt->u.dst);
if (nrt) {
return rt;
}
-static struct rt6_info *ip6_pol_route_input(struct fib6_table *table,
+static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
struct flowi *fl, int flags)
{
- return ip6_pol_route(table, fl->iif, fl, flags);
+ return ip6_pol_route(net, table, fl->iif, fl, flags);
}
void ip6_route_input(struct sk_buff *skb)
skb->dst = fib6_rule_lookup(net, &fl, flags, ip6_pol_route_input);
}
-static struct rt6_info *ip6_pol_route_output(struct fib6_table *table,
+static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
struct flowi *fl, int flags)
{
- return ip6_pol_route(table, fl->oif, fl, flags);
+ 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;
if (!ipv6_addr_any(&fl->fl6_src))
flags |= RT6_LOOKUP_F_HAS_SADDR;
- 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);
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;
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,
goto out;
}
- rt = ip6_dst_alloc();
+ rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
if (rt == NULL) {
err = -ENOMEM;
{
int err;
struct fib6_table *table;
+ struct net *net = rt->rt6i_dev->nd_net;
- if (rt == &ip6_null_entry)
+ if (rt == net->ipv6.ip6_null_entry)
return -ENOENT;
table = rt->rt6i_table;
struct in6_addr gateway;
};
-static struct rt6_info *__ip6_route_redirect(struct fib6_table *table,
+static struct rt6_info *__ip6_route_redirect(struct net *net,
+ struct fib6_table *table,
struct flowi *fl,
int flags)
{
}
if (!rt)
- rt = &ip6_null_entry;
- BACKTRACK(&fl->fl6_src);
+ rt = net->ipv6.ip6_null_entry;
+ BACKTRACK(net, &fl->fl6_src);
out:
dst_hold(&rt->u.dst);
{
struct rt6_info *rt, *nrt = NULL;
struct netevent_redirect netevent;
+ struct net *net = neigh->dev->nd_net;
rt = ip6_route_redirect(dest, src, saddr, neigh->dev);
- if (rt == &ip6_null_entry) {
+ if (rt == net->ipv6.ip6_null_entry) {
if (net_ratelimit())
printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
"for redirect target\n");
static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
{
- struct rt6_info *rt = ip6_dst_alloc();
+ struct net *net = ort->rt6i_dev->nd_net;
+ struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
if (rt) {
rt->u.dst.input = ort->u.dst.input;
int anycast)
{
struct net *net = idev->dev->nd_net;
- struct rt6_info *rt = ip6_dst_alloc();
+ struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
if (rt == NULL)
return ERR_PTR(-ENOMEM);
return rt;
}
+struct arg_dev_net {
+ struct net_device *dev;
+ struct net *net;
+};
+
static int fib6_ifdown(struct rt6_info *rt, void *arg)
{
- if (((void*)rt->rt6i_dev == arg || arg == NULL) &&
- rt != &ip6_null_entry) {
+ struct net_device *dev = ((struct arg_dev_net *)arg)->dev;
+ struct net *net = ((struct arg_dev_net *)arg)->net;
+
+ if (((void *)rt->rt6i_dev == dev || dev == NULL) &&
+ rt != net->ipv6.ip6_null_entry) {
RT6_TRACE("deleted by ifdown %p\n", rt);
return -1;
}
void rt6_ifdown(struct net *net, struct net_device *dev)
{
- fib6_clean_all(net, fib6_ifdown, 0, dev);
+ struct arg_dev_net adn = {
+ .dev = dev,
+ .net = net,
+ };
+
+ fib6_clean_all(net, fib6_ifdown, 0, &adn);
}
struct rt6_mtu_change_arg
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,
rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
}
+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;
+
+ if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
+ net->ipv6.ip6_null_entry->u.dst.dev = dev;
+ net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
+#ifdef CONFIG_IPV6_MULTIPLE_TABLES
+ net->ipv6.ip6_prohibit_entry->u.dst.dev = dev;
+ net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
+ net->ipv6.ip6_blk_hole_entry->u.dst.dev = dev;
+ net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
+#endif
+ }
+
+ return NOTIFY_OK;
+}
+
/*
* /proc
*/
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;
{
.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,
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;
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_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,
+ sizeof(*net->ipv6.ip6_prohibit_entry),
+ GFP_KERNEL);
+ if (!net->ipv6.ip6_prohibit_entry) {
+ kfree(net->ipv6.ip6_null_entry);
+ goto out;
+ }
+ 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),
+ GFP_KERNEL);
+ if (!net->ipv6.ip6_blk_hole_entry) {
+ kfree(net->ipv6.ip6_null_entry);
+ kfree(net->ipv6.ip6_prohibit_entry);
+ goto out;
+ }
+ 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
- return 0;
+ 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)
proc_net_remove(net, "ipv6_route");
proc_net_remove(net, "rt6_stats");
#endif
- rt6_ifdown(net, NULL);
+ kfree(net->ipv6.ip6_null_entry);
+#ifdef CONFIG_IPV6_MULTIPLE_TABLES
+ 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 = {
.exit = ip6_route_net_exit,
};
+static struct notifier_block ip6_route_dev_notifier = {
+ .notifier_call = ip6_route_dev_notify,
+ .priority = 0,
+};
+
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;
+ if (!ip6_dst_ops_template.kmem_cachep)
+ goto out;;
- ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops.kmem_cachep;
+ ret = register_pernet_subsys(&ip6_route_net_ops);
+ if (ret)
+ goto out_kmem_cache;
+ /* 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 */
+ init_net.ipv6.ip6_null_entry->u.dst.dev = init_net.loopback_dev;
+ init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
+ #ifdef CONFIG_IPV6_MULTIPLE_TABLES
+ init_net.ipv6.ip6_prohibit_entry->u.dst.dev = init_net.loopback_dev;
+ init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
+ init_net.ipv6.ip6_blk_hole_entry->u.dst.dev = init_net.loopback_dev;
+ init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
+ #endif
ret = fib6_init();
if (ret)
- goto out_kmem_cache;
+ goto out_register_subsys;
ret = xfrm6_init();
if (ret)
__rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL))
goto fib6_rules_init;
- ret = register_pernet_subsys(&ip6_route_net_ops);
+ ret = register_netdevice_notifier(&ip6_route_dev_notifier);
if (ret)
goto fib6_rules_init;
+
out:
return ret;
xfrm6_fini();
out_fib6_init:
fib6_gc_cleanup();
+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;
}
void ip6_route_cleanup(void)
{
- unregister_pernet_subsys(&ip6_route_net_ops);
+ unregister_netdevice_notifier(&ip6_route_dev_notifier);
fib6_rules_cleanup();
xfrm6_fini();
fib6_gc_cleanup();
- kmem_cache_destroy(ip6_dst_ops.kmem_cachep);
+ unregister_pernet_subsys(&ip6_route_net_ops);
+ kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
}