summaryrefslogtreecommitdiffhomepage
path: root/protocols
diff options
context:
space:
mode:
Diffstat (limited to 'protocols')
-rw-r--r--protocols/luci-proto-external/Makefile (renamed from protocols/luci-proto-cni/Makefile)4
-rw-r--r--protocols/luci-proto-external/htdocs/luci-static/resources/protocol/external.js (renamed from protocols/luci-proto-cni/htdocs/luci-static/resources/protocol/cni.js)13
-rw-r--r--protocols/luci-proto-modemmanager/htdocs/luci-static/resources/protocol/modemmanager.js12
-rw-r--r--protocols/luci-proto-vxlan/htdocs/luci-static/resources/protocol/vxlan6.js9
-rw-r--r--protocols/luci-proto-yggdrasil/Makefile18
-rw-r--r--protocols/luci-proto-yggdrasil/htdocs/luci-static/resources/protocol/yggdrasil.js271
-rwxr-xr-xprotocols/luci-proto-yggdrasil/root/usr/libexec/rpcd/luci.yggdrasil36
-rw-r--r--protocols/luci-proto-yggdrasil/root/usr/share/rpcd/acl.d/luci-proto-yggdrasil.json10
8 files changed, 365 insertions, 8 deletions
diff --git a/protocols/luci-proto-cni/Makefile b/protocols/luci-proto-external/Makefile
index 6405fca0fd..b9b146d7bd 100644
--- a/protocols/luci-proto-cni/Makefile
+++ b/protocols/luci-proto-external/Makefile
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
-LUCI_TITLE:=Support for CNI protocol
-LUCI_DEPENDS:=+cni-protocol
+LUCI_TITLE:=Support for externally managed protocol
+LUCI_DEPENDS:=+external-protocol
include ../../luci.mk
diff --git a/protocols/luci-proto-cni/htdocs/luci-static/resources/protocol/cni.js b/protocols/luci-proto-external/htdocs/luci-static/resources/protocol/external.js
index 273067fd43..06bd5f3f66 100644
--- a/protocols/luci-proto-cni/htdocs/luci-static/resources/protocol/cni.js
+++ b/protocols/luci-proto-external/htdocs/luci-static/resources/protocol/external.js
@@ -2,13 +2,13 @@
'require form';
'require network';
-return network.registerProtocol('cni', {
+return network.registerProtocol('external', {
getI18n: function () {
- return _('CNI (Externally managed interface)');
+ return _('Externally managed interface');
},
getOpkgPackage: function() {
- return "cni-protocol";
+ return "external-protocol";
},
isFloating: function() {
@@ -31,12 +31,17 @@ return network.registerProtocol('cni', {
o.optional = false;
o.rmempty = false;
- o = s.taboption('general', form.Value, '_delay', _('Delay'), _('Afer making changes to network using CNI protocol, network must be manually restarted.'));
+ o = s.taboption('general', form.Value, '_delay', _('Delay'), _('Afer making changes to network using external protocol, network must be manually restarted.'));
o.ucioption = 'delay';
o.placeholder = '10';
o.datatype = 'min(1)';
o.optional = true;
o.rmempty = true;
+
+ o = s.taboption('general', form.Value, '_searchdomain', _('Search domain'));
+ o.ucioption = 'searchdomain'
+ o.optional = true;
+ o.rmempty = true;
}
});
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 e3d98d75ef..b13dd310c7 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
@@ -145,5 +145,17 @@ return network.registerProtocol('modemmanager', {
o = s.taboption('general', form.Value, 'signalrate', _('Signal Refresh Rate'), _("In seconds"));
o.datatype = 'uinteger';
+
+ s.taboption('general', form.Value, 'metric', _('Gateway metric'));
+
+ s.taboption('advanced', form.Flag, 'debugmode', _('Enable Debugmode'));
+
+ o = s.taboption('advanced', form.ListValue, 'loglevel', _('Log output level'));
+ o.value('ERR', _('Error'))
+ o.value('WARN', _('Warning'));
+ o.value('INFO', _('Info'));
+ o.value('DEBUG', _('Debug'));
+ o.default = 'ERR';
+
}
});
diff --git a/protocols/luci-proto-vxlan/htdocs/luci-static/resources/protocol/vxlan6.js b/protocols/luci-proto-vxlan/htdocs/luci-static/resources/protocol/vxlan6.js
index 30250cdcfa..04e702897b 100644
--- a/protocols/luci-proto-vxlan/htdocs/luci-static/resources/protocol/vxlan6.js
+++ b/protocols/luci-proto-vxlan/htdocs/luci-static/resources/protocol/vxlan6.js
@@ -39,11 +39,16 @@ return network.registerProtocol('vxlan6', {
o = s.taboption('general', form.Value, 'peer6addr', _('Remote IPv6 address'), _('The IPv6 address or the fully-qualified domain name of the remote end.'));
o.optional = false;
- o.datatype = 'or(hostname,cidr6)';
+ o.datatype = 'or(hostname,ip6addr("nomask"))';
o = s.taboption('general', form.Value, 'ip6addr', _('Local IPv6 address'), _('The local IPv6 address over which the tunnel is created (optional).'));
o.optional = true;
- o.datatype = 'cidr6';
+ o.datatype = 'ip6addr("nomask")';
+
+ o = s.taboption('general', form.Value, 'port', _('Destination port'));
+ o.optional = true;
+ o.placeholder = 4789;
+ o.datatype = 'port';
o = s.taboption('general', form.Value, 'vid', _('VXLAN network identifier'), _('ID used to uniquely identify the VXLAN'));
o.optional = true;
diff --git a/protocols/luci-proto-yggdrasil/Makefile b/protocols/luci-proto-yggdrasil/Makefile
new file mode 100644
index 0000000000..ecd20fb655
--- /dev/null
+++ b/protocols/luci-proto-yggdrasil/Makefile
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2023 kulupu.io development team (turretkeeper@kulupu.io)
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=Support for Yggdrasil Network
+LUCI_DEPENDS:=+yggdrasil
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0.0
+
+PKG_PROVIDES:=luci-proto-yggdrasil
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/protocols/luci-proto-yggdrasil/htdocs/luci-static/resources/protocol/yggdrasil.js b/protocols/luci-proto-yggdrasil/htdocs/luci-static/resources/protocol/yggdrasil.js
new file mode 100644
index 0000000000..849242abff
--- /dev/null
+++ b/protocols/luci-proto-yggdrasil/htdocs/luci-static/resources/protocol/yggdrasil.js
@@ -0,0 +1,271 @@
+'use strict';
+'require form';
+'require network';
+'require rpc';
+'require tools.widgets as widgets';
+'require uci';
+'require ui';
+network.registerPatternVirtual(/^yggdrasil-.+$/);
+
+function validatePrivateKey(section_id,value) {
+ if (value.length == 0) {
+ return true;
+ };
+ if (!value.match(/^([0-9a-fA-F]){128}$/)) {
+ if (value != "auto") {
+ return _('Invalid private key string %s').format(value);
+ }
+ return true;
+ }
+ return true;
+};
+
+function validatePublicKey(section_id,value) {
+ if (value.length == 0) {
+ return true;
+ };
+ if (!value.match(/^([0-9a-fA-F]){64}$/))
+ return _('Invalid public key string %s').format(value);
+ return true;
+};
+
+function validateYggdrasilListenUri(section_id,value) {
+ if (value.length == 0) {
+ return true;
+ };
+ if (!value.match(/^(tls|tcp|unix|quic):\/\//))
+ return _('Unsupported URI scheme in %s').format(value);
+ return true;
+};
+
+function validateYggdrasilPeerUri(section_id,value) {
+ if (!value.match(/^(tls|tcp|unix|quic|socks|sockstls):\/\//))
+ return _('URI scheme %s not supported').format(value);
+ return true;
+};
+
+var cbiKeyPairGenerate = form.DummyValue.extend({
+ cfgvalue: function(section_id, value) {
+ return E('button', {
+ 'class':'btn',
+ 'click':ui.createHandlerFn(this, function(section_id,ev) {
+ var prv = this.section.getUIElement(section_id,'private_key'),
+ pub = this.section.getUIElement(section_id,'public_key'),
+ map = this.map;
+
+ return generateKey().then(function(keypair){
+ prv.setValue(keypair.priv);
+ pub.setValue(keypair.pub);
+ map.save(null,true);
+ });
+ },section_id)
+ },[_('Generate new key pair')]);
+ }
+});
+
+function updateActivePeers(ifname) {
+ getPeers(ifname).then(function(peers){
+ var table = document.querySelector('#yggdrasil-active-peerings-' + ifname);
+ if (table) {
+ while (table.rows.length > 1) { table.deleteRow(1); }
+ peers.forEach(function(peer) {
+ var row = table.insertRow(-1);
+ row.style.fontSize = "xx-small";
+ if (!peer.up) {
+ row.style.opacity = "66%";
+ }
+ var cell = row.insertCell(-1)
+ cell.className = "td"
+ cell.textContent = peer.remote;
+
+ cell = row.insertCell(-1)
+ cell.className = "td"
+ cell.textContent = peer.up ? "Up" : "Down";
+
+ cell = row.insertCell(-1)
+ cell.className = "td"
+ cell.textContent = peer.inbound ? "In" : "Out";
+
+ cell = row.insertCell(-1)
+ cell.className = "td"
+ cell.innerHTML = "<u style='cursor: default'>" + peer.address + "</u>"
+ cell.dataToggle = "tooltip";
+ cell.title = "Key: " + peer.key;
+
+ cell = row.insertCell(-1)
+ cell.className = "td"
+ cell.textContent = '%t'.format(peer.uptime);
+
+ cell = row.insertCell(-1)
+ cell.className = "td"
+ cell.textContent = '%.2mB'.format(peer.bytes_recvd);
+
+ cell = row.insertCell(-1)
+ cell.className = "td"
+ cell.textContent = '%.2mB'.format(peer.bytes_sent);
+
+ cell = row.insertCell(-1)
+ cell.className = "td"
+ cell.textContent = peer.priority;
+
+ cell = row.insertCell(-1)
+ cell.className = "td"
+ if (!peer.up) {
+ cell.innerHTML = "<u style='cursor: default'>%t ago</u>".format(peer.last_error_time)
+ cell.dataToggle = "tooltip"
+ cell.title = peer.last_error
+ } else {
+ cell.innerHTML = "-"
+ }
+ });
+ setTimeout(updateActivePeers.bind(this, ifname), 5000);
+ }
+ });
+}
+
+var cbiActivePeers = form.DummyValue.extend({
+ cfgvalue: function(section_id, value) {
+ updateActivePeers(this.option);
+ return E('table', {
+ 'class': 'table',
+ 'id': 'yggdrasil-active-peerings-' + this.option,
+ },[
+ E('tr', {'class': 'tr'}, [
+ E('th', {'class': 'th'}, _('URI')),
+ E('th', {'class': 'th'}, _('State')),
+ E('th', {'class': 'th'}, _('Dir')),
+ E('th', {'class': 'th'}, _('IP Address')),
+ E('th', {'class': 'th'}, _('Uptime')),
+ E('th', {'class': 'th'}, _('RX')),
+ E('th', {'class': 'th'}, _('TX')),
+ E('th', {'class': 'th'}, _('Priority')),
+ E('th', {'class': 'th'}, _('Last Error')),
+ ])
+ ]);
+ }
+});
+
+var generateKey = rpc.declare({
+ object:'luci.yggdrasil',
+ method:'generateKeyPair',
+ expect:{keys:{}}
+});
+
+var getPeers = rpc.declare({
+ object:'luci.yggdrasil',
+ method:'getPeers',
+ params:['interface'],
+ expect:{peers:[]}
+});
+
+return network.registerProtocol('yggdrasil',
+ {
+ getI18n: function() {
+ return _('Yggdrasil Network');
+ },
+ getIfname: function() {
+ return this._ubus('l3_device') || this.sid;
+ },
+ getType: function() {
+ return "tunnel";
+ },
+ getOpkgPackage: function() {
+ return 'yggdrasil';
+ },
+ isFloating: function() {
+ return true;
+ },
+ isVirtual: function() {
+ return true;
+ },
+ getDevices: function() {
+ return null;
+ },
+ containsDevice: function(ifname) {
+ return(network.getIfnameOf(ifname)==this.getIfname());
+ },
+ renderFormOptions: function(s) {
+ var o, ss;
+ o=s.taboption('general',form.Value,'private_key',_('Private key'),_('The private key for your Yggdrasil node'));
+ o.optional=false;
+ o.password=true;
+ o.validate=validatePrivateKey;
+
+ o=s.taboption('general',form.Value,'public_key',_('Public key'),_('The public key for your Yggdrasil node'));
+ o.optional=true;
+ o.validate=validatePublicKey;
+
+ s.taboption('general',cbiKeyPairGenerate,'_gen_server_keypair',' ');
+
+ o=s.taboption('advanced',form.Value,'mtu',_('MTU'),_('A default MTU of 65535 is set by Yggdrasil. It is recomended to utilize the default.'));
+ o.optional=true;
+ o.placeholder=65535;
+ o.datatype='range(1280, 65535)';
+
+ o=s.taboption('general',form.TextValue,'node_info',_('Node info'),_('Optional node info. This must be a { "key": "value", ... } map or set as null. This is entirely optional but, if set, is visible to the whole network on request.'));
+ o.optional=true;
+ o.placeholder="{}";
+
+ o=s.taboption('general',form.Flag,'node_info_privacy',_('Node info privacy'),_('Enable node info privacy so that only items specified in "Node info" are sent back. Otherwise defaults including the platform, architecture and Yggdrasil version are included.'));
+ o.default=o.disabled;
+
+ try {
+ s.tab('peers',_('Peers'));
+ } catch(e) {};
+ o=s.taboption('peers', form.SectionValue, '_active', form.NamedSection, this.sid, "interface", _("Active peers"))
+ ss=o.subsection;
+ ss.option(cbiActivePeers, this.sid);
+
+ o=s.taboption('peers', form.SectionValue, '_listen', form.NamedSection, this.sid, "interface", _("Listen for peers"))
+ ss=o.subsection;
+
+ o=ss.option(form.DynamicList,'listen_address',_('Listen addresses'), _('Add listeners in order to accept incoming peerings from non-local nodes. Multicast peer discovery works regardless of listeners set here. URI Format: <code>tls://0.0.0.0:0</code> or <code>tls://[::]:0</code> to listen on all interfaces. Choose an acceptable URI <code>tls://</code>, <code>tcp://</code>, <code>unix://</code> or <code>quic://</code>'));
+ o.placeholder="tls://0.0.0.0:0"
+ o.validate=validateYggdrasilListenUri;
+
+ o=s.taboption('peers',form.DynamicList,'allowed_public_key',_('Accept from public keys'),_('If empty, all incoming connections will be allowed (default). This does not affect outgoing peerings, nor link-local peers discovered via multicast.'));
+ o.validate=validatePublicKey;
+
+ o=s.taboption('peers', form.SectionValue, '_peers', form.TableSection, 'yggdrasil_%s_peer'.format(this.sid), _("Peer addresses"))
+ ss=o.subsection;
+ ss.addremove=true;
+ ss.anonymous=true;
+ ss.addbtntitle=_("Add peer address");
+
+ o=ss.option(form.Value,"address",_("Peer URI"));
+ o.placeholder="tls://0.0.0.0:0"
+ o.validate=validateYggdrasilPeerUri;
+ ss.option(widgets.NetworkSelect,"interface",_("Peer interface"));
+
+ o=s.taboption('peers', form.SectionValue, '_interfaces', form.TableSection, 'yggdrasil_%s_interface'.format(this.sid), _("Multicast rules"))
+ ss=o.subsection;
+ ss.addbtntitle=_("Add multicast rule");
+ ss.addremove=true;
+ ss.anonymous=true;
+
+ o=ss.option(widgets.DeviceSelect,"interface",_("Devices"));
+ o.multiple=true;
+
+ ss.option(form.Flag,"beacon",_("Send multicast beacon"));
+
+ ss.option(form.Flag,"listen",_("Listen to multicast beacons"));
+
+ o=ss.option(form.Value,"port",_("Port"));
+ o.optional=true;
+ o.datatype='range(1, 65535)';
+
+ o=ss.option(form.Value,"password",_("Password"));
+ o.optional=true;
+
+ return;
+ },
+ deleteConfiguration: function() {
+ uci.sections('network', 'yggdrasil_%s_interface'.format(this.sid), function(s) {
+ uci.remove('network', s['.name']);
+ });
+ uci.sections('network', 'yggdrasil_%s_peer'.format(this.sid), function(s) {
+ uci.remove('network', s['.name']);
+ });
+ }
+ }
+);
diff --git a/protocols/luci-proto-yggdrasil/root/usr/libexec/rpcd/luci.yggdrasil b/protocols/luci-proto-yggdrasil/root/usr/libexec/rpcd/luci.yggdrasil
new file mode 100755
index 0000000000..35d6627be7
--- /dev/null
+++ b/protocols/luci-proto-yggdrasil/root/usr/libexec/rpcd/luci.yggdrasil
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+. /usr/share/libubox/jshn.sh
+
+case "$1" in
+ list)
+ json_init
+ json_add_object "generateKeyPair"
+ json_close_object
+ json_add_object "getPeers"
+ json_add_string "interface"
+ json_close_object
+ json_dump
+ ;;
+ call)
+ case "$2" in
+ generateKeyPair)
+ json_load "$(yggdrasil -genconf -json)"
+ json_get_vars PrivateKey
+ json_cleanup
+ json_init
+ json_add_object "keys"
+ json_add_string "priv" "$PrivateKey"
+ json_add_string "pub" "${PrivateKey:64}"
+ json_close_object
+ json_dump
+ ;;
+ getPeers)
+ read -r input
+ json_load "$input"
+ json_get_vars interface
+ yggdrasilctl -endpoint="unix:///tmp/yggdrasil/${interface}.sock" -json getPeers
+ ;;
+ esac
+ ;;
+esac
diff --git a/protocols/luci-proto-yggdrasil/root/usr/share/rpcd/acl.d/luci-proto-yggdrasil.json b/protocols/luci-proto-yggdrasil/root/usr/share/rpcd/acl.d/luci-proto-yggdrasil.json
new file mode 100644
index 0000000000..0351d8610d
--- /dev/null
+++ b/protocols/luci-proto-yggdrasil/root/usr/share/rpcd/acl.d/luci-proto-yggdrasil.json
@@ -0,0 +1,10 @@
+{
+ "luci-proto-yggdrasil": {
+ "description": "Grant access to LuCI Yggdrasil procedures",
+ "write": {
+ "ubus": {
+ "luci.yggdrasil": [ "generateKeyPair", "getPeers" ]
+ }
+ }
+ }
+}