From 5557d01c7170c5ff19dcda20c62e4bb3aa483689 Mon Sep 17 00:00:00 2001 From: Erin Date: Mon, 12 Jan 2026 11:17:26 +0100 Subject: [PATCH] send DHCPv6 lease events over U-Bus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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á --- src/dhcpv6-ia.c | 12 ++++++- src/odhcpd.h | 6 ++++ src/ubus.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index 8cc95156..ea8a63bf 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 916d6dae..ee4b6f55 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 00fd171b..4b2c6c0b 100644 --- a/src/ubus.c +++ b/src/ubus.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -6,7 +7,9 @@ #include #include +#include +#include "libubox/blobmsg.h" #include "odhcpd.h" #include "dhcpv6.h" #include "dhcpv4.h" @@ -416,6 +419,88 @@ 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) + 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); + + struct in6_addr prefix = entry->addr.in6; + if (assignment->flags & OAF_DHCPV6_NA) { + prefix.s6_addr32[2] = htonl(assignment->assigned_host_id >> 32); + prefix.s6_addr32[3] = htonl(assignment->assigned_host_id & UINT32_MAX); + } else { + prefix.s6_addr32[1] |= htonl(assignment->assigned_subnet_id); + prefix.s6_addr32[2] = 0; + prefix.s6_addr32[3] = 0; + } + + char *addr_buf = blobmsg_alloc_string_buffer(&b, "address", INET6_ADDRSTRLEN); + memset(addr_buf, 0, INET6_ADDRSTRLEN); + + (void) inet_ntop(AF_INET6, &prefix, addr_buf, INET6_ADDRSTRLEN); + blobmsg_add_string_buffer(&b); + + if (entry->prefix != 128) + blobmsg_add_u8(&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) {