leasetime string 12h DHCPv4 address leasetime
start integer 100 DHCPv4 pool start
limit integer 150 DHCPv4 pool size
-preferred_lifetime string 7d Value for the preferred lifetime
+max_preferred_lifetime string 45m Upper limit for the preferred lifetime
+ for a prefix
+max_valid_lifetime string 90m Upper limit for the valid lifetime
for a prefix
ra_default integer 0 Override default route
0: default, 1: ignore no public address, 2: ignore all
sending unsolicited RA
ra_mininterval integer 200 Minimum time allowed between
sending unsolicited RA
-ra_lifetime integer 1800 Value to be placed in Router
- Lifetime field of RA
-ra_useleasetime bool 0 Use configured leasetime as
- limit for the preferred and
- valid lifetime of a prefix
+ra_lifetime integer 2700 Value to be placed in Router
+ Lifetime field of RA. Not recommended to be
+ more than 2700 (RFC9096).
ra_reachabletime integer 0 Reachable Time in milliseconds to be
advertised in RA messages
ra_retranstime integer 0 Retransmit Time in milliseconds to be
IFACE_ATTR_RA_MININTERVAL,
IFACE_ATTR_RA_MAXINTERVAL,
IFACE_ATTR_RA_LIFETIME,
- IFACE_ATTR_RA_USELEASETIME,
IFACE_ATTR_RA_REACHABLETIME,
IFACE_ATTR_RA_RETRANSTIME,
IFACE_ATTR_RA_HOPLIMIT,
IFACE_ATTR_NDPROXY_ROUTING,
IFACE_ATTR_NDPROXY_SLAVE,
IFACE_ATTR_PREFIX_FILTER,
- IFACE_ATTR_PREFERRED_LIFETIME,
+ IFACE_ATTR_MAX_PREFERRED_LIFETIME,
+ IFACE_ATTR_MAX_VALID_LIFETIME,
IFACE_ATTR_NTP,
IFACE_ATTR_MAX
};
[IFACE_ATTR_RA_MININTERVAL] = { .name = "ra_mininterval", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_RA_MAXINTERVAL] = { .name = "ra_maxinterval", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_RA_LIFETIME] = { .name = "ra_lifetime", .type = BLOBMSG_TYPE_INT32 },
- [IFACE_ATTR_RA_USELEASETIME] = { .name = "ra_useleasetime", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_RA_REACHABLETIME] = { .name = "ra_reachabletime", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_RA_RETRANSTIME] = { .name = "ra_retranstime", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_RA_HOPLIMIT] = { .name = "ra_hoplimit", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_NDPROXY_ROUTING] = { .name = "ndproxy_routing", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_NDPROXY_SLAVE] = { .name = "ndproxy_slave", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_PREFIX_FILTER] = { .name = "prefix_filter", .type = BLOBMSG_TYPE_STRING },
- [IFACE_ATTR_PREFERRED_LIFETIME] = { .name = "preferred_lifetime", .type = BLOBMSG_TYPE_STRING },
+ [IFACE_ATTR_MAX_PREFERRED_LIFETIME] = { .name = "max_preferred_lifetime", .type = BLOBMSG_TYPE_STRING },
+ [IFACE_ATTR_MAX_VALID_LIFETIME] = { .name = "max_valid_lifetime", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_NTP] = { .name = "ntp", .type = BLOBMSG_TYPE_ARRAY },
};
iface->ndp = MODE_DISABLED;
iface->learn_routes = 1;
iface->dhcp_leasetime = 43200;
- iface->preferred_lifetime = 604800; /* rfc4861#section-6.2.1: AdvPreferredLifetime 7 days */
+ iface->max_preferred_lifetime = ND_PREFERRED_LIMIT;
+ iface->max_valid_lifetime = ND_VALID_LIMIT;
iface->dhcpv4_start.s_addr = htonl(START_DEFAULT);
iface->dhcpv4_end.s_addr = htonl(START_DEFAULT + LIMIT_DEFAULT - 1);
iface->dhcpv6_assignall = true;
}
- if ((c = tb[IFACE_ATTR_PREFERRED_LIFETIME])) {
+ if ((c = tb[IFACE_ATTR_MAX_PREFERRED_LIFETIME])) {
double time = parse_leasetime(c);
- if (time >= 0)
- iface->preferred_lifetime = time;
- else
+ if (time >= 0) {
+ iface->max_preferred_lifetime = time;
+ } else {
syslog(LOG_ERR, "Invalid %s value configured for interface '%s'",
- iface_attrs[IFACE_ATTR_PREFERRED_LIFETIME].name, iface->name);
+ iface_attrs[IFACE_ATTR_MAX_PREFERRED_LIFETIME].name, iface->name);
+ }
+ }
+ if ((c = tb[IFACE_ATTR_MAX_VALID_LIFETIME])) {
+ double time = parse_leasetime(c);
+
+ if (time >= 0) {
+ iface->max_valid_lifetime = time;
+ } else {
+ syslog(LOG_ERR, "Invalid %s value configured for interface '%s'",
+ iface_attrs[IFACE_ATTR_MAX_VALID_LIFETIME].name, iface->name);
+ }
}
if ((c = tb[IFACE_ATTR_START])) {
if ((c = tb[IFACE_ATTR_RA_LIFETIME]))
iface->ra_lifetime = blobmsg_get_u32(c);
- if ((c = tb[IFACE_ATTR_RA_USELEASETIME]))
- iface->ra_useleasetime = blobmsg_get_bool(c);
-
if ((c = tb[IFACE_ATTR_RA_DNS]))
iface->ra_dns = blobmsg_get_bool(c);
static inline bool valid_addr(const struct odhcpd_ipaddr *addr, time_t now)
{
- return (addr->prefix <= 96 && addr->preferred_lt > (uint32_t)now);
+ return (addr->prefix <= 96 && addr->valid_lt > (uint32_t)now && addr->preferred_lt > (uint32_t)now);
}
static size_t get_preferred_addr(const struct odhcpd_ipaddr *addrs, const size_t addrlen)
}
if (a) {
- uint32_t leasetime, preferred_lt;
+ uint32_t leasetime;
if (a->leasetime) {
leasetime = a->leasetime;
- preferred_lt = a->leasetime;
} else {
leasetime = iface->dhcp_leasetime;
- preferred_lt = iface->preferred_lifetime;
}
- uint32_t valid_lt = leasetime;
+ uint32_t floor_preferred_lifetime, floor_valid_lifetime; /* For calculating T1 / T2 */
+
+ if (iface->max_preferred_lifetime && iface->max_preferred_lifetime < leasetime) {
+ floor_preferred_lifetime = iface->max_preferred_lifetime;
+ } else {
+ floor_preferred_lifetime = leasetime;
+ }
+
+ if (iface->max_valid_lifetime && iface->max_valid_lifetime < leasetime) {
+ floor_valid_lifetime = iface->max_valid_lifetime;
+ } else {
+ floor_valid_lifetime = leasetime;
+ }
struct odhcpd_ipaddr *addrs = (a->managed) ? a->managed : iface->addr6;
size_t addrlen = (a->managed) ? (size_t)a->managed_size : iface->addr6_len;
prefix_preferred_lt = addrs[i].preferred_lt;
prefix_valid_lt = addrs[i].valid_lt;
- if (prefix_preferred_lt != UINT32_MAX)
+ if (prefix_preferred_lt != UINT32_MAX) {
prefix_preferred_lt -= now;
- if (prefix_preferred_lt > preferred_lt)
- prefix_preferred_lt = preferred_lt;
+ if (iface->max_preferred_lifetime && prefix_preferred_lt > iface->max_preferred_lifetime)
+ prefix_preferred_lt = iface->max_preferred_lifetime;
+ }
- if (prefix_valid_lt != UINT32_MAX)
+ if (prefix_valid_lt != UINT32_MAX) {
prefix_valid_lt -= now;
+ if (iface->max_valid_lifetime && prefix_valid_lt > iface->max_valid_lifetime)
+ prefix_valid_lt = iface->max_valid_lifetime;
+ }
+
if (prefix_valid_lt > leasetime)
prefix_valid_lt = leasetime;
/* Calculate T1 / T2 based on non-deprecated addresses */
if (prefix_preferred_lt > 0) {
- if (prefix_preferred_lt < preferred_lt)
- preferred_lt = prefix_preferred_lt;
+ if (floor_preferred_lifetime > prefix_preferred_lt)
+ floor_preferred_lifetime = prefix_preferred_lt;
- if (prefix_valid_lt < valid_lt)
- valid_lt = prefix_valid_lt;
+ if (floor_valid_lifetime > prefix_valid_lt)
+ floor_valid_lifetime = prefix_valid_lt;
}
}
if (!INFINITE_VALID(a->valid_until))
/* UINT32_MAX is RFC defined as infinite lease-time */
- a->valid_until = (valid_lt == UINT32_MAX) ? 0 : valid_lt + now;
+ a->valid_until = (floor_valid_lifetime == UINT32_MAX) ? 0 : floor_valid_lifetime + now;
if (!INFINITE_VALID(a->preferred_until))
/* UINT32_MAX is RFC defined as infinite lease-time */
- a->preferred_until = (preferred_lt == UINT32_MAX) ? 0 : preferred_lt + now;
+ a->preferred_until = (floor_preferred_lifetime == UINT32_MAX) ? 0 : floor_preferred_lifetime + now;
- o_ia.t1 = htonl((preferred_lt == UINT32_MAX) ? preferred_lt : preferred_lt * 5 / 10);
- o_ia.t2 = htonl((preferred_lt == UINT32_MAX) ? preferred_lt : preferred_lt * 8 / 10);
+ o_ia.t1 = htonl((floor_preferred_lifetime == UINT32_MAX) ? floor_preferred_lifetime : floor_preferred_lifetime * 5 / 10);
+ o_ia.t2 = htonl((floor_preferred_lifetime == UINT32_MAX) ? floor_preferred_lifetime : floor_preferred_lifetime * 8 / 10);
if (!o_ia.t1)
o_ia.t1 = htonl(1);
// RFC 8781 defines PREF64 option
#define ND_OPT_PREF64 38
+// RFC9096 defines recommended option lifetimes configuration values
+#define ND_PREFERRED_LIMIT 2700
+#define ND_VALID_LIMIT 5400
+
// RFC 9463 - Discovery of Network-designated Resolvers (DNR)
#define ND_OPT_DNR 144
bool ra_slaac;
bool ra_not_onlink;
bool ra_advrouter;
- bool ra_useleasetime;
bool ra_dns;
uint8_t pref64_length;
uint8_t pref64_plc;
uint32_t ra_retranstime;
uint32_t ra_hoplimit;
int ra_mtu;
- uint32_t preferred_lifetime;
+ uint32_t max_preferred_lifetime;
+ uint32_t max_valid_lifetime;
// DHCP
uint32_t dhcp_leasetime;
*maxival = iface->ra_maxinterval;
- /*
- * rfc4861#section-6.2.1 : AdvDefaultLifetime Default: 3 * MaxRtrAdvInterval
- * therefore max interval shall be no greater than 1/3 of the lowest valid
- * lease time of all known prefixes.
- */
- if (*maxival > lowest_found_lifetime/3)
- *maxival = lowest_found_lifetime/3;
+ if (*maxival > lowest_found_lifetime)
+ *maxival = lowest_found_lifetime;
if (*maxival > MaxRtrAdvInterval)
*maxival = MaxRtrAdvInterval;
static uint32_t calc_ra_lifetime(struct interface *iface, uint32_t maxival)
{
- uint32_t lifetime = 3*maxival;
+ uint32_t lifetime = iface->max_preferred_lifetime;
if (iface->ra_lifetime >= 0) {
lifetime = iface->ra_lifetime;
- if (lifetime > 0 && lifetime < maxival)
- lifetime = maxival;
- else if (lifetime > 9000)
- lifetime = 9000;
}
+ if (lifetime > 0 && lifetime < maxival)
+ lifetime = maxival;
+ else if (lifetime > 9000)
+ lifetime = 9000;
+
return lifetime;
}
size_t valid_addr_cnt = 0, invalid_addr_cnt = 0;
/*
* lowest_found_lifetime stores the lowest lifetime of all prefixes;
- * necessary to find shortest adv interval necessary
+ * necessary to find longest adv interval necessary
* for shortest lived prefix
*/
- uint32_t lowest_found_lifetime = UINT32_MAX, maxival, lifetime;
+ uint32_t lowest_found_lifetime = UINT32_MAX, highest_found_lifetime = 0, maxival, ra_lifetime;
int msecs, mtu = iface->ra_mtu, hlim = iface->ra_hoplimit;
bool default_route = false;
bool valid_prefix = false;
if (addr->preferred_lt > (uint32_t)now) {
preferred_lt = TIME_LEFT(addr->preferred_lt, now);
- if (preferred_lt > iface->preferred_lifetime) {
- /* set to possibly user mandated preferred_lt */
- preferred_lt = iface->preferred_lifetime;
+ if (iface->max_preferred_lifetime && preferred_lt > iface->max_preferred_lifetime) {
+ preferred_lt = iface->max_preferred_lifetime;
}
}
if (addr->valid_lt > (uint32_t)now) {
valid_lt = TIME_LEFT(addr->valid_lt, now);
- if (iface->ra_useleasetime && valid_lt > iface->dhcp_leasetime)
- valid_lt = iface->dhcp_leasetime;
+ if (iface->max_valid_lifetime && valid_lt > iface->max_valid_lifetime)
+ valid_lt = iface->max_valid_lifetime;
}
if (preferred_lt > valid_lt) {
if ((!IN6_IS_ADDR_ULA(&addr->addr.in6) || iface->default_router) && valid_lt)
valid_prefix = true;
+ if (!IN6_IS_ADDR_ULA(&addr->addr.in6) && valid_lt) {
+ if (highest_found_lifetime < valid_lt)
+ highest_found_lifetime = valid_lt;
+ }
+
odhcpd_bmemcpy(&p->nd_opt_pi_prefix, &addr->addr.in6,
(iface->ra_advrouter) ? 128 : addr->prefix);
p->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
/* Calculate periodic transmit */
msecs = calc_adv_interval(iface, lowest_found_lifetime, &maxival);
- lifetime = calc_ra_lifetime(iface, maxival);
+ ra_lifetime = calc_ra_lifetime(iface, maxival);
+ if (!highest_found_lifetime)
+ highest_found_lifetime = ra_lifetime;
if (!iface->have_link_local) {
syslog(LOG_NOTICE, "Skip sending a RA on %s as no link local address is available", iface->name);
}
if (default_route && valid_prefix) {
- adv.h.nd_ra_router_lifetime = htons(lifetime < UINT16_MAX ? lifetime : UINT16_MAX);
+ adv.h.nd_ra_router_lifetime = htons(ra_lifetime < UINT16_MAX ? ra_lifetime : UINT16_MAX);
} else {
adv.h.nd_ra_router_lifetime = 0;
if (default_route) {
syslog(LOG_WARNING, "A default route is present but there is no public prefix "
- "on %s thus we announce no default route by overriding ra_lifetime to 0!", iface->name);
+ "on %s thus we announce no default route by setting ra_lifetime to 0!", iface->name);
} else {
- syslog(LOG_WARNING, "No default route present, overriding ra_lifetime to 0!");
+ syslog(LOG_WARNING, "No default route present, setting ra_lifetime to 0!");
}
}
memset(dns, 0, dns_sz);
dns->type = ND_OPT_RECURSIVE_DNS;
dns->len = 1 + (2 * dns_cnt);
- dns->lifetime = htonl(lifetime);
+ dns->lifetime = htonl(highest_found_lifetime);
memcpy(dns->addr, dns_addr, sizeof(struct in6_addr)*dns_cnt);
}
memset(search, 0, search_sz);
search->type = ND_OPT_DNS_SEARCH;
search->len = search_len ? ((sizeof(*search) + search_padded) / 8) : 0;
- search->lifetime = htonl(lifetime);
+ search->lifetime = htonl(highest_found_lifetime);
memcpy(search->name, search_domain, search_len);
memset(&search->name[search_len], 0, search_padded - search_len);
}
if (iface->pref64_length) {
/* RFC 8781 § 4.1 rounding up lifetime to multiple of 8 */
- uint16_t pref64_lifetime = lifetime < (UINT16_MAX - 7) ? lifetime + 7 : UINT16_MAX;
+ uint16_t pref64_lifetime = ra_lifetime < (UINT16_MAX - 7) ? ra_lifetime + 7 : (UINT16_MAX - 7);
pref64_sz = sizeof(*pref64);
pref64 = alloca(pref64_sz);
if (iface->dnr[i].lifetime_set)
dnr->lifetime = htonl(iface->dnr[i].lifetime);
else
- dnr->lifetime = htonl(lifetime);
+ dnr->lifetime = htonl(highest_found_lifetime);
dnr->adn_len = htons(iface->dnr[i].adn_len);
memcpy(tmp, iface->dnr[i].adn, iface->dnr[i].adn_len);
routes[routes_cnt].flags |= ND_RA_PREF_HIGH;
valid_lt = TIME_LEFT(addr->valid_lt, now);
- routes[routes_cnt].lifetime = htonl(valid_lt < lifetime ? valid_lt : lifetime);
+ if (iface->max_valid_lifetime && valid_lt > iface->max_valid_lifetime)
+ valid_lt = iface->max_valid_lifetime;
+ routes[routes_cnt].lifetime = htonl(valid_lt);
routes[routes_cnt].addr[0] = addr->addr.in6.s6_addr32[0];
routes[routes_cnt].addr[1] = addr->addr.in6.s6_addr32[1];
routes[routes_cnt].addr[2] = 0;