mirror of
https://github.com/openwrt/luci.git
synced 2026-06-17 14:50:21 +04:00
luci-app-strongswan-swanctl: merge SAs and connections
Merges strongSwan conns and SAs into a detailed table of connections and a table of children for each of them. Signed-off-by: Lukas Voegl <lvoegl@tdt.de>
This commit is contained in:
committed by
Florian Eckert
parent
a65bf6d132
commit
55a4b33aaf
+251
-60
@@ -48,6 +48,213 @@ function buildKeyValueTable(kvPairs) {
|
||||
return buildTable(rows);
|
||||
}
|
||||
|
||||
function mapConnectionSas(conns, sas) {
|
||||
/* map connections to SAs and connection children to SA children */
|
||||
const connMap = new Map();
|
||||
const childConnMap = new Map();
|
||||
conns.forEach(function (connObject) {
|
||||
const [connName, conn] = Object.entries(connObject)[0];
|
||||
connMap.set(connName, conn);
|
||||
Object.entries(conn.children).forEach(function ([childName, child]) {
|
||||
childConnMap.set(childName, child);
|
||||
});
|
||||
});
|
||||
|
||||
sas.forEach(function (saObject) {
|
||||
const [saName, sa] = Object.entries(saObject)[0];
|
||||
const connection = connMap.get(saName);
|
||||
if (connection) {
|
||||
connection.childSa = sa;
|
||||
}
|
||||
|
||||
Object.entries(sa['child-sas']).forEach(function ([childSaName, childSa]) {
|
||||
const childConnection = childConnMap.get(childSaName);
|
||||
if (childConnection) {
|
||||
childConnection.childSa = childSa;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return conns;
|
||||
}
|
||||
|
||||
function swanctlCommand(parameters) {
|
||||
return fs.exec('/usr/sbin/swanctl', parameters)
|
||||
.catch(e => ui.addNotification(null, E('p', e.message)));
|
||||
}
|
||||
|
||||
function handleConnectionUp(connectionName) {
|
||||
return swanctlCommand(['--initiate', '--ike', connectionName]);
|
||||
}
|
||||
|
||||
function handleConnectionDown(connectionName) {
|
||||
return swanctlCommand(['--terminate', '--ike', connectionName]);
|
||||
}
|
||||
|
||||
function handleChildUp(childName) {
|
||||
return swanctlCommand(['--initiate', '--child', childName]);
|
||||
}
|
||||
|
||||
function handleChildDown(childName) {
|
||||
return swanctlCommand(['--terminate', '--child', childName]);
|
||||
}
|
||||
|
||||
function renderDetailsSection(connection, connectionName) {
|
||||
const sa = connection.sa;
|
||||
|
||||
return buildSection(_('Details'), buildKeyValueTable([
|
||||
[_('Name'), connectionName],
|
||||
[_('Unique ID'), sa ? sa.uniqueid : ''],
|
||||
[_('Local Addresses'), connection.local_addrs.join(', ')],
|
||||
[_('Remote Addresses'), connection.remote_addrs.join(', ')],
|
||||
[_('Local Port'), connection.local_port],
|
||||
[_('Remote Port'), connection.remote_port],
|
||||
[_('Version'), connection.version],
|
||||
[_('Reauthentication Interval'), _('%d seconds').format(connection.reauth_time)],
|
||||
[_('Rekeying Interval'), _('%d seconds').format(connection.rekey_time)],
|
||||
[_('Established'), sa ? formatTime(parseInt(sa.established), 3) : '']
|
||||
]));
|
||||
}
|
||||
|
||||
function handleChildDetails(childName, child, childSa) {
|
||||
const modal = buildSection(_('Details'), buildKeyValueTable([
|
||||
[_('Name'), childName],
|
||||
[_('Mode'), child.mode],
|
||||
[_('Protocol'), childSa ? childSa.protocol : ''],
|
||||
[_('Local Traffic Selectors'), child['local-ts'].join(', ')],
|
||||
[_('Remote Traffic Selectors'), child['remote-ts'].join(', ')],
|
||||
[_('Rekey in'), childSa ? _('%d seconds').format(childSa['rekey-time']) : ''],
|
||||
[_('Encryption Algorithm'), childSa ? childSa['encr-alg'] : ''],
|
||||
[_('Encryption Keysize'), childSa ? childSa['encr-keysize'] : ''],
|
||||
[_('Bytes in'), childSa ? childSa['bytes-in'] : ''],
|
||||
[_('Bytes out'), childSa ? childSa['bytes-out'] : ''],
|
||||
[_('Life Time'), childSa ? formatTime(parseInt(childSa['life-time']), 2) : ''],
|
||||
[_('Install Time'), childSa ? formatTime(parseInt(childSa['install-time']), 2) : ''],
|
||||
[_('SPI in'), childSa ? childSa['spi-in'] : ''],
|
||||
[_('SPI out'), childSa ? childSa['spi-out'] : '']
|
||||
]));
|
||||
|
||||
ui.showModal(_('Child Details'), [modal, E('div', { 'class': 'right' }, [
|
||||
E('button', {
|
||||
'class': 'btn cbi-button',
|
||||
'click': ui.hideModal
|
||||
}, [_('Dismiss')])
|
||||
])], 'cbi-modal');
|
||||
}
|
||||
|
||||
function renderChildTable(children) {
|
||||
const tableHeaders = [
|
||||
[_('Name')],
|
||||
[_('State')],
|
||||
[_('Mode')],
|
||||
[_('Protocol')],
|
||||
[_('Local Traffic Selectors')],
|
||||
[_('Remote Traffic Selectors')],
|
||||
[_('Rekey in')],
|
||||
[/* details button */],
|
||||
[/* up/down button */]
|
||||
]
|
||||
const childTableRows = [
|
||||
E('tr', { 'class': 'tr table-titles' }, tableHeaders.map(
|
||||
header => E('th', { 'class': 'th' }, header)
|
||||
))
|
||||
];
|
||||
|
||||
Object.entries(children).forEach(([childName, child]) => {
|
||||
const childSa = child.ChildSa;
|
||||
const state = childSa ? childSa.state : _('Inactive');
|
||||
const isDown = !childSa;
|
||||
|
||||
const tableValues = [
|
||||
[childName],
|
||||
[state],
|
||||
[child.mode],
|
||||
[childSa ? childSa.protocol : ''],
|
||||
[child['local-ts'].join(', ')],
|
||||
[child['remote-ts'].join(', ')],
|
||||
[childSa ? _('%d seconds').format(childSa['rekey-time']) : ''],
|
||||
[
|
||||
E('button', {
|
||||
'title': _('Details'),
|
||||
'class': 'btn cbi-button cbi-button-primary',
|
||||
'click': ui.createHandlerFn(null, handleChildDetails, childName, child, childSa)
|
||||
}, [_('Details')])
|
||||
],
|
||||
[
|
||||
E('button', {
|
||||
'title': _('Start'),
|
||||
'class': 'btn cbi-button cbi-button-positive',
|
||||
...(isDown ? {} : { 'disabled': 'disabled' }),
|
||||
'click': ui.createHandlerFn(null, handleChildUp, childName)
|
||||
}, [_('Start')]),
|
||||
E('button', {
|
||||
'title': _('Stop'),
|
||||
'class': 'btn cbi-button cbi-button-negative',
|
||||
...(isDown ? { 'disabled': 'disabled' } : {}),
|
||||
'click': ui.createHandlerFn(null, handleChildDown, childName)
|
||||
}, [_('Stop')])
|
||||
]
|
||||
];
|
||||
|
||||
childTableRows.push(E('tr', { 'class': 'tr' }, tableValues.map(
|
||||
value => E('td', { 'class': 'td' }, value)
|
||||
)));
|
||||
});
|
||||
|
||||
return E('table', { 'class': 'table' }, childTableRows);
|
||||
}
|
||||
|
||||
function renderAuthTable(auths) {
|
||||
const authTableRows = [
|
||||
E('tr', { 'class': 'tr table-titles' }, [
|
||||
E('th', { 'class': 'th' }, [_('Class')]),
|
||||
E('th', { 'class': 'th' }, [_('ID')])
|
||||
])
|
||||
];
|
||||
|
||||
auths.forEach(auth => {
|
||||
authTableRows.push(E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td' }, [auth.class]),
|
||||
E('td', { 'class': 'td' }, [auth.id || ''])
|
||||
]));
|
||||
});
|
||||
|
||||
return E('table', { 'class': 'table' }, authTableRows);
|
||||
}
|
||||
|
||||
function filterConnectionAuths(connection, prefix) {
|
||||
const auths = Object.entries(connection).filter(([key, value]) => key.startsWith(prefix));
|
||||
return auths.map(([key, value]) => value);
|
||||
}
|
||||
|
||||
function handleConnectionDetails(connection, connectionName) {
|
||||
const detailSection = renderDetailsSection(connection, connectionName);
|
||||
const childTable = renderChildTable(connection.children);
|
||||
const localAuths = filterConnectionAuths(connection, 'local-');
|
||||
const remoteAuths = filterConnectionAuths(connection, 'remote-');
|
||||
const localAuthTable = renderAuthTable(localAuths);
|
||||
const remoteAuthTable = renderAuthTable(remoteAuths);
|
||||
|
||||
const modal = E([], [E('div', {}, [
|
||||
E('div', { 'class': 'cbi-section', 'data-tab': 'details', 'data-tab-title': _('Connection') }, [
|
||||
detailSection,
|
||||
E('h3', _('Local Auth')),
|
||||
localAuthTable,
|
||||
E('h3', _('Remote Auth')),
|
||||
remoteAuthTable
|
||||
]),
|
||||
E('div', { 'class': 'cbi-section', 'data-tab': 'children', 'data-tab-title': _('Children') }, [childTable])
|
||||
])]);
|
||||
|
||||
ui.tabs.initTabGroup(modal.lastElementChild.childNodes);
|
||||
ui.showModal(_('Connection Details'), [modal, E('div', { 'class': 'right' }, [
|
||||
E('button', {
|
||||
'class': 'btn cbi-button',
|
||||
'click': ui.hideModal
|
||||
}, [_('Dismiss')])
|
||||
])], 'cbi-modal');
|
||||
}
|
||||
|
||||
function collectErrorMessages(results) {
|
||||
const errorMessages = results.reduce(function (messages, result) {
|
||||
return messages.concat(result.errors.map(function (error) {
|
||||
@@ -64,6 +271,7 @@ return view.extend({
|
||||
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-conns'], 'json'),
|
||||
fs.exec_direct('/usr/sbin/swanmon', ['list-sas'], 'json')
|
||||
]);
|
||||
},
|
||||
@@ -92,80 +300,63 @@ return view.extend({
|
||||
return node;
|
||||
}
|
||||
|
||||
const [version, stats, sas] = results.map(function (r) {
|
||||
const [version, stats, conns, sas] = results.map(function (r) {
|
||||
return r.data;
|
||||
});
|
||||
|
||||
const uptimeSeconds = (new Date() - new Date(stats.uptime.since)) / 1000;
|
||||
const statsSection = buildSection(_('Stats'), buildKeyValueTable([
|
||||
const overviewSection = buildSection(_('Overview'), 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);
|
||||
firstNode.appendChild(overviewSection);
|
||||
|
||||
const tableRows = sas.map(function (conn) {
|
||||
const name = Object.keys(conn)[0];
|
||||
const data = conn[name];
|
||||
const childSas = [];
|
||||
const connections = mapConnectionSas(conns, sas);
|
||||
const connectionTableRows = [
|
||||
E('tr', { 'class': 'tr table-titles' }, [
|
||||
E('th', { 'class': 'th' }, _('Name')),
|
||||
E('th', { 'class': 'th' }, _('State')),
|
||||
E('th', { 'class': 'th' }), /* details button */
|
||||
E('th', { 'class': 'th' }) /* up/down button */
|
||||
])
|
||||
];
|
||||
connections.forEach(function (connectionObject) {
|
||||
const [connectionName, connection] = Object.entries(connectionObject)[0];
|
||||
const state = connection.sa ? connection.sa.state : _('Inactive');
|
||||
const isDown = !connection.sa;
|
||||
|
||||
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'))])
|
||||
]);
|
||||
connectionTableRows.push(E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td' }, [connectionName]),
|
||||
E('td', { 'class': 'td' }, [state]),
|
||||
E('td', { 'class': 'td', 'width': '20%' }, [E('button', {
|
||||
'title': _('Details'),
|
||||
'class': 'btn cbi-button cbi-button-primary',
|
||||
'click': ui.createHandlerFn(null, handleConnectionDetails, connection, connectionName)
|
||||
}, [_('Details')])]),
|
||||
E('td', { 'class': 'td', 'width': '25%' }, [
|
||||
E('button', {
|
||||
'title': _('Start'),
|
||||
'class': 'btn cbi-button cbi-button-positive',
|
||||
...(isDown ? {} : { 'disabled': 'disabled' }),
|
||||
'click': ui.createHandlerFn(null, handleConnectionUp, connectionName)
|
||||
}, [_('Start')]),
|
||||
E('button', {
|
||||
'title': _('Stop'),
|
||||
'class': 'btn cbi-button cbi-button-negative',
|
||||
...(isDown ? { 'disabled': 'disabled' } : {}),
|
||||
'click': ui.createHandlerFn(null, handleConnectionDown, connectionName)
|
||||
}, [_('Stop')])
|
||||
])
|
||||
]));
|
||||
});
|
||||
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(E([
|
||||
E('h2', _('Connections')),
|
||||
E('table', { 'class': 'table' }, connectionTableRows)
|
||||
]));
|
||||
firstNode.appendChild(connSection);
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
+7
@@ -5,11 +5,18 @@
|
||||
"file": {
|
||||
"/usr/sbin/swanmon version": [ "exec" ],
|
||||
"/usr/sbin/swanmon stats": [ "exec" ],
|
||||
"/usr/sbin/swanmon list-conns": [ "exec" ],
|
||||
"/usr/sbin/swanmon list-sas": [ "exec" ]
|
||||
},
|
||||
"uci": [ "ipsec" ]
|
||||
},
|
||||
"write": {
|
||||
"file": {
|
||||
"/usr/sbin/swanctl --initiate --ike *": [ "exec" ],
|
||||
"/usr/sbin/swanctl --initiate --child *": [ "exec" ],
|
||||
"/usr/sbin/swanctl --terminate --ike *": [ "exec" ],
|
||||
"/usr/sbin/swanctl --terminate --child *": [ "exec" ]
|
||||
},
|
||||
"uci": [ "ipsec" ]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user