#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
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);
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;
rwlock_init(&ndev->lock);
ndev->dev = dev;
- memcpy(&ndev->cnf, &ipv6_devconf_dflt, sizeof(ndev->cnf));
+ memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));
ndev->cnf.mtu6 = dev->mtu;
ndev->cnf.sysctl = NULL;
ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
if (snmp6_alloc_dev(ndev) < 0) {
ADBG((KERN_WARNING
"%s(): cannot allocate memory for statistics; dev=%s.\n",
- __FUNCTION__, dev->name));
+ __func__, dev->name));
neigh_parms_release(&nd_tbl, ndev->nd_parms);
ndev->dead = 1;
in6_dev_finish_destroy(ndev);
if (snmp6_register_dev(ndev) < 0) {
ADBG((KERN_WARNING
"%s(): cannot create /proc/net/dev_snmp6/%s\n",
- __FUNCTION__, dev->name));
+ __func__, dev->name));
neigh_parms_release(&nd_tbl, ndev->nd_parms);
ndev->dead = 1;
in6_dev_finish_destroy(ndev);
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);
}
-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);
}
static void addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old)
{
- if (p == &ipv6_devconf_dflt.forwarding)
+ struct net *net;
+
+ net = (struct net *)table->extra2;
+ if (p == &net->ipv6.devconf_dflt->forwarding)
return;
- if (p == &ipv6_devconf.forwarding) {
- ipv6_devconf_dflt.forwarding = ipv6_devconf.forwarding;
- addrconf_forward_change();
+ 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();
+ rt6_purge_dflt_routers(net);
}
#endif
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(dev_net(idev->dev), addr, idev->dev)) {
ADBG(("ipv6_add_addr: already assigned\n"));
err = -EEXIST;
goto out;
if ((ifp->flags & IFA_F_PERMANENT) && onlink < 1) {
struct in6_addr prefix;
struct rt6_info *rt;
-
+ struct net *net = dev_net(ifp->idev->dev);
ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len);
- rt = rt6_lookup(&prefix, NULL, ifp->idev->dev->ifindex, 1);
+ rt = rt6_lookup(net, &prefix, NULL, ifp->idev->dev->ifindex, 1);
if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
if (onlink == 0) {
/*
* Choose an appropriate source address (RFC3484)
*/
+enum {
+ IPV6_SADDR_RULE_INIT = 0,
+ IPV6_SADDR_RULE_LOCAL,
+ IPV6_SADDR_RULE_SCOPE,
+ IPV6_SADDR_RULE_PREFERRED,
+#ifdef CONFIG_IPV6_MIP6
+ IPV6_SADDR_RULE_HOA,
+#endif
+ IPV6_SADDR_RULE_OIF,
+ IPV6_SADDR_RULE_LABEL,
+#ifdef CONFIG_IPV6_PRIVACY
+ IPV6_SADDR_RULE_PRIVACY,
+#endif
+ IPV6_SADDR_RULE_ORCHID,
+ IPV6_SADDR_RULE_PREFIX,
+ IPV6_SADDR_RULE_MAX
+};
+
struct ipv6_saddr_score {
- int addr_type;
- unsigned int attrs;
- int matchlen;
- int scope;
- unsigned int rule;
+ int rule;
+ int addr_type;
+ struct inet6_ifaddr *ifa;
+ DECLARE_BITMAP(scorebits, IPV6_SADDR_RULE_MAX);
+ int scopedist;
+ int matchlen;
};
-#define IPV6_SADDR_SCORE_LOCAL 0x0001
-#define IPV6_SADDR_SCORE_PREFERRED 0x0004
-#define IPV6_SADDR_SCORE_HOA 0x0008
-#define IPV6_SADDR_SCORE_OIF 0x0010
-#define IPV6_SADDR_SCORE_LABEL 0x0020
-#define IPV6_SADDR_SCORE_PRIVACY 0x0040
+struct ipv6_saddr_dst {
+ struct in6_addr *addr;
+ int ifindex;
+ int scope;
+ int label;
+ unsigned int prefs;
+};
static inline int ipv6_saddr_preferred(int type)
{
return 0;
}
-int ipv6_dev_get_saddr(struct net_device *daddr_dev,
- struct in6_addr *daddr, struct in6_addr *saddr)
+static int ipv6_get_saddr_eval(struct ipv6_saddr_score *score,
+ struct ipv6_saddr_dst *dst,
+ int i)
{
- struct ipv6_saddr_score hiscore;
- struct inet6_ifaddr *ifa_result = NULL;
- int daddr_type = __ipv6_addr_type(daddr);
- int daddr_scope = __ipv6_addr_src_scope(daddr_type);
- int daddr_ifindex = daddr_dev ? daddr_dev->ifindex : 0;
- u32 daddr_label = ipv6_addr_label(daddr, daddr_type, daddr_ifindex);
+ int ret;
+
+ if (i <= score->rule) {
+ switch (i) {
+ case IPV6_SADDR_RULE_SCOPE:
+ ret = score->scopedist;
+ break;
+ case IPV6_SADDR_RULE_PREFIX:
+ ret = score->matchlen;
+ break;
+ default:
+ ret = !!test_bit(i, score->scorebits);
+ }
+ goto out;
+ }
+
+ switch (i) {
+ case IPV6_SADDR_RULE_INIT:
+ /* Rule 0: remember if hiscore is not ready yet */
+ ret = !!score->ifa;
+ break;
+ case IPV6_SADDR_RULE_LOCAL:
+ /* Rule 1: Prefer same address */
+ ret = ipv6_addr_equal(&score->ifa->addr, dst->addr);
+ break;
+ case IPV6_SADDR_RULE_SCOPE:
+ /* Rule 2: Prefer appropriate scope
+ *
+ * ret
+ * ^
+ * -1 | d 15
+ * ---+--+-+---> scope
+ * |
+ * | d is scope of the destination.
+ * B-d | \
+ * | \ <- smaller scope is better if
+ * B-15 | \ if scope is enough for destinaion.
+ * | ret = B - scope (-1 <= scope >= d <= 15).
+ * d-C-1 | /
+ * |/ <- greater is better
+ * -C / if scope is not enough for destination.
+ * /| ret = scope - C (-1 <= d < scope <= 15).
+ *
+ * d - C - 1 < B -15 (for all -1 <= d <= 15).
+ * C > d + 14 - B >= 15 + 14 - B = 29 - B.
+ * Assume B = 0 and we get C > 29.
+ */
+ ret = __ipv6_addr_src_scope(score->addr_type);
+ if (ret >= dst->scope)
+ ret = -ret;
+ else
+ ret -= 128; /* 30 is enough */
+ score->scopedist = ret;
+ break;
+ case IPV6_SADDR_RULE_PREFERRED:
+ /* Rule 3: Avoid deprecated and optimistic addresses */
+ ret = ipv6_saddr_preferred(score->addr_type) ||
+ !(score->ifa->flags & (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC));
+ break;
+#ifdef CONFIG_IPV6_MIP6
+ case IPV6_SADDR_RULE_HOA:
+ {
+ /* Rule 4: Prefer home address */
+ int prefhome = !(dst->prefs & IPV6_PREFER_SRC_COA);
+ ret = !(score->ifa->flags & IFA_F_HOMEADDRESS) ^ prefhome;
+ break;
+ }
+#endif
+ case IPV6_SADDR_RULE_OIF:
+ /* Rule 5: Prefer outgoing interface */
+ ret = (!dst->ifindex ||
+ dst->ifindex == score->ifa->idev->dev->ifindex);
+ break;
+ case IPV6_SADDR_RULE_LABEL:
+ /* Rule 6: Prefer matching label */
+ ret = ipv6_addr_label(&score->ifa->addr, score->addr_type,
+ score->ifa->idev->dev->ifindex) == dst->label;
+ break;
+#ifdef CONFIG_IPV6_PRIVACY
+ case IPV6_SADDR_RULE_PRIVACY:
+ {
+ /* Rule 7: Prefer public address
+ * Note: prefer temprary address if use_tempaddr >= 2
+ */
+ int preftmp = dst->prefs & (IPV6_PREFER_SRC_PUBLIC|IPV6_PREFER_SRC_TMP) ?
+ !!(dst->prefs & IPV6_PREFER_SRC_TMP) :
+ score->ifa->idev->cnf.use_tempaddr >= 2;
+ ret = (!(score->ifa->flags & IFA_F_TEMPORARY)) ^ preftmp;
+ break;
+ }
+#endif
+ case IPV6_SADDR_RULE_ORCHID:
+ /* Rule 8-: Prefer ORCHID vs ORCHID or
+ * non-ORCHID vs non-ORCHID
+ */
+ ret = !(ipv6_addr_orchid(&score->ifa->addr) ^
+ ipv6_addr_orchid(dst->addr));
+ break;
+ case IPV6_SADDR_RULE_PREFIX:
+ /* Rule 8: Use longest matching prefix */
+ score->matchlen = ret = ipv6_addr_diff(&score->ifa->addr,
+ dst->addr);
+ break;
+ default:
+ ret = 0;
+ }
+
+ if (ret)
+ __set_bit(i, score->scorebits);
+ score->rule = i;
+out:
+ return ret;
+}
+
+int ipv6_dev_get_saddr(struct net_device *dst_dev,
+ struct in6_addr *daddr, unsigned int prefs,
+ struct in6_addr *saddr)
+{
+ struct ipv6_saddr_score scores[2],
+ *score = &scores[0], *hiscore = &scores[1];
+ struct net *net = dev_net(dst_dev);
+ struct ipv6_saddr_dst dst;
struct net_device *dev;
+ int dst_type;
+
+ dst_type = __ipv6_addr_type(daddr);
+ dst.addr = daddr;
+ dst.ifindex = dst_dev ? dst_dev->ifindex : 0;
+ dst.scope = __ipv6_addr_src_scope(dst_type);
+ dst.label = ipv6_addr_label(daddr, dst_type, dst.ifindex);
+ dst.prefs = prefs;
- memset(&hiscore, 0, sizeof(hiscore));
+ hiscore->rule = -1;
+ hiscore->ifa = NULL;
read_lock(&dev_base_lock);
rcu_read_lock();
- for_each_netdev(&init_net, dev) {
+ for_each_netdev(net, dev) {
struct inet6_dev *idev;
- struct inet6_ifaddr *ifa;
- /* Rule 0: Candidate Source Address (section 4)
+ /* Candidate Source Address (section 4)
* - multicast and link-local destination address,
* the set of candidate source address MUST only
* include addresses assigned to interfaces
* belonging to the same site as the outgoing
* interface.)
*/
- if ((daddr_type & IPV6_ADDR_MULTICAST ||
- daddr_scope <= IPV6_ADDR_SCOPE_LINKLOCAL) &&
- daddr_dev && dev != daddr_dev)
+ if (((dst_type & IPV6_ADDR_MULTICAST) ||
+ dst.scope <= IPV6_ADDR_SCOPE_LINKLOCAL) &&
+ dst.ifindex && dev->ifindex != dst.ifindex)
continue;
idev = __in6_dev_get(dev);
continue;
read_lock_bh(&idev->lock);
- for (ifa = idev->addr_list; ifa; ifa = ifa->if_next) {
- struct ipv6_saddr_score score;
+ for (score->ifa = idev->addr_list; score->ifa; score->ifa = score->ifa->if_next) {
+ int i;
- score.addr_type = __ipv6_addr_type(&ifa->addr);
-
- /* Rule 0:
+ /*
* - Tentative Address (RFC2462 section 5.4)
* - A tentative address is not considered
* "assigned to an interface" in the traditional
* addresses, and the unspecified address MUST
* NOT be included in a candidate set.
*/
- if ((ifa->flags & IFA_F_TENTATIVE) &&
- (!(ifa->flags & IFA_F_OPTIMISTIC)))
+ if ((score->ifa->flags & IFA_F_TENTATIVE) &&
+ (!(score->ifa->flags & IFA_F_OPTIMISTIC)))
continue;
- if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
- score.addr_type & IPV6_ADDR_MULTICAST)) {
+
+ score->addr_type = __ipv6_addr_type(&score->ifa->addr);
+
+ if (unlikely(score->addr_type == IPV6_ADDR_ANY ||
+ score->addr_type & IPV6_ADDR_MULTICAST)) {
LIMIT_NETDEBUG(KERN_DEBUG
"ADDRCONF: unspecified / multicast address "
"assigned as unicast address on %s",
continue;
}
- score.attrs = 0;
- score.matchlen = 0;
- score.scope = 0;
- score.rule = 0;
-
- if (ifa_result == NULL) {
- /* record it if the first available entry */
- goto record_it;
- }
-
- /* Rule 1: Prefer same address */
- if (hiscore.rule < 1) {
- if (ipv6_addr_equal(&ifa_result->addr, daddr))
- hiscore.attrs |= IPV6_SADDR_SCORE_LOCAL;
- hiscore.rule++;
- }
- if (ipv6_addr_equal(&ifa->addr, daddr)) {
- score.attrs |= IPV6_SADDR_SCORE_LOCAL;
- if (!(hiscore.attrs & IPV6_SADDR_SCORE_LOCAL)) {
- score.rule = 1;
- goto record_it;
- }
- } else {
- if (hiscore.attrs & IPV6_SADDR_SCORE_LOCAL)
- continue;
- }
-
- /* Rule 2: Prefer appropriate scope */
- if (hiscore.rule < 2) {
- hiscore.scope = __ipv6_addr_src_scope(hiscore.addr_type);
- hiscore.rule++;
- }
- score.scope = __ipv6_addr_src_scope(score.addr_type);
- if (hiscore.scope < score.scope) {
- if (hiscore.scope < daddr_scope) {
- score.rule = 2;
- goto record_it;
- } else
- continue;
- } else if (score.scope < hiscore.scope) {
- if (score.scope < daddr_scope)
- break; /* addresses sorted by scope */
- else {
- score.rule = 2;
- goto record_it;
- }
- }
+ score->rule = -1;
+ bitmap_zero(score->scorebits, IPV6_SADDR_RULE_MAX);
+
+ for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) {
+ int minihiscore, miniscore;
+
+ minihiscore = ipv6_get_saddr_eval(hiscore, &dst, i);
+ miniscore = ipv6_get_saddr_eval(score, &dst, i);
+
+ if (minihiscore > miniscore) {
+ if (i == IPV6_SADDR_RULE_SCOPE &&
+ score->scopedist > 0) {
+ /*
+ * special case:
+ * each remaining entry
+ * has too small (not enough)
+ * scope, because ifa entries
+ * are sorted by their scope
+ * values.
+ */
+ goto try_nextdev;
+ }
+ break;
+ } else if (minihiscore < miniscore) {
+ struct ipv6_saddr_score *tmp;
- /* Rule 3: Avoid deprecated and optimistic addresses */
- if (hiscore.rule < 3) {
- if (ipv6_saddr_preferred(hiscore.addr_type) ||
- (((ifa_result->flags &
- (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)))
- hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
- hiscore.rule++;
- }
- if (ipv6_saddr_preferred(score.addr_type) ||
- (((ifa->flags &
- (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) {
- score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
- if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
- score.rule = 3;
- goto record_it;
- }
- } else {
- if (hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)
- continue;
- }
+ if (hiscore->ifa)
+ in6_ifa_put(hiscore->ifa);
- /* Rule 4: Prefer home address */
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
- if (hiscore.rule < 4) {
- if (ifa_result->flags & IFA_F_HOMEADDRESS)
- hiscore.attrs |= IPV6_SADDR_SCORE_HOA;
- hiscore.rule++;
- }
- if (ifa->flags & IFA_F_HOMEADDRESS) {
- score.attrs |= IPV6_SADDR_SCORE_HOA;
- if (!(ifa_result->flags & IFA_F_HOMEADDRESS)) {
- score.rule = 4;
- goto record_it;
- }
- } else {
- if (hiscore.attrs & IPV6_SADDR_SCORE_HOA)
- continue;
- }
-#else
- if (hiscore.rule < 4)
- hiscore.rule++;
-#endif
+ in6_ifa_hold(score->ifa);
- /* Rule 5: Prefer outgoing interface */
- if (hiscore.rule < 5) {
- if (daddr_dev == NULL ||
- daddr_dev == ifa_result->idev->dev)
- hiscore.attrs |= IPV6_SADDR_SCORE_OIF;
- hiscore.rule++;
- }
- if (daddr_dev == NULL ||
- daddr_dev == ifa->idev->dev) {
- score.attrs |= IPV6_SADDR_SCORE_OIF;
- if (!(hiscore.attrs & IPV6_SADDR_SCORE_OIF)) {
- score.rule = 5;
- goto record_it;
- }
- } else {
- if (hiscore.attrs & IPV6_SADDR_SCORE_OIF)
- continue;
- }
+ tmp = hiscore;
+ hiscore = score;
+ score = tmp;
- /* Rule 6: Prefer matching label */
- if (hiscore.rule < 6) {
- if (ipv6_addr_label(&ifa_result->addr,
- hiscore.addr_type,
- ifa_result->idev->dev->ifindex) == daddr_label)
- hiscore.attrs |= IPV6_SADDR_SCORE_LABEL;
- hiscore.rule++;
- }
- if (ipv6_addr_label(&ifa->addr,
- score.addr_type,
- ifa->idev->dev->ifindex) == daddr_label) {
- score.attrs |= IPV6_SADDR_SCORE_LABEL;
- if (!(hiscore.attrs & IPV6_SADDR_SCORE_LABEL)) {
- score.rule = 6;
- goto record_it;
- }
- } else {
- if (hiscore.attrs & IPV6_SADDR_SCORE_LABEL)
- continue;
- }
+ /* restore our iterator */
+ score->ifa = hiscore->ifa;
-#ifdef CONFIG_IPV6_PRIVACY
- /* Rule 7: Prefer public address
- * Note: prefer temprary address if use_tempaddr >= 2
- */
- if (hiscore.rule < 7) {
- if ((!(ifa_result->flags & IFA_F_TEMPORARY)) ^
- (ifa_result->idev->cnf.use_tempaddr >= 2))
- hiscore.attrs |= IPV6_SADDR_SCORE_PRIVACY;
- hiscore.rule++;
- }
- if ((!(ifa->flags & IFA_F_TEMPORARY)) ^
- (ifa->idev->cnf.use_tempaddr >= 2)) {
- score.attrs |= IPV6_SADDR_SCORE_PRIVACY;
- if (!(hiscore.attrs & IPV6_SADDR_SCORE_PRIVACY)) {
- score.rule = 7;
- goto record_it;
+ break;
}
- } else {
- if (hiscore.attrs & IPV6_SADDR_SCORE_PRIVACY)
- continue;
}
-#else
- if (hiscore.rule < 7)
- hiscore.rule++;
-#endif
- /* Rule 8: Use longest matching prefix */
- if (hiscore.rule < 8) {
- hiscore.matchlen = ipv6_addr_diff(&ifa_result->addr, daddr);
- hiscore.rule++;
- }
- score.matchlen = ipv6_addr_diff(&ifa->addr, daddr);
- if (score.matchlen > hiscore.matchlen) {
- score.rule = 8;
- goto record_it;
- }
-#if 0
- else if (score.matchlen < hiscore.matchlen)
- continue;
-#endif
-
- /* Final Rule: choose first available one */
- continue;
-record_it:
- if (ifa_result)
- in6_ifa_put(ifa_result);
- in6_ifa_hold(ifa);
- ifa_result = ifa;
- hiscore = score;
}
+try_nextdev:
read_unlock_bh(&idev->lock);
}
rcu_read_unlock();
read_unlock(&dev_base_lock);
- if (!ifa_result)
+ if (!hiscore->ifa)
return -EADDRNOTAVAIL;
- ipv6_addr_copy(saddr, &ifa_result->addr);
- in6_ifa_put(ifa_result);
+ ipv6_addr_copy(saddr, &hiscore->ifa->addr);
+ in6_ifa_put(hiscore->ifa);
return 0;
}
-
-int ipv6_get_saddr(struct dst_entry *dst,
- struct in6_addr *daddr, struct in6_addr *saddr)
-{
- return ipv6_dev_get_saddr(dst ? ip6_dst_idev(dst)->dev : NULL, daddr, saddr);
-}
-
-EXPORT_SYMBOL(ipv6_get_saddr);
+EXPORT_SYMBOL(ipv6_dev_get_saddr);
int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
unsigned char banned_flags)
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 (!net_eq(dev_net(ifp->idev->dev), net))
+ continue;
if (ipv6_addr_equal(&ifp->addr, addr) &&
!(ifp->flags&IFA_F_TENTATIVE)) {
if (dev == NULL || ifp->idev->dev == dev ||
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 (!net_eq(dev_net(ifp->idev->dev), net))
+ continue;
if (ipv6_addr_equal(&ifp->addr, addr)) {
if (dev == NULL || ifp->idev->dev == dev)
break;
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 (!net_eq(dev_net(ifp->idev->dev), net))
+ continue;
if (ipv6_addr_equal(&ifp->addr, addr)) {
if (dev == NULL || ifp->idev->dev == dev ||
!(ifp->scope&(IFA_LINK|IFA_HOST) || strict)) {
.fc_expires = expires,
.fc_dst_len = plen,
.fc_flags = RTF_UP | flags,
+ .fc_nlinfo.nl_net = dev_net(dev),
};
ipv6_addr_copy(&cfg.fc_dst, pfx);
.fc_ifindex = dev->ifindex,
.fc_dst_len = 8,
.fc_flags = RTF_UP,
+ .fc_nlinfo.nl_net = dev_net(dev),
};
ipv6_addr_set(&cfg.fc_dst, htonl(0xFF000000), 0, 0, 0);
.fc_ifindex = dev->ifindex,
.fc_dst_len = 96,
.fc_flags = RTF_UP | RTF_NONEXTHOP,
+ .fc_nlinfo.nl_net = dev_net(dev),
};
/* prefix length - 96 bits "::d.d.d.d" */
if (pinfo->onlink) {
struct rt6_info *rt;
- rt = rt6_lookup(&pinfo->prefix, NULL, dev->ifindex, 1);
+ rt = rt6_lookup(dev_net(dev), &pinfo->prefix, NULL,
+ dev->ifindex, 1);
if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
if (rt->rt6i_flags&RTF_EXPIRES) {
ok:
- ifp = ipv6_get_ifaddr(&addr, dev, 1);
+ ifp = ipv6_get_ifaddr(dev_net(dev), &addr, dev, 1);
if (ifp == NULL && valid_lft) {
int max_addresses = in6_dev->cnf.max_addresses;
* Special case for SIT interfaces where we create a new "virtual"
* device.
*/
-int addrconf_set_dstaddr(void __user *arg)
+int addrconf_set_dstaddr(struct net *net, void __user *arg)
{
struct in6_ifreq ireq;
struct net_device *dev;
if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
goto err_exit;
- dev = __dev_get_by_index(&init_net, ireq.ifr6_ifindex);
+ dev = __dev_get_by_index(net, ireq.ifr6_ifindex);
err = -ENODEV;
if (dev == NULL)
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);
if (err == 0) {
err = -ENOBUFS;
- if ((dev = __dev_get_by_name(&init_net, p.name)) == NULL)
+ dev = __dev_get_by_name(net, p.name);
+ if (!dev)
goto err_exit;
err = dev_open(dev);
}
/*
* Manual configuration of address on an interface
*/
-static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
- __u8 ifa_flags, __u32 prefered_lft, __u32 valid_lft)
+static int inet6_addr_add(struct net *net, int ifindex, struct in6_addr *pfx,
+ int plen, __u8 ifa_flags, __u32 prefered_lft,
+ __u32 valid_lft)
{
struct inet6_ifaddr *ifp;
struct inet6_dev *idev;
if (!valid_lft || prefered_lft > valid_lft)
return -EINVAL;
- if ((dev = __dev_get_by_index(&init_net, ifindex)) == NULL)
+ dev = __dev_get_by_index(net, ifindex);
+ if (!dev)
return -ENODEV;
if ((idev = addrconf_add_dev(dev)) == NULL)
return PTR_ERR(ifp);
}
-static int inet6_addr_del(int ifindex, struct in6_addr *pfx, int plen)
+static int inet6_addr_del(struct net *net, int ifindex, struct in6_addr *pfx,
+ int plen)
{
struct inet6_ifaddr *ifp;
struct inet6_dev *idev;
struct net_device *dev;
- if ((dev = __dev_get_by_index(&init_net, ifindex)) == NULL)
+ dev = __dev_get_by_index(net, ifindex);
+ if (!dev)
return -ENODEV;
if ((idev = __in6_dev_get(dev)) == NULL)
}
-int addrconf_add_ifaddr(void __user *arg)
+int addrconf_add_ifaddr(struct net *net, void __user *arg)
{
struct in6_ifreq ireq;
int err;
return -EFAULT;
rtnl_lock();
- err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen,
- IFA_F_PERMANENT, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
+ err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr,
+ ireq.ifr6_prefixlen, IFA_F_PERMANENT,
+ INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
rtnl_unlock();
return err;
}
-int addrconf_del_ifaddr(void __user *arg)
+int addrconf_del_ifaddr(struct net *net, void __user *arg)
{
struct in6_ifreq ireq;
int err;
return -EFAULT;
rtnl_lock();
- err = inet6_addr_del(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen);
+ err = inet6_addr_del(net, ireq.ifr6_ifindex, &ireq.ifr6_addr,
+ ireq.ifr6_prefixlen);
rtnl_unlock();
return err;
}
struct inet6_ifaddr * ifp;
struct in6_addr addr;
struct net_device *dev;
+ struct net *net = dev_net(idev->dev);
int scope;
ASSERT_RTNL();
return;
}
- for_each_netdev(&init_net, dev) {
+ for_each_netdev(net, dev) {
struct in_device * in_dev = __in_dev_get_rtnl(dev);
if (in_dev && (dev->flags & IFF_UP)) {
struct in_ifaddr * ifa;
static void ip6_tnl_add_linklocal(struct inet6_dev *idev)
{
struct net_device *link_dev;
+ struct net *net = dev_net(idev->dev);
/* first try to inherit the link-local address from the link device */
if (idev->dev->iflink &&
- (link_dev = __dev_get_by_index(&init_net, idev->dev->iflink))) {
+ (link_dev = __dev_get_by_index(net, idev->dev->iflink))) {
if (!ipv6_inherit_linklocal(idev, link_dev))
return;
}
/* then try to inherit it from any device */
- for_each_netdev(&init_net, link_dev) {
+ for_each_netdev(net, link_dev) {
if (!ipv6_inherit_linklocal(idev, link_dev))
return;
}
int run_pending = 0;
int err;
- if (dev->nd_net != &init_net)
- return NOTIFY_DONE;
-
switch(event) {
case NETDEV_REGISTER:
if (!idev && dev->mtu >= IPV6_MIN_MTU) {
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);
{
struct inet6_dev *idev;
struct inet6_ifaddr *ifa, **bifa;
+ struct net *net = dev_net(dev);
int i;
ASSERT_RTNL();
if (dev == init_net.loopback_dev && how == 1)
how = 0;
- rt6_ifdown(dev);
+ rt6_ifdown(net, dev);
neigh_ifdown(&nd_tbl, dev);
idev = __in6_dev_get(dev);
/* 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);
#ifdef CONFIG_PROC_FS
struct if6_iter_state {
+ struct seq_net_private p;
int bucket;
};
{
struct inet6_ifaddr *ifa = NULL;
struct if6_iter_state *state = seq->private;
+ struct net *net = seq_file_net(seq);
for (state->bucket = 0; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) {
ifa = inet6_addr_lst[state->bucket];
+
+ while (ifa && !net_eq(dev_net(ifa->idev->dev), net))
+ ifa = ifa->lst_next;
if (ifa)
break;
}
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 = seq_file_net(seq);
ifa = ifa->lst_next;
try_again:
+ if (ifa) {
+ if (!net_eq(dev_net(ifa->idev->dev), 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;
}
}
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);
}
static void if6_seq_stop(struct seq_file *seq, void *v)
+ __releases(addrconf_hash_lock)
{
read_unlock_bh(&addrconf_hash_lock);
}
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 = {
.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 (!net_eq(dev_net(ifp->idev->dev), net))
+ continue;
if (ipv6_addr_cmp(&ifp->addr, addr) == 0 &&
(ifp->flags & IFA_F_HOMEADDRESS)) {
ret = 1;
static int
inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
- struct net *net = skb->sk->sk_net;
+ struct net *net = sock_net(skb->sk);
struct ifaddrmsg *ifm;
struct nlattr *tb[IFA_MAX+1];
struct in6_addr *pfx;
int err;
- if (net != &init_net)
- return -EINVAL;
-
err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
if (err < 0)
return err;
if (pfx == NULL)
return -EINVAL;
- return inet6_addr_del(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
+ return inet6_addr_del(net, ifm->ifa_index, pfx, ifm->ifa_prefixlen);
}
static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
static int
inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
- struct net *net = skb->sk->sk_net;
+ struct net *net = sock_net(skb->sk);
struct ifaddrmsg *ifm;
struct nlattr *tb[IFA_MAX+1];
struct in6_addr *pfx;
u8 ifa_flags;
int err;
- if (net != &init_net)
- return -EINVAL;
-
err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
if (err < 0)
return err;
valid_lft = INFINITY_LIFE_TIME;
}
- dev = __dev_get_by_index(&init_net, ifm->ifa_index);
+ dev = __dev_get_by_index(net, ifm->ifa_index);
if (dev == NULL)
return -ENODEV;
/* 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
* userspace alreay relies on not having to provide this.
*/
- return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen,
- ifa_flags, preferred_lft, valid_lft);
+ return inet6_addr_add(net, ifm->ifa_index, pfx,
+ ifm->ifa_prefixlen, ifa_flags,
+ preferred_lft, valid_lft);
}
if (nlh->nlmsg_flags & NLM_F_EXCL ||
struct inet6_ifaddr *ifa;
struct ifmcaddr6 *ifmca;
struct ifacaddr6 *ifaca;
+ struct net *net = sock_net(skb->sk);
s_idx = cb->args[0];
s_ip_idx = ip_idx = cb->args[1];
idx = 0;
- for_each_netdev(&init_net, dev) {
+ for_each_netdev(net, dev) {
if (idx < s_idx)
goto cont;
if (idx > s_idx)
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:
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:
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:
}
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;
static int inet6_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
{
- struct net *net = skb->sk->sk_net;
enum addr_type_t type = UNICAST_ADDR;
- if (net != &init_net)
- return 0;
-
return inet6_dump_addr(skb, cb, type);
}
static int inet6_dump_ifmcaddr(struct sk_buff *skb, struct netlink_callback *cb)
{
- struct net *net = skb->sk->sk_net;
enum addr_type_t type = MULTICAST_ADDR;
- if (net != &init_net)
- return 0;
-
return inet6_dump_addr(skb, cb, type);
}
static int inet6_dump_ifacaddr(struct sk_buff *skb, struct netlink_callback *cb)
{
- struct net *net = skb->sk->sk_net;
enum addr_type_t type = ANYCAST_ADDR;
- if (net != &init_net)
- return 0;
-
return inet6_dump_addr(skb, cb, type);
}
static int inet6_rtm_getaddr(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 ifaddrmsg *ifm;
struct nlattr *tb[IFA_MAX+1];
struct in6_addr *addr = NULL;
struct sk_buff *skb;
int err;
- if (net != &init_net)
- return -EINVAL;
-
err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
if (err < 0)
goto errout;
ifm = nlmsg_data(nlh);
if (ifm->ifa_index)
- dev = __dev_get_by_index(&init_net, ifm->ifa_index);
+ dev = __dev_get_by_index(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;
}
kfree_skb(skb);
goto errout_ifa;
}
- err = rtnl_unicast(skb, &init_net, NETLINK_CB(in_skb).pid);
+ err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
errout_ifa:
in6_ifa_put(ifa);
errout:
static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa)
{
struct sk_buff *skb;
+ struct net *net = dev_net(ifa->idev->dev);
int err = -ENOBUFS;
skb = nlmsg_new(inet6_ifaddr_msgsize(), GFP_ATOMIC);
kfree_skb(skb);
goto errout;
}
- err = rtnl_notify(skb, &init_net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
+ err = rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
errout:
if (err < 0)
- rtnl_set_sk_err(&init_net, RTNLGRP_IPV6_IFADDR, err);
+ rtnl_set_sk_err(net, RTNLGRP_IPV6_IFADDR, err);
}
static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
{
- struct net *net = skb->sk->sk_net;
+ struct net *net = sock_net(skb->sk);
int idx, err;
int s_idx = cb->args[0];
struct net_device *dev;
struct inet6_dev *idev;
- if (net != &init_net)
- return 0;
-
read_lock(&dev_base_lock);
idx = 0;
- for_each_netdev(&init_net, dev) {
+ for_each_netdev(net, dev) {
if (idx < s_idx)
goto cont;
if ((idev = in6_dev_get(dev)) == NULL)
void inet6_ifinfo_notify(int event, struct inet6_dev *idev)
{
struct sk_buff *skb;
+ struct net *net = dev_net(idev->dev);
int err = -ENOBUFS;
skb = nlmsg_new(inet6_if_nlmsg_size(), GFP_ATOMIC);
kfree_skb(skb);
goto errout;
}
- err = rtnl_notify(skb, &init_net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
+ err = rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
errout:
if (err < 0)
- rtnl_set_sk_err(&init_net, RTNLGRP_IPV6_IFADDR, err);
+ rtnl_set_sk_err(net, RTNLGRP_IPV6_IFADDR, err);
}
static inline size_t inet6_prefix_nlmsg_size(void)
struct prefix_info *pinfo)
{
struct sk_buff *skb;
+ struct net *net = dev_net(idev->dev);
int err = -ENOBUFS;
skb = nlmsg_new(inet6_prefix_nlmsg_size(), GFP_ATOMIC);
kfree_skb(skb);
goto errout;
}
- err = rtnl_notify(skb, &init_net, 0, RTNLGRP_IPV6_PREFIX, NULL, GFP_ATOMIC);
+ err = rtnl_notify(skb, net, 0, RTNLGRP_IPV6_PREFIX, NULL, GFP_ATOMIC);
errout:
if (err < 0)
- rtnl_set_sk_err(&init_net, RTNLGRP_IPV6_PREFIX, err);
+ rtnl_set_sk_err(net, RTNLGRP_IPV6_PREFIX, err);
}
static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
},
};
-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;
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;
}
/*
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_paths(addrconf_ctl_path,
+ 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(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(dev_net(idev->dev), 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->dev_name);
- 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
*/
EXPORT_SYMBOL(unregister_inet6addr_notifier);
+
+static int addrconf_net_init(struct net *net)
+{
+ return 0;
+}
+
+static void addrconf_net_exit(struct net *net)
+{
+ struct net_device *dev;
+
+ rtnl_lock();
+ /* clean dev list */
+ for_each_netdev(net, dev) {
+ if (__in6_dev_get(dev) == NULL)
+ continue;
+ addrconf_ifdown(dev, 1);
+ }
+ addrconf_ifdown(net->loopback_dev, 2);
+ rtnl_unlock();
+}
+
+static struct pernet_operations addrconf_net_ops = {
+ .init = addrconf_net_init,
+ .exit = addrconf_net_exit,
+};
+
/*
* Init / cleanup code
*/
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
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);
-#ifdef CONFIG_IPV6_MULTIPLE_TABLES
- ip6_prohibit_entry.u.dst.dev = init_net.loopback_dev;
- ip6_prohibit_entry.rt6i_idev = in6_dev_get(init_net.loopback_dev);
- ip6_blk_hole_entry.u.dst.dev = init_net.loopback_dev;
- ip6_blk_hole_entry.rt6i_idev = in6_dev_get(init_net.loopback_dev);
-#endif
+ err = register_pernet_device(&addrconf_net_ops);
+ if (err)
+ return err;
register_netdevice_notifier(&ipv6_dev_notf);
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;
int i;
unregister_netdevice_notifier(&ipv6_dev_notf);
+ unregister_pernet_device(&addrconf_net_ops);
-#ifdef CONFIG_SYSCTL
- addrconf_sysctl_unregister(&ipv6_devconf_dflt);
- addrconf_sysctl_unregister(&ipv6_devconf);
-#endif
+ unregister_pernet_subsys(&addrconf_ops);
rtnl_lock();
- /*
- * clean dev list.
- */
-
- for_each_netdev(&init_net, dev) {
- if (__in6_dev_get(dev) == NULL)
- continue;
- addrconf_ifdown(dev, 1);
- }
- addrconf_ifdown(init_net.loopback_dev, 2);
-
/*
* Check hash table.
*/
-
write_lock_bh(&addrconf_hash_lock);
for (i=0; i < IN6_ADDR_HSIZE; i++) {
for (ifa=inet6_addr_lst[i]; ifa; ) {
write_unlock_bh(&addrconf_hash_lock);
del_timer(&addr_chk_timer);
-
rtnl_unlock();
+
+ unregister_pernet_subsys(&addrconf_net_ops);
}