Re: [PATCH 1/2 net] ipv6: addrconf: fix temp address generation after prefix deprecation

Fernando Fernandez Mancera
2026-05-04T22:58:34Z

On 5/4/26 9:51 PM, Fernando Fernandez Mancera wrote:
> On 5/4/26 6:35 PM, Ido Schimmel wrote:
>> On Mon, May 04, 2026 at 12:11:40AM +0200, Fernando Fernandez Mancera
>> wrote:
>>> 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, burning the
>>> retry counter for temporary address generation of that prefix.
>>>
>>> When the router eventually restores the prefix, the temporary address
>>> becomes active again. However, once it naturally expires, the kernel
>>> sees the exhausted retry limit and permanently stops generating new
>>> privacy addresses.
>>
>> It's not clear to me to which "retry counter" you are referring to. Are
>> you referring to the counter of the temporary address or to that of the
>> "public address" from which it was generated?
>>
>> AFAICT, in the case of temporary addresses (those w/o "mngtmpaddr") this
>> isn't really a counter, but a boolean that tells you if an address was
>> already spawned from this address.
>>
>>>
>>> Fix this by verifying that the base prefix has sufficient preferred
>>> lifetime remaining before attempting to generate a new temporary
>>> address. This prevents the worker from burning through its retry counter
>>> during temporary network deprecation events like a router reboot.
>>
>> Again, I think that "retry counter" here is confusing. IIUC, what
>> happens is that the kernel marks the temporary address as having spawned
>> an address ('ifp->regen_count++'), then tries to spawn an address by
>> calling ipv6_create_tempaddr(), which fails because the preferred
>> lifetime of the public address is 0.
>>
>
> Yes, you are right. I used the word counter as the variable was ifp-
> >regen_count although for temporary addresses it is just a boolean. Let
> me modify the commit message to make it clear.
>
> Thanks Ido for reviewing!
>

I have checked sashiko feedback and it spotted another race condition.
To fix this altogether I believe we could improve ipv6_create_tempaddr()
to return meaningful return value. This way we could catch a
-ETIME/-ETIMEDOUT and reset the regen_count on the temporary address.

https://sashiko.dev/#/patchset/20260503221139.3742-3-fmancera%40suse.de?part=1

>>>
>>> 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>
>>> ---
>>>   net/ipv6/addrconf.c | 4 +++-
>>>   1 file changed, 3 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
>>> index 5476b6536eb7..f6a3d9da3cb1 100644
>>> --- a/net/ipv6/addrconf.c
>>> +++ b/net/ipv6/addrconf.c
>>> @@ -4654,9 +4654,11 @@ static void addrconf_verify_rtnl(struct net *net)
>>>                   !ifp->regen_count && ifp->ifpub) {
>>>                   /* This is a non-regenerated temporary addr. */
>>> +                unsigned long pub_age = (now - READ_ONCE(ifp->ifpub-
>>> >tstamp)) / HZ;
>>>                   unsigned long regen_advance =
>>> ipv6_get_regen_advance(ifp->idev);
>>> -                if (age + regen_advance >= ifp->prefered_lft) {
>>> +                if (age + regen_advance >= ifp->prefered_lft &&
>>> +                    pub_age + regen_advance < READ_ONCE(ifp->ifpub-
>>> >prefered_lft)) {
>>>                       struct inet6_ifaddr *ifpub = ifp->ifpub;
>>>                       if (time_before(ifp->tstamp + ifp->prefered_lft
>>> * HZ, next))
>>>                           next = ifp->tstamp + ifp->prefered_lft * HZ;
>>> --
>>> 2.53.0
>>>
>