summaryrefslogtreecommitdiffhomepage
path: root/modules
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2019-08-20 15:31:35 +0200
committerJo-Philipp Wich <jo@mein.io>2019-09-10 15:28:16 +0200
commit6a2a53a82918ea2ccbbbe23510aa0279827b2783 (patch)
tree7f065f96cc0f255ced7256c1d9092f0e1433fda0 /modules
parent0674fc20414e575c346ceb2066ff3af7e8601a48 (diff)
protocols: add client side protocol handler implementations
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'modules')
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/protocol/dhcp.js60
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/protocol/none.js8
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/protocol/static.js222
3 files changed, 290 insertions, 0 deletions
diff --git a/modules/luci-base/htdocs/luci-static/resources/protocol/dhcp.js b/modules/luci-base/htdocs/luci-static/resources/protocol/dhcp.js
new file mode 100644
index 000000000..b0b65974a
--- /dev/null
+++ b/modules/luci-base/htdocs/luci-static/resources/protocol/dhcp.js
@@ -0,0 +1,60 @@
+'use strict';
+'require rpc';
+'require form';
+'require network';
+
+var callHostname = rpc.declare({
+ object: 'luci',
+ method: 'getHostname',
+ expect: { result: '' }
+});
+
+return network.registerProtocol('dhcp', {
+ getI18n: function() {
+ return _('DHCP client');
+ },
+
+ renderFormOptions: function(s) {
+ var dev = this.getL2Device() || this.getDevice(), o;
+
+ o = s.taboption('general', form.Value, 'hostname', _('Hostname to send when requesting DHCP'));
+ o.datatype = 'hostname';
+ o.load = function(section_id) {
+ return callHostname().then(L.bind(function(hostname) {
+ this.placeholder = hostname;
+ return form.Value.prototype.load.apply(this, [section_id]);
+ }, this));
+ };
+
+ 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';
+
+ s.taboption('advanced', form.Value, 'vendorid', _('Vendor Class to send when requesting DHCP'));
+
+ o = s.taboption('advanced', form.Value, 'macaddr', _('Override MAC address'));
+ o.datatype = 'macaddr';
+ o.placeholder = dev ? (dev.getMAC() || '') : '';
+
+ o = s.taboption('advanced', form.Value, 'mtu', _('Override MTU'));
+ o.placeholder = dev ? (dev.getMTU() || '1500') : '1500';
+ o.datatype = 'max(9200)';
+ }
+});
diff --git a/modules/luci-base/htdocs/luci-static/resources/protocol/none.js b/modules/luci-base/htdocs/luci-static/resources/protocol/none.js
new file mode 100644
index 000000000..37674c0ea
--- /dev/null
+++ b/modules/luci-base/htdocs/luci-static/resources/protocol/none.js
@@ -0,0 +1,8 @@
+'use strict';
+'require network';
+
+return network.registerProtocol('none', {
+ getI18n: function() {
+ return _('Unmanaged');
+ }
+});
diff --git a/modules/luci-base/htdocs/luci-static/resources/protocol/static.js b/modules/luci-base/htdocs/luci-static/resources/protocol/static.js
new file mode 100644
index 000000000..f8a6d9486
--- /dev/null
+++ b/modules/luci-base/htdocs/luci-static/resources/protocol/static.js
@@ -0,0 +1,222 @@
+'use strict';
+'require form';
+'require network';
+'require validation';
+
+function isCIDR(value) {
+ return Array.isArray(value) || /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/(\d{1,2}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.test(value);
+}
+
+function calculateBroadcast(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];
+
+ if (firstsubnet == null)
+ return null;
+
+ var addr_mask = firstsubnet.split('/'),
+ addr = validation.parseIPv4(addr_mask[0]),
+ mask = addr_mask[1];
+
+ if (!isNaN(mask))
+ mask = validation.parseIPv4(network.prefixToMask(+mask));
+ else
+ mask = validation.parseIPv4(mask);
+
+ var bc = [
+ addr[0] | (~mask[0] >>> 0 & 255),
+ addr[1] | (~mask[1] >>> 0 & 255),
+ addr[2] | (~mask[2] >>> 0 & 255),
+ addr[3] | (~mask[3] >>> 0 & 255)
+ ];
+
+ return bc.join('.');
+}
+
+function validateBroadcast(section_id, value) {
+ var opt = this.map.lookupOption('broadcast', section_id),
+ node = opt ? this.map.findElement('id', opt[0].cbid(section_id)) : null,
+ addr = node ? calculateBroadcast(this.section, false) : null;
+
+ if (node != null) {
+ if (addr != null)
+ node.querySelector('input').setAttribute('placeholder', addr);
+ else
+ node.querySelector('input').removeAttribute('placeholder');
+ }
+
+ return true;
+}
+
+return network.registerProtocol('static', {
+ CBIIPValue: form.Value.extend({
+ handleSwitch: function(section_id, option_index, ev) {
+ var maskopt = this.map.lookupOption('netmask', section_id);
+
+ if (maskopt == null || !this.isValid(section_id))
+ return;
+
+ var maskval = maskopt[0].formvalue(section_id),
+ addrval = this.formvalue(section_id),
+ prefix = maskval ? network.maskToPrefix(maskval) : 32;
+
+ if (prefix == null)
+ return;
+
+ this.datatype = 'or(cidr4,ipmask4)';
+
+ var parent = L.dom.parent(ev.target, '.cbi-value-field');
+ L.dom.content(parent, form.DynamicList.prototype.renderWidget.apply(this, [
+ section_id,
+ option_index,
+ addrval ? '%s/%d'.format(addrval, prefix) : ''
+ ]));
+
+ var masknode = this.map.findElement('id', maskopt[0].cbid(section_id));
+ if (masknode) {
+ parent = L.dom.parent(masknode, '.cbi-value');
+ parent.parentNode.removeChild(parent);
+ }
+ },
+
+ renderWidget: function(section_id, option_index, cfgvalue) {
+ var maskopt = this.map.lookupOption('netmask', section_id),
+ widget = isCIDR(cfgvalue) ? 'DynamicList' : 'Value';
+
+ if (widget == 'DynamicList') {
+ this.datatype = 'or(cidr4,ipmask4)';
+ this.placeholder = _('Add IPv4 address…');
+ }
+ else {
+ this.datatype = 'ip4addr("nomask")';
+ }
+
+ var node = form[widget].prototype.renderWidget.apply(this, [ section_id, option_index, cfgvalue ]);
+
+ if (widget == 'Value')
+ L.dom.append(node, E('button', {
+ 'class': 'cbi-button cbi-button-neutral',
+ 'title': _('Switch to CIDR list notation'),
+ 'aria-label': _('Switch to CIDR list notation'),
+ 'click': L.bind(this.handleSwitch, this, section_id, option_index)
+ }, '…'));
+
+ return node;
+ },
+
+ validate: validateBroadcast
+ }),
+
+ CBINetmaskValue: form.Value.extend({
+ render: function(option_index, section_id, in_table) {
+ var addropt = this.section.children.filter(function(o) { return o.option == 'ipaddr' })[0],
+ addrval = addropt ? addropt.cfgvalue(section_id) : null;
+
+ if (addrval != null && isCIDR(addrval))
+ return E([]);
+
+ this.value('255.255.255.0');
+ this.value('255.255.0.0');
+ this.value('255.0.0.0');
+
+ return form.Value.prototype.render.apply(this, [ option_index, section_id, in_table ]);
+ },
+
+ validate: validateBroadcast
+ }),
+
+ CBIGatewayValue: form.Value.extend({
+ datatype: 'ip4addr("nomask")',
+
+ render: function(option_index, section_id, in_table) {
+ return network.getWANNetworks().then(L.bind(function(wans) {
+ if (wans.length == 1) {
+ var gwaddr = wans[0].getGatewayAddr();
+ this.placeholder = gwaddr ? '%s (%s)'.format(gwaddr, wans[0].getName()) : '';
+ }
+
+ return form.Value.prototype.render.apply(this, [ option_index, section_id, in_table ]);
+ }, this));
+ },
+
+ validate: function(section_id, value) {
+ var addropt = this.section.children.filter(function(o) { return o.option == 'ipaddr' })[0],
+ addrval = addropt ? L.toArray(addropt.cfgvalue(section_id)) : null;
+
+ if (addrval != null) {
+ for (var i = 0; i < addrval.length; i++) {
+ var addr = addrval[i].split('/')[0];
+ if (value == addr)
+ return _('The gateway address must not be a local IP address');
+ }
+ }
+
+ return true;
+ }
+ }),
+
+ CBIBroadcastValue: form.Value.extend({
+ datatype: 'ip4addr("nomask")',
+
+ render: function(option_index, section_id, in_table) {
+ this.placeholder = calculateBroadcast(this.section, true);
+ return form.Value.prototype.render.apply(this, [ option_index, section_id, in_table ]);
+ }
+ }),
+
+ getI18n: function() {
+ return _('Static address');
+ },
+
+ renderFormOptions: function(s) {
+ var dev = this.getL2Device() || this.getDevice(), o;
+
+ s.taboption('general', this.CBIIPValue, 'ipaddr', _('IPv4 address'));
+ 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.'));
+ for (var i = 33; i <= 64; i++)
+ o.depends('ip6assign', i);
+
+ o = s.taboption('general', form.DynamicList, 'ip6addr', _('IPv6 address'));
+ o.datatype = 'ip6addr';
+ o.placeholder = _('Add IPv6 address…');
+ o.depends('ip6assign', '');
+
+ o = s.taboption('general', form.Value, 'ip6gw', _('IPv6 gateway'));
+ o.datatype = 'ip6addr("nomask")';
+ o.depends('ip6assign', '');
+
+ o = s.taboption('general', form.Value, 'ip6prefix', _('IPv6 routed prefix'), _('Public prefix routed to this device for distribution to clients.'));
+ 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() || '') : '';
+
+ 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';
+ }
+});