From: Fernando Fernandez Mancera <fmancera@suse.de>
To: netdev@vger.kernel.org
Cc: linux-kselftest@vger.kernel.org, horms@kernel.org,
pabeni@redhat.com, kuba@kernel.org, edumazet@google.com,
davem@davemloft.net, idosch@nvidia.com, dsahern@kernel.org,
"Fernando Fernandez Mancera" <fmancera@suse.de>,
"Łukasz Stelmach" <steelman@post.pl>
Subject: [PATCH 1/2 net v3] ipv6: addrconf: fix temp address generation after prefix deprecation
Date: Thu, 7 May 2026 15:28:27 +0200 [thread overview]
Message-ID: <20260507132828.3923-1-fmancera@suse.de> (raw)
When a router temporarily deprecates an IPv6 prefix (either by sending a
Router Advertisement with Preferred Lifetime = 0 or by letting the
lifetime expire) and later restores it, the kernel permanently loses its
ability to generate temporary privacy addresses (RFC 8981) for that
prefix.
This happens because the address worker attempts to generate a
replacement temporary address when the current one nears expiration. As
the base prefix is deprecated already, the generation fails after
marking the temporary address already having spawned a replacement
(ifp->regen_count++).
When the router eventually restores the prefix, the temporary address
becomes active again. However, once it naturally expires, the address
worker sees this temporary address already tried to generate one and
skips the regeneration.
Fix this by verifying that the base prefix has sufficient preferred
lifetime remaining before attempting to generate a new temporary
address. In addition, make ipv6_create_tempaddr() return meaningful
error codes. This way, we can catch if a 0-lft RA arrived just after we
passed the verification mentioned above. If we don't have sufficient
preferred lifetime remaining, the worker will keep the next timer as it
is.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: Łukasz Stelmach <steelman@post.pl>
Closes: https://lore.kernel.org/netdev/87340td30q.fsf%25steelman@post.pl/
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
---
v2: adjusted commit message, adjusted the implementation to cover all
race conditions
v3: regen now if ipv6_create_tempaddr failed due to timer to avoid an
infinite loop as we restart the loop and we need to check now against
prefered_lft again.
---
net/ipv6/addrconf.c | 28 ++++++++++++++++++++++------
1 file changed, 22 insertions(+), 6 deletions(-)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 5476b6536eb7..d54737b5610d 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1379,7 +1379,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
write_unlock_bh(&idev->lock);
pr_info("%s: use_tempaddr is disabled\n", __func__);
in6_dev_put(idev);
- ret = -1;
+ ret = -EOPNOTSUPP;
goto out;
}
spin_lock_bh(&ifp->lock);
@@ -1390,7 +1390,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
pr_warn("%s: regeneration time exceeded - disabled temporary address support\n",
__func__);
in6_dev_put(idev);
- ret = -1;
+ ret = -EADDRNOTAVAIL;
goto out;
}
in6_ifa_hold(ifp);
@@ -1466,7 +1466,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
cfg.preferred_lft > if_public_preferred_lft) {
in6_ifa_put(ifp);
in6_dev_put(idev);
- ret = -1;
+ ret = -EINVAL;
goto out;
}
}
@@ -4655,8 +4655,17 @@ static void addrconf_verify_rtnl(struct net *net)
/* This is a non-regenerated temporary addr. */
unsigned long regen_advance = ipv6_get_regen_advance(ifp->idev);
+ unsigned long pub_tstamp = READ_ONCE(ifp->ifpub->tstamp);
+ unsigned long pub_age = 0;
+ bool pub_expired = false;
+
+ if (time_after(now, pub_tstamp))
+ pub_age = (now - pub_tstamp) / HZ;
- if (age + regen_advance >= ifp->prefered_lft) {
+ if (pub_age + regen_advance >= READ_ONCE(ifp->ifpub->prefered_lft))
+ pub_expired = true;
+
+ if (age + regen_advance >= ifp->prefered_lft && !pub_expired) {
struct inet6_ifaddr *ifpub = ifp->ifpub;
if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next))
next = ifp->tstamp + ifp->prefered_lft * HZ;
@@ -4670,12 +4679,19 @@ static void addrconf_verify_rtnl(struct net *net)
ifpub->regen_count = 0;
spin_unlock(&ifpub->lock);
rcu_read_unlock_bh();
- ipv6_create_tempaddr(ifpub, true);
+
+ if (ipv6_create_tempaddr(ifpub, true) == -EINVAL) {
+ spin_lock_bh(&ifp->lock);
+ ifp->regen_count = 0;
+ spin_unlock_bh(&ifp->lock);
+ now = jiffies;
+ }
in6_ifa_put(ifpub);
in6_ifa_put(ifp);
rcu_read_lock_bh();
goto restart;
- } else if (time_before(ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ, next))
+ } else if (time_before(ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ, next) &&
+ !pub_expired)
next = ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ;
}
--
2.53.0
next reply other threads:[~2026-05-07 13:28 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-07 13:28 Fernando Fernandez Mancera [this message]
2026-05-07 13:28 ` [PATCH 2/2 net v3] selftests: fib_tests: add temporary IPv6 address renewal test Fernando Fernandez Mancera
2026-05-10 14:55 ` [PATCH 1/2 net v3] ipv6: addrconf: fix temp address generation after prefix deprecation Ido Schimmel
2026-05-10 15:43 ` Fernando Fernandez Mancera
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260507132828.3923-1-fmancera@suse.de \
--to=fmancera@suse.de \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=kuba@kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=steelman@post.pl \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox