summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/openvpn/client.conf.tmpl21
-rw-r--r--data/templates/openvpn/server.conf.tmpl33
-rw-r--r--interface-definitions/interfaces-openvpn.xml.in94
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py230
-rwxr-xr-xsrc/validators/ipv63
5 files changed, 320 insertions, 61 deletions
diff --git a/data/templates/openvpn/client.conf.tmpl b/data/templates/openvpn/client.conf.tmpl
index 3099f2ca7..508d8da94 100644
--- a/data/templates/openvpn/client.conf.tmpl
+++ b/data/templates/openvpn/client.conf.tmpl
@@ -1,8 +1,9 @@
### Autogenerated by interfaces-openvpn.py ###
{% if ip -%}
-ifconfig-push {{ ip }} {{ remote_netmask }}
+ifconfig-push {{ ip[0] }} {{ remote_netmask }}
{% endif -%}
+
{% for route in push_route -%}
push "route {{ route }}"
{% endfor -%}
@@ -11,6 +12,24 @@ push "route {{ route }}"
iroute {{ net }}
{% endfor -%}
+{# ipv6_remote is only set when IPv6 server is enabled #}
+{% if ipv6_remote -%}
+# IPv6
+
+{%- if ipv6_ip %}
+ifconfig-ipv6-push {{ ipv6_ip[0] }} {{ ipv6_remote }}
+{%- endif %}
+
+{%- for route6 in ipv6_push_route %}
+push "route-ipv6 {{ route6 }}"
+{%- endfor %}
+
+{%- for net6 in ipv6_subnet %}
+iroute {{ net6 }}
+{%- endfor %}
+
+{% endif -%}
+
{% if disable -%}
disable
{% endif -%}
diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl
index e2f9062a1..0f563dc2b 100644
--- a/data/templates/openvpn/server.conf.tmpl
+++ b/data/templates/openvpn/server.conf.tmpl
@@ -18,7 +18,7 @@ dev {{ intf }}
persist-key
iproute /usr/libexec/vyos/system/unpriv-ip
-proto {% if 'tcp-active' in protocol -%}tcp-client{% elif 'tcp-passive' in protocol -%}tcp-server{% else %}udp{% endif %}
+proto {% if 'tcp-active' in protocol -%}tcp6-client{% elif 'tcp-passive' in protocol -%}tcp6-server{% else %}udp6{% endif %}
{%- if local_host %}
local {{ local_host }}
@@ -78,7 +78,7 @@ topology {% if server_topology == 'point-to-point' %}p2p{% else %}{{ server_topo
mode server
tls-server
{%- else %}
-server {{ server_subnet }} nopool
+server {{ server_subnet[0] }} nopool
{%- endif %}
{%- if server_pool %}
@@ -110,7 +110,26 @@ push "dhcp-option DNS {{ ns }}"
{%- if server_domain -%}
push "dhcp-option DOMAIN {{ server_domain }}"
-{% endif %}
+{% endif -%}
+
+{%- if server_ipv6_local %}
+# IPv6
+push "tun-ipv6"
+ifconfig-ipv6 {{ server_ipv6_local }}/{{ server_ipv6_prefixlen }} {{ server_ipv6_remote }}
+
+{%- if server_ipv6_pool %}
+ifconfig-ipv6-pool {{ server_ipv6_pool_base }}/{{ server_ipv6_pool_prefixlen }}
+{%- endif %}
+
+{%- for route6 in server_ipv6_push_route %}
+push "route-ipv6 {{ route6 }}"
+{%- endfor %}
+
+{%- for ns6 in server_ipv6_dns_nameserver %}
+push "dhcp-option DNS6 {{ ns6 }}"
+{%- endfor %}
+
+{%- endif %}
{% else -%}
#
@@ -120,9 +139,13 @@ ping {{ ping_interval }}
ping-restart {{ ping_restart }}
{% if local_address_subnet -%}
-ifconfig {{ local_address }} {{ local_address_subnet }}
+ifconfig {{ local_address[0] }} {{ local_address_subnet }}
{%- elif remote_address -%}
-ifconfig {{ local_address }} {{ remote_address }}
+ifconfig {{ local_address[0] }} {{ remote_address[0] }}
+{%- endif %}
+
+{% if ipv6_local_address -%}
+ifconfig-ipv6 {{ ipv6_local_address[0] }} {{ ipv6_remote_address[0] }}
{%- endif %}
{% endif -%}
diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in
index 574a3a58c..b5da8cf76 100644
--- a/interface-definitions/interfaces-openvpn.xml.in
+++ b/interface-definitions/interfaces-openvpn.xml.in
@@ -233,15 +233,15 @@
</node>
<tagNode name="local-address">
<properties>
- <help>Local IP address of tunnel</help>
+ <help>Local IP address of tunnel (IPv4 or IPv6)</help>
<constraint>
- <validator name="ipv4-address"/>
+ <validator name="ip-address"/>
</constraint>
</properties>
<children>
<leafNode name="subnet-mask">
<properties>
- <help>Subnet-mask for local IP address of tunnel</help>
+ <help>Subnet-mask for local IP address of tunnel (IPv4 only)</help>
<constraint>
<validator name="ipv4-address"/>
</constraint>
@@ -256,8 +256,12 @@
<format>ipv4</format>
<description>Local IPv4 address</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Local IPv6 address</description>
+ </valueHelp>
<constraint>
- <validator name="ipv4-address"/>
+ <validator name="ip-address"/>
</constraint>
</properties>
</leafNode>
@@ -341,9 +345,14 @@
<format>ipv4</format>
<description>Remote end IPv4 address</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Remote end IPv6 address</description>
+ </valueHelp>
<constraint>
<validator name="ipv4-address"/>
</constraint>
+ <multi/>
</properties>
</leafNode>
<leafNode name="remote-host">
@@ -351,7 +360,11 @@
<help>Remote host to connect to (dynamic if not set)</help>
<valueHelp>
<format>ipv4</format>
- <description>IP address of remote host</description>
+ <description>IPv4 address of remote host</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address of remote host</description>
</valueHelp>
<valueHelp>
<format>txt</format>
@@ -411,9 +424,14 @@
<format>ipv4</format>
<description>Client IPv4 address</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Client IPv6 address</description>
+ </valueHelp>
<constraint>
- <validator name="ipv4-address"/>
+ <validator name="ip-address"/>
</constraint>
+ <multi/>
</properties>
</leafNode>
<leafNode name="push-route">
@@ -423,21 +441,29 @@
<format>ipv4net</format>
<description>IPv4 network and prefix length</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 network and prefix length</description>
+ </valueHelp>
<constraint>
- <validator name="ipv4-prefix"/>
+ <validator name="ip-prefix"/>
</constraint>
<multi/>
</properties>
</leafNode>
<leafNode name="subnet">
<properties>
- <help>Subnet belonging to the client</help>
+ <help>Subnet belonging to the client (iroute)</help>
<valueHelp>
<format>ipv4net</format>
<description>IPv4 network and prefix length belonging to the client</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 network and prefix length belonging to the client</description>
+ </valueHelp>
<constraint>
- <validator name="ipv4-prefix"/>
+ <validator name="ip-prefix"/>
</constraint>
<multi/>
</properties>
@@ -446,7 +472,7 @@
</tagNode>
<node name="client-ip-pool">
<properties>
- <help>Pool of client IP addresses</help>
+ <help>Pool of client IPv4 addresses</help>
</properties>
<children>
<leafNode name="disable">
@@ -496,6 +522,31 @@
</leafNode>
</children>
</node>
+ <node name="client-ipv6-pool">
+ <properties>
+ <help>Pool of client IPv6 addresses</help>
+ </properties>
+ <children>
+ <leafNode name="base">
+ <properties>
+ <help>Client IPv6 pool base address with optional prefix length</help>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>Client IPv6 pool base address with optional prefix length (defaults: base = server subnet + 0x1000, prefix length = server prefix length)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="disable">
+ <properties>
+ <help>Disable client IPv6 pool</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<leafNode name="domain-name">
<properties>
<help>DNS suffix to be pushed to all clients</help>
@@ -524,8 +575,12 @@
<format>ipv4</format>
<description>DNS server IPv4 address</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>DNS server IPv6 address</description>
+ </valueHelp>
<constraint>
- <validator name="ipv4-address"/>
+ <validator name="ip-address"/>
</constraint>
<multi/>
</properties>
@@ -537,8 +592,12 @@
<format>ipv4net</format>
<description>IPv4 network and prefix length</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 network and prefix length</description>
+ </valueHelp>
<constraint>
- <validator name="ipv4-prefix"/>
+ <validator name="ip-prefix"/>
</constraint>
<multi/>
</properties>
@@ -555,9 +614,14 @@
<format>ipv4net</format>
<description>IPv4 network and prefix length</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 network and prefix length</description>
+ </valueHelp>
<constraint>
- <validator name="ipv4-prefix"/>
+ <validator name="ip-prefix"/>
</constraint>
+ <multi/>
</properties>
</leafNode>
<leafNode name="topology">
@@ -568,7 +632,7 @@
</completionHelp>
<valueHelp>
<format>net30</format>
- <description>net30 topology</description>
+ <description>net30 topology (default)</description>
</valueHelp>
<valueHelp>
<format>point-to-point</format>
@@ -579,7 +643,7 @@
<description>Subnet topology</description>
</valueHelp>
<constraint>
- <regex>(subnet|point-to-point)</regex>
+ <regex>(subnet|point-to-point|net30)</regex>
</constraint>
</properties>
</leafNode>
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 435e8a8f0..836deb64b 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -19,7 +19,7 @@ import re
from copy import deepcopy
from sys import exit,stderr
-from ipaddress import IPv4Address,IPv4Network,summarize_address_range
+from ipaddress import ip_address,ip_network,IPv4Address,IPv4Network,IPv6Address,IPv6Network,summarize_address_range
from netifaces import interfaces
from time import sleep
from shutil import rmtree
@@ -53,9 +53,11 @@ default_config_data = {
'ipv6_eui64_prefix': '',
'ipv6_forwarding': 1,
'ipv6_dup_addr_detect': 1,
+ 'ipv6_local_address': [],
+ 'ipv6_remote_address': [],
'ping_restart': '60',
'ping_interval': '10',
- 'local_address': '',
+ 'local_address': [],
'local_address_subnet': '',
'local_host': '',
'local_port': '',
@@ -65,7 +67,7 @@ default_config_data = {
'persistent_tunnel': False,
'protocol': 'udp',
'redirect_gateway': '',
- 'remote_address': '',
+ 'remote_address': [],
'remote_host': [],
'remote_port': '',
'client': [],
@@ -78,8 +80,17 @@ default_config_data = {
'server_pool_netmask': '',
'server_push_route': [],
'server_reject_unconfigured': False,
- 'server_subnet': '',
+ 'server_subnet': [],
'server_topology': '',
+ 'server_ipv6_dns_nameserver': [],
+ 'server_ipv6_local': '',
+ 'server_ipv6_prefixlen': '',
+ 'server_ipv6_remote': '',
+ 'server_ipv6_pool': True,
+ 'server_ipv6_pool_base': '',
+ 'server_ipv6_pool_prefixlen': '',
+ 'server_ipv6_push_route': [],
+ 'server_ipv6_subnet': [],
'shared_secret_file': '',
'tls': False,
'tls_auth': '',
@@ -119,8 +130,7 @@ def checkCertHeader(header, filename):
def getDefaultServer(network, topology, devtype):
"""
- Gets the default server parameters for a "server" directive.
- Currently only IPv4 routed but may be extended to support bridged and/or IPv6 in the future.
+ Gets the default server parameters for a IPv4 "server" directive.
Logic from openvpn's src/openvpn/helper.c.
Returns a dict with addresses or False if the input parameters were incorrect.
"""
@@ -198,6 +208,7 @@ def get_config():
# bridged server should not have a pool by default (but can be specified manually)
if openvpn['bridge_member']:
openvpn['server_pool'] = False
+ openvpn['server_ipv6_pool'] = False
# set configuration level
conf.set_level('interfaces openvpn ' + openvpn['intf'])
@@ -276,9 +287,15 @@ def get_config():
# Local IP address of tunnel - even as it is a tag node - we can only work
# on the first address
if conf.exists('local-address'):
- openvpn['local_address'] = conf.list_nodes('local-address')[0]
- if conf.exists('local-address {} subnet-mask'.format(openvpn['local_address'])):
- openvpn['local_address_subnet'] = conf.return_value('local-address {} subnet-mask'.format(openvpn['local_address']))
+ for tmp in conf.list_nodes('local-address'):
+ tmp_ip = ip_address(tmp)
+ if tmp_ip.version == 4:
+ openvpn['local_address'].append(tmp)
+ if conf.exists('local-address {} subnet-mask'.format(tmp)):
+ openvpn['local_address_subnet'] = conf.return_value('local-address {} subnet-mask'.format(tmp))
+ elif tmp_ip.version == 6:
+ # input IPv6 address could be expanded so get the compressed version
+ openvpn['ipv6_local_address'].append(str(tmp_ip))
# Local IP address to accept connections
if conf.exists('local-host'):
@@ -322,7 +339,12 @@ def get_config():
# IP address of remote end of tunnel
if conf.exists('remote-address'):
- openvpn['remote_address'] = conf.return_value('remote-address')
+ for tmp in conf.return_values('remote-address'):
+ tmp_ip = ip_address(tmp)
+ if tmp_ip.version == 4:
+ openvpn['remote_address'].append(tmp)
+ elif tmp_ip.version == 6:
+ openvpn['ipv6_remote_address'].append(str(tmp_ip))
# Remote host to connect to (dynamic if not set)
if conf.exists('remote-host'):
@@ -346,12 +368,18 @@ def get_config():
openvpn['server_topology'] = conf.return_value('server topology')
# Server-mode subnet (from which client IPs are allocated)
- server_network = None
+ server_network_v4 = None
+ server_network_v6 = None
if conf.exists('server subnet'):
- # server_network is used later in this function
- server_network = IPv4Network(conf.return_value('server subnet'))
- # convert the network in format: "192.0.2.0 255.255.255.0" for later use in template
- openvpn['server_subnet'] = server_network.with_netmask.replace(r'/', ' ')
+ for tmp in conf.return_values('server subnet'):
+ tmp_ip = ip_network(tmp)
+ if tmp_ip.version == 4:
+ server_network_v4 = tmp_ip
+ # convert the network to format: "192.0.2.0 255.255.255.0" for later use in template
+ openvpn['server_subnet'].append(tmp_ip.with_netmask.replace(r'/', ' '))
+ elif tmp_ip.version == 6:
+ server_network_v6 = tmp_ip
+ openvpn['server_ipv6_subnet'].append(str(tmp_ip))
# Client-specific settings
for client in conf.list_nodes('server client'):
@@ -360,7 +388,11 @@ def get_config():
data = {
'name': client,
'disable': False,
- 'ip': '',
+ 'ip': [],
+ 'ipv6_ip': [],
+ 'ipv6_remote': '',
+ 'ipv6_push_route': [],
+ 'ipv6_subnet': [],
'push_route': [],
'subnet': [],
'remote_netmask': ''
@@ -371,16 +403,28 @@ def get_config():
data['disable'] = True
# IP address of the client
- if conf.exists('ip'):
- data['ip'] = conf.return_value('ip')
+ for tmp in conf.return_values('ip'):
+ tmp_ip = ip_address(tmp)
+ if tmp_ip.version == 4:
+ data['ip'].append(tmp)
+ elif tmp_ip.version == 6:
+ data['ipv6_ip'].append(str(tmp_ip))
# Route to be pushed to the client
- for network in conf.return_values('push-route'):
- data['push_route'].append(IPv4Network(network).with_netmask.replace(r'/', ' '))
+ for tmp in conf.return_values('push-route'):
+ tmp_ip = ip_network(tmp)
+ if tmp_ip.version == 4:
+ data['push_route'].append(tmp_ip.with_netmask.replace(r'/', ' '))
+ elif tmp_ip.version == 6:
+ data['ipv6_push_route'].append(str(tmp_ip))
# Subnet belonging to the client
- for network in conf.return_values('subnet'):
- data['subnet'].append(IPv4Network(network).with_netmask.replace(r'/', ' '))
+ for tmp in conf.return_values('subnet'):
+ tmp_ip = ip_network(tmp)
+ if tmp_ip.version == 4:
+ data['subnet'].append(tmp_ip.with_netmask.replace(r'/', ' '))
+ elif tmp_ip.version == 6:
+ data['ipv6_subnet'].append(str(tmp_ip))
# Append to global client list
openvpn['client'].append(data)
@@ -407,6 +451,18 @@ def get_config():
conf.set_level('interfaces openvpn ' + openvpn['intf'])
+ # Server client IPv6 pool
+ if conf.exists('server client-ipv6-pool'):
+ conf.set_level('interfaces openvpn ' + openvpn['intf'] + ' server client-ipv6-pool')
+ openvpn['server_ipv6_pool'] = not conf.exists('disable')
+ if conf.exists('base'):
+ tmp = conf.return_value('base').split('/')
+ openvpn['server_ipv6_pool_base'] = str(IPv6Address(tmp[0]))
+ if 1 < len(tmp):
+ openvpn['server_ipv6_pool_prefixlen'] = tmp[1]
+
+ conf.set_level('interfaces openvpn ' + openvpn['intf'])
+
# DNS suffix to be pushed to all clients
if conf.exists('server domain-name'):
openvpn['server_domain'] = conf.return_value('server domain-name')
@@ -417,12 +473,21 @@ def get_config():
# Domain Name Server (DNS)
if conf.exists('server name-server'):
- openvpn['server_dns_nameserver'] = conf.return_values('server name-server')
+ for tmp in conf.return_values('server name-server'):
+ tmp_ip = ip_address(tmp)
+ if tmp_ip.version == 4:
+ openvpn['server_dns_nameserver'].append(tmp)
+ elif tmp_ip.version == 6:
+ openvpn['server_ipv6_dns_nameserver'].append(str(tmp_ip))
# Route to be pushed to all clients
if conf.exists('server push-route'):
- for network in conf.return_values('server push-route'):
- openvpn['server_push_route'].append(IPv4Network(network).with_netmask.replace(r'/', ' '))
+ for tmp in conf.return_values('server push-route'):
+ tmp_ip = ip_network(tmp)
+ if tmp_ip.version == 4:
+ openvpn['server_push_route'].append(tmp_ip.with_netmask.replace(r'/', ' '))
+ elif tmp_ip.version == 6:
+ openvpn['server_ipv6_push_route'].append(str(tmp_ip))
# Reject connections from clients that are not explicitly configured
if conf.exists('server reject-unconfigured-clients'):
@@ -491,9 +556,9 @@ def get_config():
# Set defaults where necessary.
# If any of the input parameters are wrong,
# this will return False and no defaults will be set.
- if server_network and openvpn['server_topology'] and openvpn['type']:
+ if server_network_v4 and openvpn['server_topology'] and openvpn['type']:
default_server = None
- default_server = getDefaultServer(server_network, openvpn['server_topology'], openvpn['type'])
+ default_server = getDefaultServer(server_network_v4, openvpn['server_topology'], openvpn['type'])
if default_server:
# server-bridge doesn't require a pool so don't set defaults for it
if openvpn['server_pool'] and not openvpn['bridge_member']:
@@ -509,6 +574,26 @@ def get_config():
for client in openvpn['client']:
client['remote_netmask'] = default_server['client_remote_netmask']
+ if server_network_v6:
+ if not openvpn['server_ipv6_local']:
+ openvpn['server_ipv6_local'] = server_network_v6[1]
+ if not openvpn['server_ipv6_prefixlen']:
+ openvpn['server_ipv6_prefixlen'] = server_network_v6.prefixlen
+ if not openvpn['server_ipv6_remote']:
+ openvpn['server_ipv6_remote'] = server_network_v6[2]
+
+ if openvpn['server_ipv6_pool'] and server_network_v6.prefixlen < 112:
+ if not openvpn['server_ipv6_pool_base']:
+ openvpn['server_ipv6_pool_base'] = server_network_v6[0x1000]
+ if not openvpn['server_ipv6_pool_prefixlen']:
+ openvpn['server_ipv6_pool_prefixlen'] = openvpn['server_ipv6_prefixlen']
+
+ for client in openvpn['client']:
+ client['ipv6_remote'] = openvpn['server_ipv6_local']
+
+ if openvpn['redirect_gateway']:
+ openvpn['redirect_gateway'] += ' ipv6'
+
return openvpn
def verify(openvpn):
@@ -562,26 +647,45 @@ def verify(openvpn):
raise ConfigError('encryption ncp-ciphers cannot be specified in site-to-site mode, only server or client')
if openvpn['mode'] == 'site-to-site' and not openvpn['bridge_member']:
- if not openvpn['local_address']:
+ if not (openvpn['local_address'] or openvpn['ipv6_local_address']):
raise ConfigError('Must specify "local-address" or "bridge member interface"')
+ if len(openvpn['local_address']) > 1 or len(openvpn['ipv6_local_address']) > 1:
+ raise ConfigError('Cannot specify more than 1 IPv4 and 1 IPv6 "local-address"')
+
+ if len(openvpn['remote_address']) > 1 or len(openvpn['ipv6_remote_address']) > 1:
+ raise ConfigError('Cannot specify more than 1 IPv4 and 1 IPv6 "remote-address"')
+
for host in openvpn['remote_host']:
- if host == openvpn['remote_address']:
+ if host in openvpn['remote_address'] or host in openvpn['ipv6_remote_address']:
raise ConfigError('"remote-address" cannot be the same as "remote-host"')
+ if openvpn['local_address'] and not (openvpn['remote_address'] or openvpn['local_address_subnet']):
+ raise ConfigError('IPv4 "local-address" requires IPv4 "remote-address" or IPv4 "local-address subnet"')
+
+ if openvpn['remote_address'] and not openvpn['local_address']:
+ raise ConfigError('IPv4 "remote-address" requires IPv4 "local-address"')
+
+ if openvpn['ipv6_local_address'] and not openvpn['ipv6_remote_address']:
+ raise ConfigError('IPv6 "local-address" requires IPv6 "remote-address"')
+
+ if openvpn['ipv6_remote_address'] and not openvpn['ipv6_local_address']:
+ raise ConfigError('IPv6 "remote-address" requires IPv6 "local-address"')
+
if openvpn['type'] == 'tun':
- if not openvpn['remote_address']:
+ if not (openvpn['remote_address'] or openvpn['ipv6_remote_address']):
raise ConfigError('Must specify "remote-address"')
- if openvpn['local_address'] == openvpn['remote_address']:
+ if ( (openvpn['local_address'] and openvpn['local_address'] == openvpn['remote_address']) or
+ (openvpn['ipv6_local_address'] and openvpn['ipv6_local_address'] == openvpn['ipv6_remote_address']) ):
raise ConfigError('"local-address" and "remote-address" cannot be the same')
- if openvpn['local_address'] == openvpn['local_host']:
+ if openvpn['local_host'] in openvpn['local_address'] or openvpn['local_host'] in openvpn['ipv6_local_address']:
raise ConfigError('"local-address" cannot be the same as "local-host"')
else:
# checks for client-server or site-to-site bridged
- if openvpn['local_address'] or openvpn['remote_address']:
+ if openvpn['local_address'] or openvpn['ipv6_local_address'] or openvpn['remote_address'] or openvpn['ipv6_remote_address']:
raise ConfigError('Cannot specify "local-address" or "remote-address" in client-server or bridge mode')
#
@@ -603,8 +707,15 @@ def verify(openvpn):
if not openvpn['tls_dh'] and not checkCertHeader('-----BEGIN EC PRIVATE KEY-----', openvpn['tls_key']):
raise ConfigError('Must specify "tls dh-file" when not using EC keys in server mode')
+ if len(openvpn['server_subnet']) > 1 or len(openvpn['server_ipv6_subnet']) > 1:
+ raise ConfigError('Cannot specify more than 1 IPv4 and 1 IPv6 server subnet')
+
+ for client in openvpn['client']:
+ if len(client['ip']) > 1 or len(client['ipv6_ip']) > 1:
+ raise ConfigError(f'Server client "{client["name"]}": cannot specify more than 1 IPv4 and 1 IPv6 IP')
+
if openvpn['server_subnet']:
- subnet = IPv4Network(openvpn['server_subnet'].replace(' ', '/'))
+ subnet = IPv4Network(openvpn['server_subnet'][0].replace(' ', '/'))
if openvpn['type'] == 'tun' and subnet.prefixlen > 29:
raise ConfigError('Server subnets smaller than /29 with device type "tun" are not supported')
@@ -612,8 +723,8 @@ def verify(openvpn):
raise ConfigError('Server subnets smaller than /30 with device type "tap" are not supported')
for client in openvpn['client']:
- if client['ip'] and not IPv4Address(client['ip']) in subnet:
- raise ConfigError(f'Client IP "{client["ip"]}" not in server subnet "{subnet}"')
+ if client['ip'] and not IPv4Address(client['ip'][0]) in subnet:
+ raise ConfigError(f'Client "{client["name"]}" IP {client["ip"][0]} not in server subnet {subnet}')
else:
if not openvpn['bridge_member']:
@@ -627,17 +738,56 @@ def verify(openvpn):
v4PoolStop = IPv4Address(openvpn['server_pool_stop'])
if v4PoolStart > v4PoolStop:
raise ConfigError(f'Server client-ip-pool start address {v4PoolStart} is larger than stop address {v4PoolStop}')
- if (int(v4PoolStop) - int(v4PoolStart) >= 65536):
- raise ConfigError(f'Server client-ip-pool is too large [{v4PoolStart} -> {v4PoolStop}], maximum is 65536 addresses.')
+
+ v4PoolSize = int(v4PoolStop) - int(v4PoolStart)
+ if v4PoolSize >= 65536:
+ raise ConfigError(f'Server client-ip-pool is too large [{v4PoolStart} -> {v4PoolStop} = {v4PoolSize}], maximum is 65536 addresses.')
v4PoolNets = list(summarize_address_range(v4PoolStart, v4PoolStop))
for client in openvpn['client']:
if client['ip']:
for v4PoolNet in v4PoolNets:
- if IPv4Address(client['ip']) in v4PoolNet:
- print(f'Warning: Client "{client["name"]}" IP {client["ip"]} is in server IP pool, it is not reserved for this client.',
+ if IPv4Address(client['ip'][0]) in v4PoolNet:
+ print(f'Warning: Client "{client["name"]}" IP {client["ip"][0]} is in server IP pool, it is not reserved for this client.',
+ file=stderr)
+
+ if openvpn['server_ipv6_subnet']:
+ if not openvpn['server_subnet']:
+ raise ConfigError('IPv6 server requires an IPv4 server subnet')
+
+ if openvpn['server_ipv6_pool']:
+ if not openvpn['server_pool']:
+ raise ConfigError('IPv6 server pool requires an IPv4 server pool')
+
+ if int(openvpn['server_ipv6_pool_prefixlen']) >= 112:
+ raise ConfigError('IPv6 server pool must be larger than /112')
+
+ v6PoolStart = IPv6Address(openvpn['server_ipv6_pool_base'])
+ v6PoolStop = IPv6Network((v6PoolStart, openvpn['server_ipv6_pool_prefixlen']), strict=False)[-1] # don't remove the parentheses, it's a 2-tuple
+ v6PoolSize = int(v6PoolStop) - int(v6PoolStart) if int(openvpn['server_ipv6_pool_prefixlen']) > 96 else 65536
+ if v6PoolSize < v4PoolSize:
+ raise ConfigError(f'IPv6 server pool must be at least as large as the IPv4 pool (current sizes: IPv6={v6PoolSize} IPv4={v4PoolSize})')
+
+ v6PoolNets = list(summarize_address_range(v6PoolStart, v6PoolStop))
+ for client in openvpn['client']:
+ if client['ipv6_ip']:
+ for v6PoolNet in v6PoolNets:
+ if IPv6Address(client['ipv6_ip'][0]) in v6PoolNet:
+ print(f'Warning: Client "{client["name"]}" IP {client["ipv6_ip"][0]} is in server IP pool, it is not reserved for this client.',
file=stderr)
+ else:
+ if openvpn['server_ipv6_push_route']:
+ raise ConfigError('IPv6 push-route requires an IPv6 server subnet')
+
+ for client in openvpn ['client']:
+ if client['ipv6_ip']:
+ raise ConfigError(f'Server client "{client["name"]}" IPv6 IP requires an IPv6 server subnet')
+ if client['ipv6_push_route']:
+ raise ConfigError(f'Server client "{client["name"]} IPv6 push-route requires an IPv6 server subnet"')
+ if client['ipv6_subnet']:
+ raise ConfigError(f'Server client "{client["name"]} IPv6 subnet requires an IPv6 server subnet"')
+
else:
# checks for both client and site-to-site go here
if openvpn['server_reject_unconfigured']:
diff --git a/src/validators/ipv6 b/src/validators/ipv6
new file mode 100755
index 000000000..f18d4a63e
--- /dev/null
+++ b/src/validators/ipv6
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+ipaddrcheck --is-ipv6 $1