dhcpv4: simplify pool determination
authorDavid Härdeman <[email protected]>
Wed, 19 Nov 2025 18:43:36 +0000 (19:43 +0100)
committerÁlvaro Fernández Rojas <[email protected]>
Thu, 27 Nov 2025 07:24:05 +0000 (08:24 +0100)
This simplifies the logic to determine a dynamic dhcp pool range based
on the prefix length (when "start" and "limit" aren't set).

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/320
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
src/config.c
src/dhcpv4.c
src/dhcpv4.h
src/odhcpd.c
src/odhcpd.h

index afecffe7ba6df2213a77489b02e7abc74f4f2c4a..8609c5fff78eacda2967277e49eb739fc007a2e5 100644 (file)
@@ -63,9 +63,7 @@ struct sys_conf sys_conf = {
        .tzdb_tz_len = 0,
 };
 
-#define DHCPV4_POOL_START_DEFAULT      100
 #define DHCPV4_POOL_LIMIT_DEFAULT      150
-#define DHCPV4_POOL_END_DEFAULT                (DHCPV4_POOL_START_DEFAULT + DHCPV4_POOL_LIMIT_DEFAULT - 1)
 
 #define HOSTID_LEN_MIN 12
 #define HOSTID_LEN_MAX 64
@@ -319,8 +317,8 @@ static void set_interface_defaults(struct interface *iface)
        iface->max_preferred_lifetime = ND_PREFERRED_LIMIT;
        iface->max_valid_lifetime = ND_VALID_LIMIT;
        iface->captive_portal_uri = NULL;
-       iface->dhcpv4_pool_start = DHCPV4_POOL_START_DEFAULT;
-       iface->dhcpv4_pool_end = DHCPV4_POOL_END_DEFAULT;
+       iface->dhcpv4_pool_start = 0;
+       iface->dhcpv4_pool_end = 0;
        iface->dhcpv6_assignall = true;
        iface->dhcpv6_pd = true;
        iface->dhcpv6_pd_preferred = false;
@@ -1216,8 +1214,7 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
        if ((c = tb[IFACE_ATTR_DHCPV4_POOL_LIMIT]))
                iface->dhcpv4_pool_end = iface->dhcpv4_pool_start + blobmsg_get_u32(c) - 1;
 
-       if (iface->dhcpv4_pool_start == 0 ||
-           iface->dhcpv4_pool_start > UINT16_MAX ||
+       if (iface->dhcpv4_pool_start > UINT16_MAX ||
            iface->dhcpv4_pool_end > UINT16_MAX ||
            iface->dhcpv4_pool_start > iface->dhcpv4_pool_end) {
                warn("Invalid DHCPv4 pool range for %s, disabling dynamic leases", iface->name);
index c7a391fc22f1b0528a978750ea5c203db60232cf..f489232a8468a37f1daef01da75a4424a5467b1b 100644 (file)
@@ -38,8 +38,6 @@
 #include "dhcpv6.h"
 #include "statefiles.h"
 
-#define MAX_PREFIX_LEN 28
-
 static uint32_t serial = 0;
 
 struct odhcpd_ref_ip {
@@ -1352,8 +1350,8 @@ static void dhcpv4_handle_dgram(void *addr, void *data, size_t len,
 
 static bool dhcpv4_setup_addresses(struct interface *iface)
 {
-       uint32_t start = iface->dhcpv4_pool_start;
-       uint32_t end = iface->dhcpv4_pool_end;
+       uint32_t pool_start = iface->dhcpv4_pool_start;
+       uint32_t pool_end = iface->dhcpv4_pool_end;
 
        iface->dhcpv4_start_ip.s_addr = INADDR_ANY;
        iface->dhcpv4_end_ip.s_addr = INADDR_ANY;
@@ -1361,73 +1359,80 @@ static bool dhcpv4_setup_addresses(struct interface *iface)
        iface->dhcpv4_bcast.s_addr = INADDR_ANY;
        iface->dhcpv4_mask.s_addr = INADDR_ANY;
 
-       if (!iface->oaddrs4_cnt)
-               goto out;
-
        if (iface->no_dynamic_dhcp) {
+               if (!iface->oaddrs4_cnt)
+                       goto error;
+
                iface->dhcpv4_local.s_addr = iface->oaddrs4[0].addr.in.s_addr;
-               iface->dhcpv4_bcast.s_addr = iface->oaddrs4[0].broadcast.s_addr;
-               odhcpd_bitlen2netmask(false, iface->oaddrs4[0].prefix_len, &iface->dhcpv4_mask);
+               iface->dhcpv4_bcast = iface->oaddrs4[0].broadcast;
+               iface->dhcpv4_mask.s_addr = iface->oaddrs4[0].netmask;
 
                info("DHCPv4: providing static leases on interface '%s'", iface->name);
                return true;
        }
 
-       for (size_t i = 0; i < iface->oaddrs4_cnt && start && end; i++) {
-               struct in_addr *addr = &iface->oaddrs4[i].addr.in;
-               struct in_addr mask;
+       for (size_t i = 0; i < iface->oaddrs4_cnt; i++) {
+               struct odhcpd_ipaddr *oaddr = &iface->oaddrs4[i];
+               uint32_t hostmask = ntohl(~oaddr->netmask);
+               char pool_start_str[INET_ADDRSTRLEN];
+               char pool_end_str[INET_ADDRSTRLEN];
 
-               if (addr_is_fr_ip(iface, addr))
+               if (oaddr->prefix_len > DHCPV4_MAX_PREFIX_LEN)
                        continue;
 
-               odhcpd_bitlen2netmask(false, iface->oaddrs4[i].prefix_len, &mask);
-               if ((start & ntohl(~mask.s_addr)) == start &&
-                               (end & ntohl(~mask.s_addr)) == end &&
-                               end < ntohl(~mask.s_addr)) {    /* Exclude broadcast address */
-                       iface->dhcpv4_start_ip.s_addr = htonl(start) |
-                                                       (addr->s_addr & mask.s_addr);
-                       iface->dhcpv4_end_ip.s_addr = htonl(end) |
-                                                       (addr->s_addr & mask.s_addr);
-                       iface->dhcpv4_local = *addr;
-                       iface->dhcpv4_bcast = iface->oaddrs4[i].broadcast;
-                       iface->dhcpv4_mask = mask;
-                       return 0;
+               if (addr_is_fr_ip(iface, &oaddr->addr.in))
+                       continue;
+
+               /* pool_start outside range? */
+               if (pool_start && ((pool_start & hostmask) != pool_start))
+                       continue;
+
+               /* pool_end outside range? */
+               if (pool_end && ((pool_end & hostmask) != pool_end))
+                       continue;
+
+               /* pool_end == broadcast? */
+               if (pool_end && (pool_end == hostmask))
+                       continue;
+
+               if (!pool_start || !pool_end) {
+                       switch (oaddr->prefix_len) {
+                       case 28:
+                               pool_start = 3;
+                               pool_end = 12;
+                               break;
+                       case 27:
+                               pool_start = 10;
+                               pool_end = 29;
+                               break;
+                       case 26:
+                               pool_start = 10;
+                               pool_end = 59;
+                               break;
+                       case 25:
+                               pool_start = 20;
+                               pool_end = 119;
+                               break;
+                       default: /* <= 24 */
+                               pool_start = 100;
+                               pool_end = 249;
+                               break;
+                       }
                }
-       }
 
-       /* Don't allocate IP range for subnets smaller than /28 */
-       if (iface->oaddrs4[0].prefix_len > MAX_PREFIX_LEN) {
-               warn("Auto allocation of DHCP range fails on %s (prefix length must be < %d).",
-                    iface->name, MAX_PREFIX_LEN + 1);
-               return -1;
-       }
+               iface->dhcpv4_start_ip.s_addr = (oaddr->addr.in.s_addr & oaddr->netmask) | htonl(pool_start);
+               iface->dhcpv4_end_ip.s_addr = (oaddr->addr.in.s_addr & oaddr->netmask) | htonl(pool_end);
+               iface->dhcpv4_local.s_addr = oaddr->addr.in.s_addr;
+               iface->dhcpv4_bcast = oaddr->broadcast;
+               iface->dhcpv4_mask.s_addr = oaddr->netmask;
 
-       iface->dhcpv4_local = iface->oaddrs4[0].addr.in;
-       iface->dhcpv4_bcast = iface->oaddrs4[0].broadcast;
-       odhcpd_bitlen2netmask(false, iface->oaddrs4[0].prefix_len, &iface->dhcpv4_mask);
-       end = start = iface->dhcpv4_local.s_addr & iface->dhcpv4_mask.s_addr;
-
-       /* Auto allocate ranges */
-       if (ntohl(iface->dhcpv4_mask.s_addr) <= 0xffffff00) {           /* /24, 150 of 256, [100..249] */
-               iface->dhcpv4_start_ip.s_addr = start | htonl(100);
-               iface->dhcpv4_end_ip.s_addr = end | htonl(100 + 150 - 1);
-       } else if (ntohl(iface->dhcpv4_mask.s_addr) <= 0xffffff80) {    /* /25, 100 of 128, [20..119] */
-               iface->dhcpv4_start_ip.s_addr = start | htonl(20);
-               iface->dhcpv4_end_ip.s_addr = end | htonl(20 + 100 - 1);
-       } else if (ntohl(iface->dhcpv4_mask.s_addr) <= 0xffffffc0) {    /* /26, 50 of 64, [10..59] */
-               iface->dhcpv4_start_ip.s_addr = start | htonl(10);
-               iface->dhcpv4_end_ip.s_addr = end | htonl(10 + 50 - 1);
-       } else if (ntohl(iface->dhcpv4_mask.s_addr) <= 0xffffffe0) {    /* /27, 20 of 32, [10..29] */
-               iface->dhcpv4_start_ip.s_addr = start | htonl(10);
-               iface->dhcpv4_end_ip.s_addr = end | htonl(10 + 20 - 1);
-       } else {                                                        /* /28, 10 of 16, [3..12] */
-               iface->dhcpv4_start_ip.s_addr = start | htonl(3);
-               iface->dhcpv4_end_ip.s_addr = end | htonl(3 + 10 - 1);
+               info("DHCPv4: providing dynamic/static leases on interface '%s', pool: %s - %s", iface->name,
+                    inet_ntop(AF_INET, &iface->dhcpv4_start_ip, pool_start_str, sizeof(pool_start_str)),
+                    inet_ntop(AF_INET, &iface->dhcpv4_end_ip, pool_end_str, sizeof(pool_end_str)));
+               return true;
        }
 
-       return 0;
-
-out:
+error:
        warn("DHCPv4: no suitable networks on interface '%s'", iface->name);
        return false;
 }
index 2aa791ed03b57687ce45e31162fdab3a56abd7ce..330df61d97d4ff87dcde166182d5b94e8815db02 100644 (file)
@@ -18,6 +18,7 @@
 
 #define DHCPV4_CLIENT_PORT 68
 #define DHCPV4_SERVER_PORT 67
+#define DHCPV4_MAX_PREFIX_LEN 28
 
 #define DHCPV4_FLAG_BROADCAST 0x8000
 
index c6bfbc9a0837687a921c484f02a2ef338fc563da..f9e07feed8685005f7e65816a19969a0daf88562 100644 (file)
@@ -775,38 +775,6 @@ int odhcpd_netmask2bitlen(bool inet6, void *mask)
        return bits;
 }
 
-bool odhcpd_bitlen2netmask(bool inet6, unsigned int bits, void *mask)
-{
-       uint8_t b;
-       struct in_addr *v4;
-       struct in6_addr *v6;
-
-       if (inet6)
-       {
-               if (bits > 128)
-                       return false;
-
-               v6 = mask;
-
-               for (unsigned int i = 0; i < sizeof(v6->s6_addr); i++)
-               {
-                       b = (bits > 8) ? 8 : bits;
-                       v6->s6_addr[i] = (uint8_t)(0xFF << (8 - b));
-                       bits -= b;
-               }
-       }
-       else
-       {
-               if (bits > 32)
-                       return false;
-
-               v4 = mask;
-               v4->s_addr = bits ? htonl(~((1 << (32 - bits)) - 1)) : 0;
-       }
-
-       return true;
-}
-
 bool odhcpd_valid_hostname(const char *name)
 {
 #define MAX_LABEL      63
index 88135f1968cf8e5e173c8f167471b3b870e07324..1da50ddede22ba210d00c63913c26c82c3122e5e 100644 (file)
@@ -567,7 +567,6 @@ void odhcpd_enum_addr6(struct interface *iface, struct dhcpv6_lease *lease,
                       time_t now, odhcpd_enum_addr6_cb_t func, void *arg);
 int odhcpd_parse_addr6_prefix(const char *str, struct in6_addr *addr, uint8_t *prefix);
 int odhcpd_netmask2bitlen(bool v6, void *mask);
-bool odhcpd_bitlen2netmask(bool v6, unsigned int bits, void *mask);
 bool odhcpd_valid_hostname(const char *name);
 
 int config_parse_interface(void *data, size_t len, const char *iname, bool overwrite);