luci-mod-network: add support for network.device sections
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
parent
069a97e55c
commit
faad7464a8
3 changed files with 960 additions and 88 deletions
|
@ -0,0 +1,653 @@
|
|||
'use strict';
|
||||
'require uci';
|
||||
'require form';
|
||||
'require network';
|
||||
'require baseclass';
|
||||
'require validation';
|
||||
'require tools.widgets as widgets';
|
||||
|
||||
function validateAddr(section_id, value) {
|
||||
if (value == '')
|
||||
return true;
|
||||
|
||||
var ipv6 = /6$/.test(this.section.formvalue(section_id, 'mode')),
|
||||
addr = ipv6 ? validation.parseIPv6(value) : validation.parseIPv4(value);
|
||||
|
||||
return addr ? true : (ipv6 ? _('Expecting a valid IPv6 address') : _('Expecting a valid IPv4 address'));
|
||||
}
|
||||
|
||||
function setIfActive(section_id, value) {
|
||||
if (this.isActive(section_id)) {
|
||||
uci.set('network', section_id, this.ucioption, value);
|
||||
|
||||
/* Requires http://lists.openwrt.org/pipermail/openwrt-devel/2020-July/030397.html */
|
||||
if (false && this.option == 'ifname_multi') {
|
||||
var devname = this.section.formvalue(section_id, 'name_complex'),
|
||||
m = devname ? devname.match(/^br-([A-Za-z0-9_]+)$/) : null;
|
||||
|
||||
if (m && uci.get('network', m[1], 'type') == 'bridge') {
|
||||
uci.set('network', m[1], 'ifname', devname);
|
||||
uci.unset('network', m[1], 'type');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateQoSMap(section_id, value) {
|
||||
if (value == '')
|
||||
return true;
|
||||
|
||||
var m = value.match(/^(\d+):(\d+)$/);
|
||||
|
||||
if (!m || +m[1] > 0xFFFFFFFF || +m[2] > 0xFFFFFFFF)
|
||||
return _('Expecting two priority values separated by a colon');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function deviceSectionExists(section_id, devname) {
|
||||
var exists = false;
|
||||
|
||||
uci.sections('network', 'device', function(ss) {
|
||||
exists = exists || (ss['.name'] != section_id && ss.name == devname /* && !ss.type*/);
|
||||
});
|
||||
|
||||
/* Until http://lists.openwrt.org/pipermail/openwrt-devel/2020-July/030397.html lands,
|
||||
prevent redeclaring interface bridges */
|
||||
if (!exists) {
|
||||
var m = devname.match(/^br-([A-Za-z0-9_]+)$/),
|
||||
s = m ? uci.get('network', m[1]) : null;
|
||||
|
||||
if (s && s['.type'] == 'interface' && s.type == 'bridge')
|
||||
exists = true;
|
||||
}
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
function isBridgePort(dev) {
|
||||
if (!dev)
|
||||
return false;
|
||||
|
||||
if (dev.isBridgePort())
|
||||
return true;
|
||||
|
||||
var isPort = false;
|
||||
|
||||
uci.sections('network', null, function(s) {
|
||||
if (s['.type'] != 'interface' && s['.type'] != 'device')
|
||||
return;
|
||||
|
||||
if (s.type == 'bridge' && L.toArray(s.ifname).indexOf(dev.getName()) > -1)
|
||||
isPort = true;
|
||||
});
|
||||
|
||||
return isPort;
|
||||
}
|
||||
|
||||
function lookupDevName(s, section_id) {
|
||||
var typeui = s.getUIElement(section_id, 'type'),
|
||||
typeval = typeui ? typeui.getValue() : s.cfgvalue(section_id, 'type'),
|
||||
ifnameui = s.getUIElement(section_id, 'ifname_single'),
|
||||
ifnameval = ifnameui ? ifnameui.getValue() : s.cfgvalue(section_id, 'ifname_single');
|
||||
|
||||
return (typeval == 'bridge') ? 'br-%s'.format(section_id) : ifnameval;
|
||||
}
|
||||
|
||||
function lookupDevSection(s, section_id, autocreate) {
|
||||
var devname = lookupDevName(s, section_id),
|
||||
devsection = null;
|
||||
|
||||
uci.sections('network', 'device', function(ds) {
|
||||
if (ds.name == devname)
|
||||
devsection = ds['.name'];
|
||||
});
|
||||
|
||||
if (autocreate && !devsection) {
|
||||
devsection = uci.add('network', 'device');
|
||||
uci.set('network', devsection, 'name', devname);
|
||||
}
|
||||
|
||||
return devsection;
|
||||
}
|
||||
|
||||
function getDeviceValue(dev, method) {
|
||||
if (dev && dev.getL3Device)
|
||||
dev = dev.getL3Device();
|
||||
|
||||
if (dev && typeof(dev[method]) == 'function')
|
||||
return dev[method].apply(dev);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function deviceCfgValue(section_id) {
|
||||
if (arguments.length == 2)
|
||||
return;
|
||||
|
||||
var ds = lookupDevSection(this.section, section_id, false);
|
||||
|
||||
return (ds ? uci.get('network', ds, this.option) : null) ||
|
||||
uci.get('network', section_id, this.option) ||
|
||||
this.default;
|
||||
}
|
||||
|
||||
function deviceWrite(section_id, formvalue) {
|
||||
var ds = lookupDevSection(this.section, section_id, true);
|
||||
|
||||
uci.set('network', ds, this.option, formvalue);
|
||||
uci.unset('network', section_id, this.option);
|
||||
}
|
||||
|
||||
function deviceRemove(section_id) {
|
||||
var ds = lookupDevSection(this.section, section_id, false),
|
||||
sv = ds ? uci.get('network', ds) : null;
|
||||
|
||||
if (sv) {
|
||||
var empty = true;
|
||||
|
||||
for (var opt in sv) {
|
||||
if (opt.charAt(0) == '.' || opt == 'name' || opt == this.option)
|
||||
continue;
|
||||
|
||||
empty = false;
|
||||
}
|
||||
|
||||
if (empty)
|
||||
uci.remove('network', ds);
|
||||
}
|
||||
|
||||
uci.unset('network', section_id, this.option);
|
||||
}
|
||||
|
||||
function deviceRefresh(section_id) {
|
||||
var dev = network.instantiateDevice(lookupDevName(this.section, section_id)),
|
||||
uielem = this.getUIElement(section_id);
|
||||
|
||||
if (uielem) {
|
||||
switch (this.option) {
|
||||
case 'mtu':
|
||||
case 'mtu6':
|
||||
uielem.setPlaceholder(dev.getMTU());
|
||||
break;
|
||||
|
||||
case 'macaddr':
|
||||
uielem.setPlaceholder(dev.getMAC());
|
||||
break;
|
||||
}
|
||||
|
||||
uielem.setValue(this.cfgvalue(section_id));
|
||||
}
|
||||
}
|
||||
|
||||
return baseclass.extend({
|
||||
replaceOption: function(s, tabName, optionClass, optionName, optionTitle, optionDescription) {
|
||||
var o = s.getOption(optionName);
|
||||
|
||||
if (o) {
|
||||
if (o.tab) {
|
||||
s.tabs[o.tab].children = s.tabs[o.tab].children.filter(function(opt) {
|
||||
return opt.option != optionName;
|
||||
});
|
||||
}
|
||||
|
||||
s.children = s.children.filter(function(opt) {
|
||||
return opt.option != optionName;
|
||||
});
|
||||
}
|
||||
|
||||
return s.taboption(tabName, optionClass, optionName, optionTitle, optionDescription);
|
||||
},
|
||||
|
||||
addOption: function(s, tabName, optionClass, optionName, optionTitle, optionDescription) {
|
||||
var o = this.replaceOption(s, tabName, optionClass, optionName, optionTitle, optionDescription);
|
||||
|
||||
if (s.sectiontype == 'interface' && optionName != 'type' && optionName != 'vlan_filtering') {
|
||||
o.cfgvalue = deviceCfgValue;
|
||||
o.write = deviceWrite;
|
||||
o.remove = deviceRemove;
|
||||
o.refresh = deviceRefresh;
|
||||
}
|
||||
|
||||
return o;
|
||||
},
|
||||
|
||||
addDeviceOptions: function(s, dev, isNew) {
|
||||
var isIface = (s.sectiontype == 'interface'),
|
||||
ifc = isIface ? network.instantiateNetwork(s.section) : null,
|
||||
gensection = ifc ? 'physical' : 'devgeneral',
|
||||
advsection = ifc ? 'physical' : 'devadvanced',
|
||||
simpledep = ifc ? { type: '', ifname_single: /^[^@]/ } : { type: '' },
|
||||
o, ss;
|
||||
|
||||
if (isIface) {
|
||||
var type;
|
||||
|
||||
type = this.addOption(s, gensection, form.Flag, 'type', _('Bridge interfaces'), _('Creates a bridge over specified interface(s)'));
|
||||
type.modalonly = true;
|
||||
type.disabled = '';
|
||||
type.enabled = 'bridge';
|
||||
type.write = type.remove = function(section_id, value) {
|
||||
var protoname = this.section.formvalue(section_id, 'proto'),
|
||||
protocol = network.getProtocol(protoname),
|
||||
new_ifnames = this.isActive(section_id) ? L.toArray(this.section.formvalue(section_id, value ? 'ifname_multi' : 'ifname_single')) : [];
|
||||
|
||||
if (!protocol.isVirtual() && !this.isActive(section_id))
|
||||
return;
|
||||
|
||||
var old_ifnames = [],
|
||||
devs = ifc.getDevices() || L.toArray(ifc.getDevice());
|
||||
|
||||
for (var i = 0; i < devs.length; i++)
|
||||
old_ifnames.push(devs[i].getName());
|
||||
|
||||
if (!value)
|
||||
new_ifnames.length = Math.max(new_ifnames.length, 1);
|
||||
|
||||
old_ifnames.sort();
|
||||
new_ifnames.sort();
|
||||
|
||||
for (var i = 0; i < Math.max(old_ifnames.length, new_ifnames.length); i++) {
|
||||
if (old_ifnames[i] != new_ifnames[i]) {
|
||||
// backup_ifnames()
|
||||
for (var j = 0; j < old_ifnames.length; j++)
|
||||
ifc.deleteDevice(old_ifnames[j]);
|
||||
|
||||
for (var j = 0; j < new_ifnames.length; j++)
|
||||
ifc.addDevice(new_ifnames[j]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (value)
|
||||
uci.set('network', section_id, 'type', 'bridge');
|
||||
else
|
||||
uci.unset('network', section_id, 'type');
|
||||
};
|
||||
}
|
||||
else {
|
||||
s.tab('devgeneral', _('General device options'));
|
||||
s.tab('devadvanced', _('Advanced device options'));
|
||||
s.tab('brport', _('Bridge port specific options'));
|
||||
s.tab('bridgevlan', _('Bridge VLAN filtering'));
|
||||
|
||||
o = this.addOption(s, gensection, form.ListValue, 'type', _('Device type'));
|
||||
o.readonly = !isNew;
|
||||
o.value('', _('Network device'));
|
||||
o.value('bridge', _('Bridge device'));
|
||||
o.value('8021q', _('VLAN (802.1q)'));
|
||||
o.value('8021ad', _('VLAN (802.1ad)'));
|
||||
o.value('macvlan', _('MAC VLAN'));
|
||||
o.value('veth', _('Virtual Ethernet'));
|
||||
|
||||
o = this.addOption(s, gensection, widgets.DeviceSelect, 'name_simple', _('Existing device'));
|
||||
o.readonly = !isNew;
|
||||
o.rmempty = false;
|
||||
o.noaliases = true;
|
||||
o.default = (dev ? dev.getName() : '');
|
||||
o.ucioption = 'name';
|
||||
o.write = o.remove = setIfActive;
|
||||
o.filter = function(section_id, value) {
|
||||
return !deviceSectionExists(section_id, value);
|
||||
};
|
||||
o.validate = function(section_id, value) {
|
||||
return deviceSectionExists(section_id, value) ? _('A configuration for the device "%s" already exists').format(value) : true;
|
||||
};
|
||||
o.depends('type', '');
|
||||
}
|
||||
|
||||
o = this.addOption(s, gensection, widgets.DeviceSelect, 'ifname_single', isIface ? _('Interface') : _('Base device'));
|
||||
o.readonly = !isNew;
|
||||
o.rmempty = false;
|
||||
o.noaliases = !isIface;
|
||||
o.default = (dev ? dev.getName() : '').match(/^.+\.\d+$/) ? dev.getName().replace(/\.\d+$/, '') : '';
|
||||
o.ucioption = 'ifname';
|
||||
o.validate = function(section_id, value) {
|
||||
var type = this.section.formvalue(section_id, 'type'),
|
||||
name = this.section.getUIElement(section_id, 'name_complex');
|
||||
|
||||
if (type == 'macvlan' && value && name && !name.isChanged()) {
|
||||
var i = 0;
|
||||
|
||||
while (deviceSectionExists(section_id, '%smac%d'.format(value, i)))
|
||||
i++;
|
||||
|
||||
name.setValue('%smac%d'.format(value, i));
|
||||
name.triggerValidation();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
if (isIface) {
|
||||
o.write = o.remove = function() {};
|
||||
o.cfgvalue = function(section_id) {
|
||||
return (ifc.getDevices() || L.toArray(ifc.getDevice())).map(function(dev) {
|
||||
return dev.getName();
|
||||
});
|
||||
};
|
||||
o.onchange = function(ev, section_id, values) {
|
||||
for (var i = 0, co; (co = this.section.children[i]) != null; i++)
|
||||
if (co !== this && co.refresh)
|
||||
co.refresh(section_id);
|
||||
|
||||
};
|
||||
o.depends('type', '');
|
||||
}
|
||||
else {
|
||||
o.write = o.remove = setIfActive;
|
||||
o.depends('type', '8021q');
|
||||
o.depends('type', '8021ad');
|
||||
o.depends('type', 'macvlan');
|
||||
}
|
||||
|
||||
o = this.addOption(s, gensection, form.Value, 'vid', _('VLAN ID'));
|
||||
o.readonly = !isNew;
|
||||
o.datatype = 'range(1, 4094)';
|
||||
o.rmempty = false;
|
||||
o.default = (dev ? dev.getName() : '').match(/^.+\.\d+$/) ? dev.getName().replace(/^.+\./, '') : '';
|
||||
o.validate = function(section_id, value) {
|
||||
var base = this.section.formvalue(section_id, 'ifname_single'),
|
||||
vid = this.section.formvalue(section_id, 'vid'),
|
||||
name = this.section.getUIElement(section_id, 'name_complex');
|
||||
|
||||
if (base && vid && name && !name.isChanged()) {
|
||||
name.setValue('%s.%d'.format(base, vid));
|
||||
name.triggerValidation();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
o.depends('type', '8021q');
|
||||
o.depends('type', '8021ad');
|
||||
|
||||
o = this.addOption(s, gensection, form.ListValue, 'mode', _('Mode'));
|
||||
o.value('vepa', _('VEPA (Virtual Ethernet Port Aggregator)', 'MACVLAN mode'));
|
||||
o.value('private', _('Private (Prevent communication between MAC VLANs)', 'MACVLAN mode'));
|
||||
o.value('bridge', _('Bridge (Support direct communication between MAC VLANs)', 'MACVLAN mode'));
|
||||
o.value('passthru', _('Pass-through (Mirror physical device to single MAC VLAN)', 'MACVLAN mode'));
|
||||
o.depends('type', 'macvlan');
|
||||
|
||||
if (!isIface) {
|
||||
o = this.addOption(s, gensection, form.Value, 'name_complex', _('Device name'));
|
||||
o.rmempty = false;
|
||||
o.datatype = 'maxlength(15)';
|
||||
o.readonly = !isNew;
|
||||
o.ucioption = 'name';
|
||||
o.write = o.remove = setIfActive;
|
||||
o.validate = function(section_id, value) {
|
||||
return deviceSectionExists(section_id, value) ? _('The device name "%s" is already taken').format(value) : true;
|
||||
};
|
||||
o.depends({ type: '', '!reverse': true });
|
||||
}
|
||||
|
||||
o = this.addOption(s, advsection, form.DynamicList, 'ingress_qos_mapping', _('Ingress QoS mapping'), _('Defines a mapping of VLAN header priority to the Linux internal packet priority on incoming frames'));
|
||||
o.rmempty = true;
|
||||
o.validate = validateQoSMap;
|
||||
o.depends('type', '8021q');
|
||||
o.depends('type', '8021ad');
|
||||
|
||||
o = this.addOption(s, advsection, form.DynamicList, 'egress_qos_mapping', _('Egress QoS mapping'), _('Defines a mapping of Linux internal packet priority to VLAN header priority but for outgoing frames'));
|
||||
o.rmempty = true;
|
||||
o.validate = validateQoSMap;
|
||||
o.depends('type', '8021q');
|
||||
o.depends('type', '8021ad');
|
||||
|
||||
o = this.addOption(s, gensection, widgets.DeviceSelect, 'ifname_multi', _('Bridge ports'));
|
||||
o.size = 10;
|
||||
o.rmempty = true;
|
||||
o.multiple = true;
|
||||
o.noaliases = true;
|
||||
o.nobridges = true;
|
||||
o.ucioption = 'ifname';
|
||||
if (isIface) {
|
||||
o.write = o.remove = function() {};
|
||||
o.cfgvalue = function(section_id) {
|
||||
return (ifc.getDevices() || L.toArray(ifc.getDevice())).map(function(dev) { return dev.getName() });
|
||||
};
|
||||
}
|
||||
else {
|
||||
o.write = o.remove = setIfActive;
|
||||
o.default = L.toArray(dev ? dev.getPorts() : null).filter(function(p) { return p.getType() != 'wifi' || p.isUp() }).map(function(p) { return p.getName() });
|
||||
o.filter = function(section_id, device_name) {
|
||||
var d = network.instantiateDevice(device_name);
|
||||
return d.getType() != 'wifi' || d.isUp();
|
||||
};
|
||||
}
|
||||
o.onchange = function(ev, section_id, values) {
|
||||
ss.updatePorts(values);
|
||||
|
||||
return ss.parse().then(function() {
|
||||
ss.redraw();
|
||||
});
|
||||
};
|
||||
o.depends('type', 'bridge');
|
||||
|
||||
o = this.addOption(s, gensection, form.Flag, 'bridge_empty', _('Bring up empty bridge'), _('Bring up the bridge interface even if no ports are attached'));
|
||||
o.default = o.disabled;
|
||||
o.depends('type', 'bridge');
|
||||
|
||||
o = this.addOption(s, advsection, form.Value, 'priority', _('Priority'));
|
||||
o.placeholder = '32767';
|
||||
o.datatype = 'range(0, 65535)';
|
||||
o.depends('type', 'bridge');
|
||||
|
||||
o = this.addOption(s, advsection, form.Value, 'ageing_time', _('Ageing time'), _('Timeout in seconds for learned MAC addresses in the forwarding database'));
|
||||
o.placeholder = '30';
|
||||
o.datatype = 'uinteger';
|
||||
o.depends('type', 'bridge');
|
||||
|
||||
o = this.addOption(s, advsection, form.Flag, 'stp', _('Enable <abbr title="Spanning Tree Protocol">STP</abbr>'), _('Enables the Spanning Tree Protocol on this bridge'));
|
||||
o.default = o.disabled;
|
||||
o.depends('type', 'bridge');
|
||||
|
||||
o = this.addOption(s, advsection, form.Value, 'hello_time', _('Hello interval'), _('Interval in seconds for STP hello packets'));
|
||||
o.placeholder = '2';
|
||||
o.datatype = 'range(1, 10)';
|
||||
o.depends({ type: 'bridge', stp: '1' });
|
||||
|
||||
o = this.addOption(s, advsection, form.Value, 'forward_delay', _('Forward delay'), _('Time in seconds to spend in listening and learning states'));
|
||||
o.placeholder = '15';
|
||||
o.datatype = 'range(2, 30)';
|
||||
o.depends({ type: 'bridge', stp: '1' });
|
||||
|
||||
o = this.addOption(s, advsection, form.Value, 'max_age', _('Maximum age'), _('Timeout in seconds until topology updates on link loss'));
|
||||
o.placeholder = '20';
|
||||
o.datatype = 'range(6, 40)';
|
||||
o.depends({ type: 'bridge', stp: '1' });
|
||||
|
||||
|
||||
o = this.addOption(s, advsection, form.Flag, 'igmp_snooping', _('Enable <abbr title="Internet Group Management Protocol">IGMP</abbr> snooping'), _('Enables IGMP snooping on this bridge'));
|
||||
o.default = o.disabled;
|
||||
o.depends('type', 'bridge');
|
||||
|
||||
o = this.addOption(s, advsection, form.Value, 'hash_max', _('Maximum snooping table size'));
|
||||
o.placeholder = '512';
|
||||
o.datatype = 'uinteger';
|
||||
o.depends({ type: 'bridge', igmp_snooping: '1' });
|
||||
|
||||
o = this.addOption(s, advsection, form.Flag, 'multicast_querier', _('Enable multicast querier'));
|
||||
o.defaults = { '1': [{'igmp_snooping': '1'}], '0': [{'igmp_snooping': '0'}] };
|
||||
o.depends('type', 'bridge');
|
||||
|
||||
o = this.addOption(s, advsection, form.Value, 'robustness', _('Robustness'), _('The robustness value allows tuning for the expected packet loss on the network. If a network is expected to be lossy, the robustness value may be increased. IGMP is robust to (Robustness-1) packet losses'));
|
||||
o.placeholder = '2';
|
||||
o.datatype = 'min(1)';
|
||||
o.depends({ type: 'bridge', multicast_querier: '1' });
|
||||
|
||||
o = this.addOption(s, advsection, form.Value, 'query_interval', _('Query interval'), _('Interval in centiseconds between multicast general queries. By varying the value, an administrator may tune the number of IGMP messages on the subnet; larger values cause IGMP Queries to be sent less often'));
|
||||
o.placeholder = '12500';
|
||||
o.datatype = 'uinteger';
|
||||
o.depends({ type: 'bridge', multicast_querier: '1' });
|
||||
|
||||
o = this.addOption(s, advsection, form.Value, 'query_response_interval', _('Query response interval'), _('The max response time in centiseconds inserted into the periodic general queries. By varying the value, an administrator may tune the burstiness of IGMP messages on the subnet; larger values make the traffic less bursty, as host responses are spread out over a larger interval'));
|
||||
o.placeholder = '1000';
|
||||
o.datatype = 'uinteger';
|
||||
o.validate = function(section_id, value) {
|
||||
var qiopt = L.toArray(this.map.lookupOption('query_interval', section_id))[0],
|
||||
qival = qiopt ? (qiopt.formvalue(section_id) || qiopt.placeholder) : '';
|
||||
|
||||
if (value != '' && qival != '' && +value >= +qival)
|
||||
return _('The query response interval must be lower than the query interval value');
|
||||
|
||||
return true;
|
||||
};
|
||||
o.depends({ type: 'bridge', multicast_querier: '1' });
|
||||
|
||||
o = this.addOption(s, advsection, form.Value, 'last_member_interval', _('Last member interval'), _('The max response time in centiseconds inserted into group-specific queries sent in response to leave group messages. It is also the amount of time between group-specific query messages. This value may be tuned to modify the "leave latency" of the network. A reduced value results in reduced time to detect the loss of the last member of a group'));
|
||||
o.placeholder = '100';
|
||||
o.datatype = 'uinteger';
|
||||
o.depends({ type: 'bridge', multicast_querier: '1' });
|
||||
|
||||
o = this.addOption(s, gensection, form.Value, 'mtu', _('MTU'));
|
||||
o.placeholder = getDeviceValue(ifc || dev, 'getMTU');
|
||||
o.datatype = 'max(9200)';
|
||||
o.depends(simpledep);
|
||||
|
||||
o = this.addOption(s, gensection, form.Value, 'macaddr', _('MAC address'));
|
||||
o.placeholder = getDeviceValue(ifc || dev, 'getMAC');
|
||||
o.datatype = 'macaddr';
|
||||
o.depends(simpledep);
|
||||
o.depends('type', 'macvlan');
|
||||
o.depends('type', 'veth');
|
||||
|
||||
o = this.addOption(s, gensection, form.Value, 'peer_name', _('Peer device name'));
|
||||
o.rmempty = true;
|
||||
o.datatype = 'maxlength(15)';
|
||||
o.depends('type', 'veth');
|
||||
o.load = function(section_id) {
|
||||
var sections = uci.sections('network', 'device'),
|
||||
idx = 0;
|
||||
|
||||
for (var i = 0; i < sections.length; i++)
|
||||
if (sections[i]['.name'] == section_id)
|
||||
break;
|
||||
else if (sections[i].type == 'veth')
|
||||
idx++;
|
||||
|
||||
this.placeholder = 'veth%d'.format(idx);
|
||||
|
||||
return form.Value.prototype.load.apply(this, arguments);
|
||||
};
|
||||
|
||||
o = this.addOption(s, gensection, form.Value, 'peer_macaddr', _('Peer MAC address'));
|
||||
o.rmempty = true;
|
||||
o.datatype = 'macaddr';
|
||||
o.depends('type', 'veth');
|
||||
|
||||
o = this.addOption(s, gensection, form.Value, 'txqueuelen', _('TX queue length'));
|
||||
o.placeholder = dev ? dev._devstate('qlen') : '';
|
||||
o.datatype = 'uinteger';
|
||||
o.depends(simpledep);
|
||||
|
||||
o = this.addOption(s, advsection, form.Flag, 'promisc', _('Enable promiscious mode'));
|
||||
o.default = o.disabled;
|
||||
o.depends(simpledep);
|
||||
|
||||
o = this.addOption(s, advsection, form.ListValue, 'rpfilter', _('Reverse path filter'));
|
||||
o.default = '';
|
||||
o.value('', _('disabled'));
|
||||
o.value('loose', _('Loose filtering'));
|
||||
o.value('strict', _('Strict filtering'));
|
||||
o.cfgvalue = function(section_id) {
|
||||
var val = form.ListValue.prototype.cfgvalue.apply(this, [section_id]);
|
||||
|
||||
switch (val || '') {
|
||||
case 'loose':
|
||||
case '1':
|
||||
return 'loose';
|
||||
|
||||
case 'strict':
|
||||
case '2':
|
||||
return 'strict';
|
||||
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
o.depends(simpledep);
|
||||
|
||||
o = this.addOption(s, advsection, form.Flag, 'acceptlocal', _('Accept local'), _('Accept packets with local source addresses'));
|
||||
o.default = o.disabled;
|
||||
o.depends(simpledep);
|
||||
|
||||
o = this.addOption(s, advsection, form.Flag, 'sendredirects', _('Send ICMP redirects'));
|
||||
o.default = o.enabled;
|
||||
o.depends(simpledep);
|
||||
|
||||
o = this.addOption(s, advsection, form.Value, 'neighreachabletime', _('Neighbour cache validity'), _('Time in milliseconds'));
|
||||
o.placeholder = '30000';
|
||||
o.datatype = 'uinteger';
|
||||
o.depends(simpledep);
|
||||
|
||||
o = this.addOption(s, advsection, form.Value, 'neighgcstaletime', _('Stale neighbour cache timeout'), _('Timeout in seconds'));
|
||||
o.placeholder = '60';
|
||||
o.datatype = 'uinteger';
|
||||
o.depends(simpledep);
|
||||
|
||||
o = this.addOption(s, advsection, form.Value, 'neighlocktime', _('Minimum ARP validity time'), _('Minimum required time in seconds before an ARP entry may be replaced. Prevents ARP cache thrashing.'));
|
||||
o.placeholder = '0';
|
||||
o.datatype = 'uinteger';
|
||||
o.depends(simpledep);
|
||||
|
||||
o = this.addOption(s, gensection, form.Flag, 'ipv6', _('Enable IPv6'));
|
||||
o.default = o.enabled;
|
||||
o.depends(simpledep);
|
||||
|
||||
o = this.addOption(s, gensection, form.Value, 'mtu6', _('IPv6 MTU'));
|
||||
o.placeholder = getDeviceValue(ifc || dev, 'getMTU');
|
||||
o.datatype = 'max(9200)';
|
||||
o.depends(Object.assign({ ipv6: '1' }, simpledep));
|
||||
|
||||
o = this.addOption(s, gensection, form.Value, 'dadtransmits', _('DAD transmits'), _('Amount of Duplicate Address Detection probes to send'));
|
||||
o.placeholder = '1';
|
||||
o.datatype = 'uinteger';
|
||||
o.depends(Object.assign({ ipv6: '1' }, simpledep));
|
||||
|
||||
|
||||
o = this.addOption(s, advsection, form.Flag, 'multicast', _('Enable multicast support'));
|
||||
o.default = o.enabled;
|
||||
o.depends(simpledep);
|
||||
|
||||
o = this.addOption(s, advsection, 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(Object.assign({ multicast: '1' }, simpledep));
|
||||
|
||||
o = this.addOption(s, advsection, 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(Object.assign({ multicast: '1' }, simpledep));
|
||||
|
||||
if (isBridgePort(dev)) {
|
||||
o = this.addOption(s, 'brport', form.Flag, 'learning', _('Enable MAC address learning'));
|
||||
o.default = o.enabled;
|
||||
o.depends(simpledep);
|
||||
|
||||
o = this.addOption(s, 'brport', form.Flag, 'unicast_flood', _('Enable unicast flooding'));
|
||||
o.default = o.enabled;
|
||||
o.depends(simpledep);
|
||||
|
||||
o = this.addOption(s, 'brport', form.Flag, 'isolated', _('Port isolation'), _('Only allow communication with non-isolated bridge ports when enabled'));
|
||||
o.default = o.disabled;
|
||||
o.depends(simpledep);
|
||||
|
||||
o = this.addOption(s, 'brport', form.ListValue, 'multicast_router', _('Multicast routing'));
|
||||
o.value('', _('Never'));
|
||||
o.value('1', _('Learn'));
|
||||
o.value('2', _('Always'));
|
||||
o.depends(Object.assign({ multicast: '1' }, simpledep));
|
||||
|
||||
o = this.addOption(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(Object.assign({ multicast: '1' }, simpledep));
|
||||
|
||||
o = this.addOption(s, 'brport', form.Flag, 'multicast_fast_leave', _('Enable multicast fast leave'));
|
||||
o.default = o.disabled;
|
||||
o.depends(Object.assign({ multicast: '1' }, simpledep));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -9,6 +9,7 @@
|
|||
'require network';
|
||||
'require firewall';
|
||||
'require tools.widgets as widgets';
|
||||
'require tools.network as nettools';
|
||||
|
||||
var isReadonlyView = !L.hasViewPermission() || null;
|
||||
|
||||
|
@ -291,14 +292,24 @@ return view.extend({
|
|||
load: function() {
|
||||
return Promise.all([
|
||||
network.getDSLModemType(),
|
||||
network.getDevices(),
|
||||
fs.lines('/etc/iproute2/rt_tables'),
|
||||
uci.changes()
|
||||
]);
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var dslModemType = data[0],
|
||||
netDevs = data[1],
|
||||
m, s, o;
|
||||
|
||||
var rtTables = data[2].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');
|
||||
m.tabbed = true;
|
||||
m.chain('dhcp');
|
||||
|
@ -321,6 +332,7 @@ return view.extend({
|
|||
s.tab('general', _('General Settings'));
|
||||
s.tab('advanced', _('Advanced Settings'));
|
||||
s.tab('physical', _('Physical Settings'));
|
||||
s.tab('brport', _('Bridge port specific options'));
|
||||
s.tab('firewall', _('Firewall Settings'));
|
||||
s.tab('dhcp', _('DHCP Server'));
|
||||
|
||||
|
@ -411,80 +423,6 @@ return view.extend({
|
|||
o.modalonly = true;
|
||||
o.default = o.enabled;
|
||||
|
||||
type = s.taboption('physical', form.Flag, 'type', _('Bridge interfaces'), _('Creates a bridge over specified interface(s)'));
|
||||
type.modalonly = true;
|
||||
type.disabled = '';
|
||||
type.enabled = 'bridge';
|
||||
type.write = type.remove = function(section_id, value) {
|
||||
var protocol = network.getProtocol(proto_select.formvalue(section_id)),
|
||||
ifnameopt = this.section.children.filter(function(o) { return o.option == (value ? 'ifname_multi' : 'ifname_single') })[0];
|
||||
|
||||
if (!protocol.isVirtual() && !this.isActive(section_id))
|
||||
return;
|
||||
|
||||
var old_ifnames = [],
|
||||
devs = ifc.getDevices() || L.toArray(ifc.getDevice());
|
||||
|
||||
for (var i = 0; i < devs.length; i++)
|
||||
old_ifnames.push(devs[i].getName());
|
||||
|
||||
var new_ifnames = L.toArray(ifnameopt.formvalue(section_id));
|
||||
|
||||
if (!value)
|
||||
new_ifnames.length = Math.max(new_ifnames.length, 1);
|
||||
|
||||
old_ifnames.sort();
|
||||
new_ifnames.sort();
|
||||
|
||||
for (var i = 0; i < Math.max(old_ifnames.length, new_ifnames.length); i++) {
|
||||
if (old_ifnames[i] != new_ifnames[i]) {
|
||||
// backup_ifnames()
|
||||
for (var j = 0; j < old_ifnames.length; j++)
|
||||
ifc.deleteDevice(old_ifnames[j]);
|
||||
|
||||
for (var j = 0; j < new_ifnames.length; j++)
|
||||
ifc.addDevice(new_ifnames[j]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (value)
|
||||
uci.set('network', section_id, 'type', 'bridge');
|
||||
else
|
||||
uci.unset('network', section_id, 'type');
|
||||
};
|
||||
|
||||
stp = s.taboption('physical', form.Flag, 'stp', _('Enable <abbr title="Spanning Tree Protocol">STP</abbr>'), _('Enables the Spanning Tree Protocol on this bridge'));
|
||||
|
||||
igmp = s.taboption('physical', form.Flag, 'igmp_snooping', _('Enable <abbr title="Internet Group Management Protocol">IGMP</abbr> snooping'), _('Enables IGMP snooping on this bridge'));
|
||||
|
||||
ifname_single = s.taboption('physical', widgets.DeviceSelect, 'ifname_single', _('Interface'));
|
||||
ifname_single.nobridges = ifc.isBridge();
|
||||
ifname_single.noaliases = false;
|
||||
ifname_single.optional = false;
|
||||
ifname_single.network = ifc.getName();
|
||||
ifname_single.write = ifname_single.remove = function() {};
|
||||
|
||||
ifname_multi = s.taboption('physical', widgets.DeviceSelect, 'ifname_multi', _('Interface'));
|
||||
ifname_multi.nobridges = ifc.isBridge();
|
||||
ifname_multi.noaliases = true;
|
||||
ifname_multi.multiple = true;
|
||||
ifname_multi.optional = true;
|
||||
ifname_multi.network = ifc.getName();
|
||||
ifname_multi.display_size = 6;
|
||||
ifname_multi.write = ifname_multi.remove = function() {};
|
||||
|
||||
ifname_single.cfgvalue = ifname_multi.cfgvalue = function(section_id) {
|
||||
var devs = ifc.getDevices() || L.toArray(ifc.getDevice()),
|
||||
ifnames = [];
|
||||
|
||||
for (var i = 0; i < devs.length; i++)
|
||||
ifnames.push(devs[i].getName());
|
||||
|
||||
return ifnames;
|
||||
};
|
||||
|
||||
if (L.hasSystemFeature('firewall')) {
|
||||
o = s.taboption('firewall', widgets.ZoneSelect, '_zone', _('Create / Assign firewall-zone'), _('Choose the firewall zone you want to assign to this interface. Select <em>unspecified</em> to remove the interface from the associated zone or fill out the <em>custom</em> field to define a new zone and attach the interface to it.'));
|
||||
o.network = ifc.getName();
|
||||
|
@ -528,14 +466,6 @@ return view.extend({
|
|||
|
||||
if (protocols[i].getProtocol() != uci.get('network', s.section, 'proto'))
|
||||
proto_switch.depends('proto', protocols[i].getProtocol());
|
||||
|
||||
if (!protocols[i].isVirtual()) {
|
||||
type.depends('proto', protocols[i].getProtocol());
|
||||
stp.depends({ type: 'bridge', proto: protocols[i].getProtocol() });
|
||||
igmp.depends({ type: 'bridge', proto: protocols[i].getProtocol() });
|
||||
ifname_single.depends({ type: '', proto: protocols[i].getProtocol() });
|
||||
ifname_multi.depends({ type: 'bridge', proto: protocols[i].getProtocol() });
|
||||
}
|
||||
}
|
||||
|
||||
if (L.hasSystemFeature('dnsmasq') || L.hasSystemFeature('odhcpd')) {
|
||||
|
@ -658,25 +588,127 @@ return view.extend({
|
|||
}
|
||||
|
||||
ifc.renderFormOptions(s);
|
||||
nettools.addDeviceOptions(s, null, true);
|
||||
|
||||
// Common interface options
|
||||
o = nettools.replaceOption(s, 'advanced', form.Flag, 'defaultroute', _('Use default gateway'), _('If unchecked, no default route is configured'));
|
||||
o.default = o.enabled;
|
||||
|
||||
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'));
|
||||
o.depends('peerdns', '0');
|
||||
o.datatype = 'ipaddr';
|
||||
|
||||
o = nettools.replaceOption(s, 'advanced', form.DynamicList, 'dns_search', _('DNS search domains'));
|
||||
o.depends('peerdns', '0');
|
||||
o.datatype = 'hostname';
|
||||
|
||||
o = nettools.replaceOption(s, 'advanced', form.Value, 'dns_metric', _('DNS weight'), _('The DNS server entries in the local resolv.conf are primarily sorted by the weight specified here'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = '0';
|
||||
|
||||
o = nettools.replaceOption(s, 'advanced', form.Value, 'metric', _('Use gateway metric'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = '0';
|
||||
|
||||
o = nettools.replaceOption(s, 'advanced', form.Value, 'ip4table', _('Override IPv4 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]));
|
||||
|
||||
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][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;
|
||||
|
||||
o = nettools.replaceOption(s, 'advanced', form.Value, 'ip6assign', _('IPv6 assignment length'), _('Assign a part of given length of every public IPv6-prefix to this interface'));
|
||||
o.value('', _('disabled'));
|
||||
o.value('64');
|
||||
o.datatype = 'max(128)';
|
||||
|
||||
o = nettools.replaceOption(s, 'advanced', form.Value, 'ip6hint', _('IPv6 assignment hint'), _('Assign prefix parts using this hexadecimal subprefix ID for this interface.'));
|
||||
o.placeholder = '0';
|
||||
o.validate = function(section_id, value) {
|
||||
if (value == null || value == '')
|
||||
return true;
|
||||
|
||||
var n = parseInt(value, 16);
|
||||
|
||||
if (!/^(0x)?[0-9a-fA-F]+$/.test(value) || isNaN(n) || n >= 0xffffffff)
|
||||
return _('Expecting a hexadecimal assignment hint');
|
||||
|
||||
return true;
|
||||
};
|
||||
for (var i = 33; i <= 64; i++)
|
||||
o.depends('ip6assign', String(i));
|
||||
|
||||
|
||||
o = nettools.replaceOption(s, 'advanced', form.DynamicList, 'ip6class', _('IPv6 prefix filter'), _('If set, downstream subnets are only allocated from the given IPv6 prefix classes.'));
|
||||
o.value('local', 'local (%s)'.format(_('Local ULA')));
|
||||
|
||||
var prefixClasses = {};
|
||||
|
||||
this.networks.forEach(function(net) {
|
||||
var prefixes = net._ubus('ipv6-prefix');
|
||||
if (Array.isArray(prefixes)) {
|
||||
prefixes.forEach(function(pfx) {
|
||||
if (L.isObject(pfx) && typeof(pfx['class']) == 'string') {
|
||||
prefixClasses[pfx['class']] = prefixClasses[pfx['class']] || {};
|
||||
prefixClasses[pfx['class']][net.getName()] = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(prefixClasses).sort().forEach(function(c) {
|
||||
var networks = Object.keys(prefixClasses[c]).sort().join(', ');
|
||||
o.value(c, (c != networks) ? '%s (%s)'.format(c, networks) : c);
|
||||
});
|
||||
|
||||
|
||||
o = nettools.replaceOption(s, 'advanced', form.Value, 'ip6ifaceid', _('IPv6 suffix'), _("Optional. Allowed values: 'eui64', 'random', fixed value like '::1' or '::1:2'. When IPv6 prefix (like 'a:b:c:d::') is received from a delegating server, use the suffix (like '::1') to form the IPv6 address ('a:b:c:d::1') for the interface."));
|
||||
o.datatype = 'ip6hostid';
|
||||
o.placeholder = '::1';
|
||||
|
||||
o = nettools.replaceOption(s, 'advanced', form.Value, 'ip6weight', _('IPv6 preference'), _('When delegating prefixes to multiple downstreams, interfaces with a higher preference value are considered first when allocating subnets.'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = '0';
|
||||
|
||||
for (var i = 0; i < s.children.length; i++) {
|
||||
o = s.children[i];
|
||||
|
||||
switch (o.option) {
|
||||
case 'proto':
|
||||
case 'delegate':
|
||||
case 'auto':
|
||||
case 'type':
|
||||
case 'stp':
|
||||
case 'igmp_snooping':
|
||||
case 'ifname_single':
|
||||
case 'ifname_multi':
|
||||
case '_dhcp':
|
||||
case '_zone':
|
||||
case '_switch_proto':
|
||||
case '_ifacestat_modal':
|
||||
continue;
|
||||
|
||||
case 'ifname_multi':
|
||||
case 'ifname_single':
|
||||
case 'igmp_snooping':
|
||||
case 'stp':
|
||||
case 'type':
|
||||
var deps = [];
|
||||
for (var j = 0; j < protocols.length; j++) {
|
||||
if (!protocols[j].isVirtual()) {
|
||||
if (o.deps.length)
|
||||
for (var k = 0; k < o.deps.length; k++)
|
||||
deps.push(Object.assign({ proto: protocols[j].getProtocol() }, o.deps[k]));
|
||||
else
|
||||
deps.push({ proto: protocols[j].getProtocol() });
|
||||
}
|
||||
}
|
||||
o.deps = deps;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (o.deps.length)
|
||||
for (var j = 0; j < o.deps.length; j++)
|
||||
|
@ -869,6 +901,192 @@ return view.extend({
|
|||
};
|
||||
|
||||
|
||||
// Device configuration
|
||||
s = m.section(form.GridSection, 'device', _('Devices'));
|
||||
s.addremove = true;
|
||||
s.anonymous = true;
|
||||
s.addbtntitle = _('Add device configuration…');
|
||||
|
||||
s.cfgsections = function() {
|
||||
var sections = uci.sections('network', 'device'),
|
||||
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)
|
||||
continue;
|
||||
|
||||
if (netDevs[i].getType() == 'wifi' && !netDevs[i].isUp())
|
||||
continue;
|
||||
|
||||
/* Unless http://lists.openwrt.org/pipermail/openwrt-devel/2020-July/030397.html is implemented,
|
||||
we cannot properly redefine bridges as devices, so filter them away for now... */
|
||||
|
||||
var m = netDevs[i].isBridge() ? netDevs[i].getName().match(/^br-([A-Za-z0-9_]+)$/) : null,
|
||||
s = m ? uci.get('network', m[1]) : null;
|
||||
|
||||
if (s && s['.type'] == 'interface' && s.type == 'bridge')
|
||||
continue;
|
||||
|
||||
section_ids.push('dev:%s'.format(netDevs[i].getName()));
|
||||
}
|
||||
|
||||
return section_ids;
|
||||
};
|
||||
|
||||
s.renderMoreOptionsModal = function(section_id, ev) {
|
||||
var m = section_id.match(/^dev:(.+)$/);
|
||||
|
||||
if (m) {
|
||||
var devtype = getDevType(section_id);
|
||||
|
||||
section_id = uci.add('network', 'device');
|
||||
|
||||
uci.set('network', section_id, 'name', m[1]);
|
||||
uci.set('network', section_id, 'type', (devtype != 'ethernet') ? devtype : null);
|
||||
|
||||
this.addedSection = section_id;
|
||||
}
|
||||
|
||||
return this.super('renderMoreOptionsModal', [section_id, ev]);
|
||||
};
|
||||
|
||||
s.renderRowActions = function(section_id) {
|
||||
var trEl = this.super('renderRowActions', [ section_id, _('Configure…') ]),
|
||||
deleteBtn = trEl.querySelector('button:last-child');
|
||||
|
||||
deleteBtn.firstChild.data = _('Reset');
|
||||
deleteBtn.disabled = section_id.match(/^dev:/) ? true : null;
|
||||
|
||||
return trEl;
|
||||
};
|
||||
|
||||
s.modaltitle = function(section_id) {
|
||||
var m = section_id.match(/^dev:(.+)$/),
|
||||
name = m ? m[1] : uci.get('network', section_id, 'name');
|
||||
|
||||
return name ? '%s: %q'.format(getDevTypeDesc(section_id), name) : _('Add device configuration');
|
||||
};
|
||||
|
||||
s.addModalOptions = function(s) {
|
||||
var isNew = (uci.get('network', s.section, 'name') == null),
|
||||
dev = getDevice(s.section);
|
||||
|
||||
nettools.addDeviceOptions(s, dev, isNew);
|
||||
};
|
||||
|
||||
function getDevice(section_id) {
|
||||
var m = section_id.match(/^dev:(.+)$/),
|
||||
name = m ? m[1] : uci.get('network', section_id, 'name');
|
||||
|
||||
return netDevs.filter(function(d) { return d.getName() == name })[0];
|
||||
}
|
||||
|
||||
function getDevType(section_id) {
|
||||
var cfgtype = uci.get('network', section_id, 'type'),
|
||||
dev = getDevice(section_id);
|
||||
|
||||
switch (cfgtype || (dev ? dev.getType() : '')) {
|
||||
case '':
|
||||
return null;
|
||||
|
||||
case 'vlan':
|
||||
case '8021q':
|
||||
return '8021q';
|
||||
|
||||
case '8021ad':
|
||||
return '8021ad';
|
||||
|
||||
case 'bridge':
|
||||
return 'bridge';
|
||||
|
||||
case 'tunnel':
|
||||
return 'tunnel';
|
||||
|
||||
case 'macvlan':
|
||||
return 'macvlan';
|
||||
|
||||
case 'veth':
|
||||
return 'veth';
|
||||
|
||||
case 'wifi':
|
||||
case 'alias':
|
||||
case 'switch':
|
||||
case 'ethernet':
|
||||
default:
|
||||
return 'ethernet';
|
||||
}
|
||||
}
|
||||
|
||||
function getDevTypeDesc(section_id) {
|
||||
switch (getDevType(section_id) || '') {
|
||||
case '':
|
||||
return E('em', [ _('Device not present') ]);
|
||||
|
||||
case '8021q':
|
||||
return _('VLAN (802.1q)');
|
||||
|
||||
case '8021ad':
|
||||
return _('VLAN (802.1ad)');
|
||||
|
||||
case 'bridge':
|
||||
return _('Bridge device');
|
||||
|
||||
case 'tunnel':
|
||||
return _('Tunnel device');
|
||||
|
||||
case 'macvlan':
|
||||
return _('MAC VLAN');
|
||||
|
||||
case 'veth':
|
||||
return _('Virtual Ethernet');
|
||||
|
||||
default:
|
||||
return _('Network device');
|
||||
}
|
||||
}
|
||||
|
||||
o = s.option(form.DummyValue, 'name', _('Device'));
|
||||
o.modalonly = false;
|
||||
o.textvalue = function(section_id) {
|
||||
var dev = getDevice(section_id),
|
||||
ext = section_id.match(/^dev:/);
|
||||
|
||||
return E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'style': ext ? 'opacity:.5' : null
|
||||
}, [
|
||||
render_iface(dev), ' ', dev ? dev.getName() : (uci.get('network', section_id, 'name') || '?')
|
||||
]);
|
||||
};
|
||||
|
||||
o = s.option(form.DummyValue, 'type', _('Type'));
|
||||
o.textvalue = getDevTypeDesc;
|
||||
o.modalonly = false;
|
||||
|
||||
o = s.option(form.DummyValue, 'macaddr', _('MAC Address'));
|
||||
o.modalonly = false;
|
||||
o.textvalue = function(section_id) {
|
||||
var dev = getDevice(section_id),
|
||||
val = uci.get('network', section_id, 'macaddr'),
|
||||
mac = dev ? dev.getMAC() : null;
|
||||
|
||||
return val ? E('strong', {
|
||||
'data-tooltip': _('The value is overridden by configuration. Original: %s').format(mac || _('unknown'))
|
||||
}, [ val.toUpperCase() ]) : (mac || '-');
|
||||
};
|
||||
|
||||
o = s.option(form.DummyValue, 'mtu', _('MTU'));
|
||||
o.modalonly = false;
|
||||
o.textvalue = function(section_id) {
|
||||
var dev = getDevice(section_id),
|
||||
val = uci.get('network', section_id, 'mtu'),
|
||||
mtu = dev ? dev.getMTU() : null;
|
||||
|
||||
return val ? E('strong', {
|
||||
'data-tooltip': _('The value is overridden by configuration. Original: %s').format(mtu || _('unknown'))
|
||||
}, [ val ]) : (mtu || '-').toString();
|
||||
};
|
||||
|
||||
s = m.section(form.TypedSection, 'globals', _('Global network options'));
|
||||
s.addremove = false;
|
||||
s.anonymous = true;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"read": {
|
||||
"cgi-io": [ "exec" ],
|
||||
"file": {
|
||||
"/etc/iproute2/rt_tables": [ "read" ],
|
||||
"/usr/libexec/luci-peeraddr": [ "exec" ]
|
||||
},
|
||||
"ubus": {
|
||||
|
|
Loading…
Reference in a new issue