mirror of
https://github.com/Ysurac/openmptcprouter-feeds.git
synced 2025-03-09 15:40:03 +00:00
fix network
This commit is contained in:
parent
ebf0639cbc
commit
ce0298b9a7
11 changed files with 546 additions and 1137 deletions
4
luci-mod-network/Makefile
Normal file → Executable file
4
luci-mod-network/Makefile
Normal file → Executable file
|
@ -1,13 +1,15 @@
|
|||
#
|
||||
# Copyright (C) 2008-2014 The LuCI Team <luci@lists.subsignal.org>
|
||||
# Copyright (C) 2020-2021 Ycarus (Yannick Chabanois) <ycarus@zugaina.org> for OpenMPTCProuter
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
#
|
||||
# From https://github.com/openwrt/luci/commit/b88157e69a060ade618e48b30947729310935d61
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=LuCI Network Administration
|
||||
LUCI_DEPENDS:=+luci-base +rpcd-mod-iwinfo
|
||||
LUCI_DEPENDS:=+luci-base +libiwinfo-lua +rpcd-mod-iwinfo
|
||||
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
|
|
202
luci-mod-network/htdocs/luci-static/resources/tools/network.js
Normal file → Executable file
202
luci-mod-network/htdocs/luci-static/resources/tools/network.js
Normal file → Executable file
|
@ -1,5 +1,4 @@
|
|||
'use strict';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require dom';
|
||||
'require uci';
|
||||
|
@ -150,77 +149,25 @@ function updatePlaceholders(opt, section_id) {
|
|||
}
|
||||
}
|
||||
|
||||
var cbiFlagTristate = form.ListValue.extend({
|
||||
__init__: function(/* ... */) {
|
||||
this.super('__init__', arguments);
|
||||
this.keylist = [ '', '0!', '1!' ];
|
||||
this.vallist = [ _('automatic'), _('disabled'), _('enabled') ];
|
||||
},
|
||||
|
||||
load: function(section_id) {
|
||||
var invert = false, sysfs = this.sysfs;
|
||||
|
||||
if (sysfs) {
|
||||
if (sysfs.charAt(0) == '!') {
|
||||
invert = true;
|
||||
sysfs = sysfs.substring(1);
|
||||
}
|
||||
|
||||
return L.resolveDefault(fs.read(sysfs), '').then(L.bind(function(res) {
|
||||
res = (res || '').trim();
|
||||
|
||||
if (res == '0')
|
||||
this.sysfs_default = invert;
|
||||
else if (res == '1')
|
||||
this.sysfs_default = !invert;
|
||||
|
||||
return this.super('load', [section_id]);
|
||||
}, this));
|
||||
}
|
||||
|
||||
return this.super('load', [section_id]);
|
||||
},
|
||||
|
||||
write: function(section_id, formvalue) {
|
||||
if (formvalue == '1!')
|
||||
return this.super('write', [section_id, '1']);
|
||||
else if (formvalue == '0!')
|
||||
return this.super('write', [section_id, '0']);
|
||||
else
|
||||
return this.super('remove', [section_id]);
|
||||
},
|
||||
|
||||
renderWidget: function(section_id, option_index, cfgvalue) {
|
||||
var sysdef = this.sysfs_default;
|
||||
|
||||
if (this.sysfs_default !== null) {
|
||||
this.keylist[0] = sysdef ? '1' : '0';
|
||||
this.vallist[0] = sysdef ? _('automatic (enabled)') : _('automatic (disabled)');
|
||||
}
|
||||
|
||||
return this.super('renderWidget', [section_id, option_index, cfgvalue]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var cbiTagValue = form.Value.extend({
|
||||
renderWidget: function(section_id, option_index, cfgvalue) {
|
||||
var widget = new ui.Dropdown(cfgvalue || ['-'], {
|
||||
'-': E([], [
|
||||
E('span', { 'class': 'hide-open', 'style': 'font-family:monospace' }, [ '—' ]),
|
||||
E('span', { 'class': 'hide-close' }, [ _('Not Member', 'VLAN port state') ])
|
||||
E('span', { 'class': 'hide-close' }, [ _('Do not participate', 'VLAN port state') ])
|
||||
]),
|
||||
'u': E([], [
|
||||
E('span', { 'class': 'hide-open', 'style': 'font-family:monospace' }, [ 'U' ]),
|
||||
E('span', { 'class': 'hide-close' }, [ _('Untagged', 'VLAN port state') ])
|
||||
E('span', { 'class': 'hide-open', 'style': 'font-family:monospace' }, [ 'u' ]),
|
||||
E('span', { 'class': 'hide-close' }, [ _('Egress untagged', 'VLAN port state') ])
|
||||
]),
|
||||
't': E([], [
|
||||
E('span', { 'class': 'hide-open', 'style': 'font-family:monospace' }, [ 'T' ]),
|
||||
E('span', { 'class': 'hide-close' }, [ _('Tagged', 'VLAN port state') ])
|
||||
E('span', { 'class': 'hide-open', 'style': 'font-family:monospace' }, [ 't' ]),
|
||||
E('span', { 'class': 'hide-close' }, [ _('Egress tagged', 'VLAN port state') ])
|
||||
]),
|
||||
'*': E([], [
|
||||
E('span', { 'class': 'hide-open', 'style': 'font-family:monospace' }, [ '*' ]),
|
||||
E('span', { 'class': 'hide-close' }, [ _('Is Primary VLAN', 'VLAN port state') ])
|
||||
E('span', { 'class': 'hide-close' }, [ _('Primary VLAN ID', 'VLAN port state') ])
|
||||
])
|
||||
}, {
|
||||
id: this.cbid(section_id),
|
||||
|
@ -327,7 +274,7 @@ var cbiTagValue = form.Value.extend({
|
|||
|
||||
var t = /t/.test(s[1] || '') ? 't' : 'u';
|
||||
|
||||
return /\x2a/.test(s[1] || '') ? [t, '*'] : [t];
|
||||
return /\*/.test(s[1] || '') ? [t, '*'] : [t];
|
||||
}
|
||||
|
||||
return ['-'];
|
||||
|
@ -357,7 +304,7 @@ var cbiTagValue = form.Value.extend({
|
|||
}
|
||||
}
|
||||
|
||||
uci.set('network', section_id, 'ports', ports.length ? ports : null);
|
||||
uci.set('network', section_id, 'ports', ports);
|
||||
},
|
||||
|
||||
remove: function() {}
|
||||
|
@ -384,7 +331,6 @@ return baseclass.extend({
|
|||
|
||||
addDeviceOptions: function(s, dev, isNew) {
|
||||
var parent_dev = dev ? dev.getParent() : null,
|
||||
devname = dev ? dev.getName() : null,
|
||||
o, ss;
|
||||
|
||||
s.tab('devgeneral', _('General device options'));
|
||||
|
@ -475,7 +421,7 @@ return baseclass.extend({
|
|||
vid = this.section.formvalue(section_id, 'vid'),
|
||||
name = this.section.getUIElement(section_id, 'name_complex');
|
||||
|
||||
if (base && vid && name && !name.isChanged() && isNew) {
|
||||
if (base && vid && name && !name.isChanged()) {
|
||||
name.setValue('%s.%d'.format(base, vid));
|
||||
name.triggerValidation();
|
||||
}
|
||||
|
@ -673,16 +619,48 @@ return baseclass.extend({
|
|||
o.placeholder = dev ? dev._devstate('qlen') : '';
|
||||
o.datatype = 'uinteger';
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', cbiFlagTristate, 'promisc', _('Enable promiscuous mode'));
|
||||
o.sysfs_default = (dev && dev.dev && dev.dev.flags) ? dev.dev.flags.promisc : null;
|
||||
o = this.replaceOption(s, 'devadvanced', form.Flag, 'promisc', _('Enable promiscuous mode'));
|
||||
o.default = o.disabled;
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', form.Flag, 'autoneg', _('Autonegociation'));
|
||||
o.default = o.enabled;
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', form.Flag, 'gro', _('Generic Receive Offload (GRO)'));
|
||||
o.default = o.enabled;
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', form.Flag, 'gso', _('Generic Segmentation Offload (GSO)'));
|
||||
o.default = o.enabled;
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', form.Flag, 'tso', _('TCP Segmentation Offload (TSO)'));
|
||||
o.default = o.enabled;
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', form.Flag, 'lro', _('Large Receive Offload (LRO)'));
|
||||
o.default = o.enabled;
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', form.Flag, 'ufo', _('UDP Fragmentation Offload (UFO)'));
|
||||
o.default = o.enabled;
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', form.Value, 'speed', _('Speed'));
|
||||
o.placeholder = dev ? dev.getSpeed() : '';
|
||||
o.default = '';
|
||||
o.rmempty = true;
|
||||
o.datatype = 'uinteger';
|
||||
o.depends('autoneg', '0');
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', form.ListValue, 'duplex', _('Duplex'));
|
||||
o.default = '';
|
||||
o.value('', _('unknown'));
|
||||
o.value('half', _('half'));
|
||||
o.value('full', _('full'));
|
||||
o.depends('autoneg', '0');
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', form.ListValue, 'rpfilter', _('Reverse path filter'));
|
||||
o.default = '';
|
||||
o.value('', _('disabled'));
|
||||
o.value('loose', _('Loose filtering'));
|
||||
o.value('strict', _('Strict filtering'));
|
||||
o.cfgvalue = function(/* ... */) {
|
||||
var val = form.ListValue.prototype.cfgvalue.apply(this, arguments);
|
||||
o.cfgvalue = function(section_id) {
|
||||
var val = form.ListValue.prototype.cfgvalue.apply(this, [section_id]);
|
||||
|
||||
switch (val || '') {
|
||||
case 'loose':
|
||||
|
@ -698,17 +676,11 @@ return baseclass.extend({
|
|||
}
|
||||
};
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', cbiFlagTristate, 'acceptlocal', _('Accept local'), _('Accept packets with local source addresses'));
|
||||
o.sysfs = '/proc/sys/net/ipv4/conf/%s/accept_local'.format(devname || 'default');
|
||||
o = this.replaceOption(s, 'devadvanced', form.Flag, 'acceptlocal', _('Accept local'), _('Accept packets with local source addresses'));
|
||||
o.default = o.disabled;
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', cbiFlagTristate, 'sendredirects', _('Send ICMP redirects'));
|
||||
o.sysfs = '/proc/sys/net/ipv4/conf/%s/send_redirects'.format(devname || 'default');
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', cbiFlagTristate, 'arp_accept ', _('Honor gratuitous ARP'), _('When enabled, new ARP table entries are added from received gratuitous APR requests or replies, otherwise only preexisting table entries are updated, but no new hosts are learned.'));
|
||||
o.sysfs = '/proc/sys/net/ipv4/conf/%s/arp_accept'.format(devname || 'default');
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', cbiFlagTristate, 'drop_gratuitous_arp', _('Drop gratuitous ARP'), _('Drop all gratuitous ARP frames, for example if there’s a known good ARP proxy on the network and such frames need not be used or in the case of 802.11, must not be used to prevent attacks.'));
|
||||
o.sysfs = '/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp'.format(devname || 'default');
|
||||
o = this.replaceOption(s, 'devadvanced', form.Flag, 'sendredirects', _('Send ICMP redirects'));
|
||||
o.default = o.enabled;
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', form.Value, 'neighreachabletime', _('Neighbour cache validity'), _('Time in milliseconds'));
|
||||
o.placeholder = '30000';
|
||||
|
@ -722,75 +694,63 @@ return baseclass.extend({
|
|||
o.placeholder = '0';
|
||||
o.datatype = 'uinteger';
|
||||
|
||||
o = this.replaceOption(s, 'devgeneral', cbiFlagTristate, 'ipv6', _('Enable IPv6'));
|
||||
o.sysfs = '!/proc/sys/net/ipv6/conf/%s/disable_ipv6'.format(devname || 'default');
|
||||
o = this.replaceOption(s, 'devadvanced', form.Value, 'ttl', _('Force TTL'), _('Some LTE providers detect tethering by inspecting packet TTL values'));
|
||||
o.placeholder = '65';
|
||||
o.datatype = 'uinteger';
|
||||
|
||||
o = this.replaceOption(s, 'devgeneral', form.Flag, 'ipv6', _('Enable IPv6'));
|
||||
o.migrate = false;
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', cbiFlagTristate, 'ip6segmentrouting', _('Enable IPv6 segment routing'));
|
||||
o.sysfs = '/proc/sys/net/ipv6/conf/%s/seg6_enabled'.format(devname || 'default');
|
||||
o.depends('ipv6', /1/);
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', cbiFlagTristate, 'drop_unsolicited_na', _('Drop unsolicited NA'), _('Drop all unsolicited neighbor advertisements, for example if there’s a known good NA proxy on the network and such frames need not be used or in the case of 802.11, must not be used to prevent attacks.'));
|
||||
o.sysfs = '/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na'.format(devname || 'default');
|
||||
o.depends('ipv6', /1/);
|
||||
o.default = o.enabled;
|
||||
|
||||
o = this.replaceOption(s, 'devgeneral', form.Value, 'mtu6', _('IPv6 MTU'));
|
||||
o.datatype = 'max(9200)';
|
||||
o.depends('ipv6', /1/);
|
||||
o.depends('ipv6', '1');
|
||||
|
||||
o = this.replaceOption(s, 'devgeneral', form.Value, 'dadtransmits', _('DAD transmits'), _('Amount of Duplicate Address Detection probes to send'));
|
||||
o.placeholder = '1';
|
||||
o.datatype = 'uinteger';
|
||||
o.depends('ipv6', /1/);
|
||||
o.depends('ipv6', '1');
|
||||
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', cbiFlagTristate, 'multicast', _('Enable multicast support'));
|
||||
o.sysfs_default = (dev && dev.dev && dev.dev.flags) ? dev.dev.flags.multicast : null;
|
||||
o = this.replaceOption(s, 'devadvanced', form.Flag, 'multicast', _('Enable multicast support'));
|
||||
o.default = o.enabled;
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', form.ListValue, 'igmpversion', _('Force IGMP version'));
|
||||
o.value('', _('No enforcement'));
|
||||
o.value('1', _('Enforce IGMPv1'));
|
||||
o.value('2', _('Enforce IGMPv2'));
|
||||
o.value('3', _('Enforce IGMPv3'));
|
||||
o.depends('multicast', /1/);
|
||||
o.depends('multicast', '1');
|
||||
|
||||
o = this.replaceOption(s, 'devadvanced', form.ListValue, 'mldversion', _('Force MLD version'));
|
||||
o.value('', _('No enforcement'));
|
||||
o.value('1', _('Enforce MLD version 1'));
|
||||
o.value('2', _('Enforce MLD version 2'));
|
||||
o.depends('multicast', /1/);
|
||||
o.depends('multicast', '1');
|
||||
|
||||
if (isBridgePort(dev)) {
|
||||
o = this.replaceOption(s, 'brport', cbiFlagTristate, 'learning', _('Enable MAC address learning'));
|
||||
o.sysfs = '/sys/class/net/%s/brport/learning'.format(devname || 'default');
|
||||
o = this.replaceOption(s, 'brport', form.Flag, 'learning', _('Enable MAC address learning'));
|
||||
o.default = o.enabled;
|
||||
|
||||
o = this.replaceOption(s, 'brport', cbiFlagTristate, 'unicast_flood', _('Enable unicast flooding'));
|
||||
o.sysfs = '/sys/class/net/%s/brport/unicast_flood'.format(devname || 'default');
|
||||
o = this.replaceOption(s, 'brport', form.Flag, 'unicast_flood', _('Enable unicast flooding'));
|
||||
o.default = o.enabled;
|
||||
|
||||
o = this.replaceOption(s, 'brport', cbiFlagTristate, 'isolate', _('Port isolation'), _('Only allow communication with non-isolated bridge ports when enabled'));
|
||||
o.sysfs = '/sys/class/net/%s/brport/isolated'.format(devname || 'default');
|
||||
o = this.replaceOption(s, 'brport', form.Flag, 'isolated', _('Port isolation'), _('Only allow communication with non-isolated bridge ports when enabled'));
|
||||
o.default = o.disabled;
|
||||
|
||||
o = this.replaceOption(s, 'brport', form.ListValue, 'multicast_router', _('Multicast routing'));
|
||||
o.value('', _('Never'));
|
||||
o.value('1', _('Learn'));
|
||||
o.value('2', _('Always'));
|
||||
o.depends('multicast', /1/);
|
||||
o.depends('multicast', '1');
|
||||
|
||||
o = this.replaceOption(s, 'brport', cbiFlagTristate, 'multicast_to_unicast', _('Multicast to unicast'), _('Forward multicast packets as unicast packets on this device.'));
|
||||
o.sysfs = '/sys/class/net/%s/brport/multicast_to_unicast'.format(devname || 'default');
|
||||
o.depends('multicast', /1/);
|
||||
o = this.replaceOption(s, 'brport', form.Flag, 'multicast_to_unicast', _('Multicast to unicast'), _('Forward multicast packets as unicast packets on this device.'));
|
||||
o.default = o.disabled;
|
||||
o.depends('multicast', '1');
|
||||
|
||||
o = this.replaceOption(s, 'brport', cbiFlagTristate, 'multicast_fast_leave', _('Enable multicast fast leave'));
|
||||
o.sysfs = '/sys/class/net/%s/brport/multicast_fast_leave'.format(devname || 'default');
|
||||
o.depends('multicast', /1/);
|
||||
|
||||
o = this.replaceOption(s, 'brport', cbiFlagTristate, 'drop_v4_unicast_in_l2_multicast', _('Drop nested IPv4 unicast'), _('Drop layer 2 multicast frames containing IPv4 unicast packets.'));
|
||||
o.sysfs = '/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast'.format(devname || 'default');
|
||||
o.depends('multicast', /1/);
|
||||
|
||||
o = this.replaceOption(s, 'brport', cbiFlagTristate, 'drop_v6_unicast_in_l2_multicast', _('Drop nested IPv6 unicast'), _('Drop layer 2 multicast frames containing IPv6 unicast packets.'));
|
||||
o.sysfs = '/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast'.format(devname || 'default');
|
||||
o.depends('multicast', /1/);
|
||||
o = this.replaceOption(s, 'brport', form.Flag, 'multicast_fast_leave', _('Enable multicast fast leave'));
|
||||
o.default = o.disabled;
|
||||
o.depends('multicast', '1');
|
||||
}
|
||||
|
||||
o = this.replaceOption(s, 'bridgevlan', form.Flag, 'vlan_filtering', _('Enable VLAN filtering'));
|
||||
|
@ -856,8 +816,6 @@ return baseclass.extend({
|
|||
return network.instantiateDevice(port)
|
||||
}).filter(function(dev) {
|
||||
return dev.getType() != 'wifi' || dev.isUp();
|
||||
}).sort(function(a, b) {
|
||||
return L.naturalCompare(a.getName(), b.getName());
|
||||
});
|
||||
|
||||
this.children = this.children.filter(function(opt) { return !opt.option.match(/^port_/) });
|
||||
|
@ -971,6 +929,18 @@ return baseclass.extend({
|
|||
for (var port_name in seen_ports)
|
||||
ports.push(port_name);
|
||||
|
||||
ports.sort(function(a, b) {
|
||||
var m1 = a.match(/^(.+?)([0-9]*)$/),
|
||||
m2 = b.match(/^(.+?)([0-9]*)$/);
|
||||
|
||||
if (m1[1] < m2[1])
|
||||
return -1;
|
||||
else if (m1[1] > m2[1])
|
||||
return 1;
|
||||
else
|
||||
return +(m1[2] || 0) - +(m2[2] || 0);
|
||||
});
|
||||
|
||||
ss.updatePorts(ports);
|
||||
},
|
||||
|
||||
|
|
727
luci-mod-network/htdocs/luci-static/resources/view/network/dhcp.js
Normal file → Executable file
727
luci-mod-network/htdocs/luci-static/resources/view/network/dhcp.js
Normal file → Executable file
|
@ -5,9 +5,7 @@
|
|||
'require rpc';
|
||||
'require uci';
|
||||
'require form';
|
||||
'require network';
|
||||
'require validation';
|
||||
'require tools.widgets as widgets';
|
||||
|
||||
var callHostHints, callDUIDHints, callDHCPLeases, CBILeaseStatus, CBILease6Status;
|
||||
|
||||
|
@ -67,58 +65,6 @@ CBILease6Status = form.DummyValue.extend({
|
|||
}
|
||||
});
|
||||
|
||||
function calculateNetwork(addr, mask) {
|
||||
addr = validation.parseIPv4(String(addr));
|
||||
|
||||
if (!isNaN(mask))
|
||||
mask = validation.parseIPv4(network.prefixToMask(+mask));
|
||||
else
|
||||
mask = validation.parseIPv4(String(mask));
|
||||
|
||||
if (addr == null || mask == null)
|
||||
return null;
|
||||
|
||||
return [
|
||||
[
|
||||
addr[0] & (mask[0] >>> 0 & 255),
|
||||
addr[1] & (mask[1] >>> 0 & 255),
|
||||
addr[2] & (mask[2] >>> 0 & 255),
|
||||
addr[3] & (mask[3] >>> 0 & 255)
|
||||
].join('.'),
|
||||
mask.join('.')
|
||||
];
|
||||
}
|
||||
|
||||
function getDHCPPools() {
|
||||
return uci.load('dhcp').then(function() {
|
||||
let sections = uci.sections('dhcp', 'dhcp'),
|
||||
tasks = [], pools = [];
|
||||
|
||||
for (var i = 0; i < sections.length; i++) {
|
||||
if (sections[i].ignore == '1' || !sections[i].interface)
|
||||
continue;
|
||||
|
||||
tasks.push(network.getNetwork(sections[i].interface).then(L.bind(function(section_id, net) {
|
||||
var cidr = net ? (net.getIPAddrs()[0] || '').split('/') : null;
|
||||
|
||||
if (cidr && cidr.length == 2) {
|
||||
var net_mask = calculateNetwork(cidr[0], cidr[1]);
|
||||
|
||||
pools.push({
|
||||
section_id: section_id,
|
||||
network: net_mask[0],
|
||||
netmask: net_mask[1]
|
||||
});
|
||||
}
|
||||
}, null, sections[i]['.name'])));
|
||||
}
|
||||
|
||||
return Promise.all(tasks).then(function() {
|
||||
return pools;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function validateHostname(sid, s) {
|
||||
if (s == null || s == '')
|
||||
return true;
|
||||
|
@ -126,7 +72,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,15 +102,13 @@ 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'));
|
||||
|
||||
if (m[1] != '//' && m[1] != '/#/') {
|
||||
var res = validateAddressList(sid, m[1]);
|
||||
if (res !== true)
|
||||
return res;
|
||||
}
|
||||
var res = validateAddressList(sid, m[1]);
|
||||
if (res !== true)
|
||||
return res;
|
||||
|
||||
if (m[2] == '' || m[2] == '#')
|
||||
return true;
|
||||
|
@ -194,552 +138,274 @@ function validateServerSpec(sid, s) {
|
|||
return true;
|
||||
}
|
||||
|
||||
function validateMACAddr(pools, sid, s) {
|
||||
if (s == null || s == '')
|
||||
return true;
|
||||
|
||||
var leases = uci.sections('dhcp', 'host'),
|
||||
this_macs = L.toArray(s).map(function(m) { return m.toUpperCase() });
|
||||
|
||||
for (var i = 0; i < pools.length; i++) {
|
||||
var this_net_mask = calculateNetwork(this.section.formvalue(sid, 'ip'), pools[i].netmask);
|
||||
|
||||
if (!this_net_mask)
|
||||
continue;
|
||||
|
||||
for (var j = 0; j < leases.length; j++) {
|
||||
if (leases[j]['.name'] == sid || !leases[j].ip)
|
||||
continue;
|
||||
|
||||
var lease_net_mask = calculateNetwork(leases[j].ip, pools[i].netmask);
|
||||
|
||||
if (!lease_net_mask || this_net_mask[0] != lease_net_mask[0])
|
||||
continue;
|
||||
|
||||
var lease_macs = L.toArray(leases[j].mac).map(function(m) { return m.toUpperCase() });
|
||||
|
||||
for (var k = 0; k < lease_macs.length; k++)
|
||||
for (var l = 0; l < this_macs.length; l++)
|
||||
if (lease_macs[k] == this_macs[l])
|
||||
return _('The MAC address %h is already used by another static lease in the same DHCP pool').format(this_macs[l]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
callHostHints(),
|
||||
callDUIDHints(),
|
||||
getDHCPPools(),
|
||||
network.getDevices()
|
||||
callDUIDHints()
|
||||
]);
|
||||
},
|
||||
|
||||
render: function(hosts_duids_pools) {
|
||||
render: function(hosts_duids) {
|
||||
var has_dhcpv6 = L.hasSystemFeature('dnsmasq', 'dhcpv6') || L.hasSystemFeature('odhcpd'),
|
||||
hosts = hosts_duids_pools[0],
|
||||
duids = hosts_duids_pools[1],
|
||||
pools = hosts_duids_pools[2],
|
||||
ndevs = hosts_duids_pools[3],
|
||||
hosts = hosts_duids[0],
|
||||
duids = hosts_duids[1],
|
||||
m, s, o, ss, so;
|
||||
|
||||
m = new form.Map('dhcp', _('DHCP and DNS'),
|
||||
_('Dnsmasq is a lightweight <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr> server and <abbr title="Domain Name System">DNS</abbr> forwarder.'));
|
||||
m = new form.Map('dhcp', _('DHCP and DNS'), _('Dnsmasq is a combined <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr>-Server and <abbr title="Domain Name System">DNS</abbr>-Forwarder for <abbr title="Network Address Translation">NAT</abbr> firewalls'));
|
||||
|
||||
s = m.section(form.TypedSection, 'dnsmasq');
|
||||
s = m.section(form.TypedSection, 'dnsmasq', _('Server Settings'));
|
||||
s.anonymous = true;
|
||||
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('tftp', _('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',
|
||||
_('Domain required'),
|
||||
_('Do not forward DNS queries without dots or domain parts.'));
|
||||
_('Don\'t forward <abbr title="Domain Name System">DNS</abbr>-Requests without <abbr title="Domain Name System">DNS</abbr>-Name'));
|
||||
|
||||
s.taboption('general', form.Flag, 'authoritative',
|
||||
_('Authoritative'),
|
||||
_('This is the only DHCP server in the local network.'));
|
||||
_('This is the only <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr> in the local network'));
|
||||
|
||||
s.taboption('general', form.Value, 'local',
|
||||
_('Local server'),
|
||||
_('Never forward matching domains and subdomains, resolve from DHCP or hosts files only.'));
|
||||
|
||||
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, 'logqueries',
|
||||
_('Log queries'),
|
||||
_('Write received DNS queries to syslog.'));
|
||||
o.optional = true;
|
||||
|
||||
o = s.taboption('general', form.DynamicList, 'server',
|
||||
_('DNS forwardings'),
|
||||
_('List of upstream resolvers to forward queries to.'));
|
||||
o.optional = true;
|
||||
o.placeholder = '/example.org/10.1.2.3';
|
||||
o.validate = validateServerSpec;
|
||||
|
||||
o = s.taboption('general', form.DynamicList, 'address',
|
||||
_('Addresses'),
|
||||
_('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/router.lan/192.168.0.1';
|
||||
|
||||
o = s.taboption('general', form.DynamicList, 'ipset',
|
||||
_('IP sets'),
|
||||
_('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';
|
||||
|
||||
o = s.taboption('general', form.Flag, 'rebind_protection',
|
||||
_('Rebind protection'),
|
||||
_('Discard upstream responses containing <a href="%s">RFC1918</a> addresses.').format('https://datatracker.ietf.org/doc/html/rfc1918'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('general', form.Flag, 'rebind_localhost',
|
||||
_('Allow localhost'),
|
||||
_('Exempt <code>127.0.0.0/8</code> and <code>::1</code> from rebinding checks, e.g. for RBL services.'));
|
||||
o.depends('rebind_protection', '1');
|
||||
|
||||
o = s.taboption('general', form.DynamicList, 'rebind_domain',
|
||||
_('Domain whitelist'),
|
||||
_('List of domains to allow RFC1918 responses for.'));
|
||||
o.depends('rebind_protection', '1');
|
||||
o.optional = true;
|
||||
o.placeholder = 'ihost.netflix.com';
|
||||
o.validate = validateAddressList;
|
||||
|
||||
o = s.taboption('general', form.Flag, 'localservice',
|
||||
_('Local service only'),
|
||||
_('Accept DNS queries only from hosts whose address is on a local subnet.'));
|
||||
o.optional = false;
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('general', form.Flag, 'nonwildcard',
|
||||
_('Non-wildcard'),
|
||||
_('Bind dynamically to interfaces rather than wildcard address.'));
|
||||
o.default = o.enabled;
|
||||
o.optional = false;
|
||||
o.rmempty = true;
|
||||
|
||||
o = s.taboption('general', form.DynamicList, 'interface',
|
||||
_('Listen interfaces'),
|
||||
_('Listen only on the specified interfaces, and loopback if not excluded explicitly.'));
|
||||
o.optional = true;
|
||||
o.placeholder = 'lan';
|
||||
|
||||
o = s.taboption('general', form.DynamicList, 'notinterface',
|
||||
_('Exclude interfaces'),
|
||||
_('Do not listen on the specified interfaces.'));
|
||||
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.'));
|
||||
_('Read <code>/etc/ethers</code> to configure the <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr>-Server'));
|
||||
|
||||
s.taboption('files', form.Value, 'leasefile',
|
||||
_('Lease file'),
|
||||
_('File to store DHCP lease information.'));
|
||||
_('Leasefile'),
|
||||
_('file where given <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr>-leases will be stored'));
|
||||
|
||||
o = s.taboption('files', form.Flag, 'noresolv',
|
||||
_('Ignore resolv file'));
|
||||
o.optional = true;
|
||||
s.taboption('files', form.Flag, 'noresolv',
|
||||
_('Ignore resolve file')).optional = true;
|
||||
|
||||
o = s.taboption('files', form.Value, 'resolvfile',
|
||||
_('Resolv file'),
|
||||
_('File with upstream resolvers.'));
|
||||
_('Resolve file'),
|
||||
_('local <abbr title="Domain Name System">DNS</abbr> file'));
|
||||
|
||||
o.depends('noresolv', '0');
|
||||
o.placeholder = '/tmp/resolv.conf.d/resolv.conf.auto';
|
||||
o.optional = true;
|
||||
|
||||
o = s.taboption('files', form.Flag, 'nohosts',
|
||||
_('Ignore <code>/etc/hosts</code>'));
|
||||
o.optional = true;
|
||||
|
||||
o = s.taboption('files', form.DynamicList, 'addnhosts',
|
||||
_('Additional hosts files'));
|
||||
o.optional = true;
|
||||
o.placeholder = '/etc/dnsmasq.hosts';
|
||||
s.taboption('files', form.Flag, 'nohosts',
|
||||
_('Ignore <code>/etc/hosts</code>')).optional = true;
|
||||
|
||||
s.taboption('files', form.DynamicList, 'addnhosts',
|
||||
_('Additional Hosts files')).optional = true;
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'quietdhcp',
|
||||
_('Suppress logging'),
|
||||
_('Suppress logging of the routine operation for the DHCP protocol.'));
|
||||
_('Suppress logging of the routine operation of these protocols'));
|
||||
o.optional = true;
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'sequential_ip',
|
||||
_('Allocate IPs sequentially'),
|
||||
_('Allocate IP addresses sequentially, starting from the lowest available address.'));
|
||||
_('Allocate IP sequentially'),
|
||||
_('Allocate IP addresses sequentially, starting from the lowest available address'));
|
||||
o.optional = true;
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'boguspriv',
|
||||
_('Filter private'),
|
||||
_('Do not forward reverse lookups for local networks.'));
|
||||
_('Do not forward reverse lookups for local networks'));
|
||||
o.default = o.enabled;
|
||||
|
||||
s.taboption('advanced', form.Flag, 'filterwin2k',
|
||||
_('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.'));
|
||||
_('Filter useless'),
|
||||
_('Do not forward requests that cannot be answered by public name servers'));
|
||||
|
||||
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'),
|
||||
_('Return answers to DNS queries matching the subnet from which the query was received if multiple IPs are available.'));
|
||||
_('Localise hostname depending on the requesting subnet if multiple IPs are available'));
|
||||
|
||||
if (L.hasSystemFeature('dnsmasq', 'dnssec')) {
|
||||
o = s.taboption('advanced', form.Flag, 'dnssec',
|
||||
_('DNSSEC'),
|
||||
_('Validate DNS replies and cache DNSSEC data, requires upstream to support DNSSEC.'));
|
||||
_('DNSSEC'));
|
||||
o.optional = true;
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'dnsseccheckunsigned',
|
||||
_('DNSSEC check unsigned'),
|
||||
_('Verify unsigned domain responses really come from unsigned domains.'));
|
||||
_('Requires upstream supports DNSSEC; verify unsigned domain responses really come from unsigned domains'));
|
||||
o.default = o.enabled;
|
||||
o.optional = true;
|
||||
}
|
||||
|
||||
s.taboption('general', form.Value, 'local',
|
||||
_('Local server'),
|
||||
_('Local domain specification. Names matching this domain are never forwarded and are resolved from DHCP or hosts files only'));
|
||||
|
||||
s.taboption('general', form.Value, 'domain',
|
||||
_('Local domain'),
|
||||
_('Local domain suffix appended to DHCP names and hosts file entries'));
|
||||
|
||||
s.taboption('advanced', form.Flag, 'expandhosts',
|
||||
_('Expand hosts'),
|
||||
_('Add local domain suffix to names served from hosts files.'));
|
||||
_('Add local domain suffix to names served from hosts files'));
|
||||
|
||||
s.taboption('advanced', form.Flag, 'nonegcache',
|
||||
_('No negative cache'),
|
||||
_('Do not cache negative replies, e.g. for non-existent domains.'));
|
||||
_('Do not cache negative replies, e.g. for not existing domains'));
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'serversfile',
|
||||
s.taboption('advanced', form.Value, 'serversfile',
|
||||
_('Additional servers file'),
|
||||
_('File listing upstream resolvers, optionally domain-specific, e.g. <code>server=1.2.3.4</code>, <code>server=/domain/1.2.3.4</code>.'));
|
||||
o.placeholder = '/etc/dnsmasq.servers';
|
||||
_('This file may contain lines like \'server=/domain/1.2.3.4\' or \'server=1.2.3.4\' for domain-specific or full upstream <abbr title="Domain Name System">DNS</abbr> servers.'));
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'strictorder',
|
||||
s.taboption('advanced', form.Flag, 'strictorder',
|
||||
_('Strict order'),
|
||||
_('Upstream resolvers will be queried in the order of the resolv file.'));
|
||||
_('<abbr title="Domain Name System">DNS</abbr> servers will be queried in the order of the resolvfile')).optional = true;
|
||||
|
||||
s.taboption('advanced', form.Flag, 'allservers',
|
||||
_('All Servers'),
|
||||
_('Query all available upstream <abbr title="Domain Name System">DNS</abbr> servers')).optional = true;
|
||||
|
||||
o = s.taboption('advanced', form.DynamicList, 'bogusnxdomain', _('Bogus NX Domain Override'),
|
||||
_('List of hosts that supply bogus NX domain results'));
|
||||
|
||||
o.optional = true;
|
||||
o.placeholder = '67.215.65.132';
|
||||
|
||||
|
||||
s.taboption('general', form.Flag, 'logqueries',
|
||||
_('Log queries'),
|
||||
_('Write received DNS requests to syslog')).optional = true;
|
||||
|
||||
o = s.taboption('general', form.DynamicList, 'server', _('DNS forwardings'),
|
||||
_('List of <abbr title="Domain Name System">DNS</abbr> servers to forward requests to'));
|
||||
|
||||
o.optional = true;
|
||||
o.placeholder = '/example.org/10.1.2.3';
|
||||
o.validate = validateServerSpec;
|
||||
|
||||
|
||||
o = s.taboption('general', form.DynamicList, 'address', _('Addresses'),
|
||||
_('List of domains to force to an IP address.'));
|
||||
|
||||
o.optional = true;
|
||||
o.placeholder = '/router.local/192.168.0.1';
|
||||
|
||||
|
||||
o = s.taboption('general', form.Flag, 'rebind_protection',
|
||||
_('Rebind protection'),
|
||||
_('Discard upstream RFC1918 responses'));
|
||||
|
||||
o.rmempty = false;
|
||||
|
||||
|
||||
o = s.taboption('general', form.Flag, 'rebind_localhost',
|
||||
_('Allow localhost'),
|
||||
_('Allow upstream responses in the 127.0.0.0/8 range, e.g. for RBL services'));
|
||||
|
||||
o.depends('rebind_protection', '1');
|
||||
|
||||
|
||||
o = s.taboption('general', form.DynamicList, 'rebind_domain',
|
||||
_('Domain whitelist'),
|
||||
_('List of domains to allow RFC1918 responses for'));
|
||||
o.optional = true;
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'allservers',
|
||||
_('All servers'),
|
||||
_('Query all available upstream resolvers.'));
|
||||
o.optional = true;
|
||||
o.depends('rebind_protection', '1');
|
||||
o.placeholder = 'ihost.netflix.com';
|
||||
o.validate = validateAddressList;
|
||||
|
||||
o = s.taboption('advanced', form.DynamicList, 'bogusnxdomain',
|
||||
_('IPs to override with NXDOMAIN'),
|
||||
_('List of IP addresses to convert into NXDOMAIN responses.'));
|
||||
o.optional = true;
|
||||
o.placeholder = '64.94.110.11';
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'port',
|
||||
_('DNS server port'),
|
||||
_('Listening port for inbound DNS queries.'));
|
||||
_('<abbr title="Domain Name System">DNS</abbr> server port'),
|
||||
_('Listening port for inbound DNS queries'));
|
||||
|
||||
o.optional = true;
|
||||
o.datatype = 'port';
|
||||
o.placeholder = 53;
|
||||
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'queryport',
|
||||
_('DNS query port'),
|
||||
_('Fixed source port for outbound DNS queries.'));
|
||||
_('<abbr title="Domain Name System">DNS</abbr> query port'),
|
||||
_('Fixed source port for outbound DNS queries'));
|
||||
|
||||
o.optional = true;
|
||||
o.datatype = 'port';
|
||||
o.placeholder = _('any');
|
||||
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'dhcpleasemax',
|
||||
_('Max. DHCP leases'),
|
||||
_('Maximum allowed number of active DHCP leases.'));
|
||||
_('<abbr title="maximal">Max.</abbr> <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr> leases'),
|
||||
_('Maximum allowed number of active DHCP leases'));
|
||||
|
||||
o.optional = true;
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = _('unlimited');
|
||||
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'ednspacket_max',
|
||||
_('Max. EDNS0 packet size'),
|
||||
_('Maximum allowed size of EDNS0 UDP packets.'));
|
||||
_('<abbr title="maximal">Max.</abbr> <abbr title="Extension Mechanisms for Domain Name System">EDNS0</abbr> packet size'),
|
||||
_('Maximum allowed size of EDNS.0 UDP packets'));
|
||||
|
||||
o.optional = true;
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = 1280;
|
||||
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'dnsforwardmax',
|
||||
_('Max. concurrent queries'),
|
||||
_('Maximum allowed number of concurrent DNS queries.'));
|
||||
_('<abbr title="maximal">Max.</abbr> concurrent queries'),
|
||||
_('Maximum allowed number of concurrent DNS queries'));
|
||||
|
||||
o.optional = true;
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = 150;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'cachesize',
|
||||
_('Size of DNS query cache'),
|
||||
_('Number of cached DNS entries, 10000 is maximum, 0 is no caching.'));
|
||||
_('Number of cached DNS entries (max is 10000, 0 is no caching)'));
|
||||
o.optional = true;
|
||||
o.datatype = 'range(0,10000)';
|
||||
o.placeholder = 1000;
|
||||
o.placeholder = 150;
|
||||
|
||||
o = s.taboption('pxe_tftp', form.Flag, 'enable_tftp',
|
||||
_('Enable TFTP server'),
|
||||
_('Enable the built-in single-instance TFTP server.'));
|
||||
o.optional = true;
|
||||
s.taboption('tftp', form.Flag, 'enable_tftp',
|
||||
_('Enable TFTP server')).optional = true;
|
||||
|
||||
o = s.taboption('pxe_tftp', form.Value, 'tftp_root',
|
||||
o = s.taboption('tftp', form.Value, 'tftp_root',
|
||||
_('TFTP server root'),
|
||||
_('Root directory for files served via TFTP. <em>Enable TFTP server</em> and <em>TFTP server root</em> turn on the TFTP server and serve files from <em>TFTP server root</em>.'));
|
||||
o.depends('enable_tftp', '1');
|
||||
_('Root directory for files served via TFTP'));
|
||||
|
||||
o.optional = true;
|
||||
o.depends('enable_tftp', '1');
|
||||
o.placeholder = '/';
|
||||
|
||||
o = s.taboption('pxe_tftp', form.Value, 'dhcp_boot',
|
||||
|
||||
o = s.taboption('tftp', form.Value, 'dhcp_boot',
|
||||
_('Network boot image'),
|
||||
_('Filename of the boot image advertised to clients.'));
|
||||
o.depends('enable_tftp', '1');
|
||||
_('Filename of the boot image advertised to clients'));
|
||||
|
||||
o.optional = true;
|
||||
o.depends('enable_tftp', '1');
|
||||
o.placeholder = 'pxelinux.0';
|
||||
|
||||
/* PXE - https://openwrt.org/docs/guide-user/base-system/dhcp#booting_options */
|
||||
o = s.taboption('pxe_tftp', form.SectionValue, '__pxe__', form.GridSection, 'boot', null,
|
||||
_('Special <abbr title="Preboot eXecution Environment">PXE</abbr> boot options for Dnsmasq.'));
|
||||
ss = o.subsection;
|
||||
ss.addremove = true;
|
||||
ss.anonymous = true;
|
||||
ss.nodescriptions = true;
|
||||
o = s.taboption('general', form.Flag, 'localservice',
|
||||
_('Local Service Only'),
|
||||
_('Limit DNS service to subnets interfaces on which we are serving DNS.'));
|
||||
o.optional = false;
|
||||
o.rmempty = false;
|
||||
|
||||
so = ss.option(form.Value, 'filename',
|
||||
_('Filename'),
|
||||
_('Host requests this filename from the boot server.'));
|
||||
so.optional = false;
|
||||
so.placeholder = 'pxelinux.0';
|
||||
o = s.taboption('general', form.Flag, 'nonwildcard',
|
||||
_('Non-wildcard'),
|
||||
_('Bind dynamically to interfaces rather than wildcard address (recommended as linux default)'));
|
||||
o.default = o.enabled;
|
||||
o.optional = false;
|
||||
o.rmempty = true;
|
||||
|
||||
so = ss.option(form.Value, 'servername',
|
||||
_('Server name'),
|
||||
_('The hostname of the boot server'));
|
||||
so.optional = false;
|
||||
so.placeholder = 'myNAS';
|
||||
o = s.taboption('general', form.DynamicList, 'interface',
|
||||
_('Listen Interfaces'),
|
||||
_('Limit listening to these interfaces, and loopback.'));
|
||||
o.optional = true;
|
||||
|
||||
so = ss.option(form.Value, 'serveraddress',
|
||||
_('Server address'),
|
||||
_('The IP address of the boot server'));
|
||||
so.optional = false;
|
||||
so.placeholder = '192.168.1.2';
|
||||
|
||||
so = ss.option(form.DynamicList, 'dhcp_option',
|
||||
_('DHCP Options'),
|
||||
_('Options for the Network-ID. (Note: needs also Network-ID.) E.g. "<code>42,192.168.1.4</code>" for NTP server, "<code>3,192.168.4.4</code>" for default route. <code>0.0.0.0</code> means "the address of the system running dnsmasq".'));
|
||||
so.optional = true;
|
||||
so.placeholder = '42,192.168.1.4';
|
||||
|
||||
so = ss.option(widgets.DeviceSelect, 'networkid',
|
||||
_('Network-ID'),
|
||||
_('Apply DHCP Options to this net. (Empty = all clients).'));
|
||||
so.optional = true;
|
||||
so.noaliases = true;
|
||||
|
||||
so = ss.option(form.Flag, 'force',
|
||||
_('Force'),
|
||||
_('Always send DHCP Options. Sometimes needed, with e.g. PXELinux.'));
|
||||
so.optional = true;
|
||||
|
||||
so = ss.option(form.Value, 'instance',
|
||||
_('Instance'),
|
||||
_('Dnsmasq instance to which this boot section is bound. If unspecified, the section is valid for all dnsmasq instances.'));
|
||||
so.optional = true;
|
||||
|
||||
Object.values(L.uci.sections('dhcp', 'dnsmasq')).forEach(function(val, index) {
|
||||
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.'));
|
||||
|
||||
ss = o.subsection;
|
||||
|
||||
ss.addremove = true;
|
||||
ss.anonymous = true;
|
||||
ss.sortable = true;
|
||||
|
||||
so = ss.option(form.Value, 'name', _('Hostname'));
|
||||
so.rmempty = false;
|
||||
so.datatype = 'hostname';
|
||||
|
||||
so = ss.option(form.Value, 'ip', _('IP address'));
|
||||
so.rmempty = false;
|
||||
so.datatype = 'ipaddr';
|
||||
|
||||
var ipaddrs = {};
|
||||
|
||||
Object.keys(hosts).forEach(function(mac) {
|
||||
var addrs = L.toArray(hosts[mac].ipaddrs || hosts[mac].ipv4);
|
||||
|
||||
for (var i = 0; i < addrs.length; i++)
|
||||
ipaddrs[addrs[i]] = hosts[mac].name || mac;
|
||||
});
|
||||
|
||||
L.sortedKeys(ipaddrs, null, 'addr').forEach(function(ipv4) {
|
||||
so.value(ipv4, '%s (%s)'.format(ipv4, ipaddrs[ipv4]));
|
||||
});
|
||||
|
||||
o = s.taboption('ipsets', form.SectionValue, '__ipsets__', form.GridSection, 'ipset', null,
|
||||
_('List of IP sets to populate with the IPs of DNS lookup results of the FQDNs also specified here.'));
|
||||
|
||||
ss = o.subsection;
|
||||
|
||||
ss.addremove = true;
|
||||
ss.anonymous = true;
|
||||
ss.sortable = true;
|
||||
|
||||
so = ss.option(form.DynamicList, 'name', _('IP set'));
|
||||
so.rmempty = false;
|
||||
so.datatype = 'string';
|
||||
|
||||
so = ss.option(form.DynamicList, 'domain', _('Domain'));
|
||||
so.rmempty = false;
|
||||
so.datatype = 'hostname';
|
||||
o = s.taboption('general', form.DynamicList, 'notinterface',
|
||||
_('Exclude interfaces'),
|
||||
_('Prevent listening on these interfaces.'));
|
||||
o.optional = true;
|
||||
|
||||
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.') + '<br />' +
|
||||
|
@ -749,7 +415,6 @@ return view.extend({
|
|||
|
||||
ss.addremove = true;
|
||||
ss.anonymous = true;
|
||||
ss.sortable = true;
|
||||
|
||||
so = ss.option(form.Value, 'name', _('Hostname'));
|
||||
so.validate = validateHostname;
|
||||
|
@ -763,8 +428,8 @@ return view.extend({
|
|||
uci.unset('dhcp', section, 'dns');
|
||||
};
|
||||
|
||||
so = ss.option(form.Value, 'mac', _('MAC address'));
|
||||
so.datatype = 'list(macaddr)';
|
||||
so = ss.option(form.Value, 'mac', _('<abbr title="Media Access Control">MAC</abbr>-Address'));
|
||||
so.datatype = 'list(unique(macaddr))';
|
||||
so.rmempty = true;
|
||||
so.cfgvalue = function(section) {
|
||||
var macs = L.toArray(uci.get('dhcp', section, 'mac')),
|
||||
|
@ -803,54 +468,75 @@ return view.extend({
|
|||
|
||||
return node;
|
||||
};
|
||||
so.validate = validateMACAddr.bind(so, pools);
|
||||
Object.keys(hosts).forEach(function(mac) {
|
||||
var hint = hosts[mac].name || L.toArray(hosts[mac].ipaddrs || hosts[mac].ipv4)[0];
|
||||
so.value(mac, hint ? '%s (%s)'.format(mac, hint) : mac);
|
||||
});
|
||||
|
||||
so = ss.option(form.Value, 'ip', _('IPv4 address'));
|
||||
so.datatype = 'or(ip4addr,"ignore")';
|
||||
so.validate = function(section, value) {
|
||||
var m = this.section.formvalue(section, 'mac'),
|
||||
n = this.section.formvalue(section, 'name');
|
||||
so.write = function(section, value) {
|
||||
var ip = this.map.lookupOption('ip', section)[0].formvalue(section);
|
||||
var hosts = uci.sections('dhcp', 'host');
|
||||
var section_removed = false;
|
||||
|
||||
if ((m == null || m == '') && (n == null || n == ''))
|
||||
return _('One of hostname or MAC address must be specified!');
|
||||
|
||||
if (value == null || value == '' || value == 'ignore')
|
||||
return true;
|
||||
|
||||
var leases = uci.sections('dhcp', 'host');
|
||||
|
||||
for (var i = 0; i < leases.length; i++)
|
||||
if (leases[i]['.name'] != section && leases[i].ip == value)
|
||||
return _('The IP address %h is already used by another static lease').format(value);
|
||||
|
||||
for (var i = 0; i < pools.length; i++) {
|
||||
var net_mask = calculateNetwork(value, pools[i].netmask);
|
||||
|
||||
if (net_mask && net_mask[0] == pools[i].network)
|
||||
return true;
|
||||
for (var i = 0; i < hosts.length; i++) {
|
||||
if (ip == hosts[i].ip) {
|
||||
uci.set('dhcp', hosts[i]['.name'], 'mac', [hosts[i].mac, value].join(' '));
|
||||
uci.remove('dhcp', section);
|
||||
section_removed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return _('The IP address is outside of any DHCP pool address range');
|
||||
if (!section_removed) {
|
||||
uci.set('dhcp', section, 'mac', value);
|
||||
}
|
||||
}
|
||||
|
||||
so = ss.option(form.Value, 'ip', _('<abbr title="Internet Protocol Version 4">IPv4</abbr>-Address'));
|
||||
so.datatype = 'or(ip4addr,"ignore")';
|
||||
so.validate = function(section, value) {
|
||||
var mac = this.map.lookupOption('mac', section),
|
||||
name = this.map.lookupOption('name', section),
|
||||
m = mac ? mac[0].formvalue(section) : null,
|
||||
n = name ? name[0].formvalue(section) : null;
|
||||
|
||||
if ((m == null || m == '') && (n == null || n == ''))
|
||||
return _('One of hostname or mac address must be specified!');
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
var ipaddrs = {};
|
||||
|
||||
Object.keys(hosts).forEach(function(mac) {
|
||||
var addrs = L.toArray(hosts[mac].ipaddrs || hosts[mac].ipv4);
|
||||
|
||||
for (var i = 0; i < addrs.length; i++)
|
||||
ipaddrs[addrs[i]] = hosts[mac].name;
|
||||
});
|
||||
|
||||
L.sortedKeys(ipaddrs, null, 'addr').forEach(function(ipv4) {
|
||||
so.value(ipv4, ipaddrs[ipv4] ? '%s (%s)'.format(ipv4, ipaddrs[ipv4]) : ipv4);
|
||||
});
|
||||
|
||||
so = ss.option(form.Value, 'gw', _('Gateway IPv4 Address'));
|
||||
so.rmempty = true;
|
||||
so.datatype = 'or(ip4addr,"ignore")';
|
||||
Object.keys(hosts).forEach(function(mac) {
|
||||
if (hosts[mac].ipv4)
|
||||
so.value(hosts[mac].ipv4);
|
||||
});
|
||||
|
||||
so = ss.option(form.Value, 'leasetime', _('Lease time'));
|
||||
so.rmempty = true;
|
||||
|
||||
so = ss.option(form.Value, 'duid', _('DUID'));
|
||||
so = ss.option(form.Value, 'duid', _('<abbr title="The DHCP Unique Identifier">DUID</abbr>'));
|
||||
so.datatype = 'and(rangelength(20,36),hexstring)';
|
||||
Object.keys(duids).forEach(function(duid) {
|
||||
so.value(duid, '%s (%s)'.format(duid, duids[duid].hostname || duids[duid].macaddr || duids[duid].ip6addr || '?'));
|
||||
});
|
||||
|
||||
so = ss.option(form.Value, 'hostid', _('IPv6 suffix (hex)'));
|
||||
so = ss.option(form.Value, 'hostid', _('<abbr title="Internet Protocol Version 6">IPv6</abbr>-Suffix (hex)'));
|
||||
|
||||
o = s.taboption('leases', CBILeaseStatus, '__status__');
|
||||
|
||||
|
@ -874,17 +560,8 @@ return view.extend({
|
|||
else
|
||||
exp = '%t'.format(lease.expires);
|
||||
|
||||
var hint = lease.macaddr ? hosts[lease.macaddr] : null,
|
||||
name = hint ? hint.name : null,
|
||||
host = null;
|
||||
|
||||
if (name && lease.hostname && lease.hostname != name)
|
||||
host = '%s (%s)'.format(lease.hostname, name);
|
||||
else if (lease.hostname)
|
||||
host = lease.hostname;
|
||||
|
||||
return [
|
||||
host || '-',
|
||||
lease.hostname || '?',
|
||||
lease.ipaddr,
|
||||
lease.macaddr,
|
||||
exp
|
||||
|
|
64
luci-mod-network/htdocs/luci-static/resources/view/network/diagnostics.js
Normal file → Executable file
64
luci-mod-network/htdocs/luci-static/resources/view/network/diagnostics.js
Normal file → Executable file
|
@ -4,7 +4,6 @@
|
|||
'require fs';
|
||||
'require ui';
|
||||
'require uci';
|
||||
'require network';
|
||||
|
||||
return view.extend({
|
||||
handleCommand: function(exec, args) {
|
||||
|
@ -14,7 +13,8 @@ return view.extend({
|
|||
buttons[i].setAttribute('disabled', 'true');
|
||||
|
||||
return fs.exec(exec, args).then(function(res) {
|
||||
var out = document.querySelector('textarea');
|
||||
var out = document.querySelector('.command-output');
|
||||
out.style.display = '';
|
||||
|
||||
dom.content(out, [ res.stdout || '', res.stderr || '' ]);
|
||||
}).catch(function(err) {
|
||||
|
@ -36,7 +36,7 @@ return view.extend({
|
|||
handleTraceroute: function(ev, cmd) {
|
||||
var exec = cmd || 'traceroute',
|
||||
addr = ev.currentTarget.parentNode.previousSibling.value,
|
||||
args = (exec == 'traceroute') ? [ '-4', '-q', '1', '-w', '1', '-n', '-m', String(L.env.rpctimeout || 20), addr ] : [ '-q', '1', '-w', '2', '-n', addr ];
|
||||
args = (exec == 'traceroute') ? [ '-q', '1', '-w', '1', '-n', addr ] : [ '-q', '1', '-w', '2', '-n', addr ];
|
||||
|
||||
return this.handleCommand(exec, args);
|
||||
},
|
||||
|
@ -47,20 +47,12 @@ return view.extend({
|
|||
return this.handleCommand('nslookup', [ addr ]);
|
||||
},
|
||||
|
||||
handleArpScan: function(ev, cmd) {
|
||||
var addr = ev.currentTarget.parentNode.previousSibling.value;
|
||||
|
||||
return this.handleCommand('arp-scan', [ '-l', '-I', addr ]);
|
||||
},
|
||||
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
L.resolveDefault(fs.stat('/bin/ping6'), {}),
|
||||
L.resolveDefault(fs.stat('/usr/bin/ping6'), {}),
|
||||
L.resolveDefault(fs.stat('/bin/traceroute6'), {}),
|
||||
L.resolveDefault(fs.stat('/usr/bin/traceroute6'), {}),
|
||||
L.resolveDefault(fs.stat('/usr/bin/arp-scan'), {}),
|
||||
network.getDevices(),
|
||||
uci.load('luci')
|
||||
]);
|
||||
},
|
||||
|
@ -68,15 +60,15 @@ return view.extend({
|
|||
render: function(res) {
|
||||
var has_ping6 = res[0].path || res[1].path,
|
||||
has_traceroute6 = res[2].path || res[3].path,
|
||||
has_arpscan = res[4].path,
|
||||
devices = res[5],
|
||||
dns_host = uci.get('luci', 'diag', 'dns') || 'openwrt.org',
|
||||
ping_host = uci.get('luci', 'diag', 'ping') || 'openwrt.org',
|
||||
route_host = uci.get('luci', 'diag', 'route') || 'openwrt.org';
|
||||
|
||||
var table = E('table', { 'class': 'table' }, [
|
||||
return E([], [
|
||||
E('h2', {}, [ _('Network Utilities') ]),
|
||||
E('table', { 'class': 'table' }, [
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left', 'style': 'overflow:initial' }, [
|
||||
E('td', { 'class': 'td left' }, [
|
||||
E('input', {
|
||||
'style': 'margin:5px 0',
|
||||
'type': 'text',
|
||||
|
@ -99,7 +91,7 @@ return view.extend({
|
|||
])
|
||||
]),
|
||||
|
||||
E('td', { 'class': 'td left', 'style': 'overflow:initial' }, [
|
||||
E('td', { 'class': 'td left' }, [
|
||||
E('input', {
|
||||
'style': 'margin:5px 0',
|
||||
'type': 'text',
|
||||
|
@ -134,45 +126,11 @@ return view.extend({
|
|||
'click': ui.createHandlerFn(this, 'handleNslookup')
|
||||
}, [ _('Nslookup') ])
|
||||
])
|
||||
]),
|
||||
|
||||
has_arpscan ? E('td', { 'class': 'td left' }, [
|
||||
E('select', {
|
||||
'style': 'margin:5px 0'
|
||||
}, devices.map(function(device) {
|
||||
if (!device.isUp())
|
||||
return E([]);
|
||||
|
||||
return E('option', { 'value': device.getName() }, [ device.getI18n() ]);
|
||||
})),
|
||||
E('span', { 'class': 'diag-action' }, [
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-action',
|
||||
'click': ui.createHandlerFn(this, 'handleArpScan')
|
||||
}, [ _('Arp-scan') ])
|
||||
])
|
||||
]) : E([]),
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
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%; font-family:monospace; white-space:pre',
|
||||
'readonly': true,
|
||||
'wrap': 'off',
|
||||
'rows': '20'
|
||||
})
|
||||
)
|
||||
])
|
||||
]),
|
||||
E('pre', { 'class': 'command-output', 'style': 'display:none' })
|
||||
]);
|
||||
|
||||
return view;
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
|
|
50
luci-mod-network/htdocs/luci-static/resources/view/network/hosts.js
Executable file
50
luci-mod-network/htdocs/luci-static/resources/view/network/hosts.js
Executable file
|
@ -0,0 +1,50 @@
|
|||
'use strict';
|
||||
'require view';
|
||||
'require rpc';
|
||||
'require form';
|
||||
|
||||
return view.extend({
|
||||
callHostHints: rpc.declare({
|
||||
object: 'luci-rpc',
|
||||
method: 'getHostHints',
|
||||
expect: { '': {} }
|
||||
}),
|
||||
|
||||
load: function() {
|
||||
return this.callHostHints();
|
||||
},
|
||||
|
||||
render: function(hosts) {
|
||||
var m, s, o;
|
||||
|
||||
m = new form.Map('dhcp', _('Hostnames'));
|
||||
|
||||
s = m.section(form.GridSection, 'domain', _('Host entries'));
|
||||
s.addremove = true;
|
||||
s.anonymous = true;
|
||||
s.sortable = true;
|
||||
|
||||
o = s.option(form.Value, 'name', _('Hostname'));
|
||||
o.datatype = 'hostname';
|
||||
o.rmempty = true;
|
||||
|
||||
o = s.option(form.Value, 'ip', _('IP address'));
|
||||
o.datatype = 'ipaddr';
|
||||
o.rmempty = true;
|
||||
|
||||
var ipaddrs = {};
|
||||
|
||||
Object.keys(hosts).forEach(function(mac) {
|
||||
var addrs = L.toArray(hosts[mac].ipaddrs || hosts[mac].ipv4);
|
||||
|
||||
for (var i = 0; i < addrs.length; i++)
|
||||
ipaddrs[addrs[i]] = hosts[mac].name || mac;
|
||||
});
|
||||
|
||||
L.sortedKeys(ipaddrs, null, 'addr').forEach(function(ipv4) {
|
||||
o.value(ipv4, '%s (%s)'.format(ipv4, ipaddrs[ipv4]));
|
||||
});
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
128
luci-mod-network/htdocs/luci-static/resources/view/network/interfaces.js
Normal file → Executable file
128
luci-mod-network/htdocs/luci-static/resources/view/network/interfaces.js
Normal file → Executable file
|
@ -228,23 +228,6 @@ function get_netmask(s, use_cfgvalue) {
|
|||
return subnetmask;
|
||||
}
|
||||
|
||||
function has_peerdns(proto) {
|
||||
switch (proto) {
|
||||
case 'dhcp':
|
||||
case 'dhcpv6':
|
||||
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();
|
||||
|
@ -505,7 +488,7 @@ return view.extend({
|
|||
};
|
||||
|
||||
s.modaltitle = function(section_id) {
|
||||
return _('Interfaces') + ' » ' + section_id;
|
||||
return _('Interfaces') + ' » ' + section_id.toUpperCase();
|
||||
};
|
||||
|
||||
s.renderRowActions = function(section_id) {
|
||||
|
@ -552,7 +535,7 @@ return view.extend({
|
|||
var protocols = network.getProtocols();
|
||||
|
||||
protocols.sort(function(a, b) {
|
||||
return L.naturalCompare(a.getProtocol(), b.getProtocol());
|
||||
return a.getProtocol() > b.getProtocol();
|
||||
});
|
||||
|
||||
o = s.taboption('general', form.DummyValue, '_ifacestat_modal', _('Status'));
|
||||
|
@ -660,7 +643,7 @@ return view.extend({
|
|||
E('p', _('No DHCP Server configured for this interface') + '   '),
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-add',
|
||||
'title': _('Set up DHCP Server'),
|
||||
'title': _('Setup DHCP Server'),
|
||||
'click': ui.createHandlerFn(this, function(section_id, ev) {
|
||||
this.map.save(function() {
|
||||
uci.add('dhcp', 'dhcp', section_id);
|
||||
|
@ -676,7 +659,7 @@ return view.extend({
|
|||
}
|
||||
});
|
||||
}, ifc.getName())
|
||||
}, _('Set up DHCP Server'))
|
||||
}, _('Setup DHCP Server'))
|
||||
]);
|
||||
};
|
||||
|
||||
|
@ -822,14 +805,7 @@ return view.extend({
|
|||
return flags.length ? flags : [ 'other-config' ];
|
||||
};
|
||||
so.remove = function(section_id) {
|
||||
var existing = L.toArray(uci.get('dhcp', section_id, 'ra_flags'));
|
||||
if (this.isActive(section_id)) {
|
||||
if (existing.length != 1 || existing[0] != 'none')
|
||||
uci.set('dhcp', section_id, 'ra_flags', [ 'none' ]);
|
||||
}
|
||||
else if (existing.length) {
|
||||
uci.unset('dhcp', section_id, 'ra_flags');
|
||||
}
|
||||
uci.set('dhcp', section_id, 'ra_flags', [ 'none' ]);
|
||||
};
|
||||
|
||||
so = ss.taboption('ipv6-ra', form.Value, 'ra_maxinterval', _('Max <abbr title="Router Advertisement">RA</abbr> interval'), _('Maximum time allowed between sending unsolicited <abbr title="Router Advertisement, ICMPv6 Type 134">RA</abbr>. Default is 600 seconds.'));
|
||||
|
@ -859,17 +835,15 @@ return view.extend({
|
|||
so.depends('ra', 'server');
|
||||
so.depends({ ra: 'hybrid', master: '0' });
|
||||
so.load = function(section_id) {
|
||||
var dev = ifc.getL3Device(),
|
||||
path = dev ? "/proc/sys/net/ipv6/conf/%s/mtu".format(dev.getName()) : null;
|
||||
var dev = ifc.getL3Device();
|
||||
|
||||
return Promise.all([
|
||||
dev ? L.resolveDefault(fs.read(path), dev.getMTU()) : null,
|
||||
this.super('load', [section_id])
|
||||
]).then(L.bind(function(res) {
|
||||
this.placeholder = +res[0];
|
||||
if (dev) {
|
||||
var path = "/proc/sys/net/ipv6/conf/%s/mtu".format(dev.getName());
|
||||
|
||||
return res[1];
|
||||
}, this));
|
||||
return L.resolveDefault(fs.read(path), dev.getMTU()).then(L.bind(function(data) {
|
||||
this.placeholder = data;
|
||||
}, this));
|
||||
}
|
||||
};
|
||||
|
||||
so = ss.taboption('ipv6-ra', form.Value, 'ra_hoplimit', _('<abbr title="Router Advertisement">RA</abbr> Hop Limit'), _('The maximum hops to be published in <abbr title="Router Advertisement">RA</abbr> messages. Maximum is 255 hops.'));
|
||||
|
@ -878,17 +852,15 @@ return view.extend({
|
|||
so.depends('ra', 'server');
|
||||
so.depends({ ra: 'hybrid', master: '0' });
|
||||
so.load = function(section_id) {
|
||||
var dev = ifc.getL3Device(),
|
||||
path = dev ? "/proc/sys/net/ipv6/conf/%s/hop_limit".format(dev.getName()) : null;
|
||||
var dev = ifc.getL3Device();
|
||||
|
||||
return Promise.all([
|
||||
dev ? L.resolveDefault(fs.read(path), 64) : null,
|
||||
this.super('load', [section_id])
|
||||
]).then(L.bind(function(res) {
|
||||
this.placeholder = +res[0];
|
||||
if (dev) {
|
||||
var path = "/proc/sys/net/ipv6/conf/%s/hop_limit".format(dev.getName());
|
||||
|
||||
return res[1];
|
||||
}, this));
|
||||
return L.resolveDefault(fs.read(path), 64).then(L.bind(function(data) {
|
||||
this.placeholder = data;
|
||||
}, this));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -906,24 +878,18 @@ return view.extend({
|
|||
so = ss.taboption('ipv6', form.DynamicList, 'dns', _('Announced IPv6 DNS servers'),
|
||||
_('Specifies a fixed list of IPv6 DNS server addresses to announce via DHCPv6. If left unspecified, the device will announce itself as IPv6 DNS server unless the <em>Local IPv6 DNS server</em> option is disabled.'));
|
||||
so.datatype = 'ip6addr("nomask")'; /* restrict to IPv6 only for now since dnsmasq (DHCPv4) does not honour this option */
|
||||
so.depends('ra', 'server');
|
||||
so.depends({ ra: 'hybrid', master: '0' });
|
||||
so.depends('dhcpv6', 'server');
|
||||
so.depends({ dhcpv6: 'hybrid', master: '0' });
|
||||
|
||||
so = ss.taboption('ipv6', form.Flag, 'dns_service', _('Local IPv6 DNS server'),
|
||||
_('Announce this device as IPv6 DNS server.'));
|
||||
so.default = so.enabled;
|
||||
so.depends({ ra: 'server', dns: /^$/ });
|
||||
so.depends({ ra: 'hybrid', dns: /^$/, master: '0' });
|
||||
so.depends({ dhcpv6: 'server', dns: /^$/ });
|
||||
so.depends({ dhcpv6: 'hybrid', dns: /^$/, master: '0' });
|
||||
|
||||
so = ss.taboption('ipv6', form.DynamicList, 'domain', _('Announced DNS domains'),
|
||||
_('Specifies a fixed list of DNS search domains to announce via DHCPv6. If left unspecified, the local device DNS search domain will be announced.'));
|
||||
so.datatype = 'hostname';
|
||||
so.depends('ra', 'server');
|
||||
so.depends({ ra: 'hybrid', master: '0' });
|
||||
so.depends('dhcpv6', 'server');
|
||||
so.depends({ dhcpv6: 'hybrid', master: '0' });
|
||||
|
||||
|
@ -953,13 +919,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 (has_peerdns(protoval)) {
|
||||
if (protoval != 'static') {
|
||||
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 (has_peerdns(protoval))
|
||||
if (protoval != 'static')
|
||||
o.depends('peerdns', '0');
|
||||
o.datatype = 'ipaddr';
|
||||
|
||||
|
@ -975,6 +941,17 @@ return view.extend({
|
|||
o = nettools.replaceOption(s, 'advanced', form.Value, 'metric', _('Use gateway metric'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = '0';
|
||||
|
||||
o = nettools.replaceOption(s,'advanced', form.ListValue, 'multipath', _('Multipath setting'), _('Only one interface must be set as Master.'));
|
||||
o.value('on',_('Enabled'));
|
||||
o.value('off',_('Disabled'));
|
||||
o.value('master',_('Master'));
|
||||
o.value('backup',_('Backup'));
|
||||
o.default = 'off';
|
||||
|
||||
o = nettools.replaceOption(s,'advanced', form.Value, 'addlatency', _('Additional latency'));
|
||||
o.datatype = 'uinteger';
|
||||
o.default = '0';
|
||||
|
||||
o = nettools.replaceOption(s, 'advanced', form.Value, 'ip4table', _('Override IPv4 routing table'));
|
||||
o.datatype = 'or(uinteger, string)';
|
||||
|
@ -984,12 +961,7 @@ return view.extend({
|
|||
o = nettools.replaceOption(s, 'advanced', form.Value, 'ip6table', _('Override IPv6 routing table'));
|
||||
o.datatype = 'or(uinteger, string)';
|
||||
for (var i = 0; i < rtTables.length; i++)
|
||||
o.value(rtTables[i][1], '%s (%d)'.format(rtTables[i][1], rtTables[i][0]));
|
||||
|
||||
if (protoval == 'dhcpv6') {
|
||||
o = nettools.replaceOption(s, 'advanced', form.Flag, 'sourcefilter', _('IPv6 source routing'), _('Automatically handle multiple uplink interfaces using source-based policy routing.'));
|
||||
o.default = o.enabled;
|
||||
}
|
||||
o.value(rtTables[i][1], '%s (%d)'.format(rtTables[i][0], rtTables[i][1]));
|
||||
|
||||
o = nettools.replaceOption(s, 'advanced', form.Flag, 'delegate', _('Delegate IPv6 prefixes'), _('Enable downstream delegation of IPv6 prefixes available on this interface'));
|
||||
o.default = o.enabled;
|
||||
|
@ -1108,7 +1080,7 @@ return view.extend({
|
|||
proto, name, device;
|
||||
|
||||
protocols.sort(function(a, b) {
|
||||
return L.naturalCompare(a.getProtocol(), b.getProtocol());
|
||||
return a.getProtocol() > b.getProtocol();
|
||||
});
|
||||
|
||||
s2.render = function() {
|
||||
|
@ -1182,9 +1154,6 @@ return view.extend({
|
|||
protoclass.addDevice(device.formvalue('_new_'));
|
||||
|
||||
m.children[0].addedSection = section_id;
|
||||
|
||||
ui.hideModal();
|
||||
ui.showModal(null, E('p', { 'class': 'spinning' }, [ _('Loading data…') ]));
|
||||
}).then(L.bind(m.children[0].renderMoreOptionsModal, m.children[0], nameval));
|
||||
});
|
||||
})
|
||||
|
@ -1214,9 +1183,9 @@ return view.extend({
|
|||
var node = E('div', { 'class': 'ifacebox' }, [
|
||||
E('div', {
|
||||
'class': 'ifacebox-head',
|
||||
'style': firewall.getZoneColorStyle(zone),
|
||||
'style': 'background-color:%s'.format(zone ? zone.getColor() : '#EEEEEE'),
|
||||
'title': zone ? _('Part of zone %q').format(zone.getName()) : _('No zone assigned')
|
||||
}, E('strong', net.getName())),
|
||||
}, E('strong', net.getName().toUpperCase())),
|
||||
E('div', {
|
||||
'class': 'ifacebox-body',
|
||||
'id': '%s-ifc-devices'.format(section_id),
|
||||
|
@ -1270,7 +1239,7 @@ return view.extend({
|
|||
|
||||
s.cfgsections = function() {
|
||||
var sections = uci.sections('network', 'device'),
|
||||
section_ids = sections.sort(function(a, b) { return L.naturalCompare(a.name, b.name) }).map(function(s) { return s['.name'] });
|
||||
section_ids = sections.sort(function(a, b) { return a.name > b.name }).map(function(s) { return s['.name'] });
|
||||
|
||||
for (var i = 0; i < netDevs.length; i++) {
|
||||
if (sections.filter(function(s) { return s.name == netDevs[i].getName() }).length)
|
||||
|
@ -1315,7 +1284,7 @@ return view.extend({
|
|||
var trEl = this.super('renderRowActions', [ section_id, _('Configure…') ]),
|
||||
deleteBtn = trEl.querySelector('button:last-child');
|
||||
|
||||
deleteBtn.firstChild.data = _('Unconfigure');
|
||||
deleteBtn.firstChild.data = _('Reset');
|
||||
deleteBtn.setAttribute('title', _('Remove related device settings from the configuration'));
|
||||
deleteBtn.disabled = section_id.match(/^dev:/) ? true : null;
|
||||
|
||||
|
@ -1348,26 +1317,9 @@ return view.extend({
|
|||
for (var i = 0; i < map.addedVLANs.length; i++)
|
||||
uci.remove('network', map.addedVLANs[i]);
|
||||
|
||||
if (this.addedSection)
|
||||
uci.remove('network', this.addedSection);
|
||||
|
||||
return form.GridSection.prototype.handleModalCancel.apply(this, arguments);
|
||||
};
|
||||
|
||||
s.handleRemove = function(section_id /*, ... */) {
|
||||
var name = uci.get('network', section_id, 'name'),
|
||||
type = uci.get('network', section_id, 'type');
|
||||
|
||||
if (name != null && type == 'bridge') {
|
||||
uci.sections('network', 'bridge-vlan', function(bvs) {
|
||||
if (bvs.device == name)
|
||||
uci.remove('network', bvs['.name']);
|
||||
});
|
||||
}
|
||||
|
||||
return form.GridSection.prototype.handleRemove.apply(this, arguments);
|
||||
};
|
||||
|
||||
function getDevice(section_id) {
|
||||
var m = section_id.match(/^dev:(.+)$/),
|
||||
name = m ? m[1] : uci.get('network', section_id, 'name');
|
||||
|
@ -1470,7 +1422,7 @@ return view.extend({
|
|||
mac = dev ? dev.getMAC() : null;
|
||||
|
||||
return val ? E('strong', {
|
||||
'data-tooltip': _('The value is overridden by configuration.')
|
||||
'data-tooltip': _('The value is overridden by configuration. Original: %s').format(mac || _('unknown'))
|
||||
}, [ val.toUpperCase() ]) : (mac || '-');
|
||||
};
|
||||
|
||||
|
@ -1482,7 +1434,7 @@ return view.extend({
|
|||
mtu = dev ? dev.getMTU() : null;
|
||||
|
||||
return val ? E('strong', {
|
||||
'data-tooltip': _('The value is overridden by configuration.')
|
||||
'data-tooltip': _('The value is overridden by configuration. Original: %s').format(mtu || _('unknown'))
|
||||
}, [ val ]) : (mtu || '-').toString();
|
||||
};
|
||||
|
||||
|
|
230
luci-mod-network/htdocs/luci-static/resources/view/network/routes.js
Normal file → Executable file
230
luci-mod-network/htdocs/luci-static/resources/view/network/routes.js
Normal file → Executable file
|
@ -1,35 +1,22 @@
|
|||
'use strict';
|
||||
'require view';
|
||||
'require fs';
|
||||
'require uci';
|
||||
'require form';
|
||||
'require network';
|
||||
'require tools.widgets as widgets';
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
network.getDevices(),
|
||||
fs.lines('/etc/iproute2/rt_tables')
|
||||
]);
|
||||
return network.getDevices();
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var netDevs = data[0],
|
||||
m, s, o;
|
||||
render: function(netdevs) {
|
||||
var m, s, o;
|
||||
|
||||
var rtTables = data[1].map(function(l) {
|
||||
var m = l.trim().match(/^(\d+)\s+(\S+)$/);
|
||||
return m ? [ +m[1], m[2] ] : null;
|
||||
}).filter(function(e) {
|
||||
return e && e[0] > 0;
|
||||
});
|
||||
|
||||
m = new form.Map('network', _('Routing'), _('Routing defines over which interface and gateway a certain host or network can be reached.'));
|
||||
m = new form.Map('network', _('Routes'), _('Routes specify over which interface and gateway a certain host or network can be reached.'));
|
||||
m.tabbed = true;
|
||||
|
||||
for (var family = 4; family <= 6; family += 2) {
|
||||
s = m.section(form.GridSection, (family == 6) ? 'route6' : 'route', (family == 6) ? _('Static IPv6 Routes') : _('Static IPv4 Routes'));
|
||||
for (var i = 4; i <= 6; i += 2) {
|
||||
s = m.section(form.GridSection, (i == 4) ? 'route' : 'route6', (i == 4) ? _('Static IPv4 Routes') : _('Static IPv6 Routes'));
|
||||
s.anonymous = true;
|
||||
s.addremove = true;
|
||||
s.sortable = true;
|
||||
|
@ -38,12 +25,44 @@ return view.extend({
|
|||
s.tab('general', _('General Settings'));
|
||||
s.tab('advanced', _('Advanced Settings'));
|
||||
|
||||
o = s.taboption('general', widgets.NetworkSelect, 'interface', _('Interface'), _('Specifies the logical interface name of the parent (or master) interface this route belongs to'));
|
||||
o.loopback = true;
|
||||
o = s.taboption('general', widgets.NetworkSelect, 'interface', _('Interface'));
|
||||
o.rmempty = false;
|
||||
o.nocreate = true;
|
||||
|
||||
o = s.taboption('general', form.ListValue, 'type', _('Route type'), _('Specifies the route type to be created'));
|
||||
o = s.taboption('general', form.Flag, 'disabled', _('Disable'), _('Disable this route'));
|
||||
o.rmempty = true;
|
||||
o.default = o.disabled;
|
||||
|
||||
o = s.taboption('general', form.Value, 'target', _('Target'), (i == 4) ? _('Host-<abbr title="Internet Protocol Address">IP</abbr> or Network') : _('<abbr title="Internet Protocol Version 6">IPv6</abbr>-Address or Network (CIDR)'));
|
||||
o.datatype = (i == 4) ? 'ip4addr' : 'ip6addr';
|
||||
o.rmempty = false;
|
||||
|
||||
if (i == 4) {
|
||||
o = s.taboption('general', form.Value, 'netmask', _('<abbr title="Internet Protocol Version 4">IPv4</abbr>-Netmask'), _('if target is a network'));
|
||||
o.placeholder = '255.255.255.255';
|
||||
o.datatype = 'ip4addr';
|
||||
o.rmempty = true;
|
||||
}
|
||||
|
||||
o = s.taboption('general', form.Value, 'gateway', (i == 4) ? _('<abbr title="Internet Protocol Version 4">IPv4</abbr>-Gateway') : _('<abbr title="Internet Protocol Version 6">IPv6</abbr>-Gateway'));
|
||||
o.datatype = (i == 4) ? 'ip4addr' : 'ip6addr';
|
||||
o.rmempty = true;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'metric', _('Metric'));
|
||||
o.placeholder = 0;
|
||||
o.datatype = (i == 4) ? 'range(0,255)' : 'range(0,65535)';
|
||||
o.rmempty = true;
|
||||
o.textvalue = function(section_id) {
|
||||
return this.cfgvalue(section_id) || 0;
|
||||
};
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'mtu', _('MTU'));
|
||||
o.placeholder = 1500;
|
||||
o.datatype = 'range(64,9000)';
|
||||
o.rmempty = true;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.ListValue, 'type', _('Route type'));
|
||||
o.value('', 'unicast');
|
||||
o.value('local');
|
||||
o.value('broadcast');
|
||||
|
@ -52,155 +71,36 @@ return view.extend({
|
|||
o.value('prohibit');
|
||||
o.value('blackhole');
|
||||
o.value('anycast');
|
||||
o.default = '';
|
||||
o.rmempty = true;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('general', form.Value, 'target', _('Target'), _('Network address'));
|
||||
o.rmempty = false;
|
||||
o.datatype = (family == 6) ? 'cidr6' : 'cidr4';
|
||||
o.placeholder = (family == 6) ? '::/0' : '0.0.0.0/0';
|
||||
o = s.taboption('advanced', form.Value, 'table', _('Route table'));
|
||||
o.value('local', 'local (255)');
|
||||
o.value('main', 'main (254)');
|
||||
o.value('default', 'default (253)');
|
||||
o.rmempty = true;
|
||||
o.modalonly = true;
|
||||
o.cfgvalue = function(section_id) {
|
||||
var section_type = uci.get('network', section_id, '.type'),
|
||||
target = uci.get('network', section_id, 'target'),
|
||||
mask = uci.get('network', section_id, 'netmask'),
|
||||
v6 = (section_type == 'route6') ? true : false,
|
||||
bits = mask ? network.maskToPrefix(mask, v6) : (v6 ? 128 : 32);
|
||||
if (target) {
|
||||
return target.split('/')[1] ? target : target + '/' + bits;
|
||||
}
|
||||
var cfgvalue = this.map.data.get('network', section_id, 'table');
|
||||
return cfgvalue || 'main';
|
||||
};
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'source', _('Source Address'));
|
||||
o.placeholder = E('em', _('automatic'));
|
||||
for (var j = 0; j < netdevs.length; j++) {
|
||||
var addrs = netdevs[j].getIPAddrs();
|
||||
for (var k = 0; k < addrs.length; k++)
|
||||
o.value(addrs[k].split('/')[0]);
|
||||
}
|
||||
o.write = function(section_id, formvalue) {
|
||||
uci.set('network', section_id, 'target', formvalue);
|
||||
uci.unset('network', section_id, 'netmask');
|
||||
}
|
||||
|
||||
o = s.taboption('general', form.Value, 'gateway', _('Gateway'), _('Specifies the network gateway. If omitted, the gateway from the parent interface is taken if any, otherwise creates a link scope route. If set to 0.0.0.0 no gateway will be specified for the route'));
|
||||
o.datatype = (family == 6) ? 'ip6addr("nomask")' : 'ip4addr("nomask")';
|
||||
o.placeholder = (family == 6) ? 'fe80::1' : '192.168.0.1';
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'metric', _('Metric'), _('Specifies the route metric to use'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = 0;
|
||||
o.textvalue = function(section_id) {
|
||||
return this.cfgvalue(section_id) || 0;
|
||||
};
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'mtu', _('MTU'), _('Defines a specific MTU for this route'));
|
||||
o.datatype = (i == 4) ? 'ip4addr' : 'ip6addr';
|
||||
o.default = '';
|
||||
o.rmempty = true;
|
||||
o.modalonly = true;
|
||||
o.datatype = 'and(uinteger,range(64,9000))';
|
||||
o.placeholder = 1500;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'table', _('Table'), _('The rule target is a table lookup ID: a numeric table index ranging from 0 to 65535 or symbol alias declared in /etc/iproute2/rt_tables. Special aliases local (255), main (254) and default (253) are also valid'));
|
||||
o.datatype = 'or(uinteger, string)';
|
||||
for (var i = 0; i < rtTables.length; i++)
|
||||
o.value(rtTables[i][1], '%s (%d)'.format(rtTables[i][1], rtTables[i][0]));
|
||||
o.textvalue = function(section_id) {
|
||||
return this.cfgvalue(section_id) || 'main';
|
||||
};
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'source', _('Source'), _('Specifies the preferred source address when sending to destinations covered by the target'));
|
||||
o.modalonly = true;
|
||||
o.datatype = (family == 6) ? 'ip6addr' : 'ip4addr';
|
||||
o.placeholder = E('em', _('auto'));
|
||||
for (var i = 0; i < netDevs.length; i++) {
|
||||
var addrs = (family == 6) ? netDevs[i].getIP6Addrs() : netDevs[i].getIPAddrs();
|
||||
for (var j = 0; j < addrs.length; j++)
|
||||
o.value(addrs[j].split('/')[0]);
|
||||
}
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'onlink', _('On-link'), _('When enabled, gateway is on-link even if the gateway does not match any interface prefix'));
|
||||
o.modalonly = true;
|
||||
o.default = o.disabled;
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'disabled', _('Disable'));
|
||||
o.modalonly = false;
|
||||
o.editable = true;
|
||||
o.default = o.disabled;
|
||||
}
|
||||
|
||||
for (var family = 4; family <= 6; family += 2) {
|
||||
s = m.section(form.GridSection, (family == 6) ? 'rule6' : 'rule', (family == 6) ? _('IPv6 Rules') : _('IPv4 Rules'));
|
||||
s.anonymous = true;
|
||||
s.addremove = true;
|
||||
s.sortable = true;
|
||||
s.nodescriptions = true;
|
||||
|
||||
s.tab('general', _('General Settings'));
|
||||
s.tab('advanced', _('Advanced Settings'));
|
||||
|
||||
o = s.taboption('general', form.Value, 'priority', _('Priority'), _('Specifies the ordering of the IP rules'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = 30000;
|
||||
o.textvalue = function(section_id) {
|
||||
return this.cfgvalue(section_id) || E('em', _('auto'));
|
||||
};
|
||||
|
||||
o = s.taboption('general', form.ListValue, 'action', _('Rule type'), _('Specifies the rule target routing action'));
|
||||
o.modalonly = true;
|
||||
o.value('', 'unicast');
|
||||
o.value('unreachable');
|
||||
o.value('prohibit');
|
||||
o.value('blackhole');
|
||||
o.value('throw');
|
||||
|
||||
o = s.taboption('general', widgets.NetworkSelect, 'in', _('Incoming interface'), _('Specifies the incoming logical interface name'));
|
||||
o.loopback = true;
|
||||
o.nocreate = true;
|
||||
|
||||
o = s.taboption('general', form.Value, 'src', _('Source'), _('Specifies the source subnet to match (CIDR notation)'));
|
||||
o.datatype = (family == 6) ? 'cidr6' : 'cidr4';
|
||||
o.placeholder = (family == 6) ? '::/0' : '0.0.0.0/0';
|
||||
o.textvalue = function(section_id) {
|
||||
return this.cfgvalue(section_id) || E('em', _('any'));
|
||||
};
|
||||
|
||||
o = s.taboption('general', widgets.NetworkSelect, 'out', _('Outgoing interface'), _('Specifies the outgoing logical interface name'));
|
||||
o.loopback = true;
|
||||
o.nocreate = true;
|
||||
|
||||
o = s.taboption('general', form.Value, 'dest', _('Destination'), _('Specifies the destination subnet to match (CIDR notation)'));
|
||||
o.datatype = (family == 6) ? 'cidr6' : 'cidr4';
|
||||
o.placeholder = (family == 6) ? '::/0' : '0.0.0.0/0';
|
||||
o.textvalue = function(section_id) {
|
||||
return this.cfgvalue(section_id) || E('em', _('any'));
|
||||
};
|
||||
|
||||
o = s.taboption('general', form.Value, 'lookup', _('Table'), _('The rule target is a table lookup ID: a numeric table index ranging from 0 to 65535 or symbol alias declared in /etc/iproute2/rt_tables. Special aliases local (255), main (254) and default (253) are also valid'));
|
||||
o.datatype = 'or(uinteger, string)';
|
||||
for (var i = 0; i < rtTables.length; i++)
|
||||
o.value(rtTables[i][1], '%s (%d)'.format(rtTables[i][1], rtTables[i][0]));
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'goto', _('Jump to rule'), _('The rule target is a jump to another rule specified by its priority value'));
|
||||
o.modalonly = true;
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = 80000;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'mark', _('Firewall mark'), _('Specifies the fwmark and optionally its mask to match, e.g. 0xFF to match mark 255 or 0x0/0x1 to match any even mark value'));
|
||||
o.modalonly = true;
|
||||
o.datatype = 'string';
|
||||
o.placeholder = '0x1/0xf';
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'tos', _('Type of service'), _('Specifies the TOS value to match in IP headers'));
|
||||
o.modalonly = true;
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = 10;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'uidrange', _('User identifier'), _('Specifies an individual UID or range of UIDs to match, e.g. 1000 to match corresponding UID or 1000-1005 to inclusively match all UIDs within the corresponding range'));
|
||||
o.modalonly = true;
|
||||
o.datatype = 'string';
|
||||
o.placeholder = '1000-1005';
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'suppress_prefixlength', _('Prefix suppressor'), _('Reject routing decisions that have a prefix length less than or equal to the specified value'));
|
||||
o.modalonly = true;
|
||||
o.datatype = (family == 6) ? 'ip6prefix' : 'ip4prefix';
|
||||
o.placeholder = (family == 6) ? 64 : 24;
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'invert', _('Invert match'), _('If set, the meaning of the match options is inverted'));
|
||||
o.modalonly = true;
|
||||
o.default = o.disabled;
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'disabled', _('Disable'));
|
||||
o.modalonly = false;
|
||||
o.editable = true;
|
||||
o = s.taboption('advanced', form.Flag, 'onlink', _('On-Link route'));
|
||||
o.default = o.disabled;
|
||||
o.rmempty = true;
|
||||
}
|
||||
|
||||
return m.render();
|
||||
|
|
26
luci-mod-network/htdocs/luci-static/resources/view/network/switch.js
Normal file → Executable file
26
luci-mod-network/htdocs/luci-static/resources/view/network/switch.js
Normal file → Executable file
|
@ -180,10 +180,8 @@ return view.extend({
|
|||
s = m.section(form.NamedSection, sid, 'switch', switch_title);
|
||||
s.addremove = false;
|
||||
|
||||
if (feat.vlan_option) {
|
||||
o = s.option(form.Flag, feat.vlan_option, _('Enable VLAN functionality'));
|
||||
o.rmempty = false;
|
||||
}
|
||||
if (feat.vlan_option)
|
||||
s.option(form.Flag, feat.vlan_option, _('Enable VLAN functionality'));
|
||||
|
||||
if (feat.learning_option) {
|
||||
o = s.option(form.Flag, feat.learning_option, _('Enable learning and aging'));
|
||||
|
@ -224,7 +222,7 @@ return view.extend({
|
|||
|
||||
s.filter = function(section_id) {
|
||||
var device = uci.get('network', section_id, 'device');
|
||||
return (device == this.device);
|
||||
return (device == switch_name);
|
||||
};
|
||||
|
||||
s.cfgsections = function() {
|
||||
|
@ -248,7 +246,7 @@ return view.extend({
|
|||
max_vid = 0;
|
||||
|
||||
for (var j = 0; j < sections.length; j++) {
|
||||
if (sections[j].device != this.device)
|
||||
if (sections[j].device != s.device)
|
||||
continue;
|
||||
|
||||
var vlan = +sections[j].vlan,
|
||||
|
@ -261,7 +259,7 @@ return view.extend({
|
|||
max_vid = vid;
|
||||
}
|
||||
|
||||
uci.set('network', section_id, 'device', this.device);
|
||||
uci.set('network', section_id, 'device', s.device);
|
||||
uci.set('network', section_id, 'vlan', max_vlan + 1);
|
||||
|
||||
if (feat.vid_option)
|
||||
|
@ -270,6 +268,8 @@ return view.extend({
|
|||
return this.map.save(null, true);
|
||||
};
|
||||
|
||||
var port_opts = [];
|
||||
|
||||
o = s.option(form.Value, feat.vid_option || 'vlan', 'VLAN ID');
|
||||
o.rmempty = false;
|
||||
o.forcewrite = true;
|
||||
|
@ -297,23 +297,21 @@ return view.extend({
|
|||
return true;
|
||||
};
|
||||
|
||||
var port_opts = o.port_opts = [];
|
||||
|
||||
o.write = function(section_id, value) {
|
||||
var topology = this.section.topology,
|
||||
values = [];
|
||||
|
||||
for (var i = 0; i < this.port_opts.length; i++) {
|
||||
var tagging = this.port_opts[i].formvalue(section_id),
|
||||
for (var i = 0; i < port_opts.length; i++) {
|
||||
var tagging = port_opts[i].formvalue(section_id),
|
||||
portspec = Array.isArray(topology.ports) ? topology.ports[i] : null;
|
||||
|
||||
if (tagging == 't')
|
||||
values.push(this.port_opts[i].option + tagging);
|
||||
values.push(port_opts[i].option + tagging);
|
||||
else if (tagging == 'u')
|
||||
values.push(this.port_opts[i].option);
|
||||
values.push(port_opts[i].option);
|
||||
|
||||
if (portspec && portspec.device) {
|
||||
var old_tag = this.port_opts[i].cfgvalue(section_id),
|
||||
var old_tag = port_opts[i].cfgvalue(section_id),
|
||||
old_vid = this.cfgvalue(section_id);
|
||||
|
||||
if (old_tag != tagging || old_vid != value) {
|
||||
|
|
199
luci-mod-network/htdocs/luci-static/resources/view/network/wireless.js
Normal file → Executable file
199
luci-mod-network/htdocs/luci-static/resources/view/network/wireless.js
Normal file → Executable file
|
@ -314,15 +314,23 @@ var CBIWifiFrequencyValue = form.Value.extend({
|
|||
this.channels = {
|
||||
'2g': L.hasSystemFeature('hostapd', 'acs') ? [ 'auto', 'auto', true ] : [],
|
||||
'5g': L.hasSystemFeature('hostapd', 'acs') ? [ 'auto', 'auto', true ] : [],
|
||||
'6g': L.hasSystemFeature('hostapd', 'acs') ? [ 'auto', 'auto', true ] : [],
|
||||
'6g': [],
|
||||
'60g': []
|
||||
};
|
||||
|
||||
for (var i = 0; i < data[1].length; i++) {
|
||||
if (!data[1][i].band)
|
||||
continue;
|
||||
var band;
|
||||
|
||||
var band = '%dg'.format(data[1][i].band);
|
||||
if (data[1][i].mhz >= 2412 && data[1][i].mhz <= 2484)
|
||||
band = '2g';
|
||||
else if (data[1][i].mhz >= 5160 && data[1][i].mhz <= 5885)
|
||||
band = '5g';
|
||||
else if (data[1][i].mhz >= 5925 && data[1][i].mhz <= 7125)
|
||||
band = '6g';
|
||||
else if (data[1][i].mhz >= 58329 && data[1][i].mhz <= 69120)
|
||||
band = '60g';
|
||||
else
|
||||
continue;
|
||||
|
||||
this.channels[band].push(
|
||||
data[1][i].channel,
|
||||
|
@ -335,10 +343,10 @@ var CBIWifiFrequencyValue = form.Value.extend({
|
|||
.reduce(function(o, v) { o[v] = true; return o }, {});
|
||||
|
||||
this.modes = [
|
||||
'', 'Legacy', hwmodelist.a || hwmodelist.b || hwmodelist.g,
|
||||
'', 'Legacy', true,
|
||||
'n', 'N', hwmodelist.n,
|
||||
'ac', 'AC', L.hasSystemFeature('hostapd', '11ac') && hwmodelist.ac,
|
||||
'ax', 'AX', L.hasSystemFeature('hostapd', '11ax') && hwmodelist.ax
|
||||
'ac', 'AC', hwmodelist.ac,
|
||||
'ax', 'AX', hwmodelist.ax
|
||||
];
|
||||
|
||||
var htmodelist = L.toArray(data[0] ? data[0].getHTModes() : null)
|
||||
|
@ -367,8 +375,7 @@ var CBIWifiFrequencyValue = form.Value.extend({
|
|||
this.bands = {
|
||||
'': [
|
||||
'2g', '2.4 GHz', this.channels['2g'].length > 3,
|
||||
'5g', '5 GHz', this.channels['5g'].length > 3,
|
||||
'60g', '60 GHz', this.channels['60g'].length > 0
|
||||
'5g', '5 GHz', this.channels['5g'].length > 3
|
||||
],
|
||||
'n': [
|
||||
'2g', '2.4 GHz', this.channels['2g'].length > 3,
|
||||
|
@ -379,8 +386,7 @@ var CBIWifiFrequencyValue = form.Value.extend({
|
|||
],
|
||||
'ax': [
|
||||
'2g', '2.4 GHz', this.channels['2g'].length > 3,
|
||||
'5g', '5 GHz', this.channels['5g'].length > 3,
|
||||
'6g', '6 GHz', this.channels['6g'].length > 3
|
||||
'5g', '5 GHz', this.channels['5g'].length > 3
|
||||
]
|
||||
};
|
||||
}, this));
|
||||
|
@ -473,7 +479,7 @@ var CBIWifiFrequencyValue = form.Value.extend({
|
|||
this.toggleWifiBand(elem);
|
||||
|
||||
bwdt.value = htval;
|
||||
chan.value = chval || (chan.options[0] ? chan.options[0].value : 'auto');
|
||||
chan.value = chval || chan.options[0].value;
|
||||
|
||||
return elem;
|
||||
},
|
||||
|
@ -735,8 +741,7 @@ return view.extend({
|
|||
load: function() {
|
||||
return Promise.all([
|
||||
uci.changes(),
|
||||
uci.load('wireless'),
|
||||
uci.load('system')
|
||||
uci.load('wireless')
|
||||
]);
|
||||
},
|
||||
|
||||
|
@ -936,7 +941,7 @@ return view.extend({
|
|||
|
||||
if (hwtype == 'mac80211') {
|
||||
o = ss.taboption('general', form.Flag, 'legacy_rates', _('Allow legacy 802.11b rates'), _('Legacy or badly behaving devices may require legacy 802.11b rates to interoperate. Airtime efficiency may be significantly reduced where these are used. It is recommended to not allow 802.11b rates where possible.'));
|
||||
o.depends({'_freq': '2g', '!contains': true});
|
||||
o.depends({'_freq': '11g', '!contains': true});
|
||||
|
||||
o = ss.taboption('general', CBIWifiTxPowerValue, 'txpower', _('Maximum transmit power'), _('Specifies the maximum transmit power the wireless radio may use. Depending on regulatory requirements and wireless usage, the actual transmit power may be reduced by the driver.'));
|
||||
o.wifiNetwork = radioNet;
|
||||
|
@ -980,7 +985,6 @@ 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'));
|
||||
|
@ -1085,7 +1089,6 @@ return view.extend({
|
|||
|
||||
o = ss.taboption('macfilter', form.DynamicList, 'maclist', _('MAC-List'));
|
||||
o.datatype = 'macaddr';
|
||||
o.retain = true;
|
||||
o.depends('macfilter', 'allow');
|
||||
o.depends('macfilter', 'deny');
|
||||
o.load = function(section_id) {
|
||||
|
@ -1141,27 +1144,16 @@ 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');
|
||||
|
||||
o = ss.taboption('advanced', form.Value, 'ifname', _('Interface name'), _('Override default interface name'));
|
||||
o.optional = true;
|
||||
o.datatype = 'netdevname';
|
||||
o.placeholder = radioNet.getIfname();
|
||||
if (/^radio\d+\.network/.test(o.placeholder))
|
||||
o.placeholder = '';
|
||||
|
||||
o = ss.taboption('advanced', form.Value, 'macaddr', _('MAC address'), _('Override default MAC address - the range of usable addresses might be limited by the driver'));
|
||||
o.optional = true;
|
||||
o.placeholder = radioNet.getActiveBSSID();
|
||||
o.datatype = 'macaddr';
|
||||
|
||||
o = ss.taboption('advanced', form.Flag, 'short_preamble', _('Short Preamble'));
|
||||
o.default = o.enabled;
|
||||
|
||||
|
@ -1179,7 +1171,7 @@ return view.extend({
|
|||
o.optional = true;
|
||||
o.datatype = 'uinteger';
|
||||
|
||||
o = ss.taboption('advanced', form.Value, 'max_inactivity', _('Station inactivity limit'), _('802.11v: BSS Max Idle. Units: seconds.'));
|
||||
o = ss.taboption('advanced', form.Value, 'max_inactivity', _('Station inactivity limit'), _('sec'));
|
||||
o.optional = true;
|
||||
o.placeholder = 300;
|
||||
o.datatype = 'uinteger';
|
||||
|
@ -1282,7 +1274,7 @@ return view.extend({
|
|||
if (has_hostapd || has_supplicant) {
|
||||
crypto_modes.push(['psk2', 'WPA2-PSK', 35]);
|
||||
crypto_modes.push(['psk-mixed', 'WPA-PSK/WPA2-PSK Mixed Mode', 22]);
|
||||
crypto_modes.push(['psk', 'WPA-PSK', 12]);
|
||||
crypto_modes.push(['psk', 'WPA-PSK', 21]);
|
||||
}
|
||||
else {
|
||||
encr.description = _('WPA-Encryption requires wpa_supplicant (for client mode) or hostapd (for AP and ad-hoc mode) to be installed.');
|
||||
|
@ -1382,7 +1374,7 @@ return view.extend({
|
|||
else if (hwtype == 'broadcom') {
|
||||
crypto_modes.push(['psk2', 'WPA2-PSK', 33]);
|
||||
crypto_modes.push(['psk+psk2', 'WPA-PSK/WPA2-PSK Mixed Mode', 22]);
|
||||
crypto_modes.push(['psk', 'WPA-PSK', 12]);
|
||||
crypto_modes.push(['psk', 'WPA-PSK', 21]);
|
||||
crypto_modes.push(['wep-open', _('WEP Open System'), 11]);
|
||||
crypto_modes.push(['wep-shared', _('WEP Shared Key'), 10]);
|
||||
}
|
||||
|
@ -1400,90 +1392,51 @@ return view.extend({
|
|||
}
|
||||
|
||||
|
||||
o = ss.taboption('encryption', form.Value, 'auth_server', _('RADIUS Authentication Server'));
|
||||
o = ss.taboption('encryption', form.Value, 'auth_server', _('Radius-Authentication-Server'));
|
||||
add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] });
|
||||
o.rmempty = true;
|
||||
o.datatype = 'host(0)';
|
||||
|
||||
o = ss.taboption('encryption', form.Value, 'auth_port', _('RADIUS Authentication Port'));
|
||||
o = ss.taboption('encryption', form.Value, 'auth_port', _('Radius-Authentication-Port'), _('Default %d').format(1812));
|
||||
add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] });
|
||||
o.rmempty = true;
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '1812';
|
||||
|
||||
o = ss.taboption('encryption', form.Value, 'auth_secret', _('RADIUS Authentication Secret'));
|
||||
o = ss.taboption('encryption', form.Value, 'auth_secret', _('Radius-Authentication-Secret'));
|
||||
add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] });
|
||||
o.rmempty = true;
|
||||
o.password = true;
|
||||
|
||||
o = ss.taboption('encryption', form.Value, 'acct_server', _('RADIUS Accounting Server'));
|
||||
o = ss.taboption('encryption', form.Value, 'acct_server', _('Radius-Accounting-Server'));
|
||||
add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] });
|
||||
o.rmempty = true;
|
||||
o.datatype = 'host(0)';
|
||||
|
||||
o = ss.taboption('encryption', form.Value, 'acct_port', _('RADIUS Accounting Port'));
|
||||
o = ss.taboption('encryption', form.Value, 'acct_port', _('Radius-Accounting-Port'), _('Default %d').format(1813));
|
||||
add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] });
|
||||
o.rmempty = true;
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '1813';
|
||||
|
||||
o = ss.taboption('encryption', form.Value, 'acct_secret', _('RADIUS Accounting Secret'));
|
||||
o = ss.taboption('encryption', form.Value, 'acct_secret', _('Radius-Accounting-Secret'));
|
||||
add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] });
|
||||
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.'));
|
||||
o = ss.taboption('encryption', form.Value, 'dae_client', _('DAE-Client'));
|
||||
add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] });
|
||||
o.rmempty = true;
|
||||
o.datatype = 'host(0)';
|
||||
|
||||
o = ss.taboption('encryption', form.Value, 'dae_port', _('DAE-Port'), _('Dynamic Authorization Extension port.'));
|
||||
o = ss.taboption('encryption', form.Value, 'dae_port', _('DAE-Port'), _('Default %d').format(3799));
|
||||
add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] });
|
||||
o.rmempty = true;
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '3799';
|
||||
|
||||
o = ss.taboption('encryption', form.Value, 'dae_secret', _('DAE-Secret'), _('Dynamic Authorization Extension secret.'));
|
||||
o = ss.taboption('encryption', form.Value, 'dae_secret', _('DAE-Secret'));
|
||||
add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] });
|
||||
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');
|
||||
|
@ -1547,117 +1500,66 @@ 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('roaming', form.Flag, 'ieee80211r', _('802.11r Fast Transition'), _('Enables fast roaming among access points that belong to the same Mobility Domain'));
|
||||
o = ss.taboption('encryption', 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('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.'));
|
||||
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.'));
|
||||
add_dependency_permutations(o, { mode: ['ap', 'ap-wds'], encryption: ['wpa', 'wpa2', 'wpa3', 'wpa3-mixed'] });
|
||||
o.depends({ ieee80211r: '1' });
|
||||
o.rmempty = true;
|
||||
|
||||
o = ss.taboption('roaming', form.Value, 'mobility_domain', _('Mobility Domain'), _('4-character hexadecimal ID'));
|
||||
o = ss.taboption('encryption', 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('roaming', form.Value, 'reassociation_deadline', _('Reassociation Deadline'), _('time units (TUs / 1.024 ms) [1000-65535]'));
|
||||
o = ss.taboption('encryption', 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('roaming', form.ListValue, 'ft_over_ds', _('FT protocol'));
|
||||
o = ss.taboption('encryption', form.ListValue, 'ft_over_ds', _('FT protocol'));
|
||||
o.depends({ ieee80211r: '1' });
|
||||
o.value('0', _('FT over the Air'));
|
||||
o.value('1', _('FT over DS'));
|
||||
o.value('0', _('FT over the Air'));
|
||||
o.rmempty = true;
|
||||
|
||||
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 = 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.depends({ ieee80211r: '1' });
|
||||
o.default = o.enabled;
|
||||
o.rmempty = false;
|
||||
|
||||
o = ss.taboption('roaming', form.Value, 'r0_key_lifetime', _('R0 Key Lifetime'), _('minutes'));
|
||||
o = ss.taboption('encryption', 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('roaming', form.Value, 'r1_key_holder', _('R1 Key Holder'), _('6-octet identifier as a hex string - no colons'));
|
||||
o = ss.taboption('encryption', 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('roaming', form.Flag, 'pmk_r1_push', _('PMK R1 Push'));
|
||||
o = ss.taboption('encryption', form.Flag, 'pmk_r1_push', _('PMK R1 Push'));
|
||||
o.depends({ ieee80211r: '1' });
|
||||
o.placeholder = '0';
|
||||
o.rmempty = true;
|
||||
|
||||
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 = 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.depends({ ieee80211r: '1' });
|
||||
o.rmempty = true;
|
||||
|
||||
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 = 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.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');
|
||||
|
@ -1774,7 +1676,7 @@ return view.extend({
|
|||
if (hwtype == 'mac80211') {
|
||||
// ieee802.11w options
|
||||
o = ss.taboption('encryption', form.ListValue, 'ieee80211w', _('802.11w Management Frame Protection'), _("Note: Some wireless drivers do not fully support 802.11w. E.g. mwlwifi may have problems"));
|
||||
o.value('0', _('Disabled'));
|
||||
o.value('', _('Disabled'));
|
||||
o.value('1', _('Optional'));
|
||||
o.value('2', _('Required'));
|
||||
add_dependency_permutations(o, { mode: ['ap', 'ap-wds', 'sta', 'sta-wds'], encryption: ['owe', 'psk2', 'psk-mixed', 'sae', 'sae-mixed', 'wpa2', 'wpa3', 'wpa3-mixed'] });
|
||||
|
@ -1782,14 +1684,7 @@ return view.extend({
|
|||
o.defaults = {
|
||||
'2': [{ encryption: 'sae' }, { encryption: 'owe' }, { encryption: 'wpa3' }, { encryption: 'wpa3-mixed' }],
|
||||
'1': [{ encryption: 'sae-mixed'}],
|
||||
'0': []
|
||||
};
|
||||
|
||||
o.write = function(section_id, value) {
|
||||
if (value != this.default)
|
||||
return form.ListValue.prototype.write.call(this, section_id, value);
|
||||
else
|
||||
return form.ListValue.prototype.remove.call(this, section_id);
|
||||
'': []
|
||||
};
|
||||
|
||||
o = ss.taboption('encryption', form.Value, 'ieee80211w_max_timeout', _('802.11w maximum timeout'), _('802.11w Association SA Query maximum timeout'));
|
||||
|
@ -2061,8 +1956,6 @@ return view.extend({
|
|||
});
|
||||
});
|
||||
}).then(L.bind(function() {
|
||||
ui.showModal(null, E('p', { 'class': 'spinning' }, [ _('Loading data…') ]));
|
||||
|
||||
return this.renderMoreOptionsModal(section_id);
|
||||
}, this));
|
||||
};
|
||||
|
|
46
luci-mod-network/root/usr/share/luci/menu.d/luci-mod-network.json
Normal file → Executable file
46
luci-mod-network/root/usr/share/luci/menu.d/luci-mod-network.json
Normal file → Executable file
|
@ -46,9 +46,35 @@
|
|||
}
|
||||
},
|
||||
|
||||
"admin/network/routes": {
|
||||
"title": "Routing",
|
||||
"admin/network/dhcp": {
|
||||
"title": "DHCP and DNS",
|
||||
"order": 30,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network/dhcp"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-mod-network-dhcp" ],
|
||||
"uci": { "dhcp": true }
|
||||
}
|
||||
},
|
||||
|
||||
"admin/network/hosts": {
|
||||
"title": "Hostnames",
|
||||
"order": 40,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network/hosts"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-mod-network-dhcp" ],
|
||||
"uci": { "dhcp": true }
|
||||
}
|
||||
},
|
||||
|
||||
"admin/network/routes": {
|
||||
"title": "Static Routes",
|
||||
"order": 50,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network/routes"
|
||||
|
@ -58,23 +84,9 @@
|
|||
}
|
||||
},
|
||||
|
||||
"admin/network/dhcp": {
|
||||
"title": "DHCP and DNS",
|
||||
"order": 40,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network/dhcp"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-mod-network-dhcp" ],
|
||||
"fs": { "/usr/sbin/dnsmasq": "executable" },
|
||||
"uci": { "dhcp": true }
|
||||
}
|
||||
},
|
||||
|
||||
"admin/network/diagnostics": {
|
||||
"title": "Diagnostics",
|
||||
"order": 50,
|
||||
"order": 60,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network/diagnostics"
|
||||
|
|
7
luci-mod-network/root/usr/share/rpcd/acl.d/luci-mod-network.json
Normal file → Executable file
7
luci-mod-network/root/usr/share/rpcd/acl.d/luci-mod-network.json
Normal file → Executable file
|
@ -8,9 +8,7 @@
|
|||
"/proc/sys/net/ipv6/conf/*/mtu": [ "read" ],
|
||||
"/proc/sys/net/ipv6/conf/*/hop_limit": [ "read" ],
|
||||
"/usr/libexec/luci-peeraddr": [ "exec" ],
|
||||
"/usr/lib/opkg/info/netifd.control": [ "read" ],
|
||||
"/proc/sys/net/ipv[46]/conf/*": [ "read" ],
|
||||
"/sys/class/net/*/brport/*": [ "read" ]
|
||||
"/usr/lib/opkg/info/netifd.control": [ "read" ]
|
||||
},
|
||||
"ubus": {
|
||||
"file": [ "exec" ],
|
||||
|
@ -60,8 +58,7 @@
|
|||
"/usr/bin/ping": [ "exec" ],
|
||||
"/usr/bin/ping6": [ "exec", "list" ],
|
||||
"/usr/bin/traceroute": [ "exec" ],
|
||||
"/usr/bin/traceroute6": [ "exec", "list" ],
|
||||
"/usr/bin/arp-scan": [ "exec", "list" ]
|
||||
"/usr/bin/traceroute6": [ "exec", "list" ]
|
||||
},
|
||||
"ubus": {
|
||||
"file": [ "exec", "stat" ]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue