#define CLONE_OFFLINK_ROUTE 0
-static int ip6_rt_max_size = 4096;
-static int ip6_rt_gc_min_interval = HZ / 2;
-static int ip6_rt_gc_timeout = 60*HZ;
-int ip6_rt_gc_interval = 30*HZ;
-static int ip6_rt_gc_elasticity = 9;
-static int ip6_rt_mtu_expires = 10*60*HZ;
-static int ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
-
static struct rt6_info * ip6_rt_copy(struct rt6_info *ort);
static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
static struct dst_entry *ip6_negative_advice(struct dst_entry *);
static void ip6_dst_destroy(struct dst_entry *);
static void ip6_dst_ifdown(struct dst_entry *,
struct net_device *dev, int how);
-static int ip6_dst_gc(void);
+static int ip6_dst_gc(struct dst_ops *ops);
static int ip6_pkt_discard(struct sk_buff *skb);
static int ip6_pkt_discard_out(struct sk_buff *skb);
.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),
+ .entries = ATOMIC_INIT(0),
};
static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu)
.check = ip6_dst_check,
.update_pmtu = ip6_rt_blackhole_update_pmtu,
.entry_size = sizeof(struct rt6_info),
+ .entries = ATOMIC_INIT(0),
};
struct rt6_info ip6_null_entry = {
static int ip6_pkt_prohibit(struct sk_buff *skb);
static int ip6_pkt_prohibit_out(struct sk_buff *skb);
-static int ip6_pkt_blk_hole(struct sk_buff *skb);
struct rt6_info ip6_prohibit_entry = {
.u = {
.obsolete = -1,
.error = -EINVAL,
.metrics = { [RTAX_HOPLIMIT - 1] = 255, },
- .input = ip6_pkt_blk_hole,
- .output = ip6_pkt_blk_hole,
+ .input = dst_discard,
+ .output = dst_discard,
.ops = &ip6_dst_ops,
.path = (struct dst_entry*)&ip6_blk_hole_entry,
}
{
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;
- if (dev != init_net.loopback_dev && idev != NULL && idev->dev == dev) {
- struct inet6_dev *loopback_idev = in6_dev_get(init_net.loopback_dev);
+ if (dev != loopback_dev && idev != NULL && idev->dev == dev) {
+ struct inet6_dev *loopback_idev =
+ in6_dev_get(loopback_dev);
if (loopback_idev != NULL) {
rt->rt6i_idev = loopback_idev;
in6_dev_put(idev);
flags |= RT6_LOOKUP_F_HAS_SADDR;
}
- dst = fib6_rule_lookup(&fl, flags, ip6_pol_route_lookup);
+ dst = fib6_rule_lookup(&init_net, &fl, flags, ip6_pol_route_lookup);
if (dst->error == 0)
return (struct rt6_info *) dst;
int ip6_ins_rt(struct rt6_info *rt)
{
- return __ip6_ins_rt(rt, NULL);
+ struct nl_info info = {
+ .nl_net = &init_net,
+ };
+ return __ip6_ins_rt(rt, &info);
}
static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *daddr,
if (rt6_need_strict(&iph->daddr))
flags |= RT6_LOOKUP_F_IFACE;
- skb->dst = fib6_rule_lookup(&fl, flags, ip6_pol_route_input);
+ skb->dst = fib6_rule_lookup(&init_net, &fl, flags, ip6_pol_route_input);
}
static struct rt6_info *ip6_pol_route_output(struct fib6_table *table,
if (!ipv6_addr_any(&fl->fl6_src))
flags |= RT6_LOOKUP_F_HAS_SADDR;
- return fib6_rule_lookup(fl, flags, ip6_pol_route_output);
+ return fib6_rule_lookup(&init_net, fl, flags, ip6_pol_route_output);
}
EXPORT_SYMBOL(ip6_route_output);
-static int ip6_blackhole_output(struct sk_buff *skb)
-{
- kfree_skb(skb);
- return 0;
-}
-
int ip6_dst_blackhole(struct sock *sk, struct dst_entry **dstp, struct flowi *fl)
{
struct rt6_info *ort = (struct rt6_info *) *dstp;
atomic_set(&new->__refcnt, 1);
new->__use = 1;
- new->input = ip6_blackhole_output;
- new->output = ip6_blackhole_output;
+ new->input = dst_discard;
+ new->output = dst_discard;
memcpy(new->metrics, ort->u.dst.metrics, RTAX_MAX*sizeof(u32));
new->dev = ort->u.dst.dev;
{
mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
- if (mtu < ip6_rt_min_advmss)
- mtu = ip6_rt_min_advmss;
+ if (mtu < init_net.ipv6.sysctl.ip6_rt_min_advmss)
+ mtu = init_net.ipv6.sysctl.ip6_rt_min_advmss;
/*
* Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
return mtu;
}
-static struct dst_entry *ndisc_dst_gc_list;
-static DEFINE_SPINLOCK(ndisc_lock);
+static struct dst_entry *icmp6_dst_gc_list;
+static DEFINE_SPINLOCK(icmp6_dst_lock);
-struct dst_entry *ndisc_dst_alloc(struct net_device *dev,
+struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
struct neighbour *neigh,
- struct in6_addr *addr,
- int (*output)(struct sk_buff *))
+ struct in6_addr *addr)
{
struct rt6_info *rt;
struct inet6_dev *idev = in6_dev_get(dev);
rt->u.dst.metrics[RTAX_HOPLIMIT-1] = 255;
rt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(rt->rt6i_dev);
rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(dst_mtu(&rt->u.dst));
- rt->u.dst.output = output;
+ rt->u.dst.output = ip6_output;
#if 0 /* there's no chance to use these for ndisc */
rt->u.dst.flags = ipv6_addr_type(addr) & IPV6_ADDR_UNICAST
rt->rt6i_dst.plen = 128;
#endif
- spin_lock_bh(&ndisc_lock);
- rt->u.dst.next = ndisc_dst_gc_list;
- ndisc_dst_gc_list = &rt->u.dst;
- spin_unlock_bh(&ndisc_lock);
+ spin_lock_bh(&icmp6_dst_lock);
+ rt->u.dst.next = icmp6_dst_gc_list;
+ icmp6_dst_gc_list = &rt->u.dst;
+ spin_unlock_bh(&icmp6_dst_lock);
fib6_force_start_gc();
return &rt->u.dst;
}
-int ndisc_dst_gc(int *more)
+int icmp6_dst_gc(int *more)
{
struct dst_entry *dst, *next, **pprev;
int freed;
next = NULL;
freed = 0;
- spin_lock_bh(&ndisc_lock);
- pprev = &ndisc_dst_gc_list;
+ spin_lock_bh(&icmp6_dst_lock);
+ pprev = &icmp6_dst_gc_list;
while ((dst = *pprev) != NULL) {
if (!atomic_read(&dst->__refcnt)) {
}
}
- spin_unlock_bh(&ndisc_lock);
+ spin_unlock_bh(&icmp6_dst_lock);
return freed;
}
-static int ip6_dst_gc(void)
+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 + ip6_rt_gc_min_interval, now) &&
- atomic_read(&ip6_dst_ops.entries) <= ip6_rt_max_size)
+ 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)
goto out;
expire++;
fib6_run_gc(expire);
last_gc = now;
if (atomic_read(&ip6_dst_ops.entries) < ip6_dst_ops.gc_thresh)
- expire = ip6_rt_gc_timeout>>1;
+ expire = init_net.ipv6.sysctl.ip6_rt_gc_timeout>>1;
out:
- expire -= expire>>ip6_rt_gc_elasticity;
- return (atomic_read(&ip6_dst_ops.entries) > ip6_rt_max_size);
+ 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);
}
/* Clean host part of a prefix. Not necessary in radix tree,
if (cfg->fc_metric == 0)
cfg->fc_metric = IP6_RT_PRIO_USER;
- table = fib6_new_table(cfg->fc_table);
+ table = fib6_new_table(&init_net, cfg->fc_table);
if (table == NULL) {
err = -ENOBUFS;
goto out;
int ip6_del_rt(struct rt6_info *rt)
{
- return __ip6_del_rt(rt, NULL);
+ struct nl_info info = {
+ .nl_net = &init_net,
+ };
+ return __ip6_del_rt(rt, &info);
}
static int ip6_route_del(struct fib6_config *cfg)
struct rt6_info *rt;
int err = -ESRCH;
- table = fib6_get_table(cfg->fc_table);
+ table = fib6_get_table(&init_net, cfg->fc_table);
if (table == NULL)
return err;
if (rt6_need_strict(dest))
flags |= RT6_LOOKUP_F_IFACE;
- return (struct rt6_info *)fib6_rule_lookup((struct flowi *)&rdfl, flags, __ip6_route_redirect);
+ return (struct rt6_info *)fib6_rule_lookup(&init_net,
+ (struct flowi *)&rdfl,
+ flags, __ip6_route_redirect);
}
void rt6_redirect(struct in6_addr *dest, struct in6_addr *src,
rt->u.dst.metrics[RTAX_MTU-1] = pmtu;
if (allfrag)
rt->u.dst.metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG;
- dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires);
+ dst_set_expires(&rt->u.dst, init_net.ipv6.sysctl.ip6_rt_mtu_expires);
rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES;
goto out;
}
* which is 10 mins. After 10 mins the decreased pmtu is expired
* and detecting PMTU increase will be automatically happened.
*/
- dst_set_expires(&nrt->u.dst, ip6_rt_mtu_expires);
+ dst_set_expires(&nrt->u.dst, init_net.ipv6.sysctl.ip6_rt_mtu_expires);
nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES;
ip6_ins_rt(nrt);
struct rt6_info *rt = NULL;
struct fib6_table *table;
- table = fib6_get_table(RT6_TABLE_INFO);
+ table = fib6_get_table(&init_net, RT6_TABLE_INFO);
if (table == NULL)
return NULL;
{
struct fib6_config cfg = {
.fc_table = RT6_TABLE_INFO,
- .fc_metric = 1024,
+ .fc_metric = IP6_RT_PRIO_USER,
.fc_ifindex = ifindex,
.fc_dst_len = prefixlen,
.fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
struct rt6_info *rt;
struct fib6_table *table;
- table = fib6_get_table(RT6_TABLE_DFLT);
+ table = fib6_get_table(&init_net, RT6_TABLE_DFLT);
if (table == NULL)
return NULL;
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)
{
struct fib6_config cfg = {
.fc_table = RT6_TABLE_DFLT,
- .fc_metric = 1024,
+ .fc_metric = IP6_RT_PRIO_USER,
.fc_ifindex = dev->ifindex,
.fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
struct fib6_table *table;
/* NOTE: Keep consistent with rt6_get_dflt_router */
- table = fib6_get_table(RT6_TABLE_DFLT);
+ table = fib6_get_table(&init_net, RT6_TABLE_DFLT);
if (table == NULL)
return;
cfg->fc_src_len = rtmsg->rtmsg_src_len;
cfg->fc_flags = rtmsg->rtmsg_flags;
+ cfg->fc_nlinfo.nl_net = &init_net;
+
ipv6_addr_copy(&cfg->fc_dst, &rtmsg->rtmsg_dst);
ipv6_addr_copy(&cfg->fc_src, &rtmsg->rtmsg_src);
ipv6_addr_copy(&cfg->fc_gateway, &rtmsg->rtmsg_gateway);
* Drop the packet on the floor
*/
-static inline int ip6_pkt_drop(struct sk_buff *skb, int code,
- int ipstats_mib_noroutes)
+static int ip6_pkt_drop(struct sk_buff *skb, int code, int ipstats_mib_noroutes)
{
int type;
switch (ipstats_mib_noroutes) {
return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
}
-static int ip6_pkt_blk_hole(struct sk_buff *skb)
-{
- kfree_skb(skb);
- return 0;
-}
-
#endif
/*
ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
rt->rt6i_dst.plen = 128;
- rt->rt6i_table = fib6_get_table(RT6_TABLE_LOCAL);
+ rt->rt6i_table = fib6_get_table(&init_net, RT6_TABLE_LOCAL);
atomic_set(&rt->u.dst.__refcnt, 1);
*/
if (rt->rt6i_dev == arg->dev &&
!dst_metric_locked(&rt->u.dst, RTAX_MTU) &&
- (dst_mtu(&rt->u.dst) > arg->mtu ||
+ (dst_mtu(&rt->u.dst) >= arg->mtu ||
(dst_mtu(&rt->u.dst) < arg->mtu &&
dst_mtu(&rt->u.dst) == idev->cnf.mtu6))) {
rt->u.dst.metrics[RTAX_MTU-1] = arg->mtu;
cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
cfg->fc_nlinfo.nlh = nlh;
+ cfg->fc_nlinfo.nl_net = skb->sk->sk_net;
if (tb[RTA_GATEWAY]) {
nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
{
+ struct net *net = skb->sk->sk_net;
struct fib6_config cfg;
int err;
+ if (net != &init_net)
+ return -EINVAL;
+
err = rtm_to_fib6_config(skb, nlh, &cfg);
if (err < 0)
return err;
static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
{
+ struct net *net = skb->sk->sk_net;
struct fib6_config cfg;
int err;
+ if (net != &init_net)
+ return -EINVAL;
+
err = rtm_to_fib6_config(skb, nlh, &cfg);
if (err < 0)
return err;
NLA_PUT_U32(skb, RTA_IIF, iif);
else if (dst) {
struct in6_addr saddr_buf;
- if (ipv6_get_saddr(&rt->u.dst, dst, &saddr_buf) == 0)
+ if (ipv6_dev_get_saddr(ip6_dst_idev(&rt->u.dst)->dev,
+ dst, &saddr_buf) == 0)
NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
}
static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
{
+ struct net *net = in_skb->sk->sk_net;
struct nlattr *tb[RTA_MAX+1];
struct rt6_info *rt;
struct sk_buff *skb;
struct flowi fl;
int err, iif = 0;
+ if (net != &init_net)
+ return -EINVAL;
+
err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
if (err < 0)
goto errout;
goto errout;
}
- err = rtnl_unicast(skb, NETLINK_CB(in_skb).pid);
+ err = rtnl_unicast(skb, &init_net, NETLINK_CB(in_skb).pid);
errout:
return err;
}
void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
{
struct sk_buff *skb;
- u32 pid = 0, seq = 0;
- struct nlmsghdr *nlh = NULL;
- int err = -ENOBUFS;
-
- if (info) {
- pid = info->pid;
- nlh = info->nlh;
- if (nlh)
- seq = nlh->nlmsg_seq;
- }
+ u32 seq;
+ int err;
+
+ err = -ENOBUFS;
+ seq = info->nlh != NULL ? info->nlh->nlmsg_seq : 0;
skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
if (skb == NULL)
goto errout;
- err = rt6_fill_node(skb, rt, NULL, NULL, 0, event, pid, seq, 0, 0);
+ err = rt6_fill_node(skb, rt, NULL, NULL, 0,
+ event, info->pid, seq, 0, 0);
if (err < 0) {
/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
WARN_ON(err == -EMSGSIZE);
kfree_skb(skb);
goto errout;
}
- err = rtnl_notify(skb, pid, RTNLGRP_IPV6_ROUTE, nlh, gfp_any());
+ err = rtnl_notify(skb, &init_net, info->pid,
+ RTNLGRP_IPV6_ROUTE, info->nlh, gfp_any());
errout:
if (err < 0)
- rtnl_set_sk_err(RTNLGRP_IPV6_ROUTE, err);
+ rtnl_set_sk_err(&init_net, RTNLGRP_IPV6_ROUTE, err);
}
/*
.llseek = seq_lseek,
.release = single_release,
};
+
+static int ipv6_route_proc_init(struct net *net)
+{
+ int ret = -ENOMEM;
+ if (!proc_net_fops_create(net, "ipv6_route",
+ 0, &ipv6_route_proc_fops))
+ goto out;
+
+ if (!proc_net_fops_create(net, "rt6_stats",
+ S_IRUGO, &rt6_stats_seq_fops))
+ goto out_ipv6_route;
+
+ ret = 0;
+out:
+ return ret;
+out_ipv6_route:
+ proc_net_remove(net, "ipv6_route");
+ goto out;
+}
+
+static void ipv6_route_proc_fini(struct net *net)
+{
+ proc_net_remove(net, "ipv6_route");
+ proc_net_remove(net, "rt6_stats");
+}
+#else
+static inline int ipv6_route_proc_init(struct net *net)
+{
+ return 0;
+}
+static inline void ipv6_route_proc_fini(struct net *net)
+{
+ return ;
+}
#endif /* CONFIG_PROC_FS */
#ifdef CONFIG_SYSCTL
-static int flush_delay;
-
static
int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, struct file * filp,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
+ int delay = init_net.ipv6.sysctl.flush_delay;
if (write) {
proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
- fib6_run_gc(flush_delay <= 0 ? ~0UL : (unsigned long)flush_delay);
+ fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay);
return 0;
} else
return -EINVAL;
}
-ctl_table ipv6_route_table[] = {
+ctl_table ipv6_route_table_template[] = {
{
.procname = "flush",
- .data = &flush_delay,
+ .data = &init_net.ipv6.sysctl.flush_delay,
.maxlen = sizeof(int),
.mode = 0200,
.proc_handler = &ipv6_sysctl_rtcache_flush
{
.ctl_name = NET_IPV6_ROUTE_MAX_SIZE,
.procname = "max_size",
- .data = &ip6_rt_max_size,
+ .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
{
.ctl_name = NET_IPV6_ROUTE_GC_MIN_INTERVAL,
.procname = "gc_min_interval",
- .data = &ip6_rt_gc_min_interval,
+ .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
{
.ctl_name = NET_IPV6_ROUTE_GC_TIMEOUT,
.procname = "gc_timeout",
- .data = &ip6_rt_gc_timeout,
+ .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
{
.ctl_name = NET_IPV6_ROUTE_GC_INTERVAL,
.procname = "gc_interval",
- .data = &ip6_rt_gc_interval,
+ .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
{
.ctl_name = NET_IPV6_ROUTE_GC_ELASTICITY,
.procname = "gc_elasticity",
- .data = &ip6_rt_gc_elasticity,
+ .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
{
.ctl_name = NET_IPV6_ROUTE_MTU_EXPIRES,
.procname = "mtu_expires",
- .data = &ip6_rt_mtu_expires,
+ .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
{
.ctl_name = NET_IPV6_ROUTE_MIN_ADVMSS,
.procname = "min_adv_mss",
- .data = &ip6_rt_min_advmss,
+ .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
{
.ctl_name = NET_IPV6_ROUTE_GC_MIN_INTERVAL_MS,
.procname = "gc_min_interval_ms",
- .data = &ip6_rt_gc_min_interval,
+ .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_ms_jiffies,
{ .ctl_name = 0 }
};
+struct ctl_table *ipv6_route_sysctl_init(struct net *net)
+{
+ struct ctl_table *table;
+
+ table = kmemdup(ipv6_route_table_template,
+ sizeof(ipv6_route_table_template),
+ GFP_KERNEL);
+
+ if (table) {
+ table[0].data = &net->ipv6.sysctl.flush_delay;
+ /* table[1].data will be handled when we have
+ routes per namespace */
+ 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;
+ table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
+ table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
+ table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
+ table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
+ }
+
+ return table;
+}
#endif
-void __init ip6_route_init(void)
+int __init ip6_route_init(void)
{
+ int ret;
+
ip6_dst_ops.kmem_cachep =
kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
- SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (!ip6_dst_ops.kmem_cachep)
+ return -ENOMEM;
+
ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops.kmem_cachep;
- fib6_init();
- proc_net_fops_create(&init_net, "ipv6_route", 0, &ipv6_route_proc_fops);
- proc_net_fops_create(&init_net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
-#ifdef CONFIG_XFRM
- xfrm6_init();
-#endif
-#ifdef CONFIG_IPV6_MULTIPLE_TABLES
- fib6_rules_init();
-#endif
+ ret = fib6_init();
+ if (ret)
+ goto out_kmem_cache;
- __rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL);
- __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL);
- __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL);
+ ret = ipv6_route_proc_init(&init_net);
+ if (ret)
+ goto out_fib6_init;
+
+ ret = xfrm6_init();
+ if (ret)
+ goto out_proc_init;
+
+ ret = fib6_rules_init();
+ if (ret)
+ goto xfrm6_init;
+
+ ret = -ENOBUFS;
+ if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL) ||
+ __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL) ||
+ __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL))
+ goto fib6_rules_init;
+
+ ret = 0;
+out:
+ return ret;
+
+fib6_rules_init:
+ fib6_rules_cleanup();
+xfrm6_init:
+ xfrm6_fini();
+out_proc_init:
+ ipv6_route_proc_fini(&init_net);
+out_fib6_init:
+ rt6_ifdown(NULL);
+ fib6_gc_cleanup();
+out_kmem_cache:
+ kmem_cache_destroy(ip6_dst_ops.kmem_cachep);
+ goto out;
}
void ip6_route_cleanup(void)
{
-#ifdef CONFIG_IPV6_MULTIPLE_TABLES
fib6_rules_cleanup();
-#endif
-#ifdef CONFIG_PROC_FS
- proc_net_remove(&init_net, "ipv6_route");
- proc_net_remove(&init_net, "rt6_stats");
-#endif
-#ifdef CONFIG_XFRM
+ ipv6_route_proc_fini(&init_net);
xfrm6_fini();
-#endif
rt6_ifdown(NULL);
fib6_gc_cleanup();
kmem_cache_destroy(ip6_dst_ops.kmem_cachep);