mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-07-27 02:11:19 +08:00
🍉 Sync 2026-07-22 20:35:20
This commit is contained in:
parent
201f770d7f
commit
9df7ccb682
@ -5,10 +5,10 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=dae
|
||||
PKG_VERSION:=2026.07.21
|
||||
PKG_RELEASE:=20
|
||||
PKG_VERSION:=2026.07.22
|
||||
PKG_RELEASE:=21
|
||||
|
||||
PKG_SOURCE:=dae-src-2026.07.21-f6edc5e8af8c.tar.gz
|
||||
PKG_SOURCE:=dae-src-2026.07.22-1ff7a6abc0ad.tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||
PKG_HASH:=skip
|
||||
|
||||
@ -5,10 +5,10 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=daed
|
||||
PKG_VERSION:=2026.07.21
|
||||
PKG_RELEASE:=28
|
||||
PKG_VERSION:=2026.07.22
|
||||
PKG_RELEASE:=29
|
||||
|
||||
PKG_SOURCE:=daed-src-2026.07.21-7ab100147a57.tar.gz
|
||||
PKG_SOURCE:=daed-src-2026.07.22-4a6a3994ced1.tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||
PKG_HASH:=skip
|
||||
|
||||
@ -8,10 +8,10 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=golang1.26
|
||||
GO_VERSION_MAJOR_MINOR:=1.26
|
||||
GO_VERSION_PATCH:=4
|
||||
GO_VERSION_PATCH:=5
|
||||
GO_VERSION_RC:=
|
||||
GO_BOOTSTRAP_VERSION:=bootstrap
|
||||
PKG_HASH:=4f668a32fbfc1132e6a881fb968c2f1dada631492a339211735fbb255a42602d
|
||||
PKG_HASH:=495be4bc87176ac567392e5b4116abd98466d33d7b49d41e764ccc6976b2dc42
|
||||
|
||||
PKG_VERSION:=$(GO_VERSION_MAJOR_MINOR)$(if $(GO_VERSION_RC),.0)$(if $(GO_VERSION_PATCH),.$(GO_VERSION_PATCH))
|
||||
PKG_FILE_VERSION:=$(GO_VERSION_MAJOR_MINOR)$(if $(GO_VERSION_RC),rc$(GO_VERSION_RC))$(if $(GO_VERSION_PATCH),.$(GO_VERSION_PATCH))
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-csshnpd
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
PKG_MAINTAINER:=Chris Swan <chris@atsign.com>
|
||||
PKG_RELEASE:=1
|
||||
|
||||
LUCI_TITLE:=NoPorts Web UI
|
||||
LUCI_DESCRIPTION:=LuCI config app for NoPorts daemon (csshnpd)
|
||||
LUCI_DEPENDS:=+luci-base +csshnpd
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
@ -1,88 +0,0 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
|
||||
function validateAtsign(section_id, value) {
|
||||
if (value.length < 1) {
|
||||
return _('Must not be empty and should start with @ (e.g., "@a").');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function validateDevice(section_id, value) {
|
||||
if (value.length < 1) {
|
||||
return _('Must be at least one character long (e.g., "a").');
|
||||
}
|
||||
if (value.length == 1) {
|
||||
if (!/^[a-z]+$/.test(value)) {
|
||||
return _('First character should be a lowercase letter (e.g., "a").');
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!/^[a-z][a-z0-9_-]+$/.test(value)) {
|
||||
return _('Device names may contain a-z 0-9 _ or - (e.g., "my_thing1").');
|
||||
}
|
||||
if (value.length > 36) {
|
||||
return _('Maximum device name length is 36 characters.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function validateOTP(section_id, value) {
|
||||
if (value.length != 6) {
|
||||
return _('Must be six characters (e.g., "S3CR3T").');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function firstAt(section_id, value) {
|
||||
if (value && !value.startsWith('@')) {
|
||||
value = '@' + value; // Ensure @ at start
|
||||
}
|
||||
return this.super('write', [section_id, value]);
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
render: function() {
|
||||
let m, s, o;
|
||||
|
||||
m = new form.Map('sshnpd', _('NoPorts'),
|
||||
_('Daemon Configuration'));
|
||||
|
||||
s = m.section(form.TypedSection, 'sshnpd', _('sshnpd config'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Value, 'atsign', _('Device atSign'),
|
||||
_('The device atSign e.g. @device'));
|
||||
o.default = '@device';
|
||||
o.validate = validateAtsign;
|
||||
o.write = firstAt;
|
||||
|
||||
o = s.option(form.Value, 'manager', _('Manager atSign'),
|
||||
_('The manager atSign e.g. @manager'));
|
||||
o.default = '@manager';
|
||||
o.validate = validateAtsign;
|
||||
o.write = firstAt;
|
||||
|
||||
o = s.option(form.Value, 'device', _('Device name'),
|
||||
_('The name for this device e.g. openwrt'));
|
||||
o.default = 'openwrt';
|
||||
o.validate = validateDevice;
|
||||
|
||||
s.option(form.Value, 'args', _('Additional arguments'),
|
||||
_('Further command line arguments for the NoPorts daemon'));
|
||||
|
||||
o = s.option(form.Value, 'otp', _('Enrollment OTP/SPP'),
|
||||
_('One Time Passcode (OTP) for device atSign enrollment'));
|
||||
o.default = '000000';
|
||||
o.validate = validateOTP;
|
||||
|
||||
o = s.option(form.Flag, 'enabled', _('Enabled'),
|
||||
_('Check here to enable the service'));
|
||||
o.default = '1';
|
||||
o.rmempty = false;
|
||||
|
||||
return m.render();
|
||||
},
|
||||
});
|
||||
@ -1,97 +0,0 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require dom';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require uci';
|
||||
'require network';
|
||||
|
||||
|
||||
return view.extend({
|
||||
handleCommand: function(exec, args) {
|
||||
let buttons = document.querySelectorAll('.diag-action > .cbi-button');
|
||||
|
||||
for (let i = 0; i < buttons.length; i++)
|
||||
buttons[i].setAttribute('disabled', 'true');
|
||||
|
||||
return fs.exec(exec, args).then(function(res) {
|
||||
let out = document.querySelector('textarea');
|
||||
|
||||
dom.content(out, [ res.stdout || '', res.stderr || '' ]);
|
||||
}).catch(function(err) {
|
||||
ui.addNotification(null, E('p', [ err ]))
|
||||
}).finally(function() {
|
||||
for (let i = 0; i < buttons.length; i++)
|
||||
buttons[i].removeAttribute('disabled');
|
||||
});
|
||||
},
|
||||
|
||||
handleEnroll: function() {
|
||||
return this.handleCommand('at_enroll.sh', "");
|
||||
},
|
||||
|
||||
load: function() {
|
||||
return uci.load('sshnpd').then(function() {
|
||||
let atsign = uci.get_first('sshnpd','','atsign'),
|
||||
keyfile = '/root/.atsign/keys/'+atsign+'_key.atKeys';
|
||||
return L.resolveDefault(fs.stat(keyfile), {});
|
||||
});
|
||||
},
|
||||
|
||||
render: function(res) {
|
||||
|
||||
const has_atkey = res.path;
|
||||
const atsign = uci.get_first('sshnpd','','atsign');
|
||||
const device = uci.get_first('sshnpd','','device');
|
||||
const otp = uci.get_first('sshnpd','','otp');
|
||||
const enrollready = atsign && device && otp && !has_atkey;
|
||||
|
||||
const instructions = E('div', { 'class': 'cbi-map-descr'}, _('Press the Enroll button then run this command on a system where '+atsign+' is activated:'));
|
||||
|
||||
const enrollcmd = E('code','at_activate approve -a '+atsign+' --arx noports --drx '+device);
|
||||
|
||||
let table = E('table', { 'class': 'table' }, [
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left' }, [
|
||||
E('span', { 'class': 'diag-action' }, [
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-action',
|
||||
'click': ui.createHandlerFn(this, 'handleEnroll')
|
||||
}, [ _('Enroll') ])
|
||||
])
|
||||
]),
|
||||
])
|
||||
]);
|
||||
|
||||
const cmdwindow = E('div', {'class': 'cbi-section'}, [
|
||||
E('div', { 'id' : 'command-output'},
|
||||
E('textarea', {
|
||||
'id': 'widget.command-output',
|
||||
'style': 'width: 100%; font-family:monospace; white-space:pre',
|
||||
'readonly': true,
|
||||
'wrap': 'on',
|
||||
'rows': '20'
|
||||
})
|
||||
)
|
||||
]);
|
||||
|
||||
let view = E('div', { 'class': 'cbi-map'}, [
|
||||
E('h2', {}, [ _('NoPorts atSign Enrollment') ]),
|
||||
atsign ? E([]) : E('div', { 'class': 'cbi-map-descr'}, _('atSign must be configured')),
|
||||
device ? E([]) : E('div', { 'class': 'cbi-map-descr'}, _('Device must be configured')),
|
||||
otp ? E([]) : E('div', { 'class': 'cbi-map-descr'}, _('OTP must be configured. An OTP can be generated using:')),
|
||||
otp ? E([]) : E('code','at_activate otp -a '+atsign),
|
||||
has_atkey ? E('div', { 'class': 'cbi-map-descr'}, _('Existing key found at: '+has_atkey)) : E([]),
|
||||
enrollready ? instructions : E([]),
|
||||
enrollready ? enrollcmd : E([]),
|
||||
enrollready ? table : E([]),
|
||||
enrollready ? cmdwindow : E([]),
|
||||
]);
|
||||
|
||||
return view;
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
||||
@ -1,30 +0,0 @@
|
||||
{
|
||||
"admin/network/sshnpd": {
|
||||
"title": "NoPorts",
|
||||
"order": 70,
|
||||
"action": {
|
||||
"type": "firstchild"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-app-csshnpd" ]
|
||||
}
|
||||
},
|
||||
|
||||
"admin/network/sshnpd/config": {
|
||||
"title": "NoPorts Config",
|
||||
"order": 1,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "sshnpd/config"
|
||||
}
|
||||
},
|
||||
|
||||
"admin/network/sshnpd/enroll": {
|
||||
"title": "NoPorts Enrollment",
|
||||
"order": 2,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "sshnpd/enroll"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"luci-app-csshnpd": {
|
||||
"description": "Grant UCI access for luci-app-csshnpd",
|
||||
"read": {
|
||||
"uci": [ "sshnpd" ],
|
||||
"file": {
|
||||
"/usr/bin/at_enroll.sh": ["exec"],
|
||||
"/root/.atsign/keys/*": ["stat"]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"uci": [ "sshnpd" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -171,9 +171,9 @@ return view.extend({
|
||||
.catch(function(e) { ui.addNotification(null, E('p', e.message)) });
|
||||
},
|
||||
|
||||
HandleStopDDnsRule(m, section_id, ev) {
|
||||
handleStopDDnsRule(m, section_id, ev) {
|
||||
return fs.exec('/usr/lib/ddns/dynamic_dns_lucihelper.sh',
|
||||
[ '-S', section_id, '--', 'start' ])
|
||||
[ '-S', section_id, '--', 'stop' ])
|
||||
.then(L.bind(m.render, m))
|
||||
.catch(function(e) { ui.addNotification(null, E('p', e.message)) });
|
||||
},
|
||||
@ -551,7 +551,7 @@ return view.extend({
|
||||
},
|
||||
stop_opt = {
|
||||
'class': 'cbi-button cbi-button-neutral stop',
|
||||
'click': ui.createHandlerFn(_this, 'HandleStopDDnsRule', m, section_id),
|
||||
'click': ui.createHandlerFn(_this, 'handleStopDDnsRule', m, section_id),
|
||||
'title': _('Stop this service'),
|
||||
};
|
||||
|
||||
|
||||
@ -15,7 +15,12 @@ const luci_helper = '/usr/lib/ddns/dynamic_dns_lucihelper.sh';
|
||||
const ddns_version_file = '/usr/share/ddns/version';
|
||||
|
||||
|
||||
function shellquote(value) {
|
||||
if (value == null)
|
||||
value = '';
|
||||
|
||||
return "'" + replace(value, "'", "'\\''") + "'";
|
||||
}
|
||||
|
||||
function get_dateformat() {
|
||||
return uci.get('ddns', 'global', 'ddns_dateformat') || '%F %R';
|
||||
@ -126,9 +131,9 @@ const methods = {
|
||||
if (forceDnsTcp == 1) push(command, '-t');
|
||||
// if (isGlue == 1) push(command, '-g');
|
||||
|
||||
push(command, '-l', lookupHost);
|
||||
push(command, '-S', section);
|
||||
if (length(dnsServer) > 0) push(command, '-d', dnsServer);
|
||||
push(command, '-l', shellquote(lookupHost));
|
||||
push(command, '-S', shellquote(section));
|
||||
if (length(dnsServer) > 0) push(command, '-d', shellquote(dnsServer));
|
||||
push(command, '-- get_registered_ip');
|
||||
|
||||
const result = system(`${join(' ', command)}`);
|
||||
@ -221,50 +226,40 @@ const methods = {
|
||||
|
||||
const hasCommand = (command) => { return (system(`command -v ${command} 1>/dev/null`) == 0) ? true : false };
|
||||
|
||||
const hasWget = () => hasCommand('wget');
|
||||
const hasWget = () => {
|
||||
return cache.has_wget ??= hasCommand('wget');
|
||||
}
|
||||
|
||||
const hasWgetSsl = () => {
|
||||
if (cache['has_wgetssl']) return cache['has_wgetssl'];
|
||||
const result = hasWget() && system(`wget 2>&1 | grep -iqF 'https'`) == 0 ? true : false;
|
||||
cache['has_wgetssl'] = result;
|
||||
return result;
|
||||
return cache.has_wgetssl ??= hasWget() && system(`wget 2>&1 | grep -iqF 'https'`) == 0 ? true : false;
|
||||
};
|
||||
|
||||
const hasGNUWgetSsl = () => {
|
||||
if (cache['has_gnuwgetssl']) return cache['has_gnuwgetssl'];
|
||||
const result = hasWget() && system(`wget -V 2>&1 | grep -iqF '+https'`) == 0 ? true : false;
|
||||
cache['has_gnuwgetssl'] = result;
|
||||
return result;
|
||||
return cache.has_gnuwgetssl ??= hasWget() && system(`wget -V 2>&1 | grep -iqF '+https'`) == 0 ? true : false;
|
||||
};
|
||||
|
||||
const hasCurl = () => {
|
||||
if (cache['has_curl']) return cache['has_curl'];
|
||||
const result = hasCommand('curl');
|
||||
cache['has_curl'] = result;
|
||||
return result;
|
||||
return cache.has_curl ??= hasCommand('curl');
|
||||
};
|
||||
|
||||
const hasCurlSsl = () => {
|
||||
return system(`curl -V 2>&1 | grep -qF 'https'`) == 0 ? true : false;
|
||||
return cache.has_curl_ssl ??= system(`curl -V 2>&1 | grep -qF 'https'`) == 0 ? true : false;
|
||||
};
|
||||
|
||||
const hasFetch = () => {
|
||||
if (cache['has_fetch']) return cache['has_fetch'];
|
||||
const result = hasCommand('uclient-fetch');
|
||||
cache['has_fetch'] = result;
|
||||
return result;
|
||||
return cache.has_fetch ??= hasCommand('uclient-fetch');
|
||||
};
|
||||
|
||||
const hasFetchSsl = () => {
|
||||
return stat('/lib/libustream-ssl.so') == 0 ? true : false;
|
||||
return cache.has_fetch_ssl ??= stat('/lib/libustream-ssl.so') ? true : false;
|
||||
};
|
||||
|
||||
const hasCurlPxy = () => {
|
||||
return system(`grep -i 'all_proxy' /usr/lib/libcurl.so*`) == 0 ? true : false;
|
||||
return cache.has_curl_proxy ??= system(`grep -i 'all_proxy' /usr/lib/libcurl.so*`) == 0 ? true : false;
|
||||
};
|
||||
|
||||
const hasBbwget = () => {
|
||||
return system(`wget -V 2>&1 | grep -iqF 'busybox'`) == 0 ? true : false;
|
||||
return cache.has_bbwget ??= system(`wget -V 2>&1 | grep -iqF 'busybox'`) == 0 ? true : false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2022 Jaymin Patel <jem.patel@gmail.com>
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_LICENSE:=GPL-2.0-or-later
|
||||
PKG_MAINTAINER:=Jaymin Patel <jem.patel@gmail.com>
|
||||
|
||||
LUCI_TITLE=Luci Application for IPSec VPN (Libreswan)
|
||||
LUCI_DEPENDS:=+luci-base +libreswan
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
|
||||
@ -1,71 +0,0 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require network';
|
||||
'require tools.widgets as widgets';
|
||||
|
||||
return view.extend({
|
||||
load() {
|
||||
return Promise.all([
|
||||
network.getDevices(),
|
||||
]);
|
||||
},
|
||||
|
||||
render([netDevs]) {
|
||||
let m, s, o;
|
||||
|
||||
m = new form.Map('libreswan', _('IPSec Global Settings'));
|
||||
|
||||
s = m.section(form.NamedSection, 'globals', 'libreswan');
|
||||
s.anonymous = false;
|
||||
s.addremove = false;
|
||||
|
||||
o = s.option(form.ListValue, 'debug', _('Debug Logs'));
|
||||
o.default = false;
|
||||
o.rmempty = false;
|
||||
o.value('none', _('none - No Logging'));
|
||||
o.value('base', _('base - Moderate Logging'));
|
||||
o.value('cpu-usage', _('cpu-usage - Timing/Load Logging'));
|
||||
o.value('crypto', _('crypto - All crypto related Logging'));
|
||||
o.value('tmi', _('tmi - Too Much/Excessive Logging'));
|
||||
o.value('private', _('private - Sensitive private-key/password Logging'));
|
||||
o.default = 'none'
|
||||
|
||||
o = s.option(form.Flag, 'uniqueids', _('Uniquely Identify Remotes'),
|
||||
_('Whether IDs should be considered identifying remote parties uniquely'));
|
||||
o.default = false;
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(widgets.NetworkSelect, 'listen_interface', _('Listen Interface'),
|
||||
_('Interface for IPsec to use'));
|
||||
o.datatype = 'string';
|
||||
o.multiple = false;
|
||||
o.optional = true;
|
||||
|
||||
o = s.option(form.Value, 'listen', _('Listen Address'),
|
||||
_('IP address to listen on, default depends on Listen Interface'));
|
||||
o.datatype = 'ip4addr';
|
||||
for (let nd of netDevs) {
|
||||
var addrs = nd.getIPAddrs();
|
||||
for (let ad of addrs) {
|
||||
o.value(ad.split('/')[0]);
|
||||
}
|
||||
}
|
||||
o.depends({ 'listen_interface' : '' });
|
||||
|
||||
o = s.option(form.Value, 'nflog_all', _('Enable nflog on nfgroup'),
|
||||
_('NFLOG group number to log all pre-crypt and post-decrypt traffic to'));
|
||||
o.datatype = 'uinteger';
|
||||
o.default = 0;
|
||||
o.rmempty = true;
|
||||
o.optional = true;
|
||||
|
||||
o = s.option(form.DynamicList, 'virtual_private', _('Allowed Virtual Private'),
|
||||
_('The address ranges that may live behind a NAT router through which a client connects'));
|
||||
o.datatype = 'neg(ip4addr)';
|
||||
o.multiple = true;
|
||||
o.optional = true;
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
@ -1,82 +0,0 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require rpc';
|
||||
'require form';
|
||||
'require poll';
|
||||
|
||||
const callLibreswanStatus = rpc.declare({
|
||||
object: 'libreswan',
|
||||
method: 'status',
|
||||
expect: { },
|
||||
});
|
||||
|
||||
function secondsToString(seconds) {
|
||||
const numdays = Math.floor(seconds / 86400);
|
||||
seconds %= 86400;
|
||||
const numhours = Math.floor(seconds / 3600);
|
||||
seconds %= 3600;
|
||||
const numminutes = Math.floor(seconds / 60);
|
||||
const numseconds = seconds % 60;
|
||||
|
||||
return [
|
||||
numdays ? `${numdays}d` : '',
|
||||
numhours ? `${numhours}h` : '',
|
||||
numminutes ? `${numminutes}m` : '',
|
||||
`${numseconds}s`
|
||||
].filter(Boolean).join(' ');
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
render: function() {
|
||||
const table =
|
||||
E('table', { 'class': 'table lases' }, [
|
||||
E('tr', { 'class': 'tr table-titles' }, [
|
||||
E('th', { 'class': 'th' }, _('Name')),
|
||||
E('th', { 'class': 'th' }, _('Remote')),
|
||||
E('th', { 'class': 'th' }, _('Local Subnet')),
|
||||
E('th', { 'class': 'th' }, _('Remote Subnet')),
|
||||
E('th', { 'class': 'th' }, _('Tx')),
|
||||
E('th', { 'class': 'th' }, _('Rx')),
|
||||
E('th', { 'class': 'th' }, _('Phase1')),
|
||||
E('th', { 'class': 'th' }, _('Phase2')),
|
||||
E('th', { 'class': 'th' }, _('Status')),
|
||||
E('th', { 'class': 'th' }, _('Uptime')),
|
||||
E([])
|
||||
])
|
||||
]);
|
||||
|
||||
poll.add(function() {
|
||||
return callLibreswanStatus().then(function(tunnelsInfo) {
|
||||
const tunnels = Array.isArray(tunnelsInfo.tunnels) ? tunnelsInfo.tunnels : [];
|
||||
|
||||
cbi_update_table(table,
|
||||
tunnels.map(function(tunnel) {
|
||||
return [
|
||||
tunnel.name,
|
||||
tunnel.right,
|
||||
tunnel.leftsubnet,
|
||||
tunnel.rightsubnet,
|
||||
tunnel.tx,
|
||||
tunnel.rx,
|
||||
tunnel.phase1 ? _('Up') : _('Down'),
|
||||
tunnel.phase2 ? _('Up') : _('Down'),
|
||||
tunnel.connected ? _('Up') : _('Down'),
|
||||
secondsToString(tunnel.uptime),
|
||||
];
|
||||
}),
|
||||
E('em', _('There are no active Tunnels'))
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
return E([
|
||||
E('h3', _('IPSec Tunnels Summary')),
|
||||
E('br'),
|
||||
table
|
||||
]);
|
||||
},
|
||||
|
||||
handleSave: null,
|
||||
handleSaveApply:null,
|
||||
handleReset: null
|
||||
});
|
||||
@ -1,76 +0,0 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require ui';
|
||||
'require form';
|
||||
'require uci';
|
||||
|
||||
return view.extend({
|
||||
render() {
|
||||
let m, s, o;
|
||||
|
||||
m = new form.Map('libreswan', _('IPSec Proposals'));
|
||||
|
||||
s = m.section(form.GridSection, 'crypto_proposal');
|
||||
s.anonymous = false;
|
||||
s.addremove = true;
|
||||
s.nodescriptions = true;
|
||||
s.addbtntitle = _('Add Proposal');
|
||||
|
||||
s.renderSectionAdd = function(extra_class) {
|
||||
const el = form.GridSection.prototype.renderSectionAdd.apply(this, arguments),
|
||||
nameEl = el.querySelector('.cbi-section-create-name');
|
||||
ui.addValidator(nameEl, 'uciname', true, function(v) {
|
||||
let sections = [
|
||||
...uci.sections('libreswan', 'crypto_proposal'),
|
||||
...uci.sections('libreswan', 'tunnel'),
|
||||
];
|
||||
if (sections.find(function(s) {
|
||||
return s['.name'] == v;
|
||||
})) {
|
||||
return _('This may not share the same name as other proposals or configured tunnels.');
|
||||
}
|
||||
if (v.length > 15) return _('Name length shall not exceed 15 characters');
|
||||
return true;
|
||||
}, 'blur', 'keyup');
|
||||
return el;
|
||||
};
|
||||
|
||||
o = s.tab('general', _('General'));
|
||||
|
||||
o = s.taboption('general', form.MultiValue, 'hash_algorithm', _('Hash Algorithm'), ('* = %s').format(_('Unsafe')));
|
||||
o.default = 'md5';
|
||||
o.value('md5', _('MD5*'));
|
||||
o.value('sha1', _('SHA1*'));
|
||||
o.value('sha256', _('SHA256'));
|
||||
o.value('sha384', _('SHA384'));
|
||||
o.value('sha512', _('SHA512'));
|
||||
|
||||
o = s.taboption('general', form.MultiValue, 'encryption_algorithm', _('Encryption Method'), ('* = %s').format(_('Unsafe')));
|
||||
o.default = 'aes';
|
||||
o.value('3des', _('3DES*'))
|
||||
o.value('aes', _('AES'))
|
||||
o.value('aes_ctr', _('AES_CTR'));
|
||||
o.value('aes_cbc', _('AES_CBC'));
|
||||
o.value('aes128', _('AES128'));
|
||||
o.value('aes192', _('AES192'));
|
||||
o.value('aes256', _('AES256'));
|
||||
o.value('camellia_cbc', _('CAMELLIA_CBC'));
|
||||
|
||||
o = s.taboption('general', form.MultiValue, 'dh_group', _('DH Group'),
|
||||
('* = %s <a href="%s">RFC8247</a>.').format(_('Unsafe, See'), 'https://www.rfc-editor.org/rfc/rfc8247#section-2.4'));
|
||||
o.default = 'modp1536';
|
||||
o.value('modp1536', _('DH Group 5*'));
|
||||
o.value('modp2048', _('DH Group 14'));
|
||||
o.value('modp3072', _('DH Group 15'));
|
||||
o.value('modp4096', _('DH Group 16'));
|
||||
o.value('modp6144', _('DH Group 17'));
|
||||
o.value('modp8192', _('DH Group 18'));
|
||||
o.value('dh19', _('DH Group 19'));
|
||||
o.value('dh20', _('DH Group 20'));
|
||||
o.value('dh21', _('DH Group 21'));
|
||||
o.value('dh22', _('DH Group 22*'));
|
||||
o.value('dh31', _('DH Group 31'));
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
@ -1,274 +0,0 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require ui';
|
||||
'require uci';
|
||||
'require network';
|
||||
'require validation';
|
||||
'require tools.widgets as widgets';
|
||||
|
||||
function calculateNetwork(addr, mask) {
|
||||
const parsedAddr = validation.parseIPv4(String(addr));
|
||||
if (parsedAddr == null) return null;
|
||||
|
||||
const parsedMask = !isNaN(mask)
|
||||
? validation.parseIPv4(network.prefixToMask(+mask))
|
||||
: validation.parseIPv4(String(mask));
|
||||
if (parsedMask == null) return null;
|
||||
|
||||
const networkAddr = parsedAddr.map((byte, i) => byte & (parsedMask[i] >>> 0 & 255));
|
||||
|
||||
return `${networkAddr.join('.')}/${network.maskToPrefix(parsedMask.join('.'))}`;
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load() {
|
||||
return Promise.all([
|
||||
network.getDevices(),
|
||||
uci.load('libreswan'),
|
||||
]);
|
||||
},
|
||||
|
||||
render([netDevs]) {
|
||||
let m, s, o;
|
||||
|
||||
let proposals = uci.sections('libreswan', 'crypto_proposal');
|
||||
if (proposals == '') {
|
||||
ui.addNotification(null, E('p', _('Proposals must be configured for Tunnels')));
|
||||
return;
|
||||
}
|
||||
|
||||
m = new form.Map('libreswan', 'IPSec Tunnels');
|
||||
|
||||
s = m.section(form.GridSection, 'tunnel');
|
||||
s.anonymous = false;
|
||||
s.addremove = true;
|
||||
s.nodedescription = false;
|
||||
s.addbtntitle = _('Add Tunnel');
|
||||
|
||||
s.renderSectionAdd = function(extra_class) {
|
||||
const el = form.GridSection.prototype.renderSectionAdd.apply(this, arguments);
|
||||
const nameEl = el.querySelector('.cbi-section-create-name');
|
||||
ui.addValidator(nameEl, 'uciname', true, function(v) {
|
||||
let sections = [
|
||||
...uci.sections('libreswan', 'crypto_proposal'),
|
||||
...uci.sections('libreswan', 'tunnel'),
|
||||
];
|
||||
|
||||
if (sections.find(function(s) {
|
||||
return s['.name'] == v;
|
||||
})) {
|
||||
return _('This may not share the same name as other proposals or configured tunnels.');
|
||||
}
|
||||
if (v.length > 15) return _('Name length shall not exceed 15 characters');
|
||||
return true;
|
||||
}, 'blur', 'keyup');
|
||||
return el;
|
||||
};
|
||||
|
||||
o = s.tab('general', _('General'));
|
||||
o = s.tab('authentication', _('Authentication'));
|
||||
o = s.tab('interface', _('Interface'));
|
||||
o = s.tab('advanced', _('Advanced'));
|
||||
|
||||
o = s.taboption('general', form.ListValue, 'auto', _('Mode'));
|
||||
o.default = 'start';
|
||||
o.value('add', _('Listen'));
|
||||
o.value('start', _('Initiate'));
|
||||
|
||||
o = s.taboption('general', widgets.NetworkSelect, 'left_interface', _('Left Interface'));
|
||||
o.datatype = 'string';
|
||||
o.multiple = false;
|
||||
o.optional = true;
|
||||
|
||||
o = s.taboption('general', form.Value, 'left', _('Left IP/Device'));
|
||||
o.datatype = 'or(string, ipaddr)';
|
||||
netDevs.forEach(netDev => {
|
||||
netDev.getIPAddrs().forEach(addr => {
|
||||
o.value(addr.split('/')[0]);
|
||||
});
|
||||
});
|
||||
netDevs.forEach(netDev => {
|
||||
o.value(`%${netDev.device}`);
|
||||
});
|
||||
o.value('%defaultroute');
|
||||
o.optional = false;
|
||||
o.depends({ 'left_interface' : '' });
|
||||
|
||||
o = s.taboption('general', form.Value, 'leftid', _('Left ID'));
|
||||
o.datatype = 'string';
|
||||
o.value('%any');
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('general', form.Value, 'right', _('Remote IP'));
|
||||
o.datatype = 'or(string, ipaddr)';
|
||||
o.value('0.0.0.0');
|
||||
o.value('%any');
|
||||
o.optional = false;
|
||||
|
||||
o = s.taboption('general', form.Value, 'rightid', _('Right ID'));
|
||||
o.datatype = 'string';
|
||||
o.value('%any');
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('general', form.Value, 'leftsourceip', _('Local Source IP'));
|
||||
o.datatype = 'ipaddr';
|
||||
netDevs.forEach(netDev => {
|
||||
netDev.getIPAddrs().forEach(addr => {
|
||||
o.value(addr.split('/')[0]);
|
||||
});
|
||||
});
|
||||
o.optional = false;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('general', form.Value, 'rightsourceip', _('Remote Source IP'));
|
||||
o.datatype = 'ipaddr';
|
||||
o.optional = false;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('general', form.DynamicList, 'leftsubnets', _('Local Subnets'));
|
||||
o.datatype = 'ipaddr';
|
||||
for (let nd of netDevs) {
|
||||
const addrs = nd.getIPAddrs();
|
||||
for (let ad of addrs) {
|
||||
const subnet = calculateNetwork(ad.split('/')[0], ad.split('/')[1]);
|
||||
if (subnet) {
|
||||
o.value(subnet);
|
||||
}
|
||||
}
|
||||
}
|
||||
o.value('0.0.0.0/0');
|
||||
|
||||
o = s.taboption('general', form.DynamicList, 'rightsubnets', _('Remote Subnets'));
|
||||
o.datatype = 'ipaddr';
|
||||
o.value('0.0.0.0/0');
|
||||
|
||||
o = s.taboption('authentication', form.ListValue, 'authby', _('Auth Method'));
|
||||
o.default = 'secret'
|
||||
o.value('secret', _('Shared Secret'));
|
||||
o.value('never', 'Never');
|
||||
o.value('null', 'Null');
|
||||
o.modalonly = true;
|
||||
o.optional = false;
|
||||
|
||||
o = s.taboption('authentication', form.Value, 'psk', _('Preshared Key'));
|
||||
o.datatype = 'and(string, minlength(8))'
|
||||
o.depends({ 'authby' : 'secret' });
|
||||
o.password = true;
|
||||
o.modalonly = true;
|
||||
o.optional = false;
|
||||
|
||||
o = s.taboption('advanced', form.ListValue, 'ikev2', _('IKE V2'));
|
||||
o.default = 'yes';
|
||||
o.value('yes', _('IKE Version 2'));
|
||||
o.value('no', _('IKE Version 1'));
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.MultiValue, 'ike', _('Phase1 Proposals'));
|
||||
for (let prop of proposals) {
|
||||
o.value(prop['.name']);
|
||||
}
|
||||
o.modalonly = true;
|
||||
|
||||
function timevalidate(section_id, value) {
|
||||
if (!/^[0-9]{1,3}[smhd]$/.test(value)) {
|
||||
return _('Acceptable values are an integer followed by m, h, d');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'ikelifetime', _('IKE Life Time'), _('Acceptable values are an integer followed by m, h, d'));
|
||||
o.default = '8h';
|
||||
o.value('1h', '1h');
|
||||
o.value('2h', '2h');
|
||||
o.value('4h', '4h');
|
||||
o.value('8h', '8h');
|
||||
o.value('12h', '12h');
|
||||
o.value('16h', '16h');
|
||||
o.value('24h', '24h');
|
||||
o.modalonly = false;
|
||||
o.modalonly = true;
|
||||
o.validate = timevalidate;
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'rekey', _('Rekey'));
|
||||
o.default = false;
|
||||
o.modalonly = false;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'rekeymargin', _('Rekey Margin Time'), _('Acceptable values are an integer followed by m, h, d'));
|
||||
o.default = '9m';
|
||||
o.value('5m', '5m');
|
||||
o.value('9m', '9m');
|
||||
o.value('15m', '15m');
|
||||
o.value('20m', '20m');
|
||||
o.value('30m', '30m');
|
||||
o.value('60m', '60m');
|
||||
o.modalonly = false;
|
||||
o.modalonly = true;
|
||||
o.validate = timevalidate;
|
||||
|
||||
o = s.taboption('advanced', form.ListValue, 'dpdaction', _('DPD Action'));
|
||||
o.default = 'restart';
|
||||
o.value('none', _('None'));
|
||||
o.value('clear', _('Clear'));
|
||||
o.value('hold', _('Hold'));
|
||||
o.value('restart', _('Restart'));
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'dpddelay', _('DPD Delay'));
|
||||
o.datatype = 'uinteger';
|
||||
o.default = 30;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'dpdtimeout', _('DPD Timeout'));
|
||||
o.datatype = 'uinteger';
|
||||
o.default = 150;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.ListValue, 'phase2', _('Phase2 Protocol'));
|
||||
o.default = 'esp';
|
||||
o.value('esp', 'ESP');
|
||||
o.modalonly = true;
|
||||
o.optional = false;
|
||||
|
||||
o = s.taboption('advanced', form.MultiValue, 'phase2alg', _('Phase2 Proposals'));
|
||||
for (let prop of proposals) {
|
||||
o.value(prop['.name']);
|
||||
}
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'nflog', _('Enable nflog on nfgroup'));
|
||||
o.default = 0;
|
||||
o.datatype = 'uinteger';
|
||||
o.rmempty = true;
|
||||
o.optional = true;
|
||||
o.modalonly = true;
|
||||
|
||||
let interfaces = uci.sections('network', 'interface');
|
||||
o = s.taboption('advanced', form.ListValue, 'interface', _('Tunnel Interface'),
|
||||
_('Lists XFRM interfaces in format "ipsecN", N denotes ifid of xfrm interface') + '<br>' +
|
||||
_('Lists VTI interfaces configured with ikey and okey'));
|
||||
o.datatype = 'string';
|
||||
o.rmempty = true;
|
||||
o.modalonly = true;
|
||||
o.value('');
|
||||
interfaces.forEach(iface => {
|
||||
const { proto, ikey, okey, ifid, ['.name']: name } = iface;
|
||||
|
||||
if (proto === "vti" && ikey && okey) {
|
||||
o.value(name, `VTI - ${name}`);
|
||||
}
|
||||
|
||||
if (proto === "xfrm" && ifid && name.match(`ipsec${ifid}`)) {
|
||||
o.value(name, `XFRM - ${name}`);
|
||||
}
|
||||
});
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'update_peeraddr', _('Update Peer Address'),
|
||||
_('Auto Update Peer Address of VTI interface'));
|
||||
o.rmempty = true;
|
||||
o.modalonly = true;
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
@ -1,45 +0,0 @@
|
||||
{
|
||||
"admin/vpn/libreswan": {
|
||||
"title": "Libreswan IPSec",
|
||||
"order": 90,
|
||||
"action": {
|
||||
"type": "firstchild"
|
||||
}
|
||||
},
|
||||
|
||||
"admin/vpn/libreswan/overview": {
|
||||
"title": "Overview",
|
||||
"order": 10,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "libreswan/overview"
|
||||
}
|
||||
},
|
||||
|
||||
"admin/vpn/libreswan/globals": {
|
||||
"title": "IPSec Globals",
|
||||
"order": 20,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "libreswan/globals"
|
||||
}
|
||||
},
|
||||
|
||||
"admin/vpn/libreswan/proposals": {
|
||||
"title": "IPSec Proposals",
|
||||
"order": 30,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "libreswan/proposals"
|
||||
}
|
||||
},
|
||||
|
||||
"admin/vpn/libreswan/tunnels": {
|
||||
"title": "IPSec Tunnels",
|
||||
"order": 40,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "libreswan/tunnels"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
{
|
||||
"luci-app-libreswan" : {
|
||||
"description" : "Grant access to LuCI app Libreswan IPSec",
|
||||
"read" : {
|
||||
"ubus" : {
|
||||
"libreswan" : [ "*" ]
|
||||
},
|
||||
"uci": [ "libreswan" ]
|
||||
},
|
||||
"write" : {
|
||||
"uci": [ "libreswan" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"config": "libreswan",
|
||||
"init": "ipsec"
|
||||
}
|
||||
@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall
|
||||
PKG_VERSION:=26.7.16
|
||||
PKG_RELEASE:=189
|
||||
PKG_RELEASE:=190
|
||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
|
||||
@ -1049,7 +1049,7 @@ function gen_config(var)
|
||||
|
||||
api.log(" - 加载 Xray 负载均衡 节点【" .. (_node.remarks or "") .. "】,子节点数量:" .. #(blc_nodes or {}))
|
||||
|
||||
local valid_nodes = {}
|
||||
local valid_nodes, valid_fallback = {}, true
|
||||
for i = 1, #(blc_nodes or {}) do
|
||||
local blc_node_id = blc_nodes[i]
|
||||
local blc_node_tag = "blc-" .. blc_node_id
|
||||
@ -1067,12 +1067,16 @@ function gen_config(var)
|
||||
valid_nodes[#valid_nodes + 1] = outboundTag
|
||||
end
|
||||
end
|
||||
-- Check if balancing node duplicates fallback node
|
||||
if _node.fallback_node == blc_node_id then
|
||||
valid_fallback = false
|
||||
end
|
||||
end
|
||||
if #valid_nodes == 0 then return nil end
|
||||
|
||||
-- fallback node
|
||||
local fallback_node_id = _node.fallback_node
|
||||
fallback_node_id = (fallback_node_id and fallback_node_id ~= "") and fallback_node_id or nil
|
||||
fallback_node_id = (valid_fallback and fallback_node_id and fallback_node_id ~= "") and fallback_node_id or nil
|
||||
local fallback_node_tag = (fallback_node_id == "_direct") and "direct" or "blackhole"
|
||||
if fallback_node_id and fallback_node_id ~= "_direct" then
|
||||
local is_new_node = true
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
# Copyright 2021 Nicholas Smith (nicholas@nbembedded.com)
|
||||
# Copyright (C) 2023 TDT AG <development@tdt.de>
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_LICENSE:=GPL-2.0-or-later
|
||||
PKG_MAINTAINER:=Nicholas Smith <nicholas@nbembedded.com>, Lukas Voegl <lvoegl@tdt.de>
|
||||
|
||||
LUCI_TITLE:=LuCI support for strongSwan via swanctl
|
||||
LUCI_DESCRIPTION:=Status and configuration for strongSwan based on swanctl
|
||||
LUCI_DEPENDS:=+strongswan-swanctl +swanmon
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
@ -1,143 +0,0 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
|
||||
return baseclass.extend({
|
||||
|
||||
_encryptionAlgorithms: new Map([
|
||||
['3des', true],
|
||||
['cast128', true],
|
||||
['blowfish128', true],
|
||||
['blowfish192', true],
|
||||
['blowfish256', true],
|
||||
['null', true],
|
||||
['aes128'],
|
||||
['aes192'],
|
||||
['aes256'],
|
||||
['aes128ctr'],
|
||||
['aes192ctr'],
|
||||
['aes256ctr'],
|
||||
['camellia128'],
|
||||
['camellia192'],
|
||||
['camellia256'],
|
||||
['camellia128ctr'],
|
||||
['camellia192ctr'],
|
||||
['camellia256ctr']
|
||||
]),
|
||||
|
||||
_authenticatedEncryptionAlgorithms: new Map([
|
||||
['aes128ccm64'],
|
||||
['aes192ccm64'],
|
||||
['aes256ccm64'],
|
||||
['aes128ccm96'],
|
||||
['aes192ccm96'],
|
||||
['aes256ccm96'],
|
||||
['aes128ccm128'],
|
||||
['aes192ccm128'],
|
||||
['aes256ccm128'],
|
||||
['aes128gcm64'],
|
||||
['aes192gcm64'],
|
||||
['aes256gcm64'],
|
||||
['aes128gcm96'],
|
||||
['aes192gcm96'],
|
||||
['aes256gcm96'],
|
||||
['aes128gcm128'],
|
||||
['aes192gcm128'],
|
||||
['aes256gcm128'],
|
||||
['aes128gmac'],
|
||||
['aes192gmac'],
|
||||
['aes256gmac'],
|
||||
['camellia128ccm64'],
|
||||
['camellia192ccm64'],
|
||||
['camellia256ccm64'],
|
||||
['camellia128ccm96'],
|
||||
['camellia192ccm96'],
|
||||
['camellia256ccm96'],
|
||||
['camellia128ccm128'],
|
||||
['camellia192ccm128'],
|
||||
['camellia256ccm128'],
|
||||
['chacha20poly1305']
|
||||
]),
|
||||
|
||||
_hashAlgorithms: new Map([
|
||||
['md5', true],
|
||||
['md5_128', true],
|
||||
['sha1', true],
|
||||
['sha1_160', true],
|
||||
['aesxcbc'],
|
||||
['aescmac'],
|
||||
['aes128gmac'],
|
||||
['aes192gmac'],
|
||||
['aes256gmac'],
|
||||
['sha256'],
|
||||
['sha384'],
|
||||
['sha512'],
|
||||
['sha256_96']
|
||||
]),
|
||||
|
||||
_dhAlgorithms: new Map([
|
||||
['modp768', true],
|
||||
['modp1024', true],
|
||||
['modp1536', true],
|
||||
['modp2048'],
|
||||
['modp3072'],
|
||||
['modp4096'],
|
||||
['modp6144'],
|
||||
['modp8192'],
|
||||
['modp1024s160', true],
|
||||
['modp2048s224', true],
|
||||
['modp2048s256', true],
|
||||
['ecp192', true],
|
||||
['ecp224'],
|
||||
['ecp256'],
|
||||
['ecp384'],
|
||||
['ecp521'],
|
||||
['ecp224bp'],
|
||||
['ecp256bp'],
|
||||
['ecp384bp'],
|
||||
['ecp512bp'],
|
||||
['curve25519'],
|
||||
['curve448']
|
||||
]),
|
||||
|
||||
_prfAlgorithms: new Map([
|
||||
['prfmd5', true],
|
||||
['prfsha1', true],
|
||||
['prfaesxcbc'],
|
||||
['prfaescmac'],
|
||||
['prfsha256'],
|
||||
['prfsha384'],
|
||||
['prfsha512']
|
||||
]),
|
||||
|
||||
_getAlgorithmNames(algorithms) {
|
||||
return Array.from(algorithms.keys());
|
||||
},
|
||||
|
||||
isInsecure(algorithmName) {
|
||||
return this._encryptionAlgorithms.get(algorithmName) == true
|
||||
|| this._authenticatedEncryptionAlgorithms.get(algorithmName) == true
|
||||
|| this._hashAlgorithms.get(algorithmName) == true
|
||||
|| this._dhAlgorithms.get(algorithmName) == true
|
||||
|| this._prfAlgorithms.get(algorithmName) == true;
|
||||
},
|
||||
|
||||
getEncryptionAlgorithms() {
|
||||
return this._getAlgorithmNames(this._encryptionAlgorithms);
|
||||
},
|
||||
|
||||
getAuthenticatedEncryptionAlgorithms() {
|
||||
return this._getAlgorithmNames(this._authenticatedEncryptionAlgorithms);
|
||||
},
|
||||
|
||||
getHashAlgorithms() {
|
||||
return this._getAlgorithmNames(this._hashAlgorithms);
|
||||
},
|
||||
|
||||
getDiffieHellmanAlgorithms() {
|
||||
return this._getAlgorithmNames(this._dhAlgorithms);
|
||||
},
|
||||
|
||||
getPrfAlgorithms() {
|
||||
return this._getAlgorithmNames(this._prfAlgorithms);
|
||||
}
|
||||
});
|
||||
@ -1,189 +0,0 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require dom';
|
||||
'require poll';
|
||||
'require fs';
|
||||
'require ui';
|
||||
|
||||
function formatTime(seconds, selectCount) {
|
||||
const days = Math.floor(seconds / (60 * 60 * 24));
|
||||
const hours = Math.floor(seconds / (60 * 60)) % 24;
|
||||
const minutes = Math.floor(seconds / 60) % 60;
|
||||
seconds = Math.floor(seconds % 60);
|
||||
|
||||
const times = [
|
||||
[days, _('Day'), _('Days')],
|
||||
[hours, _('Hour'), _('Hours')],
|
||||
[minutes, _('Minute'), _('Minutes')],
|
||||
[seconds, _('Second'), _('Seconds')]
|
||||
].filter(function ([time, singular, plural]) {
|
||||
return time > 0;
|
||||
});
|
||||
|
||||
const selectedTimes = times.slice(0, selectCount);
|
||||
return selectedTimes.map(function ([time, singular, plural]) {
|
||||
const unit = time > 1 ? plural : singular;
|
||||
return '%d %s'.format(time, unit);
|
||||
}).join(', ');
|
||||
}
|
||||
|
||||
function buildSection(name, table) {
|
||||
return E('div', { 'class': 'cbi-section' }, [
|
||||
E('h2', [name]),
|
||||
table
|
||||
]);
|
||||
}
|
||||
|
||||
function buildTable(rows) {
|
||||
return E('table', { 'class': 'table', }, rows);
|
||||
}
|
||||
|
||||
function buildKeyValueTable(kvPairs) {
|
||||
const rows = kvPairs.map(function (row) {
|
||||
return E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td', 'width': '33%' }, E('strong', [row[0]])),
|
||||
E('td', { 'class': 'td' }, [row[1]])
|
||||
]);
|
||||
});
|
||||
return buildTable(rows);
|
||||
}
|
||||
|
||||
function collectErrorMessages(results) {
|
||||
const errorMessages = results.reduce(function (messages, result) {
|
||||
return messages.concat(result.errors.map(function (error) {
|
||||
return error.message;
|
||||
}));
|
||||
}, []);
|
||||
const uniqueErrorMessages = new Set(errorMessages);
|
||||
|
||||
return [...uniqueErrorMessages];
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load() {
|
||||
return Promise.all([
|
||||
fs.exec_direct('/usr/sbin/swanmon', ['version'], 'json'),
|
||||
fs.exec_direct('/usr/sbin/swanmon', ['stats'], 'json'),
|
||||
fs.exec_direct('/usr/sbin/swanmon', ['list-sas'], 'json')
|
||||
]);
|
||||
},
|
||||
|
||||
pollData(container) {
|
||||
poll.add(L.bind(function () {
|
||||
return this.load().then(L.bind(function (results) {
|
||||
dom.content(container, this.renderContent(results));
|
||||
}, this));
|
||||
}, this));
|
||||
},
|
||||
|
||||
renderContent(results) {
|
||||
const node = E('div', [E('div')]);
|
||||
const firstNode = node.firstElementChild;
|
||||
|
||||
const errorMessages = collectErrorMessages(results);
|
||||
if (errorMessages.length > 0) {
|
||||
const messageEls = errorMessages.map(function (message) {
|
||||
return E('li', message);
|
||||
});
|
||||
|
||||
firstNode.appendChild(E('h4', _('Querying strongSwan failed')));
|
||||
firstNode.appendChild(E('ul', messageEls));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
const [version, stats, sas] = results.map(function (r) {
|
||||
return r.data;
|
||||
});
|
||||
|
||||
const uptimeSeconds = (new Date() - new Date(stats.uptime.since)) / 1000;
|
||||
const statsSection = buildSection(_('Stats'), buildKeyValueTable([
|
||||
[_('Version'), version.version],
|
||||
[_('Uptime'), formatTime(uptimeSeconds, 2)],
|
||||
[_('Daemon'), version.daemon],
|
||||
[_('Active IKE_SAs'), stats.ikesas.total],
|
||||
[_('Half-Open IKE_SAs'), stats.ikesas['half-open']]
|
||||
]));
|
||||
firstNode.appendChild(statsSection);
|
||||
|
||||
const tableRows = sas.map(function (conn) {
|
||||
const name = Object.keys(conn)[0];
|
||||
const data = conn[name];
|
||||
const childSas = [];
|
||||
|
||||
Object.entries(data['child-sas']).forEach(function ([name, data]) {
|
||||
const table = buildKeyValueTable([
|
||||
[_('State'), data.state],
|
||||
[_('Mode'), data.mode],
|
||||
[_('Protocol'), data.protocol],
|
||||
[_('Local Traffic Selectors'), data['local-ts'].join(', ')],
|
||||
[_('Remote Traffic Selectors'), data['remote-ts'].join(', ')],
|
||||
[_('Encryption Algorithm'), data['encr-alg']],
|
||||
[_('Encryption Keysize'), data['encr-keysize']],
|
||||
[_('Bytes in'), data['bytes-in']],
|
||||
[_('Bytes out'), data['bytes-out']],
|
||||
[_('Life Time'), formatTime(data['life-time'], 2)],
|
||||
[_('Install Time'), formatTime(data['install-time'], 2)],
|
||||
[_('Rekey in'), formatTime(data['rekey-time'], 2)],
|
||||
[_('SPI in'), data['spi-in']],
|
||||
[_('SPI out'), data['spi-out']]
|
||||
]);
|
||||
childSas.push(E('div', { 'class': 'cbi-section' }, [
|
||||
E('h4', { 'style': 'margin-top: 0; padding-top: 0;' }, [name]),
|
||||
table
|
||||
]));
|
||||
});
|
||||
childSas.push(E('button', {
|
||||
'class': 'btn cbi-button cbi-button-apply',
|
||||
'click': ui.hideModal
|
||||
}, _('Close')));
|
||||
|
||||
return E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td' }, [name]),
|
||||
E('td', { 'class': 'td' }, [data.state]),
|
||||
E('td', { 'class': 'td' }, [data['remote-host']]),
|
||||
E('td', { 'class': 'td' }, [data.version]),
|
||||
E('td', { 'class': 'td' }, [formatTime(data.established, 2)]),
|
||||
E('td', { 'class': 'td' }, [formatTime(data['reauth-time'], 2)]),
|
||||
E('td', { 'class': 'td' }, [E('button', {
|
||||
'class': 'btn cbi-button cbi-button-apply',
|
||||
'click': function (ev) {
|
||||
ui.showModal(_('CHILD_SAs'), childSas)
|
||||
}
|
||||
}, _('Show Details'))])
|
||||
]);
|
||||
});
|
||||
const connSection = buildSection(_('Security Associations (SAs)'), buildTable([
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('th', { 'class': 'th' }, [_('Name')]),
|
||||
E('th', { 'class': 'th' }, [_('State')]),
|
||||
E('th', { 'class': 'th' }, [_('Remote')]),
|
||||
E('th', { 'class': 'th' }, [_('IKE Version')]),
|
||||
E('th', { 'class': 'th' }, [_('Established for')]),
|
||||
E('th', { 'class': 'th' }, [_('Reauthentication in')]),
|
||||
E('th', { 'class': 'th' }, [_('Details')])
|
||||
]),
|
||||
...tableRows
|
||||
]));
|
||||
firstNode.appendChild(connSection);
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
render(results) {
|
||||
const content = E([], [
|
||||
E('h2', [_('strongSwan Status')]),
|
||||
E('div')
|
||||
]);
|
||||
const container = content.lastElementChild;
|
||||
|
||||
dom.content(container, this.renderContent(results));
|
||||
this.pollData(container);
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
||||
@ -1,435 +0,0 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require uci';
|
||||
'require ui';
|
||||
'require tools.widgets as widgets';
|
||||
'require strongswan_algorithms';
|
||||
|
||||
function validateTimeFormat(section_id, value) {
|
||||
if (value && !value.match(/^\d+[smhd]$/)) {
|
||||
return _('Number must have suffix s, m, h or d');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function addAlgorithms(o, algorithms) {
|
||||
algorithms.forEach(function (algorithm) {
|
||||
if (strongswan_algorithms.isInsecure(algorithm)) {
|
||||
o.value(algorithm, '%s*'.format(algorithm));
|
||||
} else {
|
||||
o.value(algorithm);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sectionNameCheck(extra_class) {
|
||||
var el = form.GridSection.prototype.renderSectionAdd.apply(this, arguments),
|
||||
nameEl = el.querySelector('.cbi-section-create-name');
|
||||
ui.addValidator(nameEl, 'uciname', true, function(v) {
|
||||
let sections = [
|
||||
...uci.sections('ipsec', 'remote'),
|
||||
...uci.sections('ipsec', 'tunnel'),
|
||||
...uci.sections('ipsec', 'crypto_proposal'),
|
||||
];
|
||||
if (sections.find(function(s) {
|
||||
return s['.name'] == v;
|
||||
})) {
|
||||
return _('Remotes, Encryption Proposals and Tunnels may not share the same names.') + ' ' +
|
||||
_('Use combinations like tunnel1_phase1 that do not exceed 15 characters.');
|
||||
}
|
||||
if (v.length > 15) return _('Name length shall not exceed 15 characters');
|
||||
return true;
|
||||
}, 'blur', 'keyup');
|
||||
return el;
|
||||
};
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return uci.load('network');
|
||||
},
|
||||
|
||||
render: function () {
|
||||
let m, s, o;
|
||||
|
||||
m = new form.Map('ipsec', _('strongSwan Configuration'),
|
||||
_('Configure strongSwan for secure VPN connections.'));
|
||||
m.tabbed = true;
|
||||
|
||||
// strongSwan General Settings
|
||||
s = m.section(form.TypedSection, 'ipsec', _('General Settings'));
|
||||
s.anonymous = true;
|
||||
s.addremove = true;
|
||||
|
||||
o = s.option(widgets.ZoneSelect, 'zone', _('Zone'),
|
||||
_('Firewall zone that has to match the defined firewall zone'));
|
||||
o.default = 'lan';
|
||||
o.multiple = true;
|
||||
|
||||
o = s.option(widgets.NetworkSelect, 'listen', _('Listening Interfaces'),
|
||||
_('Interfaces that accept VPN traffic'));
|
||||
o.datatype = 'interface';
|
||||
o.placeholder = _('Select an interface or leave empty for all interfaces');
|
||||
o.default = 'wan';
|
||||
o.multiple = true;
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'debug', _('Debug Level'),
|
||||
_('Trace level: 0 is least verbose, 4 is most'));
|
||||
o.default = '0';
|
||||
o.datatype = 'range(0,4)';
|
||||
|
||||
// Remote Configuration
|
||||
s = m.section(form.GridSection, 'remote', _('Remote Configuration'),
|
||||
_('Define Remote IKE Configurations.'));
|
||||
s.addremove = true;
|
||||
s.nodescriptions = true;
|
||||
s.renderSectionAdd = sectionNameCheck
|
||||
|
||||
o = s.tab('general', _('General'));
|
||||
o = s.tab('authentication', _('Authentication'));
|
||||
o = s.tab('advanced', _('Advanced'));
|
||||
|
||||
o = s.taboption('general', form.Flag, 'enabled', _('Enabled'),
|
||||
_('Configuration is enabled or not'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('general', form.Value, 'gateway', _('Gateway (Remote Endpoint)'),
|
||||
_('IP address or FQDN name of the tunnel remote endpoint'));
|
||||
o.datatype = 'or(hostname,ipaddr)';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('general', form.Value, 'local_gateway', _('Local Gateway'),
|
||||
_('IP address or FQDN of the tunnel local endpoint'));
|
||||
o.datatype = 'or(hostname,ipaddr)';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('general', form.Value, 'local_sourceip', _('Local Source IP'),
|
||||
_('Virtual IP(s) to request in IKEv2 configuration payloads requests'));
|
||||
o.datatype = 'ipaddr';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('general', form.Value, 'local_ip', _('Local IP'),
|
||||
_('Local address(es) to use in IKE negotiation'));
|
||||
o.datatype = 'ipaddr';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('general', form.MultiValue, 'crypto_proposal', _('Crypto Proposal'),
|
||||
_('List of IKE (phase 1) proposals to use for authentication'));
|
||||
o.load = function (section_id) {
|
||||
this.keylist = [];
|
||||
this.vallist = [];
|
||||
|
||||
var sections = uci.sections('ipsec', 'crypto_proposal').filter(function (section) {
|
||||
return section.is_esp != '1';
|
||||
});
|
||||
if (sections.length == 0) {
|
||||
this.value('', _('Please create a Proposal first'));
|
||||
} else {
|
||||
sections.forEach(L.bind(function (section) {
|
||||
this.value(section['.name']);
|
||||
}, this));
|
||||
}
|
||||
|
||||
return this.super('load', [section_id]);
|
||||
};
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('general', form.MultiValue, 'tunnel', _('Tunnel'),
|
||||
_('The Tunnel containing the ESP (phase 2) section'));
|
||||
o.load = function (section_id) {
|
||||
this.keylist = [];
|
||||
this.vallist = [];
|
||||
|
||||
var sections = uci.sections('ipsec', 'tunnel');
|
||||
if (sections.length == 0) {
|
||||
this.value('', _('Please create a Tunnel first'));
|
||||
} else {
|
||||
sections.forEach(L.bind(function (section) {
|
||||
this.value(section['.name']);
|
||||
}, this));
|
||||
}
|
||||
|
||||
return this.super('load', [section_id]);
|
||||
};
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('authentication', form.ListValue, 'authentication_method',
|
||||
_('Authentication Method'), _('IKE authentication (phase 1)'));
|
||||
o.modalonly = true;
|
||||
o.value('psk', 'Pre-shared Key');
|
||||
o.value('pubkey', 'Public Key');
|
||||
|
||||
o = s.taboption('authentication', form.Value, 'local_identifier', _('Local Identifier'),
|
||||
_('Local identifier for IKE (phase 1)'));
|
||||
o.datatype = 'string';
|
||||
o.placeholder = 'C=US, O=Acme Corporation, CN=headquarters';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('authentication', form.Value, 'remote_identifier', _('Remote Identifier'),
|
||||
_('Remote identifier for IKE (phase 1)'));
|
||||
o.datatype = 'string';
|
||||
o.placeholder = 'C=US, O=Acme Corporation, CN=soho';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('authentication', form.Value, 'pre_shared_key', _('Pre-Shared Key'),
|
||||
_('The pre-shared key for the tunnel'));
|
||||
o.datatype = 'string';
|
||||
o.password = true;
|
||||
o.modalonly = true;
|
||||
o.rmempty = false;
|
||||
o.depends('authentication_method', 'psk');
|
||||
|
||||
o = s.taboption('authentication', form.Value, 'local_cert', _('Local Certificate'),
|
||||
_('Certificate pathname to use for authentication'));
|
||||
o.datatype = 'file';
|
||||
o.depends('authentication_method', 'pubkey');
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('authentication', form.Value, 'local_key', _('Local Key'),
|
||||
_('Private key pathname to use with above certificate'));
|
||||
o.datatype = 'file';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('authentication', form.Value, 'ca_cert', _('CA Certificate'),
|
||||
_("CA certificate that need to lie in remote peer's certificate's path of trust"));
|
||||
o.datatype = 'file';
|
||||
o.depends('authentication_method', 'pubkey');
|
||||
o.modalonly = true;
|
||||
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'mobike', _('MOBIKE'),
|
||||
_('MOBIKE (IKEv2 Mobility and Multihoming Protocol)'));
|
||||
o.default = '1';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.ListValue, 'fragmentation', _('IKE Fragmentation'),
|
||||
_('Use IKE fragmentation'));
|
||||
o.value('yes');
|
||||
o.value('no');
|
||||
o.value('force');
|
||||
o.value('accept');
|
||||
o.default = 'yes';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'keyingtries', _('Keying Retries'),
|
||||
_('Number of retransmissions attempts during initial negotiation'));
|
||||
o.datatype = 'uinteger';
|
||||
o.default = '3';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'dpddelay', _('DPD Delay'),
|
||||
_('Interval to check liveness of a peer'));
|
||||
o.validate = validateTimeFormat;
|
||||
o.default = '30s';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'inactivity', _('Inactivity'),
|
||||
_('Interval before closing an inactive CHILD_SA'));
|
||||
o.validate = validateTimeFormat;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'rekeytime', _('Rekey Time'),
|
||||
_('IKEv2 interval to refresh keying material; also used to compute lifetime'));
|
||||
o.validate = validateTimeFormat;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'overtime', _('Overtime'),
|
||||
_('Limit on time to complete rekeying/reauthentication'));
|
||||
o.validate = validateTimeFormat;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.ListValue, 'keyexchange', _('Keyexchange'),
|
||||
_('Version of IKE for negotiation'));
|
||||
o.value('ikev1', 'IKEv1 (%s)', _('deprecated'));
|
||||
o.value('ikev2', 'IKEv2');
|
||||
o.value('ike', 'IKE (%s, %s)'.format(_('both'), _('deprecated')));
|
||||
o.default = 'ikev2';
|
||||
o.modalonly = true;
|
||||
|
||||
// Tunnel Configuration
|
||||
s = m.section(form.GridSection, 'tunnel', _('Tunnel Configuration'),
|
||||
_('Define Connection Children to be used as Tunnels in Remote Configurations.'));
|
||||
s.addremove = true;
|
||||
s.nodescriptions = true;
|
||||
s.renderSectionAdd = sectionNameCheck;
|
||||
|
||||
o = s.tab('general', _('General'));
|
||||
o = s.tab('advanced', _('Advanced'));
|
||||
|
||||
o = s.taboption('general', form.DynamicList, 'local_subnet', _('Local Subnet'),
|
||||
_('Local network(s)'));
|
||||
o.datatype = 'subnet';
|
||||
o.placeholder = '192.168.1.1/24';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('general', form.DynamicList, 'remote_subnet', _('Remote Subnet'),
|
||||
_('Remote network(s)'));
|
||||
o.datatype = 'subnet';
|
||||
o.placeholder = '192.168.2.1/24';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('general', form.Value, 'local_nat', _('Local NAT'),
|
||||
_('NAT range for tunnels with overlapping IP addresses'));
|
||||
o.datatype = 'subnet';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('general', form.ListValue, 'if_id', ('XFRM Interface ID'),
|
||||
_('XFRM interface ID set on input and output interfaces'));
|
||||
o.load = function (section_id) {
|
||||
this.keylist = [];
|
||||
this.vallist = [];
|
||||
|
||||
var xfrmSections = uci.sections('network').filter(function (section) {
|
||||
return section.proto == 'xfrm';
|
||||
});
|
||||
|
||||
xfrmSections.forEach(L.bind(function (section) {
|
||||
this.value(section.ifid,
|
||||
'%s (%s)'.format(section.ifid, section['.name']));
|
||||
}, this));
|
||||
|
||||
return this.super('load', [section_id]);
|
||||
}
|
||||
o.optional = true;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('general', form.ListValue, 'startaction', _('Start Action'),
|
||||
_('Action on initial configuration load'));
|
||||
o.value('none');
|
||||
o.value('trap');
|
||||
o.value('start');
|
||||
o.default = 'trap';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('general', form.ListValue, 'closeaction', _('Close Action'),
|
||||
_('Action when CHILD_SA is closed'));
|
||||
o.value('none');
|
||||
o.value('trap');
|
||||
o.value('start');
|
||||
o.optional = true;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('general', form.MultiValue, 'crypto_proposal',
|
||||
_('Crypto Proposal (Phase 2)'),
|
||||
_('List of ESP (phase two) proposals. Only Proposals with checked ESP flag are selectable'));
|
||||
o.load = function (section_id) {
|
||||
this.keylist = [];
|
||||
this.vallist = [];
|
||||
|
||||
var sections = uci.sections('ipsec', 'crypto_proposal').filter(function (section) {
|
||||
return section.is_esp == '1';
|
||||
});
|
||||
if (sections.length == 0) {
|
||||
this.value('', _('Please create an ESP Proposal first'));
|
||||
} else {
|
||||
sections.forEach(L.bind(function (section) {
|
||||
this.value(section['.name']);
|
||||
}, this));
|
||||
}
|
||||
|
||||
return this.super('load', [section_id]);
|
||||
};
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'updown', _('Up/Down Script Path'),
|
||||
_('Path to script to run on CHILD_SA up/down events'));
|
||||
o.datatype = 'file';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'lifetime', _('Lifetime'),
|
||||
_('Maximum duration of the CHILD_SA before closing'));
|
||||
o.validate = validateTimeFormat;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.ListValue, 'dpdaction', _('DPD Action'),
|
||||
_('Action when DPD timeout occurs'));
|
||||
o.value('none');
|
||||
o.value('clear');
|
||||
o.value('trap');
|
||||
o.value('start');
|
||||
o.optional = true;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'rekeytime', _('Rekey Time'),
|
||||
_('Duration of the CHILD_SA before rekeying'));
|
||||
o.validate = validateTimeFormat;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.Flag, 'ipcomp', _('IPComp'),
|
||||
_('Enable ipcomp compression'));
|
||||
o.default = '0';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.ListValue, 'hw_offload', _('H/W Offload'),
|
||||
_('Enable Hardware offload'));
|
||||
o.value('yes');
|
||||
o.value('no');
|
||||
o.value('auto');
|
||||
o.optional = true;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'priority', _('Priority'),
|
||||
_('Priority of the CHILD_SA'));
|
||||
o.datatype = 'uinteger';
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption('advanced', form.Value, 'replay_window', _('Replay Window'),
|
||||
'%s; %s'.format(_('Replay Window of the CHILD_SA'),
|
||||
_('Values larger than 32 are supported by the Netlink backend only')));
|
||||
o.datatype = 'uinteger';
|
||||
o.modalonly = true;
|
||||
|
||||
// Crypto Proposals
|
||||
s = m.section(form.GridSection, 'crypto_proposal',
|
||||
_('Encryption Proposals'),
|
||||
_('Configure Cipher Suites to define IKE (Phase 1) or ESP (Phase 2) Proposals.'));
|
||||
s.addremove = true;
|
||||
s.nodescriptions = true;
|
||||
s.renderSectionAdd = sectionNameCheck;
|
||||
|
||||
o = s.option(form.Flag, 'is_esp', _('ESP Proposal'),
|
||||
_('Whether this is an ESP (phase 2) proposal or not'));
|
||||
|
||||
o = s.option(form.ListValue, 'encryption_algorithm',
|
||||
_('Encryption Algorithm'),
|
||||
_('Algorithms marked with * are considered insecure'));
|
||||
o.default = 'aes256gcm128';
|
||||
addAlgorithms(o, strongswan_algorithms.getEncryptionAlgorithms());
|
||||
addAlgorithms(o, strongswan_algorithms.getAuthenticatedEncryptionAlgorithms());
|
||||
|
||||
|
||||
o = s.option(form.ListValue, 'hash_algorithm', _('Hash Algorithm'),
|
||||
_('Algorithms marked with * are considered insecure'));
|
||||
strongswan_algorithms.getEncryptionAlgorithms().forEach(function (algorithm) {
|
||||
o.depends('encryption_algorithm', algorithm);
|
||||
});
|
||||
o.default = 'sha512';
|
||||
o.rmempty = false;
|
||||
addAlgorithms(o, strongswan_algorithms.getHashAlgorithms());
|
||||
|
||||
o = s.option(form.ListValue, 'dh_group', _('Diffie-Hellman Group'),
|
||||
_('Algorithms marked with * are considered insecure'));
|
||||
o.default = 'modp3072';
|
||||
addAlgorithms(o, strongswan_algorithms.getDiffieHellmanAlgorithms());
|
||||
|
||||
o = s.option(form.ListValue, 'prf_algorithm', _('PRF Algorithm'),
|
||||
_('Algorithms marked with * are considered insecure'));
|
||||
o.validate = function (section_id, value) {
|
||||
var encryptionAlgorithm = this.section.formvalue(section_id, 'encryption_algorithm');
|
||||
|
||||
if (strongswan_algorithms.getAuthenticatedEncryptionAlgorithms().includes(
|
||||
encryptionAlgorithm) && !value) {
|
||||
return _('PRF Algorithm must be configured when using an Authenticated Encryption Algorithm');
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
o.optional = true;
|
||||
o.depends('is_esp', '0');
|
||||
addAlgorithms(o, strongswan_algorithms.getPrfAlgorithms());
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
@ -1,28 +0,0 @@
|
||||
{
|
||||
"admin/vpn/strongswan-swanctl": {
|
||||
"title": "strongSwan IPsec",
|
||||
"order": 90,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "strongswan-swanctl/swanctl"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [
|
||||
"luci-app-strongswan-swanctl"
|
||||
]
|
||||
}
|
||||
},
|
||||
"admin/status/strongswan": {
|
||||
"title": "strongSwan IPsec",
|
||||
"order": 90,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "strongswan-swanctl/status"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [
|
||||
"luci-app-strongswan-swanctl"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
{
|
||||
"luci-app-strongswan-swanctl": {
|
||||
"description": "Grant access to luci-app-strongswan-swanctl",
|
||||
"read": {
|
||||
"file": {
|
||||
"/usr/sbin/swanmon version": [ "exec" ],
|
||||
"/usr/sbin/swanmon stats": [ "exec" ],
|
||||
"/usr/sbin/swanmon list-sas": [ "exec" ]
|
||||
},
|
||||
"uci": [ "ipsec" ]
|
||||
},
|
||||
"write": {
|
||||
"uci": [ "ipsec" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=LuCI support for WiFi Station History
|
||||
LUCI_DEPENDS:=+luci-base +rpcd-mod-iwinfo +ucode +ucode-mod-fs +ucode-mod-ubus
|
||||
LUCI_DESCRIPTION:=Track and display history of WiFi associated stations
|
||||
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
@ -1,181 +0,0 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require poll';
|
||||
'require rpc';
|
||||
'require ui';
|
||||
|
||||
const callGetHistory = rpc.declare({
|
||||
object: 'luci.wifihistory',
|
||||
method: 'getHistory',
|
||||
expect: { history: {} }
|
||||
});
|
||||
|
||||
const callClearHistory = rpc.declare({
|
||||
object: 'luci.wifihistory',
|
||||
method: 'clearHistory',
|
||||
expect: { result: true }
|
||||
});
|
||||
|
||||
function formatDate(epoch) {
|
||||
if (!epoch || epoch <= 0)
|
||||
return '-';
|
||||
|
||||
return new Date(epoch * 1000).toLocaleString();
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load() {
|
||||
return callGetHistory();
|
||||
},
|
||||
|
||||
updateTable(table, history) {
|
||||
const stations = Object.values(history);
|
||||
|
||||
/* Sort connected stations first, then by most recently seen */
|
||||
stations.sort((a, b) => {
|
||||
if (a.connected !== b.connected)
|
||||
return a.connected ? -1 : 1;
|
||||
|
||||
return (b.last_seen || 0) - (a.last_seen || 0);
|
||||
});
|
||||
|
||||
const rows = [];
|
||||
|
||||
for (const s of stations) {
|
||||
let hint;
|
||||
if (s.hostname && s.ipv4 && s.ipv6)
|
||||
hint = '%s (%s, %s)'.format(s.hostname, s.ipv4, s.ipv6);
|
||||
else if (s.hostname && (s.ipv4 || s.ipv6))
|
||||
hint = '%s (%s)'.format(s.hostname, s.ipv4 || s.ipv6);
|
||||
else if (s.ipv4 || s.ipv6)
|
||||
hint = s.ipv4 || s.ipv6;
|
||||
else
|
||||
hint = '-';
|
||||
|
||||
let sig_value = '-';
|
||||
let sig_title = '';
|
||||
|
||||
if (s.signal && s.signal !== 0) {
|
||||
if (s.noise && s.noise !== 0) {
|
||||
sig_value = '%d/%d\xa0%s'.format(s.signal, s.noise, _('dBm'));
|
||||
sig_title = '%s: %d %s / %s: %d %s / %s %d'.format(
|
||||
_('Signal'), s.signal, _('dBm'),
|
||||
_('Noise'), s.noise, _('dBm'),
|
||||
_('SNR'), s.signal - s.noise);
|
||||
}
|
||||
else {
|
||||
sig_value = '%d\xa0%s'.format(s.signal, _('dBm'));
|
||||
sig_title = '%s: %d %s'.format(_('Signal'), s.signal, _('dBm'));
|
||||
}
|
||||
}
|
||||
|
||||
let icon;
|
||||
if (!s.connected) {
|
||||
icon = L.resource('icons/signal-none.svg');
|
||||
}
|
||||
else {
|
||||
/* Estimate signal quality as percentage:
|
||||
* Map dBm range [-110, -40] to [0%, 100%] */
|
||||
const q = Math.min((s.signal + 110) / 70 * 100, 100);
|
||||
if (q == 0)
|
||||
icon = L.resource('icons/signal-000-000.svg');
|
||||
else if (q < 25)
|
||||
icon = L.resource('icons/signal-000-025.svg');
|
||||
else if (q < 50)
|
||||
icon = L.resource('icons/signal-025-050.svg');
|
||||
else if (q < 75)
|
||||
icon = L.resource('icons/signal-050-075.svg');
|
||||
else
|
||||
icon = L.resource('icons/signal-075-100.svg');
|
||||
}
|
||||
|
||||
rows.push([
|
||||
E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'style': s.connected ? '' : 'opacity:0.5',
|
||||
'title': s.connected ? _('Connected') : _('Disconnected')
|
||||
}, [
|
||||
E('img', { 'src': icon, 'style': 'width:16px;height:16px' }),
|
||||
E('span', {}, [ ' ', s.connected ? _('Yes') : _('No') ])
|
||||
]),
|
||||
s.mac,
|
||||
hint,
|
||||
s.network || '-',
|
||||
E('span', { 'title': sig_title }, sig_value),
|
||||
formatDate(s.first_seen),
|
||||
formatDate(s.last_seen)
|
||||
]);
|
||||
}
|
||||
|
||||
cbi_update_table(table, rows, E('em', _('No station history available')));
|
||||
},
|
||||
|
||||
handleClearHistory(ev) {
|
||||
return ui.showModal(_('Clear Station History'), [
|
||||
E('p', _('This will permanently delete all recorded station history. Are you sure?')),
|
||||
E('div', { 'class': 'right' }, [
|
||||
E('button', {
|
||||
'class': 'btn',
|
||||
'click': ui.hideModal
|
||||
}, _('Cancel')), ' ',
|
||||
E('button', {
|
||||
'class': 'btn cbi-button-negative',
|
||||
'click': ui.createHandlerFn(this, function() {
|
||||
return callClearHistory().then(L.bind(function() {
|
||||
ui.hideModal();
|
||||
return callGetHistory().then(L.bind(function(history) {
|
||||
this.updateTable('#wifi_history_table', history);
|
||||
}, this));
|
||||
}, this));
|
||||
})
|
||||
}, _('Clear'))
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
render(history) {
|
||||
const isReadonlyView = !L.hasViewPermission();
|
||||
|
||||
const v = E([], [
|
||||
E('h2', _('Station History')),
|
||||
E('div', { 'class': 'cbi-map-descr' },
|
||||
_('This page displays a history of all WiFi stations that have connected to this device, including currently connected and previously seen devices.')),
|
||||
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('div', { 'class': 'right', 'style': 'margin-bottom:1em' }, [
|
||||
E('button', {
|
||||
'class': 'btn cbi-button-negative',
|
||||
'disabled': isReadonlyView,
|
||||
'click': ui.createHandlerFn(this, 'handleClearHistory')
|
||||
}, _('Clear History'))
|
||||
]),
|
||||
|
||||
E('table', { 'class': 'table', 'id': 'wifi_history_table' }, [
|
||||
E('tr', { 'class': 'tr table-titles' }, [
|
||||
E('th', { 'class': 'th' }, _('Connected')),
|
||||
E('th', { 'class': 'th' }, _('MAC address')),
|
||||
E('th', { 'class': 'th' }, _('Host')),
|
||||
E('th', { 'class': 'th' }, _('Network')),
|
||||
E('th', { 'class': 'th' }, '%s / %s'.format(_('Signal'), _('Noise'))),
|
||||
E('th', { 'class': 'th' }, _('First seen')),
|
||||
E('th', { 'class': 'th' }, _('Last seen'))
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
this.updateTable(v.querySelector('#wifi_history_table'), history);
|
||||
|
||||
poll.add(L.bind(function() {
|
||||
return callGetHistory().then(L.bind(function(history) {
|
||||
this.updateTable('#wifi_history_table', history);
|
||||
}, this));
|
||||
}, this), 5);
|
||||
|
||||
return v;
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
||||
@ -1,82 +0,0 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"PO-Revision-Date: 2026-05-03 08:19+0000\n"
|
||||
"Last-Translator: BoneNI <bounkirdni@gmail.com>\n"
|
||||
"Language-Team: Lao <https://hosted.weblate.org/projects/openwrt/"
|
||||
"luciapplicationswifihistory/lo/>\n"
|
||||
"Language: lo\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Weblate 5.17.1\n"
|
||||
|
||||
#~ msgid "Cancel"
|
||||
#~ msgstr "ຍົກເລີກ"
|
||||
|
||||
#~ msgid "Clear"
|
||||
#~ msgstr "ລ້າງ"
|
||||
|
||||
#~ msgid "Clear History"
|
||||
#~ msgstr "ລ້າງປະຫວັດ"
|
||||
|
||||
#~ msgid "Clear Station History"
|
||||
#~ msgstr "ລ້າງປະຫວັດສະຖານີ"
|
||||
|
||||
#~ msgid "Connected"
|
||||
#~ msgstr "ເຊື່ອມຕໍ່ແລ້ວ"
|
||||
|
||||
#~ msgid "Disconnected"
|
||||
#~ msgstr "ຕັດການເຊື່ອມຕໍ່ແລ້ວ"
|
||||
|
||||
#~ msgid "First seen"
|
||||
#~ msgstr "ເຫັນຄັ້ງທຳອິດ"
|
||||
|
||||
#~ msgid "Grant access to WiFi station history"
|
||||
#~ msgstr "ອະນຸຍາດໃຫ້ເຂົ້າເຖິງປະຫວັດສະຖານີ WiFi"
|
||||
|
||||
#~ msgid "Host"
|
||||
#~ msgstr "ໂຮສ (Host)"
|
||||
|
||||
#~ msgid "Last seen"
|
||||
#~ msgstr "ເຫັນຄັ້ງສຸດທ້າຍ"
|
||||
|
||||
#~ msgid "MAC address"
|
||||
#~ msgstr "ທີ່ຢູ່ MAC"
|
||||
|
||||
#~ msgid "Network"
|
||||
#~ msgstr "ເຄືອຂ່າຍ"
|
||||
|
||||
#~ msgid "No"
|
||||
#~ msgstr "ບໍ່"
|
||||
|
||||
#~ msgid "No station history available"
|
||||
#~ msgstr "ບໍ່ມີຂໍ້ມູນປະຫວັດສະຖານີ"
|
||||
|
||||
#~ msgid "Noise"
|
||||
#~ msgstr "ສັນຍານລົບກວນ"
|
||||
|
||||
#~ msgid "SNR"
|
||||
#~ msgstr "SNR"
|
||||
|
||||
#~ msgid "Signal"
|
||||
#~ msgstr "ສັນຍານ"
|
||||
|
||||
#~ msgid "Station History"
|
||||
#~ msgstr "ປະຫວັດສະຖານີ"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "This page displays a history of all WiFi stations that have connected to "
|
||||
#~ "this device, including currently connected and previously seen devices."
|
||||
#~ msgstr ""
|
||||
#~ "ໜ້ານີ້ສະແດງປະຫວັດຂອງທຸກສະຖານີ WiFi ທີ່ເຄີຍເຊື່ອມຕໍ່ກັບອຸປະກອນນີ້, ລວມທັງອຸປະກອນທີ່ກຳລັງເຊື່ອມຕໍ່ຢູ່ "
|
||||
#~ "ແລະ ອຸປະກອນທີ່ເຄີຍເຫັນກ່ອນໜ້ານີ້."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "This will permanently delete all recorded station history. Are you sure?"
|
||||
#~ msgstr "ນີ້ຈະເປັນການລຶບປະຫວັດສະຖານີທີ່ບັນທຶກໄວ້ທັງໝົດແບບຖາວອນ. ທ່ານແນ່ໃຈຫຼືບໍ່?"
|
||||
|
||||
#~ msgid "Yes"
|
||||
#~ msgstr "ແມ່ນ"
|
||||
|
||||
#~ msgid "dBm"
|
||||
#~ msgstr "dBm"
|
||||
@ -1,24 +0,0 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=99
|
||||
STOP=10
|
||||
USE_PROCD=1
|
||||
|
||||
NAME=wifihistory
|
||||
PROG=/usr/sbin/wifihistory
|
||||
INTERVAL=30
|
||||
|
||||
start_service() {
|
||||
mkdir -p /var/lib/wifihistory
|
||||
|
||||
procd_open_instance "$NAME"
|
||||
procd_set_param command /bin/sh -c "while true; do /usr/bin/ucode $PROG; sleep $INTERVAL; done"
|
||||
procd_set_param respawn 3600 5 5
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "wireless"
|
||||
}
|
||||
@ -1,124 +0,0 @@
|
||||
#!/usr/bin/env ucode
|
||||
|
||||
'use strict';
|
||||
|
||||
import { readfile, writefile, open, mkdir, rename } from 'fs';
|
||||
import { connect } from 'ubus';
|
||||
|
||||
const HISTORY_DIR = '/var/lib/wifihistory';
|
||||
const HISTORY_FILE = HISTORY_DIR + '/history.json';
|
||||
const LOCK_FILE = '/var/lock/wifihistory.lock';
|
||||
|
||||
function load_history() {
|
||||
let content = readfile(HISTORY_FILE);
|
||||
if (content == null)
|
||||
return {};
|
||||
|
||||
try {
|
||||
return json(content) || {};
|
||||
}
|
||||
catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function save_history(data) {
|
||||
let tmp = HISTORY_FILE + '.tmp';
|
||||
|
||||
writefile(tmp, sprintf('%J', data));
|
||||
rename(tmp, HISTORY_FILE);
|
||||
}
|
||||
|
||||
function poll_stations() {
|
||||
let ubus = connect();
|
||||
if (!ubus) {
|
||||
warn('Failed to connect to ubus\n');
|
||||
return;
|
||||
}
|
||||
|
||||
let now = time();
|
||||
let history = load_history();
|
||||
let seen_macs = {};
|
||||
|
||||
let wifi_status = ubus.call('network.wireless', 'status');
|
||||
if (!wifi_status)
|
||||
return;
|
||||
|
||||
let hints = ubus.call('luci-rpc', 'getHostHints') || {};
|
||||
|
||||
for (let radio in wifi_status) {
|
||||
let ifaces = wifi_status[radio]?.interfaces;
|
||||
if (!ifaces)
|
||||
continue;
|
||||
|
||||
for (let iface in ifaces) {
|
||||
let ifname = iface?.ifname;
|
||||
if (!ifname)
|
||||
continue;
|
||||
|
||||
let info = ubus.call('iwinfo', 'info', { device: ifname });
|
||||
let ssid = info?.ssid || '';
|
||||
|
||||
let assoc = ubus.call('iwinfo', 'assoclist', { device: ifname });
|
||||
if (!assoc?.results)
|
||||
continue;
|
||||
|
||||
for (let bss in assoc.results) {
|
||||
let mac = bss?.mac;
|
||||
if (!mac)
|
||||
continue;
|
||||
|
||||
mac = uc(mac);
|
||||
seen_macs[mac] = true;
|
||||
|
||||
let hostname = hints?.[mac]?.name || '';
|
||||
let ipv4 = hints?.[mac]?.ipaddrs?.[0] || '';
|
||||
let ipv6 = hints?.[mac]?.ip6addrs?.[0] || '';
|
||||
|
||||
let existing = history[mac];
|
||||
let first_seen = existing?.first_seen || now;
|
||||
|
||||
history[mac] = {
|
||||
mac: mac,
|
||||
hostname: hostname,
|
||||
ipv4: ipv4,
|
||||
ipv6: ipv6,
|
||||
network: ssid,
|
||||
ifname: ifname,
|
||||
connected: true,
|
||||
signal: bss?.signal || 0,
|
||||
noise: bss?.noise || 0,
|
||||
first_seen: first_seen,
|
||||
last_seen: now
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let mac in history)
|
||||
if (!seen_macs[mac])
|
||||
history[mac].connected = false;
|
||||
|
||||
ubus.disconnect();
|
||||
|
||||
save_history(history);
|
||||
}
|
||||
|
||||
mkdir(HISTORY_DIR);
|
||||
|
||||
let lock_fd = open(LOCK_FILE, 'w');
|
||||
if (!lock_fd) {
|
||||
warn('Failed to open lock file\n');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!lock_fd.lock('xn')) {
|
||||
warn('Another instance is already running\n');
|
||||
lock_fd.close();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
poll_stations();
|
||||
|
||||
lock_fd.lock('u');
|
||||
lock_fd.close();
|
||||
@ -1,14 +0,0 @@
|
||||
{
|
||||
"admin/status/wifihistory": {
|
||||
"title": "Station History",
|
||||
"order": 8,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "status/wifihistory"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-app-wifihistory" ],
|
||||
"uci": { "wireless": { "@wifi-device": true } }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
{
|
||||
"luci-app-wifihistory": {
|
||||
"description": "Grant access to WiFi station history",
|
||||
"read": {
|
||||
"ubus": {
|
||||
"iwinfo": [ "assoclist" ],
|
||||
"luci.wifihistory": [ "getHistory" ]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"ubus": {
|
||||
"luci.wifihistory": [ "clearHistory" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
#!/usr/bin/env ucode
|
||||
|
||||
'use strict';
|
||||
|
||||
import { readfile, writefile } from 'fs';
|
||||
|
||||
const HISTORY_FILE = '/var/lib/wifihistory/history.json';
|
||||
|
||||
const methods = {
|
||||
getHistory: {
|
||||
call: function() {
|
||||
let content = readfile(HISTORY_FILE);
|
||||
if (content == null)
|
||||
return { history: {} };
|
||||
|
||||
try {
|
||||
return { history: json(content) || {} };
|
||||
}
|
||||
catch (e) {
|
||||
return { history: {} };
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
clearHistory: {
|
||||
call: function() {
|
||||
writefile(HISTORY_FILE, '{}');
|
||||
return { result: true };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return { 'luci.wifihistory': methods };
|
||||
@ -11,7 +11,7 @@ PKG_NAME:=luci-theme-$(THEME_NAME)
|
||||
LUCI_TITLE:=Kucat Theme by sirpdboy
|
||||
LUCI_DEPENDS:=+wget +curl +jsonfilter
|
||||
PKG_VERSION:=3.3.2
|
||||
PKG_RELEASE:=2
|
||||
PKG_RELEASE:=3
|
||||
|
||||
define Package/luci-theme-$(THEME_NAME)/conffiles
|
||||
/www/luci-static/resources/background/
|
||||
|
||||
@ -1428,7 +1428,6 @@ button:hover,
|
||||
.btn:hover,
|
||||
.cbi-button:hover
|
||||
{
|
||||
color: var(--menu-hover-color);
|
||||
transform: translateY(-2px) ;
|
||||
box-shadow: 0 0.3rem 0.5rem var(--input-boxhovercolor);
|
||||
-webkit-box-shadow: 0 0.3rem 0.5rem var(--input-boxhovercolor);
|
||||
@ -1463,6 +1462,32 @@ button:active {
|
||||
box-shadow: none
|
||||
}
|
||||
|
||||
|
||||
.modal label.btn {
|
||||
color: var(--body-color);
|
||||
background-color: rgba(var(--primary-rgbm), 0);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
border: none;
|
||||
background-image: none;
|
||||
box-shadow: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
.modal label.btn:focus,
|
||||
.modal label.btn:hover
|
||||
{
|
||||
color: var(--body-color);
|
||||
background-color: rgba(var(--primary-rgbm), 0.1);
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.cbi-dynlist>.add-item:not([ondrop])>input,
|
||||
.cbi-dynlist>.item>span,
|
||||
.cbi-dynlist>.add-item input {
|
||||
@ -2690,7 +2715,7 @@ td>.ifacebadge,
|
||||
}
|
||||
|
||||
.ifacebox-head {
|
||||
width: 7rem;
|
||||
width: 6rem;
|
||||
min-width: 100%;
|
||||
background-color: rgba(var(--primary-rgbm), 0.3);
|
||||
transform: translate(-50, -50%);
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=qBittorrent-Enhanced-Edition
|
||||
PKG_VERSION:=5.2.1.10
|
||||
PKG_RELEASE:=11
|
||||
PKG_VERSION:=5.2.3.10
|
||||
PKG_RELEASE:=12
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/c0re100/qBittorrent-Enhanced-Edition/tar.gz/release-$(PKG_VERSION)?
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
--- a/src/base/bittorrent/sessionimpl.cpp
|
||||
+++ b/src/base/bittorrent/sessionimpl.cpp
|
||||
@@ -128,7 +128,7 @@ const std::chrono::seconds FREEDISKSPACE
|
||||
@@ -129,7 +129,7 @@ const std::chrono::seconds FREEDISKSPACE
|
||||
namespace
|
||||
{
|
||||
const char PEER_ID[] = "qB";
|
||||
@ -14,7 +14,7 @@
|
||||
@@ -31,7 +31,7 @@
|
||||
#define QBT_VERSION_MAJOR 5
|
||||
#define QBT_VERSION_MINOR 2
|
||||
#define QBT_VERSION_BUGFIX 1
|
||||
#define QBT_VERSION_BUGFIX 3
|
||||
-#define QBT_VERSION_BUILD 10
|
||||
+#define QBT_VERSION_BUILD 0
|
||||
#define QBT_VERSION_STATUS "" // Should be empty for stable releases!
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=rustdesk-server
|
||||
PKG_VERSION:=1.1.15
|
||||
PKG_RELEASE:=2
|
||||
PKG_VERSION:=1.1.16
|
||||
PKG_RELEASE:=3
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://github.com/rustdesk/rustdesk-server.git
|
||||
|
||||
@ -9,9 +9,9 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=watchcat
|
||||
PKG_VERSION:=1
|
||||
PKG_RELEASE:=4
|
||||
PKG_RELEASE:=5
|
||||
|
||||
PKG_MAINTAINER:=Roger D <rogerdammit@gmail.com>
|
||||
PKG_MAINTAINER:=Daniel F. Dickinson <dfdpublic@wildtechgarden.ca>, Dharmik Parmar <dharmikparmar2004@yahoo.com>
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
@ -7,6 +7,77 @@
|
||||
#
|
||||
|
||||
. /lib/network/config.sh
|
||||
. /lib/functions/network.sh
|
||||
|
||||
# Accept the historical real-device input while also handling @logical
|
||||
# interface references used by other OpenWrt configs.
|
||||
watchcat_resolve_ping_iface() {
|
||||
local iface="$1"
|
||||
local logical device network
|
||||
|
||||
[ -n "$iface" ] || return 1
|
||||
|
||||
case "$iface" in
|
||||
@*)
|
||||
logical="${iface#@}"
|
||||
[ -n "$logical" ] || return 1
|
||||
if network_get_device device "$logical"; then
|
||||
printf '%s\n' "$device"
|
||||
return 0
|
||||
fi
|
||||
printf '%s\n' "$iface"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
network="$(find_config "$iface")"
|
||||
if [ -n "$network" ]; then
|
||||
printf '%s\n' "$iface"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if network_get_device device "$iface"; then
|
||||
printf '%s\n' "$device"
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf '%s\n' "$iface"
|
||||
return 1
|
||||
}
|
||||
|
||||
watchcat_resolve_restart_iface() {
|
||||
local iface="$1"
|
||||
local network device
|
||||
|
||||
[ -n "$iface" ] || return 1
|
||||
|
||||
case "$iface" in
|
||||
@*)
|
||||
network="${iface#@}"
|
||||
[ -n "$network" ] || return 1
|
||||
if ! network_get_device device "$network"; then
|
||||
printf '%s\n' "$network"
|
||||
return 1
|
||||
fi
|
||||
printf '%s\n' "$network"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
network="$(find_config "$iface")"
|
||||
if [ -n "$network" ]; then
|
||||
printf '%s\n' "$network"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if network_get_device device "$iface"; then
|
||||
printf '%s\n' "$iface"
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf '%s\n' "$iface"
|
||||
return 1
|
||||
}
|
||||
|
||||
get_ping_size() {
|
||||
ps=$1
|
||||
@ -84,8 +155,11 @@ watchcat_restart_modemmanager_iface() {
|
||||
}
|
||||
|
||||
watchcat_restart_network_iface() {
|
||||
local network
|
||||
network="$(find_config "$1")"
|
||||
local iface="$1"
|
||||
local network="$2"
|
||||
|
||||
[ -z "$network" ] && network="$(watchcat_resolve_restart_iface "$iface")"
|
||||
|
||||
logger -p daemon.info -t "watchcat[$$]" "Restarting network interface: \"$1\" (network: \"$network\")."
|
||||
ifup "$network"
|
||||
}
|
||||
@ -110,11 +184,26 @@ watchcat_monitor_network() {
|
||||
mm_iface_unlock_bands="$7"
|
||||
address_family="$8"
|
||||
script="$9"
|
||||
ping_iface=""
|
||||
restart_iface=""
|
||||
reset_failure_timer=""
|
||||
if [ "$#" -gt 9 ]; then
|
||||
shift 9
|
||||
reset_failure_timer="$1"
|
||||
fi
|
||||
[ "$mm_iface_name" = "null" ] && mm_iface_name=""
|
||||
if [ "$iface" != "" ]; then
|
||||
if ! ping_iface="$(watchcat_resolve_ping_iface "$iface")"; then
|
||||
logger -p daemon.warn -t "watchcat[$$]" "Could not resolve interface \"$iface\" for pinging."
|
||||
case "$iface" in
|
||||
@*) ping_iface="" ;;
|
||||
*) ping_iface="$iface" ;;
|
||||
esac
|
||||
fi
|
||||
if ! restart_iface="$(watchcat_resolve_restart_iface "$iface")"; then
|
||||
logger -p daemon.warn -t "watchcat[$$]" "Could not resolve interface \"$iface\" for restart."
|
||||
fi
|
||||
fi
|
||||
|
||||
time_now="$(cat /proc/uptime)"
|
||||
time_now="${time_now%%.*}"
|
||||
@ -143,9 +232,9 @@ watchcat_monitor_network() {
|
||||
time_lastcheck="$time_now"
|
||||
|
||||
for host in $ping_hosts; do
|
||||
if [ "$iface" != "" ]; then
|
||||
if [ "$ping_iface" != "" ]; then
|
||||
ping_result="$(
|
||||
ping $ping_family -I "$iface" -s "$ping_size" -c 1 "$host" &> /dev/null
|
||||
ping $ping_family -I "$ping_iface" -s "$ping_size" -c 1 "$host" &> /dev/null
|
||||
echo $?
|
||||
)"
|
||||
else
|
||||
@ -178,7 +267,7 @@ watchcat_monitor_network() {
|
||||
watchcat_restart_modemmanager_iface "$mm_iface_name" "$mm_iface_unlock_bands"
|
||||
fi
|
||||
if [ "$iface" != "" ]; then
|
||||
watchcat_restart_network_iface "$iface"
|
||||
watchcat_restart_network_iface "$iface" "$restart_iface"
|
||||
else
|
||||
watchcat_restart_all_network
|
||||
fi
|
||||
@ -207,6 +296,16 @@ watchcat_ping() {
|
||||
ping_size="$5"
|
||||
address_family="$6"
|
||||
iface="$7"
|
||||
ping_iface=""
|
||||
if [ "$iface" != "" ]; then
|
||||
if ! ping_iface="$(watchcat_resolve_ping_iface "$iface")"; then
|
||||
logger -p daemon.warn -t "watchcat[$$]" "Could not resolve interface \"$iface\" for pinging."
|
||||
case "$iface" in
|
||||
@*) ping_iface="" ;;
|
||||
*) ping_iface="$iface" ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
time_now="$(cat /proc/uptime)"
|
||||
time_now="${time_now%%.*}"
|
||||
@ -235,9 +334,9 @@ watchcat_ping() {
|
||||
time_lastcheck="$time_now"
|
||||
|
||||
for host in $ping_hosts; do
|
||||
if [ "$iface" != "" ]; then
|
||||
if [ "$ping_iface" != "" ]; then
|
||||
ping_result="$(
|
||||
ping $ping_family -I "$iface" -s "$ping_size" -c 1 "$host" &> /dev/null
|
||||
ping $ping_family -I "$ping_iface" -s "$ping_size" -c 1 "$host" &> /dev/null
|
||||
echo $?
|
||||
)"
|
||||
else
|
||||
|
||||
Loading…
Reference in New Issue
Block a user