5 'require tools.widgets as widgets';
8 network
.registerPatternVirtual(/^yggdrasil-.+$/);
10 function validatePrivateKey(section_id
,value
) {
11 if (value
.length
== 0) {
14 if (!value
.match(/^([0-9a-fA-F]){128}$/)) {
15 if (value
!= "auto") {
16 return _('Invalid private key string %s').format(value
);
23 function validatePublicKey(section_id
,value
) {
24 if (value
.length
== 0) {
27 if (!value
.match(/^([0-9a-fA-F]){64}$/))
28 return _('Invalid public key string %s').format(value
);
32 function validateYggdrasilListenUri(section_id
,value
) {
33 if (value
.length
== 0) {
36 if (!value
.match(/^(tls|tcp|unix|quic):\/\//))
37 return _('Unsupported URI scheme in %s').format(value
);
41 function validateYggdrasilPeerUri(section_id
,value
) {
42 if (!value
.match(/^(tls|tcp|unix|quic|socks|sockstls):\/\//))
43 return _('URI scheme %s not supported').format(value
);
47 var cbiKeyPairGenerate
= form
.DummyValue
.extend({
48 cfgvalue: function(section_id
, value
) {
51 'click':ui
.createHandlerFn(this, function(section_id
,ev
) {
52 var prv
= this.section
.getUIElement(section_id
,'private_key'),
53 pub
= this.section
.getUIElement(section_id
,'public_key'),
56 return generateKey().then(function(keypair
){
57 prv
.setValue(keypair
.priv
);
58 pub
.setValue(keypair
.pub
);
62 },[_('Generate new key pair')]);
66 function updateActivePeers(ifname
) {
67 getPeers(ifname
).then(function(peers
){
68 var table
= document
.querySelector('#yggdrasil-active-peerings-' + ifname
);
70 while (table
.rows
.length
> 1) { table
.deleteRow(1); }
71 peers
.forEach(function(peer
) {
72 var row
= table
.insertRow(-1);
73 row
.style
.fontSize
= "xx-small";
75 row
.style
.opacity
= "66%";
77 var cell
= row
.insertCell(-1)
79 cell
.textContent
= peer
.remote
;
81 cell
= row
.insertCell(-1)
83 cell
.textContent
= peer
.up
? "Up" : "Down";
85 cell
= row
.insertCell(-1)
87 cell
.textContent
= peer
.inbound
? "In" : "Out";
89 cell
= row
.insertCell(-1)
91 cell
.innerHTML
= "<u style='cursor: default'>" + peer
.address
+ "</u>"
92 cell
.dataToggle
= "tooltip";
93 cell
.title
= "Key: " + peer
.key
;
95 cell
= row
.insertCell(-1)
97 cell
.textContent
= '%t'.format(peer
.uptime
);
99 cell
= row
.insertCell(-1)
100 cell
.className
= "td"
101 cell
.textContent
= '%.2mB'.format(peer
.bytes_recvd
);
103 cell
= row
.insertCell(-1)
104 cell
.className
= "td"
105 cell
.textContent
= '%.2mB'.format(peer
.bytes_sent
);
107 cell
= row
.insertCell(-1)
108 cell
.className
= "td"
109 cell
.textContent
= peer
.priority
;
111 cell
= row
.insertCell(-1)
112 cell
.className
= "td"
114 cell
.innerHTML
= "<u style='cursor: default'>%t ago</u>".format(peer
.last_error_time
)
115 cell
.dataToggle
= "tooltip"
116 cell
.title
= peer
.last_error
121 setTimeout(updateActivePeers
.bind(this, ifname
), 5000);
126 var cbiActivePeers
= form
.DummyValue
.extend({
127 cfgvalue: function(section_id
, value
) {
128 updateActivePeers(this.option
);
131 'id': 'yggdrasil-active-peerings-' + this.option
,
133 E('tr', {'class': 'tr'}, [
134 E('th', {'class': 'th'}, _('URI')),
135 E('th', {'class': 'th'}, _('State')),
136 E('th', {'class': 'th'}, _('Dir')),
137 E('th', {'class': 'th'}, _('IP Address')),
138 E('th', {'class': 'th'}, _('Uptime')),
139 E('th', {'class': 'th'}, _('RX')),
140 E('th', {'class': 'th'}, _('TX')),
141 E('th', {'class': 'th'}, _('Priority')),
142 E('th', {'class': 'th'}, _('Last Error')),
148 var generateKey
= rpc
.declare({
149 object
:'luci.yggdrasil',
150 method
:'generateKeyPair',
154 var getPeers
= rpc
.declare({
155 object
:'luci.yggdrasil',
157 params
:['interface'],
161 return network
.registerProtocol('yggdrasil',
163 getI18n: function() {
164 return _('Yggdrasil Network');
166 getIfname: function() {
167 return this._ubus('l3_device') || this.sid
;
169 getType: function() {
172 getOpkgPackage: function() {
175 isFloating: function() {
178 isVirtual: function() {
181 getDevices: function() {
184 containsDevice: function(ifname
) {
185 return(network
.getIfnameOf(ifname
)==this.getIfname());
187 renderFormOptions: function(s
) {
189 o
=s
.taboption('general',form
.Value
,'private_key',_('Private key'),_('The private key for your Yggdrasil node'));
192 o
.validate
=validatePrivateKey
;
194 o
=s
.taboption('general',form
.Value
,'public_key',_('Public key'),_('The public key for your Yggdrasil node'));
196 o
.validate
=validatePublicKey
;
198 s
.taboption('general',cbiKeyPairGenerate
,'_gen_server_keypair',' ');
200 o
=s
.taboption('advanced',form
.Value
,'mtu',_('MTU'),_('A default MTU of 65535 is set by Yggdrasil. It is recomended to utilize the default.'));
203 o
.datatype
='range(1280, 65535)';
205 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.'));
209 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.'));
210 o
.default=o
.disabled
;
213 s
.tab('peers',_('Peers'));
215 o
=s
.taboption('peers', form
.SectionValue
, '_active', form
.NamedSection
, this.sid
, "interface", _("Active peers"))
217 ss
.option(cbiActivePeers
, this.sid
);
219 o
=s
.taboption('peers', form
.SectionValue
, '_listen', form
.NamedSection
, this.sid
, "interface", _("Listen for peers"))
222 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>'));
223 o
.placeholder
="tls://0.0.0.0:0"
224 o
.validate
=validateYggdrasilListenUri
;
226 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.'));
227 o
.validate
=validatePublicKey
;
229 o
=s
.taboption('peers', form
.SectionValue
, '_peers', form
.TableSection
, 'yggdrasil_%s_peer'.format(this.sid
), _("Peer addresses"))
233 ss
.addbtntitle
=_("Add peer address");
235 o
=ss
.option(form
.Value
,"address",_("Peer URI"));
236 o
.placeholder
="tls://0.0.0.0:0"
237 o
.validate
=validateYggdrasilPeerUri
;
238 ss
.option(widgets
.NetworkSelect
,"interface",_("Peer interface"));
240 o
=s
.taboption('peers', form
.SectionValue
, '_interfaces', form
.TableSection
, 'yggdrasil_%s_interface'.format(this.sid
), _("Multicast rules"))
242 ss
.addbtntitle
=_("Add multicast rule");
246 o
=ss
.option(widgets
.DeviceSelect
,"interface",_("Devices"));
249 ss
.option(form
.Flag
,"beacon",_("Send multicast beacon"));
251 ss
.option(form
.Flag
,"listen",_("Listen to multicast beacons"));
253 o
=ss
.option(form
.Value
,"port",_("Port"));
255 o
.datatype
='range(1, 65535)';
257 o
=ss
.option(form
.Value
,"password",_("Password"));
262 deleteConfiguration: function() {
263 uci
.sections('network', 'yggdrasil_%s_interface'.format(this.sid
), function(s
) {
264 uci
.remove('network', s
['.name']);
266 uci
.sections('network', 'yggdrasil_%s_peer'.format(this.sid
), function(s
) {
267 uci
.remove('network', s
['.name']);