All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next 0/2] ipv6: addrconf: keep lifetimes of manual addresses on RA
@ 2026-10-30  5:57 Hermin Anggawijaya
  2026-10-30  5:57 ` [PATCH net-next 1/2] ipv6: addrconf: don't update lifetimes of manual addresses from RA Hermin Anggawijaya
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Hermin Anggawijaya @ 2026-10-30  5:57 UTC (permalink / raw)
  To: dsahern, idosch, davem, edumazet, kuba, pabeni, horms
  Cc: netdev, linux-kselftest, Hermin Anggawijaya

A PIO with the autonomous flag set refreshes the lifetimes of any
address on the interface that equals the address SLAAC would form from
the prefix, including manually added ones. RFC 4862 Section 5.5.3 e)
only applies this to addresses configured by stateless
autoconfiguration, and the result is that a static, permanent address
silently becomes one that expires.

Patch 1 restricts the lifetime update to addresses carrying
IFA_F_MANAGETEMPADDR, i.e. SLAAC addresses and manual addresses added
with mngtmpaddr. Patch 2 adds a selftest to rtnetlink.py.

Hermin Anggawijaya (2):
  ipv6: addrconf: don't update lifetimes of manual addresses from RA
  selftests: net: test RA PIO handling of manually added addresses

 net/ipv6/addrconf.c                      |  9 +++
 tools/testing/selftests/net/rtnetlink.py | 79 +++++++++++++++++++++++-
 2 files changed, 87 insertions(+), 1 deletion(-)

-- 
2.56.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH net-next 1/2] ipv6: addrconf: don't update lifetimes of manual addresses from RA
  2026-10-30  5:57 [PATCH net-next 0/2] ipv6: addrconf: keep lifetimes of manual addresses on RA Hermin Anggawijaya
@ 2026-10-30  5:57 ` Hermin Anggawijaya
  2026-10-30  5:57 ` [PATCH net-next 2/2] selftests: net: test RA PIO handling of manually added addresses Hermin Anggawijaya
  2026-10-30 22:36 ` [PATCH net-next 0/2] ipv6: addrconf: keep lifetimes of manual addresses on RA Jakub Kicinski
  2 siblings, 0 replies; 4+ messages in thread
From: Hermin Anggawijaya @ 2026-10-30  5:57 UTC (permalink / raw)
  To: dsahern, idosch, davem, edumazet, kuba, pabeni, horms
  Cc: netdev, linux-kselftest, Hermin Anggawijaya

When a Router Advertisement carries a Prefix Information Option with
the autonomous flag set, addrconf_prefix_rcv_add_addr() looks up the
address that SLAAC would form from the prefix. If an address with that
value already exists on the interface, its valid and preferred
lifetimes are refreshed from the PIO, regardless of how the address
was created.

RFC 4862 Section 5.5.3 e) only requires this for "an address
configured by stateless autoconfiguration". A manually configured
address that happens to match the SLAAC-derived address, e.g. a static
EUI-64 address in an advertised prefix, has its infinite lifetimes
replaced with the finite ones from the RA. The address then expires
and is removed once the router stops advertising the prefix, even
though the administrator configured it as permanent.

This is easy to hit on a router that accepts RAs on an interface that
it also advertises a prefix on with an EUI-64 address configured from
that prefix: its own RAs turn the configured address into one with
finite lifetimes. If the RA daemon advertises the remaining lifetime
of the address (e.g. radvd with DecrementLifetimes), the advertised
lifetimes then count down on every RA.

Addresses created by SLAAC are marked with IFA_F_MANAGETEMPADDR, so
only refresh the lifetimes of addresses carrying that flag. A user
can still opt in to RA-driven lifetime management of a manually added
address by adding it with the mngtmpaddr flag.

Signed-off-by: Hermin Anggawijaya <hermin.anggawijaya@alliedtelesis.co.nz>
---
 net/ipv6/addrconf.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index c8c0dd04982d..43d80f09229b 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2762,6 +2762,15 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
 		unsigned long now;
 		u32 stored_lft;
 
+		/* RFC 4862 Section 5.5.3e only applies to addresses
+		 * configured by stateless autoconfiguration. Leave the
+		 * lifetimes of manually configured addresses alone.
+		 */
+		if (!(ifp->flags & IFA_F_MANAGETEMPADDR)) {
+			in6_ifa_put(ifp);
+			return 0;
+		}
+
 		/* update lifetime (RFC2462 5.5.3 e) */
 		spin_lock_bh(&ifp->lock);
 		now = jiffies;
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH net-next 2/2] selftests: net: test RA PIO handling of manually added addresses
  2026-10-30  5:57 [PATCH net-next 0/2] ipv6: addrconf: keep lifetimes of manual addresses on RA Hermin Anggawijaya
  2026-10-30  5:57 ` [PATCH net-next 1/2] ipv6: addrconf: don't update lifetimes of manual addresses from RA Hermin Anggawijaya
@ 2026-10-30  5:57 ` Hermin Anggawijaya
  2026-10-30 22:36 ` [PATCH net-next 0/2] ipv6: addrconf: keep lifetimes of manual addresses on RA Jakub Kicinski
  2 siblings, 0 replies; 4+ messages in thread
From: Hermin Anggawijaya @ 2026-10-30  5:57 UTC (permalink / raw)
  To: dsahern, idosch, davem, edumazet, kuba, pabeni, horms
  Cc: netdev, linux-kselftest, Hermin Anggawijaya

Add a test that sends a Router Advertisement with three autonomous
PIOs to a host that has two manually added addresses matching the
addresses SLAAC would form from the first two prefixes:

 - a plain manual address must keep its infinite lifetimes,
 - a manual address added with mngtmpaddr must have its lifetimes
   refreshed from the PIO,
 - the third prefix must create a SLAAC address with the advertised
   lifetimes.

Extend _send_ra() so it can emit PIOs with the autonomous flag set.

Signed-off-by: Hermin Anggawijaya <hermin.anggawijaya@alliedtelesis.co.nz>
---
 tools/testing/selftests/net/rtnetlink.py | 79 +++++++++++++++++++++++-
 1 file changed, 78 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/net/rtnetlink.py b/tools/testing/selftests/net/rtnetlink.py
index 5534ade056a5..a1294460e9b5 100755
--- a/tools/testing/selftests/net/rtnetlink.py
+++ b/tools/testing/selftests/net/rtnetlink.py
@@ -266,7 +266,7 @@ def ipv6_route_del_reason_expired() -> None:
             ksft_eq(attrs.get('del-reason'), 'expired')
 
 
-def _send_ra(sock, ifindex, lifetime, rio=None, pio=None):
+def _send_ra(sock, ifindex, lifetime, rio=None, pio=None, slaac_pios=()):
     """The kernel fills in the ICMPv6 checksum on raw ICMPv6 sockets."""
 
     # type, code, cksum, hop limit, flags, router lifetime,
@@ -282,6 +282,11 @@ def _send_ra(sock, ifindex, lifetime, rio=None, pio=None):
         # RFC 4861 prefix information option, on-link only (L set, A clear)
         ra += struct.pack('!BBBBIII', 3, 4, plen, 0x80, valid_lft, 0, 0)
         ra += socket.inet_pton(socket.AF_INET6, prefix)
+    for prefix, plen, valid_lft in slaac_pios:
+        # RFC 4861 prefix information option, on-link and autonomous
+        ra += struct.pack('!BBBBIII', 3, 4, plen, 0xc0, valid_lft,
+                          valid_lft, 0)
+        ra += socket.inet_pton(socket.AF_INET6, prefix)
     sock.sendto(ra, ('ff02::1', 0, 0, ifindex))
 
 
@@ -382,6 +387,77 @@ def ipv6_route_del_reason_absent() -> None:
                         "user deletion must not carry del-reason")
 
 
+def _ipv6_addr_lifetimes(ns, ifname):
+    """Map each global IPv6 address on ifname to (valid, preferred)."""
+
+    addrs = ip(f"-6 addr show dev {ifname} scope global", json=True, ns=ns)
+    return {a['local']: (a['valid_life_time'], a['preferred_life_time'])
+            for a in addrs[0]['addr_info'] if 'local' in a}
+
+
+def ipv6_ra_pio_manual_addr_lifetime() -> None:
+    """
+    A PIO must refresh the lifetimes of SLAAC addresses only (RFC 4862
+    Section 5.5.3 e). A manually added address that matches the address
+    SLAAC would form keeps its lifetimes, unless it was added with
+    mngtmpaddr.
+    """
+
+    forever = 0xffffffff
+    lft = 600
+    # EUI-64 interface identifier of 02:00:00:00:00:01
+    iid = '::ff:fe00:1'
+    manual = '2001:db8:7' + iid
+    mngtmp = '2001:db8:8' + iid
+    slaac = '2001:db8:9' + iid
+
+    with NetNS() as ns_h, NetNS() as ns_r:
+        ip(f"link add veth0 netns {ns_h} type veth peer name veth1 netns {ns_r}")
+        with NetNSEnter(str(ns_h)):
+            _write_ipv6_sysctl("conf/veth0/accept_ra", 2)
+            _write_ipv6_sysctl("conf/veth0/accept_dad", 0)
+            _write_ipv6_sysctl("conf/veth0/addr_gen_mode", 0)
+        with NetNSEnter(str(ns_r)):
+            _write_ipv6_sysctl("conf/veth1/accept_dad", 0)
+        ip("link set dev veth0 address 02:00:00:00:00:01", ns=str(ns_h))
+        ip(f"-6 addr add {manual}/64 dev veth0 nodad", ns=str(ns_h))
+        ip(f"-6 addr add {mngtmp}/64 dev veth0 nodad mngtmpaddr",
+           ns=str(ns_h))
+        ip("link set dev veth0 up", ns=str(ns_h))
+        ip("link set dev veth1 up", ns=str(ns_r))
+
+        sock, ifindex = _ra_router_sock(ns_r, "veth1")
+        # The PIOs are processed in order, so once the SLAAC address
+        # shows up the other two prefixes have been handled as well.
+        pios = [('2001:db8:7::', 64, lft), ('2001:db8:8::', 64, lft),
+                ('2001:db8:9::', 64, lft)]
+
+        lfts = {}
+        for _ in range(20):
+            try:
+                _send_ra(sock, ifindex, 0, slaac_pios=pios)
+            except OSError:
+                # router link-local address still tentative
+                pass
+            time.sleep(0.2)
+            lfts = _ipv6_addr_lifetimes(str(ns_h), "veth0")
+            if slaac in lfts:
+                break
+
+        ksft_in(slaac, lfts, "RA did not create the SLAAC address")
+        if slaac not in lfts:
+            return
+
+        ksft_eq(lfts[manual], (forever, forever),
+                "RA changed the lifetimes of a manually added address")
+        # valid_lft is not lowered below two hours (RFC 4862 5.5.3 e),
+        # so only the preferred lifetime follows the PIO
+        ksft_true(0 < lfts[mngtmp][1] <= lft,
+                  f"RA did not refresh mngtmpaddr lifetimes: {lfts[mngtmp]}")
+        ksft_true(0 < lfts[slaac][0] <= lft and 0 < lfts[slaac][1] <= lft,
+                  f"unexpected SLAAC address lifetimes: {lfts[slaac]}")
+
+
 def _insert_and_get_addrs_ipv4(test_addrs: list[str], scopes: list[str]) -> list[str]:
     with NetNS() as ns, NetNSEnter(str(ns)):
         dev_name = "dummy_dev"
@@ -497,6 +573,7 @@ def main() -> None:
               ipv6_route_del_reason_expired,
               ipv6_route_del_reason_ra_withdrawn,
               ipv6_route_del_reason_absent,
+              ipv6_ra_pio_manual_addr_lifetime,
               ipv4_verify_same_scope_addr_order, ipv4_verify_inter_scope_addr_order,
               ipv6_verify_same_scope_addr_order, ipv6_verify_inter_scope_addr_order])
     ksft_exit()
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH net-next 0/2] ipv6: addrconf: keep lifetimes of manual addresses on RA
  2026-10-30  5:57 [PATCH net-next 0/2] ipv6: addrconf: keep lifetimes of manual addresses on RA Hermin Anggawijaya
  2026-10-30  5:57 ` [PATCH net-next 1/2] ipv6: addrconf: don't update lifetimes of manual addresses from RA Hermin Anggawijaya
  2026-10-30  5:57 ` [PATCH net-next 2/2] selftests: net: test RA PIO handling of manually added addresses Hermin Anggawijaya
@ 2026-10-30 22:36 ` Jakub Kicinski
  2 siblings, 0 replies; 4+ messages in thread
From: Jakub Kicinski @ 2026-10-30 22:36 UTC (permalink / raw)
  To: Hermin Anggawijaya
  Cc: dsahern, idosch, davem, edumazet, pabeni, horms, netdev,
	linux-kselftest

On Fri, 30 Oct 2026 18:57:55 +1300 Hermin Anggawijaya wrote:
> A PIO with the autonomous flag set refreshes the lifetimes of any
> address on the interface that equals the address SLAAC would form from
> the prefix, including manually added ones. RFC 4862 Section 5.5.3 e)
> only applies this to addresses configured by stateless
> autoconfiguration, and the result is that a static, permanent address
> silently becomes one that expires.

## Form letter - net-next-closed

The merge window for Linux v7.4 has begun, and the net-next
tree is now closed for new drivers, features, code refactoring
and optimizations. We are currently accepting bug fixes only.

Please repost when net-next reopens. There is some uncertainty
about how long this merge window will last, so please check
https://netdev.bots.linux.dev/net-next.html
before reposting. net-next _may_ reopen on Nov 2nd.

See: https://www.kernel.org/doc/html/next/process/maintainer-netdev.html#development-cycle
-- 
pw-bot: defer
pv-bot: closed


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-10-30 22:36 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-30  5:57 [PATCH net-next 0/2] ipv6: addrconf: keep lifetimes of manual addresses on RA Hermin Anggawijaya
2026-10-30  5:57 ` [PATCH net-next 1/2] ipv6: addrconf: don't update lifetimes of manual addresses from RA Hermin Anggawijaya
2026-10-30  5:57 ` [PATCH net-next 2/2] selftests: net: test RA PIO handling of manually added addresses Hermin Anggawijaya
2026-10-30 22:36 ` [PATCH net-next 0/2] ipv6: addrconf: keep lifetimes of manual addresses on RA Jakub Kicinski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.