Merge git://git.skbuff.net/gitroot/yoshfuji/linux-2.6.14+git+ipv6-fix-20051221a
authorDavid S. Miller <davem@sunset.davemloft.net>
Thu, 22 Dec 2005 15:41:27 +0000 (07:41 -0800)
committerDavid S. Miller <davem@sunset.davemloft.net>
Thu, 22 Dec 2005 15:41:27 +0000 (07:41 -0800)
1  2 
net/ipv6/addrconf.c

diff --combined net/ipv6/addrconf.c
index e717a034c953d367806f8678c4ba3d8178a787d9,fd03c39443663d0613bef59182e48bb3c31ad802..510220f2ae8bebafd56d0779479da8b8365a3103
@@@ -137,6 -137,7 +137,7 @@@ static int addrconf_ifdown(struct net_d
  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);
@@@ -388,6 -389,9 +389,9 @@@ static struct inet6_dev * ipv6_add_dev(
                }
  #endif
  
+               if (netif_carrier_ok(dev))
+                       ndev->if_flags |= IF_READY;
                write_lock_bh(&addrconf_lock);
                dev->ip6_ptr = ndev;
                write_unlock_bh(&addrconf_lock);
@@@ -415,6 -419,7 +419,7 @@@ static struct inet6_dev * ipv6_find_ide
                if ((idev = ipv6_add_dev(dev)) == NULL)
                        return NULL;
        }
        if (dev->flags&IFF_UP)
                ipv6_mc_up(idev);
        return idev;
@@@ -634,7 -639,8 +639,7 @@@ static void ipv6_del_addr(struct inet6_
        }
  #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);
  
@@@ -904,11 -908,18 +909,18 @@@ int ipv6_dev_get_saddr(struct net_devic
  
                        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
@@@ -1216,10 -1227,8 +1228,8 @@@ int ipv6_rcv_saddr_equal(const struct s
  
  /* Gets referenced address, destroys ifaddr */
  
- void addrconf_dad_failure(struct inet6_ifaddr *ifp)
+ 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. */
  
@@@ -2134,9 -2149,42 +2150,42 @@@ static int addrconf_notify(struct notif
  {
        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;
+                       }
+               } 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) {
@@@ -2269,7 -2319,7 +2320,7 @@@ static int addrconf_ifdown(struct net_d
  
        /* 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)
+               addrconf_dad_kick(ifp);
+       else {
+               /*
+                * 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);
+       }
  
        spin_unlock_bh(&ifp->lock);
  out:
@@@ -2493,6 -2560,22 +2561,22 @@@ static void addrconf_dad_completed(stru
        }
  }
  
+ 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;