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 c8d8942ed..95041a723 100644
--- a/src/opnsense/mvc/app/controllers/OPNsense/OpenVPN/forms/dialogInstance.xml
+++ b/src/opnsense/mvc/app/controllers/OPNsense/OpenVPN/forms/dialogInstance.xml
@@ -107,6 +107,22 @@
The server itself will take the next base address (+1) of the given network for use as the server-side endpoint of the local TUN/TAP interface
+
+ instance.bridge_gateway
+
+ text
+
+
+ This directive will set up an OpenVPN server which will allocate addresses to clients out of the given network pool.
+
+
+
+ instance.bridge_pool
+
+ text
+
+ Specify an ip range which should be used to offer IPv4 addresses to the client.
+
instance.topology
diff --git a/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.php b/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.php
index 03eb062f6..e1c2920f3 100644
--- a/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.php
+++ b/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.php
@@ -72,36 +72,60 @@ class OpenVPN extends BaseModel
));
}
} elseif ($instance->role == 'server') {
- if (
- $instance->dev_type == 'tun' &&
- empty((string)$instance->server) &&
- empty((string)$instance->server_ipv6)
- ) {
- $messages->appendMessage(
- new Message(gettext('At least one IPv4 or IPv6 tunnel network is required.'), $key . '.server')
- );
- $messages->appendMessage(
- new Message(gettext('At least one IPv4 or IPv6 tunnel network is required.'), $key . '.server_ipv6')
- );
- }
- if (!empty((string)$instance->server) && strpos((string)$instance->server, '/') !== false) {
- if (
- explode('/', (string)$instance->server)[1] > 29 && !(
- (string)$instance->dev_type == 'tun' && (string)$instance->topology == 'p2p'
- )
- ) {
- /* tun + p2p is the exceptions here */
- $msg = gettext(
- 'Server directive must define a subnet of /29 or lower unless topology equals p2p.'
+ if ($instance->dev_type == 'tun') {
+ if (empty((string)$instance->server) && empty((string)$instance->server_ipv6)) {
+ $messages->appendMessage(
+ new Message(gettext('At least one IPv4 or IPv6 tunnel network is required.'), $key . '.server')
);
- $messages->appendMessage(new Message($msg, $key . '.server'));
+ $messages->appendMessage(
+ new Message(gettext('At least one IPv4 or IPv6 tunnel network is required.'), $key . '.server_ipv6')
+ );
+ }
+ if (!empty((string)$instance->server) && strpos((string)$instance->server, '/') !== false) {
+ if (
+ explode('/', (string)$instance->server)[1] > 29 && !(
+ (string)$instance->dev_type == 'tun' && (string)$instance->topology == 'p2p'
+ )
+ ) {
+ /* tun + p2p is the exceptions here */
+ $msg = gettext(
+ 'Server directive must define a subnet of /29 or lower unless topology equals p2p.'
+ );
+ $messages->appendMessage(new Message($msg, $key . '.server'));
+ }
+ }
+ } elseif ($instance->dev_type == 'tap') {
+ if (!(empty((string)$instance->bridge_gateway) xor ((string)$instance->bridge_pool))) {
+ $messages->appendMessage(new Message(
+ gettext('When specifying a bridge gateway, a pool should also be provided.'),
+ $key . ".bridge_gateway"
+ ));
+ } elseif (!empty((string)$instance->bridge_pool)) {
+ $parts = array_map('trim', explode('-', (string)$instance->bridge_pool));
+ if (count($parts) != 2 || !Util::isIpv4Address($parts[0]) || !Util::isIpv4Address($parts[1])) {
+ $messages->appendMessage(new Message(
+ gettext('Invalid range provided.'),
+ $key . ".bridge_pool"
+ ));
+ } else {
+ $ip = (string)$instance->bridge_gateway;
+ if (!Util::isIPInCIDR($parts[0], $ip) || !Util::isIPInCIDR($parts[1], $ip)) {
+ $messages->appendMessage(new Message(
+ gettext('Range does not match specified subnet.'),
+ $key . ".bridge_pool"
+ ));
+ }
+ }
+
}
}
if ((string)$instance->verify_client_cert != 'none') {
- $messages->appendMessage(new Message(
- gettext('To validate a certificate one has to be provided.'),
- $key . ".verify_client_cert"
- ));
+ if (empty((string)$instance->cert)) {
+ $messages->appendMessage(new Message(
+ gettext('To validate a certificate one has to be provided.'),
+ $key . ".verify_client_cert"
+ ));
+ }
} elseif (empty((string)$instance->authmode)) {
$messages->appendMessage(new Message(
gettext(
@@ -463,6 +487,7 @@ class OpenVPN extends BaseModel
//$options['user'] = 'openvpn';
//$options['group'] = 'openvpn';
} else {
+ // server only settings
$event_script = '/usr/local/opnsense/scripts/openvpn/ovpn_event.py';
$options['dev'] = "ovpns{$node->vpnid}";
$options['ping-timer-rem'] = null;
@@ -494,8 +519,17 @@ class OpenVPN extends BaseModel
$options['server'] = $parts[0] . " " . $mask;
}
} elseif ((string)$node->dev_type == 'tap') {
- $options['mode'] = 'server';
- $options['tls-server'] = null;
+ if (!empty((string)$node->bridge_gateway)) {
+ $parts = explode('/', (string)$node->bridge_gateway);
+ $options['server-bridge'] = sprintf(
+ "%s %s %s",
+ $parts[0],
+ Util::CIDRToMask($parts[1]),
+ str_replace('-', ' ', (string)$node->bridge_pool)
+ );
+ } else {
+ $options['server-bridge'] = '';
+ }
}
if (!empty((string)$node->server_ipv6)) {
$options['server-ipv6'] = (string)$node->server_ipv6;
@@ -503,24 +537,17 @@ class OpenVPN extends BaseModel
if (!empty((string)$node->username_as_common_name)) {
$options['username-as-common-name'] = null;
}
- // server only settings
- if (
- !empty((string)$node->server) ||
- !empty((string)$node->server_ipv6) ||
- (string)$node->dev_type == 'tap'
- ){
- $options['client-config-dir'] = "/var/etc/openvpn-csc/{$node->vpnid}";
- // hook event handlers
- if (!empty((string)$node->authmode)) {
- $options['auth-user-pass-verify'] = "\"{$event_script} --defer '{$node_uuid}'\" via-env";
- $options['learn-address'] = "\"{$event_script} '{$node->vpnid}'\"";
- } else {
- // client specific profiles are being deployed using the connect event when no auth is used
- $options['client-connect'] = "\"{$event_script} '{$node_uuid}'\"";
- }
- $options['client-disconnect'] = "\"{$event_script} '{$node_uuid}'\"";
- $options['tls-verify'] = "\"{$event_script} '{$node_uuid}'\"";
+ $options['client-config-dir'] = "/var/etc/openvpn-csc/{$node->vpnid}";
+ // hook event handlers
+ if (!empty((string)$node->authmode)) {
+ $options['auth-user-pass-verify'] = "\"{$event_script} --defer '{$node_uuid}'\" via-env";
+ $options['learn-address'] = "\"{$event_script} '{$node->vpnid}'\"";
+ } else {
+ // client specific profiles are being deployed using the connect event when no auth is used
+ $options['client-connect'] = "\"{$event_script} '{$node_uuid}'\"";
}
+ $options['client-disconnect'] = "\"{$event_script} '{$node_uuid}'\"";
+ $options['tls-verify'] = "\"{$event_script} '{$node_uuid}'\"";
if (!empty((string)$node->maxclients)) {
$options['max-clients'] = (string)$node->maxclients;
diff --git a/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.xml b/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.xml
index 9f688062c..33aac799c 100644
--- a/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.xml
+++ b/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.xml
@@ -161,6 +161,10 @@
N
+
+ N
+
+
,
Y