From 2e8aebcbdbe17cfdfaff3cf3236876d0a2252587 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20H=C3=A4rdeman?= Date: Tue, 21 Oct 2025 22:56:00 +0200 Subject: [PATCH] luci-mod-network: move dnsmasq cfg to own function in dhcp.js MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This makes it a bit clearer what is specific to dnsmasq and also prepares for the following patches. No actual code changes. Signed-off-by: David Härdeman --- .../resources/view/network/dhcp.js | 407 +++++++++--------- 1 file changed, 207 insertions(+), 200 deletions(-) diff --git a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/dhcp.js b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/dhcp.js index 5ae0c12cc2..ad4d98cfcb 100644 --- a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/dhcp.js +++ b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/dhcp.js @@ -237,210 +237,11 @@ return view.extend({ m = new form.Map('dhcp', _('DHCP')); - s = m.section(form.TypedSection, 'dnsmasq'); - s.anonymous = false; - s.addremove = true; - s.addbtntitle = _('Add server instance', 'Dnsmasq instance'); - - s.renderContents = function(/* ... */) { - var renderTask = form.TypedSection.prototype.renderContents.apply(this, arguments), - sections = this.cfgsections(); - - return Promise.resolve(renderTask).then(function(nodes) { - if (sections.length < 2) { - nodes.querySelector('#cbi-dhcp-dnsmasq > h3').remove(); - nodes.querySelector('#cbi-dhcp-dnsmasq > .cbi-section-remove').remove(); - } - else { - nodes.querySelectorAll('#cbi-dhcp-dnsmasq > .cbi-section-remove').forEach(function(div, i) { - var section = uci.get('dhcp', sections[i]), - hline = div.nextElementSibling, - btn = div.firstElementChild; + s = this.add_dnsmasq_cfg(m, networks); - if (!section || section['.anonymous']) { - hline.innerText = i ? _('Unnamed instance #%d', 'Dnsmasq instance').format(i+1) : _('Default instance', 'Dnsmasq instance'); - btn.innerText = i ? _('Remove instance #%d', 'Dnsmasq instance').format(i+1) : _('Remove default instance', 'Dnsmasq instance'); - } - else { - hline.innerText = _('Instance "%q"', 'Dnsmasq instance').format(section['.name']); - btn.innerText = _('Remove instance "%q"', 'Dnsmasq instance').format(section['.name']); - } - }); - } - - nodes.querySelector('#cbi-dhcp-dnsmasq > .cbi-section-create input').placeholder = _('New instance name…', 'Dnsmasq instance'); - - return nodes; - }); - }; - - - s.tab('general', _('General')); - s.tab('devices', _('Devices & Ports')); - s.tab('logging', _('Log')); - s.tab('files', _('Files')); - s.tab('relay', _('Relay')); s.tab('leases', _('Static Leases')); s.tab('pxe_tftp', _('PXE/TFTP')); - // Begin general - s.taboption('general', form.Flag, 'authoritative', - _('Authoritative'), - _('This is the only DHCP server in the local network.')); - - s.taboption('general', form.Value, 'domain', - _('Local domain'), - _('Local domain suffix appended to DHCP names and hosts file entries.')); - - o = s.taboption('general', form.Flag, 'sequential_ip', - _('Allocate IPs sequentially'), - _('Allocate IP addresses sequentially, starting from the lowest available address.')); - o.optional = true; - - o = s.taboption('general', form.Value, 'dhcpleasemax', - _('Max. DHCP leases'), - _('Maximum allowed number of active DHCP leases.')); - o.optional = true; - o.datatype = 'uinteger'; - o.placeholder = 150; - // End general - - // Begin devices - o = s.taboption('devices', form.Flag, 'nonwildcard', - _('Non-wildcard'), - _('Bind only to configured interface addresses, instead of the wildcard address.')); - o.default = o.enabled; - o.optional = false; - o.rmempty = true; - - o = s.taboption('devices', widgets.NetworkSelect, 'interface', - _('Listen interfaces'), - _('Listen only on the specified interfaces, and loopback if not excluded explicitly.')); - o.multiple = true; - o.nocreate = true; - - o = s.taboption('devices', widgets.NetworkSelect, 'notinterface', - _('Exclude interfaces'), - _('Do not listen on the specified interfaces.')); - o.loopback = true; - o.multiple = true; - o.nocreate = true; - // End devices - - // Begin logging - o = s.taboption('logging', form.Flag, 'logdhcp', - _('Extra DHCP logging'), - _('Log all options sent to DHCP clients and the tags used to determine them.')); - o.optional = true; - - o = s.taboption('logging', form.Value, 'logfacility', - _('Log facility'), - _('Set log class/facility for syslog entries.')); - o.optional = true; - o.value('KERN'); - o.value('USER'); - o.value('MAIL'); - o.value('DAEMON'); - o.value('AUTH'); - o.value('LPR'); - o.value('NEWS'); - o.value('UUCP'); - o.value('CRON'); - o.value('LOCAL0'); - o.value('LOCAL1'); - o.value('LOCAL2'); - o.value('LOCAL3'); - o.value('LOCAL4'); - o.value('LOCAL5'); - o.value('LOCAL6'); - o.value('LOCAL7'); - o.value('-', _('stderr')); - - o = s.taboption('logging', form.Flag, 'quietdhcp', - _('Suppress logging'), - _('Suppress logging of the routine operation for the DHCP protocol.')); - o.optional = true; - o.depends('logdhcp', '0'); - // End logging - - // Begin files - s.taboption('files', form.Flag, 'readethers', - _('Use %s').format('/etc/ethers'), - _('Read %s to configure the DHCP server.').format('/etc/ethers')); - - s.taboption('files', form.Value, 'leasefile', - _('Lease file'), - _('File to store DHCP lease information.')); - // End files - - // Begin relay - o = s.taboption('relay', form.SectionValue, '__relays__', form.TableSection, 'relay', null, - _('Relay DHCP requests elsewhere. OK: v4↔v4, v6↔v6. Not OK: v4↔v6, v6↔v4.') - + '
' + _('Note: you may also need a DHCP Proxy (currently unavailable) when specifying a non-standard Relay To port(addr#port).') - + '
' + _('You may add multiple unique Relay To on the same Listen addr.')); - - ss = o.subsection; - - ss.addremove = true; - ss.anonymous = true; - ss.sortable = true; - ss.rowcolors = true; - ss.nodescriptions = true; - - so = ss.option(form.Value, 'local_addr', _('Relay from')); - so.rmempty = false; - so.datatype = 'ipaddr'; - - for (var family = 4; family <= 6; family += 2) { - for (var i = 0; i < networks.length; i++) { - if (networks[i].getName() != 'loopback') { - var addrs = (family == 6) ? networks[i].getIP6Addrs() : networks[i].getIPAddrs(); - for (var j = 0; j < addrs.length; j++) { - var addr = addrs[j].split('/')[0]; - so.value(addr, E([], [ - addr, ' (', - widgets.NetworkSelect.prototype.renderIfaceBadge(networks[i]), - ')' - ])); - } - } - } - } - - so = ss.option(form.Value, 'server_addr', _('Relay to address')); - so.rmempty = false; - so.optional = false; - so.placeholder = '192.168.10.1#535'; - so.validate = function(section, value) { - var m = this.section.formvalue(section, 'local_addr'), - n = this.section.formvalue(section, 'server_addr'), - p; - - if (!m || !n) { - return _('Both "Relay from" and "Relay to address" must be specified.'); - } - else { - p = n.split('#'); - if (p.length > 1 && !/^[0-9]+$/.test(p[1])) - return _('Expected port number.'); - else - n = p[0]; - - if ((validation.parseIPv6(m) && validation.parseIPv6(n)) || - validation.parseIPv4(m) && validation.parseIPv4(n)) - return true; - else - return _('Address families of "Relay from" and "Relay to address" must match.') - } - return true; - }; - - so = ss.option(widgets.NetworkSelect, 'interface', _('Only accept replies via')); - so.optional = true; - so.rmempty = false; - so.placeholder = 'lan'; - // End relay - // Begin leases o = s.taboption('leases', form.SectionValue, '__leases__', form.GridSection, 'host', null, _('Static leases are used to assign fixed IP addresses and symbolic hostnames to DHCP clients. They are also required for non-dynamic interface configurations where only hosts with a corresponding lease are served.') + '

' + @@ -836,5 +637,211 @@ return view.extend({ return mapEl; }); + }, + + add_dnsmasq_cfg: function(m, networks) { + var s, o, ss, so; + + s = m.section(form.TypedSection, 'dnsmasq'); + s.anonymous = false; + s.addremove = true; + s.addbtntitle = _('Add server instance', 'Dnsmasq instance'); + s.renderContents = function(/* ... */) { + var renderTask = form.TypedSection.prototype.renderContents.apply(this, arguments), + sections = this.cfgsections(); + + return Promise.resolve(renderTask).then(function(nodes) { + if (sections.length < 2) { + nodes.querySelector('#cbi-dhcp-dnsmasq > h3').remove(); + nodes.querySelector('#cbi-dhcp-dnsmasq > .cbi-section-remove').remove(); + } + else { + nodes.querySelectorAll('#cbi-dhcp-dnsmasq > .cbi-section-remove').forEach(function(div, i) { + var section = uci.get('dhcp', sections[i]), + hline = div.nextElementSibling, + btn = div.firstElementChild; + + if (!section || section['.anonymous']) { + hline.innerText = i ? _('Unnamed instance #%d', 'Dnsmasq instance').format(i+1) : _('Default instance', 'Dnsmasq instance'); + btn.innerText = i ? _('Remove instance #%d', 'Dnsmasq instance').format(i+1) : _('Remove default instance', 'Dnsmasq instance'); + } + else { + hline.innerText = _('Instance "%q"', 'Dnsmasq instance').format(section['.name']); + btn.innerText = _('Remove instance "%q"', 'Dnsmasq instance').format(section['.name']); + } + }); + } + + nodes.querySelector('#cbi-dhcp-dnsmasq > .cbi-section-create input').placeholder = _('New instance name…', 'Dnsmasq instance'); + + return nodes; + }); + }; + + s.tab('general', _('General')); + s.tab('devices', _('Devices & Ports')); + s.tab('logging', _('Log')); + s.tab('files', _('Files')); + s.tab('relay', _('Relay')); + + // Begin general + s.taboption('general', form.Flag, 'authoritative', + _('Authoritative'), + _('This is the only DHCP server in the local network.')); + + s.taboption('general', form.Value, 'domain', + _('Local domain'), + _('Local domain suffix appended to DHCP names and hosts file entries.')); + + o = s.taboption('general', form.Flag, 'sequential_ip', + _('Allocate IPs sequentially'), + _('Allocate IP addresses sequentially, starting from the lowest available address.')); + o.optional = true; + + o = s.taboption('general', form.Value, 'dhcpleasemax', + _('Max. DHCP leases'), + _('Maximum allowed number of active DHCP leases.')); + o.optional = true; + o.datatype = 'uinteger'; + o.placeholder = 150; + // End general + + // Begin devices + o = s.taboption('devices', form.Flag, 'nonwildcard', + _('Non-wildcard'), + _('Bind only to configured interface addresses, instead of the wildcard address.')); + o.default = o.enabled; + o.optional = false; + o.rmempty = true; + + o = s.taboption('devices', widgets.NetworkSelect, 'interface', + _('Listen interfaces'), + _('Listen only on the specified interfaces, and loopback if not excluded explicitly.')); + o.multiple = true; + o.nocreate = true; + + o = s.taboption('devices', widgets.NetworkSelect, 'notinterface', + _('Exclude interfaces'), + _('Do not listen on the specified interfaces.')); + o.loopback = true; + o.multiple = true; + o.nocreate = true; + // End devices + + // Begin logging + o = s.taboption('logging', form.Flag, 'logdhcp', + _('Extra DHCP logging'), + _('Log all options sent to DHCP clients and the tags used to determine them.')); + o.optional = true; + + o = s.taboption('logging', form.Value, 'logfacility', + _('Log facility'), + _('Set log class/facility for syslog entries.')); + o.optional = true; + o.value('KERN'); + o.value('USER'); + o.value('MAIL'); + o.value('DAEMON'); + o.value('AUTH'); + o.value('LPR'); + o.value('NEWS'); + o.value('UUCP'); + o.value('CRON'); + o.value('LOCAL0'); + o.value('LOCAL1'); + o.value('LOCAL2'); + o.value('LOCAL3'); + o.value('LOCAL4'); + o.value('LOCAL5'); + o.value('LOCAL6'); + o.value('LOCAL7'); + o.value('-', _('stderr')); + + o = s.taboption('logging', form.Flag, 'quietdhcp', + _('Suppress logging'), + _('Suppress logging of the routine operation for the DHCP protocol.')); + o.optional = true; + o.depends('logdhcp', '0'); + // End logging + + // Begin files + s.taboption('files', form.Flag, 'readethers', + _('Use %s').format('/etc/ethers'), + _('Read %s to configure the DHCP server.').format('/etc/ethers')); + + s.taboption('files', form.Value, 'leasefile', + _('Lease file'), + _('File to store DHCP lease information.')); + // End files + + // Begin relay + o = s.taboption('relay', form.SectionValue, '__relays__', form.TableSection, 'relay', null, + _('Relay DHCP requests elsewhere. OK: v4↔v4, v6↔v6. Not OK: v4↔v6, v6↔v4.') + + '
' + _('Note: you may also need a DHCP Proxy (currently unavailable) when specifying a non-standard Relay To port(addr#port).') + + '
' + _('You may add multiple unique Relay To on the same Listen addr.')); + + ss = o.subsection; + + ss.addremove = true; + ss.anonymous = true; + ss.sortable = true; + ss.rowcolors = true; + ss.nodescriptions = true; + + so = ss.option(form.Value, 'local_addr', _('Relay from')); + so.rmempty = false; + so.datatype = 'ipaddr'; + + for (var family = 4; family <= 6; family += 2) { + for (var i = 0; i < networks.length; i++) { + if (networks[i].getName() != 'loopback') { + var addrs = (family == 6) ? networks[i].getIP6Addrs() : networks[i].getIPAddrs(); + for (var j = 0; j < addrs.length; j++) { + var addr = addrs[j].split('/')[0]; + so.value(addr, E([], [ + addr, ' (', + widgets.NetworkSelect.prototype.renderIfaceBadge(networks[i]), + ')' + ])); + } + } + } + } + + so = ss.option(form.Value, 'server_addr', _('Relay to address')); + so.rmempty = false; + so.optional = false; + so.placeholder = '192.168.10.1#535'; + so.validate = function(section, value) { + var m = this.section.formvalue(section, 'local_addr'), + n = this.section.formvalue(section, 'server_addr'), + p; + + if (!m || !n) { + return _('Both "Relay from" and "Relay to address" must be specified.'); + } + else { + p = n.split('#'); + if (p.length > 1 && !/^[0-9]+$/.test(p[1])) + return _('Expected port number.'); + else + n = p[0]; + + if ((validation.parseIPv6(m) && validation.parseIPv6(n)) || + validation.parseIPv4(m) && validation.parseIPv4(n)) + return true; + else + return _('Address families of "Relay from" and "Relay to address" must match.') + } + return true; + }; + + so = ss.option(widgets.NetworkSelect, 'interface', _('Only accept replies via')); + so.optional = true; + so.rmempty = false; + so.placeholder = 'lan'; + // End relay + + return s; } }); -- 2.30.2