diff options
author | David S. Miller <davem@davemloft.net> | 2011-01-24 02:27:15 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-01-25 15:49:08 -0500 |
commit | 73a8bd74e2618990dbb218c3d82f53e60acd9af0 (patch) | |
tree | 6bc0dddddd6a5f76a6d0a6adb41d466814cbc023 /net/ipv6/addrconf.c | |
parent | de0368d5fec7b9ef95228510f2edb79610beb448 (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.c | 81 |
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 | } |