diff --git a/net/wireguard/Makefile b/net/wireguard/Makefile
index 4092c7e06..4bbfb2247 100644
--- a/net/wireguard/Makefile
+++ b/net/wireguard/Makefile
@@ -1,5 +1,5 @@
PLUGIN_NAME= wireguard
-PLUGIN_VERSION= 2.1
+PLUGIN_VERSION= 2.2
PLUGIN_COMMENT= WireGuard VPN service kernel implementation
PLUGIN_DEPENDS= wireguard-kmod
PLUGIN_CONFLICTS= wireguard-go
diff --git a/net/wireguard/pkg-descr b/net/wireguard/pkg-descr
index 97bd55ffc..1b9ba24a0 100644
--- a/net/wireguard/pkg-descr
+++ b/net/wireguard/pkg-descr
@@ -16,6 +16,10 @@ WWW: https://www.wireguard.com/
Changelog
---------
+2.2
+
+* add vhid (carp) tracking support
+
2.1
* Only reload when interface configuration did not change
diff --git a/net/wireguard/src/etc/rc.syshook.d/carp/20-wireguard b/net/wireguard/src/etc/rc.syshook.d/carp/20-wireguard
new file mode 100755
index 000000000..d8337b6bf
--- /dev/null
+++ b/net/wireguard/src/etc/rc.syshook.d/carp/20-wireguard
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+configctl -dq wireguard configure
\ No newline at end of file
diff --git a/net/wireguard/src/opnsense/mvc/app/controllers/OPNsense/Wireguard/forms/dialogEditWireguardServer.xml b/net/wireguard/src/opnsense/mvc/app/controllers/OPNsense/Wireguard/forms/dialogEditWireguardServer.xml
index eff1f9504..04bb1a71b 100644
--- a/net/wireguard/src/opnsense/mvc/app/controllers/OPNsense/Wireguard/forms/dialogEditWireguardServer.xml
+++ b/net/wireguard/src/opnsense/mvc/app/controllers/OPNsense/Wireguard/forms/dialogEditWireguardServer.xml
@@ -59,6 +59,13 @@
true
List of addresses to configure on the tunnel adapter. Please use CIDR notation like 10.0.0.1/24.
+
+ server.carp_depend_on
+
+ dropdown
+ The carp VHID to depend on, when this virtual address is not in master state,
+ the instance will be shutdown.
+
server.peers
diff --git a/net/wireguard/src/opnsense/mvc/app/models/OPNsense/Wireguard/Server.xml b/net/wireguard/src/opnsense/mvc/app/models/OPNsense/Wireguard/Server.xml
index 1dfada39e..fe738e8af 100644
--- a/net/wireguard/src/opnsense/mvc/app/models/OPNsense/Wireguard/Server.xml
+++ b/net/wireguard/src/opnsense/mvc/app/models/OPNsense/Wireguard/Server.xml
@@ -59,6 +59,11 @@
N
+
+ carp
+ N
+ mvc
+
diff --git a/net/wireguard/src/opnsense/mvc/app/views/OPNsense/Wireguard/diagnostics.volt b/net/wireguard/src/opnsense/mvc/app/views/OPNsense/Wireguard/diagnostics.volt
index dc97ad14a..a8d870f83 100644
--- a/net/wireguard/src/opnsense/mvc/app/views/OPNsense/Wireguard/diagnostics.volt
+++ b/net/wireguard/src/opnsense/mvc/app/views/OPNsense/Wireguard/diagnostics.volt
@@ -82,6 +82,7 @@
| {{ lang._('Interface') }} |
{{ lang._('Type') }} |
+ {{ lang._('Status') }} |
{{ lang._('Public key') }} |
{{ lang._('Name') }} |
{{ lang._('Port / Endpoint') }} |
diff --git a/net/wireguard/src/opnsense/scripts/Wireguard/wg-service-control.php b/net/wireguard/src/opnsense/scripts/Wireguard/wg-service-control.php
index 6ba6eaab5..9e56b0405 100755
--- a/net/wireguard/src/opnsense/scripts/Wireguard/wg-service-control.php
+++ b/net/wireguard/src/opnsense/scripts/Wireguard/wg-service-control.php
@@ -31,10 +31,35 @@ require_once('script/load_phalcon.php');
require_once('util.inc');
require_once('interfaces.inc');
+/**
+ * collect carp status per vhid
+ */
+function get_vhid_status()
+{
+ $vhids = [];
+ $uuids = [];
+ foreach ((new OPNsense\Interfaces\Vip())->vip->iterateItems() as $id => $item) {
+ if ($item->mode == 'carp') {
+ $uuids[(string)$item->vhid] = $id;
+ }
+ }
+ foreach (legacy_interfaces_details() as $ifdata) {
+ if (!empty($ifdata['carp'])) {
+ foreach ($ifdata['carp'] as $data) {
+ if (isset($uuids[$data['vhid']])) {
+ $vhids[$uuids[$data['vhid']]] = $data['status'];
+ }
+ }
+ }
+ }
+ return $vhids;
+}
+
+
/**
* mimic wg-quick behaviour, but bound to our config
*/
-function wg_start($server, $fhandle)
+function wg_start($server, $fhandle, $ifcfgflag='up')
{
if (!does_interface_exist($server->interface)) {
mwexecf('/sbin/ifconfig wg create name %s', [$server->interface]);
@@ -49,7 +74,7 @@ function wg_start($server, $fhandle)
if (!empty((string)$server->mtu)) {
mwexecf('/sbin/ifconfig %s mtu %s', [$server->interface, $server->mtu]);
}
- mwexecf('/sbin/ifconfig %s up', [$server->interface]);
+ mwexecf('/sbin/ifconfig %s %s', [$server->interface, $ifcfgflag]);
if (empty((string)$server->disableroutes)) {
/**
@@ -161,6 +186,8 @@ if (isset($opts['h']) || empty($args) || !in_array($args[0], ['start', 'stop', '
$server_devs = [];
if (!empty((string)(new OPNsense\Wireguard\General())->enabled)) {
+ $ifdetails = legacy_interfaces_details();
+ $vhids = get_vhid_status();
foreach ((new OPNsense\Wireguard\Server())->servers->server->iterateItems() as $key => $node) {
if (empty((string)$node->enabled)) {
continue;
@@ -168,6 +195,19 @@ if (isset($opts['h']) || empty($args) || !in_array($args[0], ['start', 'stop', '
if ($server_id != null && $key != $server_id) {
continue;
}
+ /**
+ * CARP may influence the interface status (up or down).
+ * In order to fluently switch between roles, one should only have to change the interface flag in this
+ * case, which means we can still reconfigure an interface in the usual way and just omit sending traffic
+ * when in BACKUP or INIT mode.
+ */
+ $carp_if_flag = 'up';
+ if (
+ !empty($vhids[(string)$node->carp_depend_on]) &&
+ $vhids[(string)$node->carp_depend_on] != 'MASTER'
+ ) {
+ $carp_if_flag = 'down';
+ }
$server_devs[] = (string)$node->interface;
$statHandle = fopen($node->statFilename, "a+");
if (flock($statHandle, LOCK_EX)) {
@@ -176,16 +216,16 @@ if (isset($opts['h']) || empty($args) || !in_array($args[0], ['start', 'stop', '
wg_stop($node);
break;
case 'start':
- wg_start($node, $statHandle);
+ wg_start($node, $statHandle, $carp_if_flag);
break;
case 'restart':
wg_stop($node);
- wg_start($node, $statHandle);
+ wg_start($node, $statHandle, $carp_if_flag);
break;
case 'configure':
if (
@md5_file($node->cnfFilename) != get_stat_hash($statHandle)['file'] ||
- !does_interface_exist((string)$node->interface)
+ !isset($ifdetails[(string)$node->interface])
) {
if (get_stat_hash($statHandle)['interface'] != wg_reconfigure_hash($node)) {
// Fluent reloading not supported for this instance, make sure the user is informed
@@ -196,7 +236,13 @@ if (isset($opts['h']) || empty($args) || !in_array($args[0], ['start', 'stop', '
);
wg_stop($node);
}
- wg_start($node, $statHandle);
+ wg_start($node, $statHandle, $carp_if_flag);
+ } else {
+ // when triggered via a CARP event, check our interface status [UP|DOWN]
+ $tmp = in_array('up', $ifdetails[(string)$node->interface]['flags']) ? 'up' : 'down';
+ if ($tmp != $carp_if_flag) {
+ mwexecf('/sbin/ifconfig %s %s', [$node->interface, $carp_if_flag]);
+ }
}
break;
}
diff --git a/net/wireguard/src/opnsense/scripts/Wireguard/wg_show.py b/net/wireguard/src/opnsense/scripts/Wireguard/wg_show.py
index 6cfa6c4bc..987efb443 100755
--- a/net/wireguard/src/opnsense/scripts/Wireguard/wg_show.py
+++ b/net/wireguard/src/opnsense/scripts/Wireguard/wg_show.py
@@ -28,6 +28,13 @@
import subprocess
import ujson
+
+interfaces = {}
+for line in subprocess.run(['/sbin/ifconfig'], capture_output=True, text=True).stdout.split("\n"):
+ if not line.startswith('\t') and line.find('<') > -1:
+ ifname = line.split(':')[0]
+ interfaces[ifname] = 'up' if 'UP' in line.split('<')[1].split('>')[0].split(',') else 'down'
+
sp = subprocess.run(['/usr/bin/wg', 'show', 'all', 'dump'], capture_output=True, text=True)
result = {'records': []}
if sp.returncode == 0:
@@ -44,6 +51,7 @@ if sp.returncode == 0:
record['fwmark'] = parts[4]
# convenience, copy listen-port to endpoint
record['endpoint'] = parts[3]
+ record['status'] = interfaces.get(record['if'], 'down')
elif len(parts) == 9:
record['type'] = 'peer'
record['public-key'] = parts[1]