odhcpd: add support for dhcpv6_pd_min_len parameter
authorJohn Kohl <jtk.git@bostonpog.org>
Sat, 24 Jun 2023 14:18:03 +0000 (10:18 -0400)
committerChristian Marangi <ansuelsmth@gmail.com>
Sat, 24 Jun 2023 16:48:29 +0000 (18:48 +0200)
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 <jtk.git@bostonpog.org>
[ use different comment style and fix commit description ]
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
README
src/config.c
src/dhcpv6-ia.c
src/odhcpd.h

diff --git a/README b/README
index 24e57d48519d433d6ff29be35cbc117d7eb3a955..8f0e6a4336e204ce3c9e6cf8d8e5e08c6ee5b632 100644 (file)
--- 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    <local address>         Routers to announce
                                                        accepts IPv4 only
 dns                    list    <local address>         DNS servers to announce
index d7cd5dd0aa9c87427698a6d0c94b0ed120e49910..e63181400193f38970c0a5dab6e0620565bda9e2 100644 (file)
@@ -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);
 
index 99fd2fda61cab453158ef8e4ea05fe903d210994..41c9f30fbcc9cb6c7e9cba895626a035569cd4d3 100644 (file)
@@ -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;
index 8ab51dc4ceca2288eec2f7488abae7957df6015e..08b4920a56e2049de47cae2cb53629e94da1eaf8 100644 (file)
@@ -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;