IPv6: only notify protocols if address is compeletely gone
[pandora-kernel.git] / net / ipv6 / addrconf.c
index 21b4c9e..7cba884 100644 (file)
@@ -53,6 +53,7 @@
 #include <linux/route.h>
 #include <linux/inetdevice.h>
 #include <linux/init.h>
+#include <linux/slab.h>
 #ifdef CONFIG_SYSCTL
 #include <linux/sysctl.h>
 #endif
@@ -675,7 +676,6 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
        hash = ipv6_addr_hash(addr);
 
        hlist_add_head_rcu(&ifa->addr_lst, &inet6_addr_lst[hash]);
-       in6_ifa_hold(ifa);
        spin_unlock(&addrconf_hash_lock);
 
        write_lock(&idev->lock);
@@ -723,7 +723,6 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
 
        spin_lock_bh(&addrconf_hash_lock);
        hlist_del_init_rcu(&ifp->addr_lst);
-       __in6_ifa_put(ifp);
        spin_unlock_bh(&addrconf_hash_lock);
 
        write_lock_bh(&idev->lock);
@@ -2703,20 +2702,22 @@ static int addrconf_ifdown(struct net_device *dev, int how)
                        /* Flag it for later restoration when link comes up */
                        ifa->flags |= IFA_F_TENTATIVE;
                        in6_ifa_hold(ifa);
+                       write_unlock_bh(&idev->lock);
                } else {
                        list_del(&ifa->if_list);
                        ifa->dead = 1;
-               }
-               write_unlock_bh(&idev->lock);
+                       write_unlock_bh(&idev->lock);
 
-               /* clear hash table */
-               spin_lock_bh(&addrconf_hash_lock);
-               hlist_del_init_rcu(&ifa->addr_lst);
-               __in6_ifa_put(ifa);
-               spin_unlock_bh(&addrconf_hash_lock);
+                       /* clear hash table */
+                       spin_lock_bh(&addrconf_hash_lock);
+                       hlist_del_init_rcu(&ifa->addr_lst);
+                       spin_unlock_bh(&addrconf_hash_lock);
+               }
 
                __ipv6_ifa_notify(RTM_DELADDR, ifa);
-               atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
+               if (ifa->dead)
+                       atomic_notifier_call_chain(&inet6addr_chain,
+                                                  NETDEV_DOWN, ifa);
                in6_ifa_put(ifa);
 
                write_lock_bh(&idev->lock);
@@ -3607,7 +3608,7 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
                hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
                        if (idx < s_idx)
                                goto cont;
-                       if (idx > s_idx)
+                       if (h > s_h || idx > s_idx)
                                s_ip_idx = 0;
                        ip_idx = 0;
                        idev = __in6_dev_get(dev);
@@ -4046,7 +4047,8 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
                        addrconf_leave_anycast(ifp);
                addrconf_leave_solict(ifp->idev, &ifp->addr);
                dst_hold(&ifp->rt->u.dst);
-               if (ip6_del_rt(ifp->rt))
+
+               if (ifp->dead && ip6_del_rt(ifp->rt))
                        dst_free(&ifp->rt->u.dst);
                break;
        }