aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/addrconf.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2011-01-24 02:27:15 -0500
committerDavid S. Miller <davem@davemloft.net>2011-01-25 15:49:08 -0500
commit73a8bd74e2618990dbb218c3d82f53e60acd9af0 (patch)
tree6bc0dddddd6a5f76a6d0a6adb41d466814cbc023 /net/ipv6/addrconf.c
parentde0368d5fec7b9ef95228510f2edb79610beb448 (diff)
ipv6: Revert 'administrative down' address handling changes.
This reverts the following set of commits: d1ed113f1669390da9898da3beddcc058d938587 ("ipv6: remove duplicate neigh_ifdown") 29ba5fed1bbd09c2cba890798c8f9eaab251401d ("ipv6: don't flush routes when setting loopback down") 9d82ca98f71fd686ef2f3017c5e3e6a4871b6e46 ("ipv6: fix missing in6_ifa_put in addrconf") 2de795707294972f6c34bae9de713e502c431296 ("ipv6: addrconf: don't remove address state on ifdown if the address is being kept") 8595805aafc8b077e01804c9a3668e9aa3510e89 ("IPv6: only notify protocols if address is compeletely gone") 27bdb2abcc5edb3526e25407b74bf17d1872c329 ("IPv6: keep tentative addresses in hash table") 93fa159abe50d3c55c7f83622d3f5c09b6e06f4b ("IPv6: keep route for tentative address") 8f37ada5b5f6bfb4d251a7f510f249cb855b77b3 ("IPv6: fix race between cleanup and add/delete address") 84e8b803f1e16f3a2b8b80f80a63fa2f2f8a9be6 ("IPv6: addrconf notify when address is unavailable") dc2b99f71ef477a31020511876ab4403fb7c4420 ("IPv6: keep permanent addresses on admin down") because the core semantic change to ipv6 address handling on ifdown has broken some things, in particular "disable_ipv6" sysctl handling. Stephen has made several attempts to get things back in working order, but nothing has restored disable_ipv6 fully yet. Reported-by: Eric W. Biederman <ebiederm@xmission.com> Tested-by: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r--net/ipv6/addrconf.c81
1 files changed, 33 insertions, 48 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 24a1cf110d80..fd6782e3a038 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2661,14 +2661,12 @@ static int addrconf_ifdown(struct net_device *dev, int how)
2661 struct net *net = dev_net(dev); 2661 struct net *net = dev_net(dev);
2662 struct inet6_dev *idev; 2662 struct inet6_dev *idev;
2663 struct inet6_ifaddr *ifa; 2663 struct inet6_ifaddr *ifa;
2664 LIST_HEAD(keep_list); 2664 int state, i;
2665 int state;
2666 2665
2667 ASSERT_RTNL(); 2666 ASSERT_RTNL();
2668 2667
2669 /* Flush routes if device is being removed or it is not loopback */ 2668 rt6_ifdown(net, dev);
2670 if (how || !(dev->flags & IFF_LOOPBACK)) 2669 neigh_ifdown(&nd_tbl, dev);
2671 rt6_ifdown(net, dev);
2672 2670
2673 idev = __in6_dev_get(dev); 2671 idev = __in6_dev_get(dev);
2674 if (idev == NULL) 2672 if (idev == NULL)
@@ -2689,6 +2687,23 @@ static int addrconf_ifdown(struct net_device *dev, int how)
2689 2687
2690 } 2688 }
2691 2689
2690 /* Step 2: clear hash table */
2691 for (i = 0; i < IN6_ADDR_HSIZE; i++) {
2692 struct hlist_head *h = &inet6_addr_lst[i];
2693 struct hlist_node *n;
2694
2695 spin_lock_bh(&addrconf_hash_lock);
2696 restart:
2697 hlist_for_each_entry_rcu(ifa, n, h, addr_lst) {
2698 if (ifa->idev == idev) {
2699 hlist_del_init_rcu(&ifa->addr_lst);
2700 addrconf_del_timer(ifa);
2701 goto restart;
2702 }
2703 }
2704 spin_unlock_bh(&addrconf_hash_lock);
2705 }
2706
2692 write_lock_bh(&idev->lock); 2707 write_lock_bh(&idev->lock);
2693 2708
2694 /* Step 2: clear flags for stateless addrconf */ 2709 /* Step 2: clear flags for stateless addrconf */
@@ -2722,52 +2737,23 @@ static int addrconf_ifdown(struct net_device *dev, int how)
2722 struct inet6_ifaddr, if_list); 2737 struct inet6_ifaddr, if_list);
2723 addrconf_del_timer(ifa); 2738 addrconf_del_timer(ifa);
2724 2739
2725 /* If just doing link down, and address is permanent 2740 list_del(&ifa->if_list);
2726 and not link-local, then retain it. */
2727 if (!how &&
2728 (ifa->flags&IFA_F_PERMANENT) &&
2729 !(ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)) {
2730 list_move_tail(&ifa->if_list, &keep_list);
2731
2732 /* If not doing DAD on this address, just keep it. */
2733 if ((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) ||
2734 idev->cnf.accept_dad <= 0 ||
2735 (ifa->flags & IFA_F_NODAD))
2736 continue;
2737 2741
2738 /* If it was tentative already, no need to notify */ 2742 write_unlock_bh(&idev->lock);
2739 if (ifa->flags & IFA_F_TENTATIVE)
2740 continue;
2741 2743
2742 /* Flag it for later restoration when link comes up */ 2744 spin_lock_bh(&ifa->state_lock);
2743 ifa->flags |= IFA_F_TENTATIVE; 2745 state = ifa->state;
2744 ifa->state = INET6_IFADDR_STATE_DAD; 2746 ifa->state = INET6_IFADDR_STATE_DEAD;
2745 } else { 2747 spin_unlock_bh(&ifa->state_lock);
2746 list_del(&ifa->if_list);
2747
2748 /* clear hash table */
2749 spin_lock_bh(&addrconf_hash_lock);
2750 hlist_del_init_rcu(&ifa->addr_lst);
2751 spin_unlock_bh(&addrconf_hash_lock);
2752
2753 write_unlock_bh(&idev->lock);
2754 spin_lock_bh(&ifa->state_lock);
2755 state = ifa->state;
2756 ifa->state = INET6_IFADDR_STATE_DEAD;
2757 spin_unlock_bh(&ifa->state_lock);
2758
2759 if (state != INET6_IFADDR_STATE_DEAD) {
2760 __ipv6_ifa_notify(RTM_DELADDR, ifa);
2761 atomic_notifier_call_chain(&inet6addr_chain,
2762 NETDEV_DOWN, ifa);
2763 }
2764 2748
2765 in6_ifa_put(ifa); 2749 if (state != INET6_IFADDR_STATE_DEAD) {
2766 write_lock_bh(&idev->lock); 2750 __ipv6_ifa_notify(RTM_DELADDR, ifa);
2751 atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
2767 } 2752 }
2768 } 2753 in6_ifa_put(ifa);
2769 2754
2770 list_splice(&keep_list, &idev->addr_list); 2755 write_lock_bh(&idev->lock);
2756 }
2771 2757
2772 write_unlock_bh(&idev->lock); 2758 write_unlock_bh(&idev->lock);
2773 2759
@@ -4156,8 +4142,7 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
4156 addrconf_leave_solict(ifp->idev, &ifp->addr); 4142 addrconf_leave_solict(ifp->idev, &ifp->addr);
4157 dst_hold(&ifp->rt->dst); 4143 dst_hold(&ifp->rt->dst);
4158 4144
4159 if (ifp->state == INET6_IFADDR_STATE_DEAD && 4145 if (ip6_del_rt(ifp->rt))
4160 ip6_del_rt(ifp->rt))
4161 dst_free(&ifp->rt->dst); 4146 dst_free(&ifp->rt->dst);
4162 break; 4147 break;
4163 } 4148 }