#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/if_arcnet.h>
+#include <linux/if_infiniband.h>
#include <linux/route.h>
#include <linux/inetdevice.h>
#include <linux/init.h>
static void addrconf_sysctl_unregister(struct ipv6_devconf *p);
#endif
-int inet6_dev_count;
-int inet6_ifa_count;
-
#ifdef CONFIG_IPV6_PRIVACY
static int __ipv6_regen_rndid(struct inet6_dev *idev);
static int __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr);
static int desync_factor = MAX_DESYNC_FACTOR * HZ;
static struct crypto_tfm *md5_tfm;
-static spinlock_t md5_tfm_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(md5_tfm_lock);
#endif
static int ipv6_count_addresses(struct inet6_dev *idev);
* Configured unicast address hash table
*/
static struct inet6_ifaddr *inet6_addr_lst[IN6_ADDR_HSIZE];
-static rwlock_t addrconf_hash_lock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(addrconf_hash_lock);
/* Protects inet6 devices */
-rwlock_t addrconf_lock = RW_LOCK_UNLOCKED;
+DEFINE_RWLOCK(addrconf_lock);
static void addrconf_verify(unsigned long);
static struct timer_list addr_chk_timer =
TIMER_INITIALIZER(addrconf_verify, 0, 0);
-static spinlock_t addrconf_verify_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(addrconf_verify_lock);
static void addrconf_join_anycast(struct inet6_ifaddr *ifp);
static void addrconf_leave_anycast(struct inet6_ifaddr *ifp);
static void addrconf_dad_timer(unsigned long data);
static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
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);
static void inet6_prefix_notify(int event, struct inet6_dev *idev,
};
/* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
+#if 0
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+#endif
const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
int ipv6_addr_type(const struct in6_addr *addr)
printk("Freeing alive inet6 device %p\n", idev);
return;
}
- snmp6_unregister_dev(idev);
- inet6_dev_count--;
+ snmp6_free_dev(idev);
kfree(idev);
}
if (ndev) {
memset(ndev, 0, sizeof(struct inet6_dev));
- ndev->lock = RW_LOCK_UNLOCKED;
+ rwlock_init(&ndev->lock);
ndev->dev = dev;
memcpy(&ndev->cnf, &ipv6_devconf_dflt, sizeof(ndev->cnf));
ndev->cnf.mtu6 = dev->mtu;
kfree(ndev);
return NULL;
}
- inet6_dev_count++;
/* 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;
+ }
+
if (snmp6_register_dev(ndev) < 0) {
ADBG((KERN_WARNING
"%s(): cannot create /proc/net/dev_snmp6/%s\n",
return idev;
}
+#ifdef CONFIG_SYSCTL
static void dev_forward_change(struct inet6_dev *idev)
{
struct net_device *dev;
}
read_unlock(&dev_base_lock);
}
-
+#endif
/* Nobody refers to this ifaddr, destroy it */
}
dst_release(&ifp->rt->u.dst);
- inet6_ifa_count--;
kfree(ifp);
}
struct inet6_ifaddr *ifa = NULL;
struct rt6_info *rt;
int hash;
- static spinlock_t lock = SPIN_LOCK_UNLOCKED;
int err = 0;
- spin_lock_bh(&lock);
+ read_lock_bh(&addrconf_lock);
+ if (idev->dead) {
+ err = -ENODEV; /*XXX*/
+ goto out2;
+ }
+
+ write_lock(&addrconf_hash_lock);
/* Ignore adding duplicate addresses on an interface */
if (ipv6_chk_same_addr(addr, idev->dev)) {
ifa->flags = flags | IFA_F_TENTATIVE;
ifa->cstamp = ifa->tstamp = jiffies;
- read_lock(&addrconf_lock);
- if (idev->dead) {
- read_unlock(&addrconf_lock);
- err = -ENODEV; /*XXX*/
- goto out;
- }
-
- inet6_ifa_count++;
ifa->idev = idev;
in6_dev_hold(idev);
/* For caller */
/* Add to big hash table */
hash = ipv6_addr_hash(addr);
- write_lock_bh(&addrconf_hash_lock);
ifa->lst_next = inet6_addr_lst[hash];
inet6_addr_lst[hash] = ifa;
in6_ifa_hold(ifa);
- write_unlock_bh(&addrconf_hash_lock);
+ write_unlock(&addrconf_hash_lock);
- write_lock_bh(&idev->lock);
+ write_lock(&idev->lock);
/* Add to inet6_dev unicast addr list. */
ifa->if_next = idev->addr_list;
idev->addr_list = ifa;
#ifdef CONFIG_IPV6_PRIVACY
- ifa->regen_count = 0;
if (ifa->flags&IFA_F_TEMPORARY) {
ifa->tmp_next = idev->tempaddr_list;
idev->tempaddr_list = ifa;
in6_ifa_hold(ifa);
- } else {
- ifa->tmp_next = NULL;
}
#endif
ifa->rt = rt;
in6_ifa_hold(ifa);
- write_unlock_bh(&idev->lock);
- read_unlock(&addrconf_lock);
-out:
- spin_unlock_bh(&lock);
+ write_unlock(&idev->lock);
+out2:
+ read_unlock_bh(&addrconf_lock);
if (unlikely(err == 0))
notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa);
}
return ifa;
+out:
+ write_unlock(&addrconf_hash_lock);
+ goto out2;
}
/* This function wants to get referenced ifp and releases it before return */
#ifdef CONFIG_IPV6_PRIVACY
static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *ift)
{
- struct inet6_dev *idev;
+ struct inet6_dev *idev = ifp->idev;
struct in6_addr addr, *tmpaddr;
- unsigned long tmp_prefered_lft, tmp_valid_lft;
+ unsigned long tmp_prefered_lft, tmp_valid_lft, tmp_cstamp, tmp_tstamp;
int tmp_plen;
int ret = 0;
int max_addresses;
+ write_lock(&idev->lock);
if (ift) {
spin_lock_bh(&ift->lock);
memcpy(&addr.s6_addr[8], &ift->addr.s6_addr[8], 8);
tmpaddr = NULL;
}
retry:
- spin_lock_bh(&ifp->lock);
- in6_ifa_hold(ifp);
- idev = ifp->idev;
in6_dev_hold(idev);
- memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
- write_lock(&idev->lock);
if (idev->cnf.use_tempaddr <= 0) {
write_unlock(&idev->lock);
- spin_unlock_bh(&ifp->lock);
printk(KERN_INFO
"ipv6_create_tempaddr(): use_tempaddr is disabled.\n");
in6_dev_put(idev);
- in6_ifa_put(ifp);
ret = -1;
goto out;
}
+ spin_lock_bh(&ifp->lock);
if (ifp->regen_count++ >= idev->cnf.regen_max_retry) {
idev->cnf.use_tempaddr = -1; /*XXX*/
- write_unlock(&idev->lock);
spin_unlock_bh(&ifp->lock);
+ write_unlock(&idev->lock);
printk(KERN_WARNING
"ipv6_create_tempaddr(): regeneration time exceeded. disabled temporary address support.\n");
in6_dev_put(idev);
- in6_ifa_put(ifp);
ret = -1;
goto out;
}
+ in6_ifa_hold(ifp);
+ memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
if (__ipv6_try_regen_rndid(idev, tmpaddr) < 0) {
- write_unlock(&idev->lock);
spin_unlock_bh(&ifp->lock);
+ write_unlock(&idev->lock);
printk(KERN_WARNING
"ipv6_create_tempaddr(): regeneration of randomized interface id failed.\n");
- in6_dev_put(idev);
in6_ifa_put(ifp);
+ in6_dev_put(idev);
ret = -1;
goto out;
}
idev->cnf.temp_prefered_lft - desync_factor / HZ);
tmp_plen = ifp->prefix_len;
max_addresses = idev->cnf.max_addresses;
- write_unlock(&idev->lock);
+ tmp_cstamp = ifp->cstamp;
+ tmp_tstamp = ifp->tstamp;
spin_unlock_bh(&ifp->lock);
+
+ write_unlock(&idev->lock);
ift = !max_addresses ||
ipv6_count_addresses(idev) < max_addresses ?
ipv6_add_addr(idev, &addr, tmp_plen,
ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
if (!ift || IS_ERR(ift)) {
- in6_dev_put(idev);
in6_ifa_put(ifp);
+ in6_dev_put(idev);
printk(KERN_INFO
"ipv6_create_tempaddr(): retry temporary address regeneration.\n");
tmpaddr = &addr;
+ write_lock(&idev->lock);
goto retry;
}
+
spin_lock_bh(&ift->lock);
ift->ifpub = ifp;
ift->valid_lft = tmp_valid_lft;
ift->prefered_lft = tmp_prefered_lft;
- ift->cstamp = ifp->cstamp;
- ift->tstamp = ifp->tstamp;
+ ift->cstamp = tmp_cstamp;
+ ift->tstamp = tmp_tstamp;
spin_unlock_bh(&ift->lock);
+
addrconf_dad_start(ift, 0);
in6_ifa_put(ift);
in6_dev_put(idev);
#endif
int ipv6_dev_get_saddr(struct net_device *dev,
- struct in6_addr *daddr, struct in6_addr *saddr, int onlink)
+ struct in6_addr *daddr, struct in6_addr *saddr)
{
struct inet6_ifaddr *ifp = NULL;
struct inet6_ifaddr *match = NULL;
int err;
int hiscore = -1, score;
- if (!onlink)
- scope = ipv6_addr_scope(daddr);
- else
- scope = IFA_LINK;
+ scope = ipv6_addr_scope(daddr);
/*
* known dev
int ipv6_get_saddr(struct dst_entry *dst,
struct in6_addr *daddr, struct in6_addr *saddr)
{
- struct rt6_info *rt;
- struct net_device *dev = NULL;
- int onlink;
-
- rt = (struct rt6_info *) dst;
- if (rt)
- dev = rt->rt6i_dev;
-
- onlink = (rt && (rt->rt6i_flags & RTF_ALLONLINK));
-
- return ipv6_dev_get_saddr(dev, daddr, saddr, onlink);
+ return ipv6_dev_get_saddr(dst ? dst->dev : NULL, daddr, saddr);
}
read_lock_bh(&addrconf_hash_lock);
for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
- if (ipv6_addr_cmp(&ifp->addr, addr) == 0 &&
+ if (ipv6_addr_equal(&ifp->addr, addr) &&
!(ifp->flags&IFA_F_TENTATIVE)) {
if (dev == NULL || ifp->idev->dev == dev ||
!(ifp->scope&(IFA_LINK|IFA_HOST) || 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 (ipv6_addr_cmp(&ifp->addr, addr) == 0) {
+ if (ipv6_addr_equal(&ifp->addr, addr)) {
if (dev == NULL || ifp->idev->dev == dev)
break;
}
}
- read_unlock_bh(&addrconf_hash_lock);
return ifp != NULL;
}
read_lock_bh(&addrconf_hash_lock);
for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
- if (ipv6_addr_cmp(&ifp->addr, addr) == 0) {
+ if (ipv6_addr_equal(&ifp->addr, addr)) {
if (dev == NULL || ifp->idev->dev == dev ||
!(ifp->scope&(IFA_LINK|IFA_HOST) || strict)) {
in6_ifa_hold(ifp);
return 1;
if (sk2_rcv_saddr6 &&
- !ipv6_addr_cmp(sk_rcv_saddr6, sk2_rcv_saddr6))
+ ipv6_addr_equal(sk_rcv_saddr6, sk2_rcv_saddr6))
return 1;
if (addr_type == IPV6_ADDR_MAPPED &&
if (dev->addr_len != ETH_ALEN)
return -1;
memcpy(eui, dev->dev_addr, 3);
- memcpy(eui + 5, dev->dev_addr+3, 3);
- eui[3] = 0xFF;
- eui[4] = 0xFE;
- eui[0] ^= 2;
+ 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;
case ARPHRD_ARCNET:
/* XXX: inherit EUI-64 from other interface -- yoshfuji */
memset(eui, 0, 7);
eui[7] = *(u8*)dev->dev_addr;
return 0;
+ case ARPHRD_INFINIBAND:
+ if (dev->addr_len != INFINIBAND_ALEN)
+ return -1;
+ memcpy(eui, dev->dev_addr + 12, 8);
+ eui[0] |= 2;
+ return 0;
}
return -1;
}
read_lock_bh(&idev->lock);
for (ifp = idev->addr_list; ifp; ifp=ifp->if_next) {
if (ifp->prefix_len == plen &&
- (!memcmp(pfx, &ifp->addr, sizeof(struct in6_addr)))) {
+ ipv6_addr_equal(pfx, &ifp->addr)) {
in6_ifa_hold(ifp);
read_unlock_bh(&idev->lock);
if ((dev->type != ARPHRD_ETHER) &&
(dev->type != ARPHRD_FDDI) &&
(dev->type != ARPHRD_IEEE802_TR) &&
- (dev->type != ARPHRD_ARCNET)) {
+ (dev->type != ARPHRD_ARCNET) &&
+ (dev->type != ARPHRD_INFINIBAND)) {
/* Alas, we support only Ethernet autoconfiguration. */
return;
}
dev->ip6_ptr = NULL;
idev->dead = 1;
write_unlock_bh(&addrconf_lock);
+
+ /* Step 1.5: remove snmp6 entry */
+ snmp6_unregister_dev(idev);
+
}
/* Step 2: clear hash table */
addrconf_del_timer(ifa);
write_unlock_bh(&idev->lock);
- ipv6_ifa_notify(RTM_DELADDR, ifa);
+ __ipv6_ifa_notify(RTM_DELADDR, ifa);
in6_ifa_put(ifa);
write_lock_bh(&idev->lock);
*/
static void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags)
{
- struct net_device *dev;
+ struct inet6_dev *idev = ifp->idev;
+ struct net_device *dev = idev->dev;
unsigned long rand_num;
- dev = ifp->idev->dev;
-
addrconf_join_solict(dev, &ifp->addr);
if (ifp->prefix_len != 128 && (ifp->flags&IFA_F_PERMANENT))
flags);
net_srandom(ifp->addr.s6_addr32[3]);
- rand_num = net_random() % (ifp->idev->cnf.rtr_solicit_delay ? : 1);
+ rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+ read_lock_bh(&idev->lock);
+ if (ifp->dead)
+ goto out;
spin_lock_bh(&ifp->lock);
if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
!(ifp->flags&IFA_F_TENTATIVE)) {
ifp->flags &= ~IFA_F_TENTATIVE;
spin_unlock_bh(&ifp->lock);
+ read_unlock_bh(&idev->lock);
addrconf_dad_completed(ifp);
return;
}
- ifp->probes = ifp->idev->cnf.dad_transmits;
+ ifp->probes = idev->cnf.dad_transmits;
addrconf_mod_timer(ifp, AC_DAD, rand_num);
spin_unlock_bh(&ifp->lock);
+out:
+ read_unlock_bh(&idev->lock);
}
static void addrconf_dad_timer(unsigned long data)
{
struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data;
+ struct inet6_dev *idev = ifp->idev;
struct in6_addr unspec;
struct in6_addr mcaddr;
+ read_lock_bh(&idev->lock);
+ if (idev->dead) {
+ read_unlock_bh(&idev->lock);
+ goto out;
+ }
spin_lock_bh(&ifp->lock);
if (ifp->probes == 0) {
/*
ifp->flags &= ~IFA_F_TENTATIVE;
spin_unlock_bh(&ifp->lock);
+ read_unlock_bh(&idev->lock);
addrconf_dad_completed(ifp);
- in6_ifa_put(ifp);
- return;
+ goto out;
}
ifp->probes--;
addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time);
spin_unlock_bh(&ifp->lock);
+ read_unlock_bh(&idev->lock);
/* send a neighbour solicitation for our addr */
memset(&unspec, 0, sizeof(unspec));
addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
-
+out:
in6_ifa_put(ifp);
}
static int if6_seq_show(struct seq_file *seq, void *v)
{
struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)v;
- seq_printf(seq,
+
+ /* no ipv6 inside a vserver for now */
+ if (vx_check(0, VX_ADMIN|VX_WATCH))
+ seq_printf(seq,
"%04x%04x%04x%04x%04x%04x%04x%04x %02x %02x %02x %02x %8s\n",
NIP6(ifp->addr),
ifp->idev->dev->ifindex,
.dumpit = inet6_dump_fib, },
};
-static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
+static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
{
inet6_ifa_notify(event ? : RTM_NEWADDR, ifp);
}
}
+static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
+{
+ read_lock_bh(&addrconf_lock);
+ if (likely(ifp->idev->dead == 0))
+ __ipv6_ifa_notify(event, ifp);
+ read_unlock_bh(&addrconf_lock);
+}
+
#ifdef CONFIG_SYSCTL
static
addrconf_forward_change();
}
if (*valp)
- rt6_purge_dflt_routers(0);
+ rt6_purge_dflt_routers();
}
return ret;
}
if (*valp)
- rt6_purge_dflt_routers(0);
+ rt6_purge_dflt_routers();
} else
*valp = new;
void __init addrconf_init(void)
{
+ /* 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
+ * to other devices which are up.
+ *
+ * Unfortunately, loopback_dev is not necessarily the first
+ * entry in the global dev_base list of net devices. In fact,
+ * it is likely to be the very last entry on that list.
+ * So this causes the notifier registry below to try and
+ * give link-local addresses to all devices besides loopback_dev
+ * first, then loopback_dev, which cases all the non-loopback_dev
+ * devices to fail to get a link-local address.
+ *
+ * So, as a temporary fix, register loopback_dev first by hand.
+ * Longer term, all of the dependencies ipv6 has upon the loopback
+ * device and it being up should be removed.
+ */
+ rtnl_lock();
+ addrconf_notify(&ipv6_dev_notf, NETDEV_REGISTER, &loopback_dev);
+ if (loopback_dev.flags & IFF_UP)
+ addrconf_notify(&ipv6_dev_notf, NETDEV_UP, &loopback_dev);
+ rtnl_unlock();
+
register_netdevice_notifier(&ipv6_dev_notf);
#ifdef CONFIG_IPV6_PRIVACY