luci-mod-status: improve detection of static cfg
authorDavid Härdeman <david@hardeman.nu>
Sun, 9 Nov 2025 10:36:03 +0000 (11:36 +0100)
committerPaul Donald <newtwen+github@gmail.com>
Tue, 18 Nov 2025 22:24:22 +0000 (23:24 +0100)
Note that "duid" from UCI can be *either* "<DUID>" or "<DUID>%<IAID>",
so update the code to handle both cases.

Also, make sure the "Reserve IP" button is disabled if the "duid" is
unknown (can only happen if dnsmasq is responsible for DHCPv6) and that
DUIDs/IAIDs are always rendered in lowercase in the UI.

Signed-off-by: David Härdeman <david@hardeman.nu>
modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/40_dhcp.js

index b7f84c66e04abd87cc7158f358f99bd4c4346411..db338c7e174fbeb7c742f0b1bc90af22e84e4c2d 100644 (file)
@@ -21,6 +21,7 @@ return baseclass.extend({
 
        isMACStatic: {},
        isDUIDStatic: {},
+       isDUIDIAIDStatic: {},
 
        load() {
                return Promise.all([
@@ -63,9 +64,10 @@ return baseclass.extend({
                const ip6arr = ip6addr ? validation.parseIPv6(ip6addr) : null;
 
                // Combine DUID and IAID if both available
-               let duid_iaid = lease.duid ? lease.duid.toUpperCase() : null;
-               if (duid_iaid && lease.iaid)
-                       duid_iaid += `%${lease.iaid}`;
+               // (note that we know that lease.duid is set here)
+               let duid_iaid = lease.duid.toLowerCase();
+               if (lease.iaid)
+                       duid_iaid += `%${lease.iaid}`.toLowerCase();
 
                uci.set('dhcp', cfg, 'name', lease.hostname);
                uci.set('dhcp', cfg, 'duid', [duid_iaid]);
@@ -85,24 +87,20 @@ return baseclass.extend({
                if (leases.length == 0 && leases6.length == 0)
                        return E([]);
                const machints = host_hints.getMACHints(false);
-               const hosts = uci.sections('dhcp', 'host');
                const isReadonlyView = !L.hasViewPermission();
 
                for (const host of uci.sections('dhcp', 'host')) {
 
-                       if (host.mac) {
-                               for (const mac of L.toArray(host.mac).map(m => m.toUpperCase())) {
-                                       this.isMACStatic[mac] = true;
-                               }
-                       }
-                       if (host.duid) {
-                               if (Array.isArray(host.duid)){
-                                       host.duid.map(m => {
-                                               this.isDUIDStatic[m.toUpperCase()] = true;
-                                       })
-                               } else {
-                                       this.isDUIDStatic[host.duid.toUpperCase()] = true;
-                               }
+                       for (const mac of L.toArray(host.mac).map(m => m.toLowerCase()))
+                               this.isMACStatic[mac] = true;
+
+                       for (const duid_iaid of L.toArray(host.duid).map(m => m.toLowerCase())) {
+                               const parts = duid_iaid.split('%').length;
+
+                               if (parts == 1)
+                                       this.isDUIDStatic[duid_iaid] = true;
+                               else if (parts == 2)
+                                       this.isDUIDIAIDStatic[duid_iaid] = true;
                        }
                };
 
@@ -190,11 +188,23 @@ return baseclass.extend({
                        else if (hint)
                                host = hint[1];
 
+                       const duid = lease.duid?.toLowerCase();
+                       const iaid = lease.iaid?.toLowerCase();
+
+                       // Note: "disabled: false" doesn't work
+                       let disabled = null;
+                       if (!duid)
+                               disabled = true;
+                       else if (duid && this.isDUIDStatic[duid])
+                               disabled = true;
+                       else if (duid && iaid && this.isDUIDIAIDStatic[`${duid}%${iaid}`])
+                               disabled = true;
+
                        const columns = [
                                host || '-',
                                lease.ip6addrs ? lease.ip6addrs.join('<br />') : lease.ip6addr,
-                               lease?.duid,
-                               lease?.iaid,
+                               duid || '-',
+                               iaid || '-',
                                exp
                        ];
 
@@ -203,7 +213,7 @@ return baseclass.extend({
                                        'class': 'cbi-button cbi-button-apply',
                                        'click': L.bind(this.handleCreateStaticLease6, this, lease),
                                        'data-tooltip': _('Reserve a specific IP address for this device'),
-                                       'disabled': this.isDUIDStatic[lease?.duid?.toUpperCase()]
+                                       'disabled': disabled
                                }, [ _('Reserve IP') ]));
                        }