diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Wireguard/Api/ServiceController.php b/src/opnsense/mvc/app/controllers/OPNsense/Wireguard/Api/ServiceController.php
index 0d11d5722..7a97615be 100644
--- a/src/opnsense/mvc/app/controllers/OPNsense/Wireguard/Api/ServiceController.php
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Wireguard/Api/ServiceController.php
@@ -95,6 +95,26 @@ class ServiceController extends ApiMutableServiceControllerBase
$record['name'] = $key_descriptions[$key];
}
}
+
+ if (!empty($record['latest-handshake'])) {
+ $record['latest-handshake-age'] = time() - (int)$record['latest-handshake'];
+ $record['latest-handshake-epoch'] = date('Y-m-d H:i:s', (int)$record['latest-handshake']);
+ } else {
+ $record['latest-handshake-age'] = null;
+ $record['latest-handshake-epoch'] = null;
+ }
+
+ // Peer is considered online if handshake was within 300s, wg handshakes approx every 120s.
+ if ($record['type'] === 'peer' && !is_null($record['latest-handshake-age'])) {
+ if ($record['latest-handshake-age'] <= 300) {
+ $record['peer-status'] = 'online';
+ } elseif ($record['latest-handshake-age'] > 300) {
+ $record['peer-status'] = 'stale';
+ }
+ } else {
+ $record['peer-status'] = 'offline';
+ }
+
$record['ifname'] = $ifnames[$record['if']];
}
$filter_funct = null;
diff --git a/src/opnsense/mvc/app/views/OPNsense/Wireguard/diagnostics.volt b/src/opnsense/mvc/app/views/OPNsense/Wireguard/diagnostics.volt
index af3cf7631..35e885e93 100644
--- a/src/opnsense/mvc/app/views/OPNsense/Wireguard/diagnostics.volt
+++ b/src/opnsense/mvc/app/views/OPNsense/Wireguard/diagnostics.volt
@@ -41,13 +41,36 @@
return row[column.id];
},
epoch: function(column, row) {
- if (row[column.id]) {
- return moment.unix(row[column.id]).local().format('YYYY-MM-DD HH:mm:ss');
+ if (row[column.id] !== null) {
+ return row[column.id]
} else {
return '';
}
+ },
+ seconds: function(column, row) {
+ if (row[column.id] !== null) {
+ return row[column.id] + "s";
+ } else {
+ return '';
+ }
+ },
+ status: function(column, row) {
+ if (row.type === 'peer' && row['peer-status'] === 'stale') {
+ return '';
+ }
+
+ if (
+ (row.type === 'interface' && row.status === 'up') ||
+ (row.type === 'peer' && row['peer-status'] === 'online')
+ ) {
+ return '';
+ }
+
+ return '';
+ },
+
+
- }
},
requestHandler: function(request){
if ( $('#type_filter').val().length > 0) {
@@ -62,6 +85,10 @@
$('#grid-sessions').bootgrid('reload');
});
+ $("#grid-sessions").on('loaded.rs.jquery.bootgrid', function() {
+ $('[data-toggle="tooltip"]').tooltip();
+ });
+
$("#type_filter_container").detach().prependTo('#grid-sessions-header > .row > .actionBar > .actions');
});
@@ -80,13 +107,14 @@
- | {{ lang._('Device') }} |
- {{ lang._('Type') }} |
- {{ lang._('Status') }} |
- {{ lang._('Public key') }} |
+ {{ lang._('Status') }} |
+ {{ lang._('Device') }} |
+ {{ lang._('Type') }} |
+ {{ lang._('Public key') }} |
{{ lang._('Name') }} |
{{ lang._('Port / Endpoint') }} |
- {{ lang._('Handshake') }} |
+ {{ lang._('Handshake') }} |
+ {{ lang._('Handshake Age') }} |
{{ lang._('Sent') }} |
{{ lang._('Received') }} |
diff --git a/src/opnsense/www/js/widgets/Metadata/Core.xml b/src/opnsense/www/js/widgets/Metadata/Core.xml
index 87ca4695d..b5a201be5 100644
--- a/src/opnsense/www/js/widgets/Metadata/Core.xml
+++ b/src/opnsense/www/js/widgets/Metadata/Core.xml
@@ -288,9 +288,8 @@
No tunnels connected
Online
Offline
- Tunnels
+ Stale
N/A
- Peer disconnected
diff --git a/src/opnsense/www/js/widgets/Wireguard.js b/src/opnsense/www/js/widgets/Wireguard.js
index 57f251aeb..71af82fa3 100644
--- a/src/opnsense/www/js/widgets/Wireguard.js
+++ b/src/opnsense/www/js/widgets/Wireguard.js
@@ -69,7 +69,7 @@ export default class Wireguard extends BaseTableWidget {
}
displayError(message) {
- $('#wgTunnelTable'). empty().append(
+ $('#wgTunnelTable').empty().append(
$(``)
);
}
@@ -77,29 +77,61 @@ export default class Wireguard extends BaseTableWidget {
processTunnels(newTunnels) {
$('.wireguard-interface').tooltip('hide');
- let now = moment().unix(); // Current time in seconds
- let tunnels = newTunnels.filter(row => row.type == 'peer').map(row => ({
- ifname: row.ifname ? row.if + ' (' + row.ifname + ') ' : row.if,
- name: row.name,
- allowed_ips: row['allowed-ips'] || this.translations.notavailable,
- rx: row['transfer-rx'] ? this._formatBytes(row['transfer-rx']) : this.translations.notavailable,
- tx: row['transfer-tx'] ? this._formatBytes(row['transfer-tx']) : this.translations.notavailable,
- latest_handshake: row['latest-handshake'], // No fallback since we handle if 0
- latest_handshake_fmt: row['latest-handshake'] ? moment.unix(row['latest-handshake']).local().format('YYYY-MM-DD HH:mm:ss') : null,
- connected: row['latest-handshake'] && (now - row['latest-handshake']) <= 180, // Considered online if last handshake was within 3 minutes
- statusIcon: row['latest-handshake'] && (now - row['latest-handshake']) <= 180 ? 'fa-exchange text-success' : 'fa-exchange text-danger',
- publicKey: row['public-key'],
- uniqueId: row.if + row['public-key']
- }));
+ let tunnels = newTunnels
+ .filter(row => row.type == 'peer')
+ .map(row => ({
+ if: row.if,
- tunnels.sort((a, b) => a.connected === b.connected ? 0 : a.connected ? -1 : 1);
+ name: row.name,
+ allowed_ips: row['allowed-ips'] || this.translations.notavailable,
- let onlineCount = tunnels.filter(tunnel => tunnel.connected).length;
- let offlineCount = tunnels.length - onlineCount;
+ rx: row['transfer-rx']
+ ? this._formatBytes(row['transfer-rx'])
+ : this.translations.notavailable,
+
+ tx: row['transfer-tx']
+ ? this._formatBytes(row['transfer-tx'])
+ : this.translations.notavailable,
+
+ // No fallback since we handle if null
+ latest_handshake_epoch: row['latest-handshake-epoch'],
+
+ peerStatus: row['peer-status'],
+
+ statusIcon: row['peer-status'] === 'online'
+ ? 'fa-check-circle fa-fw text-success'
+ : row['peer-status'] === 'stale'
+ ? 'fa-question-circle fa-fw'
+ : 'fa-times-circle fa-fw text-danger',
+
+ statusTooltip: row['peer-status'] === 'online'
+ ? this.translations.online
+ : row['peer-status'] === 'stale'
+ ? this.translations.stale
+ : this.translations.offline,
+
+ publicKey: row['public-key'],
+ uniqueId: row.if + row['public-key']
+ }));
+
+ tunnels.sort((a, b) => {
+ if (a.peerStatus === b.peerStatus) return 0;
+ if (a.peerStatus === 'online') return -1;
+ if (a.peerStatus === 'stale' && b.peerStatus !== 'online') return -1;
+ return 1;
+ });
+
+ let onlineCount = tunnels.filter(tunnel => tunnel.peerStatus === 'online').length;
+ let staleCount = tunnels.filter(tunnel => tunnel.peerStatus === 'stale').length;
+ let offlineCount = tunnels.length - onlineCount - staleCount;
let summaryRow = `
- ${this.translations.total}: ${tunnels.length} | ${this.translations.online}: ${onlineCount} | ${this.translations.offline}: ${offlineCount}
+
+ ${this.translations.online}: ${onlineCount} |
+ ${this.translations.stale}: ${staleCount} |
+ ${this.translations.offline}: ${offlineCount}
+
`;
super.updateTable('wgTunnelTable', [[summaryRow, '']], 'wg-summary');
@@ -110,23 +142,23 @@ export default class Wireguard extends BaseTableWidget {
`;
let row = `
- ${tunnel.latest_handshake_fmt
- ? `
${tunnel.latest_handshake_fmt}
+ ${tunnel.latest_handshake_epoch
+ ? `
${tunnel.latest_handshake_epoch}
${tunnel.rx}
@@ -134,7 +166,7 @@ export default class Wireguard extends BaseTableWidget {
${tunnel.tx}
`
- : `
${this.translations.disconnected}`}
+ : ''}
`;
// Update the HTML table with the sorted rows