From 52112643308bb02a3b4fa2894dd7d4340ba4a237 Mon Sep 17 00:00:00 2001 From: John Kohl Date: Sat, 24 Jun 2023 10:18:03 -0400 Subject: [PATCH] odhcpd: add support for dhcpv6_pd_min_len parameter The dhcpv6_pd_min_len configuration clamps the requested prefix delegation to be at least as big as the option. This allows a router to manage the size of each downstream router's prefix delegation length independently from the delegating interface's prefix length. This behavior is an implementation choice permitted by the RFCs. The delegating router (us) is not required to honor the hint (RFC3633, section 11.2, we MAY choose to use the information in the option; RFC8168, section 3.2 has several SHOULDs about desired choices for selecting a prefix to delegate). This configuration allows us to conserve prefix space so that any single router can't grab too much of it. Consider if we have an interface with a /56 prefix. A requesting router could ask for a /58 and take 1/4 of our total address space. But if we set a minimum of /60, we can limit each requesting router to get only 1/16 of our total address space. sample config: config dhcp 'pd' ... option dhcpv6_pd_min_len '60' Signed-off-by: John Kohl [ use different comment style and fix commit description ] Signed-off-by: Christian Marangi --- README | 3 +++ src/config.c | 14 ++++++++++++++ src/dhcpv6-ia.c | 25 +++++++++++++++++++++++++ src/odhcpd.h | 1 + 4 files changed, 43 insertions(+) diff --git a/README b/README index 24e57d4..8f0e6a4 100644 --- a/README +++ b/README @@ -101,6 +101,9 @@ dhcpv6_na bool 1 DHCPv6 stateful addressing hands out IA_NA - Internet Address - Network Address dhcpv6_pd bool 1 DHCPv6 stateful addressing hands out IA_PD - Internet Address - Prefix Delegation +dhcpv6_pd_min_len integer - Minimum prefix length to delegate with IA_PD + (value is adjusted if needed to be greater + than the interface prefix length). Range [1,62] router list Routers to announce accepts IPv4 only dns list DNS servers to announce diff --git a/src/config.c b/src/config.c index d7cd5dd..e631814 100644 --- a/src/config.c +++ b/src/config.c @@ -39,6 +39,8 @@ struct config config = {.legacy = false, .main_dhcpv4 = false, #define HOSTID_LEN_MAX 64 #define HOSTID_LEN_DEFAULT HOSTID_LEN_MIN +#define PD_MIN_LEN_MAX (64-2) // must delegate at least 2 bits of prefix + #define OAF_DHCPV6 (OAF_DHCPV6_NA | OAF_DHCPV6_PD) enum { @@ -64,6 +66,7 @@ enum { IFACE_ATTR_DHCPV6_RAW, IFACE_ATTR_DHCPV6_ASSIGNALL, IFACE_ATTR_DHCPV6_PD, + IFACE_ATTR_DHCPV6_PD_MIN_LEN, IFACE_ATTR_DHCPV6_NA, IFACE_ATTR_DHCPV6_HOSTID_LEN, IFACE_ATTR_RA_DEFAULT, @@ -116,6 +119,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = { [IFACE_ATTR_DHCPV6_RAW] = { .name = "dhcpv6_raw", .type = BLOBMSG_TYPE_STRING }, [IFACE_ATTR_DHCPV6_ASSIGNALL] = { .name ="dhcpv6_assignall", .type = BLOBMSG_TYPE_BOOL }, [IFACE_ATTR_DHCPV6_PD] = { .name = "dhcpv6_pd", .type = BLOBMSG_TYPE_BOOL }, + [IFACE_ATTR_DHCPV6_PD_MIN_LEN] = { .name = "dhcpv6_pd_min_len", .type = BLOBMSG_TYPE_INT32 }, [IFACE_ATTR_DHCPV6_NA] = { .name = "dhcpv6_na", .type = BLOBMSG_TYPE_BOOL }, [IFACE_ATTR_DHCPV6_HOSTID_LEN] = { .name = "dhcpv6_hostidlength", .type = BLOBMSG_TYPE_INT32 }, [IFACE_ATTR_PD_MANAGER] = { .name = "pd_manager", .type = BLOBMSG_TYPE_STRING }, @@ -214,6 +218,7 @@ static void set_interface_defaults(struct interface *iface) iface->dhcpv4_end.s_addr = htonl(START_DEFAULT + LIMIT_DEFAULT - 1); iface->dhcpv6_assignall = true; iface->dhcpv6_pd = true; + iface->dhcpv6_pd_min_len = 0; iface->dhcpv6_na = true; iface->dhcpv6_hostid_len = HOSTID_LEN_DEFAULT; iface->dns_service = true; @@ -851,6 +856,15 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr if ((c = tb[IFACE_ATTR_DHCPV6_PD])) iface->dhcpv6_pd = blobmsg_get_bool(c); + if ((c = tb[IFACE_ATTR_DHCPV6_PD_MIN_LEN])) { + uint32_t pd_min_len = blobmsg_get_u32(c); + if (pd_min_len != 0 && pd_min_len <= PD_MIN_LEN_MAX) + iface->dhcpv6_pd_min_len = pd_min_len; + else + syslog(LOG_ERR, "Invalid %s value configured for interface '%s'", + iface_attrs[IFACE_ATTR_DHCPV6_PD_MIN_LEN].name, iface->name); + } + if ((c = tb[IFACE_ATTR_DHCPV6_NA])) iface->dhcpv6_na = blobmsg_get_bool(c); diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index 99fd2fd..41c9f30 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -1332,6 +1332,31 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac if (reqlen > 64) reqlen = 64; + + /* + * A requesting router can include a desired prefix length for its + * delegation. The delegating router (us) is not required to honor + * the hint (RFC3633, section 11.2, we MAY choose to use the + * information in the option; RFC8168, section 3.2 has several SHOULDs + * about desired choices for selecting a prefix to delegate). + * + * We support a policy setting to conserve prefix space, which purposely + * assigns prefixes that might not match the requesting router's hint. + * + * If the minimum prefix length is set in this interface's + * configuration, we use it as a floor for the requested (hinted) + * prefix length. This allows us to conserve prefix space so that + * any single router can't grab too much of it. Consider if we have + * an interface with a /56 prefix. A requesting router could ask for + * a /58 and take 1/4 of our total address space. But if we set a + * minimum of /60, we can limit each requesting router to get only + * 1/16 of our total address space. + */ + if (iface->dhcpv6_pd_min_len && reqlen < iface->dhcpv6_pd_min_len) { + syslog(LOG_INFO, "clamping requested PD from %d to %d", + reqlen, iface->dhcpv6_pd_min_len); + reqlen = iface->dhcpv6_pd_min_len; + } } else if (is_na) { uint8_t *sdata; uint16_t stype, slen; diff --git a/src/odhcpd.h b/src/odhcpd.h index 8ab51dc..08b4920 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -350,6 +350,7 @@ struct interface { bool dhcpv6_pd; bool dhcpv6_na; uint32_t dhcpv6_hostid_len; + uint32_t dhcpv6_pd_min_len; // minimum delegated prefix length char *upstream; size_t upstream_len; -- 2.30.2