From 029f9839c21317ec5959b331eee25da472d08dc1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 18 Oct 2020 18:04:34 +0200 Subject: openvpn: T2969: force creation of tunnel interfaces A lot of VyOS code requires the Kernel interface to be present in order to properly work and adjust the interface to the users CLI intends (alias, ipv6, vrf - just to name a few). OpenVPN - when run in client mode - only creates the interface (e.g. vtun1) when the connection to the OpenVPN server was successful. This can't be always the case due to e.g. software-updates or routing issues to the remote side. This will in the end result in a zombie OpenVPN client interface where some config items might not have been set when the interface finally comes up - imagine a wrong assigned VRF instance. By always creating the OpenVPN interface manuall we ensure that all the CLI settings are properly configured in the OS kernel. --- smoketest/scripts/cli/test_interfaces_openvpn.py | 4 +- src/conf_mode/interfaces-openvpn.py | 86 ++++++++++-------------- 2 files changed, 39 insertions(+), 51 deletions(-) diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py index 0ac91c170..2af07a579 100755 --- a/smoketest/scripts/cli/test_interfaces_openvpn.py +++ b/smoketest/scripts/cli/test_interfaces_openvpn.py @@ -17,6 +17,8 @@ import os import unittest +from netifaces import interfaces + from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError from vyos.util import cmd @@ -72,8 +74,8 @@ class TestInterfacesOpenVPN(unittest.TestCase): self.assertIn(f'remote {remote_host}', config) self.assertIn('persist-tun', config) - self.assertTrue(process_named_running(PROCESS_NAME)) + self.assertIn(interface, interfaces()) if __name__ == '__main__': # Our SSL certificates need a subject ... diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index f2b580c6f..9cd72f691 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -32,6 +32,7 @@ from vyos.template import render from vyos.util import call, chown, chmod_600, chmod_755 from vyos.validate import is_addr_assigned, is_ipv4 from vyos import ConfigError +from vyos.util import cmd from vyos import airbag airbag.enable() @@ -1057,56 +1058,41 @@ def apply(openvpn): # existed - nevertheless, spawn new OpenVPN process call(f'systemctl start openvpn@{interface}.service') - # better late then sorry ... but we can only set interface alias after - # OpenVPN has been launched and created the interface - cnt = 0 - while interface not in interfaces(): - # If VPN tunnel can't be established because the peer/server isn't - # (temporarily) available, the vtun interface never becomes registered - # with the kernel, and the commit would hang if there is no bail out - # condition - cnt += 1 - if cnt == 50: - break - - # sleep 250ms - sleep(0.250) - - try: - # we need to catch the exception if the interface is not up due to - # reason stated above - o = VTunIf(interface) - # update interface description used e.g. within SNMP - o.set_alias(openvpn['description']) - # IPv6 accept RA - o.set_ipv6_accept_ra(openvpn['ipv6_accept_ra']) - # IPv6 address autoconfiguration - o.set_ipv6_autoconf(openvpn['ipv6_autoconf']) - # IPv6 forwarding - o.set_ipv6_forwarding(openvpn['ipv6_forwarding']) - # IPv6 Duplicate Address Detection (DAD) tries - o.set_ipv6_dad_messages(openvpn['ipv6_dup_addr_detect']) - - # IPv6 EUI-based addresses - only in TAP mode (TUN's have no MAC) - # If MAC has changed, old EUI64 addresses won't get deleted, - # but this isn't easy to solve, so leave them. - # This is even more difficult as openvpn uses a random MAC for the - # initial interface creation, unless set by 'lladdr'. - # NOTE: right now the interface is always deleted. For future - # compatibility when tap's are not deleted, leave the del_ in - if openvpn['mode'] == 'tap': - for addr in openvpn['ipv6_eui64_prefix_remove']: - o.del_ipv6_eui64_address(addr) - for addr in openvpn['ipv6_eui64_prefix']: - o.add_ipv6_eui64_address(addr) - - # assign/remove VRF (ONLY when not a member of a bridge, - # otherwise 'nomaster' removes it from it) - if not openvpn['is_bridge_member']: - o.set_vrf(openvpn['vrf']) - - except: - pass + if interface not in interfaces(): + dev_type = openvpn['type'] + cmd(f'sudo openvpn --mktun --dev-type {dev_type} --dev {interface}') + + # we need to catch the exception if the interface is not up due to + # reason stated above + o = VTunIf(interface) + # update interface description used e.g. within SNMP + o.set_alias(openvpn['description']) + # IPv6 accept RA + o.set_ipv6_accept_ra(openvpn['ipv6_accept_ra']) + # IPv6 address autoconfiguration + o.set_ipv6_autoconf(openvpn['ipv6_autoconf']) + # IPv6 forwarding + o.set_ipv6_forwarding(openvpn['ipv6_forwarding']) + # IPv6 Duplicate Address Detection (DAD) tries + o.set_ipv6_dad_messages(openvpn['ipv6_dup_addr_detect']) + + # IPv6 EUI-based addresses - only in TAP mode (TUN's have no MAC) + # If MAC has changed, old EUI64 addresses won't get deleted, + # but this isn't easy to solve, so leave them. + # This is even more difficult as openvpn uses a random MAC for the + # initial interface creation, unless set by 'lladdr'. + # NOTE: right now the interface is always deleted. For future + # compatibility when tap's are not deleted, leave the del_ in + if openvpn['mode'] == 'tap': + for addr in openvpn['ipv6_eui64_prefix_remove']: + o.del_ipv6_eui64_address(addr) + for addr in openvpn['ipv6_eui64_prefix']: + o.add_ipv6_eui64_address(addr) + + # assign/remove VRF (ONLY when not a member of a bridge, + # otherwise 'nomaster' removes it from it) + if not openvpn['is_bridge_member']: + o.set_vrf(openvpn['vrf']) # TAP interface needs to be brought up explicitly if openvpn['type'] == 'tap': -- cgit v1.2.3