diff --git a/src/etc/rc.syshook.d/carp/20-openvpn-instances b/src/etc/rc.syshook.d/carp/20-openvpn-instances new file mode 100755 index 000000000..675a7a0ca --- /dev/null +++ b/src/etc/rc.syshook.d/carp/20-openvpn-instances @@ -0,0 +1,3 @@ +#!/bin/sh + +configctl -dq openvpn configure diff --git a/src/opnsense/mvc/app/controllers/OPNsense/OpenVPN/forms/dialogInstance.xml b/src/opnsense/mvc/app/controllers/OPNsense/OpenVPN/forms/dialogInstance.xml index 08263d8e8..0215d4c93 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/OpenVPN/forms/dialogInstance.xml +++ b/src/opnsense/mvc/app/controllers/OPNsense/OpenVPN/forms/dialogInstance.xml @@ -129,6 +129,14 @@ my.remote.local dead:beaf:: my.remote.local:1494 [dead:beaf::]:1494 192.168.1.1:1494 + + instance.carp_depend_on + + dropdown + + The carp VHID to depend on, when this virtual address is not in master state, + the instance will be shutdown. + header diff --git a/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/VirtualIPField.php b/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/VirtualIPField.php index 4b0db34d9..0864b3006 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/VirtualIPField.php +++ b/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/VirtualIPField.php @@ -41,6 +41,11 @@ class VirtualIPField extends BaseListField */ private $vipType = "*"; + /** + * @var boolean legacy key usage + */ + private $isLegacyKey = true; + /** * @var array cached collected certs */ @@ -55,6 +60,18 @@ class VirtualIPField extends BaseListField $this->vipType = $value; } + /** + * as this field type is used to hook legacy fields and MVC ones, specify a key here. + * default it uses a legacy (subnet) key. + * @param $value string vip type + */ + public function setKey($value) + { + if (strtolower($value) == 'mvc') { + $this->isLegacyKey = false; + } + } + /** * generate validation data (list of virtual ips) */ @@ -83,7 +100,12 @@ class VirtualIPField extends BaseListField } else { $caption = sprintf(gettext("[%s] %s on %s"), $vip->subnet, $vip->descr, $intf_name); } - self::$internalStaticOptionList[$this->vipType][(string)$vip->subnet] = $caption; + if ($this->isLegacyKey) { + $key = (string)$vip->subnet; + } else { + $key = (string)$vip->attributes()['uuid']; + } + self::$internalStaticOptionList[$this->vipType][$key] = $caption; } } natcasesort(self::$internalStaticOptionList[$this->vipType]); diff --git a/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.xml b/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.xml index 64ddc83f3..55daa7986 100644 --- a/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.xml +++ b/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.xml @@ -381,6 +381,11 @@ , Y + + carp + N + mvc + N diff --git a/src/opnsense/scripts/openvpn/ovpn_service_control.php b/src/opnsense/scripts/openvpn/ovpn_service_control.php index f0000d178..612fafe27 100755 --- a/src/opnsense/scripts/openvpn/ovpn_service_control.php +++ b/src/opnsense/scripts/openvpn/ovpn_service_control.php @@ -91,6 +91,27 @@ function ovpn_instance_stats($instance, $fhandle) return $data; } +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; +} + $opts = getopt('ah', [], $optind); $args = array_slice($argv, $optind); @@ -109,6 +130,7 @@ if (isset($opts['h']) || empty($args) || !in_array($args[0], ['start', 'stop', ' if ($action != 'stop') { $mdl->generateInstanceConfig($instance_id); } + $vhids = $action == 'configure' ? get_vhid_status() : []; $instance_ids = []; foreach ($mdl->Instances->Instance->iterateItems() as $key => $node) { if (empty((string)$node->enabled)) { @@ -133,7 +155,15 @@ if (isset($opts['h']) || empty($args) || !in_array($args[0], ['start', 'stop', ' ovpn_start($node, $statHandle); break; case 'configure': - if ($instance_stats['has_changed'] || !isvalidpid($node->pidFilename)) { + $carp_down = false; + if ((string)$node->role == 'client' && !empty($vhids[(string)$node->carp_depend_on])) { + $carp_down = $vhids[(string)$node->carp_depend_on] != 'MASTER'; + } + if ($carp_down) { + if (isvalidpid($node->pidFilename)) { + ovpn_stop($node); + } + } elseif ($instance_stats['has_changed'] || !isvalidpid($node->pidFilename)) { ovpn_stop($node); ovpn_start($node, $statHandle); }