diff options
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.js | 12 | ||||
-rw-r--r-- | protocols/luci-proto-vxlan/htdocs/luci-static/resources/protocol/vxlan6.js | 9 | ||||
-rw-r--r-- | protocols/luci-proto-yggdrasil/Makefile | 18 | ||||
-rw-r--r-- | protocols/luci-proto-yggdrasil/htdocs/luci-static/resources/protocol/yggdrasil.js | 271 | ||||
-rwxr-xr-x | protocols/luci-proto-yggdrasil/root/usr/libexec/rpcd/luci.yggdrasil | 36 | ||||
-rw-r--r-- | protocols/luci-proto-yggdrasil/root/usr/share/rpcd/acl.d/luci-proto-yggdrasil.json | 10 |
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" ] + } + } + } +} |