[PATCH] odhcpd: send DHCPv6 lease events over U-Bus

Erin Kalousková erin.kalouskova at nic.cz
Wed Nov 26 04:23:10 PST 2025


odhcpd currently only sends DHCPv4 lease events over U-Bus. For DHCPv6 
the only option was to use a trigger hook script and then parse a lease 
file and diff it against previous version.
This made it very inconvenient to use in cases like adding DHCP hosts to 
DNS server and the behavior is also inconsistent with DHCPv4 counterpart.
Format of the U-Bus message is based on reply of `ipv6leases` U-Bus method.
This patch is largely based on 
https://github.com/mikma/openwrt-odhcpd/commit/b796d613dfe74fd36a8df8564c51907321f6a187
Signed-off-by: Erin Kalousková <erin.kalouskova at nic.cz>
---
src/dhcpv6-ia.c | 12 +++++++-
src/odhcpd.h | 6 ++++
src/ubus.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 99 insertions(+), 1 deletion(-)
diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c
index 8cc9515..ea8a63b 100644
--- a/src/dhcpv6-ia.c
+++ b/src/dhcpv6-ia.c
@@ -208,7 +208,7 @@ static void dhcpv6_ia_free_assignment(struct 
dhcp_assignment *a)
close(a->managed_sock.fd.fd);
}
- if ((a->flags & OAF_BOUND) && (a->flags & OAF_DHCPV6_PD))
+ if (a->flags & OAF_BOUND)
apply_lease(a, false);
if (a->fr_cnt)
@@ -591,6 +591,10 @@ void dhcpv6_ia_write_statefile(void)
static void __apply_lease(struct dhcp_assignment *a,
struct odhcpd_ipaddr *addrs, ssize_t addr_len, bool add)
{
+ #ifdef WITH_UBUS
+ ubus_bcast_dhcp6_event(add ? "dhcpv6.ack" : "dhcpv6.release", a, 
addr_len, addrs);
+ #endif
+
if (a->flags & OAF_DHCPV6_NA)
return;
@@ -844,6 +848,9 @@ static bool assign_na(struct interface *iface, 
struct dhcp_assignment *a)
list_for_each_entry(c, &iface->ia_assignments, head) {
if (!(c->flags & OAF_DHCPV6_NA) || c->assigned_host_id > 
a->assigned_host_id ) {
list_add_tail(&a->head, &c->head);
+ if (a->flags &OAF_BOUND)
+ apply_lease(a, true);
+
return true;
} else if (c->assigned_host_id == a->assigned_host_id)
return false;
@@ -891,6 +898,9 @@ static bool assign_na(struct interface *iface, 
struct dhcp_assignment *a)
if (!(c->flags & OAF_DHCPV6_NA) || c->assigned_host_id > try) {
a->assigned_host_id = try;
list_add_tail(&a->head, &c->head);
+ if (a->flags &OAF_BOUND)
+ apply_lease(a, true);
+
return true;
} else if (c->assigned_host_id == try)
break;
diff --git a/src/odhcpd.h b/src/odhcpd.h
index 916d6da..ee4b6f5 100644
--- a/src/odhcpd.h
+++ b/src/odhcpd.h
@@ -510,6 +510,12 @@ void ubus_apply_network(void);
bool ubus_has_prefix(const char *name, const char *ifname);
void ubus_bcast_dhcp_event(const char *type, const uint8_t *mac, const 
size_t mac_len,
const struct in_addr *addr, const char *name, const char *interface);
+void ubus_bcast_dhcp6_event(
+ const char* type,
+ const struct dhcp_assignment *assignment,
+ size_t addrs_len,
+ const struct odhcpd_ipaddr addrs[static addrs_len]
+);
#endif
ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct 
interface *iface,
diff --git a/src/ubus.c b/src/ubus.c
index 00fd171..f5ebc95 100644
--- a/src/ubus.c
+++ b/src/ubus.c
@@ -1,3 +1,4 @@
+#include <stdint.h>
#include <syslog.h>
#include <libubus.h>
#include <libubox/uloop.h>
@@ -6,7 +7,9 @@
#include <inttypes.h>
#include <libubox/utils.h>
+#include <time.h>
+#include "libubox/blobmsg.h"
#include "odhcpd.h"
#include "dhcpv6.h"
#include "dhcpv4.h"
@@ -416,6 +419,85 @@ void ubus_bcast_dhcp_event(const char *type, const 
uint8_t *mac,
ubus_notify(ubus, &main_object, type, b.head, -1);
}
+void ubus_bcast_dhcp6_event(
+ const char* type,
+ const struct dhcp_assignment *assignment,
+ size_t addrs_len_,
+ const struct odhcpd_ipaddr addrs[static addrs_len_]
+) {
+ if (
+ type == NULL
+ || assignment == NULL
+ || (addrs_len_ != 0 && addrs == NULL)
+ || addrs_len_ > PTRDIFF_MAX
+ ) {
+ fputs(stderr, "[ubus_bcast_dhcp6_event]: parameter assertion failed");
+ return;
+ }
+
+ ptrdiff_t addrs_len = addrs_len_;
+
+ if (ubus == NULL || !main_object.has_subscribers)
+ return;
+
+ blob_buf_init(&b, 0);
+ if (assignment->hostname != NULL)
+ blobmsg_add_string(&b, "hostname", assignment->hostname);
+
+ if (assignment->iface != NULL && assignment->iface->ifname != NULL)
+ blobmsg_add_string(&b, "interface", assignment->iface->ifname);
+
+ if (assignment->iaid != 0)
+ blobmsg_add_u32(&b, "iaid", ntohl(assignment->iaid));
+
+ if (assignment->clid_len != 0) {
+ char *duid_buf = blobmsg_alloc_string_buffer(&b, "duid", 
assignment->clid_len * 2 + 1);
+ odhcpd_hexlify(duid_buf, assignment->clid_data, assignment->clid_len);
+ blobmsg_add_string_buffer(&b);
+ }
+
+ blobmsg_add_u8(&b, "accept-reconf", assignment->accept_fr_nonce);
+ blobmsg_add_u64(&b, "valid-until", assignment->valid_until);
+ blobmsg_add_u64(&b, "preferred-until", assignment->preferred_until);
+
+ if (assignment->flags & OAF_DHCPV6_NA)
+ blobmsg_add_u64(&b, "assigned", assignment->assigned_host_id);
+ else
+ blobmsg_add_u16(&b, "assigned", assignment->assigned_subnet_id);
+
+ {
+ void *array_h = blobmsg_open_array(&b, "flags");
+ if (assignment->flags & OAF_BOUND)
+ blobmsg_add_string(&b, NULL, "bound");
+
+ if (assignment->flags & OAF_STATIC)
+ blobmsg_add_string(&b, NULL, "static");
+
+ blobmsg_close_array(&b, array_h);
+ }
+
+ void *array_h = blobmsg_open_array(&b, assignment->flags & 
OAF_DHCPV6_NA ? "ipv6-addr" : "ipv6-prefix");
+ for (ptrdiff_t ix = 0; ix != addrs_len; ++ix) {
+ const struct odhcpd_ipaddr *entry = &addrs[ix];
+
+ void *table_h = blobmsg_open_table(&b, NULL);
+
+ char *addr_buf = blobmsg_alloc_string_buffer(&b, "address", 
INET6_ADDRSTRLEN);
+ memset(addr_buf, 0, INET6_ADDRSTRLEN);
+
+ (void) inet_ntop(AF_INET6, &entry->addr.in6, addr_buf, INET6_ADDRSTRLEN);
+ blobmsg_add_string_buffer(&b);
+
+ if (entry->prefix != 128)
+ blobmsg_add_u32(&b, "prefix-length", entry->prefix);
+
+ blobmsg_close_table(&b, table_h);
+ }
+
+ blobmsg_close_array(&b, array_h);
+ ubus_notify(ubus, &main_object, type, b.head, -1);
+}
+
static void handle_event(_unused struct ubus_context *ctx, _unused 
struct ubus_event_handler *ev,
_unused const char *type, struct blob_attr *msg)
{
-- 
2.52.0


-------------- next part --------------
A non-text attachment was scrubbed...
Name: OpenPGP_0x399339FCF5EEBDCF.asc
Type: application/pgp-keys
Size: 656 bytes
Desc: OpenPGP public key
URL: <http://lists.openwrt.org/pipermail/openwrt-devel/attachments/20251126/4820bd8a/attachment.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: OpenPGP_signature.asc
Type: application/pgp-signature
Size: 236 bytes
Desc: OpenPGP digital signature
URL: <http://lists.openwrt.org/pipermail/openwrt-devel/attachments/20251126/4820bd8a/attachment.sig>


More information about the openwrt-devel mailing list