diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-10-25 02:44:29 +0900 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-10-27 23:58:28 +0900 |
commit | 95e104e04957cd984600ecdaa4216950303031dc (patch) | |
tree | 4f60a694a7626d5ed1907cf17631ee9f3b432659 | |
parent | eef5ebec9775d15b07f5660b501cb8fc7f3446c8 (diff) | |
download | systemd-95e104e04957cd984600ecdaa4216950303031dc.tar.gz |
sd-radv: make prefix/route prefix lifetime can be specified with independently with valid_until
Previously, valid_until (or preferred_until for preferred lifetime) was
calculated from lifetime. So, when an upstream interface acquire a
dynamic prefix (e.g. through DHCPv6-PD) with long lifetime, then sd-radv
advertise the same lifetime. It may not be desired for some situations.
-rw-r--r-- | src/libsystemd-network/radv-internal.h | 13 | ||||
-rw-r--r-- | src/libsystemd-network/sd-radv.c | 169 | ||||
-rw-r--r-- | src/libsystemd-network/test-ndisc-ra.c | 31 | ||||
-rw-r--r-- | src/network/networkd-radv.c | 19 | ||||
-rw-r--r-- | src/systemd/sd-radv.h | 12 |
5 files changed, 127 insertions, 117 deletions
diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h index 260d1a826f..df3c22c8c0 100644 --- a/src/libsystemd-network/radv-internal.h +++ b/src/libsystemd-network/radv-internal.h @@ -105,8 +105,8 @@ struct sd_radv { uint8_t length; \ uint8_t prefixlen; \ uint8_t flags; \ - be32_t valid_lifetime; \ - be32_t preferred_lifetime; \ + be32_t lifetime_valid; \ + be32_t lifetime_preferred; \ uint32_t reserved; \ struct in6_addr in6_addr; \ } @@ -131,6 +131,10 @@ struct sd_radv_prefix { LIST_FIELDS(struct sd_radv_prefix, prefix); + /* These are timespans, NOT points in time. */ + usec_t lifetime_valid_usec; + usec_t lifetime_preferred_usec; + /* These are points in time specified with clock_boottime_or_monotonic(), NOT timespans. */ usec_t valid_until; usec_t preferred_until; }; @@ -155,6 +159,11 @@ struct sd_radv_route_prefix { struct radv_route_prefix_opt opt; LIST_FIELDS(struct sd_radv_route_prefix, prefix); + + /* This is a timespan, NOT a point in time. */ + usec_t lifetime_usec; + /* This is a point in time specified with clock_boottime_or_monotonic(), NOT a timespan. */ + usec_t valid_until; }; #define log_radv_errno(radv, error, fmt, ...) \ diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index 00bf7db519..4e146fe282 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -135,6 +135,18 @@ static bool router_lifetime_is_valid(usec_t lifetime_usec) { lifetime_usec <= RADV_MAX_ROUTER_LIFETIME_USEC); } +static be32_t usec_to_be32_sec(usec_t usec) { + if (usec == USEC_INFINITY) + /* UINT32_MAX is handled as infinity. */ + return htobe32(UINT32_MAX); + + if (usec >= UINT32_MAX * USEC_PER_SEC) + /* Finite but too large. Let's use the largest finite value. */ + return htobe32(UINT32_MAX - 1); + + return htobe32(usec / USEC_PER_SEC); +} + static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_usec) { sd_radv_route_prefix *rt; sd_radv_prefix *p; @@ -198,23 +210,27 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_us } LIST_FOREACH(prefix, p, ra->prefixes) { - if (p->valid_until) { + usec_t lifetime_valid_usec, lifetime_preferred_usec; - if (time_now > p->valid_until) - p->opt.valid_lifetime = 0; - else - p->opt.valid_lifetime = htobe32((p->valid_until - time_now) / USEC_PER_SEC); + lifetime_valid_usec = MIN(usec_sub_unsigned(p->valid_until, time_now), + p->lifetime_valid_usec); + + lifetime_preferred_usec = MIN3(usec_sub_unsigned(p->preferred_until, time_now), + p->lifetime_preferred_usec, + lifetime_valid_usec); + + p->opt.lifetime_valid = usec_to_be32_sec(lifetime_valid_usec); + p->opt.lifetime_preferred = usec_to_be32_sec(lifetime_preferred_usec); - if (time_now > p->preferred_until) - p->opt.preferred_lifetime = 0; - else - p->opt.preferred_lifetime = htobe32((p->preferred_until - time_now) / USEC_PER_SEC); - } iov[msg.msg_iovlen++] = IOVEC_MAKE(&p->opt, sizeof(p->opt)); } - LIST_FOREACH(prefix, rt, ra->route_prefixes) + LIST_FOREACH(prefix, rt, ra->route_prefixes) { + rt->opt.lifetime = usec_to_be32_sec(MIN(usec_sub_unsigned(rt->valid_until, time_now), + rt->lifetime_usec)); + iov[msg.msg_iovlen++] = IOVEC_MAKE(&rt->opt, sizeof(rt->opt)); + } if (ra->rdnss) iov[msg.msg_iovlen++] = IOVEC_MAKE(ra->rdnss, ra->rdnss->length * 8); @@ -555,11 +571,10 @@ _public_ int sd_radv_set_preference(sd_radv *ra, unsigned preference) { return 0; } -_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, int dynamic) { +_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { + _cleanup_free_ char *addr_p = NULL; sd_radv_prefix *cur; int r; - _cleanup_free_ char *addr_p = NULL; - usec_t time_now, valid, preferred, valid_until, preferred_until; assert_return(ra, -EINVAL); assert_return(p, -EINVAL); @@ -582,29 +597,40 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, int dynamic) { if (r == 0) continue; - if (dynamic && cur->opt.prefixlen == p->opt.prefixlen) - goto update; + if (cur->opt.prefixlen == p->opt.prefixlen) { + /* p and cur may be equivalent. First increment the counter. */ + sd_radv_prefix_ref(p); + + /* Then, remove the old entry. */ + LIST_REMOVE(prefix, ra->prefixes, cur); + sd_radv_prefix_unref(cur); + + /* Finally, add the new entry. */ + LIST_APPEND(prefix, ra->prefixes, p); + + log_radv(ra, "Updated/replaced IPv6 prefix %s (preferred: %s, valid: %s)", + strna(addr_p), + FORMAT_TIMESPAN(p->lifetime_preferred_usec, USEC_PER_SEC), + FORMAT_TIMESPAN(p->lifetime_valid_usec, USEC_PER_SEC)); + return 0; + } _cleanup_free_ char *addr_cur = NULL; (void) in6_addr_prefix_to_string(&cur->opt.in6_addr, cur->opt.prefixlen, &addr_cur); return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST), - "IPv6 prefix %s already configured, ignoring %s", - strna(addr_cur), strna(addr_p)); + "IPv6 prefix %s conflicts with %s, ignoring.", + strna(addr_p), strna(addr_cur)); } - p = sd_radv_prefix_ref(p); - + sd_radv_prefix_ref(p); LIST_APPEND(prefix, ra->prefixes, p); - ra->n_prefixes++; - if (!dynamic) { + if (ra->state == RADV_STATE_IDLE) { log_radv(ra, "Added prefix %s", strna(addr_p)); return 0; } - cur = p; - /* If RAs have already been sent, send an RA immediately to announce the newly-added prefix */ if (ra->ra_sent > 0) { r = radv_send(ra, NULL, ra->lifetime_usec); @@ -614,29 +640,6 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, int dynamic) { log_radv(ra, "Sent Router Advertisement for added prefix"); } - update: - r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return r; - - valid = be32toh(p->opt.valid_lifetime) * USEC_PER_SEC; - valid_until = usec_add(valid, time_now); - if (valid_until == USEC_INFINITY) - return -EOVERFLOW; - - preferred = be32toh(p->opt.preferred_lifetime) * USEC_PER_SEC; - preferred_until = usec_add(preferred, time_now); - if (preferred_until == USEC_INFINITY) - return -EOVERFLOW; - - cur->valid_until = valid_until; - cur->preferred_until = preferred_until; - - log_radv(ra, "Updated prefix %s preferred %s valid %s", - strna(addr_p), - FORMAT_TIMESPAN(preferred, USEC_PER_SEC), - FORMAT_TIMESPAN(valid, USEC_PER_SEC)); - return 0; } @@ -665,8 +668,7 @@ _public_ sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra, return cur; } -_public_ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p, int dynamic) { - usec_t time_now, valid, valid_until; +_public_ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) { _cleanup_free_ char *addr_p = NULL; sd_radv_route_prefix *cur; int r; @@ -688,23 +690,36 @@ _public_ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p, int if (r == 0) continue; - if (dynamic && cur->opt.prefixlen == p->opt.prefixlen) - goto update; + if (cur->opt.prefixlen == p->opt.prefixlen) { + /* p and cur may be equivalent. First increment the counter. */ + sd_radv_route_prefix_ref(p); + + /* Then, remove the old entry. */ + LIST_REMOVE(prefix, ra->route_prefixes, cur); + sd_radv_route_prefix_unref(cur); + + /* Finally, add the new entry. */ + LIST_APPEND(prefix, ra->route_prefixes, p); + + log_radv(ra, "Updated/replaced IPv6 route prefix %s (lifetime: %s)", + strna(addr_p), + FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC)); + return 0; + } _cleanup_free_ char *addr_cur = NULL; (void) in6_addr_prefix_to_string(&cur->opt.in6_addr, cur->opt.prefixlen, &addr_cur); return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST), - "IPv6 route prefix %s already configured, ignoring %s", - strna(addr_cur), strna(addr_p)); + "IPv6 route prefix %s conflicts with %s, ignoring.", + strna(addr_p), strna(addr_cur)); } - p = sd_radv_route_prefix_ref(p); - + sd_radv_route_prefix_ref(p); LIST_APPEND(prefix, ra->route_prefixes, p); ra->n_route_prefixes++; - if (!dynamic) { - log_radv(ra, "Added prefix %s", strna(addr_p)); + if (ra->state == RADV_STATE_IDLE) { + log_radv(ra, "Added route prefix %s", strna(addr_p)); return 0; } @@ -717,20 +732,6 @@ _public_ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p, int log_radv(ra, "Sent Router Advertisement for added route prefix"); } - update: - r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return r; - - valid = be32toh(p->opt.lifetime) * USEC_PER_SEC; - valid_until = usec_add(valid, time_now); - if (valid_until == USEC_INFINITY) - return -EOVERFLOW; - - log_radv(ra, "Updated route prefix %s valid %s", - strna(addr_p), - FORMAT_TIMESPAN(valid, USEC_PER_SEC)); - return 0; } @@ -836,8 +837,10 @@ _public_ int sd_radv_prefix_new(sd_radv_prefix **ret) { /* RFC 4861, Section 6.2.1 */ .opt.flags = ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO, - .opt.preferred_lifetime = htobe32(604800), - .opt.valid_lifetime = htobe32(2592000), + .lifetime_valid_usec = 30 * USEC_PER_DAY, + .lifetime_preferred_usec = 7 * USEC_PER_DAY, + .valid_until = USEC_INFINITY, + .preferred_until = USEC_INFINITY, }; *ret = p; @@ -893,20 +896,20 @@ _public_ int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p, return 0; } -_public_ int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p, - uint32_t valid_lifetime) { +_public_ int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until) { assert_return(p, -EINVAL); - p->opt.valid_lifetime = htobe32(valid_lifetime); + p->lifetime_valid_usec = lifetime_usec; + p->valid_until = valid_until; return 0; } -_public_ int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p, - uint32_t preferred_lifetime) { +_public_ int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until) { assert_return(p, -EINVAL); - p->opt.preferred_lifetime = htobe32(preferred_lifetime); + p->lifetime_preferred_usec = lifetime_usec; + p->preferred_until = valid_until; return 0; } @@ -927,7 +930,8 @@ _public_ int sd_radv_route_prefix_new(sd_radv_route_prefix **ret) { .opt.length = DIV_ROUND_UP(sizeof(p->opt), 8), .opt.prefixlen = 64, - .opt.lifetime = htobe32(604800), + .lifetime_usec = 7 * USEC_PER_DAY, + .valid_until = USEC_INFINITY, }; *ret = p; @@ -954,10 +958,11 @@ _public_ int sd_radv_route_prefix_set_prefix(sd_radv_route_prefix *p, const stru return 0; } -_public_ int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint32_t valid_lifetime) { +_public_ int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint64_t lifetime_usec, uint64_t valid_until) { assert_return(p, -EINVAL); - p->opt.lifetime = htobe32(valid_lifetime); + p->lifetime_usec = lifetime_usec; + p->valid_until = valid_until; return 0; } diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c index 45902862d3..88a32dcc58 100644 --- a/src/libsystemd-network/test-ndisc-ra.c +++ b/src/libsystemd-network/test-ndisc-ra.c @@ -123,15 +123,15 @@ static void test_radv_prefix(void) { assert_se(sd_radv_prefix_set_address_autoconfiguration(p, true) >= 0); assert_se(sd_radv_prefix_set_address_autoconfiguration(p, false) >= 0); - assert_se(sd_radv_prefix_set_valid_lifetime(NULL, true) < 0); - assert_se(sd_radv_prefix_set_valid_lifetime(p, ~0) >= 0); - assert_se(sd_radv_prefix_set_valid_lifetime(p, 42) >= 0); - assert_se(sd_radv_prefix_set_valid_lifetime(p, 0) >= 0); + assert_se(sd_radv_prefix_set_valid_lifetime(NULL, 1, 1) < 0); + assert_se(sd_radv_prefix_set_valid_lifetime(p, 0, 0) >= 0); + assert_se(sd_radv_prefix_set_valid_lifetime(p, 300 * USEC_PER_SEC, USEC_INFINITY) >= 0); + assert_se(sd_radv_prefix_set_valid_lifetime(p, 300 * USEC_PER_SEC, USEC_PER_YEAR) >= 0); - assert_se(sd_radv_prefix_set_preferred_lifetime(NULL, true) < 0); - assert_se(sd_radv_prefix_set_preferred_lifetime(p, ~0) >= 0); - assert_se(sd_radv_prefix_set_preferred_lifetime(p, 42) >= 0); - assert_se(sd_radv_prefix_set_preferred_lifetime(p, 0) >= 0); + assert_se(sd_radv_prefix_set_preferred_lifetime(NULL, 1, 1) < 0); + assert_se(sd_radv_prefix_set_preferred_lifetime(p, 0, 0) >= 0); + assert_se(sd_radv_prefix_set_preferred_lifetime(p, 300 * USEC_PER_SEC, USEC_INFINITY) >= 0); + assert_se(sd_radv_prefix_set_preferred_lifetime(p, 300 * USEC_PER_SEC, USEC_PER_YEAR) >= 0); assert_se(sd_radv_prefix_set_prefix(NULL, NULL, 0) < 0); assert_se(sd_radv_prefix_set_prefix(p, NULL, 0) < 0); @@ -325,13 +325,14 @@ static void test_ra(void) { assert_se(sd_radv_prefix_set_prefix(p, &prefix[i].address, prefix[i].prefixlen) >= 0); - if (prefix[i].valid) - assert_se(sd_radv_prefix_set_valid_lifetime(p, prefix[i].valid) >= 0); - if (prefix[i].preferred) - assert_se(sd_radv_prefix_set_preferred_lifetime(p, prefix[i].preferred) >= 0); - - assert_se((sd_radv_add_prefix(ra, p, false) >= 0) == prefix[i].successful); - assert_se(sd_radv_add_prefix(ra, p, false) < 0); + if (prefix[i].valid > 0) + assert_se(sd_radv_prefix_set_valid_lifetime(p, prefix[i].valid * USEC_PER_SEC, USEC_INFINITY) >= 0); + if (prefix[i].preferred > 0) + assert_se(sd_radv_prefix_set_preferred_lifetime(p, prefix[i].preferred * USEC_PER_SEC, USEC_INFINITY) >= 0); + + assert_se((sd_radv_add_prefix(ra, p) >= 0) == prefix[i].successful); + /* If the previous sd_radv_add_prefix() succeeds, then also the second call should also succeed. */ + assert_se((sd_radv_add_prefix(ra, p) >= 0) == prefix[i].successful); p = sd_radv_prefix_unref(p); assert_se(!p); diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 08a8bbf8da..eda44e191e 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -249,11 +249,11 @@ static int radv_set_prefix(Link *link, Prefix *prefix) { if (r < 0) return r; - r = sd_radv_prefix_set_preferred_lifetime(p, usec_to_lifetime(prefix->preferred_lifetime)); + r = sd_radv_prefix_set_preferred_lifetime(p, prefix->preferred_lifetime, USEC_INFINITY); if (r < 0) return r; - r = sd_radv_prefix_set_valid_lifetime(p, usec_to_lifetime(prefix->valid_lifetime)); + r = sd_radv_prefix_set_valid_lifetime(p, prefix->valid_lifetime, USEC_INFINITY); if (r < 0) return r; @@ -265,7 +265,7 @@ static int radv_set_prefix(Link *link, Prefix *prefix) { if (r < 0) return r; - return sd_radv_add_prefix(link->radv, p, false); + return sd_radv_add_prefix(link->radv, p); } static int radv_set_route_prefix(Link *link, RoutePrefix *prefix) { @@ -284,11 +284,11 @@ static int radv_set_route_prefix(Link *link, RoutePrefix *prefix) { if (r < 0) return r; - r = sd_radv_route_prefix_set_lifetime(p, usec_to_lifetime(prefix->lifetime)); + r = sd_radv_route_prefix_set_lifetime(p, prefix->lifetime, USEC_INFINITY); if (r < 0) return r; - return sd_radv_add_route_prefix(link->radv, p, false); + return sd_radv_add_route_prefix(link->radv, p); } static int network_get_ipv6_dns(Network *network, struct in6_addr **ret_addresses, size_t *ret_size) { @@ -654,7 +654,6 @@ int radv_add_prefix( usec_t lifetime_valid_usec) { _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL; - usec_t now_usec; int r; assert(link); @@ -662,8 +661,6 @@ int radv_add_prefix( if (!link->radv) return 0; - now_usec = now(clock_boottime_or_monotonic()); - r = sd_radv_prefix_new(&p); if (r < 0) return r; @@ -672,15 +669,15 @@ int radv_add_prefix( if (r < 0) return r; - r = sd_radv_prefix_set_preferred_lifetime(p, usec_sub_unsigned(lifetime_preferred_usec, now_usec) / USEC_PER_SEC); + r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred_usec, lifetime_preferred_usec); if (r < 0) return r; - r = sd_radv_prefix_set_valid_lifetime(p, usec_sub_unsigned(lifetime_valid_usec, now_usec) / USEC_PER_SEC); + r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid_usec, lifetime_valid_usec); if (r < 0) return r; - r = sd_radv_add_prefix(link->radv, p, true); + r = sd_radv_add_prefix(link->radv, p); if (r < 0 && r != -EEXIST) return r; diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h index 820cbd872f..e604df1371 100644 --- a/src/systemd/sd-radv.h +++ b/src/systemd/sd-radv.h @@ -57,8 +57,8 @@ int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t lifetime_usec); int sd_radv_set_managed_information(sd_radv *ra, int managed); int sd_radv_set_other_information(sd_radv *ra, int other); int sd_radv_set_preference(sd_radv *ra, unsigned preference); -int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, int dynamic); -int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p, int dynamic); +int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p); +int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p); sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra, const struct in6_addr *prefix, unsigned char prefixlen); int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime, @@ -77,17 +77,15 @@ int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_in6_addr, int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink); int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p, int address_autoconfiguration); -int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p, - uint32_t valid_lifetime); -int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p, - uint32_t preferred_lifetime); +int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until); +int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until); int sd_radv_route_prefix_new(sd_radv_route_prefix **ret); sd_radv_route_prefix *sd_radv_route_prefix_ref(sd_radv_route_prefix *ra); sd_radv_route_prefix *sd_radv_route_prefix_unref(sd_radv_route_prefix *ra); int sd_radv_route_prefix_set_prefix(sd_radv_route_prefix *p, const struct in6_addr *in6_addr, unsigned char prefixlen); -int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint32_t valid_lifetime); +int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint64_t lifetime_usec, uint64_t valid_until); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv, sd_radv_unref); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_prefix, sd_radv_prefix_unref); |