]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-radv: make prefix/route prefix lifetime can be specified with independently with...
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 24 Oct 2021 17:44:29 +0000 (02:44 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 27 Oct 2021 14:58:28 +0000 (23:58 +0900)
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.

src/libsystemd-network/radv-internal.h
src/libsystemd-network/sd-radv.c
src/libsystemd-network/test-ndisc-ra.c
src/network/networkd-radv.c
src/systemd/sd-radv.h

index 260d1a826fec753478558ef6e1e84e5bc5a4b233..df3c22c8c0bbc5b34ee7bbb600838f26353e9a1f 100644 (file)
@@ -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, ...)           \
index 00bf7db519b031afbf5f7c97e41f385f7b4028bc..4e146fe282ccae2dd7dd7104af7ea3accfc4d1c9 100644 (file)
@@ -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;
 }
index 45902862d3db6866b78d5503547b87db18aa92ca..88a32dcc58c53e02c5115543e1e373683b789f8a 100644 (file)
@@ -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);
index 08a8bbf8da626624e0f9651d23f546a39413e44f..eda44e191ee5ffeeeebec0d060e7bda73dbff8d5 100644 (file)
@@ -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;
 
index 820cbd872f365d9f42c343c29a65f19fdd7571e2..e604df1371853b29828504d14b71411f8411f5d5 100644 (file)
@@ -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);