diff options
Diffstat (limited to 'modules/luci-mod-network/htdocs/luci-static/resources')
4 files changed, 310 insertions, 38 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 5059b389b4..c62231fe20 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 @@ -126,7 +126,7 @@ function validateHostname(sid, s) { if (s.length > 256) return _('Expecting: %s').format(_('valid hostname')); - var labels = s.replace(/^\.+|\.$/g, '').split(/\./); + var labels = s.replace(/^\*?\.?|\.$/g, '').split(/\./); for (var i = 0; i < labels.length; i++) if (!labels[i].match(/^[a-z0-9_](?:[a-z0-9-]{0,61}[a-z0-9])?$/i)) @@ -156,13 +156,15 @@ function validateServerSpec(sid, s) { if (s == null || s == '') return true; - var m = s.match(/^(?:\/(.+)\/)?(.*)$/); + var m = s.match(/^(\/.*\/)?(.*)$/); if (!m) return _('Expecting: %s').format(_('valid hostname')); - var res = validateAddressList(sid, m[1]); - if (res !== true) - return res; + if (m[1] != '//' && m[1] != '/#/') { + var res = validateAddressList(sid, m[1]); + if (res !== true) + return res; + } if (m[2] == '' || m[2] == '#') return true; @@ -231,7 +233,8 @@ return view.extend({ return Promise.all([ callHostHints(), callDUIDHints(), - getDHCPPools() + getDHCPPools(), + network.getDevices() ]); }, @@ -240,6 +243,7 @@ return view.extend({ hosts = hosts_duids_pools[0], duids = hosts_duids_pools[1], pools = hosts_duids_pools[2], + ndevs = hosts_duids_pools[3], m, s, o, ss, so; m = new form.Map('dhcp', _('DHCP and DNS'), @@ -250,11 +254,14 @@ return view.extend({ s.addremove = false; s.tab('general', _('General Settings')); + s.tab('relay', _('Relay')); s.tab('files', _('Resolv and Hosts Files')); s.tab('pxe_tftp', _('PXE/TFTP Settings')); s.tab('advanced', _('Advanced Settings')); s.tab('leases', _('Static Leases')); s.tab('hosts', _('Hostnames')); + s.tab('srvhosts', _('SRV')); + s.tab('mxhosts', _('MX')); s.tab('ipsets', _('IP Sets')); s.taboption('general', form.Flag, 'domainneeded', @@ -287,13 +294,16 @@ return view.extend({ o = s.taboption('general', form.DynamicList, 'address', _('Addresses'), - _('List of domains to force to an IP address.')); + _('Resolve specified FQDNs to an IP.') + '<br />' + + _('Syntax: <code>/fqdn[/fqdn…]/[ipaddr]</code>.') + '<br />' + + _('<code>/#/</code> matches any domain. <code>/example.com/</code> returns NXDOMAIN.') + '<br />' + + _('<code>/example.com/#</code> returns NULL addresses (<code>0.0.0.0</code> and <code>::</code>) for example.com and its subdomains.')); o.optional = true; - o.placeholder = '/router.local/192.168.0.1'; + o.placeholder = '/router.local/router.lan/192.168.0.1'; o = s.taboption('general', form.DynamicList, 'ipset', _('IP sets'), - _('List of IP sets to populate with the specified domain IPs.')); + _('List of IP sets to populate with the IPs of DNS lookup results of the FQDNs also specified here.')); o.optional = true; o.placeholder = '/example.org/ipset,ipset6'; @@ -340,6 +350,66 @@ return view.extend({ o.optional = true; o.placeholder = 'loopback'; + 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.') + + '<br />' + _('Note: you may also need a DHCP Proxy (currently unavailable) when specifying a non-standard Relay To port(<code>addr#port</code>).') + + '<br />' + _('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, 'id', _('ID')); + so.rmempty = false; + so.optional = true; + + so = ss.option(widgets.NetworkSelect, 'interface', _('Interface')); + so.optional = true; + so.rmempty = false; + so.placeholder = 'lan'; + + so = ss.option(form.Value, 'local_addr', _('Listen address')); + so.rmempty = false; + so.datatype = 'ipaddr'; + + for (var family = 4; family <= 6; family += 2) { + for (var i = 0; i < ndevs.length; i++) { + var addrs = (family == 6) ? ndevs[i].getIP6Addrs() : ndevs[i].getIPAddrs(); + for (var j = 0; j < addrs.length; j++) + so.value(addrs[j].split('/')[0]); + } + } + + 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 (n != null && n != '') + p = n.split('#'); + if (p.length > 1 && !/^[0-9]+$/.test(p[1])) + return _('Expected port number.'); + else + n = p[0]; + + if ((m == null || m == '') && (n == null || n == '')) + return _('Both Listen addr and Relay To must be specified.'); + + if ((validation.parseIPv6(m) && validation.parseIPv6(n)) || + validation.parseIPv4(m) && validation.parseIPv4(n)) + return true; + else + return _('Listen and Relay To IP family must be homogeneous.') + }; + s.taboption('files', form.Flag, 'readethers', _('Use <code>/etc/ethers</code>'), _('Read <code>/etc/ethers</code> to configure the DHCP server.')); @@ -384,8 +454,20 @@ return view.extend({ o.default = o.enabled; s.taboption('advanced', form.Flag, 'filterwin2k', - _('Filter useless'), - _('Do not forward queries that cannot be answered by public resolvers.')); + _('Filter SRV/SOA service discovery'), + _('Filters SRV/SOA service discovery, to avoid triggering dial-on-demand links.') + '<br />' + + _('May prevent VoIP or other services from working.')); + + o = s.taboption('advanced', form.Flag, 'filter_aaaa', + _('Filter IPv6 AAAA records'), + _('Remove IPv6 addresses from the results and only return IPv4 addresses.') + '<br />' + + _('Can be useful if ISP has IPv6 nameservers but does not provide IPv6 routing.')); + o.optional = true; + + o = s.taboption('advanced', form.Flag, 'filter_a', + _('Filter IPv4 A records'), + _('Remove IPv4 addresses from the results and only return IPv6 addresses.')); + o.optional = true; s.taboption('advanced', form.Flag, 'localise_queries', _('Localise queries'), @@ -546,6 +628,72 @@ return view.extend({ so.value(index, '%s (Domain: %s, Local: %s)'.format(index, val.domain || '?', val.local || '?')); }); + o = s.taboption('srvhosts', form.SectionValue, '__srvhosts__', form.TableSection, 'srvhost', null, + _('Bind service records to a domain name: specify the location of services. See <a href="%s">RFC2782</a>.').format('https://datatracker.ietf.org/doc/html/rfc2782') + + '<br />' + _('_service: _sip, _ldap, _imap, _stun, _xmpp-client, … . (Note: while _http is possible, no browsers support SRV records.)') + + '<br />' + _('_proto: _tcp, _udp, _sctp, _quic, … .') + + '<br />' + _('You may add multiple records for the same Target.') + + '<br />' + _('Larger weights (of the same prio) are given a proportionately higher probability of being selected.')); + + ss = o.subsection; + + ss.addremove = true; + ss.anonymous = true; + ss.sortable = true; + ss.rowcolors = true; + + so = ss.option(form.Value, 'srv', _('SRV'), _('Syntax: <code>_service._proto.example.com</code>.')); + so.rmempty = false; + so.datatype = 'hostname'; + so.placeholder = '_sip._tcp.example.com'; + + so = ss.option(form.Value, 'target', _('Target'), _('CNAME or fqdn')); + so.rmempty = false; + so.datatype = 'hostname'; + so.placeholder = 'sip.example.com'; + + so = ss.option(form.Value, 'port', _('Port')); + so.rmempty = false; + so.datatype = 'port'; + so.placeholder = '5060'; + + so = ss.option(form.Value, 'class', _('Priority'), _('Ordinal: lower comes first.')); + so.rmempty = true; + so.datatype = 'range(0,65535)'; + so.placeholder = '10'; + + so = ss.option(form.Value, 'weight', _('Weight')); + so.rmempty = true; + so.datatype = 'range(0,65535)'; + so.placeholder = '50'; + + o = s.taboption('mxhosts', form.SectionValue, '__mxhosts__', form.TableSection, 'mxhost', null, + _('Bind service records to a domain name: specify the location of services.') + + '<br />' + _('You may add multiple records for the same domain.')); + + ss = o.subsection; + + ss.addremove = true; + ss.anonymous = true; + ss.sortable = true; + ss.rowcolors = true; + ss.nodescriptions = true; + + so = ss.option(form.Value, 'domain', _('Domain')); + so.rmempty = false; + so.datatype = 'hostname'; + so.placeholder = 'example.com'; + + so = ss.option(form.Value, 'relay', _('Relay')); + so.rmempty = false; + so.datatype = 'hostname'; + so.placeholder = 'relay.example.com'; + + so = ss.option(form.Value, 'pref', _('Priority'), _('Ordinal: lower comes first.')); + so.rmempty = true; + so.datatype = 'range(0,65535)'; + so.placeholder = '0'; + o = s.taboption('hosts', form.SectionValue, '__hosts__', form.GridSection, 'domain', null, _('Hostnames are used to bind a domain name to an IP address. This setting is redundant for hostnames already configured with static leases, but it can be useful to rebind an FQDN.')); @@ -577,7 +725,7 @@ return view.extend({ }); o = s.taboption('ipsets', form.SectionValue, '__ipsets__', form.GridSection, 'ipset', null, - _('List of IP sets to populate with the specified domain IPs.')); + _('List of IP sets to populate with the IPs of DNS lookup results of the FQDNs also specified here.')); ss = o.subsection; diff --git a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/diagnostics.js b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/diagnostics.js index da7cd95bdc..90ad67aeaf 100644 --- a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/diagnostics.js +++ b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/diagnostics.js @@ -14,8 +14,7 @@ return view.extend({ buttons[i].setAttribute('disabled', 'true'); return fs.exec(exec, args).then(function(res) { - var out = document.querySelector('.command-output'); - out.style.display = ''; + var out = document.querySelector('textarea'); dom.content(out, [ res.stdout || '', res.stderr || '' ]); }).catch(function(err) { @@ -75,9 +74,7 @@ return view.extend({ ping_host = uci.get('luci', 'diag', 'ping') || 'openwrt.org', route_host = uci.get('luci', 'diag', 'route') || 'openwrt.org'; - return E([], [ - E('h2', {}, [ _('Network Utilities') ]), - E('table', { 'class': 'table' }, [ + var table = E('table', { 'class': 'table' }, [ E('tr', { 'class': 'tr' }, [ E('td', { 'class': 'td left' }, [ E('input', { @@ -156,9 +153,26 @@ return view.extend({ ]) ]) : E([]), ]) - ]), - E('pre', { 'class': 'command-output', 'style': 'display:none' }) + ]); + + var view = E('div', { 'class': 'cbi-map'}, [ + E('h2', {}, [ _('Diagnostics') ]), + E('div', { 'class': 'cbi-map-descr'}, _('Execution of various network commands to check the connection and name resolution to other systems.')), + table, + E('div', {'class': 'cbi-section'}, [ + E('div', { 'id' : 'command-output'}, + E('textarea', { + 'id': 'widget.command-output', + 'style': 'width: 100%', + 'readonly': true, + 'wrap': 'off', + 'rows': '20' + }) + ) + ]) ]); + + return view; }, handleSaveApply: null, diff --git a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/interfaces.js b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/interfaces.js index b88183d51f..d44b0cf6d0 100644 --- a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/interfaces.js +++ b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/interfaces.js @@ -228,6 +228,22 @@ function get_netmask(s, use_cfgvalue) { return subnetmask; } +function has_peerdns(proto) { + switch (proto) { + case 'dhcp': + case 'qmi': + case 'ppp': + case 'pppoe': + case 'pppoa': + case 'pptp': + case 'openvpn': + case 'sstp': + return true; + } + + return false; +} + var cbiRichListValue = form.ListValue.extend({ renderWidget: function(section_id, option_index, cfgvalue) { var choices = this.transformChoices(); @@ -488,7 +504,7 @@ return view.extend({ }; s.modaltitle = function(section_id) { - return _('Interfaces') + ' » ' + section_id.toUpperCase(); + return _('Interfaces') + ' » ' + section_id; }; s.renderRowActions = function(section_id) { @@ -936,13 +952,13 @@ return view.extend({ o = nettools.replaceOption(s, 'advanced', form.Flag, 'defaultroute', _('Use default gateway'), _('If unchecked, no default route is configured')); o.default = o.enabled; - if (protoval != 'static') { + if (has_peerdns(protoval)) { o = nettools.replaceOption(s, 'advanced', form.Flag, 'peerdns', _('Use DNS servers advertised by peer'), _('If unchecked, the advertised DNS server addresses are ignored')); o.default = o.enabled; } o = nettools.replaceOption(s, 'advanced', form.DynamicList, 'dns', _('Use custom DNS servers')); - if (protoval != 'static') + if (has_peerdns(protoval)) o.depends('peerdns', '0'); o.datatype = 'ipaddr'; @@ -1199,7 +1215,7 @@ return view.extend({ 'class': 'ifacebox-head', 'style': firewall.getZoneColorStyle(zone), 'title': zone ? _('Part of zone %q').format(zone.getName()) : _('No zone assigned') - }, E('strong', net.getName().toUpperCase())), + }, E('strong', net.getName())), E('div', { 'class': 'ifacebox-body', 'id': '%s-ifc-devices'.format(section_id), diff --git a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/wireless.js b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/wireless.js index 2704ee474b..420b381e2f 100644 --- a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/wireless.js +++ b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/wireless.js @@ -480,7 +480,7 @@ var CBIWifiFrequencyValue = form.Value.extend({ this.toggleWifiBand(elem); bwdt.value = htval; - chan.value = chval || chan.options[0].value; + chan.value = chval || (chan.options[0] ? chan.options[0].value : 'auto'); return elem; }, @@ -742,7 +742,8 @@ return view.extend({ load: function() { return Promise.all([ uci.changes(), - uci.load('wireless') + uci.load('wireless'), + uci.load('system') ]); }, @@ -986,6 +987,7 @@ return view.extend({ ss.tab('encryption', _('Wireless Security')); ss.tab('macfilter', _('MAC-Filter')); ss.tab('advanced', _('Advanced Settings')); + ss.tab('roaming', _('WLAN roaming'), _('Settings for assisting wireless clients in roaming between multiple APs: 802.11r, 802.11k and 802.11v')); o = ss.taboption('general', form.ListValue, 'mode', _('Mode')); o.value('ap', _('Access Point')); @@ -1146,6 +1148,11 @@ return view.extend({ o.depends('mode', 'ap-wds'); o.default = o.enabled; + /* https://w1.fi/cgit/hostap/commit/?id=34f7c699a6bcb5c45f82ceb6743354ad79296078 */ + /* multicast_to_unicast https://github.com/openwrt/openwrt/commit/7babb978ad9d7fc29acb1ff86afb1eb343af303a */ + o = ss.taboption('advanced', form.Flag, 'multicast_to_unicast', _('Multi To Unicast'), _('ARP, IPv4 and IPv6 (even 802.1Q) with multicast destination MACs are unicast to the STA MAC address. Note: This is not Directed Multicast Service (DMS) in 802.11v. Note: might break receiver STA multicast expectations.')); + o.rmempty = true; + o = ss.taboption('advanced', form.Flag, 'isolate', _('Isolate Clients'), _('Prevents client-to-client communication')); o.depends('mode', 'ap'); o.depends('mode', 'ap-wds'); @@ -1178,7 +1185,7 @@ return view.extend({ o.optional = true; o.datatype = 'uinteger'; - o = ss.taboption('advanced', form.Value, 'max_inactivity', _('Station inactivity limit'), _('sec')); + o = ss.taboption('advanced', form.Value, 'max_inactivity', _('Station inactivity limit'), _('802.11v: BSS Max Idle. Units: seconds.')); o.optional = true; o.placeholder = 300; o.datatype = 'uinteger'; @@ -1431,6 +1438,38 @@ return view.extend({ o.rmempty = true; o.password = true; + /* extra RADIUS settings start */ + o = ss.taboption('encryption', form.ListValue, 'dynamic_vlan', _('RADIUS Dynamic VLAN Assignment'), _('Required: Rejects auth if RADIUS server does not provide appropriate VLAN attributes.')); + add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] }); + o.value('0', _('Disabled')); + o.value('1', _('Optional')); + o.value('2', _('Required')); + o.write = function (section_id, value) { + return this.super('write', [section_id, (value == 0) ? null: value]); + } + + o = ss.taboption('encryption', form.Flag, 'per_sta_vif', _('RADIUS Per STA VLAN'), _('Each STA is assigned its own AP_VLAN interface.')); + add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] }); + + //hostapd internally defaults to vlan_naming=1 even with dynamic VLAN off + o = ss.taboption('encryption', form.Flag, 'vlan_naming', _('RADIUS VLAN Naming'), _('Off: <code>vlanXXX</code>, e.g., <code>vlan1</code>. On: <code>vlan_tagged_interface.XXX</code>, e.g. <code>eth0.1</code>.')); + add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] }); + + o = ss.taboption('encryption', widgets.DeviceSelect, 'vlan_tagged_interface', _('RADIUS VLAN Tagged Interface'), _('E.g. eth0, eth1')); + add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] }); + o.size = 1; + o.rmempty = true; + o.multiple = false; + o.noaliases = true; + o.nobridges = true; + o.nocreate = true; + o.noinactive = true; + + o = ss.taboption('encryption', form.Value, 'vlan_bridge', _('RADIUS VLAN Bridge Naming Scheme'), _('E.g. <code>br-vlan</code> or <code>brvlan</code>.')); + add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] }); + o.rmempty = true; + /* extra RADIUS settings end */ + o = ss.taboption('encryption', form.Value, 'dae_client', _('DAE-Client'), _('Dynamic Authorization Extension client.')); add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] }); o.rmempty = true; @@ -1447,6 +1486,10 @@ return view.extend({ o.rmempty = true; o.password = true; + //WPA(1) has only WPA IE. Only >= WPA2 has RSN IE Preauth frames. + o = ss.taboption('encryption', form.Flag, 'rsn_preauth', _('RSN Preauth'), _('Robust Security Network (RSN): Allow roaming preauth for WPA2-EAP networks (and advertise it in WLAN beacons). Only works if the specified network interface is a bridge. Shortens the time-critical reassociation process.')); + add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa2', 'wpa3', 'wpa3-mixed'] }); + o = ss.taboption('encryption', form.Value, '_wpa_key', _('Key')); o.depends('encryption', 'psk'); @@ -1510,66 +1553,117 @@ return view.extend({ // Probe 802.11r support (and EAP support as a proxy for Openwrt) var has_80211r = L.hasSystemFeature('hostapd', '11r') || L.hasSystemFeature('hostapd', 'eap'); - o = ss.taboption('encryption', form.Flag, 'ieee80211r', _('802.11r Fast Transition'), _('Enables fast roaming among access points that belong to the same Mobility Domain')); + o = ss.taboption('roaming', form.Flag, 'ieee80211r', _('802.11r Fast Transition'), _('Enables fast roaming among access points that belong to the same Mobility Domain')); add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] }); if (has_80211r) add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['psk', 'psk2', 'psk-mixed', 'sae', 'sae-mixed'] }); o.rmempty = true; - o = ss.taboption('encryption', form.Value, 'nasid', _('NAS ID'), _('Used for two different purposes: RADIUS NAS ID and 802.11r R0KH-ID. Not needed with normal WPA(2)-PSK.')); + o = ss.taboption('roaming', form.Value, 'nasid', _('NAS ID'), _('Used for two different purposes: RADIUS NAS ID and 802.11r R0KH-ID. Not needed with normal WPA(2)-PSK.')); add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] }); o.depends({ ieee80211r: '1' }); o.rmempty = true; - o = ss.taboption('encryption', form.Value, 'mobility_domain', _('Mobility Domain'), _('4-character hexadecimal ID')); + o = ss.taboption('roaming', form.Value, 'mobility_domain', _('Mobility Domain'), _('4-character hexadecimal ID')); o.depends({ ieee80211r: '1' }); o.placeholder = '4f57'; o.datatype = 'and(hexstring,length(4))'; o.rmempty = true; - o = ss.taboption('encryption', form.Value, 'reassociation_deadline', _('Reassociation Deadline'), _('time units (TUs / 1.024 ms) [1000-65535]')); + o = ss.taboption('roaming', form.Value, 'reassociation_deadline', _('Reassociation Deadline'), _('time units (TUs / 1.024 ms) [1000-65535]')); o.depends({ ieee80211r: '1' }); o.placeholder = '1000'; o.datatype = 'range(1000,65535)'; o.rmempty = true; - o = ss.taboption('encryption', form.ListValue, 'ft_over_ds', _('FT protocol')); + o = ss.taboption('roaming', form.ListValue, 'ft_over_ds', _('FT protocol')); o.depends({ ieee80211r: '1' }); - o.value('1', _('FT over DS')); o.value('0', _('FT over the Air')); + o.value('1', _('FT over DS')); o.rmempty = true; - o = ss.taboption('encryption', form.Flag, 'ft_psk_generate_local', _('Generate PMK locally'), _('When using a PSK, the PMK can be automatically generated. When enabled, the R0/R1 key options below are not applied. Disable this to use the R0 and R1 key options.')); + o = ss.taboption('roaming', form.Flag, 'ft_psk_generate_local', _('Generate PMK locally'), _('When using a PSK, the PMK can be automatically generated. When enabled, the R0/R1 key options below are not applied. Disable this to use the R0 and R1 key options.')); o.depends({ ieee80211r: '1' }); o.default = o.enabled; o.rmempty = false; - o = ss.taboption('encryption', form.Value, 'r0_key_lifetime', _('R0 Key Lifetime'), _('minutes')); + o = ss.taboption('roaming', form.Value, 'r0_key_lifetime', _('R0 Key Lifetime'), _('minutes')); o.depends({ ieee80211r: '1' }); o.placeholder = '10000'; o.datatype = 'uinteger'; o.rmempty = true; - o = ss.taboption('encryption', form.Value, 'r1_key_holder', _('R1 Key Holder'), _('6-octet identifier as a hex string - no colons')); + o = ss.taboption('roaming', form.Value, 'r1_key_holder', _('R1 Key Holder'), _('6-octet identifier as a hex string - no colons')); o.depends({ ieee80211r: '1' }); o.placeholder = '00004f577274'; o.datatype = 'and(hexstring,length(12))'; o.rmempty = true; - o = ss.taboption('encryption', form.Flag, 'pmk_r1_push', _('PMK R1 Push')); + o = ss.taboption('roaming', form.Flag, 'pmk_r1_push', _('PMK R1 Push')); o.depends({ ieee80211r: '1' }); o.placeholder = '0'; o.rmempty = true; - o = ss.taboption('encryption', form.DynamicList, 'r0kh', _('External R0 Key Holder List'), _('List of R0KHs in the same Mobility Domain. <br />Format: MAC-address,NAS-Identifier,128-bit key as hex string. <br />This list is used to map R0KH-ID (NAS Identifier) to a destination MAC address when requesting PMK-R1 key from the R0KH that the STA used during the Initial Mobility Domain Association.')); + o = ss.taboption('roaming', form.DynamicList, 'r0kh', _('External R0 Key Holder List'), _('List of R0KHs in the same Mobility Domain. <br />Format: MAC-address,NAS-Identifier,128-bit key as hex string. <br />This list is used to map R0KH-ID (NAS Identifier) to a destination MAC address when requesting PMK-R1 key from the R0KH that the STA used during the Initial Mobility Domain Association.')); o.depends({ ieee80211r: '1' }); o.rmempty = true; - o = ss.taboption('encryption', form.DynamicList, 'r1kh', _('External R1 Key Holder List'), _ ('List of R1KHs in the same Mobility Domain. <br />Format: MAC-address,R1KH-ID as 6 octets with colons,128-bit key as hex string. <br />This list is used to map R1KH-ID to a destination MAC address when sending PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD that can request PMK-R1 keys.')); + o = ss.taboption('roaming', form.DynamicList, 'r1kh', _('External R1 Key Holder List'), _ ('List of R1KHs in the same Mobility Domain. <br />Format: MAC-address,R1KH-ID as 6 octets with colons,128-bit key as hex string. <br />This list is used to map R1KH-ID to a destination MAC address when sending PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD that can request PMK-R1 keys.')); o.depends({ ieee80211r: '1' }); o.rmempty = true; // End of 802.11r options + // Probe 802.11k and 802.11v support via EAP support (full hostapd has EAP) + if (L.hasSystemFeature('hostapd', 'eap')) { + /* 802.11k settings start */ o = + ss.taboption('roaming', form.Flag, 'ieee80211k', _('802.11k RRM'), _('Radio Resource Measurement - Sends beacons to assist roaming. Not all clients support this.')); + // add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['psk', 'psk2', 'psk-mixed', 'sae', 'sae-mixed'] }); + o.depends('mode', 'ap'); + o.depends('mode', 'ap-wds'); + + o = ss.taboption('roaming', form.Flag, 'rrm_neighbor_report', _('Neighbour Report'), _('802.11k: Enable neighbor report via radio measurements.')); + o.depends({ ieee80211k: '1' }); + o.default = o.enabled; + + o = ss.taboption('roaming', form.Flag, 'rrm_beacon_report', _('Beacon Report'), _('802.11k: Enable beacon report via radio measurements.')); + o.depends({ ieee80211k: '1' }); + o.default = o.enabled; + /* 802.11k settings end */ + + /* 802.11v settings start */ + o = ss.taboption('roaming', form.ListValue, 'time_advertisement', _('Time advertisement'), _('802.11v: Time Advertisement in management frames.')); + o.value('0', _('Disabled')); + o.value('2', _('Enabled')); + o.write = function (section_id, value) { + return this.super('write', [section_id, (value == 2) ? value: null]); + } + + //Pull current System TZ setting + var tz = uci.get('system', '@system[0]', 'timezone'); + o = ss.taboption('roaming', form.Value, 'time_zone', _('Time zone'), _('802.11v: Local Time Zone Advertisement in management frames.')); + o.value(tz); + o.rmempty = true; + + o = ss.taboption('roaming', form.Flag, 'wnm_sleep_mode', _('WNM Sleep Mode'), _('802.11v: Wireless Network Management (WNM) Sleep Mode (extended sleep mode for stations).')); + o.rmempty = true; + + /* wnm_sleep_mode_no_keys: https://git.openwrt.org/?p=openwrt/openwrt.git;a=commitdiff;h=bf98faaac8ed24cf7d3d93dd4fcd7304d109363b */ + o = ss.taboption('roaming', form.Flag, 'wnm_sleep_mode_no_keys', _('WNM Sleep Mode Fixes'), _('802.11v: Wireless Network Management (WNM) Sleep Mode Fixes: Prevents reinstallation attacks.')); + o.rmempty = true; + + o = ss.taboption('roaming', form.Flag, 'bss_transition', _('BSS Transition'), _('802.11v: Basic Service Set (BSS) transition management.')); + o.rmempty = true; + + /* in master, but not 21.02.1: proxy_arp */ + o = ss.taboption('roaming', form.Flag, 'proxy_arp', _('ProxyARP'), _('802.11v: Proxy ARP enables non-AP STA to remain in power-save for longer.')); + o.rmempty = true; + + /* TODO: na_mcast_to_ucast is missing: needs adding to hostapd.sh - nice to have */ + } + /* 802.11v settings end */ + } + + if (hwtype == 'mac80211') { o = ss.taboption('encryption', form.ListValue, 'eap_type', _('EAP-Method')); o.value('tls', 'TLS'); o.value('ttls', 'TTLS'); |