summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLucas Christian <lucas@lucasec.com>2023-12-28 22:26:56 -0800
committerLucas Christian <lucas@lucasec.com>2024-07-22 10:57:45 -0700
commit4d2c89dcd50d3c158dc76ac5ab843dd66105bc02 (patch)
tree89d8e422877570c818ab49ae9f0f17ef9492bb1b
parente64322c2171a63d5fe52a431b948727d1df27d9c (diff)
downloadvyos-1x-4d2c89dcd50d3c158dc76ac5ab843dd66105bc02.tar.gz
vyos-1x-4d2c89dcd50d3c158dc76ac5ab843dd66105bc02.zip
T5873: vpn ipsec remote-access: support VTI interfaces
-rw-r--r--data/templates/ipsec/swanctl.conf.j22
-rw-r--r--data/templates/ipsec/swanctl/remote_access.j27
-rw-r--r--interface-definitions/include/ipsec/bind.xml.i10
-rw-r--r--interface-definitions/vpn_ipsec.xml.in49
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py256
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py74
6 files changed, 382 insertions, 16 deletions
diff --git a/data/templates/ipsec/swanctl.conf.j2 b/data/templates/ipsec/swanctl.conf.j2
index d44d0f5e4..698a9135e 100644
--- a/data/templates/ipsec/swanctl.conf.j2
+++ b/data/templates/ipsec/swanctl.conf.j2
@@ -31,6 +31,8 @@ pools {
{{ pool }} {
{% if pool_config.prefix is vyos_defined %}
addrs = {{ pool_config.prefix }}
+{% elif pool_config.range is vyos_defined %}
+ addrs = {{ pool_config.range.start }}-{{ pool_config.range.stop }}
{% endif %}
{% if pool_config.name_server is vyos_defined %}
dns = {{ pool_config.name_server | join(',') }}
diff --git a/data/templates/ipsec/swanctl/remote_access.j2 b/data/templates/ipsec/swanctl/remote_access.j2
index e384ae972..a3b61f781 100644
--- a/data/templates/ipsec/swanctl/remote_access.j2
+++ b/data/templates/ipsec/swanctl/remote_access.j2
@@ -69,6 +69,13 @@
{% set local_port = rw_conf.local.port if rw_conf.local.port is vyos_defined else '' %}
{% set local_suffix = '[%any/{1}]'.format(local_port) if local_port else '' %}
local_ts = {{ local_prefix | join(local_suffix + ",") }}{{ local_suffix }}
+{% if rw_conf.bind is vyos_defined %}
+{# The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. #}
+{# Thus we simply shift the key by one to also support a vti0 interface #}
+{% set if_id = rw_conf.bind | replace('vti', '') | int + 1 %}
+ if_id_in = {{ if_id }}
+ if_id_out = {{ if_id }}
+{% endif %}
}
}
}
diff --git a/interface-definitions/include/ipsec/bind.xml.i b/interface-definitions/include/ipsec/bind.xml.i
new file mode 100644
index 000000000..edc46d403
--- /dev/null
+++ b/interface-definitions/include/ipsec/bind.xml.i
@@ -0,0 +1,10 @@
+<!-- include start from ipsec/bind.xml.i -->
+<leafNode name="bind">
+ <properties>
+ <help>VTI tunnel interface associated with this configuration</help>
+ <completionHelp>
+ <path>interfaces vti</path>
+ </completionHelp>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in
index 4a7fde75b..d9d6fd93b 100644
--- a/interface-definitions/vpn_ipsec.xml.in
+++ b/interface-definitions/vpn_ipsec.xml.in
@@ -854,6 +854,7 @@
#include <include/dhcp-interface.xml.i>
#include <include/ipsec/local-traffic-selector.xml.i>
#include <include/ipsec/replay-window.xml.i>
+ #include <include/ipsec/bind.xml.i>
<leafNode name="timeout">
<properties>
<help>Timeout to close connection if no data is transmitted</help>
@@ -978,6 +979,45 @@
</constraint>
</properties>
</leafNode>
+ <node name="range">
+ <properties>
+ <help>Local IPv4 or IPv6 pool range</help>
+ </properties>
+ <children>
+ <leafNode name="start">
+ <properties>
+ <help>First IP address for local pool range</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 start address of pool</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 start address of pool</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="stop">
+ <properties>
+ <help>Last IP address for local pool range</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 end address of pool</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 end address of pool</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
#include <include/name-server-ipv4-ipv6.xml.i>
</children>
</tagNode>
@@ -1201,14 +1241,7 @@
<help>Virtual tunnel interface</help>
</properties>
<children>
- <leafNode name="bind">
- <properties>
- <help>VTI tunnel interface associated with this configuration</help>
- <completionHelp>
- <path>interfaces vti</path>
- </completionHelp>
- </properties>
- </leafNode>
+ #include <include/ipsec/bind.xml.i>
#include <include/ipsec/esp-group.xml.i>
</children>
</node>
diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py
index 2dc66485b..2674b37b6 100755
--- a/smoketest/scripts/cli/test_vpn_ipsec.py
+++ b/smoketest/scripts/cli/test_vpn_ipsec.py
@@ -1086,5 +1086,261 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.tearDownPKI()
+ def test_remote_access_pool_range(self):
+ # Same as test_remote_access but using an IP pool range instead of prefix
+ self.setupPKI()
+
+ ike_group = 'IKE-RW'
+ esp_group = 'ESP-RW'
+
+ conn_name = 'vyos-rw'
+ local_address = '192.0.2.1'
+ ip_pool_name = 'ra-rw-ipv4'
+ username = 'vyos'
+ password = 'secret'
+ ike_lifetime = '7200'
+ eap_lifetime = '3600'
+ local_id = 'ipsec.vyos.net'
+
+ name_servers = ['172.16.254.100', '172.16.254.101']
+ range_start = '172.16.250.2'
+ range_stop = '172.16.250.254'
+
+ # IKE
+ self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'lifetime', ike_lifetime])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'hash', 'sha512'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'hash', 'sha256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'dh-group', '2'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'hash', 'sha256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'encryption', 'aes128gcm128'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'hash', 'sha256'])
+
+ # ESP
+ self.cli_set(base_path + ['esp-group', esp_group, 'lifetime', eap_lifetime])
+ self.cli_set(base_path + ['esp-group', esp_group, 'pfs', 'disable'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'hash', 'sha512'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '2', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '2', 'hash', 'sha384'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'hash', 'sha256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '4', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '4', 'hash', 'sha1'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '10', 'encryption', 'aes128gcm128'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '10', 'hash', 'sha256'])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'local-id', local_id])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'local-users', 'username', username, 'password', password])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'server-mode', 'x509'])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'x509', 'certificate', peer_name])
+ # verify() - CA cert required for x509 auth
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'x509', 'ca-certificate', ca_name])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'esp-group', esp_group])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'ike-group', ike_group])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'local-address', local_address])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'pool', ip_pool_name])
+
+ for ns in name_servers:
+ self.cli_set(base_path + ['remote-access', 'pool', ip_pool_name, 'name-server', ns])
+ self.cli_set(base_path + ['remote-access', 'pool', ip_pool_name, 'range', 'start', range_start])
+ self.cli_set(base_path + ['remote-access', 'pool', ip_pool_name, 'range', 'stop', range_stop])
+
+ self.cli_commit()
+
+ # verify applied configuration
+ swanctl_conf = read_file(swanctl_file)
+ swanctl_lines = [
+ f'{conn_name}',
+ f'remote_addrs = %any',
+ f'local_addrs = {local_address}',
+ f'proposals = aes256-sha512-modp2048,aes256-sha256-modp2048,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048',
+ f'version = 2',
+ f'send_certreq = no',
+ f'rekey_time = {ike_lifetime}s',
+ f'keyingtries = 0',
+ f'pools = {ip_pool_name}',
+ f'id = "{local_id}"',
+ f'auth = pubkey',
+ f'certs = peer1.pem',
+ f'auth = eap-mschapv2',
+ f'eap_id = %any',
+ f'esp_proposals = aes256-sha512,aes256-sha384,aes256-sha256,aes256-sha1,aes128gcm128-sha256',
+ f'life_time = {eap_lifetime}s',
+ f'dpd_action = clear',
+ f'replay_window = 32',
+ f'inactivity = 28800',
+ f'local_ts = 0.0.0.0/0,::/0',
+ ]
+ for line in swanctl_lines:
+ self.assertIn(line, swanctl_conf)
+
+ swanctl_secrets_lines = [
+ f'eap-{conn_name}-{username}',
+ f'secret = "{password}"',
+ f'id-{conn_name}-{username} = "{username}"',
+ ]
+ for line in swanctl_secrets_lines:
+ self.assertIn(line, swanctl_conf)
+
+ swanctl_pool_lines = [
+ f'{ip_pool_name}',
+ f'addrs = {range_start}-{range_stop}',
+ f'dns = {",".join(name_servers)}',
+ ]
+ for line in swanctl_pool_lines:
+ self.assertIn(line, swanctl_conf)
+
+ # Check Root CA, Intermediate CA and Peer cert/key pair is present
+ self.assertTrue(os.path.exists(os.path.join(CA_PATH, f'{ca_name}.pem')))
+ self.assertTrue(os.path.exists(os.path.join(CERT_PATH, f'{peer_name}.pem')))
+
+ self.tearDownPKI()
+
+ def test_remote_access_vti(self):
+ # Set up and use a VTI interface for the remote access VPN
+ self.setupPKI()
+
+ ike_group = 'IKE-RW'
+ esp_group = 'ESP-RW'
+
+ conn_name = 'vyos-rw'
+ local_address = '192.0.2.1'
+ vti = 'vti10'
+ ip_pool_name = 'ra-rw-ipv4'
+ username = 'vyos'
+ password = 'secret'
+ ike_lifetime = '7200'
+ eap_lifetime = '3600'
+ local_id = 'ipsec.vyos.net'
+
+ name_servers = ['10.1.1.1']
+ range_start = '10.1.1.10'
+ range_stop = '10.1.1.254'
+
+ # VTI interface
+ self.cli_set(vti_path + [vti, 'address', '10.1.1.1/24'])
+
+ # IKE
+ self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'lifetime', ike_lifetime])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'hash', 'sha512'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'hash', 'sha256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'dh-group', '2'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'hash', 'sha256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'encryption', 'aes128gcm128'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'hash', 'sha256'])
+
+ # ESP
+ self.cli_set(base_path + ['esp-group', esp_group, 'lifetime', eap_lifetime])
+ self.cli_set(base_path + ['esp-group', esp_group, 'pfs', 'disable'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'hash', 'sha512'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '2', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '2', 'hash', 'sha384'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'hash', 'sha256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '4', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '4', 'hash', 'sha1'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '10', 'encryption', 'aes128gcm128'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '10', 'hash', 'sha256'])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'local-id', local_id])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'local-users', 'username', username, 'password', password])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'server-mode', 'x509'])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'x509', 'certificate', peer_name])
+ # verify() - CA cert required for x509 auth
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'x509', 'ca-certificate', ca_name])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'esp-group', esp_group])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'ike-group', ike_group])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'bind', vti])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'local-address', local_address])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'pool', ip_pool_name])
+
+ for ns in name_servers:
+ self.cli_set(base_path + ['remote-access', 'pool', ip_pool_name, 'name-server', ns])
+ self.cli_set(base_path + ['remote-access', 'pool', ip_pool_name, 'range', 'start', range_start])
+ self.cli_set(base_path + ['remote-access', 'pool', ip_pool_name, 'range', 'stop', range_stop])
+
+ self.cli_commit()
+
+ # verify applied configuration
+ swanctl_conf = read_file(swanctl_file)
+
+ if_id = vti.lstrip('vti')
+ # The key defaults to 0 and will match any policies which similarly do
+ # not have a lookup key configuration - thus we shift the key by one
+ # to also support a vti0 interface
+ if_id = str(int(if_id) +1)
+
+ swanctl_lines = [
+ f'{conn_name}',
+ f'remote_addrs = %any',
+ f'local_addrs = {local_address}',
+ f'proposals = aes256-sha512-modp2048,aes256-sha256-modp2048,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048',
+ f'version = 2',
+ f'send_certreq = no',
+ f'rekey_time = {ike_lifetime}s',
+ f'keyingtries = 0',
+ f'pools = {ip_pool_name}',
+ f'id = "{local_id}"',
+ f'auth = pubkey',
+ f'certs = peer1.pem',
+ f'auth = eap-mschapv2',
+ f'eap_id = %any',
+ f'esp_proposals = aes256-sha512,aes256-sha384,aes256-sha256,aes256-sha1,aes128gcm128-sha256',
+ f'life_time = {eap_lifetime}s',
+ f'dpd_action = clear',
+ f'replay_window = 32',
+ f'if_id_in = {if_id}', # will be 11 for vti10 - shifted by one
+ f'if_id_out = {if_id}',
+ f'inactivity = 28800',
+ f'local_ts = 0.0.0.0/0,::/0',
+ ]
+ for line in swanctl_lines:
+ self.assertIn(line, swanctl_conf)
+
+ swanctl_secrets_lines = [
+ f'eap-{conn_name}-{username}',
+ f'secret = "{password}"',
+ f'id-{conn_name}-{username} = "{username}"',
+ ]
+ for line in swanctl_secrets_lines:
+ self.assertIn(line, swanctl_conf)
+
+ swanctl_pool_lines = [
+ f'{ip_pool_name}',
+ f'addrs = {range_start}-{range_stop}',
+ f'dns = {",".join(name_servers)}',
+ ]
+ for line in swanctl_pool_lines:
+ self.assertIn(line, swanctl_conf)
+
+ # Check Root CA, Intermediate CA and Peer cert/key pair is present
+ self.assertTrue(os.path.exists(os.path.join(CA_PATH, f'{ca_name}.pem')))
+ self.assertTrue(os.path.exists(os.path.join(CERT_PATH, f'{peer_name}.pem')))
+
+ self.tearDownPKI()
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
index cf82b767f..2c1ddc245 100755
--- a/src/conf_mode/vpn_ipsec.py
+++ b/src/conf_mode/vpn_ipsec.py
@@ -21,6 +21,9 @@ import jmespath
from sys import exit
from time import sleep
+from ipaddress import ip_address
+from netaddr import IPNetwork
+from netaddr import IPRange
from vyos.base import Warning
from vyos.config import Config
@@ -304,6 +307,14 @@ def verify(ipsec):
if dict_search('remote_access.radius.server', ipsec) == None:
raise ConfigError('RADIUS authentication requires at least one server')
+ if 'bind' in ra_conf:
+ if dict_search('options.disable_route_autoinstall', ipsec) == None:
+ Warning('It\'s recommended to use ipsec vti with the next command\n[set vpn ipsec option disable-route-autoinstall]')
+
+ vti_interface = ra_conf['bind']
+ if not os.path.exists(f'/sys/class/net/{vti_interface}'):
+ raise ConfigError(f'VTI interface {vti_interface} for remote-access connection {name} does not exist!')
+
if 'pool' in ra_conf:
if {'dhcp', 'radius'} <= set(ra_conf['pool']):
raise ConfigError(f'Can not use both DHCP and RADIUS for address allocation '\
@@ -330,26 +341,73 @@ def verify(ipsec):
raise ConfigError(f'Requested pool "{pool}" does not exist!')
if 'pool' in ipsec['remote_access']:
+ pool_networks = []
for pool, pool_config in ipsec['remote_access']['pool'].items():
- if 'prefix' not in pool_config:
- raise ConfigError(f'Missing madatory prefix option for pool "{pool}"!')
+ if 'prefix' not in pool_config and 'range' not in pool_config:
+ raise ConfigError(f'Mandatory prefix or range must be specified for pool "{pool}"!')
+
+ if 'prefix' in pool_config and 'range' in pool_config:
+ raise ConfigError(f'Only one of prefix or range can be specified for pool "{pool}"!')
+
+ if 'prefix' in pool_config:
+ range_is_ipv4 = is_ipv4(pool_config['prefix'])
+ range_is_ipv6 = is_ipv6(pool_config['prefix'])
+
+ net = IPNetwork(pool_config['prefix'])
+ start = net.first
+ stop = net.last
+ for network in pool_networks:
+ if start in network or stop in network:
+ raise ConfigError(f'Prefix for pool "{pool}" is already part of another pool\'s range!')
+
+ tmp = IPRange(start, stop)
+ pool_networks.append(tmp)
+
+ if 'range' in pool_config:
+ range_config = pool_config['range']
+ if not {'start', 'stop'} <= set(range_config.keys()):
+ raise ConfigError(f'Range start and stop address must be defined for pool "{pool}"!')
+
+ range_both_ipv4 = is_ipv4(range_config['start']) and is_ipv4(range_config['stop'])
+ range_both_ipv6 = is_ipv6(range_config['start']) and is_ipv6(range_config['stop'])
+
+ if not (range_both_ipv4 or range_both_ipv6):
+ raise ConfigError(f'Range start and stop must be of the same address family for pool "{pool}"!')
+
+ if ip_address(range_config['stop']) < ip_address(range_config['start']):
+ raise ConfigError(f'Range stop address must be greater or equal\n' \
+ 'to the range\'s start address for pool "{pool}"!')
+
+ range_is_ipv4 = is_ipv4(range_config['start'])
+ range_is_ipv6 = is_ipv6(range_config['start'])
+
+ start = range_config['start']
+ stop = range_config['stop']
+ for network in pool_networks:
+ if start in network:
+ raise ConfigError(f'Range "{range}" start address "{start}" already part of another pool\'s range!')
+ if stop in network:
+ raise ConfigError(f'Range "{range}" stop address "{stop}" already part of another pool\'s range!')
+
+ tmp = IPRange(start, stop)
+ pool_networks.append(tmp)
if 'name_server' in pool_config:
if len(pool_config['name_server']) > 2:
raise ConfigError(f'Only two name-servers are supported for remote-access pool "{pool}"!')
for ns in pool_config['name_server']:
- v4_addr_and_ns = is_ipv4(ns) and not is_ipv4(pool_config['prefix'])
- v6_addr_and_ns = is_ipv6(ns) and not is_ipv6(pool_config['prefix'])
+ v4_addr_and_ns = is_ipv4(ns) and not range_is_ipv4
+ v6_addr_and_ns = is_ipv6(ns) and not range_is_ipv6
if v4_addr_and_ns or v6_addr_and_ns:
- raise ConfigError('Must use both IPv4 or IPv6 addresses for pool prefix and name-server adresses!')
+ raise ConfigError('Must use both IPv4 or IPv6 addresses for pool prefix/range and name-server addresses!')
if 'exclude' in pool_config:
for exclude in pool_config['exclude']:
- v4_addr_and_exclude = is_ipv4(exclude) and not is_ipv4(pool_config['prefix'])
- v6_addr_and_exclude = is_ipv6(exclude) and not is_ipv6(pool_config['prefix'])
+ v4_addr_and_exclude = is_ipv4(exclude) and not range_is_ipv4
+ v6_addr_and_exclude = is_ipv6(exclude) and not range_is_ipv6
if v4_addr_and_exclude or v6_addr_and_exclude:
- raise ConfigError('Must use both IPv4 or IPv6 addresses for pool prefix and exclude prefixes!')
+ raise ConfigError('Must use both IPv4 or IPv6 addresses for pool prefix/range and exclude prefixes!')
if 'radius' in ipsec['remote_access'] and 'server' in ipsec['remote_access']['radius']:
for server, server_config in ipsec['remote_access']['radius']['server'].items():