summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/interfaces_openvpn.py64
1 files changed, 64 insertions, 0 deletions
diff --git a/src/conf_mode/interfaces_openvpn.py b/src/conf_mode/interfaces_openvpn.py
index a2367cfcb..1f85d98f1 100755
--- a/src/conf_mode/interfaces_openvpn.py
+++ b/src/conf_mode/interfaces_openvpn.py
@@ -76,6 +76,26 @@ group = 'openvpn'
cfg_dir = '/run/openvpn'
cfg_file = '/run/openvpn/{ifname}.conf'
+# Ciphers implemented by the in-tree "ovpn" Kernel module. Any other cipher
+# must be handled in userspace and thus rules out DCO. The module also does
+# ChaCha20-Poly1305, which the CLI does not offer.
+dco_ciphers = ['aes128gcm', 'aes192gcm', 'aes256gcm']
+# Raw options that make OpenVPN fall back to the userspace data path, taken
+# from dco_check_option() and dco_check_option_ce()
+dco_incompatible_options = [
+ 'comp-lzo',
+ 'disable-dco',
+ 'fragment',
+ 'http-proxy',
+ 'management-query-proxy',
+ 'socks-proxy',
+]
+# these rule out the offload for every value but one - notably "compress
+# migrate", which is what OpenVPN suggests to keep it
+dco_conditional_options = {
+ 'allow-compression': 'no',
+ 'compress': 'migrate',
+}
otp_path = '/config/auth/openvpn'
otp_file = '/config/auth/openvpn/{ifname}-otp-secrets'
secret_chars = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
@@ -209,6 +229,49 @@ def verify_data_ciphers_fallback(openvpn):
if dict_search('encryption.data_ciphers_fallback', openvpn):
raise ConfigError('Cipher fallback is valid only in site-to-site mode')
+def verify_dco(openvpn):
+ if dict_search('offload.dco', openvpn) is None:
+ return
+
+ if openvpn['device_type'] != 'tun':
+ raise ConfigError('DCO requires "device-type tun"')
+
+ if openvpn['mode'] == 'server':
+ topology = dict_search('server.topology', openvpn)
+ if topology != 'subnet':
+ raise ConfigError(
+ f'DCO requires "server topology subnet", got "{topology}"'
+ )
+
+ if 'shared_secret_key' in openvpn:
+ raise ConfigError('DCO is incompatible with "shared-secret-key"')
+
+ if 'use_lzo_compression' in openvpn:
+ raise ConfigError('DCO is incompatible with "use-lzo-compression"')
+
+ ciphers = dict_search('encryption.data_ciphers', openvpn) or []
+ fallback = dict_search('encryption.data_ciphers_fallback', openvpn)
+ if fallback:
+ ciphers = ciphers + [fallback]
+
+ for cipher in ciphers:
+ if cipher not in dco_ciphers:
+ raise ConfigError(f'DCO does not support cipher "{cipher}"')
+
+ # A raw option OpenVPN refuses to offload leaves the daemon on the
+ # userspace data path, where it can not use the interface it was given
+ for option in dict_search('openvpn_option', openvpn) or []:
+ tmp = option.split()
+ if not tmp:
+ continue
+ keyword = tmp[0].lstrip('-')
+ if keyword in dco_incompatible_options:
+ raise ConfigError(f'DCO is incompatible with "openvpn-option {keyword}"')
+ if keyword in dco_conditional_options:
+ keep = dco_conditional_options[keyword]
+ if tmp[1:] != [keep]:
+ raise ConfigError(f'DCO requires "openvpn-option {keyword} {keep}"')
+
def verify_pki(openvpn):
pki = openvpn['pki']
interface = openvpn['ifname']
@@ -662,6 +725,7 @@ def verify(openvpn):
verify_mirror_redirect(openvpn)
verify_data_ciphers_fallback(openvpn)
+ verify_dco(openvpn)
return None