ieee1394: shrink tlabel pools, remove tpool semaphores
[safe/jmp/linux-2.6] / net / ipv6 / addrconf.c
index 1db5048..c7852b3 100644 (file)
@@ -40,7 +40,6 @@
  *                                             status etc.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -78,8 +77,6 @@
 
 #ifdef CONFIG_IPV6_PRIVACY
 #include <linux/random.h>
-#include <linux/crypto.h>
-#include <linux/scatterlist.h>
 #endif
 
 #include <asm/uaccess.h>
@@ -110,8 +107,6 @@ static int __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpad
 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);
@@ -147,7 +142,7 @@ 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 struct notifier_block *inet6addr_chain;
+static ATOMIC_NOTIFIER_HEAD(inet6addr_chain);
 
 struct ipv6_devconf ipv6_devconf = {
        .forwarding             = 0,
@@ -169,6 +164,15 @@ struct ipv6_devconf ipv6_devconf = {
        .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 = {
@@ -190,6 +194,15 @@ 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 */
@@ -327,86 +340,83 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
        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
-                              "%s: Disabled Privacy Extensions\n",
-                              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
 
-               if (netif_carrier_ok(dev))
-                       ndev->if_flags |= IF_READY;
+       if (netif_carrier_ok(dev))
+               ndev->if_flags |= IF_READY;
 
-               write_lock_bh(&addrconf_lock);
-               dev->ip6_ptr = ndev;
-               write_unlock_bh(&addrconf_lock);
+       write_lock_bh(&addrconf_lock);
+       dev->ip6_ptr = ndev;
+       write_unlock_bh(&addrconf_lock);
 
-               ipv6_mc_init_dev(ndev);
-               ndev->tstamp = jiffies;
+       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;
 }
 
@@ -498,6 +508,26 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
        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 *
@@ -524,7 +554,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
                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"));
@@ -538,7 +568,6 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
                goto out;
        }
 
-       memset(ifa, 0, sizeof(struct inet6_ifaddr));
        ipv6_addr_copy(&ifa->addr, addr);
 
        spin_lock_init(&ifa->lock);
@@ -549,6 +578,8 @@ 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;
 
+       ifa->rt = rt;
+
        ifa->idev = idev;
        in6_dev_hold(idev);
        /* For caller */
@@ -564,8 +595,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
 
        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) {
@@ -575,15 +605,13 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
        }
 #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);
@@ -678,7 +706,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
 
        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);
 
@@ -822,7 +850,7 @@ struct ipv6_saddr_score {
        int             addr_type;
        unsigned int    attrs;
        int             matchlen;
-       unsigned int    scope;
+       int             scope;
        unsigned int    rule;
 };
 
@@ -852,6 +880,8 @@ static int inline ipv6_saddr_label(const struct in6_addr *addr, int type)
   *    2002::/16               2
   *    ::/96                   3
   *    ::ffff:0:0/96           4
+  *    fc00::/7                5
+  *    2001::/32               6
   */
        if (type & IPV6_ADDR_LOOPBACK)
                return 0;
@@ -859,8 +889,12 @@ static int inline ipv6_saddr_label(const struct in6_addr *addr, int type)
                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;
 }
 
@@ -972,7 +1006,7 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
                                        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;
@@ -1059,6 +1093,9 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
                                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) {
@@ -1305,52 +1342,67 @@ static void addrconf_leave_anycast(struct inet6_ifaddr *ifp)
        __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;
 }
@@ -1376,34 +1428,9 @@ static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev)
 /* (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>:
@@ -1842,15 +1869,21 @@ err_exit:
 /*
  *     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;
        
@@ -1862,10 +1895,29 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen)
 
        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;
        }
 
@@ -1918,7 +1970,8 @@ int addrconf_add_ifaddr(void __user *arg)
                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;
 }
@@ -2143,7 +2196,6 @@ static void addrconf_ip6_tnl_config(struct net_device *dev)
                return;
        }
        ip6_tnl_add_linklocal(idev);
-       addrconf_add_mroute(dev);
 }
 
 static int addrconf_notify(struct notifier_block *this, unsigned long event, 
@@ -2165,6 +2217,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                                        dev->name);
                                break;
                        }
+
+                       if (idev)
+                               idev->if_flags |= IF_READY;
                } else {
                        if (!netif_carrier_ok(dev)) {
                                /* device is still not ready. */
@@ -2665,11 +2720,10 @@ static int if6_seq_open(struct inode *inode, struct file *file)
 {
        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)
@@ -2743,12 +2797,16 @@ restart:
                                        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);
                                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;
@@ -2825,7 +2883,8 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                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]);
        }
@@ -2836,11 +2895,61 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 }
 
 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]) {
@@ -2849,16 +2958,41 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                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)
 {
@@ -3088,10 +3222,66 @@ static int inet6_dump_ifacaddr(struct sk_buff *skb, struct netlink_callback *cb)
        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) {
@@ -3130,8 +3320,28 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
        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)
 {
@@ -3225,8 +3435,7 @@ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
 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) {
@@ -3242,6 +3451,11 @@ void inet6_ifinfo_notify(int event, struct inet6_dev *idev)
        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)
@@ -3286,7 +3500,7 @@ static void inet6_prefix_notify(int event, struct inet6_dev *idev,
                         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) {
@@ -3306,7 +3520,8 @@ static struct rtnetlink_link inet6_rtnetlink_table[RTM_NR_MSGTYPES] = {
        [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, },
@@ -3583,6 +3798,51 @@ static struct addrconf_sysctl_table
                        .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 */
                }
        },
@@ -3712,12 +3972,12 @@ static void addrconf_sysctl_unregister(struct ipv6_devconf *p)
 
 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);
 }
 
 /*
@@ -3757,13 +4017,6 @@ int __init addrconf_init(void)
 
        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
@@ -3826,11 +4079,6 @@ void __exit addrconf_cleanup(void)
 
        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