From 4df45c8c37227b67319ca39ee4eb2d21966ee693 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20H=C3=A4rdeman?= Date: Thu, 9 Oct 2025 07:13:58 +0200 Subject: [PATCH] dhcpv4: create struct dhcpv4_lease MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 Link: https://github.com/openwrt/odhcpd/pull/292 Signed-off-by: Álvaro Fernández Rojas --- src/config.c | 34 ++--- src/dhcpv4.c | 372 +++++++++++++++++++++++++----------------------- src/dhcpv6-ia.c | 10 +- src/odhcpd.h | 52 +++++-- src/ubus.c | 4 +- 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; -- 2.30.2