X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=net%2Fipv6%2Faddrconf.c;h=e40213db9e4c6355e665c8684e4549feca77614d;hb=59bd26582de660d4c9c26125747f1b4a5eb40d1e;hp=ea1673d290783b9e9008f4a3396a149f55aa8370;hpb=f52295a9c55ccb4d9b3580ce889f958ac740a44b;p=powerpc.git diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index ea1673d290..e40213db9e 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -102,7 +102,15 @@ #ifdef CONFIG_SYSCTL static void addrconf_sysctl_register(struct inet6_dev *idev); -static void addrconf_sysctl_unregister(struct ipv6_devconf *p); +static void addrconf_sysctl_unregister(struct inet6_dev *idev); +#else +static inline void addrconf_sysctl_register(struct inet6_dev *idev) +{ +} + +static inline void addrconf_sysctl_unregister(struct inet6_dev *idev) +{ +} #endif #ifdef CONFIG_IPV6_PRIVACY @@ -141,7 +149,8 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa); static void inet6_prefix_notify(int event, struct inet6_dev *idev, struct prefix_info *pinfo); -static int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev); +static int ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr, + struct net_device *dev); static ATOMIC_NOTIFIER_HEAD(inet6addr_chain); @@ -256,16 +265,13 @@ static void addrconf_mod_timer(struct inet6_ifaddr *ifp, static int snmp6_alloc_dev(struct inet6_dev *idev) { if (snmp_mib_init((void **)idev->stats.ipv6, - sizeof(struct ipstats_mib), - __alignof__(struct ipstats_mib)) < 0) + sizeof(struct ipstats_mib)) < 0) goto err_ip; if (snmp_mib_init((void **)idev->stats.icmpv6, - sizeof(struct icmpv6_mib), - __alignof__(struct icmpv6_mib)) < 0) + sizeof(struct icmpv6_mib)) < 0) goto err_icmp; if (snmp_mib_init((void **)idev->stats.icmpv6msg, - sizeof(struct icmpv6msg_mib), - __alignof__(struct icmpv6msg_mib)) < 0) + sizeof(struct icmpv6msg_mib)) < 0) goto err_icmpmsg; return 0; @@ -329,7 +335,7 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) rwlock_init(&ndev->lock); ndev->dev = dev; - memcpy(&ndev->cnf, &ipv6_devconf_dflt, sizeof(ndev->cnf)); + memcpy(&ndev->cnf, dev->nd_net->ipv6.devconf_dflt, sizeof(ndev->cnf)); ndev->cnf.mtu6 = dev->mtu; ndev->cnf.sysctl = NULL; ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl); @@ -395,13 +401,7 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) ipv6_mc_init_dev(ndev); ndev->tstamp = jiffies; -#ifdef CONFIG_SYSCTL - neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6, - NET_IPV6_NEIGH, "ipv6", - &ndisc_ifinfo_sysctl_change, - NULL); addrconf_sysctl_register(ndev); -#endif /* protected by rtnl_lock */ rcu_assign_pointer(dev->ip6_ptr, ndev); @@ -457,18 +457,18 @@ static void dev_forward_change(struct inet6_dev *idev) } -static void addrconf_forward_change(void) +static void addrconf_forward_change(struct net *net, __s32 newf) { struct net_device *dev; struct inet6_dev *idev; read_lock(&dev_base_lock); - for_each_netdev(&init_net, dev) { + for_each_netdev(net, dev) { rcu_read_lock(); idev = __in6_dev_get(dev); if (idev) { - int changed = (!idev->cnf.forwarding) ^ (!ipv6_devconf.forwarding); - idev->cnf.forwarding = ipv6_devconf.forwarding; + int changed = (!idev->cnf.forwarding) ^ (!newf); + idev->cnf.forwarding = newf; if (changed) dev_forward_change(idev); } @@ -476,6 +476,25 @@ static void addrconf_forward_change(void) } read_unlock(&dev_base_lock); } + +static void addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old) +{ + struct net *net; + + net = (struct net *)table->extra2; + if (p == &net->ipv6.devconf_dflt->forwarding) + return; + + if (p == &net->ipv6.devconf_all->forwarding) { + __s32 newf = net->ipv6.devconf_all->forwarding; + net->ipv6.devconf_dflt->forwarding = newf; + addrconf_forward_change(net, newf); + } else if ((!*p) ^ (!old)) + dev_forward_change((struct inet6_dev *)table->extra1); + + if (*p) + rt6_purge_dflt_routers(); +} #endif /* Nobody refers to this ifaddr, destroy it */ @@ -542,7 +561,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, write_lock(&addrconf_hash_lock); /* Ignore adding duplicate addresses on an interface */ - if (ipv6_chk_same_addr(addr, idev->dev)) { + if (ipv6_chk_same_addr(&init_net, addr, idev->dev)) { ADBG(("ipv6_add_addr: already assigned\n")); err = -EEXIST; goto out; @@ -1188,13 +1207,16 @@ static int ipv6_count_addresses(struct inet6_dev *idev) return cnt; } -int ipv6_chk_addr(struct in6_addr *addr, struct net_device *dev, int strict) +int ipv6_chk_addr(struct net *net, struct in6_addr *addr, + struct net_device *dev, int strict) { struct inet6_ifaddr * ifp; u8 hash = ipv6_addr_hash(addr); read_lock_bh(&addrconf_hash_lock); for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) { + if (ifp->idev->dev->nd_net != net) + continue; if (ipv6_addr_equal(&ifp->addr, addr) && !(ifp->flags&IFA_F_TENTATIVE)) { if (dev == NULL || ifp->idev->dev == dev || @@ -1205,16 +1227,18 @@ int ipv6_chk_addr(struct in6_addr *addr, struct net_device *dev, int strict) read_unlock_bh(&addrconf_hash_lock); return ifp != NULL; } - EXPORT_SYMBOL(ipv6_chk_addr); static -int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev) +int ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr, + struct net_device *dev) { struct inet6_ifaddr * ifp; u8 hash = ipv6_addr_hash(addr); for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) { + if (ifp->idev->dev->nd_net != net) + continue; if (ipv6_addr_equal(&ifp->addr, addr)) { if (dev == NULL || ifp->idev->dev == dev) break; @@ -1223,13 +1247,16 @@ int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev) return ifp != NULL; } -struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, struct net_device *dev, int strict) +struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, struct in6_addr *addr, + struct net_device *dev, int strict) { struct inet6_ifaddr * ifp; u8 hash = ipv6_addr_hash(addr); read_lock_bh(&addrconf_hash_lock); for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) { + if (ifp->idev->dev->nd_net != net) + continue; if (ipv6_addr_equal(&ifp->addr, addr)) { if (dev == NULL || ifp->idev->dev == dev || !(ifp->scope&(IFA_LINK|IFA_HOST) || strict)) { @@ -1715,7 +1742,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len) ok: - ifp = ipv6_get_ifaddr(&addr, dev, 1); + ifp = ipv6_get_ifaddr(&init_net, &addr, dev, 1); if (ifp == NULL && valid_lft) { int max_addresses = in6_dev->cnf.max_addresses; @@ -1873,7 +1900,7 @@ int addrconf_set_dstaddr(void __user *arg) p.iph.ihl = 5; p.iph.protocol = IPPROTO_IPV6; p.iph.ttl = 64; - ifr.ifr_ifru.ifru_data = (void __user *)&p; + ifr.ifr_ifru.ifru_data = (__force void __user *)&p; oldfs = get_fs(); set_fs(KERNEL_DS); err = dev->do_ioctl(dev, &ifr, SIOCADDTUNNEL); @@ -2379,15 +2406,8 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, case NETDEV_CHANGENAME: if (idev) { snmp6_unregister_dev(idev); -#ifdef CONFIG_SYSCTL - addrconf_sysctl_unregister(&idev->cnf); - neigh_sysctl_unregister(idev->nd_parms); - neigh_sysctl_register(dev, idev->nd_parms, - NET_IPV6, NET_IPV6_NEIGH, "ipv6", - &ndisc_ifinfo_sysctl_change, - NULL); + addrconf_sysctl_unregister(idev); addrconf_sysctl_register(idev); -#endif err = snmp6_register_dev(idev); if (err) return notifier_from_errno(err); @@ -2511,10 +2531,7 @@ static int addrconf_ifdown(struct net_device *dev, int how) /* Shot the device (if unregistered) */ if (how == 1) { -#ifdef CONFIG_SYSCTL - addrconf_sysctl_unregister(&idev->cnf); - neigh_sysctl_unregister(idev->nd_parms); -#endif + addrconf_sysctl_unregister(idev); neigh_parms_release(&nd_tbl, idev->nd_parms); neigh_ifdown(&nd_tbl, dev); in6_dev_put(idev); @@ -2728,6 +2745,7 @@ static void addrconf_dad_run(struct inet6_dev *idev) { #ifdef CONFIG_PROC_FS struct if6_iter_state { + struct seq_net_private p; int bucket; }; @@ -2735,9 +2753,13 @@ static struct inet6_ifaddr *if6_get_first(struct seq_file *seq) { struct inet6_ifaddr *ifa = NULL; struct if6_iter_state *state = seq->private; + struct net *net = state->p.net; for (state->bucket = 0; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) { ifa = inet6_addr_lst[state->bucket]; + + while (ifa && ifa->idev->dev->nd_net != net) + ifa = ifa->lst_next; if (ifa) break; } @@ -2747,13 +2769,22 @@ static struct inet6_ifaddr *if6_get_first(struct seq_file *seq) static struct inet6_ifaddr *if6_get_next(struct seq_file *seq, struct inet6_ifaddr *ifa) { struct if6_iter_state *state = seq->private; + struct net *net = state->p.net; ifa = ifa->lst_next; try_again: + if (ifa) { + if (ifa->idev->dev->nd_net != net) { + ifa = ifa->lst_next; + goto try_again; + } + } + if (!ifa && ++state->bucket < IN6_ADDR_HSIZE) { ifa = inet6_addr_lst[state->bucket]; goto try_again; } + return ifa; } @@ -2768,6 +2799,7 @@ static struct inet6_ifaddr *if6_get_idx(struct seq_file *seq, loff_t pos) } static void *if6_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(addrconf_hash_lock) { read_lock_bh(&addrconf_hash_lock); return if6_get_idx(seq, *pos); @@ -2783,6 +2815,7 @@ static void *if6_seq_next(struct seq_file *seq, void *v, loff_t *pos) } static void if6_seq_stop(struct seq_file *seq, void *v) + __releases(addrconf_hash_lock) { read_unlock_bh(&addrconf_hash_lock); } @@ -2810,8 +2843,8 @@ static const struct seq_operations if6_seq_ops = { static int if6_seq_open(struct inode *inode, struct file *file) { - return seq_open_private(file, &if6_seq_ops, - sizeof(struct if6_iter_state)); + return seq_open_net(inode, file, &if6_seq_ops, + sizeof(struct if6_iter_state)); } static const struct file_operations if6_fops = { @@ -2819,31 +2852,48 @@ static const struct file_operations if6_fops = { .open = if6_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release_private, + .release = seq_release_net, }; -int __init if6_proc_init(void) +static int if6_proc_net_init(struct net *net) { - if (!proc_net_fops_create(&init_net, "if_inet6", S_IRUGO, &if6_fops)) + if (!proc_net_fops_create(net, "if_inet6", S_IRUGO, &if6_fops)) return -ENOMEM; return 0; } +static void if6_proc_net_exit(struct net *net) +{ + proc_net_remove(net, "if_inet6"); +} + +static struct pernet_operations if6_proc_net_ops = { + .init = if6_proc_net_init, + .exit = if6_proc_net_exit, +}; + +int __init if6_proc_init(void) +{ + return register_pernet_subsys(&if6_proc_net_ops); +} + void if6_proc_exit(void) { - proc_net_remove(&init_net, "if_inet6"); + unregister_pernet_subsys(&if6_proc_net_ops); } #endif /* CONFIG_PROC_FS */ #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) /* Check if address is a home address configured on any interface. */ -int ipv6_chk_home_addr(struct in6_addr *addr) +int ipv6_chk_home_addr(struct net *net, struct in6_addr *addr) { int ret = 0; struct inet6_ifaddr * ifp; u8 hash = ipv6_addr_hash(addr); read_lock_bh(&addrconf_hash_lock); for (ifp = inet6_addr_lst[hash]; ifp; ifp = ifp->lst_next) { + if (ifp->idev->dev->nd_net != net) + continue; if (ipv6_addr_cmp(&ifp->addr, addr) == 0 && (ifp->flags & IFA_F_HOMEADDRESS)) { ret = 1; @@ -3092,7 +3142,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) /* We ignore other flags so far. */ ifa_flags = ifm->ifa_flags & (IFA_F_NODAD | IFA_F_HOMEADDRESS); - ifa = ipv6_get_ifaddr(pfx, dev, 1); + ifa = ipv6_get_ifaddr(net, pfx, dev, 1); if (ifa == NULL) { /* * It would be best to check for !NLM_F_CREATE here but @@ -3285,11 +3335,11 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, ifa = ifa->if_next, ip_idx++) { if (ip_idx < s_ip_idx) continue; - if ((err = inet6_fill_ifaddr(skb, ifa, - NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, RTM_NEWADDR, - NLM_F_MULTI)) <= 0) - goto done; + err = inet6_fill_ifaddr(skb, ifa, + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, + RTM_NEWADDR, + NLM_F_MULTI); } break; case MULTICAST_ADDR: @@ -3298,11 +3348,11 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, ifmca = ifmca->next, ip_idx++) { if (ip_idx < s_ip_idx) continue; - if ((err = inet6_fill_ifmcaddr(skb, ifmca, - NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, RTM_GETMULTICAST, - NLM_F_MULTI)) <= 0) - goto done; + err = inet6_fill_ifmcaddr(skb, ifmca, + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, + RTM_GETMULTICAST, + NLM_F_MULTI); } break; case ANYCAST_ADDR: @@ -3311,11 +3361,11 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, ifaca = ifaca->aca_next, ip_idx++) { if (ip_idx < s_ip_idx) continue; - if ((err = inet6_fill_ifacaddr(skb, ifaca, - NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, RTM_GETANYCAST, - NLM_F_MULTI)) <= 0) - goto done; + err = inet6_fill_ifacaddr(skb, ifaca, + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, + RTM_GETANYCAST, + NLM_F_MULTI); } break; default: @@ -3323,14 +3373,12 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, } read_unlock_bh(&idev->lock); in6_dev_put(idev); + + if (err <= 0) + break; cont: idx++; } -done: - if (err <= 0) { - read_unlock_bh(&idev->lock); - in6_dev_put(idev); - } cb->args[0] = idx; cb->args[1] = ip_idx; return skb->len; @@ -3399,7 +3447,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr* nlh, if (ifm->ifa_index) dev = __dev_get_by_index(&init_net, ifm->ifa_index); - if ((ifa = ipv6_get_ifaddr(addr, dev, 1)) == NULL) { + if ((ifa = ipv6_get_ifaddr(net, addr, dev, 1)) == NULL) { err = -EADDRNOTAVAIL; goto errout; } @@ -3771,22 +3819,8 @@ int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp, ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); - if (write && valp != &ipv6_devconf_dflt.forwarding) { - if (valp != &ipv6_devconf.forwarding) { - if ((!*valp) ^ (!val)) { - struct inet6_dev *idev = (struct inet6_dev *)ctl->extra1; - if (idev == NULL) - return ret; - dev_forward_change(idev); - } - } else { - ipv6_devconf_dflt.forwarding = ipv6_devconf.forwarding; - addrconf_forward_change(); - } - if (*valp) - rt6_purge_dflt_routers(); - } - + if (write) + addrconf_fixup_forwarding(ctl, valp, val); return ret; } @@ -3797,6 +3831,7 @@ static int addrconf_sysctl_forward_strategy(ctl_table *table, void __user *newval, size_t newlen) { int *valp = table->data; + int val = *valp; int new; if (!newval || !newlen) @@ -3821,26 +3856,8 @@ static int addrconf_sysctl_forward_strategy(ctl_table *table, } } - if (valp != &ipv6_devconf_dflt.forwarding) { - if (valp != &ipv6_devconf.forwarding) { - struct inet6_dev *idev = (struct inet6_dev *)table->extra1; - int changed; - if (unlikely(idev == NULL)) - return -ENODEV; - changed = (!*valp) ^ (!new); - *valp = new; - if (changed) - dev_forward_change(idev); - } else { - *valp = new; - addrconf_forward_change(); - } - - if (*valp) - rt6_purge_dflt_routers(); - } else - *valp = new; - + *valp = new; + addrconf_fixup_forwarding(table, valp, val); return 1; } @@ -3848,10 +3865,7 @@ static struct addrconf_sysctl_table { struct ctl_table_header *sysctl_header; ctl_table addrconf_vars[__NET_IPV6_MAX]; - ctl_table addrconf_dev[2]; - ctl_table addrconf_conf_dir[2]; - ctl_table addrconf_proto_dir[2]; - ctl_table addrconf_root_dir[2]; + char *dev_name; } addrconf_sysctl __read_mostly = { .sysctl_header = NULL, .addrconf_vars = { @@ -4072,58 +4086,25 @@ static struct addrconf_sysctl_table .ctl_name = 0, /* sentinel */ } }, - .addrconf_dev = { - { - .ctl_name = NET_PROTO_CONF_ALL, - .procname = "all", - .mode = 0555, - .child = addrconf_sysctl.addrconf_vars, - }, - { - .ctl_name = 0, /* sentinel */ - } - }, - .addrconf_conf_dir = { - { - .ctl_name = NET_IPV6_CONF, - .procname = "conf", - .mode = 0555, - .child = addrconf_sysctl.addrconf_dev, - }, - { - .ctl_name = 0, /* sentinel */ - } - }, - .addrconf_proto_dir = { - { - .ctl_name = NET_IPV6, - .procname = "ipv6", - .mode = 0555, - .child = addrconf_sysctl.addrconf_conf_dir, - }, - { - .ctl_name = 0, /* sentinel */ - } - }, - .addrconf_root_dir = { - { - .ctl_name = CTL_NET, - .procname = "net", - .mode = 0555, - .child = addrconf_sysctl.addrconf_proto_dir, - }, - { - .ctl_name = 0, /* sentinel */ - } - }, }; -static void __addrconf_sysctl_register(char *dev_name, int ctl_name, - struct inet6_dev *idev, struct ipv6_devconf *p) +static int __addrconf_sysctl_register(struct net *net, char *dev_name, + int ctl_name, struct inet6_dev *idev, struct ipv6_devconf *p) { int i; struct addrconf_sysctl_table *t; +#define ADDRCONF_CTL_PATH_DEV 3 + + struct ctl_path addrconf_ctl_path[] = { + { .procname = "net", .ctl_name = CTL_NET, }, + { .procname = "ipv6", .ctl_name = NET_IPV6, }, + { .procname = "conf", .ctl_name = NET_IPV6_CONF, }, + { /* to be set */ }, + { }, + }; + + t = kmemdup(&addrconf_sysctl, sizeof(*t), GFP_KERNEL); if (t == NULL) goto out; @@ -4131,6 +4112,7 @@ static void __addrconf_sysctl_register(char *dev_name, int ctl_name, for (i=0; t->addrconf_vars[i].data; i++) { t->addrconf_vars[i].data += (char*)p - (char*)&ipv6_devconf; t->addrconf_vars[i].extra1 = idev; /* embedded; no ref */ + t->addrconf_vars[i].extra2 = net; } /* @@ -4138,53 +4120,126 @@ static void __addrconf_sysctl_register(char *dev_name, int ctl_name, * by sysctl and we wouldn't want anyone to change it under our feet * (see SIOCSIFNAME). */ - dev_name = kstrdup(dev_name, GFP_KERNEL); - if (!dev_name) + t->dev_name = kstrdup(dev_name, GFP_KERNEL); + if (!t->dev_name) goto free; - t->addrconf_dev[0].ctl_name = ctl_name; - t->addrconf_dev[0].procname = dev_name; - - t->addrconf_dev[0].child = t->addrconf_vars; - t->addrconf_conf_dir[0].child = t->addrconf_dev; - t->addrconf_proto_dir[0].child = t->addrconf_conf_dir; - t->addrconf_root_dir[0].child = t->addrconf_proto_dir; + addrconf_ctl_path[ADDRCONF_CTL_PATH_DEV].procname = t->dev_name; + addrconf_ctl_path[ADDRCONF_CTL_PATH_DEV].ctl_name = ctl_name; - t->sysctl_header = register_sysctl_table(t->addrconf_root_dir); + t->sysctl_header = register_net_sysctl_table(net, addrconf_ctl_path, + t->addrconf_vars); if (t->sysctl_header == NULL) goto free_procname; p->sysctl = t; - return; + return 0; free_procname: - kfree(dev_name); + kfree(t->dev_name); free: kfree(t); out: - return; + return -ENOBUFS; +} + +static void __addrconf_sysctl_unregister(struct ipv6_devconf *p) +{ + struct addrconf_sysctl_table *t; + + if (p->sysctl == NULL) + return; + + t = p->sysctl; + p->sysctl = NULL; + unregister_sysctl_table(t->sysctl_header); + kfree(t->dev_name); + kfree(t); } static void addrconf_sysctl_register(struct inet6_dev *idev) { - __addrconf_sysctl_register(idev->dev->name, idev->dev->ifindex, - idev, &idev->cnf); + neigh_sysctl_register(idev->dev, idev->nd_parms, NET_IPV6, + NET_IPV6_NEIGH, "ipv6", + &ndisc_ifinfo_sysctl_change, + NULL); + __addrconf_sysctl_register(idev->dev->nd_net, idev->dev->name, + idev->dev->ifindex, idev, &idev->cnf); } -static void addrconf_sysctl_unregister(struct ipv6_devconf *p) +static void addrconf_sysctl_unregister(struct inet6_dev *idev) { - if (p->sysctl) { - struct addrconf_sysctl_table *t = p->sysctl; - p->sysctl = NULL; - unregister_sysctl_table(t->sysctl_header); - kfree(t->addrconf_dev[0].procname); - kfree(t); - } + __addrconf_sysctl_unregister(&idev->cnf); + neigh_sysctl_unregister(idev->nd_parms); } #endif +static int addrconf_init_net(struct net *net) +{ + int err; + struct ipv6_devconf *all, *dflt; + + err = -ENOMEM; + all = &ipv6_devconf; + dflt = &ipv6_devconf_dflt; + + if (net != &init_net) { + all = kmemdup(all, sizeof(ipv6_devconf), GFP_KERNEL); + if (all == NULL) + goto err_alloc_all; + + dflt = kmemdup(dflt, sizeof(ipv6_devconf_dflt), GFP_KERNEL); + if (dflt == NULL) + goto err_alloc_dflt; + } + + net->ipv6.devconf_all = all; + net->ipv6.devconf_dflt = dflt; + +#ifdef CONFIG_SYSCTL + err = __addrconf_sysctl_register(net, "all", NET_PROTO_CONF_ALL, + NULL, all); + if (err < 0) + goto err_reg_all; + + err = __addrconf_sysctl_register(net, "default", NET_PROTO_CONF_DEFAULT, + NULL, dflt); + if (err < 0) + goto err_reg_dflt; +#endif + return 0; + +#ifdef CONFIG_SYSCTL +err_reg_dflt: + __addrconf_sysctl_unregister(all); +err_reg_all: + kfree(dflt); +#endif +err_alloc_dflt: + kfree(all); +err_alloc_all: + return err; +} + +static void addrconf_exit_net(struct net *net) +{ +#ifdef CONFIG_SYSCTL + __addrconf_sysctl_unregister(net->ipv6.devconf_dflt); + __addrconf_sysctl_unregister(net->ipv6.devconf_all); +#endif + if (net != &init_net) { + kfree(net->ipv6.devconf_dflt); + kfree(net->ipv6.devconf_all); + } +} + +static struct pernet_operations addrconf_ops = { + .init = addrconf_init_net, + .exit = addrconf_exit_net, +}; + /* * Device notifier */ @@ -4217,6 +4272,8 @@ int __init addrconf_init(void) return err; } + register_pernet_subsys(&addrconf_ops); + /* The addrconf netdev notifier requires that loopback_dev * has it's ipv6 private information allocated and setup * before it can bring up and give link-local addresses @@ -4240,7 +4297,7 @@ int __init addrconf_init(void) err = -ENOMEM; rtnl_unlock(); if (err) - return err; + goto errlo; ip6_null_entry.u.dst.dev = init_net.loopback_dev; ip6_null_entry.rt6i_idev = in6_dev_get(init_net.loopback_dev); @@ -4268,21 +4325,16 @@ int __init addrconf_init(void) ipv6_addr_label_rtnl_register(); -#ifdef CONFIG_SYSCTL - __addrconf_sysctl_register("all", NET_PROTO_CONF_ALL, - NULL, &ipv6_devconf); - __addrconf_sysctl_register("default", NET_PROTO_CONF_DEFAULT, - NULL, &ipv6_devconf_dflt); -#endif - return 0; errout: unregister_netdevice_notifier(&ipv6_dev_notf); +errlo: + unregister_pernet_subsys(&addrconf_ops); return err; } -void __exit addrconf_cleanup(void) +void addrconf_cleanup(void) { struct net_device *dev; struct inet6_ifaddr *ifa; @@ -4290,10 +4342,7 @@ void __exit addrconf_cleanup(void) unregister_netdevice_notifier(&ipv6_dev_notf); -#ifdef CONFIG_SYSCTL - addrconf_sysctl_unregister(&ipv6_devconf_dflt); - addrconf_sysctl_unregister(&ipv6_devconf); -#endif + unregister_pernet_subsys(&addrconf_ops); rtnl_lock();