aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2010-05-18 18:54:18 -0400
committerDavid S. Miller <davem@davemloft.net>2010-05-18 18:54:18 -0400
commit4c5ff6a6fe794f102479db998c69054319279e3c (patch)
tree07e2e4de160e3eb2dbbb3f7749314cd556671826 /net/ipv6
parente9d3e084975869754d16f639378675c353560be9 (diff)
ipv6: Use state_lock to protect ifa state
This patch makes use of the new state_lock to synchronise between updates to the ifa state. This fixes the issue where a remotely triggered address deletion (through DAD failure) coincides with a local administrative address deletion, causing certain actions to be performed twice incorrectly. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/addrconf.c27
1 files changed, 23 insertions, 4 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 4e5ad9de1679..2e42162c9042 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -715,13 +715,20 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
715{ 715{
716 struct inet6_ifaddr *ifa, *ifn; 716 struct inet6_ifaddr *ifa, *ifn;
717 struct inet6_dev *idev = ifp->idev; 717 struct inet6_dev *idev = ifp->idev;
718 int state;
718 int hash; 719 int hash;
719 int deleted = 0, onlink = 0; 720 int deleted = 0, onlink = 0;
720 unsigned long expires = jiffies; 721 unsigned long expires = jiffies;
721 722
722 hash = ipv6_addr_hash(&ifp->addr); 723 hash = ipv6_addr_hash(&ifp->addr);
723 724
725 spin_lock_bh(&ifp->state_lock);
726 state = ifp->state;
724 ifp->state = INET6_IFADDR_STATE_DEAD; 727 ifp->state = INET6_IFADDR_STATE_DEAD;
728 spin_unlock_bh(&ifp->state_lock);
729
730 if (state == INET6_IFADDR_STATE_DEAD)
731 goto out;
725 732
726 spin_lock_bh(&addrconf_hash_lock); 733 spin_lock_bh(&addrconf_hash_lock);
727 hlist_del_init_rcu(&ifp->addr_lst); 734 hlist_del_init_rcu(&ifp->addr_lst);
@@ -819,6 +826,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
819 dst_release(&rt->u.dst); 826 dst_release(&rt->u.dst);
820 } 827 }
821 828
829out:
822 in6_ifa_put(ifp); 830 in6_ifa_put(ifp);
823} 831}
824 832
@@ -2626,6 +2634,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
2626 struct inet6_dev *idev; 2634 struct inet6_dev *idev;
2627 struct inet6_ifaddr *ifa; 2635 struct inet6_ifaddr *ifa;
2628 LIST_HEAD(keep_list); 2636 LIST_HEAD(keep_list);
2637 int state;
2629 2638
2630 ASSERT_RTNL(); 2639 ASSERT_RTNL();
2631 2640
@@ -2666,7 +2675,6 @@ static int addrconf_ifdown(struct net_device *dev, int how)
2666 ifa = list_first_entry(&idev->tempaddr_list, 2675 ifa = list_first_entry(&idev->tempaddr_list,
2667 struct inet6_ifaddr, tmp_list); 2676 struct inet6_ifaddr, tmp_list);
2668 list_del(&ifa->tmp_list); 2677 list_del(&ifa->tmp_list);
2669 ifa->state = INET6_IFADDR_STATE_DEAD;
2670 write_unlock_bh(&idev->lock); 2678 write_unlock_bh(&idev->lock);
2671 spin_lock_bh(&ifa->lock); 2679 spin_lock_bh(&ifa->lock);
2672 2680
@@ -2704,23 +2712,34 @@ static int addrconf_ifdown(struct net_device *dev, int how)
2704 2712
2705 /* Flag it for later restoration when link comes up */ 2713 /* Flag it for later restoration when link comes up */
2706 ifa->flags |= IFA_F_TENTATIVE; 2714 ifa->flags |= IFA_F_TENTATIVE;
2707 in6_ifa_hold(ifa); 2715
2708 write_unlock_bh(&idev->lock); 2716 write_unlock_bh(&idev->lock);
2717
2718 in6_ifa_hold(ifa);
2709 } else { 2719 } else {
2710 list_del(&ifa->if_list); 2720 list_del(&ifa->if_list);
2711 ifa->state = INET6_IFADDR_STATE_DEAD;
2712 write_unlock_bh(&idev->lock);
2713 2721
2714 /* clear hash table */ 2722 /* clear hash table */
2715 spin_lock_bh(&addrconf_hash_lock); 2723 spin_lock_bh(&addrconf_hash_lock);
2716 hlist_del_init_rcu(&ifa->addr_lst); 2724 hlist_del_init_rcu(&ifa->addr_lst);
2717 spin_unlock_bh(&addrconf_hash_lock); 2725 spin_unlock_bh(&addrconf_hash_lock);
2726
2727 write_unlock_bh(&idev->lock);
2728 spin_lock_bh(&ifa->state_lock);
2729 state = ifa->state;
2730 ifa->state = INET6_IFADDR_STATE_DEAD;
2731 spin_unlock_bh(&ifa->state_lock);
2732
2733 if (state == INET6_IFADDR_STATE_DEAD)
2734 goto put_ifa;
2718 } 2735 }
2719 2736
2720 __ipv6_ifa_notify(RTM_DELADDR, ifa); 2737 __ipv6_ifa_notify(RTM_DELADDR, ifa);
2721 if (ifa->state == INET6_IFADDR_STATE_DEAD) 2738 if (ifa->state == INET6_IFADDR_STATE_DEAD)
2722 atomic_notifier_call_chain(&inet6addr_chain, 2739 atomic_notifier_call_chain(&inet6addr_chain,
2723 NETDEV_DOWN, ifa); 2740 NETDEV_DOWN, ifa);
2741
2742put_ifa:
2724 in6_ifa_put(ifa); 2743 in6_ifa_put(ifa);
2725 2744
2726 write_lock_bh(&idev->lock); 2745 write_lock_bh(&idev->lock);