Skip to content

Commit

Permalink
IPv6: addrconf notify when address is unavailable
Browse files Browse the repository at this point in the history
My recent change in net-next to retain permanent addresses caused regression.
Device refcount would not go to zero when device was unregistered because
left over anycast reference would hold ipv6 dev reference which would hold
device references...

The correct procedure is to call notify chain when address is no longer
available for use.  When interface comes back DAD timer will notify
back that address is available.

Also, link local addresses should be purged when interface is brought
down. The address might be changed.

Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
stephen hemminger authored and David S. Miller committed Mar 4, 2010
1 parent 5b2a195 commit 84e8b80
Showing 1 changed file with 29 additions and 17 deletions.
46 changes: 29 additions & 17 deletions net/ipv6/addrconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -2649,11 +2649,11 @@ static int addrconf_ifdown(struct net_device *dev, int how)
write_lock_bh(&addrconf_hash_lock);
while ((ifa = *bifa) != NULL) {
if (ifa->idev == idev &&
(how || !(ifa->flags&IFA_F_PERMANENT))) {
(how || !(ifa->flags&IFA_F_PERMANENT) ||
ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)) {
*bifa = ifa->lst_next;
ifa->lst_next = NULL;
addrconf_del_timer(ifa);
in6_ifa_put(ifa);
__in6_ifa_put(ifa);
continue;
}
bifa = &ifa->lst_next;
Expand Down Expand Up @@ -2691,28 +2691,40 @@ static int addrconf_ifdown(struct net_device *dev, int how)
#endif
bifa = &idev->addr_list;
while ((ifa = *bifa) != NULL) {
if (how == 0 && (ifa->flags&IFA_F_PERMANENT)) {
/* Retain permanent address on admin down */
addrconf_del_timer(ifa);

/* If just doing link down, and address is permanent
and not link-local, then retain it. */
if (how == 0 &&
(ifa->flags&IFA_F_PERMANENT) &&
!(ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)) {
bifa = &ifa->if_next;

/* Restart DAD if needed when link comes back up */
if ( !((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) ||
idev->cnf.accept_dad <= 0 ||
(ifa->flags & IFA_F_NODAD)))
ifa->flags |= IFA_F_TENTATIVE;
/* If not doing DAD on this address, just keep it. */
if ((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) ||
idev->cnf.accept_dad <= 0 ||
(ifa->flags & IFA_F_NODAD))
continue;

/* If it was tentative already, no need to notify */
if (ifa->flags & IFA_F_TENTATIVE)
continue;

/* Flag it for later restoration when link comes up */
ifa->flags |= IFA_F_TENTATIVE;
in6_ifa_hold(ifa);
} else {
*bifa = ifa->if_next;
ifa->if_next = NULL;

ifa->dead = 1;
write_unlock_bh(&idev->lock);
}
write_unlock_bh(&idev->lock);

__ipv6_ifa_notify(RTM_DELADDR, ifa);
atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
in6_ifa_put(ifa);
__ipv6_ifa_notify(RTM_DELADDR, ifa);
atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
in6_ifa_put(ifa);

write_lock_bh(&idev->lock);
}
write_lock_bh(&idev->lock);
}
write_unlock_bh(&idev->lock);

Expand Down

0 comments on commit 84e8b80

Please sign in to comment.