diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h index 441939b717..837e7f2603 100644 --- a/src/libsystemd-network/radv-internal.h +++ b/src/libsystemd-network/radv-internal.h @@ -93,6 +93,9 @@ struct sd_radv_prefix { } _packed_ opt; LIST_FIELDS(struct sd_radv_prefix, prefix); + + usec_t valid_until; + usec_t preferred_until; }; #define log_radv_full(level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "RADV: " fmt, ##__VA_ARGS__) diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index 46704acdef..169278d5e3 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -169,6 +169,12 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, .msg_namelen = sizeof(dst_addr), .msg_iov = iov, }; + usec_t time_now; + int r; + + r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + return r; if (dst && !in_addr_is_null(AF_INET6, (union in_addr_union*) dst)) dst_addr.sin6_addr = *dst; @@ -198,6 +204,18 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, } LIST_FOREACH(prefix, p, ra->prefixes) { + if (p->valid_until) { + + 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); + + 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].iov_base = &p->opt; iov[msg.msg_iovlen].iov_len = sizeof(p->opt); msg.msg_iovlen++; @@ -518,9 +536,13 @@ _public_ int sd_radv_set_preference(sd_radv *ra, unsigned preference) { return r; } -_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { +_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, bool dynamic) { sd_radv_prefix *cur; + int r; _cleanup_free_ char *addr_p = NULL; + char time_string_preferred[FORMAT_TIMESPAN_MAX]; + char time_string_valid[FORMAT_TIMESPAN_MAX]; + usec_t time_now, valid, preferred, valid_until, preferred_until; assert_return(ra, -EINVAL); @@ -528,7 +550,6 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { return -EINVAL; LIST_FOREACH(prefix, cur, ra->prefixes) { - int r; r = in_addr_prefix_intersect(AF_INET6, (union in_addr_union*) &cur->opt.in6_addr, @@ -538,13 +559,16 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { if (r > 0) { _cleanup_free_ char *addr_cur = NULL; - (void) in_addr_to_string(AF_INET6, - (union in_addr_union*) &cur->opt.in6_addr, - &addr_cur); (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &p->opt.in6_addr, &addr_p); + if (dynamic && cur->opt.prefixlen == p->opt.prefixlen) + goto update; + + (void) in_addr_to_string(AF_INET6, + (union in_addr_union*) &cur->opt.in6_addr, + &addr_cur); log_radv("IPv6 prefix %s/%u already configured, ignoring %s/%u", addr_cur, cur->opt.prefixlen, addr_p, p->opt.prefixlen); @@ -560,7 +584,39 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { ra->n_prefixes++; (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &p->opt.in6_addr, &addr_p); - log_radv("Added prefix %s/%d", addr_p, p->opt.prefixlen); + + if (!dynamic) { + log_radv("Added prefix %s/%d", addr_p, p->opt.prefixlen); + return 0; + } + + cur = p; + + 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("%s prefix %s/%u preferred %s valid %s", + cur? "Updated": "Added", + addr_p, p->opt.prefixlen, + format_timespan(time_string_preferred, FORMAT_TIMESPAN_MAX, + preferred, USEC_PER_SEC), + format_timespan(time_string_valid, FORMAT_TIMESPAN_MAX, + valid, USEC_PER_SEC)); return 0; } diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c index c1a8d5a00d..1fc8ca9eba 100644 --- a/src/libsystemd-network/test-ndisc-ra.c +++ b/src/libsystemd-network/test-ndisc-ra.c @@ -342,8 +342,8 @@ static void test_ra(void) { 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) >= 0) == prefix[i].succesful); - assert_se(sd_radv_add_prefix(ra, p) < 0); + assert_se((sd_radv_add_prefix(ra, p, false) >= 0) == prefix[i].succesful); + assert_se(sd_radv_add_prefix(ra, p, false) < 0); p = sd_radv_prefix_unref(p); assert_se(!p); diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index a7e66f575b..5fdab89f2d 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -502,7 +502,7 @@ int radv_configure(Link *link) { RADV_PREFIX_DELEGATION_STATIC, RADV_PREFIX_DELEGATION_BOTH)) { LIST_FOREACH(prefixes, p, link->network->static_prefixes) { - r = sd_radv_add_prefix(link->radv, p->radv_prefix); + r = sd_radv_add_prefix(link->radv, p->radv_prefix, false); if (r != -EEXIST && r < 0) return r; } diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h index 94d5e71e8a..393c977bf1 100644 --- a/src/systemd/sd-radv.h +++ b/src/systemd/sd-radv.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "sd-ndisc.h" @@ -62,7 +63,7 @@ int sd_radv_set_router_lifetime(sd_radv *ra, uint32_t router_lifetime); 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 sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, bool dynamic); int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime, const struct in6_addr *dns, size_t n_dns); int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime, char **search_list);