* [PATCH 1/2 net v4] ipv6: addrconf: fix temp address generation after prefix deprecation
@ 2026-05-11 12:26 Fernando Fernandez Mancera
2026-05-11 12:26 ` [PATCH 2/2 net v4] selftests: fib_tests: add temporary IPv6 address renewal test Fernando Fernandez Mancera
2026-05-12 18:24 ` [PATCH 1/2 net v4] ipv6: addrconf: fix temp address generation after prefix deprecation Fernando Fernandez Mancera
0 siblings, 2 replies; 6+ messages in thread
From: Fernando Fernandez Mancera @ 2026-05-11 12:26 UTC (permalink / raw)
To: netdev
Cc: linux-kselftest, horms, pabeni, kuba, edumazet, dsahern, davem,
Fernando Fernandez Mancera, Łukasz Stelmach, Ido Schimmel
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 as 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 checking if all temporary addresses for a given prefix have
already tried to spawn a replacement when processing a new RA. If so,
spawn a new temporary address.
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/
Suggested-by: Ido Schimmel <idosch@nvidia.com>
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.
v4: change the proposed fix completely, now we address the problem when
doing manage_tempaddrs().
---
net/ipv6/addrconf.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 5476b6536eb7..18a6f2de30ce 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2595,8 +2595,9 @@ static void manage_tempaddrs(struct inet6_dev *idev,
__u32 valid_lft, __u32 prefered_lft,
bool create, unsigned long now)
{
- u32 flags;
struct inet6_ifaddr *ift;
+ bool all_regen = true;
+ u32 flags;
read_lock_bh(&idev->lock);
/* update all temporary addresses in the list */
@@ -2637,6 +2638,8 @@ static void manage_tempaddrs(struct inet6_dev *idev,
ift->tstamp = now;
if (prefered_lft > 0)
ift->flags &= ~IFA_F_DEPRECATED;
+ if (!ift->regen_count)
+ all_regen = false;
spin_unlock(&ift->lock);
if (!(flags&IFA_F_TENTATIVE))
@@ -2644,12 +2647,14 @@ static void manage_tempaddrs(struct inet6_dev *idev,
}
/* Also create a temporary address if it's enabled but no temporary
- * address currently exists.
+ * address currently exists or if all temporary addresses already
+ * generated an address.
* However, we get called with valid_lft == 0, prefered_lft == 0, create == false
* as part of cleanup (ie. deleting the mngtmpaddr).
* We don't want that to result in creating a new temporary ip address.
*/
- if (list_empty(&idev->tempaddr_list) && (valid_lft || prefered_lft))
+ if ((list_empty(&idev->tempaddr_list) || all_regen) &&
+ (valid_lft || prefered_lft))
create = true;
if (create && READ_ONCE(idev->cnf.use_tempaddr) > 0) {
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 2/2 net v4] selftests: fib_tests: add temporary IPv6 address renewal test
2026-05-11 12:26 [PATCH 1/2 net v4] ipv6: addrconf: fix temp address generation after prefix deprecation Fernando Fernandez Mancera
@ 2026-05-11 12:26 ` Fernando Fernandez Mancera
2026-05-11 17:01 ` Breno Leitao
2026-05-12 18:24 ` [PATCH 1/2 net v4] ipv6: addrconf: fix temp address generation after prefix deprecation Fernando Fernandez Mancera
1 sibling, 1 reply; 6+ messages in thread
From: Fernando Fernandez Mancera @ 2026-05-11 12:26 UTC (permalink / raw)
To: netdev
Cc: linux-kselftest, horms, pabeni, kuba, edumazet, dsahern, davem,
Fernando Fernandez Mancera
Add a test to check that temporary IPv6 address is regenerated properly
after the base prefix is deprecated and restored.
Fib6 temporary address renewal test
TEST: IPv6 temporary address cleanly deprecated and regenerated [ OK ]
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
---
v2: adjusted the sleep so there is enough time for the issue to trigger,
added cleanup at the end
v3: no changes
v4: no changes
---
tools/testing/selftests/net/fib_tests.sh | 59 +++++++++++++++++++++++-
1 file changed, 58 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh
index af64f93bb2e1..8f10de0eb985 100755
--- a/tools/testing/selftests/net/fib_tests.sh
+++ b/tools/testing/selftests/net/fib_tests.sh
@@ -12,7 +12,7 @@ TESTS="unregister down carrier nexthop suppress ipv6_notify ipv4_notify \
ipv4_route_metrics ipv4_route_v6_gw rp_filter ipv4_del_addr \
ipv6_del_addr ipv4_mangle ipv6_mangle ipv4_bcast_neigh fib6_gc_test \
ipv4_mpath_list ipv6_mpath_list ipv4_mpath_balance ipv6_mpath_balance \
- ipv4_mpath_balance_preferred fib6_ra_to_static"
+ ipv4_mpath_balance_preferred fib6_ra_to_static fib6_temp_addr_renewal"
VERBOSE=0
PAUSE_ON_FAIL=no
@@ -1611,6 +1611,62 @@ fib6_ra_to_static()
cleanup &> /dev/null
}
+fib6_temp_addr_renewal() {
+ setup
+
+ echo
+ echo "Fib6 temporary address renewal test"
+ set -e
+
+ # ra6 is required for the test. (ipv6toolkit)
+ if [ ! -x "$(command -v ra6)" ]; then
+ echo "SKIP: ra6 not found."
+ set +e
+ cleanup &> /dev/null
+ return
+ fi
+
+ # Create a pair of veth devices to send a RA message from one
+ # device to another.
+ $IP link add veth1 type veth peer name veth2
+ $IP link set dev veth1 up
+ $IP link set dev veth2 up
+
+ # Make veth1 ready to receive RA messages.
+ $NS_EXEC sysctl -wq net.ipv6.conf.veth1.accept_ra=2
+ $NS_EXEC sysctl -wq net.ipv6.conf.veth1.use_tempaddr=2
+ $NS_EXEC sysctl -wq net.ipv6.conf.veth1.temp_prefered_lft=15
+ $NS_EXEC sysctl -wq net.ipv6.conf.veth1.max_desync_factor=0
+
+ # Send a RA message with a prefix from veth2.
+ $NS_EXEC ra6 -i veth2 -s fe80::1 -d ff02::1 -P 2001:12::/64\#LA\#3600\#3600 -e
+ sleep 3
+
+ # Deprecate it
+ $NS_EXEC ra6 -i veth2 -s fe80::1 -d ff02::1 -P 2001:12::/64\#LA\#3600\#0 -e
+ sleep 3
+
+ # Restore it
+ $NS_EXEC ra6 -i veth2 -s fe80::1 -d ff02::1 -P 2001:12::/64\#LA\#3600\#3600 -e
+
+ ret=1
+ for i in $(seq 1 25); do
+ sleep 1
+ num_dep="$($IP -6 addr | grep -c "temporary deprecated" || true)"
+ num_tot="$($IP -6 addr | grep -c "temporary" || true)"
+
+ if [ "$num_dep" -eq 1 ] && [ "$num_tot" -ge 2 ]; then
+ ret=0
+ break
+ fi
+ done
+ log_test "$ret" 0 "IPv6 temporary address cleanly deprecated and regenerated"
+
+ set +e
+
+ cleanup &> /dev/null
+}
+
# add route for a prefix, flushing any existing routes first
# expected to be the first step of a test
add_route()
@@ -3002,6 +3058,7 @@ do
ipv6_mpath_balance) ipv6_mpath_balance_test;;
ipv4_mpath_balance_preferred) ipv4_mpath_balance_preferred_test;;
fib6_ra_to_static) fib6_ra_to_static;;
+ fib6_temp_addr_renewal) fib6_temp_addr_renewal;;
help) echo "Test names: $TESTS"; exit 0;;
esac
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH 2/2 net v4] selftests: fib_tests: add temporary IPv6 address renewal test
2026-05-11 12:26 ` [PATCH 2/2 net v4] selftests: fib_tests: add temporary IPv6 address renewal test Fernando Fernandez Mancera
@ 2026-05-11 17:01 ` Breno Leitao
2026-05-12 0:08 ` Jakub Kicinski
0 siblings, 1 reply; 6+ messages in thread
From: Breno Leitao @ 2026-05-11 17:01 UTC (permalink / raw)
To: Fernando Fernandez Mancera
Cc: netdev, linux-kselftest, horms, pabeni, kuba, edumazet, dsahern,
davem
On Mon, May 11, 2026 at 02:26:46PM +0200, Fernando Fernandez Mancera wrote:
> Add a test to check that temporary IPv6 address is regenerated properly
> after the base prefix is deprecated and restored.
>
> Fib6 temporary address renewal test
> TEST: IPv6 temporary address cleanly deprecated and regenerated [ OK ]
>
> Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
> ---
> v2: adjusted the sleep so there is enough time for the issue to trigger,
> added cleanup at the end
> v3: no changes
> v4: no changes
> ---
> tools/testing/selftests/net/fib_tests.sh | 59 +++++++++++++++++++++++-
> 1 file changed, 58 insertions(+), 1 deletion(-)
>
> diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh
> index af64f93bb2e1..8f10de0eb985 100755
> --- a/tools/testing/selftests/net/fib_tests.sh
> +++ b/tools/testing/selftests/net/fib_tests.sh
> @@ -12,7 +12,7 @@ TESTS="unregister down carrier nexthop suppress ipv6_notify ipv4_notify \
> ipv4_route_metrics ipv4_route_v6_gw rp_filter ipv4_del_addr \
> ipv6_del_addr ipv4_mangle ipv6_mangle ipv4_bcast_neigh fib6_gc_test \
> ipv4_mpath_list ipv6_mpath_list ipv4_mpath_balance ipv6_mpath_balance \
> - ipv4_mpath_balance_preferred fib6_ra_to_static"
> + ipv4_mpath_balance_preferred fib6_ra_to_static fib6_temp_addr_renewal"
>
> VERBOSE=0
> PAUSE_ON_FAIL=no
> @@ -1611,6 +1611,62 @@ fib6_ra_to_static()
> cleanup &> /dev/null
> }
>
> +fib6_temp_addr_renewal() {
> + setup
> +
> + echo
> + echo "Fib6 temporary address renewal test"
> + set -e
> +
> + # ra6 is required for the test. (ipv6toolkit)
> + if [ ! -x "$(command -v ra6)" ]; then
> + echo "SKIP: ra6 not found."
> + set +e
> + cleanup &> /dev/null
> + return
> + fi
Should you check for ra6 before the "setup" above?
Also, kselftest convention is to exit with $ksft_skip (4) rather than just
return so that the framework reports SKIP rather than PASS with 0 tests.
A bare return here will make the test silently disappear from CI on hosts
without ipv6toolkit.
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH 2/2 net v4] selftests: fib_tests: add temporary IPv6 address renewal test
2026-05-11 17:01 ` Breno Leitao
@ 2026-05-12 0:08 ` Jakub Kicinski
2026-05-12 8:08 ` Breno Leitao
0 siblings, 1 reply; 6+ messages in thread
From: Jakub Kicinski @ 2026-05-12 0:08 UTC (permalink / raw)
To: Breno Leitao
Cc: Fernando Fernandez Mancera, netdev, linux-kselftest, horms,
pabeni, edumazet, dsahern, davem
On Mon, 11 May 2026 10:01:00 -0700 Breno Leitao wrote:
> Should you check for ra6 before the "setup" above?
Not really. AI bots love to complain about this. But "ra6 command
not found" is as good of a message as the explicit skip, honestly.
And we don't want writing tests to be too burdensome.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/2 net v4] selftests: fib_tests: add temporary IPv6 address renewal test
2026-05-12 0:08 ` Jakub Kicinski
@ 2026-05-12 8:08 ` Breno Leitao
0 siblings, 0 replies; 6+ messages in thread
From: Breno Leitao @ 2026-05-12 8:08 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Fernando Fernandez Mancera, netdev, linux-kselftest, horms,
pabeni, edumazet, dsahern, davem
On Mon, May 11, 2026 at 05:08:48PM -0700, Jakub Kicinski wrote:
> On Mon, 11 May 2026 10:01:00 -0700 Breno Leitao wrote:
> > Should you check for ra6 before the "setup" above?
>
> Not really. AI bots love to complain about this. But "ra6 command
> not found" is as good of a message as the explicit skip, honestly.
Right, but why call setup() before checking if ra6 is available?
Running setup() performs unnecessary work if the test will be skipped anyway.
It would be cleaner to:
1) Check for required tools (ra6) and skip early if missing
2) Run setup() to create namespace and configure the environment
3) Proceed with the test
Rather than:
1) Run setup() to configure namespace and network interfaces
2) Check for tool availability
3) Clean up when tools are missing
If you look at netcons_basic.sh for instance, it has the following
lines, which seems saner:
# Check for basic system dependency and exit if not found
check_for_dependencies
# Remove the namespace, interfaces and netconsole target on exit
trap cleanup EXIT
<Do the test>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2 net v4] ipv6: addrconf: fix temp address generation after prefix deprecation
2026-05-11 12:26 [PATCH 1/2 net v4] ipv6: addrconf: fix temp address generation after prefix deprecation Fernando Fernandez Mancera
2026-05-11 12:26 ` [PATCH 2/2 net v4] selftests: fib_tests: add temporary IPv6 address renewal test Fernando Fernandez Mancera
@ 2026-05-12 18:24 ` Fernando Fernandez Mancera
1 sibling, 0 replies; 6+ messages in thread
From: Fernando Fernandez Mancera @ 2026-05-12 18:24 UTC (permalink / raw)
To: netdev
Cc: linux-kselftest, horms, pabeni, kuba, edumazet, dsahern, davem,
Łukasz Stelmach, Ido Schimmel
On 5/11/26 2:26 PM, 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 after
> marking the temporary address as 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 checking if all temporary addresses for a given prefix have
> already tried to spawn a replacement when processing a new RA. If so,
> spawn a new temporary address.
>
> 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/
> Suggested-by: Ido Schimmel <idosch@nvidia.com>
> 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.
> v4: change the proposed fix completely, now we address the problem when
> doing manage_tempaddrs().
> ---
Sashiko feedback [1] is right about the DoS, that is a router that sends
multiple 0-lft RA until it exhausts all spawn attempts, leaving
temporary addresses disabled on the system.
About the leaked address, I do not think the feedback is right. If an
ifp does not have any ift, it means something went wrong most likely.
Either this address was removed manually (any RA would restore it, even
with previous implementation) or for some reason that prefix didn't get
an RA but we didn't try to generate one and we MUST do it.
I think we can cover it by avoiding to attempt create a new temporary
address for a 0-lft RA, it makes sense to me. Something like this:
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 18a6f2de30ce..6c511e9c1bf5 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2654,7 +2654,7 @@ static void manage_tempaddrs(struct inet6_dev *idev,
* We don't want that to result in creating a new temporary ip address.
*/
if ((list_empty(&idev->tempaddr_list) || all_regen) &&
- (valid_lft || prefered_lft))
+ (valid_lft && prefered_lft))
create = true;
if (create && READ_ONCE(idev->cnf.use_tempaddr) > 0) {
Any thoughts?
[1]
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260511122645.6233-2-fmancera%40suse.de
> net/ipv6/addrconf.c | 11 ++++++++---
> 1 file changed, 8 insertions(+), 3 deletions(-)
>
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 5476b6536eb7..18a6f2de30ce 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -2595,8 +2595,9 @@ static void manage_tempaddrs(struct inet6_dev *idev,
> __u32 valid_lft, __u32 prefered_lft,
> bool create, unsigned long now)
> {
> - u32 flags;
> struct inet6_ifaddr *ift;
> + bool all_regen = true;
> + u32 flags;
>
> read_lock_bh(&idev->lock);
> /* update all temporary addresses in the list */
> @@ -2637,6 +2638,8 @@ static void manage_tempaddrs(struct inet6_dev *idev,
> ift->tstamp = now;
> if (prefered_lft > 0)
> ift->flags &= ~IFA_F_DEPRECATED;
> + if (!ift->regen_count)
> + all_regen = false;
>
> spin_unlock(&ift->lock);
> if (!(flags&IFA_F_TENTATIVE))
> @@ -2644,12 +2647,14 @@ static void manage_tempaddrs(struct inet6_dev *idev,
> }
>
> /* Also create a temporary address if it's enabled but no temporary
> - * address currently exists.
> + * address currently exists or if all temporary addresses already
> + * generated an address.
> * However, we get called with valid_lft == 0, prefered_lft == 0, create == false
> * as part of cleanup (ie. deleting the mngtmpaddr).
> * We don't want that to result in creating a new temporary ip address.
> */
> - if (list_empty(&idev->tempaddr_list) && (valid_lft || prefered_lft))
> + if ((list_empty(&idev->tempaddr_list) || all_regen) &&
> + (valid_lft || prefered_lft))
> create = true;
>
> if (create && READ_ONCE(idev->cnf.use_tempaddr) > 0) {
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-05-12 18:24 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-11 12:26 [PATCH 1/2 net v4] ipv6: addrconf: fix temp address generation after prefix deprecation Fernando Fernandez Mancera
2026-05-11 12:26 ` [PATCH 2/2 net v4] selftests: fib_tests: add temporary IPv6 address renewal test Fernando Fernandez Mancera
2026-05-11 17:01 ` Breno Leitao
2026-05-12 0:08 ` Jakub Kicinski
2026-05-12 8:08 ` Breno Leitao
2026-05-12 18:24 ` [PATCH 1/2 net v4] ipv6: addrconf: fix temp address generation after prefix deprecation Fernando Fernandez Mancera
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox