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;
}
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);
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);
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);
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;
}
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;
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;
}
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;
}
/* 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;
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;
}
.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;
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;
}
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);
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);
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,
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);