summaryrefslogtreecommitdiffstats
path: root/src/network/networkd-ndisc.c
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2024-04-11 07:02:21 +0200
committerYu Watanabe <watanabe.yu+github@gmail.com>2024-04-11 08:56:01 +0200
commite42a74803a5a9a8acf0d630dd775736610af4600 (patch)
tree0cd4d4e39528b7744696260df804c3b84e52de13 /src/network/networkd-ndisc.c
parentnetwork/ndisc: set provider address in caller (diff)
downloadsystemd-e42a74803a5a9a8acf0d630dd775736610af4600.tar.xz
systemd-e42a74803a5a9a8acf0d630dd775736610af4600.zip
network/ndisc: fix updating valid lifetime of configured address
This makes the logic mostly follows RFC 4862 section 5.5.3 (e), as draft-ietf-6man-slaac-renum-07 is deprecated. Fixes #31449.
Diffstat (limited to 'src/network/networkd-ndisc.c')
-rw-r--r--src/network/networkd-ndisc.c93
1 files changed, 72 insertions, 21 deletions
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index 47d973ad82..f0c58e8842 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -1073,6 +1073,64 @@ static int ndisc_router_process_mtu(Link *link, sd_ndisc_router *rt) {
return 0;
}
+static int ndisc_address_set_lifetime(Address *address, Link *link, sd_ndisc_router *rt) {
+ Address *existing;
+ usec_t t;
+ int r;
+
+ assert(address);
+ assert(link);
+ assert(rt);
+
+ /* This is mostly based on RFC 4862 section 5.5.3 (e). However, the definition of 'RemainingLifetime'
+ * is ambigous, and there is no clear explanation when the address is not assigned yet. If we assume
+ * that 'RemainingLifetime' is zero in that case, then IPv6 Core Conformance test [v6LC.3.2.5 Part C]
+ * fails. So, in such case, we skip the conditions about 'RemainingLifetime'. */
+
+ r = sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_BOOTTIME, &address->lifetime_valid_usec);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(rt, CLOCK_BOOTTIME, &address->lifetime_preferred_usec);
+ if (r < 0)
+ return r;
+
+ /* RFC 4862 section 5.5.3 (e)
+ * 1. If the received Valid Lifetime is greater than 2 hours or greater than RemainingLifetime,
+ * set the valid lifetime of the corresponding address to the advertised Valid Lifetime. */
+ r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &t);
+ if (r < 0)
+ return r;
+
+ if (t > 2 * USEC_PER_HOUR)
+ return 0;
+
+ r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, &t);
+ if (r < 0)
+ return r;
+
+ if (address_get(link, address, &existing) >= 0 && existing->source == NETWORK_CONFIG_SOURCE_NDISC) {
+ if (address->lifetime_valid_usec > existing->lifetime_valid_usec)
+ return 0;
+
+ /* 2. If RemainingLifetime is less than or equal to 2 hours, ignore the Prefix Information
+ * option with regards to the valid lifetime, unless the Router Advertisement from which
+ * this option was obtained has been authenticated (e.g., via Secure Neighbor Discovery
+ * [RFC3971]). If the Router Advertisement was authenticated, the valid lifetime of the
+ * corresponding address should be set to the Valid Lifetime in the received option.
+ *
+ * Currently, authentication is not supported. So check the lifetime of the existing address. */
+ if (existing->lifetime_valid_usec <= usec_add(t, 2 * USEC_PER_HOUR)) {
+ address->lifetime_valid_usec = existing->lifetime_valid_usec;
+ return 0;
+ }
+ }
+
+ /* 3. Otherwise, reset the valid lifetime of the corresponding address to 2 hours. */
+ address->lifetime_valid_usec = usec_add(t, 2 * USEC_PER_HOUR);
+ return 0;
+}
+
static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
usec_t lifetime_valid_usec, lifetime_preferred_usec;
struct in6_addr prefix, router;
@@ -1105,15 +1163,17 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
return 0;
}
- r = sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_valid_usec);
+ r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid_usec);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to get prefix valid lifetime: %m");
- r = sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_preferred_usec);
+ r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred_usec);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to get prefix preferred lifetime: %m");
- /* The preferred lifetime is never greater than the valid lifetime */
+ /* RFC 4862 section 5.5.3 (c)
+ * If the preferred lifetime is greater than the valid lifetime, silently ignore the Prefix
+ * Information option. */
if (lifetime_preferred_usec > lifetime_valid_usec)
return 0;
@@ -1136,26 +1196,17 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
address->in_addr.in6 = *a;
address->prefixlen = prefixlen;
address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
- address->lifetime_valid_usec = lifetime_valid_usec;
- address->lifetime_preferred_usec = lifetime_preferred_usec;
address->token = ipv6_token_ref(token);
- /* draft-ietf-6man-slaac-renum-07 section 4.2
- * https://datatracker.ietf.org/doc/html/draft-ietf-6man-slaac-renum-07#section-4.2
- *
- * If the advertised prefix is equal to the prefix of an address configured by stateless
- * autoconfiguration in the list, the valid lifetime and the preferred lifetime of the
- * address should be updated by processing the Valid Lifetime and the Preferred Lifetime
- * (respectively) in the received advertisement. */
- if (lifetime_valid_usec == 0) {
- r = address_remove_and_cancel(address, link);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not remove SLAAC address: %m");
- } else {
- r = ndisc_request_address(address, link);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not request SLAAC address: %m");
- }
+ r = ndisc_address_set_lifetime(address, link, rt);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to set lifetime of SLAAC address: %m");
+
+ assert(address->lifetime_preferred_usec <= address->lifetime_valid_usec);
+
+ r = ndisc_request_address(address, link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not request SLAAC address: %m");
}
return 0;