[PATCH net v4 4/4] ipv6: do not restart DAD for a dead ifaddr
From: Daehyeon Ko
Date: Thu Oct 15 2026 - 01:09:24 EST
addrconf_dad_failure() can create a stable-privacy replacement without
RTNL. After ipv6_add_addr() publishes it, addrconf_ifdown() can mark the
new ifaddr DEAD, clear its route, and unlink it before the recovery path
takes ifp2->lock.
The recovery path then unconditionally changes the state back to PREDAD
and queues delayed DAD work. The MTU teardown path can leave IF_READY
set. For an optimistic address, addrconf_dad_begin() can consequently
pass the cleared NULL route to ip6_ins_rt().
This follow-on was found by source review and was not separately
triggered at runtime.
The new ifaddr is already initialized in PREDAD. Under ifp2->lock, record
the retry and queue DAD only if teardown has not changed the state to
DEAD. If teardown wins after this check, the work observes DEAD and exits
without reviving the address.
This complements the existing lock_errdad check that preserves the original
ifaddr's DEAD state after the same function reacquires its lock.
Fixes: 5f40ef77adb2 ("ipv6: do retries on stable privacy addresses")
Cc: stable@xxxxxxxxxxxxxxx
Reported-by: Sashiko <netdev-bot+sashiko@xxxxxxxxxx>
Closes: https://lore.kernel.org/r/179161099408.434549.14449036924552933828@xxxxxxxxxx
Link: https://lore.kernel.org/r/20260513025509.3776405-1-lilinmao@xxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Daehyeon Ko <4ncienth@xxxxxxxxx>
---
net/ipv6/addrconf.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index df566681226195..a2d3fb187f7684 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2198,6 +2198,7 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
ifp->idev->dev->name, &ifp->addr, eth_hdr(skb)->h_source);
if (ifp->flags & IFA_F_STABLE_PRIVACY) {
+ bool start_dad = false;
struct in6_addr new_addr;
struct inet6_ifaddr *ifp2;
int retries = ifp->stable_privacy_retry + 1;
@@ -2236,11 +2237,14 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
goto lock_errdad;
spin_lock_bh(&ifp2->lock);
- ifp2->stable_privacy_retry = retries;
- ifp2->state = INET6_IFADDR_STATE_PREDAD;
+ if (ifp2->state != INET6_IFADDR_STATE_DEAD) {
+ ifp2->stable_privacy_retry = retries;
+ start_dad = true;
+ }
spin_unlock_bh(&ifp2->lock);
- addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay);
+ if (start_dad)
+ addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay);
in6_ifa_put(ifp2);
lock_errdad:
spin_lock_bh(&ifp->lock);
--
2.55.0