diff options
author | Jo-Philipp Wich <jo@mein.io> | 2021-03-18 19:27:31 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-18 19:27:31 +0300 |
commit | 4cde2bda732b133d0ea46cecd94f57c3b1eeab05 (patch) | |
tree | 270ee49c890cd4e77689a8e315358aca83903f0f | |
parent | 1dc16d9d3ffeee30d072c569e9a3c693d303e9fc (diff) | |
parent | bbf1a5343f1d930096cd5ff6b98f3e1f2f0d3c9a (diff) |
Merge pull request #4307 from jow-/uci-network-device-support
Introduce support for managing `config device` and `config bridge-vlan` sections
28 files changed, 1336 insertions, 384 deletions
diff --git a/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/rules.js b/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/rules.js index 7c09eeadf6..df49dcf80c 100644 --- a/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/rules.js +++ b/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/rules.js @@ -193,13 +193,8 @@ return view.extend({ s.handleAdd = function(ev) { var config_name = this.uciconfig || this.map.config, section_id = uci.add(config_name, this.sectiontype), - opt1, opt2; - - for (var i = 0; i < this.children.length; i++) - if (this.children[i].option == 'src') - opt1 = this.children[i]; - else if (this.children[i].option == 'dest') - opt2 = this.children[i]; + opt1 = this.getOption('src'), + opt2 = this.getOption('dest'); opt1.default = 'wan'; opt2.default = 'lan'; diff --git a/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/snats.js b/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/snats.js index 1d6e01c1a8..ce65fc7839 100644 --- a/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/snats.js +++ b/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/snats.js @@ -217,9 +217,8 @@ return view.extend({ o.placeholder = null; o.depends('target', 'SNAT'); o.validate = function(section_id, value) { - var port = this.map.lookupOption('snat_port', section_id), - a = this.formvalue(section_id), - p = port ? port[0].formvalue(section_id) : null; + var a = this.formvalue(section_id), + p = this.section.formvalue(section_id, 'snat_port'); if ((a == null || a == '') && (p == null || p == '') && value == '') return _('A rewrite IP must be specified!'); diff --git a/modules/luci-base/htdocs/luci-static/resources/protocol/dhcp.js b/modules/luci-base/htdocs/luci-static/resources/protocol/dhcp.js index bacbf559f9..9a63a107cf 100644 --- a/modules/luci-base/htdocs/luci-static/resources/protocol/dhcp.js +++ b/modules/luci-base/htdocs/luci-static/resources/protocol/dhcp.js @@ -34,21 +34,6 @@ return network.registerProtocol('dhcp', { o = s.taboption('advanced', form.Flag, 'broadcast', _('Use broadcast flag'), _('Required for certain ISPs, e.g. Charter with DOCSIS 3')); o.default = o.disabled; - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Use default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Flag, 'peerdns', _('Use DNS servers advertised by peer'), _('If unchecked, the advertised DNS server addresses are ignored')); - o.default = o.enabled; - - o = s.taboption('advanced', form.DynamicList, 'dns', _('Use custom DNS servers')); - o.depends('peerdns', '0'); - o.datatype = 'ipaddr'; - o.cast = 'string'; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o = s.taboption('advanced', form.Value, 'clientid', _('Client ID to send when requesting DHCP')); o.datatype = 'hexstring'; diff --git a/modules/luci-base/htdocs/luci-static/resources/protocol/static.js b/modules/luci-base/htdocs/luci-static/resources/protocol/static.js index 2d70ae681f..82f499d6b9 100644 --- a/modules/luci-base/htdocs/luci-static/resources/protocol/static.js +++ b/modules/luci-base/htdocs/luci-static/resources/protocol/static.js @@ -179,28 +179,6 @@ return network.registerProtocol('static', { s.taboption('general', this.CBINetmaskValue, 'netmask', _('IPv4 netmask')); s.taboption('general', this.CBIGatewayValue, 'gateway', _('IPv4 gateway')); s.taboption('general', this.CBIBroadcastValue, 'broadcast', _('IPv4 broadcast')); - s.taboption('general', form.DynamicList, 'dns', _('Use custom DNS servers')); - - o = s.taboption('general', 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(64)'; - - o = s.taboption('general', 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 = s.taboption('general', form.DynamicList, 'ip6addr', _('IPv6 address')); o.datatype = 'ip6addr'; @@ -215,10 +193,6 @@ return network.registerProtocol('static', { o.datatype = 'ip6addr'; o.depends('ip6assign', ''); - o = s.taboption('general', 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 = s.taboption('advanced', form.Value, 'macaddr', _('Override MAC address')); o.datatype = 'macaddr'; o.placeholder = dev ? (dev.getMAC() || '') : ''; @@ -226,9 +200,5 @@ return network.registerProtocol('static', { o = s.taboption('advanced', form.Value, 'mtu', _('Override MTU')); o.datatype = 'max(9200)'; o.placeholder = dev ? (dev.getMTU() || '1500') : '1500'; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = this.getMetric() || '0'; - o.datatype = 'uinteger'; } }); diff --git a/modules/luci-mod-network/htdocs/luci-static/resources/tools/network.js b/modules/luci-mod-network/htdocs/luci-static/resources/tools/network.js new file mode 100644 index 0000000000..d5eea4c826 --- /dev/null +++ b/modules/luci-mod-network/htdocs/luci-static/resources/tools/network.js @@ -0,0 +1,984 @@ +'use strict'; +'require ui'; +'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 renderDevBadge(dev) { + var type = dev.getType(), up = dev.isUp(); + + return E('span', { 'class': 'ifacebadge', 'style': 'font-weight:normal' }, [ + E('img', { + 'class': 'middle', + 'src': L.resource('icons/%s%s.png').format(type, up ? '' : '_disabled') + }), + ' ', dev.getName() + ]); +} + +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)); + } +} + + +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' }, [ _('Do not participate', 'VLAN port state') ]) + ]), + 'u': E([], [ + 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' }, [ _('Egress tagged', 'VLAN port state') ]) + ]), + '*': E([], [ + E('span', { 'class': 'hide-open', 'style': 'font-family:monospace' }, [ '*' ]), + E('span', { 'class': 'hide-close' }, [ _('Primary VLAN ID', 'VLAN port state') ]) + ]) + }, { + id: this.cbid(section_id), + sort: [ '-', 'u', 't', '*' ], + optional: false, + multiple: true + }); + + var field = this; + + widget.toggleItem = function(sb, li, force_state) { + var lis = li.parentNode.querySelectorAll('li'), + toggle = ui.Dropdown.prototype.toggleItem; + + toggle.apply(this, [sb, li, force_state]); + + if (force_state != null) + return; + + switch (li.getAttribute('data-value')) + { + case '-': + if (li.hasAttribute('selected')) { + for (var i = 0; i < lis.length; i++) { + switch (lis[i].getAttribute('data-value')) { + case '-': + break; + + case '*': + toggle.apply(this, [sb, lis[i], false]); + lis[i].setAttribute('unselectable', ''); + break; + + default: + toggle.apply(this, [sb, lis[i], false]); + } + } + } + break; + + case 't': + case 'u': + if (li.hasAttribute('selected')) { + for (var i = 0; i < lis.length; i++) { + switch (lis[i].getAttribute('data-value')) { + case li.getAttribute('data-value'): + break; + + case '*': + lis[i].removeAttribute('unselectable'); + break; + + default: + toggle.apply(this, [sb, lis[i], false]); + } + } + } + else { + toggle.apply(this, [sb, li, true]); + } + break; + + case '*': + if (li.hasAttribute('selected')) { + var section_ids = field.section.cfgsections(); + + for (var i = 0; i < section_ids.length; i++) { + var other_widget = field.getUIElement(section_ids[i]), + other_value = L.toArray(other_widget.getValue()); + + if (other_widget === this) + continue; + + var new_value = other_value.filter(function(v) { return v != '*' }); + + if (new_value.length == other_value.length) + continue; + + other_widget.setValue(new_value); + break; + } + } + } + }; + + var node = widget.render(); + + node.style.minWidth = '4em'; + + if (cfgvalue == '-') + node.querySelector('li[data-value="*"]').setAttribute('unselectable', ''); + + return node; + }, + + cfgvalue: function(section_id) { + var pname = this.port, + spec = L.toArray(uci.get('network', section_id, 'ports')).filter(function(p) { return p.replace(/:[ut*]+$/, '') == pname })[0]; + + if (spec && spec.match(/t/)) + return spec.match(/\*/) ? ['t', '*'] : ['t']; + else if (spec) + return spec.match(/\*/) ? ['u', '*'] : ['u']; + else + return ['-']; + }, + + write: function(section_id, value) { + var ports = []; + + for (var i = 0; i < this.section.children.length; i++) { + var opt = this.section.children[i]; + + if (opt.port) { + var val = L.toArray(opt.formvalue(section_id)).join(''); + + switch (val) { + case '-': + break; + + case 'u': + ports.push(opt.port); + break; + + default: + ports.push('%s:%s'.format(opt.port, val)); + break; + } + } + } + + uci.set('network', section_id, 'ports', ports); + }, + + remove: function() {} +}); + +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)); + } + + o = this.addOption(s, 'bridgevlan', form.Flag, 'vlan_filtering', _('Enable VLAN filterering')); + o.depends('type', 'bridge'); + o.updateDefaultValue = function(section_id) { + var device = isIface ? 'br-%s'.format(s.section) : uci.get('network', s.section, 'name'), + uielem = this.getUIElement(section_id), + has_vlans = false; + + uci.sections('network', 'bridge-vlan', function(bvs) { + has_vlans = has_vlans || (bvs.device == device); + }); + + this.default = has_vlans ? this.enabled : this.disabled; + + if (uielem && !uielem.isChanged()) + uielem.setValue(this.default); + }; + + o = this.addOption(s, 'bridgevlan', form.SectionValue, 'bridge-vlan', form.TableSection, 'bridge-vlan'); + o.depends('type', 'bridge'); + o.renderWidget = function(/* ... */) { + return form.SectionValue.prototype.renderWidget.apply(this, arguments).then(L.bind(function(node) { + node.style.overflowX = 'auto'; + node.style.overflowY = 'visible'; + node.style.paddingBottom = '100px'; + node.style.marginBottom = '-100px'; + + return node; + }, this)); + }; + + ss = o.subsection; + ss.addremove = true; + ss.anonymous = true; + + ss.renderHeaderRows = function(/* ... */) { + var node = form.TableSection.prototype.renderHeaderRows.apply(this, arguments); + + node.querySelectorAll('.th').forEach(function(th) { + th.classList.add('middle'); + }); + + return node; + }; + + ss.filter = function(section_id) { + var devname = isIface ? 'br-%s'.format(s.section) : uci.get('network', s.section, 'name'); + return (uci.get('network', section_id, 'device') == devname); + }; + + ss.render = function(/* ... */) { + return form.TableSection.prototype.render.apply(this, arguments).then(L.bind(function(node) { + if (this.node) + this.node.parentNode.replaceChild(node, this.node); + + this.node = node; + + return node; + }, this)); + }; + + ss.redraw = function() { + return this.load().then(L.bind(this.render, this)); + }; + + ss.updatePorts = function(ports) { + var devices = ports.map(function(port) { + return network.instantiateDevice(port) + }).filter(function(dev) { + return dev.getType() != 'wifi' || dev.isUp(); + }); + + this.children = this.children.filter(function(opt) { return !opt.option.match(/^port_/) }); + + for (var i = 0; i < devices.length; i++) { + o = ss.option(cbiTagValue, 'port_%s'.format(sfh(devices[i].getName())), renderDevBadge(devices[i])); + o.port = devices[i].getName(); + } + + var section_ids = this.cfgsections(), + device_names = devices.reduce(function(names, dev) { names[dev.getName()] = true; return names }, {}); + + for (var i = 0; i < section_ids.length; i++) { + var old_spec = L.toArray(uci.get('network', section_ids[i], 'ports')), + new_spec = old_spec.filter(function(spec) { return device_names[spec.replace(/:[ut*]+$/, '')] }); + + if (old_spec.length != new_spec.length) + uci.set('network', section_ids[i], 'ports', new_spec.length ? new_spec : null); + } + }; + + ss.handleAdd = function(ev) { + return s.parse().then(L.bind(function() { + var device = isIface ? 'br-%s'.format(s.section) : uci.get('network', s.section, 'name'), + section_ids = this.cfgsections(), + section_id = null, + max_vlan_id = 0; + + if (!device) + return; + + for (var i = 0; i < section_ids.length; i++) { + var vid = +uci.get('network', section_ids[i], 'vlan'); + + if (vid > max_vlan_id) + max_vlan_id = vid; + } + + section_id = uci.add('network', 'bridge-vlan'); + uci.set('network', section_id, 'device', device); + uci.set('network', section_id, 'vlan', max_vlan_id + 1); + + s.children.forEach(function(opt) { + switch (opt.option) { + case 'type': + case 'name_complex': + var input = opt.map.findElement('id', 'widget.%s'.format(opt.cbid(s.section))); + if (input) + input.disabled = true; + break; + } + }); + + s.getOption('vlan_filtering').updateDefaultValue(s.section); + + return this.redraw(); + }, this)); + }; + + o = ss.option(form.Value, 'vlan', _('VLAN ID')); + o.datatype = 'range(1, 4094)'; + + o.renderWidget = function(/* ... */) { + var node = form.Value.prototype.renderWidget.apply(this, arguments); + + node.style.width = '5em'; + + return node; + }; + + o.validate = function(section_id, value) { + var section_ids = this.section.cfgsections(); + + for (var i = 0; i < section_ids.length; i++) { + if (section_ids[i] == section_id) + continue; + + if (uci.get('network', section_ids[i], 'vlan') == value) + return _('The VLAN ID must be unique'); + } + + return true; + }; + + o = ss.option(form.Flag, 'local', _('Local')); + o.default = o.enabled; + + var ports = isIface + ? (ifc.getDevices() || L.toArray(ifc.getDevice())).map(function(dev) { return dev.getName() }) + : L.toArray(uci.get('network', s.section, 'ifname')); + + ss.updatePorts(ports); + } +}); diff --git a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/interfaces.js b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/interfaces.js index 80b02c96fb..0829a92fdd 100644 --- a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/interfaces.js +++ b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/interfaces.js @@ -9,6 +9,7 @@ 'require network'; 'require firewall'; 'require tools.widgets as widgets'; +'require tools.network as nettools'; var isReadonlyView = !L.hasViewPermission() || null; @@ -212,21 +213,19 @@ function iface_updown(up, id, ev, force) { function get_netmask(s, use_cfgvalue) { var readfn = use_cfgvalue ? 'cfgvalue' : 'formvalue', - addropt = s.children.filter(function(o) { return o.option == 'ipaddr'})[0], - addrvals = addropt ? L.toArray(addropt[readfn](s.section)) : [], - maskopt = s.children.filter(function(o) { return o.option == 'netmask'})[0], - maskval = maskopt ? maskopt[readfn](s.section) : null, - firstsubnet = maskval ? addrvals[0] + '/' + maskval : addrvals.filter(function(a) { return a.indexOf('/') > 0 })[0]; + addrs = L.toArray(s[readfn](s.section, 'ipaddr')), + mask = s[readfn](s.section, 'netmask'), + firstsubnet = mask ? addrs[0] + '/' + mask : addrs.filter(function(a) { return a.indexOf('/') > 0 })[0]; if (firstsubnet == null) return null; - var mask = firstsubnet.split('/')[1]; + var subnetmask = firstsubnet.split('/')[1]; - if (!isNaN(mask)) - mask = network.prefixToMask(+mask); + if (!isNaN(subnetmask)) + subnetmask = network.prefixToMask(+subnetmask); - return mask; + return subnetmask; } return view.extend({ @@ -293,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'); @@ -323,6 +332,8 @@ 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('bridgevlan', _('Bridge VLAN filtering')); s.tab('firewall', _('Firewall Settings')); s.tab('dhcp', _('DHCP Server')); @@ -413,80 +424,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(); @@ -530,14 +467,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')) { @@ -610,9 +539,9 @@ return view.extend({ }; so.validate = function(section_id, value) { - var node = this.map.findElement('id', this.cbid(section_id)); - if (node) - node.querySelector('input').setAttribute('placeholder', get_netmask(s, false)); + var uielem = this.getUIElement(section_id); + if (uielem) + uielem.setPlaceholder(get_netmask(s, false)); return form.Value.prototype.validate.apply(this, [ section_id, value ]); }; @@ -660,25 +589,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++) @@ -687,9 +718,23 @@ return view.extend({ o.depends('proto', protoval); } } + + this.activeSection = s.section; }, this)); }; + s.handleModalCancel = function(/* ... */) { + var type = uci.get('network', this.activeSection || this.addedSection, 'type'), + ifname = (type == 'bridge') ? 'br-%s'.format(this.activeSection || this.addedSection) : null; + + uci.sections('network', 'bridge-vlan', function(bvs) { + if (ifname != null && bvs.device == ifname) + uci.remove('network', bvs['.name']); + }); + + return form.GridSection.prototype.handleModalCancel.apply(this, arguments); + }; + s.handleAdd = function(ev) { var m2 = new form.Map('network'), s2 = m2.section(form.NamedSection, '_new_'), @@ -793,8 +838,9 @@ return view.extend({ protoclass.addDevice(dev); }); } - }).then(L.bind(m.children[0].renderMoreOptionsModal, m.children[0], nameval)); + m.children[0].addedSection = section_id; + }).then(L.bind(m.children[0].renderMoreOptionsModal, m.children[0], nameval)); }); }) }, _('Create interface')) @@ -865,14 +911,208 @@ return view.extend({ o = s.taboption('advanced', form.Flag, 'force_link', _('Force link'), _('Set interface properties regardless of the link carrier (If set, carrier sense events do not invoke hotplug handlers).')); o.modalonly = true; - o.render = function(option_index, section_id, in_table) { - var protoopt = this.section.children.filter(function(o) { return o.option == 'proto' })[0], - protoval = protoopt ? protoopt.cfgvalue(section_id) : null; + o.defaults = { + '1': [{ proto: 'static' }], + '0': [] + }; - this.default = (protoval == 'static') ? this.enabled : this.disabled; - return this.super('render', [ option_index, section_id, in_table ]); + + // 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); + }; + + s.handleModalCancel = function(/* ... */) { + var name = uci.get('network', this.addedSection, 'name') + + uci.sections('network', 'bridge-vlan', function(bvs) { + if (name != null && bvs.device == name) + uci.remove('network', bvs['.name']); + }); + + return form.GridSection.prototype.handleModalCancel.apply(this, arguments); + }; + + 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; diff --git a/modules/luci-mod-network/root/usr/share/rpcd/acl.d/luci-mod-network.json b/modules/luci-mod-network/root/usr/share/rpcd/acl.d/luci-mod-network.json index d6c84bab27..5d7888f765 100644 --- a/modules/luci-mod-network/root/usr/share/rpcd/acl.d/luci-mod-network.json +++ b/modules/luci-mod-network/root/usr/share/rpcd/acl.d/luci-mod-network.json @@ -4,6 +4,7 @@ "read": { "cgi-io": [ "exec" ], "file": { + "/etc/iproute2/rt_tables": [ "read" ], "/usr/libexec/luci-peeraddr": [ "exec" ] }, "ubus": { diff --git a/protocols/luci-proto-3g/htdocs/luci-static/resources/protocol/3g.js b/protocols/luci-proto-3g/htdocs/luci-static/resources/protocol/3g.js index 07bed36c23..12acb74d9f 100644 --- a/protocols/luci-proto-3g/htdocs/luci-static/resources/protocol/3g.js +++ b/protocols/luci-proto-3g/htdocs/luci-static/resources/protocol/3g.js @@ -113,21 +113,6 @@ return network.registerProtocol('3g', { o.placeholder = '10'; o.datatype = 'min(1)'; - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Use default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o.depends('defaultroute', '1'); - - o = s.taboption('advanced', form.Flag, 'peerdns', _('Use DNS servers advertised by peer'), _('If unchecked, the advertised DNS server addresses are ignored')); - o.default = o.enabled; - - o = s.taboption('advanced', form.DynamicList, 'dns', _('Use custom DNS servers')); - o.depends('peerdns', '0'); - o.datatype = 'ipaddr'; - o = s.taboption('advanced', form.Value, '_keepalive_failure', _('LCP echo failure threshold'), _('Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures')); o.placeholder = '0'; o.datatype = 'uinteger'; diff --git a/protocols/luci-proto-hnet/htdocs/luci-static/resources/protocol/hnet.js b/protocols/luci-proto-hnet/htdocs/luci-static/resources/protocol/hnet.js index 84396ede08..b8776e873f 100644 --- a/protocols/luci-proto-hnet/htdocs/luci-static/resources/protocol/hnet.js +++ b/protocols/luci-proto-hnet/htdocs/luci-static/resources/protocol/hnet.js @@ -24,10 +24,6 @@ return network.registerProtocol('hnet', { o.value('hybrid', _('Hybrid')); o.default = 'auto'; - o = s.taboption('advanced', form.Value, 'ip6assign', _('IPv6 assignment length'), _('Assign a part of given length of every public IPv6-prefix to this interface')); - o.datatype = 'max(128)'; - o.default = '64'; - s.taboption('advanced', form.Value, 'link_id', _('IPv6 assignment hint'), _('Assign prefix parts using this hexadecimal subprefix ID for this interface.')); o = s.taboption('advanced', form.Value, 'ip4assign', _('IPv4 assignment length')); diff --git a/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/464xlat.js b/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/464xlat.js index a74708fd01..9cfdadd9a6 100644 --- a/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/464xlat.js +++ b/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/464xlat.js @@ -45,14 +45,6 @@ return network.registerProtocol('464xlat', { o.nocreate = true; o.exclude = s.section; - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o.depends('defaultroute', '1'); - o = s.taboption('advanced', form.Value, 'mtu', _('Use MTU on tunnel interface')); o.placeholder = '1280'; o.datatype = 'max(9200)'; diff --git a/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/6in4.js b/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/6in4.js index f26ced7b40..92540570ef 100644 --- a/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/6in4.js +++ b/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/6in4.js @@ -82,14 +82,6 @@ return network.registerProtocol('6in4', { o.password = true; o.depends('_update', '1'); - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o.depends('defaultroute', '1'); - o = s.taboption('advanced', form.Value, 'ttl', _('Use TTL on tunnel interface')); o.placeholder = '64'; o.datatype = 'range(1,255)'; diff --git a/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/6rd.js b/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/6rd.js index 6a8f506bc9..415c3a89e7 100644 --- a/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/6rd.js +++ b/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/6rd.js @@ -62,14 +62,6 @@ return network.registerProtocol('6rd', { o.placeholder = '0'; o.datatype = 'range(0,32)'; - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o.depends('defaultroute', '1'); - o = s.taboption('advanced', form.Value, 'ttl', _('Use TTL on tunnel interface')); o.placeholder = '64'; o.datatype = 'range(1,255)'; diff --git a/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/6to4.js b/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/6to4.js index abfe1c8fc4..f572562332 100644 --- a/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/6to4.js +++ b/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/6to4.js @@ -46,14 +46,6 @@ return network.registerProtocol('6to4', { }, this)); }; - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o.depends('defaultroute', '1'); - o = s.taboption('advanced', form.Value, 'ttl', _('Use TTL on tunnel interface')); o.placeholder = '64'; o.datatype = 'range(1,255)'; diff --git a/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/dhcpv6.js b/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/dhcpv6.js index eba58f4248..41fdc6d9d5 100644 --- a/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/dhcpv6.js +++ b/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/dhcpv6.js @@ -30,20 +30,6 @@ return network.registerProtocol('dhcpv6', { o.value('64'); o.default = 'auto'; - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Use default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.DynamicList, 'ip6prefix', _('Custom delegated IPv6-prefix')); - o.datatype = 'cidr6'; - - o = s.taboption('advanced', form.Flag, 'peerdns', _('Use DNS servers advertised by peer'), _('If unchecked, the advertised DNS server addresses are ignored')); - o.default = o.enabled; - - o = s.taboption('advanced', form.DynamicList, 'dns', _('Use custom DNS servers')); - o.depends('peerdns', '0'); - o.datatype = 'ipaddr'; - o.cast = 'string'; - o = s.taboption('advanced', form.Value, 'clientid', _('Client ID to send when requesting DHCP')); o.datatype = 'hexstring'; diff --git a/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/dslite.js b/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/dslite.js index fa0e68ddc3..5f1fbf4f7a 100644 --- a/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/dslite.js +++ b/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/dslite.js @@ -64,14 +64,6 @@ return network.registerProtocol('dslite', { for (var i = 0; i < 256; i++) o.value(i); - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o.depends('defaultroute', '1'); - o = s.taboption('advanced', form.Value, 'mtu', _('Use MTU on tunnel interface')); o.placeholder = '1280'; o.datatype = 'max(9200)'; diff --git a/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/map.js b/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/map.js index 5c292af619..82e0169210 100644 --- a/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/map.js +++ b/protocols/luci-proto-ipv6/htdocs/luci-static/resources/protocol/map.js @@ -77,14 +77,6 @@ return network.registerProtocol('map', { o.nocreate = true; o.exclude = s.section; - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o.depends('defaultroute', '1'); - o = s.taboption('advanced', form.Value, 'ttl', _('Use TTL on tunnel interface')); o.placeholder = '64'; o.datatype = 'range(1,255)'; diff --git a/protocols/luci-proto-modemmanager/htdocs/luci-static/resources/protocol/modemmanager.js b/protocols/luci-proto-modemmanager/htdocs/luci-static/resources/protocol/modemmanager.js index 4a6913e8bd..1851cbe1ca 100644 --- a/protocols/luci-proto-modemmanager/htdocs/luci-static/resources/protocol/modemmanager.js +++ b/protocols/luci-proto-modemmanager/htdocs/luci-static/resources/protocol/modemmanager.js @@ -120,11 +120,8 @@ return network.registerProtocol('modemmanager', { o = s.taboption('advanced', form.Value, 'mtu', _('Override MTU')); o.placeholder = dev ? (dev.getMTU() || '1500') : '1500'; o.datatype = 'max(9200)'; - + o = s.taboption('general', form.Value, 'signalrate', _('Signal Refresh Rate'), _("In seconds")); o.datatype = 'uinteger'; - - s.taboption('general', form.Value, 'metric', _('Gateway metric')); - } }); diff --git a/protocols/luci-proto-ncm/htdocs/luci-static/resources/protocol/ncm.js b/protocols/luci-proto-ncm/htdocs/luci-static/resources/protocol/ncm.js index 3ab6c01d61..8496b7de3c 100644 --- a/protocols/luci-proto-ncm/htdocs/luci-static/resources/protocol/ncm.js +++ b/protocols/luci-proto-ncm/htdocs/luci-static/resources/protocol/ncm.js @@ -104,20 +104,5 @@ return network.registerProtocol('ncm', { o = s.taboption('advanced', form.Value, 'delay', _('Modem init timeout'), _('Maximum amount of seconds to wait for the modem to become ready')); o.placeholder = '10'; o.datatype = 'min(1)'; - - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o.depends('defaultroute', '1'); - - o = s.taboption('advanced', form.Flag, 'peerdns', _('Use DNS servers advertised by peer'), _('If unchecked, the advertised DNS server addresses are ignored')); - o.default = o.enabled; - - o = s.taboption('advanced', form.DynamicList, 'dns', _('Use custom DNS servers')); - o.depends('peerdns', '0'); - o.datatype = 'ipaddr'; } }); diff --git a/protocols/luci-proto-openconnect/htdocs/luci-static/resources/protocol/openconnect.js b/protocols/luci-proto-openconnect/htdocs/luci-static/resources/protocol/openconnect.js index 388812d893..2c29614aee 100644 --- a/protocols/luci-proto-openconnect/htdocs/luci-static/resources/protocol/openconnect.js +++ b/protocols/luci-proto-openconnect/htdocs/luci-static/resources/protocol/openconnect.js @@ -153,14 +153,6 @@ return network.registerProtocol('openconnect', { return callSetCertificateFiles(section_id, null, null, sanitizeCert(value)); }; - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o.depends('defaultroute', '1'); - o = s.taboption('advanced', form.Value, 'mtu', _('Override MTU')); o.optional = true; o.placeholder = 1406; diff --git a/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/l2tp.js b/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/l2tp.js index 13bb3548a7..4ea0e2f7d0 100644 --- a/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/l2tp.js +++ b/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/l2tp.js @@ -53,21 +53,6 @@ return network.registerProtocol('l2tp', { o.default = 'auto'; } - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Use default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Flag, 'peerdns', _('Use DNS servers advertised by peer'), _('If unchecked, the advertised DNS server addresses are ignored')); - o.default = o.enabled; - - o = s.taboption('advanced', form.DynamicList, 'dns', _('Use custom DNS servers')); - o.depends('peerdns', '0'); - o.datatype = 'ipaddr'; - o.cast = 'string'; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o = s.taboption('advanced', form.Value, 'mtu', _('Override MTU')); o.placeholder = dev ? (dev.getMTU() || '1500') : '1500'; o.datatype = 'max(9200)'; diff --git a/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/ppp.js b/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/ppp.js index 57a7b6a0e1..f5a484f357 100644 --- a/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/ppp.js +++ b/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/ppp.js @@ -97,21 +97,6 @@ return network.registerProtocol('ppp', { o.default = 'auto'; } - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Use default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Flag, 'peerdns', _('Use DNS servers advertised by peer'), _('If unchecked, the advertised DNS server addresses are ignored')); - o.default = o.enabled; - - o = s.taboption('advanced', form.DynamicList, 'dns', _('Use custom DNS servers')); - o.depends('peerdns', '0'); - o.datatype = 'ipaddr'; - o.cast = 'string'; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o = s.taboption('advanced', form.Value, '_keepalive_failure', _('LCP echo failure threshold'), _('Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures')); o.placeholder = '0'; o.datatype = 'uinteger'; diff --git a/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/pppoa.js b/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/pppoa.js index 483ac9e555..495a581a6c 100644 --- a/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/pppoa.js +++ b/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/pppoa.js @@ -84,21 +84,6 @@ return network.registerProtocol('pppoa', { o.default = 'auto'; } - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Use default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Flag, 'peerdns', _('Use DNS servers advertised by peer'), _('If unchecked, the advertised DNS server addresses are ignored')); - o.default = o.enabled; - - o = s.taboption('advanced', form.DynamicList, 'dns', _('Use custom DNS servers')); - o.depends('peerdns', '0'); - o.datatype = 'ipaddr'; - o.cast = 'string'; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o = s.taboption('advanced', form.Value, '_keepalive_failure', _('LCP echo failure threshold'), _('Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures')); o.placeholder = '0'; o.datatype = 'uinteger'; diff --git a/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/pppoe.js b/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/pppoe.js index 5d71c43376..9ec2a81d3e 100644 --- a/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/pppoe.js +++ b/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/pppoe.js @@ -58,21 +58,6 @@ return network.registerProtocol('pppoe', { o.default = 'auto'; } - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Use default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Flag, 'peerdns', _('Use DNS servers advertised by peer'), _('If unchecked, the advertised DNS server addresses are ignored')); - o.default = o.enabled; - - o = s.taboption('advanced', form.DynamicList, 'dns', _('Use custom DNS servers')); - o.depends('peerdns', '0'); - o.datatype = 'ipaddr'; - o.cast = 'string'; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o = s.taboption('advanced', form.Value, '_keepalive_failure', _('LCP echo failure threshold'), _('Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures')); o.placeholder = '0'; o.datatype = 'uinteger'; diff --git a/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/pptp.js b/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/pptp.js index 006adc1a1e..871fb34b4c 100644 --- a/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/pptp.js +++ b/protocols/luci-proto-ppp/htdocs/luci-static/resources/protocol/pptp.js @@ -71,21 +71,6 @@ return network.registerProtocol('pptp', { o.default = 'auto'; } - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Use default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Flag, 'peerdns', _('Use DNS servers advertised by peer'), _('If unchecked, the advertised DNS server addresses are ignored')); - o.default = o.enabled; - - o = s.taboption('advanced', form.DynamicList, 'dns', _('Use custom DNS servers')); - o.depends('peerdns', '0'); - o.datatype = 'ipaddr'; - o.cast = 'string'; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o = s.taboption('advanced', form.Value, '_keepalive_failure', _('LCP echo failure threshold'), _('Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures')); o.placeholder = '0'; o.datatype = 'uinteger'; diff --git a/protocols/luci-proto-pppossh/htdocs/luci-static/resources/protocol/pppossh.js b/protocols/luci-proto-pppossh/htdocs/luci-static/resources/protocol/pppossh.js index ec8e8a152e..3c233f1e1b 100644 --- a/protocols/luci-proto-pppossh/htdocs/luci-static/resources/protocol/pppossh.js +++ b/protocols/luci-proto-pppossh/htdocs/luci-static/resources/protocol/pppossh.js @@ -94,21 +94,6 @@ return network.registerProtocol('pppossh', { o.default = o.disabled; } - o = s.taboption('advanced', form.Flag, 'defaultroute', _('Use default gateway'), _('If unchecked, no default route is configured')); - o.default = o.enabled; - - o = s.taboption('advanced', form.Flag, 'peerdns', _('Use DNS servers advertised by peer'), _('If unchecked, the advertised DNS server addresses are ignored')); - o.default = o.enabled; - - o = s.taboption('advanced', form.DynamicList, 'dns', _('Use custom DNS servers')); - o.depends('peerdns', '0'); - o.datatype = 'ipaddr'; - o.cast = 'string'; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o = s.taboption('advanced', form.Value, '_keepalive_failure', _('LCP echo failure threshold'), _('Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures')); o.placeholder = '0'; o.datatype = 'uinteger'; diff --git a/protocols/luci-proto-sstp/htdocs/luci-static/resources/protocol/sstp.js b/protocols/luci-proto-sstp/htdocs/luci-static/resources/protocol/sstp.js index 55ae2f97e6..b568f17018 100644 --- a/protocols/luci-proto-sstp/htdocs/luci-static/resources/protocol/sstp.js +++ b/protocols/luci-proto-sstp/htdocs/luci-static/resources/protocol/sstp.js @@ -58,22 +58,6 @@ return network.registerProtocol('sstp', { o.value('4', _('4', 'sstp log level value')); o.default = '0'; - var defaultroute = s.taboption('advanced', form.Flag, 'defaultroute', _('Use default gateway'), _('If unchecked, no default route is configured')); - defaultroute.default = defaultroute.enabled; - - o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric')); - o.placeholder = '0'; - o.datatype = 'uinteger'; - o.depends('defaultroute', defaultroute.enabled); - - o = s.taboption('advanced', form.Flag, 'peerdns', _('Use DNS servers advertised by peer'), _('If unchecked, the advertised DNS server addresses are ignored')); - o.default = o.enabled; - - o = s.taboption('advanced', form.DynamicList, 'dns', _('Use custom DNS servers')); - o.depends('peerdns', '0'); - o.datatype = 'ipaddr'; - o.cast = 'string'; - o = s.taboption('advanced', form.Value, 'mtu', _('Override MTU')); o.placeholder = dev ? (dev.getMTU() || '1500') : '1500'; o.datatype = 'max(9200)'; diff --git a/protocols/luci-proto-vpnc/htdocs/luci-static/resources/protocol/vpnc.js b/protocols/luci-proto-vpnc/htdocs/luci-static/resources/protocol/vpnc.js index 87ccc9df1c..bec6c7ffa2 100644 --- a/protocols/luci-proto-vpnc/htdocs/luci-static/resources/protocol/vpnc.js +++ b/protocols/luci-proto-vpnc/htdocs/luci-static/resources/protocol/vpnc.js @@ -103,9 +103,5 @@ return network.registerProtocol('vpnc', { o = s.taboption('general', form.Value, 'target_network', _('Target network')); o.placeholder = '0.0.0.0/0'; o.datatype = 'network'; - - o = s.taboption('general', form.ListValue, 'defaultroute', _('Default Route'), _('Set VPN as Default Route')); - o.value('0', _('No')); - o.value('1', _('Yes')); } }); diff --git a/protocols/luci-proto-wireguard/htdocs/luci-static/resources/protocol/wireguard.js b/protocols/luci-proto-wireguard/htdocs/luci-static/resources/protocol/wireguard.js index 4690ecfc7f..dd933c9544 100644 --- a/protocols/luci-proto-wireguard/htdocs/luci-static/resources/protocol/wireguard.js +++ b/protocols/luci-proto-wireguard/htdocs/luci-static/resources/protocol/wireguard.js @@ -89,11 +89,6 @@ return network.registerProtocol('wireguard', { // -- advanced -------------------------------------------------------------------- - o = s.taboption('advanced', form.Value, 'metric', _('Metric'), _('Optional')); - o.datatype = 'uinteger'; - o.placeholder = '0'; - o.optional = true; - o = s.taboption('advanced', form.Value, 'mtu', _('MTU'), _('Optional. Maximum Transmission Unit of tunnel interface.')); o.datatype = 'range(1280,1420)'; o.placeholder = '1420'; |