summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Härdeman2025-10-09 05:13:58 +0000
committerÁlvaro Fernández Rojas2025-11-04 07:51:16 +0000
commit4df45c8c37227b67319ca39ee4eb2d21966ee693 (patch)
treecff9f9fddf378e55b4196fcf3a76929b1fcc5277
parenta6dccae41b6068a9bee3ccb85cff0081c1e412d2 (diff)
downloadodhcpd-4df45c8c37227b67319ca39ee4eb2d21966ee693.tar.gz
dhcpv4: create struct dhcpv4_lease
This makes it easier to understand what is actually going on, makes the structs clearer and avoids layers of indirection like using free callbacks. I understand the original motivation for unifying the struct for DHCPv4 and DHCPv6, but it's more hassle than it is worth. Signed-off-by: David Härdeman <david@hardeman.nu> Link: https://github.com/openwrt/odhcpd/pull/292 Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
-rw-r--r--src/config.c34
-rw-r--r--src/dhcpv4.c372
-rw-r--r--src/dhcpv6-ia.c10
-rw-r--r--src/odhcpd.h52
-rw-r--r--src/ubus.c4
5 files changed, 257 insertions, 215 deletions
diff --git a/src/config.c b/src/config.c
index 1133578..c424266 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1108,7 +1108,7 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
iface->ndp_ping_fd = -1;
iface->dhcpv4_event.uloop.fd = -1;
INIT_LIST_HEAD(&iface->ia_assignments);
- INIT_LIST_HEAD(&iface->dhcpv4_assignments);
+ INIT_LIST_HEAD(&iface->dhcpv4_leases);
INIT_LIST_HEAD(&iface->dhcpv4_fr_ips);
set_interface_defaults(iface);
@@ -1744,24 +1744,29 @@ static int set_interface(struct uci_section *s)
return config_parse_interface(blob_data(b.head), blob_len(b.head), s->e.name, true);
}
-static void lease_cfg_delete_assignments(struct lease_cfg *lease_cfg, bool v6)
+static void lease_cfg_delete_dhcpv6_assignments(struct lease_cfg *lease_cfg)
{
struct dhcp_assignment *a, *tmp;
- unsigned int flag = v6 ? OAF_DHCPV6 : OAF_DHCPV4;
- list_for_each_entry_safe(a, tmp, &lease_cfg->assignments, lease_cfg_list) {
- if (a->flags & flag)
- free_assignment(a);
- }
+ list_for_each_entry_safe(a, tmp, &lease_cfg->assignments, lease_cfg_list)
+ free_assignment(a);
}
static void lease_cfg_update_assignments(struct lease_cfg *lease_cfg)
{
+ struct dhcpv4_lease *a4 = lease_cfg->dhcpv4_lease;
struct dhcp_assignment *a;
+ if (a4) {
+ free(a4->hostname);
+ a4->hostname = NULL;
+
+ if (lease_cfg->hostname)
+ a4->hostname = strdup(lease_cfg->hostname);
+ }
+
list_for_each_entry(a, &lease_cfg->assignments, lease_cfg_list) {
- if (a->hostname)
- free(a->hostname);
+ free(a->hostname);
a->hostname = NULL;
if (lease_cfg->hostname)
@@ -1827,12 +1832,12 @@ static void lease_cfg_change(struct lease_cfg *lease_cfg_old, struct lease_cfg *
if (lease_cfg_old->ipaddr != lease_cfg_new->ipaddr) {
lease_cfg_old->ipaddr = lease_cfg_new->ipaddr;
- lease_cfg_delete_assignments(lease_cfg_old, false);
+ dhcpv4_free_lease(lease_cfg_old->dhcpv4_lease);
}
if (lease_cfg_old->hostid != lease_cfg_new->hostid) {
lease_cfg_old->hostid = lease_cfg_new->hostid;
- lease_cfg_delete_assignments(lease_cfg_old, true);
+ lease_cfg_delete_dhcpv6_assignments(lease_cfg_old);
}
if (update)
@@ -1843,11 +1848,8 @@ static void lease_cfg_change(struct lease_cfg *lease_cfg_old, struct lease_cfg *
static void lease_cfg_delete(struct lease_cfg *lease_cfg)
{
- struct dhcp_assignment *a, *tmp;
-
- list_for_each_entry_safe(a, tmp, &lease_cfg->assignments, lease_cfg_list)
- free_assignment(a);
-
+ dhcpv4_free_lease(lease_cfg->dhcpv4_lease);
+ lease_cfg_delete_dhcpv6_assignments(lease_cfg);
free_lease_cfg(lease_cfg);
}
diff --git a/src/dhcpv4.c b/src/dhcpv4.c
index 2b0e3a5..aaf6821 100644
--- a/src/dhcpv4.c
+++ b/src/dhcpv4.c
@@ -79,15 +79,15 @@ static bool addr_is_fr_ip(struct interface *iface, struct in_addr *addr)
}
static bool leases_require_fr(struct interface *iface, struct odhcpd_ipaddr *addr,
- uint32_t mask)
+ uint32_t mask)
{
- struct dhcp_assignment *a = NULL;
+ struct dhcpv4_lease *lease = NULL;
struct odhcpd_ref_ip *fr_ip = NULL;
- list_for_each_entry(a, &iface->dhcpv4_assignments, head) {
- if ((a->accept_fr_nonce || iface->dhcpv4_forcereconf) &&
- !a->fr_ip &&
- ((a->addr & mask) == (addr->addr.in.s_addr & mask))) {
+ list_for_each_entry(lease, &iface->dhcpv4_leases, head) {
+ if ((lease->accept_fr_nonce || iface->dhcpv4_forcereconf) &&
+ !lease->fr_ip &&
+ ((lease->addr & mask) == (addr->addr.in.s_addr & mask))) {
if (!fr_ip) {
fr_ip = calloc(1, sizeof(*fr_ip));
if (!fr_ip)
@@ -96,7 +96,7 @@ static bool leases_require_fr(struct interface *iface, struct odhcpd_ipaddr *add
list_add(&fr_ip->head, &iface->dhcpv4_fr_ips);
fr_ip->addr = *addr;
}
- inc_ref_cnt_ip(&a->fr_ip, fr_ip);
+ inc_ref_cnt_ip(&lease->fr_ip, fr_ip);
}
}
@@ -176,7 +176,7 @@ enum {
IOV_FR_TOTAL
};
-static void dhcpv4_fr_send(struct dhcp_assignment *a)
+static void dhcpv4_fr_send(struct dhcpv4_lease *lease)
{
struct dhcpv4_message fr = {
.op = DHCPV4_OP_BOOTREPLY,
@@ -214,7 +214,7 @@ static void dhcpv4_fr_send(struct dhcp_assignment *a)
struct dhcpv4_option_u32 fr_serverid = {
.code = DHCPV4_OPT_SERVERID,
.len = sizeof(struct in_addr),
- .data = a->fr_ip->addr.addr.in.s_addr,
+ .data = lease->fr_ip->addr.addr.in.s_addr,
};
uint8_t fr_end = DHCPV4_OPT_END;
@@ -231,13 +231,13 @@ static void dhcpv4_fr_send(struct dhcp_assignment *a)
struct sockaddr_in dest = {
.sin_family = AF_INET,
.sin_port = htons(DHCPV4_CLIENT_PORT),
- .sin_addr = { a->addr },
+ .sin_addr = { lease->addr },
};
odhcpd_urandom(&fr.xid, sizeof(fr.xid));
- memcpy(fr.chaddr, a->hwaddr, fr.hlen);
+ memcpy(fr.chaddr, lease->hwaddr, fr.hlen);
- if (a->accept_fr_nonce) {
+ if (lease->accept_fr_nonce) {
uint8_t secretbytes[64] = { 0 };
md5_ctx_t md5;
@@ -247,7 +247,7 @@ static void dhcpv4_fr_send(struct dhcp_assignment *a)
iov[IOV_FR_AUTH_BODY].iov_len = sizeof(fr_auth_body);
dhcpv4_add_padding(iov, ARRAY_SIZE(iov));
- memcpy(secretbytes, a->key, sizeof(a->key));
+ memcpy(secretbytes, lease->key, sizeof(lease->key));
for (size_t i = 0; i < sizeof(secretbytes); ++i)
secretbytes[i] ^= 0x36;
@@ -272,54 +272,54 @@ static void dhcpv4_fr_send(struct dhcp_assignment *a)
}
if (dhcpv4_send_reply(iov, ARRAY_SIZE(iov), (struct sockaddr *)&dest, sizeof(dest),
- &a->iface->dhcpv4_event.uloop.fd) < 0)
+ &lease->iface->dhcpv4_event.uloop.fd) < 0)
error("Failed to send %s to %s - %s: %m", dhcpv4_msg_to_string(fr_msg.data),
- odhcpd_print_mac(a->hwaddr, sizeof(a->hwaddr)), inet_ntoa(dest.sin_addr));
+ odhcpd_print_mac(lease->hwaddr, sizeof(lease->hwaddr)), inet_ntoa(dest.sin_addr));
else
debug("Sent %s to %s - %s", dhcpv4_msg_to_string(fr_msg.data),
- odhcpd_print_mac(a->hwaddr, sizeof(a->hwaddr)), inet_ntoa(dest.sin_addr));
+ odhcpd_print_mac(lease->hwaddr, sizeof(lease->hwaddr)), inet_ntoa(dest.sin_addr));
}
-static void dhcpv4_fr_stop(struct dhcp_assignment *a)
+static void dhcpv4_fr_stop(struct dhcpv4_lease *lease)
{
- uloop_timeout_cancel(&a->fr_timer);
- decr_ref_cnt_ip(&a->fr_ip, a->iface);
- a->fr_cnt = 0;
- a->fr_timer.cb = NULL;
+ uloop_timeout_cancel(&lease->fr_timer);
+ decr_ref_cnt_ip(&lease->fr_ip, lease->iface);
+ lease->fr_cnt = 0;
+ lease->fr_timer.cb = NULL;
}
static void dhcpv4_fr_timer(struct uloop_timeout *event)
{
- struct dhcp_assignment *a = container_of(event, struct dhcp_assignment, fr_timer);
+ struct dhcpv4_lease *lease = container_of(event, struct dhcpv4_lease, fr_timer);
- if (a->fr_cnt > 0 && a->fr_cnt < 8) {
- dhcpv4_fr_send(a);
- uloop_timeout_set(&a->fr_timer, 1000 << a->fr_cnt);
- a->fr_cnt++;
+ if (lease->fr_cnt > 0 && lease->fr_cnt < 8) {
+ dhcpv4_fr_send(lease);
+ uloop_timeout_set(&lease->fr_timer, 1000 << lease->fr_cnt);
+ lease->fr_cnt++;
} else
- dhcpv4_fr_stop(a);
+ dhcpv4_fr_stop(lease);
}
-static void dhcpv4_fr_start(struct dhcp_assignment *a)
+static void dhcpv4_fr_start(struct dhcpv4_lease *lease)
{
- uloop_timeout_set(&a->fr_timer, 1000 << a->fr_cnt);
- a->fr_timer.cb = dhcpv4_fr_timer;
- a->fr_cnt++;
+ uloop_timeout_set(&lease->fr_timer, 1000 << lease->fr_cnt);
+ lease->fr_timer.cb = dhcpv4_fr_timer;
+ lease->fr_cnt++;
- dhcpv4_fr_send(a);
+ dhcpv4_fr_send(lease);
}
-static void dhcpv4_fr_rand_delay(struct dhcp_assignment *a);
+static void dhcpv4_fr_rand_delay(struct dhcpv4_lease *lease);
static void dhcpv4_fr_delay_timer(struct uloop_timeout *event)
{
- struct dhcp_assignment *a = container_of(event, struct dhcp_assignment, fr_timer);
- struct interface *iface = a->iface;
+ struct dhcpv4_lease *lease = container_of(event, struct dhcpv4_lease, fr_timer);
+ struct interface *iface = lease->iface;
- (iface->dhcpv4_event.uloop.fd == -1 ? dhcpv4_fr_rand_delay(a) : dhcpv4_fr_start(a));
+ (iface->dhcpv4_event.uloop.fd == -1 ? dhcpv4_fr_rand_delay(lease) : dhcpv4_fr_start(lease));
}
-static void dhcpv4_fr_rand_delay(struct dhcp_assignment *a)
+static void dhcpv4_fr_rand_delay(struct dhcpv4_lease *lease)
{
int msecs;
@@ -327,21 +327,49 @@ static void dhcpv4_fr_rand_delay(struct dhcp_assignment *a)
msecs = abs(msecs) % DHCPV4_FR_MAX_FUZZ + DHCPV4_FR_MIN_DELAY;
- uloop_timeout_set(&a->fr_timer, msecs);
- a->fr_timer.cb = dhcpv4_fr_delay_timer;
+ uloop_timeout_set(&lease->fr_timer, msecs);
+ lease->fr_timer.cb = dhcpv4_fr_delay_timer;
}
-static void dhcpv4_free_assignment(struct dhcp_assignment *a)
+void dhcpv4_free_lease(struct dhcpv4_lease *lease)
{
- if (a->fr_ip)
- dhcpv4_fr_stop(a);
+ if (lease->fr_ip)
+ dhcpv4_fr_stop(lease);
+
+ list_del(&lease->head);
+ if (lease->lease_cfg)
+ lease->lease_cfg->dhcpv4_lease = NULL;
+
+ free(lease->hostname);
+ free(lease);
+}
+
+static struct dhcpv4_lease *
+dhcpv4_alloc_lease(struct interface *iface, const uint8_t *hwaddr, size_t hwaddr_len)
+{
+ struct dhcpv4_lease *lease;
+
+ if (!iface || !hwaddr || hwaddr_len == 0 || hwaddr_len > sizeof(lease->hwaddr))
+ return NULL;
+
+ lease = calloc(1, sizeof(*lease));
+ if (!lease)
+ return NULL;
+
+ INIT_LIST_HEAD(&lease->head);
+
+ lease->hwaddr_len = hwaddr_len;
+ memcpy(lease->hwaddr, hwaddr, hwaddr_len);
+ lease->iface = iface;
+
+ return lease;
}
-static bool dhcpv4_insert_assignment(struct list_head *list, struct dhcp_assignment *a,
- uint32_t addr)
+static bool dhcpv4_insert_lease(struct list_head *list, struct dhcpv4_lease *lease,
+ uint32_t addr)
{
uint32_t h_addr = ntohl(addr);
- struct dhcp_assignment *c;
+ struct dhcpv4_lease *c;
list_for_each_entry(c, list, head) {
uint32_t c_addr = ntohl(c->addr);
@@ -354,13 +382,13 @@ static bool dhcpv4_insert_assignment(struct list_head *list, struct dhcp_assignm
}
/* Insert new node before c (might match list head) */
- a->addr = addr;
- list_add_tail(&a->head, &c->head);
+ lease->addr = addr;
+ list_add_tail(&lease->head, &c->head);
return true;
}
-static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a,
+static bool dhcpv4_assign(struct interface *iface, struct dhcpv4_lease *lease,
uint32_t raddr)
{
uint32_t start = ntohl(iface->dhcpv4_start_ip.s_addr);
@@ -370,15 +398,15 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a,
char ipv4_str[INET_ADDRSTRLEN];
/* Preconfigured IP address by static lease */
- if (a->addr) {
- if (!dhcpv4_insert_assignment(&iface->dhcpv4_assignments, a, a->addr)) {
+ if (lease->addr) {
+ if (!dhcpv4_insert_lease(&iface->dhcpv4_leases, lease, lease->addr)) {
error("The static IP address is already assigned: %s",
- inet_ntop(AF_INET, &a->addr, ipv4_str, sizeof(ipv4_str)));
+ inet_ntop(AF_INET, &lease->addr, ipv4_str, sizeof(ipv4_str)));
return false;
}
debug("Assigned static IP address: %s",
- inet_ntop(AF_INET, &a->addr, ipv4_str, sizeof(ipv4_str)));
+ inet_ntop(AF_INET, &lease->addr, ipv4_str, sizeof(ipv4_str)));
return true;
}
@@ -389,19 +417,19 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a,
} else if (config_find_lease_cfg_by_ipaddr(raddr)) {
debug("The requested IP address is statically assigned: %s",
inet_ntop(AF_INET, &raddr, ipv4_str, sizeof(ipv4_str)));
- } else if (!dhcpv4_insert_assignment(&iface->dhcpv4_assignments, a, raddr)) {
+ } else if (!dhcpv4_insert_lease(&iface->dhcpv4_leases, lease, raddr)) {
debug("The requested IP address is already assigned: %s",
inet_ntop(AF_INET, &raddr, ipv4_str, sizeof(ipv4_str)));
} else {
debug("Assigned the requested IP address: %s",
- inet_ntop(AF_INET, &a->addr, ipv4_str, sizeof(ipv4_str)));
+ inet_ntop(AF_INET, &lease->addr, ipv4_str, sizeof(ipv4_str)));
return true;
}
/* Ok, we'll have to pick an address for the client... */
- for (size_t i = 0; i < sizeof(a->hwaddr); ++i) {
+ for (size_t i = 0; i < sizeof(lease->hwaddr); ++i) {
/* ...hash the hwaddr (Knuth's multiplicative method)... */
- uint8_t o = a->hwaddr[i];
+ uint8_t o = lease->hwaddr[i];
seed += (o * 2654435761) % UINT32_MAX;
}
@@ -416,9 +444,9 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a,
if (config_find_lease_cfg_by_ipaddr(n_try))
continue;
- if (dhcpv4_insert_assignment(&iface->dhcpv4_assignments, a, n_try)) {
+ if (dhcpv4_insert_lease(&iface->dhcpv4_leases, lease, n_try)) {
debug("Assigned IP adress from pool: %s (succeeded on attempt %u of %u)",
- inet_ntop(AF_INET, &a->addr, ipv4_str, sizeof(ipv4_str)),
+ inet_ntop(AF_INET, &lease->addr, ipv4_str, sizeof(ipv4_str)),
i + 1, count);
return true;
}
@@ -429,24 +457,24 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a,
return false;
}
-static struct dhcp_assignment *find_assignment_by_hwaddr(struct interface *iface, const uint8_t *hwaddr)
+static struct dhcpv4_lease *find_lease_by_hwaddr(struct interface *iface, const uint8_t *hwaddr)
{
- struct dhcp_assignment *a;
+ struct dhcpv4_lease *lease;
- list_for_each_entry(a, &iface->dhcpv4_assignments, head)
- if (!memcmp(a->hwaddr, hwaddr, 6))
- return a;
+ list_for_each_entry(lease, &iface->dhcpv4_leases, head)
+ if (!memcmp(lease->hwaddr, hwaddr, 6))
+ return lease;
return NULL;
}
-static struct dhcp_assignment *
+static struct dhcpv4_lease *
dhcpv4_lease(struct interface *iface, enum dhcpv4_msg req_msg, const uint8_t *req_mac,
const uint32_t req_addr, uint32_t *req_leasetime, const char *req_hostname,
const size_t req_hostname_len, const bool req_accept_fr, bool *reply_incl_fr,
uint32_t *fr_serverid)
{
- struct dhcp_assignment *a = find_assignment_by_hwaddr(iface, req_mac);
+ struct dhcpv4_lease *lease = find_lease_by_hwaddr(iface, req_mac);
struct lease_cfg *lease_cfg = config_find_lease_cfg_by_mac(req_mac);
time_t now = odhcpd_time();
@@ -455,109 +483,99 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg req_msg, const uint8_t *re
* hwaddr, we need to clear out any old assignments given to other
* hwaddrs in order to take over the IP address.
*/
- if (lease_cfg && !a && (req_msg == DHCPV4_MSG_DISCOVER || req_msg == DHCPV4_MSG_REQUEST)) {
- struct dhcp_assignment *c, *tmp;
-
- list_for_each_entry_safe(c, tmp, &lease_cfg->assignments, lease_cfg_list) {
- if (c->flags & OAF_DHCPV4 && c->flags & OAF_STATIC)
- free_assignment(c);
- }
+ if (lease_cfg && !lease && (req_msg == DHCPV4_MSG_DISCOVER || req_msg == DHCPV4_MSG_REQUEST)) {
+ if (lease_cfg->dhcpv4_lease)
+ dhcpv4_free_lease(lease_cfg->dhcpv4_lease);
}
- if (lease_cfg && a && a->lease_cfg != lease_cfg) {
- free_assignment(a);
- a = NULL;
+ if (lease_cfg && lease && lease->lease_cfg != lease_cfg) {
+ dhcpv4_free_lease(lease);
+ lease = NULL;
}
- if (a && (a->flags & OAF_BOUND) && a->fr_ip) {
- *fr_serverid = a->fr_ip->addr.addr.in.s_addr;
- dhcpv4_fr_stop(a);
+ if (lease && (lease->flags & OAF_BOUND) && lease->fr_ip) {
+ *fr_serverid = lease->fr_ip->addr.addr.in.s_addr;
+ dhcpv4_fr_stop(lease);
}
switch (req_msg) {
case DHCPV4_MSG_RELEASE:
- if (!a)
+ if (!lease)
return NULL;
ubus_bcast_dhcp_event("dhcp.release", req_mac,
- (struct in_addr *)&a->addr,
- a->hostname, iface->ifname);
- free_assignment(a);
- a = NULL;
+ (struct in_addr *)&lease->addr,
+ lease->hostname, iface->ifname);
+ dhcpv4_free_lease(lease);
+ lease = NULL;
break;
case DHCPV4_MSG_DECLINE:
- if (!a)
+ if (!lease)
return NULL;
- a->flags &= ~OAF_BOUND;
+ lease->flags &= ~OAF_BOUND;
- if (!(a->flags & OAF_STATIC) || a->lease_cfg->ipaddr != a->addr) {
- memset(a->hwaddr, 0, sizeof(a->hwaddr));
- a->valid_until = now + 3600; /* Block address for 1h */
- } else {
- a->valid_until = now - 1;
- }
+ if (!(lease->flags & OAF_STATIC) || lease->lease_cfg->ipaddr != lease->addr) {
+ memset(lease->hwaddr, 0, sizeof(lease->hwaddr));
+ lease->valid_until = now + 3600; /* Block address for 1h */
+ } else
+ lease->valid_until = now - 1;
break;
case DHCPV4_MSG_DISCOVER:
_fallthrough;
case DHCPV4_MSG_REQUEST:
- if (!a && iface->no_dynamic_dhcp && !lease_cfg)
+ if (!lease && iface->no_dynamic_dhcp && !lease_cfg)
return NULL;
- /* Old assignment, but with an address that is out-of-scope? */
- if (a && ((a->addr & iface->dhcpv4_mask.s_addr) !=
+ /* Old lease, but with an address that is out-of-scope? */
+ if (lease && ((lease->addr & iface->dhcpv4_mask.s_addr) !=
(iface->dhcpv4_start_ip.s_addr & iface->dhcpv4_mask.s_addr)) &&
- !(a->flags & OAF_STATIC)) {
+ !(lease->flags & OAF_STATIC)) {
/* Try to reassign to an address that is in-scope */
- list_del_init(&a->head);
- a->addr = INADDR_ANY;
- if (!dhcpv4_assign(iface, a, req_addr)) {
- free_assignment(a);
- a = NULL;
+ list_del_init(&lease->head);
+ lease->addr = INADDR_ANY;
+ if (!dhcpv4_assign(iface, lease, req_addr)) {
+ dhcpv4_free_lease(lease);
+ lease = NULL;
break;
}
}
- if (!a) {
+ if (!lease) {
/* Create new binding */
- a = alloc_assignment(0);
- if (!a) {
+ lease = dhcpv4_alloc_lease(iface, req_mac, ETH_ALEN);
+ if (!lease) {
warn("Failed to allocate memory for DHCPv4 lease on interface %s", iface->ifname);
return NULL;
}
- memcpy(a->hwaddr, req_mac, sizeof(a->hwaddr));
- a->dhcp_free_cb = dhcpv4_free_assignment;
- a->iface = iface;
- a->flags = OAF_DHCPV4;
-
/* static lease => infinite (0), else a placeholder */
- a->valid_until = lease_cfg ? 0 : now;
- a->addr = lease_cfg ? lease_cfg->ipaddr : INADDR_ANY;
+ lease->valid_until = lease_cfg ? 0 : now;
+ lease->addr = lease_cfg ? lease_cfg->ipaddr : INADDR_ANY;
- if (!dhcpv4_assign(iface, a, req_addr)) {
- free_assignment(a);
+ if (!dhcpv4_assign(iface, lease, req_addr)) {
+ dhcpv4_free_lease(lease);
return NULL;
}
if (lease_cfg) {
- a->flags |= OAF_STATIC;
+ lease->flags |= OAF_STATIC;
if (lease_cfg->hostname)
- a->hostname = strdup(lease_cfg->hostname);
+ lease->hostname = strdup(lease_cfg->hostname);
- list_add(&a->lease_cfg_list, &lease_cfg->assignments);
- a->lease_cfg = lease_cfg;
+ lease_cfg->dhcpv4_lease = lease;
+ lease->lease_cfg = lease_cfg;
}
}
/* See if we need to clamp the requested leasetime */
uint32_t max_leasetime;
- if (a->lease_cfg && a->lease_cfg->leasetime)
- max_leasetime = a->lease_cfg->leasetime;
+ if (lease->lease_cfg && lease->lease_cfg->leasetime)
+ max_leasetime = lease->lease_cfg->leasetime;
else
max_leasetime = iface->dhcp_leasetime;
@@ -565,41 +583,41 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg req_msg, const uint8_t *re
*req_leasetime = max_leasetime;
if (req_msg == DHCPV4_MSG_DISCOVER) {
- a->flags &= ~OAF_BOUND;
+ lease->flags &= ~OAF_BOUND;
*reply_incl_fr = req_accept_fr;
- a->valid_until = now;
+ lease->valid_until = now;
break;
}
- if ((!(a->flags & OAF_STATIC) || !a->hostname) && req_hostname_len > 0) {
- char *new_name = realloc(a->hostname, req_hostname_len + 1);
+ if ((!(lease->flags & OAF_STATIC) || !lease->hostname) && req_hostname_len > 0) {
+ char *new_name = realloc(lease->hostname, req_hostname_len + 1);
if (new_name) {
- a->hostname = new_name;
- memcpy(a->hostname, req_hostname, req_hostname_len);
- a->hostname[req_hostname_len] = 0;
+ lease->hostname = new_name;
+ memcpy(lease->hostname, req_hostname, req_hostname_len);
+ lease->hostname[req_hostname_len] = 0;
- if (odhcpd_valid_hostname(a->hostname))
- a->flags &= ~OAF_BROKEN_HOSTNAME;
+ if (odhcpd_valid_hostname(lease->hostname))
+ lease->flags &= ~OAF_BROKEN_HOSTNAME;
else
- a->flags |= OAF_BROKEN_HOSTNAME;
+ lease->flags |= OAF_BROKEN_HOSTNAME;
}
}
*reply_incl_fr = false;
- if (!(a->flags & OAF_BOUND)) {
+ if (!(lease->flags & OAF_BOUND)) {
/* This is the client's first request for the address */
if (req_accept_fr) {
- a->accept_fr_nonce = true;
+ lease->accept_fr_nonce = true;
*reply_incl_fr = true;
- odhcpd_urandom(a->key, sizeof(a->key));
+ odhcpd_urandom(lease->key, sizeof(lease->key));
}
- a->flags |= OAF_BOUND;
+ lease->flags |= OAF_BOUND;
}
if (*req_leasetime == UINT32_MAX)
- a->valid_until = 0;
+ lease->valid_until = 0;
else
- a->valid_until = (time_t)(now + *req_leasetime);
+ lease->valid_until = (time_t)(now + *req_leasetime);
break;
default:
@@ -607,7 +625,7 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg req_msg, const uint8_t *re
}
dhcpv6_ia_write_statefile();
- return a;
+ return lease;
}
static void dhcpv4_set_dest_addr(const struct interface *iface,
@@ -853,7 +871,7 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
/* Misc */
struct sockaddr_in dest_addr;
bool reply_incl_fr = false;
- struct dhcp_assignment *a = NULL;
+ struct dhcpv4_lease *lease = NULL;
uint32_t fr_serverid = INADDR_ANY;
if (iface->dhcpv4 == MODE_DISABLED)
@@ -944,9 +962,9 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
case DHCPV4_MSG_DISCOVER:
_fallthrough;
case DHCPV4_MSG_REQUEST:
- a = dhcpv4_lease(iface, req_msg, req->chaddr, req_addr,
- &req_leasetime, req_hostname, req_hostname_len,
- req_accept_fr, &reply_incl_fr, &fr_serverid);
+ lease = dhcpv4_lease(iface, req_msg, req->chaddr, req_addr,
+ &req_leasetime, req_hostname, req_hostname_len,
+ req_accept_fr, &reply_incl_fr, &fr_serverid);
break;
default:
return;
@@ -955,19 +973,19 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
/* We are at the point where we know the client expects a reply */
switch (req_msg) {
case DHCPV4_MSG_DISCOVER:
- if (!a)
+ if (!lease)
return;
reply_msg.data = DHCPV4_MSG_OFFER;
break;
case DHCPV4_MSG_REQUEST:
- if (!a) {
+ if (!lease) {
reply_msg.data = DHCPV4_MSG_NAK;
break;
}
- if ((req_addr && req_addr != a->addr) ||
- (req->ciaddr.s_addr && req->ciaddr.s_addr != a->addr)) {
+ if ((req_addr && req_addr != lease->addr) ||
+ (req->ciaddr.s_addr && req->ciaddr.s_addr != lease->addr)) {
reply_msg.data = DHCPV4_MSG_NAK;
/*
* DHCP client requested an IP which we can't offer to him. Probably the
@@ -1000,7 +1018,7 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
switch (opt) {
case DHCPV4_OPT_NETMASK:
- if (!a)
+ if (!lease)
break;
reply_netmask.data = iface->dhcpv4_mask.s_addr;
iov[IOV_NETMASK].iov_len = sizeof(reply_netmask);
@@ -1031,11 +1049,11 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
break;
case DHCPV4_OPT_HOSTNAME:
- if (!a || !a->hostname)
+ if (!lease || !lease->hostname)
break;
- reply_hostname.len = strlen(a->hostname);
+ reply_hostname.len = strlen(lease->hostname);
iov[IOV_HOSTNAME].iov_len = sizeof(reply_hostname);
- iov[IOV_HOSTNAME_NAME].iov_base = a->hostname;
+ iov[IOV_HOSTNAME_NAME].iov_base = lease->hostname;
iov[IOV_HOSTNAME_NAME].iov_len = reply_hostname.len;
break;
@@ -1053,45 +1071,45 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
break;
case DHCPV4_OPT_BROADCAST:
- if (!a || iface->dhcpv4_bcast.s_addr == INADDR_ANY)
+ if (!lease || iface->dhcpv4_bcast.s_addr == INADDR_ANY)
break;
reply_broadcast.data = iface->dhcpv4_bcast.s_addr;
iov[IOV_BROADCAST].iov_len = sizeof(reply_broadcast);
break;
case DHCPV4_OPT_NTPSERVER:
- if (!a)
+ if (!lease)
break;
iov[IOV_NTP].iov_len = sizeof(reply_ntp);
iov[IOV_NTP_ADDR].iov_len = iface->dhcpv4_ntp_cnt * sizeof(*iface->dhcpv4_ntp);
break;
case DHCPV4_OPT_LEASETIME:
- if (!a)
+ if (!lease)
break;
reply_leasetime.data = htonl(req_leasetime);
iov[IOV_LEASETIME].iov_len = sizeof(reply_leasetime);
break;
case DHCPV4_OPT_RENEW:
- if (!a || req_leasetime == UINT32_MAX)
+ if (!lease || req_leasetime == UINT32_MAX)
break;
reply_renew.data = htonl(500 * req_leasetime / 1000);
iov[IOV_RENEW].iov_len = sizeof(reply_renew);
break;
case DHCPV4_OPT_REBIND:
- if (!a || req_leasetime == UINT32_MAX)
+ if (!lease || req_leasetime == UINT32_MAX)
break;
reply_rebind.data = htonl(875 * req_leasetime / 1000);
iov[IOV_REBIND].iov_len = sizeof(reply_rebind);
break;
case DHCPV4_OPT_AUTHENTICATION:
- if (!a || !reply_incl_fr || req_msg != DHCPV4_MSG_REQUEST)
+ if (!lease || !reply_incl_fr || req_msg != DHCPV4_MSG_REQUEST)
break;
- memcpy(reply_auth_body.key, a->key, sizeof(reply_auth_body.key));
+ memcpy(reply_auth_body.key, lease->key, sizeof(reply_auth_body.key));
reply_auth_body.replay[0] = htonl(time(NULL));
reply_auth_body.replay[1] = htonl(++serial);
iov[IOV_AUTH].iov_len = sizeof(reply_auth);
@@ -1126,7 +1144,7 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
break;
case DHCPV4_OPT_FORCERENEW_NONCE_CAPABLE:
- if (!a || !reply_incl_fr || req_msg == DHCPV4_MSG_REQUEST)
+ if (!lease || !reply_incl_fr || req_msg == DHCPV4_MSG_REQUEST)
break;
iov[IOV_FR_NONCE_CAP].iov_len = sizeof(reply_fr_nonce_cap);
@@ -1136,7 +1154,7 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
struct dhcpv4_dnr *dnrs;
size_t dnrs_len = 0;
- if (!a || reply_dnr.len > 0)
+ if (!lease || reply_dnr.len > 0)
break;
for (size_t i = 0; i < iface->dnr_cnt; i++) {
@@ -1203,8 +1221,8 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
}
}
- if (a)
- reply.yiaddr.s_addr = a->addr;
+ if (lease)
+ reply.yiaddr.s_addr = lease->addr;
memcpy(reply.chaddr, req->chaddr, sizeof(reply.chaddr));
dhcpv4_set_dest_addr(iface, reply_msg.data, req, &reply, src_addr, &dest_addr);
@@ -1223,10 +1241,10 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
"ff:ff:ff:ff:ff:ff": odhcpd_print_mac(req->chaddr, req->hlen),
inet_ntoa(dest_addr.sin_addr));
- if (reply_msg.data == DHCPV4_MSG_ACK && a)
+ if (reply_msg.data == DHCPV4_MSG_ACK && lease)
ubus_bcast_dhcp_event("dhcp.ack", req->chaddr,
- (struct in_addr *)&a->addr,
- a->hostname, iface->ifname);
+ (struct in_addr *)&lease->addr,
+ lease->hostname, iface->ifname);
}
/* Handler for DHCPv4 messages */
@@ -1334,9 +1352,9 @@ int dhcpv4_setup_interface(struct interface *iface, bool enable)
}
if (!enable || iface->dhcpv4 == MODE_DISABLED) {
- while (!list_empty(&iface->dhcpv4_assignments))
- free_assignment(list_first_entry(&iface->dhcpv4_assignments,
- struct dhcp_assignment, head));
+ while (!list_empty(&iface->dhcpv4_leases))
+ dhcpv4_free_lease(list_first_entry(&iface->dhcpv4_leases,
+ struct dhcpv4_lease, head));
return 0;
}
@@ -1420,7 +1438,7 @@ static void dhcpv4_addrlist_change(struct interface *iface)
{
struct odhcpd_ipaddr ip;
struct odhcpd_ref_ip *a;
- struct dhcp_assignment *c;
+ struct dhcpv4_lease *lease;
uint32_t mask = iface->dhcpv4_mask.s_addr;
memset(&ip, 0, sizeof(ip));
@@ -1447,12 +1465,12 @@ static void dhcpv4_addrlist_change(struct interface *iface)
return;
}
- list_for_each_entry(c, &iface->dhcpv4_assignments, head) {
- if ((c->flags & OAF_BOUND) && c->fr_ip && !c->fr_cnt) {
- if (c->accept_fr_nonce || iface->dhcpv4_forcereconf)
- dhcpv4_fr_rand_delay(c);
+ list_for_each_entry(lease, &iface->dhcpv4_leases, head) {
+ if ((lease->flags & OAF_BOUND) && lease->fr_ip && !lease->fr_cnt) {
+ if (lease->accept_fr_nonce || iface->dhcpv4_forcereconf)
+ dhcpv4_fr_rand_delay(lease);
else
- dhcpv4_fr_stop(c);
+ dhcpv4_fr_stop(lease);
}
}
}
@@ -1482,17 +1500,17 @@ static void dhcpv4_valid_until_cb(struct uloop_timeout *event)
time_t now = odhcpd_time();
avl_for_each_element(&interfaces, iface, avl) {
- struct dhcp_assignment *a, *n;
+ struct dhcpv4_lease *lease, *tmp;
if (iface->dhcpv4 != MODE_SERVER)
continue;
- list_for_each_entry_safe(a, n, &iface->dhcpv4_assignments, head) {
- if (!INFINITE_VALID(a->valid_until) && a->valid_until < now) {
- ubus_bcast_dhcp_event("dhcp.expire", a->hwaddr,
- (struct in_addr *)&a->addr,
- a->hostname, iface->ifname);
- free_assignment(a);
+ list_for_each_entry_safe(lease, tmp, &iface->dhcpv4_leases, head) {
+ if (!INFINITE_VALID(lease->valid_until) && lease->valid_until < now) {
+ ubus_bcast_dhcp_event("dhcp.expire", lease->hwaddr,
+ (struct in_addr *)&lease->addr,
+ lease->hostname, iface->ifname);
+ dhcpv4_free_lease(lease);
}
}
}
diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c
index 73798c5..3046606 100644
--- a/src/dhcpv6-ia.c
+++ b/src/dhcpv6-ia.c
@@ -462,13 +462,13 @@ static void dhcpv6_ia_write_hostsfile(time_t now)
}
if (ctxt.iface->dhcpv4 == MODE_SERVER) {
- struct dhcp_assignment *c;
+ struct dhcpv4_lease *c;
- list_for_each_entry(c, &ctxt.iface->dhcpv4_assignments, head) {
+ list_for_each_entry(c, &ctxt.iface->dhcpv4_leases, head) {
if (!(c->flags & OAF_BOUND))
continue;
- char ipbuf[INET6_ADDRSTRLEN];
+ char ipbuf[INET_ADDRSTRLEN];
struct in_addr addr = {.s_addr = c->addr};
inet_ntop(AF_INET, &addr, ipbuf, sizeof(ipbuf) - 1);
@@ -585,9 +585,9 @@ void dhcpv6_ia_write_statefile(void)
}
if (ctxt.iface->dhcpv4 == MODE_SERVER) {
- struct dhcp_assignment *c;
+ struct dhcpv4_lease *c;
- list_for_each_entry(c, &ctxt.iface->dhcpv4_assignments, head) {
+ list_for_each_entry(c, &ctxt.iface->dhcpv4_leases, head) {
if (!(c->flags & OAF_BOUND))
continue;
diff --git a/src/odhcpd.h b/src/odhcpd.h
index 3d5d707..395cee5 100644
--- a/src/odhcpd.h
+++ b/src/odhcpd.h
@@ -177,9 +177,8 @@ enum odhcpd_assignment_flags {
OAF_BOUND = (1 << 1),
OAF_STATIC = (1 << 2),
OAF_BROKEN_HOSTNAME = (1 << 3),
- OAF_DHCPV4 = (1 << 4),
- OAF_DHCPV6_NA = (1 << 5),
- OAF_DHCPV6_PD = (1 << 6),
+ OAF_DHCPV6_NA = (1 << 4),
+ OAF_DHCPV6_PD = (1 << 5),
};
/* 2-byte type + 128-byte DUID, RFC8415, §11.1 */
@@ -229,22 +228,44 @@ struct duid {
bool iaid_set;
};
+struct odhcpd_ref_ip;
+
+struct dhcpv4_lease {
+ struct list_head head; // struct interface->dhcpv4_assignments
+
+ struct interface *iface; // assignment interface, non-null
+ struct lease_cfg *lease_cfg; // host lease cfg, nullable
+
+ uint32_t addr; // client IP address
+ unsigned int flags; // OAF_*
+ time_t valid_until; // CLOCK_MONOTONIC time, 0 = inf
+ char *hostname; // client hostname
+ size_t hwaddr_len; // hwaddr length
+ uint8_t hwaddr[6]; // hwaddr (only MAC supported)
+
+ // ForceRenew Nonce - RFC6704 §3.1.2
+ struct uloop_timeout fr_timer; // FR message transmission timer
+ bool accept_fr_nonce; // FR client support
+ unsigned fr_cnt; // FR messages sent
+ uint8_t key[16]; // FR nonce
+ struct odhcpd_ref_ip *fr_ip; // FR message old serverid/IP
+};
+
/* This corresponds to a UCI host section, i.e. a static lease cfg */
struct lease_cfg {
struct vlist_node node;
struct list_head assignments;
+ struct dhcpv4_lease *dhcpv4_lease;
uint32_t ipaddr;
uint64_t hostid;
size_t mac_count;
struct ether_addr *macs;
size_t duid_count;
struct duid *duids;
- uint32_t leasetime;
+ uint32_t leasetime; // duration of granted leases, UINT32_MAX = inf
char *hostname;
};
-struct odhcpd_ref_ip;
-
struct dhcp_assignment {
struct list_head head;
struct list_head lease_cfg_list;
@@ -258,14 +279,12 @@ struct dhcp_assignment {
time_t valid_until;
time_t preferred_until;
- // ForceRenew - RFC6704 §3.1.2 (IPv4), RFC8415 (IPv6), §20.4, §21.11
+ // ForceRenew Nonce - RFC8415 §20.4, §21.11
struct uloop_timeout fr_timer;
bool accept_fr_nonce;
int fr_cnt;
uint8_t key[16];
- struct odhcpd_ref_ip *fr_ip;
- uint32_t addr;
union {
uint64_t assigned_host_id;
uint32_t assigned_subnet_id;
@@ -281,13 +300,10 @@ struct dhcp_assignment {
uint32_t leasetime;
char *hostname;
- uint8_t hwaddr[6];
-
uint16_t clid_len;
uint8_t clid_data[];
};
-
// DNR - RFC9463
struct dnr_options {
uint16_t priority;
@@ -348,7 +364,7 @@ struct interface {
// DHCPv4 runtime data
struct odhcpd_event dhcpv4_event;
- struct list_head dhcpv4_assignments;
+ struct list_head dhcpv4_leases;
struct list_head dhcpv4_fr_ips;
// Managed PD
@@ -619,14 +635,20 @@ int netlink_init(void);
int router_init(void);
int dhcpv6_init(void);
int ndp_init(void);
+
#ifdef DHCPV4_SUPPORT
int dhcpv4_init(void);
-
+void dhcpv4_free_lease(struct dhcpv4_lease *a);
int dhcpv4_setup_interface(struct interface *iface, bool enable);
void dhcpv4_handle_msg(void *addr, void *data, size_t len,
struct interface *iface, _unused void *dest_addr,
send_reply_cb_t send_reply, void *opaque);
-#endif
+#else
+static inline void dhcpv4_free_lease(struct dhcpv4_lease *lease) {
+ error("Trying to free IPv4 assignment 0x%p", lease);
+}
+#endif /* DHCPV4_SUPPORT */
+
int router_setup_interface(struct interface *iface, bool enable);
int dhcpv6_setup_interface(struct interface *iface, bool enable);
int ndp_setup_interface(struct interface *iface, bool enable);
diff --git a/src/ubus.c b/src/ubus.c
index 9cb94a5..f1ce074 100644
--- a/src/ubus.c
+++ b/src/ubus.c
@@ -36,8 +36,8 @@ static int handle_dhcpv4_leases(struct ubus_context *ctx, _unused struct ubus_ob
void *i = blobmsg_open_table(&b, iface->ifname);
void *j = blobmsg_open_array(&b, "leases");
- struct dhcp_assignment *c;
- list_for_each_entry(c, &iface->dhcpv4_assignments, head) {
+ struct dhcpv4_lease *c;
+ list_for_each_entry(c, &iface->dhcpv4_leases, head) {
if (!INFINITE_VALID(c->valid_until) && c->valid_until < now)
continue;