* status etc.
*/
-#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
#endif
+#include <linux/capability.h>
#include <linux/delay.h>
#include <linux/notifier.h>
#include <linux/string.h>
#ifdef CONFIG_IPV6_PRIVACY
#include <linux/random.h>
-#include <linux/crypto.h>
-#include <linux/scatterlist.h>
#endif
#include <asm/uaccess.h>
static void ipv6_regen_rndid(unsigned long data);
static int desync_factor = MAX_DESYNC_FACTOR * HZ;
-static struct crypto_tfm *md5_tfm;
-static DEFINE_SPINLOCK(md5_tfm_lock);
#endif
static int ipv6_count_addresses(struct inet6_dev *idev);
static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags);
static void addrconf_dad_timer(unsigned long data);
static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
+static void addrconf_dad_run(struct inet6_dev *idev);
static void addrconf_rs_timer(unsigned long data);
static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
struct prefix_info *pinfo);
static int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev);
-static struct notifier_block *inet6addr_chain;
+static ATOMIC_NOTIFIER_HEAD(inet6addr_chain);
struct ipv6_devconf ipv6_devconf = {
.forwarding = 0,
.max_desync_factor = MAX_DESYNC_FACTOR,
#endif
.max_addresses = IPV6_MAX_ADDRESSES,
+ .accept_ra_defrtr = 1,
+ .accept_ra_pinfo = 1,
+#ifdef CONFIG_IPV6_ROUTER_PREF
+ .accept_ra_rtr_pref = 1,
+ .rtr_probe_interval = 60 * HZ,
+#ifdef CONFIG_IPV6_ROUTE_INFO
+ .accept_ra_rt_info_max_plen = 0,
+#endif
+#endif
};
static struct ipv6_devconf ipv6_devconf_dflt = {
.max_desync_factor = MAX_DESYNC_FACTOR,
#endif
.max_addresses = IPV6_MAX_ADDRESSES,
+ .accept_ra_defrtr = 1,
+ .accept_ra_pinfo = 1,
+#ifdef CONFIG_IPV6_ROUTER_PREF
+ .accept_ra_rtr_pref = 1,
+ .rtr_probe_interval = 60 * HZ,
+#ifdef CONFIG_IPV6_ROUTE_INFO
+ .accept_ra_rt_info_max_plen = 0,
+#endif
+#endif
};
/* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
if (dev->mtu < IPV6_MIN_MTU)
return NULL;
- ndev = kmalloc(sizeof(struct inet6_dev), GFP_KERNEL);
+ ndev = kzalloc(sizeof(struct inet6_dev), GFP_KERNEL);
- if (ndev) {
- memset(ndev, 0, sizeof(struct inet6_dev));
+ if (ndev == NULL)
+ return NULL;
- rwlock_init(&ndev->lock);
- ndev->dev = dev;
- memcpy(&ndev->cnf, &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 (ndev->nd_parms == NULL) {
- kfree(ndev);
- return NULL;
- }
- /* We refer to the device */
- dev_hold(dev);
-
- if (snmp6_alloc_dev(ndev) < 0) {
- ADBG((KERN_WARNING
- "%s(): cannot allocate memory for statistics; dev=%s.\n",
- __FUNCTION__, dev->name));
- neigh_parms_release(&nd_tbl, ndev->nd_parms);
- ndev->dead = 1;
- in6_dev_finish_destroy(ndev);
- return NULL;
- }
+ rwlock_init(&ndev->lock);
+ ndev->dev = dev;
+ memcpy(&ndev->cnf, &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 (ndev->nd_parms == NULL) {
+ kfree(ndev);
+ return NULL;
+ }
+ /* We refer to the device */
+ dev_hold(dev);
- if (snmp6_register_dev(ndev) < 0) {
- ADBG((KERN_WARNING
- "%s(): cannot create /proc/net/dev_snmp6/%s\n",
- __FUNCTION__, dev->name));
- neigh_parms_release(&nd_tbl, ndev->nd_parms);
- ndev->dead = 1;
- in6_dev_finish_destroy(ndev);
- return NULL;
- }
+ if (snmp6_alloc_dev(ndev) < 0) {
+ ADBG((KERN_WARNING
+ "%s(): cannot allocate memory for statistics; dev=%s.\n",
+ __FUNCTION__, dev->name));
+ neigh_parms_release(&nd_tbl, ndev->nd_parms);
+ ndev->dead = 1;
+ in6_dev_finish_destroy(ndev);
+ return NULL;
+ }
- /* One reference from device. We must do this before
- * we invoke __ipv6_regen_rndid().
- */
- in6_dev_hold(ndev);
+ if (snmp6_register_dev(ndev) < 0) {
+ ADBG((KERN_WARNING
+ "%s(): cannot create /proc/net/dev_snmp6/%s\n",
+ __FUNCTION__, dev->name));
+ neigh_parms_release(&nd_tbl, ndev->nd_parms);
+ ndev->dead = 1;
+ in6_dev_finish_destroy(ndev);
+ return NULL;
+ }
+
+ /* One reference from device. We must do this before
+ * we invoke __ipv6_regen_rndid().
+ */
+ in6_dev_hold(ndev);
#ifdef CONFIG_IPV6_PRIVACY
- get_random_bytes(ndev->rndid, sizeof(ndev->rndid));
- get_random_bytes(ndev->entropy, sizeof(ndev->entropy));
- init_timer(&ndev->regen_timer);
- ndev->regen_timer.function = ipv6_regen_rndid;
- ndev->regen_timer.data = (unsigned long) ndev;
- if ((dev->flags&IFF_LOOPBACK) ||
- dev->type == ARPHRD_TUNNEL ||
- dev->type == ARPHRD_NONE ||
- dev->type == ARPHRD_SIT) {
- printk(KERN_INFO
- "Disabled Privacy Extensions on device %p(%s)\n",
- dev, dev->name);
- ndev->cnf.use_tempaddr = -1;
- } else {
- in6_dev_hold(ndev);
- ipv6_regen_rndid((unsigned long) ndev);
- }
+ init_timer(&ndev->regen_timer);
+ ndev->regen_timer.function = ipv6_regen_rndid;
+ ndev->regen_timer.data = (unsigned long) ndev;
+ if ((dev->flags&IFF_LOOPBACK) ||
+ dev->type == ARPHRD_TUNNEL ||
+ dev->type == ARPHRD_NONE ||
+ dev->type == ARPHRD_SIT) {
+ printk(KERN_INFO
+ "%s: Disabled Privacy Extensions\n",
+ dev->name);
+ ndev->cnf.use_tempaddr = -1;
+ } else {
+ in6_dev_hold(ndev);
+ ipv6_regen_rndid((unsigned long) ndev);
+ }
#endif
- write_lock_bh(&addrconf_lock);
- dev->ip6_ptr = ndev;
- write_unlock_bh(&addrconf_lock);
+ if (netif_carrier_ok(dev))
+ ndev->if_flags |= IF_READY;
- ipv6_mc_init_dev(ndev);
- ndev->tstamp = jiffies;
+ write_lock_bh(&addrconf_lock);
+ dev->ip6_ptr = ndev;
+ write_unlock_bh(&addrconf_lock);
+
+ 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, &ndev->cnf);
+ neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6,
+ NET_IPV6_NEIGH, "ipv6",
+ &ndisc_ifinfo_sysctl_change,
+ NULL);
+ addrconf_sysctl_register(ndev, &ndev->cnf);
#endif
- }
return ndev;
}
if ((idev = ipv6_add_dev(dev)) == NULL)
return NULL;
}
+
if (dev->flags&IFF_UP)
ipv6_mc_up(idev);
return idev;
kfree(ifp);
}
+static void
+ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp)
+{
+ struct inet6_ifaddr *ifa, **ifap;
+ int ifp_scope = ipv6_addr_src_scope(&ifp->addr);
+
+ /*
+ * Each device address list is sorted in order of scope -
+ * global before linklocal.
+ */
+ for (ifap = &idev->addr_list; (ifa = *ifap) != NULL;
+ ifap = &ifa->if_next) {
+ if (ifp_scope >= ipv6_addr_src_scope(&ifa->addr))
+ break;
+ }
+
+ ifp->if_next = *ifap;
+ *ifap = ifp;
+}
+
/* On success it returns ifp with increased reference count */
static struct inet6_ifaddr *
goto out;
}
- ifa = kmalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC);
+ ifa = kzalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC);
if (ifa == NULL) {
ADBG(("ipv6_add_addr: malloc failed\n"));
goto out;
}
- memset(ifa, 0, sizeof(struct inet6_ifaddr));
ipv6_addr_copy(&ifa->addr, addr);
spin_lock_init(&ifa->lock);
ifa->flags = flags | IFA_F_TENTATIVE;
ifa->cstamp = ifa->tstamp = jiffies;
+ ifa->rt = rt;
+
ifa->idev = idev;
in6_dev_hold(idev);
/* For caller */
write_lock(&idev->lock);
/* Add to inet6_dev unicast addr list. */
- ifa->if_next = idev->addr_list;
- idev->addr_list = ifa;
+ ipv6_link_dev_addr(idev, ifa);
#ifdef CONFIG_IPV6_PRIVACY
if (ifa->flags&IFA_F_TEMPORARY) {
}
#endif
- ifa->rt = rt;
-
in6_ifa_hold(ifa);
write_unlock(&idev->lock);
out2:
read_unlock_bh(&addrconf_lock);
if (likely(err == 0))
- notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa);
+ atomic_notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa);
else {
kfree(ifa);
ifa = ERR_PTR(err);
}
#endif
- for (ifap = &idev->addr_list; (ifa=*ifap) != NULL;
- ifap = &ifa->if_next) {
+ for (ifap = &idev->addr_list; (ifa=*ifap) != NULL;) {
if (ifa == ifp) {
*ifap = ifa->if_next;
__in6_ifa_put(ifp);
if (!(ifp->flags & IFA_F_PERMANENT) || onlink > 0)
break;
deleted = 1;
+ continue;
} else if (ifp->flags & IFA_F_PERMANENT) {
if (ipv6_prefix_equal(&ifa->addr, &ifp->addr,
ifp->prefix_len)) {
}
}
}
+ ifap = &ifa->if_next;
}
write_unlock_bh(&idev->lock);
ipv6_ifa_notify(RTM_DELADDR, ifp);
- notifier_call_chain(&inet6addr_chain,NETDEV_DOWN,ifp);
+ atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifp);
addrconf_del_timer(ifp);
int addr_type;
unsigned int attrs;
int matchlen;
- unsigned int scope;
+ int scope;
unsigned int rule;
};
* 2002::/16 2
* ::/96 3
* ::ffff:0:0/96 4
+ * fc00::/7 5
+ * 2001::/32 6
*/
if (type & IPV6_ADDR_LOOPBACK)
return 0;
return 3;
else if (type & IPV6_ADDR_MAPPED)
return 4;
+ else if (addr->s6_addr32[0] == htonl(0x20010000))
+ return 6;
else if (addr->s6_addr16[0] == htons(0x2002))
return 2;
+ else if ((addr->s6_addr[0] & 0xfe) == 0xfc)
+ return 5;
return 1;
}
score.addr_type = __ipv6_addr_type(&ifa->addr);
- /* Rule 0: Candidate Source Address (section 4)
+ /* Rule 0:
+ * - Tentative Address (RFC2462 section 5.4)
+ * - A tentative address is not considered
+ * "assigned to an interface" in the traditional
+ * sense.
+ * - Candidate Source Address (section 4)
* - In any case, anycast addresses, multicast
* addresses, and the unspecified address MUST
* NOT be included in a candidate set.
*/
+ if (ifa->flags & IFA_F_TENTATIVE)
+ continue;
if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
score.addr_type & IPV6_ADDR_MULTICAST)) {
LIMIT_NETDEBUG(KERN_DEBUG
continue;
} else if (score.scope < hiscore.scope) {
if (score.scope < daddr_scope)
- continue;
+ break; /* addresses sorted by scope */
else {
score.rule = 2;
goto record_it;
}
/* Rule 4: Prefer home address -- not implemented yet */
+ if (hiscore.rule < 4)
+ hiscore.rule++;
/* Rule 5: Prefer outgoing interface */
if (hiscore.rule < 5) {
continue;
}
+#ifdef CONFIG_IPV6_PRIVACY
/* Rule 7: Prefer public address
* Note: prefer temprary address if use_tempaddr >= 2
*/
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)
+ if (hiscore.rule < 8) {
hiscore.matchlen = ipv6_addr_diff(&ifa_result->addr, daddr);
- score.rule++;
+ hiscore.rule++;
+ }
score.matchlen = ipv6_addr_diff(&ifa->addr, daddr);
if (score.matchlen > hiscore.matchlen) {
score.rule = 8;
int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
{
const struct in6_addr *sk_rcv_saddr6 = &inet6_sk(sk)->rcv_saddr;
- const struct in6_addr *sk2_rcv_saddr6 = tcp_v6_rcv_saddr(sk2);
+ const struct in6_addr *sk2_rcv_saddr6 = inet6_rcv_saddr(sk2);
u32 sk_rcv_saddr = inet_sk(sk)->rcv_saddr;
u32 sk2_rcv_saddr = inet_rcv_saddr(sk2);
int sk_ipv6only = ipv6_only_sock(sk);
/* Gets referenced address, destroys ifaddr */
-void addrconf_dad_failure(struct inet6_ifaddr *ifp)
+static void addrconf_dad_stop(struct inet6_ifaddr *ifp)
{
- if (net_ratelimit())
- printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name);
if (ifp->flags&IFA_F_PERMANENT) {
spin_lock_bh(&ifp->lock);
addrconf_del_timer(ifp);
ipv6_del_addr(ifp);
}
+void addrconf_dad_failure(struct inet6_ifaddr *ifp)
+{
+ if (net_ratelimit())
+ printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name);
+ addrconf_dad_stop(ifp);
+}
/* Join to solicited addr multicast group. */
__ipv6_dev_ac_dec(ifp->idev, &addr);
}
+static int addrconf_ifid_eui48(u8 *eui, struct net_device *dev)
+{
+ if (dev->addr_len != ETH_ALEN)
+ return -1;
+ memcpy(eui, dev->dev_addr, 3);
+ memcpy(eui + 5, dev->dev_addr + 3, 3);
+
+ /*
+ * The zSeries OSA network cards can be shared among various
+ * OS instances, but the OSA cards have only one MAC address.
+ * This leads to duplicate address conflicts in conjunction
+ * with IPv6 if more than one instance uses the same card.
+ *
+ * The driver for these cards can deliver a unique 16-bit
+ * identifier for each instance sharing the same card. It is
+ * placed instead of 0xFFFE in the interface identifier. The
+ * "u" bit of the interface identifier is not inverted in this
+ * case. Hence the resulting interface identifier has local
+ * scope according to RFC2373.
+ */
+ if (dev->dev_id) {
+ eui[3] = (dev->dev_id >> 8) & 0xFF;
+ eui[4] = dev->dev_id & 0xFF;
+ } else {
+ eui[3] = 0xFF;
+ eui[4] = 0xFE;
+ eui[0] ^= 2;
+ }
+ return 0;
+}
+
+static int addrconf_ifid_arcnet(u8 *eui, struct net_device *dev)
+{
+ /* XXX: inherit EUI-64 from other interface -- yoshfuji */
+ if (dev->addr_len != ARCNET_ALEN)
+ return -1;
+ memset(eui, 0, 7);
+ eui[7] = *(u8*)dev->dev_addr;
+ return 0;
+}
+
+static int addrconf_ifid_infiniband(u8 *eui, struct net_device *dev)
+{
+ if (dev->addr_len != INFINIBAND_ALEN)
+ return -1;
+ memcpy(eui, dev->dev_addr + 12, 8);
+ eui[0] |= 2;
+ return 0;
+}
+
static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
{
switch (dev->type) {
case ARPHRD_ETHER:
case ARPHRD_FDDI:
case ARPHRD_IEEE802_TR:
- if (dev->addr_len != ETH_ALEN)
- return -1;
- memcpy(eui, dev->dev_addr, 3);
- memcpy(eui + 5, dev->dev_addr + 3, 3);
-
- /*
- * The zSeries OSA network cards can be shared among various
- * OS instances, but the OSA cards have only one MAC address.
- * This leads to duplicate address conflicts in conjunction
- * with IPv6 if more than one instance uses the same card.
- *
- * The driver for these cards can deliver a unique 16-bit
- * identifier for each instance sharing the same card. It is
- * placed instead of 0xFFFE in the interface identifier. The
- * "u" bit of the interface identifier is not inverted in this
- * case. Hence the resulting interface identifier has local
- * scope according to RFC2373.
- */
- if (dev->dev_id) {
- eui[3] = (dev->dev_id >> 8) & 0xFF;
- eui[4] = dev->dev_id & 0xFF;
- } else {
- eui[3] = 0xFF;
- eui[4] = 0xFE;
- eui[0] ^= 2;
- }
- return 0;
+ return addrconf_ifid_eui48(eui, dev);
case ARPHRD_ARCNET:
- /* XXX: inherit EUI-64 from other interface -- yoshfuji */
- if (dev->addr_len != ARCNET_ALEN)
- return -1;
- memset(eui, 0, 7);
- eui[7] = *(u8*)dev->dev_addr;
- return 0;
+ return addrconf_ifid_arcnet(eui, dev);
case ARPHRD_INFINIBAND:
- if (dev->addr_len != INFINIBAND_ALEN)
- return -1;
- memcpy(eui, dev->dev_addr + 12, 8);
- eui[0] |= 2;
- return 0;
+ return addrconf_ifid_infiniband(eui, dev);
}
return -1;
}
/* (re)generation of randomized interface identifier (RFC 3041 3.2, 3.5) */
static int __ipv6_regen_rndid(struct inet6_dev *idev)
{
- struct net_device *dev;
- struct scatterlist sg[2];
-
- sg_set_buf(&sg[0], idev->entropy, 8);
- sg_set_buf(&sg[1], idev->work_eui64, 8);
-
- dev = idev->dev;
-
- if (ipv6_generate_eui64(idev->work_eui64, dev)) {
- printk(KERN_INFO
- "__ipv6_regen_rndid(idev=%p): cannot get EUI64 identifier; use random bytes.\n",
- idev);
- get_random_bytes(idev->work_eui64, sizeof(idev->work_eui64));
- }
regen:
- spin_lock(&md5_tfm_lock);
- if (unlikely(md5_tfm == NULL)) {
- spin_unlock(&md5_tfm_lock);
- return -1;
- }
- crypto_digest_init(md5_tfm);
- crypto_digest_update(md5_tfm, sg, 2);
- crypto_digest_final(md5_tfm, idev->work_digest);
- spin_unlock(&md5_tfm_lock);
-
- memcpy(idev->rndid, &idev->work_digest[0], 8);
+ get_random_bytes(idev->rndid, sizeof(idev->rndid));
idev->rndid[0] &= ~0x02;
- memcpy(idev->entropy, &idev->work_digest[8], 8);
/*
* <draft-ietf-ipngwg-temp-addresses-v2-00.txt>:
not good.
*/
if (valid_lft >= 0x7FFFFFFF/HZ)
- rt_expires = 0;
+ rt_expires = 0x7FFFFFFF - (0x7FFFFFFF % HZ);
else
- rt_expires = jiffies + valid_lft * HZ;
+ rt_expires = valid_lft * HZ;
+
+ /*
+ * We convert this (in jiffies) to clock_t later.
+ * Avoid arithmetic overflow there as well.
+ * Overflow can happen only if HZ < USER_HZ.
+ */
+ if (HZ < USER_HZ && rt_expires > 0x7FFFFFFF / USER_HZ)
+ rt_expires = 0x7FFFFFFF / USER_HZ;
if (pinfo->onlink) {
struct rt6_info *rt;
ip6_del_rt(rt, NULL, NULL, NULL);
rt = NULL;
} else {
- rt->rt6i_expires = rt_expires;
+ rt->rt6i_expires = jiffies + rt_expires;
}
}
} else if (valid_lft) {
addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
- dev, rt_expires, RTF_ADDRCONF|RTF_EXPIRES|RTF_PREFIX_RT);
+ dev, jiffies_to_clock_t(rt_expires), RTF_ADDRCONF|RTF_EXPIRES|RTF_PREFIX_RT);
}
if (rt)
dst_release(&rt->u.dst);
/*
* Manual configuration of address on an interface
*/
-static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen)
+static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
+ __u32 prefered_lft, __u32 valid_lft)
{
struct inet6_ifaddr *ifp;
struct inet6_dev *idev;
struct net_device *dev;
+ __u8 ifa_flags = 0;
int scope;
ASSERT_RTNL();
+ /* check the lifetime */
+ if (!valid_lft || prefered_lft > valid_lft)
+ return -EINVAL;
+
if ((dev = __dev_get_by_index(ifindex)) == NULL)
return -ENODEV;
scope = ipv6_addr_scope(pfx);
- ifp = ipv6_add_addr(idev, pfx, plen, scope, IFA_F_PERMANENT);
+ if (valid_lft == INFINITY_LIFE_TIME)
+ ifa_flags |= IFA_F_PERMANENT;
+ else if (valid_lft >= 0x7FFFFFFF/HZ)
+ valid_lft = 0x7FFFFFFF/HZ;
+
+ if (prefered_lft == 0)
+ ifa_flags |= IFA_F_DEPRECATED;
+ else if ((prefered_lft >= 0x7FFFFFFF/HZ) &&
+ (prefered_lft != INFINITY_LIFE_TIME))
+ prefered_lft = 0x7FFFFFFF/HZ;
+
+ ifp = ipv6_add_addr(idev, pfx, plen, scope, ifa_flags);
+
if (!IS_ERR(ifp)) {
+ spin_lock_bh(&ifp->lock);
+ ifp->valid_lft = valid_lft;
+ ifp->prefered_lft = prefered_lft;
+ ifp->tstamp = jiffies;
+ spin_unlock_bh(&ifp->lock);
+
addrconf_dad_start(ifp, 0);
in6_ifa_put(ifp);
+ addrconf_verify(0);
return 0;
}
return -EFAULT;
rtnl_lock();
- err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen);
+ err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen,
+ INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
rtnl_unlock();
return err;
}
return;
}
ip6_tnl_add_linklocal(idev);
- addrconf_add_mroute(dev);
}
static int addrconf_notify(struct notifier_block *this, unsigned long event,
{
struct net_device *dev = (struct net_device *) data;
struct inet6_dev *idev = __in6_dev_get(dev);
+ int run_pending = 0;
switch(event) {
case NETDEV_UP:
+ case NETDEV_CHANGE:
+ if (event == NETDEV_UP) {
+ if (!netif_carrier_ok(dev)) {
+ /* device is not ready yet. */
+ printk(KERN_INFO
+ "ADDRCONF(NETDEV_UP): %s: "
+ "link is not ready\n",
+ dev->name);
+ break;
+ }
+
+ if (idev)
+ idev->if_flags |= IF_READY;
+ } else {
+ if (!netif_carrier_ok(dev)) {
+ /* device is still not ready. */
+ break;
+ }
+
+ if (idev) {
+ if (idev->if_flags & IF_READY) {
+ /* device is already configured. */
+ break;
+ }
+ idev->if_flags |= IF_READY;
+ }
+
+ printk(KERN_INFO
+ "ADDRCONF(NETDEV_CHANGE): %s: "
+ "link becomes ready\n",
+ dev->name);
+
+ run_pending = 1;
+ }
+
switch(dev->type) {
case ARPHRD_SIT:
addrconf_sit_config(dev);
break;
};
if (idev) {
+ if (run_pending)
+ addrconf_dad_run(idev);
+
/* If the MTU changed during the interface down, when the
interface up, the changed MTU must be reflected in the
idev as well as routers.
*/
addrconf_ifdown(dev, event != NETDEV_DOWN);
break;
- case NETDEV_CHANGE:
- break;
+
case NETDEV_CHANGENAME:
#ifdef CONFIG_SYSCTL
if (idev) {
/* Step 3: clear flags for stateless addrconf */
if (how != 1)
- idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD);
+ idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY);
/* Step 4: clear address list */
#ifdef CONFIG_IPV6_PRIVACY
/*
* Duplicate Address Detection
*/
+static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
+{
+ unsigned long rand_num;
+ struct inet6_dev *idev = ifp->idev;
+
+ rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+ ifp->probes = idev->cnf.dad_transmits;
+ addrconf_mod_timer(ifp, AC_DAD, rand_num);
+}
+
static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
{
struct inet6_dev *idev = ifp->idev;
struct net_device *dev = idev->dev;
- unsigned long rand_num;
addrconf_join_solict(dev, &ifp->addr);
flags);
net_srandom(ifp->addr.s6_addr32[3]);
- rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
read_lock_bh(&idev->lock);
if (ifp->dead)
return;
}
- ifp->probes = idev->cnf.dad_transmits;
- addrconf_mod_timer(ifp, AC_DAD, rand_num);
-
+ if (!(idev->if_flags & IF_READY)) {
+ spin_unlock_bh(&ifp->lock);
+ read_unlock_bh(&idev->lock);
+ /*
+ * If the defice is not ready:
+ * - keep it tentative if it is a permanent address.
+ * - otherwise, kill it.
+ */
+ in6_ifa_hold(ifp);
+ addrconf_dad_stop(ifp);
+ return;
+ }
+ addrconf_dad_kick(ifp);
spin_unlock_bh(&ifp->lock);
out:
read_unlock_bh(&idev->lock);
}
}
+static void addrconf_dad_run(struct inet6_dev *idev) {
+ struct inet6_ifaddr *ifp;
+
+ read_lock_bh(&idev->lock);
+ for (ifp = idev->addr_list; ifp; ifp = ifp->if_next) {
+ spin_lock_bh(&ifp->lock);
+ if (!(ifp->flags & IFA_F_TENTATIVE)) {
+ spin_unlock_bh(&ifp->lock);
+ continue;
+ }
+ spin_unlock_bh(&ifp->lock);
+ addrconf_dad_kick(ifp);
+ }
+ read_unlock_bh(&idev->lock);
+}
+
#ifdef CONFIG_PROC_FS
struct if6_iter_state {
int bucket;
{
struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)v;
seq_printf(seq,
- "%04x%04x%04x%04x%04x%04x%04x%04x %02x %02x %02x %02x %8s\n",
+ NIP6_SEQFMT " %02x %02x %02x %02x %8s\n",
NIP6(ifp->addr),
ifp->idev->dev->ifindex,
ifp->prefix_len,
{
struct seq_file *seq;
int rc = -ENOMEM;
- struct if6_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ struct if6_iter_state *s = kzalloc(sizeof(*s), GFP_KERNEL);
if (!s)
goto out;
- memset(s, 0, sizeof(*s));
rc = seq_open(file, &if6_seq_ops);
if (rc)
for (i=0; i < IN6_ADDR_HSIZE; i++) {
restart:
- write_lock(&addrconf_hash_lock);
+ read_lock(&addrconf_hash_lock);
for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) {
unsigned long age;
#ifdef CONFIG_IPV6_PRIVACY
ifp->idev->nd_parms->retrans_time / HZ;
#endif
- if (age >= ifp->valid_lft) {
+ if (ifp->valid_lft != INFINITY_LIFE_TIME &&
+ age >= ifp->valid_lft) {
spin_unlock(&ifp->lock);
in6_ifa_hold(ifp);
- write_unlock(&addrconf_hash_lock);
+ read_unlock(&addrconf_hash_lock);
ipv6_del_addr(ifp);
goto restart;
+ } else if (ifp->prefered_lft == INFINITY_LIFE_TIME) {
+ spin_unlock(&ifp->lock);
+ continue;
} else if (age >= ifp->prefered_lft) {
/* jiffies - ifp->tsamp > age >= ifp->prefered_lft */
int deprecate = 0;
if (deprecate) {
in6_ifa_hold(ifp);
- write_unlock(&addrconf_hash_lock);
+ read_unlock(&addrconf_hash_lock);
ipv6_ifa_notify(0, ifp);
in6_ifa_put(ifp);
in6_ifa_hold(ifp);
in6_ifa_hold(ifpub);
spin_unlock(&ifp->lock);
- write_unlock(&addrconf_hash_lock);
+ read_unlock(&addrconf_hash_lock);
+ spin_lock(&ifpub->lock);
+ ifpub->regen_count = 0;
+ spin_unlock(&ifpub->lock);
ipv6_create_tempaddr(ifpub, ifp);
in6_ifa_put(ifpub);
in6_ifa_put(ifp);
spin_unlock(&ifp->lock);
}
}
- write_unlock(&addrconf_hash_lock);
+ read_unlock(&addrconf_hash_lock);
}
addr_chk_timer.expires = time_before(next, jiffies + HZ) ? jiffies + HZ : next;
pfx = RTA_DATA(rta[IFA_ADDRESS-1]);
}
if (rta[IFA_LOCAL-1]) {
- if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx)))
+ if (RTA_PAYLOAD(rta[IFA_LOCAL-1]) < sizeof(*pfx) ||
+ (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx))))
return -EINVAL;
pfx = RTA_DATA(rta[IFA_LOCAL-1]);
}
}
static int
+inet6_addr_modify(int ifindex, struct in6_addr *pfx,
+ __u32 prefered_lft, __u32 valid_lft)
+{
+ struct inet6_ifaddr *ifp = NULL;
+ struct net_device *dev;
+ int ifa_flags = 0;
+
+ if ((dev = __dev_get_by_index(ifindex)) == NULL)
+ return -ENODEV;
+
+ if (!(dev->flags&IFF_UP))
+ return -ENETDOWN;
+
+ if (!valid_lft || (prefered_lft > valid_lft))
+ return -EINVAL;
+
+ ifp = ipv6_get_ifaddr(pfx, dev, 1);
+ if (ifp == NULL)
+ return -ENOENT;
+
+ if (valid_lft == INFINITY_LIFE_TIME)
+ ifa_flags = IFA_F_PERMANENT;
+ else if (valid_lft >= 0x7FFFFFFF/HZ)
+ valid_lft = 0x7FFFFFFF/HZ;
+
+ if (prefered_lft == 0)
+ ifa_flags = IFA_F_DEPRECATED;
+ else if ((prefered_lft >= 0x7FFFFFFF/HZ) &&
+ (prefered_lft != INFINITY_LIFE_TIME))
+ prefered_lft = 0x7FFFFFFF/HZ;
+
+ spin_lock_bh(&ifp->lock);
+ ifp->flags = (ifp->flags & ~(IFA_F_DEPRECATED|IFA_F_PERMANENT)) | ifa_flags;
+
+ ifp->tstamp = jiffies;
+ ifp->valid_lft = valid_lft;
+ ifp->prefered_lft = prefered_lft;
+
+ spin_unlock_bh(&ifp->lock);
+ if (!(ifp->flags&IFA_F_TENTATIVE))
+ ipv6_ifa_notify(0, ifp);
+ in6_ifa_put(ifp);
+
+ addrconf_verify(0);
+
+ return 0;
+}
+
+static int
inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct rtattr **rta = arg;
struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
struct in6_addr *pfx;
+ __u32 valid_lft = INFINITY_LIFE_TIME, prefered_lft = INFINITY_LIFE_TIME;
pfx = NULL;
if (rta[IFA_ADDRESS-1]) {
pfx = RTA_DATA(rta[IFA_ADDRESS-1]);
}
if (rta[IFA_LOCAL-1]) {
- if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx)))
+ if (RTA_PAYLOAD(rta[IFA_LOCAL-1]) < sizeof(*pfx) ||
+ (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx))))
return -EINVAL;
pfx = RTA_DATA(rta[IFA_LOCAL-1]);
}
if (pfx == NULL)
return -EINVAL;
- return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
+ if (rta[IFA_CACHEINFO-1]) {
+ struct ifa_cacheinfo *ci;
+ if (RTA_PAYLOAD(rta[IFA_CACHEINFO-1]) < sizeof(*ci))
+ return -EINVAL;
+ ci = RTA_DATA(rta[IFA_CACHEINFO-1]);
+ valid_lft = ci->ifa_valid;
+ prefered_lft = ci->ifa_prefered;
+ }
+
+ if (nlh->nlmsg_flags & NLM_F_REPLACE) {
+ int ret;
+ ret = inet6_addr_modify(ifm->ifa_index, pfx,
+ prefered_lft, valid_lft);
+ if (ret == 0 || !(nlh->nlmsg_flags & NLM_F_CREATE))
+ return ret;
+ }
+
+ return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen,
+ prefered_lft, valid_lft);
+
}
+/* Maximum length of ifa_cacheinfo attributes */
+#define INET6_IFADDR_RTA_SPACE \
+ RTA_SPACE(16) /* IFA_ADDRESS */ + \
+ RTA_SPACE(sizeof(struct ifa_cacheinfo)) /* CACHEINFO */
+
static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
u32 pid, u32 seq, int event, unsigned int flags)
{
return inet6_dump_addr(skb, cb, type);
}
+static int inet6_rtm_getaddr(struct sk_buff *in_skb,
+ struct nlmsghdr* nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
+ struct in6_addr *addr = NULL;
+ struct net_device *dev = NULL;
+ struct inet6_ifaddr *ifa;
+ struct sk_buff *skb;
+ int size = NLMSG_SPACE(sizeof(struct ifaddrmsg) + INET6_IFADDR_RTA_SPACE);
+ int err;
+
+ if (rta[IFA_ADDRESS-1]) {
+ if (RTA_PAYLOAD(rta[IFA_ADDRESS-1]) < sizeof(*addr))
+ return -EINVAL;
+ addr = RTA_DATA(rta[IFA_ADDRESS-1]);
+ }
+ if (rta[IFA_LOCAL-1]) {
+ if (RTA_PAYLOAD(rta[IFA_LOCAL-1]) < sizeof(*addr) ||
+ (addr && memcmp(addr, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*addr))))
+ return -EINVAL;
+ addr = RTA_DATA(rta[IFA_LOCAL-1]);
+ }
+ if (addr == NULL)
+ return -EINVAL;
+
+ if (ifm->ifa_index)
+ dev = __dev_get_by_index(ifm->ifa_index);
+
+ if ((ifa = ipv6_get_ifaddr(addr, dev, 1)) == NULL)
+ return -EADDRNOTAVAIL;
+
+ if ((skb = alloc_skb(size, GFP_KERNEL)) == NULL) {
+ err = -ENOBUFS;
+ goto out;
+ }
+
+ NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid;
+ err = inet6_fill_ifaddr(skb, ifa, NETLINK_CB(in_skb).pid,
+ nlh->nlmsg_seq, RTM_NEWADDR, 0);
+ if (err < 0) {
+ err = -EMSGSIZE;
+ goto out_free;
+ }
+
+ err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
+ if (err > 0)
+ err = 0;
+out:
+ in6_ifa_put(ifa);
+ return err;
+out_free:
+ kfree_skb(skb);
+ goto out;
+}
+
static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa)
{
struct sk_buff *skb;
- int size = NLMSG_SPACE(sizeof(struct ifaddrmsg)+128);
+ int size = NLMSG_SPACE(sizeof(struct ifaddrmsg) + INET6_IFADDR_RTA_SPACE);
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb) {
array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor;
#endif
array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses;
+ array[DEVCONF_ACCEPT_RA_DEFRTR] = cnf->accept_ra_defrtr;
+ array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo;
+#ifdef CONFIG_IPV6_ROUTER_PREF
+ array[DEVCONF_ACCEPT_RA_RTR_PREF] = cnf->accept_ra_rtr_pref;
+ array[DEVCONF_RTR_PROBE_INTERVAL] = cnf->rtr_probe_interval;
+#ifdef CONFIV_IPV6_ROUTE_INFO
+ array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen;
+#endif
+#endif
}
+/* Maximum length of ifinfomsg attributes */
+#define INET6_IFINFO_RTA_SPACE \
+ RTA_SPACE(IFNAMSIZ) /* IFNAME */ + \
+ RTA_SPACE(MAX_ADDR_LEN) /* ADDRESS */ + \
+ RTA_SPACE(sizeof(u32)) /* MTU */ + \
+ RTA_SPACE(sizeof(int)) /* LINK */ + \
+ RTA_SPACE(0) /* PROTINFO */ + \
+ RTA_SPACE(sizeof(u32)) /* FLAGS */ + \
+ RTA_SPACE(sizeof(struct ifla_cacheinfo)) /* CACHEINFO */ + \
+ RTA_SPACE(sizeof(__s32[DEVCONF_MAX])) /* CONF */
+
static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
u32 pid, u32 seq, int event, unsigned int flags)
{
void inet6_ifinfo_notify(int event, struct inet6_dev *idev)
{
struct sk_buff *skb;
- /* 128 bytes ?? */
- int size = NLMSG_SPACE(sizeof(struct ifinfomsg)+128);
+ int size = NLMSG_SPACE(sizeof(struct ifinfomsg) + INET6_IFINFO_RTA_SPACE);
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb) {
netlink_broadcast(rtnl, skb, 0, RTNLGRP_IPV6_IFINFO, GFP_ATOMIC);
}
+/* Maximum length of prefix_cacheinfo attributes */
+#define INET6_PREFIX_RTA_SPACE \
+ RTA_SPACE(sizeof(((struct prefix_info *)NULL)->prefix)) /* ADDRESS */ + \
+ RTA_SPACE(sizeof(struct prefix_cacheinfo)) /* CACHEINFO */
+
static int inet6_fill_prefix(struct sk_buff *skb, struct inet6_dev *idev,
struct prefix_info *pinfo, u32 pid, u32 seq,
int event, unsigned int flags)
struct prefix_info *pinfo)
{
struct sk_buff *skb;
- int size = NLMSG_SPACE(sizeof(struct prefixmsg)+128);
+ int size = NLMSG_SPACE(sizeof(struct prefixmsg) + INET6_PREFIX_RTA_SPACE);
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb) {
[RTM_GETLINK - RTM_BASE] = { .dumpit = inet6_dump_ifinfo, },
[RTM_NEWADDR - RTM_BASE] = { .doit = inet6_rtm_newaddr, },
[RTM_DELADDR - RTM_BASE] = { .doit = inet6_rtm_deladdr, },
- [RTM_GETADDR - RTM_BASE] = { .dumpit = inet6_dump_ifaddr, },
+ [RTM_GETADDR - RTM_BASE] = { .doit = inet6_rtm_getaddr,
+ .dumpit = inet6_dump_ifaddr, },
[RTM_GETMULTICAST - RTM_BASE] = { .dumpit = inet6_dump_ifmcaddr, },
[RTM_GETANYCAST - RTM_BASE] = { .dumpit = inet6_dump_ifacaddr, },
[RTM_NEWROUTE - RTM_BASE] = { .doit = inet6_rtm_newroute, },
switch (event) {
case RTM_NEWADDR:
- dst_hold(&ifp->rt->u.dst);
- if (ip6_ins_rt(ifp->rt, NULL, NULL, NULL))
- dst_release(&ifp->rt->u.dst);
+ ip6_ins_rt(ifp->rt, NULL, NULL, NULL);
if (ifp->idev->cnf.forwarding)
addrconf_join_anycast(ifp);
break;
dst_hold(&ifp->rt->u.dst);
if (ip6_del_rt(ifp->rt, NULL, NULL, NULL))
dst_free(&ifp->rt->u.dst);
- else
- dst_release(&ifp->rt->u.dst);
break;
}
}
.proc_handler = &proc_dointvec,
},
{
+ .ctl_name = NET_IPV6_ACCEPT_RA_DEFRTR,
+ .procname = "accept_ra_defrtr",
+ .data = &ipv6_devconf.accept_ra_defrtr,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ {
+ .ctl_name = NET_IPV6_ACCEPT_RA_PINFO,
+ .procname = "accept_ra_pinfo",
+ .data = &ipv6_devconf.accept_ra_pinfo,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+#ifdef CONFIG_IPV6_ROUTER_PREF
+ {
+ .ctl_name = NET_IPV6_ACCEPT_RA_RTR_PREF,
+ .procname = "accept_ra_rtr_pref",
+ .data = &ipv6_devconf.accept_ra_rtr_pref,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ {
+ .ctl_name = NET_IPV6_RTR_PROBE_INTERVAL,
+ .procname = "router_probe_interval",
+ .data = &ipv6_devconf.rtr_probe_interval,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_jiffies,
+ .strategy = &sysctl_jiffies,
+ },
+#ifdef CONFIV_IPV6_ROUTE_INFO
+ {
+ .ctl_name = NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN,
+ .procname = "accept_ra_rt_info_max_plen",
+ .data = &ipv6_devconf.accept_ra_rt_info_max_plen,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+#endif
+#endif
+ {
.ctl_name = 0, /* sentinel */
}
},
int register_inet6addr_notifier(struct notifier_block *nb)
{
- return notifier_chain_register(&inet6addr_chain, nb);
+ return atomic_notifier_chain_register(&inet6addr_chain, nb);
}
int unregister_inet6addr_notifier(struct notifier_block *nb)
{
- return notifier_chain_unregister(&inet6addr_chain,nb);
+ return atomic_notifier_chain_unregister(&inet6addr_chain,nb);
}
/*
register_netdevice_notifier(&ipv6_dev_notf);
-#ifdef CONFIG_IPV6_PRIVACY
- md5_tfm = crypto_alloc_tfm("md5", 0);
- if (unlikely(md5_tfm == NULL))
- printk(KERN_WARNING
- "failed to load transform for md5\n");
-#endif
-
addrconf_verify(0);
rtnetlink_links[PF_INET6] = inet6_rtnetlink_table;
#ifdef CONFIG_SYSCTL
rtnl_unlock();
-#ifdef CONFIG_IPV6_PRIVACY
- crypto_free_tfm(md5_tfm);
- md5_tfm = NULL;
-#endif
-
#ifdef CONFIG_PROC_FS
proc_net_remove("if_inet6");
#endif