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 051e8d529ce4..2794476567b2 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 @@ -29,6 +29,18 @@ callDHCPLeases = rpc.declare({ expect: { '': {} } }); +var callNetworkDevices = rpc.declare({ + object: 'luci-rpc', + method: 'getNetworkDevices', + expect: { '': {} } +}); + +var listServices = rpc.declare({ + object: 'service', + method: 'list', + expect: { '': {} } +}); + CBILeaseStatus = form.DummyValue.extend({ renderWidget: function(section_id, option_id, cfgvalue) { return E([ @@ -278,6 +290,8 @@ return view.extend({ callDUIDHints(), getDHCPPools(), network.getNetworks(), + callNetworkDevices(), + listServices(), uci.load('firewall') ]); }, @@ -288,11 +302,15 @@ return view.extend({ duids = hosts_duids_pools[1], pools = hosts_duids_pools[2], networks = hosts_duids_pools[3], - m, s, o, ss, so, dnss; + m, s, o, ss, so, dnss, tagstab; + + var devices = Object.keys(L.toArray(hosts_duids_pools[4])[0]); + var services = Object.keys(L.toArray(hosts_duids_pools[5])[0]); let noi18nstrings = { etc_hosts: '/etc/hosts', etc_ethers: '/etc/ethers', + exclamationmark_invert: '!', localhost_v6: '::1', loopback_slash_8_v4: '127.0.0.0/8', not_found: 'Not found', @@ -304,6 +322,18 @@ return view.extend({ reverse_arpa: '*.IN-ADDR.ARPA,*.IP6.ARPA', servers_file_entry01: 'server=1.2.3.4', servers_file_entry02: 'server=/domain/1.2.3.4', + tagcodestring:'tag', + tag_named_ov_string: 'option(6):<opt-name>,[<value>[,<value>]]', + + //match tags + dhcp_option_code: 'option(6)', + dhcp_optioncolon_code: 'option(6):', + dhcp_option_client_arch: 'option:client-arch,6', + dhcp_value_code: ',value', + tag_match_code_name: 'match', + tag_match_option_syntax: '<option number>|option:<option name>[,<value>]', + tag_name_efi_ia32: 'efi-ia32', + wildcard_code: '*', }; @@ -367,6 +397,7 @@ return view.extend({ s.tab('ipsets', _('IP Sets')); s.tab('relay', _('Relay')); s.tab('pxe_tftp', _('PXE/TFTP')); + s.tab('tagsparent', _('Tags')); s.taboption('filteropts', form.Flag, 'domainneeded', _('Domain required'), @@ -1138,6 +1169,170 @@ return view.extend({ _('Forward/reverse DNS'), _('Add static forward and reverse DNS entries for this host.')); + + + o = s.taboption('tagsparent', form.SectionValue, '__tagsparent__', form.TypedSection, '__tagsparent__'); + + tagstab = o.subsection; + + tagstab.anonymous = true; + tagstab.cfgsections = function() { return [ '__tagsparent__' ] }; + + tagstab.tab('mac', _('MAC')); + tagstab.tab('matchtags', _('Match Tags')); + tagstab.tab('vc', _('VC')); + tagstab.tab('uc', _('UC')); + tagstab.tab('settags', _('Set Tags')); + + + o = tagstab.taboption('mac', form.SectionValue, '__mac__', form.TableSection, 'mac', null, + _('MAC hardware addresses uniquely identify clients to set tags on them.') + '

' + + _('Use the Add Button to add a new MAC.')); + ss = o.subsection; + ss.addremove = true; + ss.anonymous = true; + ss.sortable = true; + ss.nodescriptions = true; + ss.modaltitle = _('Edit MAC'); + ss.rowcolors = true; + + so = ss.option(form.Value, 'mac', _('MAC match')); + so.validate = isValidMAC; + so.rmempty = false; + so.optional = false; + + so = ss.option(form.Value, 'networkid', _('Set this Tag')); + so.rmempty = false; + so.optional = false; + + o = tagstab.taboption('matchtags', form.SectionValue, '__tags__', form.TableSection, 'tag', null, + customi18n( _('A {tagcodestring} is an alphanumeric label. dnsmasq applies chosen DHCP options when a specific {tagcodestring} is encountered.')) + '
' + + customi18n( _('In other words: "This {tagcodestring} gets these {tag_named_ov_string}".')) + '
' + + customi18n( _('Note: invalid {tag_named_ov_string} combinations may cause dnsmasq to silently crash.')) + '

' + + customi18n( _('Prepend a {tagcodestring} with {exclamationmark_invert} to invert their domain of application, e.g. to send options to a host lacking a {tagcodestring}.') ) + '

' + + customi18n( _('Use the %s Button to add a new {tagcodestring}.').format( _('Add') ) ) ); + ss = o.subsection; + ss.addremove = true; + ss.anonymous = true; + ss.sortable = true; + ss.nodescriptions = true; + ss.modaltitle = _('Edit tag'); + ss.rowcolors = true; + + so = ss.option(form.DynamicList, 'dhcp_option', + _('Apply these DHCP Options...'), + _('Options to be added for this tag.')); + so.rmempty = true; + so.optional = true; + so.placeholder = '3,192.168.10.1,10.10.10.1'; + + so = ss.option(form.Value, 'tag', _('...To this matching Tag')); + so.rmempty = false; + so.optional = false; + so.placeholder = 'specialgateways'; + so.validate = function(section, value) { + for (var i = 0, l = devices.length; i < l; i++) { + if (devices[i] == value) + return _('Tag name %s must not match any active device name').format(value); + } + for (var i = 0, l = services.length; i < l; i++) { + if (services[i] == value) + return _('Tag name %s must not match any service name').format(value); + } + return true; + }; + + so = ss.option(form.Flag, 'force', + _('Force'), + _('Send options to clients that did not request them.')); + so.rmempty = false; + so.optional = true; + + o = tagstab.taboption('vc', form.SectionValue, '__vc__', form.TableSection, 'vendorclass', null, + _('Match Vendor Class (VC) strings sent by DHCP clients as a trigger to set tags on them.') + '

' + + _('Use the Add Button to add a new VC.')); + ss = o.subsection; + ss.addremove = true; + ss.anonymous = true; + ss.sortable = true; + ss.nodescriptions = true; + ss.modaltitle = _('Edit VC'); + ss.rowcolors = true; + + so = ss.option(form.Value, 'vendorclass', _('Match this Vendor Class')); + so.rmempty = false; + so.optional = false; + + so = ss.option(form.Value, 'networkid', _('In order to set this Tag')); + so.rmempty = false; + so.optional = false; + + so = ss.option(form.Flag, 'force', + _('Force'), + _('Send options to clients that did not request them.')); + so.rmempty = false; + so.optional = true; + + o = tagstab.taboption('uc', form.SectionValue, '__uc__', form.TableSection, 'userclass', null, + _('Match User Class (UC) strings sent by DHCP clients as a trigger to set tags on them.') + '

' + + _('Use the Add Button to add a new UC.')); + ss = o.subsection; + ss.addremove = true; + ss.anonymous = true; + ss.sortable = true; + ss.nodescriptions = true; + ss.modaltitle = _('Edit UC'); + ss.rowcolors = true; + + so = ss.option(form.Value, 'userclass', _('Match this User Class')); + so.rmempty = false; + so.optional = false; + + so = ss.option(form.Value, 'networkid', _('In order to set this Tag')); + so.rmempty = false; + so.optional = false; + + so = ss.option(form.Flag, 'force', + _('Force'), + _('Send options to clients that did not request them.')); + so.rmempty = false; + so.optional = true; + + o = tagstab.taboption('settags', form.SectionValue, '__settags__', form.TableSection, 'match', null, + customi18n( _('Encountering chosen DHCP {dhcp_option_code}s (or also its {dhcp_value_code}) from clients triggers dnsmasq to set alphanumeric {tagcodestring}s.')) + '
' + + customi18n( _('In other words: "{tag_match_code_name} these {dhcp_option_code}s to set this {tagcodestring}" or "These {dhcp_option_code}s set this {tagcodestring}".')) + '
' + + customi18n( _('Internally, these configuration entries are called {tag_match_code_name}.')) + '
' + + customi18n( _('Matching option syntax: {tag_match_option_syntax}.')) + ' ' + + customi18n( _('Prefix named (IPv6) options with {dhcp_optioncolon_code}.')) + ' ' + + customi18n( _('Wildcards ({wildcard_code}) allowed.')) + '

' + + customi18n( _('Match {dhcp_option_client_arch}, Tag {tag_name_efi_ia32}, sets tag {tag_name_efi_ia32}')) + ' ' + + _('when number %s appears in the list of architectures sent by the client in option %s.').format('6', '93') + '
' + + customi18n( _('Use the %s Button to add a new {tag_match_code_name}.').format(_('Add'))) ); + ss = o.subsection; + ss.addremove = true; + ss.anonymous = true; + ss.sortable = true; + ss.nodescriptions = true; + ss.modaltitle = _('Edit Match'); + ss.rowcolors = true; + + so = ss.option(form.Value, 'match', _('Match this client option(+value)')); + so.rmempty = false; + so.optional = false; + so.placeholder = '61,8c:80:90:01:02:03'; + + so = ss.option(form.Value, 'networkid', _('In order to Set this Tag')); + so.rmempty = false; + so.optional = false; + so.placeholder = 'myuniqueclientid' + + so = ss.option(form.Flag, 'force', + _('Force'), + _('Send options to clients that did not request them.')); + so.rmempty = false; + so.optional = true; + + o = s.taboption('leases', CBILeaseStatus, '__status__'); if (has_dhcpv6) diff --git a/modules/luci-mod-network/root/usr/share/rpcd/acl.d/luci-mod-network.json b/modules/luci-mod-network/root/usr/share/rpcd/acl.d/luci-mod-network.json index 6e9118ac1bf4..c3f0442c375f 100644 --- a/modules/luci-mod-network/root/usr/share/rpcd/acl.d/luci-mod-network.json +++ b/modules/luci-mod-network/root/usr/share/rpcd/acl.d/luci-mod-network.json @@ -39,7 +39,8 @@ "description": "Grant access to DHCP configuration", "read": { "ubus": { - "luci-rpc": [ "getDHCPLeases", "getDUIDHints", "getHostHints" ] + "luci-rpc": [ "getDHCPLeases", "getDUIDHints", "getHostHints", "getNetworkDevices" ], + "service": [ "list" ], }, "uci": [ "dhcp" ] },