From 70263054b3bf88ff336325ed88a3e7d8a84afbc0 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Fri, 27 May 2022 14:54:41 +0200 Subject: [PATCH] addons: tunnel: support non-disruptive config change Current design destroys existing tunnel when a config change is detected. This behaviour causes traffic loss. Signed-off-by: Julien Fortin --- ifupdown2/addons/tunnel.py | 25 ++++++++++++++++--------- ifupdown2/lib/iproute2.py | 12 +++++++----- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/ifupdown2/addons/tunnel.py b/ifupdown2/addons/tunnel.py index 772a2bc..3ebcf48 100644 --- a/ifupdown2/addons/tunnel.py +++ b/ifupdown2/addons/tunnel.py @@ -155,6 +155,7 @@ class tunnel(Addon, moduleBase): }.get(link_kind, lambda x: {})(self.cache.get_link_info_data(ifname)) def _up(self, ifaceobj): + ifname = ifaceobj.name attr_map = { # attr_name -> ip route param name 'tunnel-local': 'local', @@ -181,26 +182,32 @@ class tunnel(Addon, moduleBase): if tos and tos != 'inherit': attrs_mapped['tos'] = "{:x}".format(int(tos)) + link_exists = self.cache.link_exists(ifname) + # Create the tunnel if it doesn't exist yet... - if not self.cache.link_exists(ifaceobj.name): - self.iproute2.tunnel_create(ifaceobj.name, mode, attrs_mapped) + if not link_exists: + self.iproute2.tunnel_create(ifname, mode, attrs_mapped) return # If it's present, check if there were changes - current_mode = self.cache.get_link_kind(ifaceobj.name) - current_attrs = self.get_linkinfo_attrs(ifaceobj.name, current_mode) + current_mode = self.cache.get_link_kind(ifname) + current_attrs = self.get_linkinfo_attrs(ifname, current_mode) self.convert_user_config_to_ipnetwork(attrs, "tunnel-local") self.convert_user_config_to_ipnetwork(attrs, "tunnel-endpoint") try: if current_attrs and current_mode != mode or self._has_config_changed(current_attrs, attrs): - # Mode and some other changes are not possible without recreating the interface, - # so just recreate it IFF there have been changes. - self.netlink.link_del(ifaceobj.name) - self.iproute2.tunnel_create(ifaceobj.name, mode, attrs_mapped) + + if link_exists and current_mode != mode: + # Mode and some other changes are not possible without recreating the interface, + # so just recreate it IFF there have been changes. + self.netlink.link_del(ifaceobj.name) + link_exists = False + + self.iproute2.tunnel_create(ifaceobj.name, mode, attrs_mapped, link_exists=link_exists) except Exception as e: - self.log_warn(str(e)) + self.log_error(str(e), ifaceobj) def _down(self, ifaceobj): if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists(ifaceobj.name): diff --git a/ifupdown2/lib/iproute2.py b/ifupdown2/lib/iproute2.py index 868b91f..cc2b859 100644 --- a/ifupdown2/lib/iproute2.py +++ b/ifupdown2/lib/iproute2.py @@ -443,18 +443,20 @@ class IPRoute2(Cache, Requirements): # TUNNEL ############################################################################ - def tunnel_create(self, tunnelname, mode, attrs=None): - if self.cache.link_exists(tunnelname): - return + def tunnel_create(self, tunnelname, mode, attrs=None, link_exists=False): + if link_exists: + op = "change" + else: + op = "add" cmd = [] if "6" in mode: cmd.append("-6") if mode in ["gretap"]: - cmd.append("link add %s type %s" % (tunnelname, mode)) + cmd.append("link %s %s type %s" % (op, tunnelname, mode)) else: - cmd.append("tunnel add %s mode %s" % (tunnelname, mode)) + cmd.append("tunnel %s %s mode %s" % (op, tunnelname, mode)) if attrs: for k, v in attrs.items():