From: David Härdeman Date: Wed, 19 Nov 2025 18:43:36 +0000 (+0100) Subject: dhcpv4: simplify pool determination X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=1c2e8b60809aaa4fa12f75cae8447523ca626d70;p=project%2Fodhcpd.git dhcpv4: simplify pool determination 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 Link: https://github.com/openwrt/odhcpd/pull/320 Signed-off-by: Álvaro Fernández Rojas --- diff --git a/src/config.c b/src/config.c index afecffe..8609c5f 100644 --- a/src/config.c +++ b/src/config.c @@ -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); diff --git a/src/dhcpv4.c b/src/dhcpv4.c index c7a391f..f489232 100644 --- a/src/dhcpv4.c +++ b/src/dhcpv4.c @@ -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; } diff --git a/src/dhcpv4.h b/src/dhcpv4.h index 2aa791e..330df61 100644 --- a/src/dhcpv4.h +++ b/src/dhcpv4.h @@ -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 diff --git a/src/odhcpd.c b/src/odhcpd.c index c6bfbc9..f9e07fe 100644 --- a/src/odhcpd.c +++ b/src/odhcpd.c @@ -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 diff --git a/src/odhcpd.h b/src/odhcpd.h index 88135f1..1da50dd 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -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);