X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv6%2Faddrconf.c;h=15f481859bece658a1e873fd7de4331d2e901451;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=226aa2bb40b54a95362344b5aa96b77e90b3635e;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 226aa2bb4..15f481859 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -98,9 +99,6 @@ static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf 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); @@ -108,7 +106,7 @@ static void ipv6_regen_rndid(unsigned long data); 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); @@ -117,16 +115,19 @@ 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 int addrconf_ifdown(struct net_device *dev, int how); @@ -134,6 +135,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags); 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, @@ -186,7 +188,9 @@ static struct ipv6_devconf ipv6_devconf_dflt = { }; /* 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) @@ -304,8 +308,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev) printk("Freeing alive inet6 device %p\n", idev); return; } - snmp6_unregister_dev(idev); - inet6_dev_count--; + snmp6_free_dev(idev); kfree(idev); } @@ -323,7 +326,7 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) 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; @@ -333,10 +336,19 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) 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", @@ -401,6 +413,7 @@ static struct inet6_dev * ipv6_find_idev(struct net_device *dev) return idev; } +#ifdef CONFIG_SYSCTL static void dev_forward_change(struct inet6_dev *idev) { struct net_device *dev; @@ -419,39 +432,34 @@ static void dev_forward_change(struct inet6_dev *idev) ipv6_dev_mc_dec(dev, &addr); } for (ifa=idev->addr_list; ifa; ifa=ifa->if_next) { - ipv6_addr_prefix(&addr, &ifa->addr, ifa->prefix_len); - if (ipv6_addr_any(&addr)) - continue; if (idev->cnf.forwarding) - ipv6_dev_ac_inc(idev->dev, &addr); + addrconf_join_anycast(ifa); else - ipv6_dev_ac_dec(idev->dev, &addr); + addrconf_leave_anycast(ifa); } } -static void addrconf_forward_change(struct inet6_dev *idev) +static void addrconf_forward_change(void) { struct net_device *dev; - - if (idev) { - dev_forward_change(idev); - return; - } + struct inet6_dev *idev; read_lock(&dev_base_lock); for (dev=dev_base; dev; dev=dev->next) { read_lock(&addrconf_lock); idev = __in6_dev_get(dev); if (idev) { + int changed = (!idev->cnf.forwarding) ^ (!ipv6_devconf.forwarding); idev->cnf.forwarding = ipv6_devconf.forwarding; - dev_forward_change(idev); + if (changed) + dev_forward_change(idev); } read_unlock(&addrconf_lock); } read_unlock(&dev_base_lock); } - +#endif /* Nobody refers to this ifaddr, destroy it */ @@ -472,7 +480,8 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) printk("Freeing alive inet6 address %p\n", ifp); return; } - inet6_ifa_count--; + dst_release(&ifp->rt->u.dst); + kfree(ifp); } @@ -482,25 +491,38 @@ static struct inet6_ifaddr * ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, int scope, unsigned flags) { - struct inet6_ifaddr *ifa; + struct inet6_ifaddr *ifa = NULL; + struct rt6_info *rt; int hash; - static spinlock_t lock = SPIN_LOCK_UNLOCKED; + int err = 0; + + read_lock_bh(&addrconf_lock); + if (idev->dead) { + err = -ENODEV; /*XXX*/ + goto out2; + } - spin_lock_bh(&lock); + write_lock(&addrconf_hash_lock); /* Ignore adding duplicate addresses on an interface */ if (ipv6_chk_same_addr(addr, idev->dev)) { - spin_unlock_bh(&lock); ADBG(("ipv6_add_addr: already assigned\n")); - return ERR_PTR(-EEXIST); + err = -EEXIST; + goto out; } ifa = kmalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC); if (ifa == NULL) { - spin_unlock_bh(&lock); ADBG(("ipv6_add_addr: malloc failed\n")); - return ERR_PTR(-ENOBUFS); + err = -ENOBUFS; + goto out; + } + + rt = addrconf_dst_alloc(idev, addr, 0); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + goto out; } memset(ifa, 0, sizeof(struct inet6_ifaddr)); @@ -514,15 +536,6 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, ifa->flags = flags | IFA_F_TENTATIVE; ifa->cstamp = ifa->tstamp = jiffies; - read_lock(&addrconf_lock); - if (idev->dead) { - read_unlock(&addrconf_lock); - spin_unlock_bh(&lock); - kfree(ifa); - return ERR_PTR(-ENODEV); /*XXX*/ - } - - inet6_ifa_count++; ifa->idev = idev; in6_dev_hold(idev); /* For caller */ @@ -531,36 +544,42 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, /* 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); - spin_unlock_bh(&lock); + write_unlock(&idev->lock); +out2: + read_unlock_bh(&addrconf_lock); - notifier_call_chain(&inet6addr_chain,NETDEV_UP,ifa); + if (unlikely(err == 0)) + notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa); + else { + kfree(ifa); + ifa = ERR_PTR(err); + } return ifa; +out: + write_unlock(&addrconf_hash_lock); + goto out2; } /* This function wants to get referenced ifp and releases it before return */ @@ -629,13 +648,14 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) #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); @@ -645,40 +665,35 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i 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; } @@ -691,27 +706,33 @@ retry: 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) : 0; + 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); @@ -745,7 +766,7 @@ static int inline ipv6_saddr_pref(const struct inet6_ifaddr *ifp, u8 invpref) #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; @@ -754,10 +775,7 @@ int ipv6_dev_get_saddr(struct net_device *dev, int err; int hiscore = -1, score; - if (!onlink) - scope = ipv6_addr_scope(daddr); - else - scope = IFA_LINK; + scope = ipv6_addr_scope(daddr); /* * known dev @@ -862,17 +880,7 @@ out: 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); } @@ -918,7 +926,7 @@ int ipv6_chk_addr(struct in6_addr *addr, struct net_device *dev, int strict) 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)) @@ -935,14 +943,12 @@ int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev) 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; } @@ -953,7 +959,7 @@ struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, struct net_device * 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); @@ -989,7 +995,7 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2) 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 && @@ -1045,17 +1051,34 @@ void addrconf_join_solict(struct net_device *dev, struct in6_addr *addr) ipv6_dev_mc_inc(dev, &maddr); } -void addrconf_leave_solict(struct net_device *dev, struct in6_addr *addr) +void addrconf_leave_solict(struct inet6_dev *idev, struct in6_addr *addr) { struct in6_addr maddr; - if (dev->flags&(IFF_LOOPBACK|IFF_NOARP)) + if (idev->dev->flags&(IFF_LOOPBACK|IFF_NOARP)) return; addrconf_addr_solict_mult(addr, &maddr); - ipv6_dev_mc_dec(dev, &maddr); + __ipv6_dev_mc_dec(idev, &maddr); } +void addrconf_join_anycast(struct inet6_ifaddr *ifp) +{ + struct in6_addr addr; + ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len); + if (ipv6_addr_any(&addr)) + return; + ipv6_dev_ac_inc(ifp->idev->dev, &addr); +} + +void addrconf_leave_anycast(struct inet6_ifaddr *ifp) +{ + struct in6_addr addr; + ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len); + if (ipv6_addr_any(&addr)) + return; + __ipv6_dev_ac_dec(ifp->idev, &addr); +} static int ipv6_generate_eui64(u8 *eui, struct net_device *dev) { @@ -1066,10 +1089,29 @@ static int ipv6_generate_eui64(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); - 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 */ @@ -1078,6 +1120,12 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev) 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; } @@ -1457,8 +1505,7 @@ ok: spin_unlock(&ifp->lock); if (!(flags&IFA_F_TENTATIVE)) - ipv6_ifa_notify((flags&IFA_F_DEPRECATED) ? - 0 : RTM_NEWADDR, ifp); + ipv6_ifa_notify(0, ifp); } else spin_unlock(&ifp->lock); @@ -1510,7 +1557,7 @@ ok: * Special case for SIT interfaces where we create a new "virtual" * device. */ -int addrconf_set_dstaddr(void *arg) +int addrconf_set_dstaddr(void __user *arg) { struct in6_ifreq ireq; struct net_device *dev; @@ -1544,7 +1591,7 @@ int addrconf_set_dstaddr(void *arg) p.iph.ihl = 5; p.iph.protocol = IPPROTO_IPV6; p.iph.ttl = 64; - ifr.ifr_ifru.ifru_data = (void*)&p; + ifr.ifr_ifru.ifru_data = (void __user *)&p; oldfs = get_fs(); set_fs(KERNEL_DS); err = dev->do_ioctl(dev, &ifr, SIOCADDTUNNEL); @@ -1611,7 +1658,7 @@ static int inet6_addr_del(int ifindex, struct in6_addr *pfx, int plen) 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); @@ -1630,7 +1677,7 @@ static int inet6_addr_del(int ifindex, struct in6_addr *pfx, int plen) } -int addrconf_add_ifaddr(void *arg) +int addrconf_add_ifaddr(void __user *arg) { struct in6_ifreq ireq; int err; @@ -1647,7 +1694,7 @@ int addrconf_add_ifaddr(void *arg) return err; } -int addrconf_del_ifaddr(void *arg) +int addrconf_del_ifaddr(void __user *arg) { struct in6_ifreq ireq; int err; @@ -1778,7 +1825,8 @@ static void addrconf_dev_config(struct net_device *dev) 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; } @@ -1975,6 +2023,10 @@ static int addrconf_ifdown(struct net_device *dev, int how) 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 */ @@ -2030,7 +2082,7 @@ static int addrconf_ifdown(struct net_device *dev, int how) 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); @@ -2056,6 +2108,7 @@ static int addrconf_ifdown(struct net_device *dev, int how) neigh_sysctl_unregister(idev->nd_parms); #endif neigh_parms_release(&nd_tbl, idev->nd_parms); + neigh_ifdown(&nd_tbl, dev); in6_dev_put(idev); } return 0; @@ -2091,21 +2144,13 @@ static void addrconf_rs_timer(unsigned long data) ndisc_send_rs(ifp->idev->dev, &ifp->addr, &all_routers); } else { - struct in6_rtmsg rtmsg; - spin_unlock(&ifp->lock); - + /* + * Note: we do not support deprecated "all on-link" + * assumption any longer. + */ printk(KERN_DEBUG "%s: no IPv6 routers present\n", ifp->idev->dev->name); - - memset(&rtmsg, 0, sizeof(struct in6_rtmsg)); - rtmsg.rtmsg_type = RTMSG_NEWROUTE; - rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF; - rtmsg.rtmsg_flags = (RTF_ALLONLINK | RTF_DEFAULT | RTF_UP); - - rtmsg.rtmsg_ifindex = ifp->idev->dev->ifindex; - - ip6_route_add(&rtmsg, NULL, NULL); } out: @@ -2117,11 +2162,10 @@ out: */ 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)) @@ -2129,31 +2173,43 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags) 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) { /* @@ -2162,22 +2218,23 @@ static void addrconf_dad_timer(unsigned long data) 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); } @@ -2216,14 +2273,6 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp) addrconf_mod_timer(ifp, AC_RS, ifp->idev->cnf.rtr_solicit_interval); spin_unlock_bh(&ifp->lock); } - - if (ifp->idev->cnf.forwarding) { - struct in6_addr addr; - - ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len); - if (!ipv6_addr_any(&addr)) - ipv6_dev_ac_inc(ifp->idev->dev, &addr); - } } #ifdef CONFIG_PROC_FS @@ -2290,7 +2339,10 @@ static void if6_seq_stop(struct seq_file *seq, void *v) 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, @@ -2646,6 +2698,10 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, struct ifmcaddr6 *ifmca; struct ifacaddr6 *ifaca; + /* no ipv6 inside a vserver for now */ + if (skb->sk && skb->sk->sk_vx_info) + return skb->len; + s_idx = cb->args[0]; s_ip_idx = ip_idx = cb->args[1]; read_lock(&dev_base_lock); @@ -2865,6 +2921,10 @@ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) struct net_device *dev; struct inet6_dev *idev; + /* no ipv6 inside a vserver for now */ + if (skb->sk && skb->sk->sk_vx_info) + return skb->len; + read_lock(&dev_base_lock); for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) { if (idx < s_idx) @@ -2976,64 +3036,75 @@ static struct rtnetlink_link inet6_rtnetlink_table[RTM_MAX - RTM_BASE + 1] = { .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); switch (event) { case RTM_NEWADDR: - ip6_rt_addr_add(&ifp->addr, ifp->idev->dev, 0); + dst_hold(&ifp->rt->u.dst); + if (ip6_ins_rt(ifp->rt, NULL, NULL)) + dst_release(&ifp->rt->u.dst); + if (ifp->idev->cnf.forwarding) + addrconf_join_anycast(ifp); break; case RTM_DELADDR: - addrconf_leave_solict(ifp->idev->dev, &ifp->addr); - if (ifp->idev->cnf.forwarding) { - struct in6_addr addr; - - ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len); - if (!ipv6_addr_any(&addr)) - ipv6_dev_ac_dec(ifp->idev->dev, &addr); - } - if (!ipv6_chk_addr(&ifp->addr, ifp->idev->dev, 1)) - ip6_rt_addr_del(&ifp->addr, ifp->idev->dev); + if (ifp->idev->cnf.forwarding) + addrconf_leave_anycast(ifp); + addrconf_leave_solict(ifp->idev, &ifp->addr); + dst_hold(&ifp->rt->u.dst); + if (ip6_del_rt(ifp->rt, NULL, NULL)) + dst_free(&ifp->rt->u.dst); + else + dst_release(&ifp->rt->u.dst); break; } } +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 int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp, - void *buffer, size_t *lenp) + void __user *buffer, size_t *lenp, loff_t *ppos) { int *valp = ctl->data; int val = *valp; int ret; - ret = proc_dointvec(ctl, write, filp, buffer, lenp); - - if (write && *valp != val && valp != &ipv6_devconf_dflt.forwarding) { - struct inet6_dev *idev = NULL; + ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); + if (write && valp != &ipv6_devconf_dflt.forwarding) { if (valp != &ipv6_devconf.forwarding) { - idev = (struct inet6_dev *)ctl->extra1; - if (idev == NULL) - return ret; - } else + 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(idev); - + addrconf_forward_change(); + } if (*valp) - rt6_purge_dflt_routers(0); + rt6_purge_dflt_routers(); } return ret; } static int addrconf_sysctl_forward_strategy(ctl_table *table, - int *name, int nlen, - void *oldval, size_t *oldlenp, - void *newval, size_t newlen, + int __user *name, int nlen, + void __user *oldval, + size_t __user *oldlenp, + void __user *newval, size_t newlen, void **context) { int *valp = table->data; @@ -3043,7 +3114,7 @@ static int addrconf_sysctl_forward_strategy(ctl_table *table, return 0; if (newlen != sizeof(int)) return -EINVAL; - if (get_user(new, (int *)newval)) + if (get_user(new, (int __user *)newval)) return -EFAULT; if (new == *valp) return 0; @@ -3062,18 +3133,22 @@ static int addrconf_sysctl_forward_strategy(ctl_table *table, } if (valp != &ipv6_devconf_dflt.forwarding) { - struct inet6_dev *idev; if (valp != &ipv6_devconf.forwarding) { - idev = (struct inet6_dev *)table->extra1; + struct inet6_dev *idev = (struct inet6_dev *)table->extra1; + int changed; if (unlikely(idev == NULL)) return -ENODEV; - } else - idev = NULL; - *valp = new; - addrconf_forward_change(idev); + changed = (!*valp) ^ (!new); + *valp = new; + if (changed) + dev_forward_change(idev); + } else { + *valp = new; + addrconf_forward_change(); + } if (*valp) - rt6_purge_dflt_routers(0); + rt6_purge_dflt_routers(); } else *valp = new; @@ -3376,6 +3451,29 @@ int unregister_inet6addr_notifier(struct notifier_block *nb) 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