]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/libsystemd-network/sd-radv.c
tree-wide: use ASSERT_PTR more
[thirdparty/systemd.git] / src / libsystemd-network / sd-radv.c
index 2d91a406f836f8367f9b6ce842effd32cffec362..6611746a589546db97eee8910afd03a7002f0c2d 100644 (file)
@@ -26,7 +26,7 @@
 #include "string-util.h"
 #include "strv.h"
 
-_public_ int sd_radv_new(sd_radv **ret) {
+int sd_radv_new(sd_radv **ret) {
         _cleanup_(sd_radv_unrefp) sd_radv *ra = NULL;
 
         assert_return(ret, -EINVAL);
@@ -38,6 +38,7 @@ _public_ int sd_radv_new(sd_radv **ret) {
         *ra = (sd_radv) {
                 .n_ref = 1,
                 .fd = -1,
+                .lifetime_usec = RADV_DEFAULT_ROUTER_LIFETIME_USEC,
         };
 
         *ret = TAKE_PTR(ra);
@@ -45,7 +46,7 @@ _public_ int sd_radv_new(sd_radv **ret) {
         return 0;
 }
 
-_public_ int sd_radv_attach_event(sd_radv *ra, sd_event *event, int64_t priority) {
+int sd_radv_attach_event(sd_radv *ra, sd_event *event, int64_t priority) {
         int r;
 
         assert_return(ra, -EINVAL);
@@ -64,7 +65,7 @@ _public_ int sd_radv_attach_event(sd_radv *ra, sd_event *event, int64_t priority
         return 0;
 }
 
-_public_ int sd_radv_detach_event(sd_radv *ra) {
+int sd_radv_detach_event(sd_radv *ra) {
 
         assert_return(ra, -EINVAL);
 
@@ -72,16 +73,16 @@ _public_ int sd_radv_detach_event(sd_radv *ra) {
         return 0;
 }
 
-_public_ sd_event *sd_radv_get_event(sd_radv *ra) {
+sd_event *sd_radv_get_event(sd_radv *ra) {
         assert_return(ra, NULL);
 
         return ra->event;
 }
 
-_public_ int sd_radv_is_running(sd_radv *ra) {
+int sd_radv_is_running(sd_radv *ra) {
         assert_return(ra, false);
 
-        return ra->state != SD_RADV_STATE_IDLE;
+        return ra->state != RADV_STATE_IDLE;
 }
 
 static void radv_reset(sd_radv *ra) {
@@ -128,9 +129,25 @@ static sd_radv *radv_free(sd_radv *ra) {
 
 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv, sd_radv, radv_free);
 
-static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_lifetime) {
-        sd_radv_route_prefix *rt;
-        sd_radv_prefix *p;
+static bool router_lifetime_is_valid(usec_t lifetime_usec) {
+        return lifetime_usec == 0 ||
+                (lifetime_usec >= RADV_MIN_ROUTER_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) {
         struct sockaddr_in6 dst_addr = {
                 .sin6_family = AF_INET6,
                 .sin6_addr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
@@ -162,8 +179,9 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_li
         int r;
 
         assert(ra);
+        assert(router_lifetime_is_valid(lifetime_usec));
 
-        r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
+        r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now);
         if (r < 0)
                 return r;
 
@@ -173,7 +191,8 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_li
         adv.nd_ra_type = ND_ROUTER_ADVERT;
         adv.nd_ra_curhoplimit = ra->hop_limit;
         adv.nd_ra_flags_reserved = ra->flags;
-        adv.nd_ra_router_lifetime = htobe16(router_lifetime);
+        assert_cc(RADV_MAX_ROUTER_LIFETIME_USEC <= UINT16_MAX * USEC_PER_SEC);
+        adv.nd_ra_router_lifetime = htobe16(DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC));
         iov[msg.msg_iovlen++] = IOVEC_MAKE(&adv, sizeof(adv));
 
         /* MAC address is optional, either because the link does not use L2
@@ -183,29 +202,33 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_li
                 iov[msg.msg_iovlen++] = IOVEC_MAKE(&opt_mac, sizeof(opt_mac));
         }
 
-        if (ra->mtu) {
+        if (ra->mtu > 0) {
                 opt_mtu.nd_opt_mtu_mtu = htobe32(ra->mtu);
                 iov[msg.msg_iovlen++] = IOVEC_MAKE(&opt_mtu, sizeof(opt_mtu));
         }
 
         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);
@@ -220,32 +243,36 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_li
 }
 
 static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        sd_radv *ra = userdata;
-        _cleanup_free_ char *addr = NULL;
+        sd_radv *ra = ASSERT_PTR(userdata);
         struct in6_addr src;
         triple_timestamp timestamp;
         int r;
-        ssize_t buflen;
-        _cleanup_free_ char *buf = NULL;
 
         assert(s);
-        assert(ra);
         assert(ra->event);
 
-        buflen = next_datagram_size_fd(fd);
-        if (buflen < 0)
-                return (int) buflen;
+        ssize_t buflen = next_datagram_size_fd(fd);
+        if (buflen < 0) {
+                if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
+                        return 0;
+
+                log_radv_errno(ra, buflen, "Failed to determine datagram size to read, ignoring: %m");
+                return 0;
+        }
 
-        buf = new0(char, buflen);
+        _cleanup_free_ char *buf = new0(char, buflen);
         if (!buf)
                 return -ENOMEM;
 
         r = icmp6_receive(fd, buf, buflen, &src, &timestamp);
         if (r < 0) {
+                if (ERRNO_IS_TRANSIENT(r) || ERRNO_IS_DISCONNECT(r))
+                        return 0;
+
                 switch (r) {
                 case -EADDRNOTAVAIL:
-                        (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &src, &addr);
-                        log_radv(ra, "Received RS from non-link-local address %s. Ignoring", addr);
+                        log_radv(ra, "Received RS from non-link-local address %s. Ignoring",
+                                 IN6_ADDR_TO_STRING(&src));
                         break;
 
                 case -EMULTIHOP:
@@ -256,11 +283,8 @@ static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdat
                         log_radv(ra, "Received invalid source address from ICMPv6 socket. Ignoring.");
                         break;
 
-                case -EAGAIN: /* ignore spurious wakeups */
-                        break;
-
                 default:
-                        log_radv_errno(ra, r, "Unexpected error receiving from ICMPv6 socket, Ignoring: %m");
+                        log_radv_errno(ra, r, "Unexpected error receiving from ICMPv6 socket, ignoring: %m");
                         break;
                 }
 
@@ -272,65 +296,64 @@ static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdat
                 return 0;
         }
 
-        (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &src, &addr);
+        const char *addr = IN6_ADDR_TO_STRING(&src);
 
-        r = radv_send(ra, &src, ra->lifetime);
+        r = radv_send(ra, &src, ra->lifetime_usec);
         if (r < 0)
-                log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", strnull(addr));
+                log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", addr);
         else
-                log_radv(ra, "Sent solicited Router Advertisement to %s", strnull(addr));
+                log_radv(ra, "Sent solicited Router Advertisement to %s", addr);
 
         return 0;
 }
 
-static usec_t radv_compute_timeout(usec_t min, usec_t max) {
-        assert_return(min <= max, SD_RADV_DEFAULT_MIN_TIMEOUT_USEC);
-
-        /* RFC 4861: min must be no less than 3s, max must be no less than 4s */
-        min = MAX(min, 3*USEC_PER_SEC);
-        max = MAX(max, 4*USEC_PER_SEC);
-
-        return min + (random_u32() % (max - min));
-}
-
 static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+        usec_t min_timeout, max_timeout, time_now, timeout;
+        sd_radv *ra = ASSERT_PTR(userdata);
         int r;
-        sd_radv *ra = userdata;
-        usec_t min_timeout = SD_RADV_DEFAULT_MIN_TIMEOUT_USEC;
-        usec_t max_timeout = SD_RADV_DEFAULT_MAX_TIMEOUT_USEC;
-        usec_t time_now, timeout;
 
         assert(s);
-        assert(ra);
         assert(ra->event);
+        assert(router_lifetime_is_valid(ra->lifetime_usec));
 
-        r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
+        r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now);
         if (r < 0)
                 goto fail;
 
-        r = radv_send(ra, NULL, ra->lifetime);
+        r = radv_send(ra, NULL, ra->lifetime_usec);
         if (r < 0)
                 log_radv_errno(ra, r, "Unable to send Router Advertisement: %m");
 
         /* RFC 4861, Section 6.2.4, sending initial Router Advertisements */
-        if (ra->ra_sent < SD_RADV_MAX_INITIAL_RTR_ADVERTISEMENTS) {
-                max_timeout = SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC;
-                min_timeout = SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC / 3;
-        }
+        if (ra->ra_sent < RADV_MAX_INITIAL_RTR_ADVERTISEMENTS)
+                max_timeout = RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC;
+        else
+                max_timeout = RADV_DEFAULT_MAX_TIMEOUT_USEC;
 
         /* RFC 4861, Section 6.2.1, lifetime must be at least MaxRtrAdvInterval,
-           so lower the interval here */
-        if (ra->lifetime > 0 && (ra->lifetime * USEC_PER_SEC) < max_timeout) {
-                max_timeout = ra->lifetime * USEC_PER_SEC;
+         * so lower the interval here */
+        if (ra->lifetime_usec > 0)
+                max_timeout = MIN(max_timeout, ra->lifetime_usec);
+
+        if (max_timeout >= 9 * USEC_PER_SEC)
                 min_timeout = max_timeout / 3;
-        }
+        else
+                min_timeout = max_timeout * 3 / 4;
 
-        timeout = radv_compute_timeout(min_timeout, max_timeout);
+        /* RFC 4861, Section 6.2.1.
+         * MaxRtrAdvInterval MUST be no less than 4 seconds and no greater than 1800 seconds.
+         * MinRtrAdvInterval MUST be no less than 3 seconds and no greater than .75 * MaxRtrAdvInterval. */
+        assert(max_timeout >= RADV_MIN_MAX_TIMEOUT_USEC);
+        assert(max_timeout <= RADV_MAX_MAX_TIMEOUT_USEC);
+        assert(min_timeout >= RADV_MIN_MIN_TIMEOUT_USEC);
+        assert(min_timeout <= max_timeout * 3 / 4);
+
+        timeout = min_timeout + random_u64_range(max_timeout - min_timeout);
         log_radv(ra, "Next Router Advertisement in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
 
         r = event_reset_time(ra->event, &ra->timeout_event_source,
-                             clock_boottime_or_monotonic(),
-                             time_now + timeout, MSEC_PER_SEC,
+                             CLOCK_BOOTTIME,
+                             usec_add(time_now, timeout), MSEC_PER_SEC,
                              radv_timeout, ra,
                              ra->event_priority, "radv-timeout", true);
         if (r < 0)
@@ -346,13 +369,13 @@ fail:
         return 0;
 }
 
-_public_ int sd_radv_stop(sd_radv *ra) {
+int sd_radv_stop(sd_radv *ra) {
         int r;
 
         if (!ra)
                 return 0;
 
-        if (ra->state == SD_RADV_STATE_IDLE)
+        if (ra->state == RADV_STATE_IDLE)
                 return 0;
 
         log_radv(ra, "Stopping IPv6 Router Advertisement daemon");
@@ -365,23 +388,23 @@ _public_ int sd_radv_stop(sd_radv *ra) {
 
         radv_reset(ra);
         ra->fd = safe_close(ra->fd);
-        ra->state = SD_RADV_STATE_IDLE;
+        ra->state = RADV_STATE_IDLE;
 
         return 0;
 }
 
-_public_ int sd_radv_start(sd_radv *ra) {
+int sd_radv_start(sd_radv *ra) {
         int r;
 
         assert_return(ra, -EINVAL);
         assert_return(ra->event, -EINVAL);
         assert_return(ra->ifindex > 0, -EINVAL);
 
-        if (ra->state != SD_RADV_STATE_IDLE)
+        if (ra->state != RADV_STATE_IDLE)
                 return 0;
 
         r = event_reset_time(ra->event, &ra->timeout_event_source,
-                             clock_boottime_or_monotonic(),
+                             CLOCK_BOOTTIME,
                              0, 0,
                              radv_timeout, ra,
                              ra->event_priority, "radv-timeout", true);
@@ -404,7 +427,7 @@ _public_ int sd_radv_start(sd_radv *ra) {
 
         (void) sd_event_source_set_description(ra->recv_event_source, "radv-receive-message");
 
-        ra->state = SD_RADV_STATE_ADVERTISING;
+        ra->state = RADV_STATE_ADVERTISING;
 
         log_radv(ra, "Started IPv6 Router Advertisement daemon");
 
@@ -416,11 +439,11 @@ _public_ int sd_radv_start(sd_radv *ra) {
         return r;
 }
 
-_public_ int sd_radv_set_ifindex(sd_radv *ra, int ifindex) {
+int sd_radv_set_ifindex(sd_radv *ra, int ifindex) {
         assert_return(ra, -EINVAL);
         assert_return(ifindex > 0, -EINVAL);
 
-        if (ra->state != SD_RADV_STATE_IDLE)
+        if (ra->state != RADV_STATE_IDLE)
                 return -EBUSY;
 
         ra->ifindex = ifindex;
@@ -453,10 +476,10 @@ int sd_radv_get_ifname(sd_radv *ra, const char **ret) {
         return 0;
 }
 
-_public_ int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
+int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
         assert_return(ra, -EINVAL);
 
-        if (ra->state != SD_RADV_STATE_IDLE)
+        if (ra->state != RADV_STATE_IDLE)
                 return -EBUSY;
 
         if (mac_addr)
@@ -467,7 +490,7 @@ _public_ int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
         return 0;
 }
 
-_public_ int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
+int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
         assert_return(ra, -EINVAL);
         assert_return(mtu >= 1280, -EINVAL);
 
@@ -476,10 +499,10 @@ _public_ int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
         return 0;
 }
 
-_public_ int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
+int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
         assert_return(ra, -EINVAL);
 
-        if (ra->state != SD_RADV_STATE_IDLE)
+        if (ra->state != RADV_STATE_IDLE)
                 return -EBUSY;
 
         ra->hop_limit = hop_limit;
@@ -487,27 +510,30 @@ _public_ int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
         return 0;
 }
 
-_public_ int sd_radv_set_router_lifetime(sd_radv *ra, uint16_t router_lifetime) {
+int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t lifetime_usec) {
         assert_return(ra, -EINVAL);
 
-        if (ra->state != SD_RADV_STATE_IDLE)
+        if (ra->state != RADV_STATE_IDLE)
                 return -EBUSY;
 
+        if (!router_lifetime_is_valid(lifetime_usec))
+                return -EINVAL;
+
         /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the preference value MUST be set
          * to (00) by the sender..." */
-        if (router_lifetime == 0 &&
+        if (lifetime_usec == 0 &&
             (ra->flags & (0x3 << 3)) != (SD_NDISC_PREFERENCE_MEDIUM << 3))
-                return -ETIME;
+                return -EINVAL;
 
-        ra->lifetime = router_lifetime;
+        ra->lifetime_usec = lifetime_usec;
 
         return 0;
 }
 
-_public_ int sd_radv_set_managed_information(sd_radv *ra, int managed) {
+int sd_radv_set_managed_information(sd_radv *ra, int managed) {
         assert_return(ra, -EINVAL);
 
-        if (ra->state != SD_RADV_STATE_IDLE)
+        if (ra->state != RADV_STATE_IDLE)
                 return -EBUSY;
 
         SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, managed);
@@ -515,10 +541,10 @@ _public_ int sd_radv_set_managed_information(sd_radv *ra, int managed) {
         return 0;
 }
 
-_public_ int sd_radv_set_other_information(sd_radv *ra, int other) {
+int sd_radv_set_other_information(sd_radv *ra, int other) {
         assert_return(ra, -EINVAL);
 
-        if (ra->state != SD_RADV_STATE_IDLE)
+        if (ra->state != RADV_STATE_IDLE)
                 return -EBUSY;
 
         SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, other);
@@ -526,7 +552,7 @@ _public_ int sd_radv_set_other_information(sd_radv *ra, int other) {
         return 0;
 }
 
-_public_ int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
+int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
         assert_return(ra, -EINVAL);
         assert_return(IN_SET(preference,
                              SD_NDISC_PREFERENCE_LOW,
@@ -535,7 +561,7 @@ _public_ int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
 
         /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the preference value MUST be set
          * to (00) by the sender..." */
-        if (ra->lifetime == 0 && preference != SD_NDISC_PREFERENCE_MEDIUM)
+        if (ra->lifetime_usec == 0 && preference != SD_NDISC_PREFERENCE_MEDIUM)
                 return -EINVAL;
 
         ra->flags = (ra->flags & ~(0x3 << 3)) | (preference << 3);
@@ -543,27 +569,20 @@ _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) {
-        sd_radv_prefix *cur;
+int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
+        sd_radv_prefix *found = NULL;
         int r;
-        _cleanup_free_ char *addr_p = NULL;
-        usec_t time_now, valid, preferred, valid_until, preferred_until;
 
         assert_return(ra, -EINVAL);
-
-        if (!p)
-                return -EINVAL;
+        assert_return(p, -EINVAL);
 
         /* Refuse prefixes that don't have a prefix set */
         if (in6_addr_is_null(&p->opt.in6_addr))
                 return -ENOEXEC;
 
-        (void) in_addr_prefix_to_string(AF_INET6,
-                                        (const union in_addr_union*) &p->opt.in6_addr,
-                                        p->opt.prefixlen, &addr_p);
+        const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen);
 
         LIST_FOREACH(prefix, cur, ra->prefixes) {
-
                 r = in_addr_prefix_intersect(AF_INET6,
                                              (const union in_addr_union*) &cur->opt.in6_addr,
                                              cur->opt.prefixlen,
@@ -574,75 +593,70 @@ _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) {
+                        found = cur;
+                        break;
+                }
 
-                _cleanup_free_ char *addr_cur = NULL;
-                (void) in_addr_prefix_to_string(AF_INET6,
-                                                (const union in_addr_union*) &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.",
+                                      addr_p,
+                                      IN6_ADDR_PREFIX_TO_STRING(&cur->opt.in6_addr, cur->opt.prefixlen));
         }
 
-        p = sd_radv_prefix_ref(p);
+        if (found) {
+                /* p and cur may be equivalent. First increment the reference counter. */
+                sd_radv_prefix_ref(p);
 
-        LIST_APPEND(prefix, ra->prefixes, p);
+                /* Then, remove the old entry. */
+                LIST_REMOVE(prefix, ra->prefixes, found);
+                sd_radv_prefix_unref(found);
 
-        ra->n_prefixes++;
+                /* Finally, add the new entry. */
+                LIST_APPEND(prefix, ra->prefixes, p);
 
-        if (!dynamic) {
-                log_radv(ra, "Added prefix %s", strna(addr_p));
-                return 0;
-        }
+                log_radv(ra, "Updated/replaced IPv6 prefix %s (preferred: %s, valid: %s)",
+                         addr_p,
+                         FORMAT_TIMESPAN(p->lifetime_preferred_usec, USEC_PER_SEC),
+                         FORMAT_TIMESPAN(p->lifetime_valid_usec, USEC_PER_SEC));
+        } else {
+                /* The prefix is new. Let's simply add it. */
 
-        cur = p;
+                sd_radv_prefix_ref(p);
+                LIST_APPEND(prefix, ra->prefixes, p);
+                ra->n_prefixes++;
 
-        /* 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);
-                if (r < 0)
-                        log_radv_errno(ra, r, "Unable to send Router Advertisement for added prefix: %m");
-                else
-                        log_radv(ra, "Sent Router Advertisement for added prefix");
+                log_radv(ra, "Added prefix %s", addr_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;
+        if (ra->state == RADV_STATE_IDLE)
+                return 0;
 
-        cur->valid_until = valid_until;
-        cur->preferred_until = preferred_until;
+        if (ra->ra_sent == 0)
+                return 0;
 
-        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));
+        /* If RAs have already been sent, send an RA immediately to announce the newly-added prefix */
+        r = radv_send(ra, NULL, ra->lifetime_usec);
+        if (r < 0)
+                log_radv_errno(ra, r, "Unable to send Router Advertisement for added prefix %s: %m", addr_p);
+        else
+                log_radv(ra, "Sent Router Advertisement for added/updated prefix %s.", addr_p);
 
         return 0;
 }
 
-_public_ sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra,
-                                               const struct in6_addr *prefix,
-                                               unsigned char prefixlen) {
-        sd_radv_prefix *cur, *next;
+void sd_radv_remove_prefix(
+                sd_radv *ra,
+                const struct in6_addr *prefix,
+                unsigned char prefixlen) {
 
-        assert_return(ra, NULL);
-        assert_return(prefix, NULL);
+        if (!ra)
+                return;
 
-        LIST_FOREACH_SAFE(prefix, cur, next, ra->prefixes) {
+        if (!prefix)
+                return;
+
+        LIST_FOREACH(prefix, cur, ra->prefixes) {
                 if (prefixlen != cur->opt.prefixlen)
                         continue;
 
@@ -652,30 +666,20 @@ _public_ sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra,
                 LIST_REMOVE(prefix, ra->prefixes, cur);
                 ra->n_prefixes--;
                 sd_radv_prefix_unref(cur);
-
-                break;
+                return;
         }
-
-        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;
-        _cleanup_free_ char *pretty = NULL;
-        sd_radv_route_prefix *cur;
+int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) {
+        sd_radv_route_prefix *found = NULL;
         int r;
 
         assert_return(ra, -EINVAL);
+        assert_return(p, -EINVAL);
 
-        if (!p)
-                return -EINVAL;
-
-        (void) in_addr_prefix_to_string(AF_INET6,
-                                        (const union in_addr_union*) &p->opt.in6_addr,
-                                        p->opt.prefixlen, &pretty);
+        const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen);
 
         LIST_FOREACH(prefix, cur, ra->route_prefixes) {
-
                 r = in_addr_prefix_intersect(AF_INET6,
                                              (const union in_addr_union*) &cur->opt.in6_addr,
                                              cur->opt.prefixlen,
@@ -686,56 +690,64 @@ _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) {
+                        found = cur;
+                        break;
+                }
 
-                _cleanup_free_ char *addr = NULL;
-                (void) in_addr_prefix_to_string(AF_INET6,
-                                                (const union in_addr_union*) &cur->opt.in6_addr,
-                                                cur->opt.prefixlen, &addr);
                 return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST),
-                                      "IPv6 route prefix %s already configured, ignoring %s",
-                                      strna(addr), strna(pretty));
+                                      "IPv6 route prefix %s conflicts with %s, ignoring.",
+                                      addr_p,
+                                      IN6_ADDR_PREFIX_TO_STRING(&cur->opt.in6_addr, cur->opt.prefixlen));
         }
 
-        p = sd_radv_route_prefix_ref(p);
+        if (found) {
+                /* p and cur may be equivalent. First increment the reference counter. */
+                sd_radv_route_prefix_ref(p);
 
-        LIST_APPEND(prefix, ra->route_prefixes, p);
-        ra->n_route_prefixes++;
+                /* Then, remove the old entry. */
+                LIST_REMOVE(prefix, ra->route_prefixes, found);
+                sd_radv_route_prefix_unref(found);
 
-        if (!dynamic) {
-                log_radv(ra, "Added prefix %s", strna(pretty));
-                return 0;
-        }
+                /* Finally, add the new entry. */
+                LIST_APPEND(prefix, ra->route_prefixes, p);
 
-        /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */
-        if (ra->ra_sent > 0) {
-                r = radv_send(ra, NULL, ra->lifetime);
-                if (r < 0)
-                        log_radv_errno(ra, r, "Unable to send Router Advertisement for added route prefix: %m");
-                else
-                        log_radv(ra, "Sent Router Advertisement for added route prefix");
+                log_radv(ra, "Updated/replaced IPv6 route prefix %s (lifetime: %s)",
+                         strna(addr_p),
+                         FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC));
+        } else {
+                /* The route prefix is new. Let's simply add it. */
+
+                sd_radv_route_prefix_ref(p);
+                LIST_APPEND(prefix, ra->route_prefixes, p);
+                ra->n_route_prefixes++;
+
+                log_radv(ra, "Added route prefix %s", strna(addr_p));
         }
 
- update:
-        r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
-        if (r < 0)
-                return r;
+        if (ra->state == RADV_STATE_IDLE)
+                return 0;
 
-        valid = be32toh(p->opt.lifetime) * USEC_PER_SEC;
-        valid_until = usec_add(valid, time_now);
-        if (valid_until == USEC_INFINITY)
-                return -EOVERFLOW;
+        if (ra->ra_sent == 0)
+                return 0;
 
-        log_radv(ra, "Updated route prefix %s valid %s",
-                 strna(pretty),
-                 FORMAT_TIMESPAN(valid, USEC_PER_SEC));
+        /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */
+        r = radv_send(ra, NULL, ra->lifetime_usec);
+        if (r < 0)
+                log_radv_errno(ra, r, "Unable to send Router Advertisement for added route prefix %s: %m",
+                               strna(addr_p));
+        else
+                log_radv(ra, "Sent Router Advertisement for added route prefix %s.", strna(addr_p));
 
         return 0;
 }
 
-_public_ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime,
-                               const struct in6_addr *dns, size_t n_dns) {
+int sd_radv_set_rdnss(
+                sd_radv *ra,
+                uint32_t lifetime,
+                const struct in6_addr *dns,
+                size_t n_dns) {
+
         _cleanup_free_ struct sd_radv_opt_dns *opt_rdnss = NULL;
         size_t len;
 
@@ -755,7 +767,7 @@ _public_ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime,
         if (!opt_rdnss)
                 return -ENOMEM;
 
-        opt_rdnss->type = SD_RADV_OPT_RDNSS;
+        opt_rdnss->type = RADV_OPT_RDNSS;
         opt_rdnss->length = len / 8;
         opt_rdnss->lifetime = htobe32(lifetime);
 
@@ -768,11 +780,13 @@ _public_ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime,
         return 0;
 }
 
-_public_ int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime,
-                               char **search_list) {
+int sd_radv_set_dnssl(
+                sd_radv *ra,
+                uint32_t lifetime,
+                char **search_list) {
+
         _cleanup_free_ struct sd_radv_opt_dns *opt_dnssl = NULL;
         size_t len = 0;
-        char **s;
         uint8_t *p;
 
         assert_return(ra, -EINVAL);
@@ -791,7 +805,7 @@ _public_ int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime,
         if (!opt_dnssl)
                 return -ENOMEM;
 
-        opt_dnssl->type = SD_RADV_OPT_DNSSL;
+        opt_dnssl->type = RADV_OPT_DNSSL;
         opt_dnssl->length = len / 8;
         opt_dnssl->lifetime = htobe32(lifetime);
 
@@ -817,7 +831,7 @@ _public_ int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime,
         return 0;
 }
 
-_public_ int sd_radv_prefix_new(sd_radv_prefix **ret) {
+int sd_radv_prefix_new(sd_radv_prefix **ret) {
         sd_radv_prefix *p;
 
         assert_return(ret, -EINVAL);
@@ -836,8 +850,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 = RADV_DEFAULT_VALID_LIFETIME_USEC,
+                .lifetime_preferred_usec = RADV_DEFAULT_PREFERRED_LIFETIME_USEC,
+                .valid_until = USEC_INFINITY,
+                .preferred_until = USEC_INFINITY,
         };
 
         *ret = p;
@@ -846,8 +862,11 @@ _public_ int sd_radv_prefix_new(sd_radv_prefix **ret) {
 
 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_prefix, sd_radv_prefix, mfree);
 
-_public_ int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr *in6_addr,
-                                       unsigned char prefixlen) {
+int sd_radv_prefix_set_prefix(
+                sd_radv_prefix *p,
+                const struct in6_addr *in6_addr,
+                unsigned char prefixlen) {
+
         assert_return(p, -EINVAL);
         assert_return(in6_addr, -EINVAL);
 
@@ -864,8 +883,11 @@ _public_ int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr
         return 0;
 }
 
-_public_ int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_in6_addr,
-                                       unsigned char *ret_prefixlen) {
+int sd_radv_prefix_get_prefix(
+                sd_radv_prefix *p,
+                struct in6_addr *ret_in6_addr,
+                unsigned char *ret_prefixlen) {
+
         assert_return(p, -EINVAL);
         assert_return(ret_in6_addr, -EINVAL);
         assert_return(ret_prefixlen, -EINVAL);
@@ -876,7 +898,7 @@ _public_ int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_i
         return 0;
 }
 
-_public_ int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) {
+int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) {
         assert_return(p, -EINVAL);
 
         SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_ONLINK, onlink);
@@ -884,8 +906,7 @@ _public_ int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) {
         return 0;
 }
 
-_public_ int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p,
-                                                          int address_autoconfiguration) {
+int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p, int address_autoconfiguration) {
         assert_return(p, -EINVAL);
 
         SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, address_autoconfiguration);
@@ -893,25 +914,25 @@ _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) {
+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) {
+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;
 }
 
-_public_ int sd_radv_route_prefix_new(sd_radv_route_prefix **ret) {
+int sd_radv_route_prefix_new(sd_radv_route_prefix **ret) {
         sd_radv_route_prefix *p;
 
         assert_return(ret, -EINVAL);
@@ -923,11 +944,12 @@ _public_ int sd_radv_route_prefix_new(sd_radv_route_prefix **ret) {
         *p = (sd_radv_route_prefix) {
                 .n_ref = 1,
 
-                .opt.type = SD_RADV_OPT_ROUTE_INFORMATION,
+                .opt.type = RADV_OPT_ROUTE_INFORMATION,
                 .opt.length = DIV_ROUND_UP(sizeof(p->opt), 8),
                 .opt.prefixlen = 64,
 
-                .opt.lifetime = htobe32(604800),
+                .lifetime_usec = RADV_DEFAULT_VALID_LIFETIME_USEC,
+                .valid_until = USEC_INFINITY,
         };
 
         *ret = p;
@@ -936,8 +958,11 @@ _public_ int sd_radv_route_prefix_new(sd_radv_route_prefix **ret) {
 
 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_route_prefix, sd_radv_route_prefix, mfree);
 
-_public_ 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_prefix(
+                sd_radv_route_prefix *p,
+                const struct in6_addr *in6_addr,
+                unsigned char prefixlen) {
+
         assert_return(p, -EINVAL);
         assert_return(in6_addr, -EINVAL);
 
@@ -954,10 +979,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) {
+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;
 }