From 726ca5340823e2d5f3b5e7b6ad83e52ba3be14a9 Mon Sep 17 00:00:00 2001 From: Aviana Cruz Date: Sat, 16 Sep 2023 15:04:12 +0000 Subject: [PATCH] odhcpd: improve RFC 9096 compliance and allow configuring upper limit for preferred and valid lifetime. Signed-off-by: Aviana Cruz --- README | 12 ++++++------ src/config.c | 35 +++++++++++++++++++++------------- src/dhcpv6-ia.c | 50 ++++++++++++++++++++++++++++++------------------- src/odhcpd.h | 8 ++++++-- src/router.c | 17 ++++++++--------- 5 files changed, 73 insertions(+), 49 deletions(-) --- a/README +++ b/README @@ -116,7 +116,9 @@ domain list Sear leasetime string 12h DHCPv4 address leasetime start integer 100 DHCPv4 pool start limit integer 150 DHCPv4 pool size -preferred_lifetime string 12h 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 @@ -131,11 +133,9 @@ ra_maxinterval integer 600 Maximum ti 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 --- a/src/config.c +++ b/src/config.c @@ -79,7 +79,6 @@ enum { 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, @@ -91,7 +90,8 @@ enum { 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 }; @@ -134,7 +134,6 @@ static const struct blobmsg_policy iface [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 }, @@ -144,7 +143,8 @@ static const struct blobmsg_policy iface [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 }, }; @@ -215,7 +215,8 @@ static void set_interface_defaults(struc iface->ndp = MODE_DISABLED; iface->learn_routes = 1; iface->dhcp_leasetime = 43200; - iface->preferred_lifetime = 43200; + 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; @@ -647,15 +648,26 @@ int config_parse_interface(void *data, s } - 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])) { @@ -978,9 +990,6 @@ int config_parse_interface(void *data, s 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); --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -120,7 +120,7 @@ static inline bool valid_prefix_length(c static inline bool valid_addr(const struct odhcpd_ipaddr *addr, time_t now) { - return (addr->prefix <= 96 && addr->preferred > (uint32_t)now); + return (addr->prefix <= 96 && addr->valid > (uint32_t)now && addr->preferred > (uint32_t)now); } static size_t get_preferred_addr(const struct odhcpd_ipaddr *addrs, const size_t addrlen) @@ -1037,17 +1037,27 @@ static size_t build_ia(uint8_t *buf, siz } if (a) { - uint32_t leasetime, pref; + uint32_t leasetime; if (a->leasetime) { leasetime = a->leasetime; - pref = a->leasetime; } else { leasetime = iface->dhcp_leasetime; - pref = iface->preferred_lifetime; } - uint32_t valid = 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; @@ -1071,17 +1081,19 @@ static size_t build_ia(uint8_t *buf, siz prefix_pref = addrs[i].preferred; prefix_valid = addrs[i].valid; - if (prefix_pref != UINT32_MAX) + if (prefix_pref != UINT32_MAX) { prefix_pref -= now; - if (prefix_pref > pref) - prefix_pref = pref; + if (iface->max_preferred_lifetime && prefix_pref > iface->max_preferred_lifetime) + prefix_pref = iface->max_preferred_lifetime; + } - if (prefix_valid != UINT32_MAX) + if (prefix_valid != UINT32_MAX) { prefix_valid -= now; - if (prefix_valid > leasetime) - prefix_valid = leasetime; + if (iface->max_valid_lifetime && prefix_valid > iface->max_valid_lifetime) + prefix_valid = iface->max_valid_lifetime; + } if (prefix_pref > prefix_valid) prefix_pref = prefix_valid; @@ -1133,24 +1145,24 @@ static size_t build_ia(uint8_t *buf, siz /* Calculate T1 / T2 based on non-deprecated addresses */ if (prefix_pref > 0) { - if (prefix_pref < pref) - pref = prefix_pref; + if (floor_preferred_lifetime > prefix_pref) + floor_preferred_lifetime = prefix_pref; - if (prefix_valid < valid) - valid = prefix_valid; + if (floor_valid_lifetime > prefix_valid) + floor_valid_lifetime = prefix_valid; } } if (!INFINITE_VALID(a->valid_until)) /* UINT32_MAX is considered as infinite leasetime */ - a->valid_until = (valid == UINT32_MAX) ? 0 : valid + now; + a->valid_until = (floor_valid_lifetime == UINT32_MAX) ? 0 : floor_valid_lifetime + now; if (!INFINITE_VALID(a->preferred_until)) /* UINT32_MAX is considered as infinite leasetime */ - a->preferred_until = (pref == UINT32_MAX) ? 0 : pref + now; + a->preferred_until = (floor_preferred_lifetime == UINT32_MAX) ? 0 : floor_preferred_lifetime + now; - o_ia.t1 = htonl((pref == UINT32_MAX) ? pref : pref * 5 / 10); - o_ia.t2 = htonl((pref == UINT32_MAX) ? pref : pref * 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); --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -37,6 +37,10 @@ // 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 + #define INFINITE_VALID(x) ((x) == 0) #define _unused __attribute__((unused)) @@ -302,7 +306,6 @@ struct interface { bool ra_slaac; bool ra_not_onlink; bool ra_advrouter; - bool ra_useleasetime; bool ra_dns; uint8_t pref64_length; struct in6_addr pref64_addr; @@ -319,7 +322,8 @@ struct interface { 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; --- a/src/router.c +++ b/src/router.c @@ -371,7 +371,7 @@ static int calc_adv_interval(struct inte static uint32_t calc_ra_lifetime(struct interface *iface, uint32_t maxival) { - uint32_t lifetime = 3*maxival; + uint32_t lifetime = maxival * 3; if (iface->ra_lifetime >= 0) { lifetime = iface->ra_lifetime; @@ -590,16 +590,15 @@ static int send_router_advert(struct int if (addr->preferred > (uint32_t)now) { preferred = TIME_LEFT(addr->preferred, now); - if (iface->ra_useleasetime && - preferred > iface->preferred_lifetime) - preferred = iface->preferred_lifetime; + if (iface->max_preferred_lifetime && preferred > iface->max_preferred_lifetime) + preferred = iface->max_preferred_lifetime; } if (addr->valid > (uint32_t)now) { valid = TIME_LEFT(addr->valid, now); - if (iface->ra_useleasetime && valid > iface->dhcp_leasetime) - valid = iface->dhcp_leasetime; + if (iface->max_valid_lifetime && valid > iface->max_valid_lifetime) + valid = iface->max_valid_lifetime; } if (minvalid > valid) @@ -643,9 +642,9 @@ static int send_router_advert(struct int if (default_route) { syslog(LOG_WARNING, "A default route is present but there is no public prefix " - "on %s thus we don't announce a default route by overriding ra_lifetime!", iface->name); + "on %s thus we don't announce a default route by setting ra_lifetime to zero!", iface->name); } else { - syslog(LOG_WARNING, "No default route present, overriding ra_lifetime!"); + syslog(LOG_WARNING, "No default route present, setting ra_lifetime to zero!"); } } @@ -710,7 +709,7 @@ static int send_router_advert(struct int if (iface->pref64_length) { /* RFC 8781 ยง 4.1 rounding up lifetime to multiply of 8 */ - uint16_t pref64_lifetime = lifetime < (UINT16_MAX - 7) ? lifetime + 7 : UINT16_MAX; + uint16_t pref64_lifetime = lifetime < (UINT16_MAX - 7) ? lifetime + 7 : (UINT16_MAX - 7); uint8_t prefix_length_code; uint32_t mask_a1, mask_a2;