Skip to content

Commit 73a8bd7

Browse files
committed
ipv6: Revert 'administrative down' address handling changes.
This reverts the following set of commits: d1ed113 ("ipv6: remove duplicate neigh_ifdown") 29ba5fe ("ipv6: don't flush routes when setting loopback down") 9d82ca9 ("ipv6: fix missing in6_ifa_put in addrconf") 2de7957 ("ipv6: addrconf: don't remove address state on ifdown if the address is being kept") 8595805 ("IPv6: only notify protocols if address is compeletely gone") 27bdb2a ("IPv6: keep tentative addresses in hash table") 93fa159 ("IPv6: keep route for tentative address") 8f37ada ("IPv6: fix race between cleanup and add/delete address") 84e8b80 ("IPv6: addrconf notify when address is unavailable") dc2b99f ("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>
1 parent de0368d commit 73a8bd7

File tree

1 file changed

+33
-48
lines changed

1 file changed

+33
-48
lines changed

net/ipv6/addrconf.c

Lines changed: 33 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2661,14 +2661,12 @@ static int addrconf_ifdown(struct net_device *dev, int how)
26612661
struct net *net = dev_net(dev);
26622662
struct inet6_dev *idev;
26632663
struct inet6_ifaddr *ifa;
2664-
LIST_HEAD(keep_list);
2665-
int state;
2664+
int state, i;
26662665

26672666
ASSERT_RTNL();
26682667

2669-
/* Flush routes if device is being removed or it is not loopback */
2670-
if (how || !(dev->flags & IFF_LOOPBACK))
2671-
rt6_ifdown(net, dev);
2668+
rt6_ifdown(net, dev);
2669+
neigh_ifdown(&nd_tbl, dev);
26722670

26732671
idev = __in6_dev_get(dev);
26742672
if (idev == NULL)
@@ -2689,6 +2687,23 @@ static int addrconf_ifdown(struct net_device *dev, int how)
26892687

26902688
}
26912689

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+
26922707
write_lock_bh(&idev->lock);
26932708

26942709
/* Step 2: clear flags for stateless addrconf */
@@ -2722,52 +2737,23 @@ static int addrconf_ifdown(struct net_device *dev, int how)
27222737
struct inet6_ifaddr, if_list);
27232738
addrconf_del_timer(ifa);
27242739

2725-
/* If just doing link down, and address is permanent
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;
2740+
list_del(&ifa->if_list);
27372741

2738-
/* If it was tentative already, no need to notify */
2739-
if (ifa->flags & IFA_F_TENTATIVE)
2740-
continue;
2742+
write_unlock_bh(&idev->lock);
27412743

2742-
/* Flag it for later restoration when link comes up */
2743-
ifa->flags |= IFA_F_TENTATIVE;
2744-
ifa->state = INET6_IFADDR_STATE_DAD;
2745-
} else {
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-
}
2744+
spin_lock_bh(&ifa->state_lock);
2745+
state = ifa->state;
2746+
ifa->state = INET6_IFADDR_STATE_DEAD;
2747+
spin_unlock_bh(&ifa->state_lock);
27642748

2765-
in6_ifa_put(ifa);
2766-
write_lock_bh(&idev->lock);
2749+
if (state != INET6_IFADDR_STATE_DEAD) {
2750+
__ipv6_ifa_notify(RTM_DELADDR, ifa);
2751+
atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
27672752
}
2768-
}
2753+
in6_ifa_put(ifa);
27692754

2770-
list_splice(&keep_list, &idev->addr_list);
2755+
write_lock_bh(&idev->lock);
2756+
}
27712757

27722758
write_unlock_bh(&idev->lock);
27732759

@@ -4156,8 +4142,7 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
41564142
addrconf_leave_solict(ifp->idev, &ifp->addr);
41574143
dst_hold(&ifp->rt->dst);
41584144

4159-
if (ifp->state == INET6_IFADDR_STATE_DEAD &&
4160-
ip6_del_rt(ifp->rt))
4145+
if (ip6_del_rt(ifp->rt))
41614146
dst_free(&ifp->rt->dst);
41624147
break;
41634148
}

0 commit comments

Comments
 (0)