From 180fcb409e3f0fcc11738010dc1663ca8677703c Mon Sep 17 00:00:00 2001 From: Monviech Date: Fri, 8 May 2026 15:03:51 +0200 Subject: [PATCH 1/4] Services: Kea DHCPv4/6: Build reservation status from control socket output, so it matches the scope of individual subnets as well. Add client-id since it's relevant for IPv4 leases as well in default configuration. We return an array now, change frontend detection if it's dynamic or static lease Missed a closing bracket Typo in client_id Remove unused imports in LeasesController Add comment to build_reserved_matches() to explain why the subnet-id logic exists now --- .../OPNsense/Kea/Api/LeasesController.php | 37 ----------- .../mvc/app/views/OPNsense/Kea/leases4.volt | 5 +- .../mvc/app/views/OPNsense/Kea/leases6.volt | 8 +-- src/opnsense/scripts/kea/get_kea_leases.py | 64 ++++++++++++++++++- 4 files changed, 69 insertions(+), 45 deletions(-) diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Kea/Api/LeasesController.php b/src/opnsense/mvc/app/controllers/OPNsense/Kea/Api/LeasesController.php index 2cbbcfe5f00..796f335b491 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Kea/Api/LeasesController.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Kea/Api/LeasesController.php @@ -31,8 +31,6 @@ use OPNsense\Base\ApiControllerBase; use OPNsense\Core\Backend; use OPNsense\Core\Config; -use OPNsense\Kea\KeaDhcpv4; -use OPNsense\Kea\KeaDhcpv6; use OPNsense\Base\UserException; abstract class LeasesController extends ApiControllerBase @@ -50,7 +48,6 @@ public function searchAction() $interfaces = []; $leases = json_decode($backend->configdpRun($this->configd_fetch_leases), true) ?? []; - $ifconfig = json_decode($backend->configdRun('interface list ifconfig'), true); $mac_db = json_decode($backend->configdRun('interface list macdb'), true) ?? []; $ifmap = []; @@ -61,23 +58,6 @@ public function searchAction() ]; } - // Mark records as reserved based on hwaddr (IPv4) or duid/hwaddr (IPv6) match - $resv4 = []; - $resv6 = []; - - foreach ((new KeaDhcpv4())->reservations->reservation->iterateItems() as $reservation) { - $resv4[strtolower($reservation->hw_address->getValue())] = 'hwaddr'; - } - - foreach ((new KeaDhcpv6())->reservations->reservation->iterateItems() as $reservation) { - // At least one of these is required in the model - if (!$reservation->duid->isEmpty()) { - $resv6[strtolower($reservation->duid->getValue())] = 'duid'; - } elseif (!$reservation->hw_address->isEmpty()) { - $resv6[strtolower($reservation->hw_address->getValue())] = 'hwaddr'; - } - } - if (!empty($leases) && isset($leases['records'])) { $records = $leases['records']; foreach ($records as &$record) { @@ -92,23 +72,6 @@ public function searchAction() // Vendor $mac = strtoupper(substr(str_replace(':', '', $record['hwaddr']), 0, 6)); $record['mac_info'] = isset($mac_db[$mac]) ? $mac_db[$mac] : ''; - // Reservation - $record['is_reserved'] = ''; - $addr = $record['address'] ?? ''; - if (strpos($addr, ':') !== false) { - $duid = strtolower($record['duid'] ?? ''); - $mac = strtolower($record['hwaddr'] ?? ''); - if (isset($resv6[$duid])) { - $record['is_reserved'] = $resv6[$duid]; - } elseif (isset($resv6[$mac])) { - $record['is_reserved'] = $resv6[$mac]; - } - } else { - $mac = strtolower($record['hwaddr'] ?? ''); - if (isset($resv4[$mac])) { - $record['is_reserved'] = $resv4[$mac]; - } - } } } else { $records = []; diff --git a/src/opnsense/mvc/app/views/OPNsense/Kea/leases4.volt b/src/opnsense/mvc/app/views/OPNsense/Kea/leases4.volt index c01ee5c707f..085db4a5d52 100644 --- a/src/opnsense/mvc/app/views/OPNsense/Kea/leases4.volt +++ b/src/opnsense/mvc/app/views/OPNsense/Kea/leases4.volt @@ -87,7 +87,7 @@ return moment.unix(row[column.id]).local().format('YYYY-MM-DD HH:mm:ss'); }, "reservation": function (column, row) { - return row.is_reserved !== '' + return row.is_reserved.length > 0 ? "{{ lang._('static') }}" : "{{ lang._('dynamic') }}"; }, @@ -103,7 +103,7 @@ let reservationBtn; - if (row.is_reserved !== '') { + if (row.is_reserved.length > 0) { reservationBtn = $(`