]> git.proxmox.com Git - mirror_ifupdown2.git/commitdiff
Merge pull request #111 from svenauhagen/feature/xfrm
authorJulien Fortin <julien@cumulusnetworks.com>
Thu, 6 Jun 2019 16:22:39 +0000 (00:22 +0800)
committerGitHub <noreply@github.com>
Thu, 6 Jun 2019 16:22:39 +0000 (00:22 +0800)
This commit adds support for the new IPSec Interface XFRM.

etc/network/ifupdown2/addons.conf
ifupdown2/addons/xfrm.py [new file with mode: 0644]
ifupdown2/ifupdown/netlink.py
ifupdown2/ifupdownaddons/LinkUtils.py
ifupdown2/nlmanager/nlmanager.py
ifupdown2/nlmanager/nlpacket.py

index df7e935fd23e016d2a8860c5caca0924e8cfa75d..dd3d2237e33edcc03c62727fbb4a5832946e419b 100644 (file)
@@ -1,3 +1,4 @@
+pre-up,xfrm
 pre-up,link
 pre-up,ppp
 pre-up,bond
@@ -39,3 +40,4 @@ post-down,batman_adv
 post-down,usercmds
 post-down,link
 post-down,tunnel
+post-down,xfrm
\ No newline at end of file
diff --git a/ifupdown2/addons/xfrm.py b/ifupdown2/addons/xfrm.py
new file mode 100644 (file)
index 0000000..d2e4e7f
--- /dev/null
@@ -0,0 +1,166 @@
+#!/usr/bin/python
+#
+# Copyright 2019 Voleatech GmbH. All rights reserved.
+# Author: Sven Auhagen, sven.auhagen@voleatech.de
+#
+
+import os
+import glob
+import socket
+
+from ipaddr import IPNetwork, IPv6Network
+
+try:
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdown.netlink import netlink
+
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+
+    import ifupdown2.ifupdown.statemanager as statemanager
+    import ifupdown2.ifupdown.policymanager as policymanager
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
+except ImportError:
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+    from ifupdown.netlink import netlink
+
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+
+    import ifupdown.statemanager as statemanager
+    import ifupdown.policymanager as policymanager
+    import ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown.ifupdownconfig as ifupdownconfig
+
+
+class xfrm(moduleBase):
+    """  ifupdown2 addon module to create a xfrm interface """
+    _modinfo = {'mhelp' : 'xfrm module creates a xfrm interface for',
+                'attrs' : {
+                    'xfrm-id' :
+                        { 'help' : 'xfrm id',
+                          'validrange' : ['1', '65535'],
+                          'example': ['xfrm-id 1'] 
+                        },
+                    'xfrm-physdev':
+                        {'help': 'xfrm physical device',
+                         'example': ['xfrm-physdev lo']
+                        },
+                    },
+                }
+
+
+    def __init__(self, *args, **kargs):
+        moduleBase.__init__(self, *args, **kargs)
+        self.ipcmd = None
+
+    def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
+
+        parent_int = self._get_parent_ifacename(ifaceobj)
+        if parent_int:
+            return [parent_int]
+
+        return None
+
+    def _get_parent_ifacename(self, ifaceobj):
+        if ifaceobj.get_attr_value('xfrm-physdev'):
+            av_attr = ifaceobj.get_attr_value_first('xfrm-physdev')
+            return av_attr
+
+        return None
+
+    def _get_xfrmid(self, ifaceobj):
+        if ifaceobj.get_attr_value('xfrm-id'):
+            av_attr = ifaceobj.get_attr_value_first('xfrm-id')
+            return av_attr
+
+        return None
+
+    def _get_xfrm_name(self, ifaceobj):
+        return ifaceobj.name
+
+    @staticmethod
+    def _is_my_interface(ifaceobj):
+        return ifaceobj.get_attr_value_first('xfrm-id')
+
+    def _up(self, ifaceobj):
+        """
+        Up the XFRM Interface
+        """
+        # Create a xfrm device on this device and set the virtual
+        # router mac and ip on it
+        link_created = False
+        xfrm_ifacename = self._get_xfrm_name(ifaceobj)
+        physdev = self._get_parent_ifacename(ifaceobj)
+        xfrmid = self._get_xfrmid(ifaceobj)
+        if not self.ipcmd.link_exists(xfrm_ifacename):
+            try:
+                netlink.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
+            except:
+                self.ipcmd.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
+            link_created = True
+        else:
+            current_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
+            xfrmid_cur = current_attrs.get('xfrm-id', None)
+            physdev_cur = current_attrs.get('xfrm-physdev', None)
+            # Check XFRM Values
+            if xfrmid != xfrmid_cur or physdev != physdev_cur:
+                # Delete and recreate
+                self.ipcmd.link_delete(xfrm_ifacename)
+                try:
+                    netlink.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
+                except:
+                    self.ipcmd.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
+                link_created = True
+
+    def _down(self, ifaceobj, ifaceobj_getfunc=None):
+        """
+        Down the XFRM Interface
+        """
+        try:
+            xfrm_ifacename = self._get_xfrm_name(ifaceobj)
+            self.ipcmd.link_delete(xfrm_ifacename)
+        except Exception, e:
+            self.log_warn(str(e))
+
+    def _query_check(self, ifaceobj, ifaceobjcurr):
+        if not self.ipcmd.link_exists(ifaceobj.name):
+            return
+        ifaceobjcurr.status = ifaceStatus.SUCCESS
+
+    def _query_running(self, ifaceobjrunning):
+        if not self.ipcmd.link_exists(ifaceobjrunning.name):
+            return
+
+    # Operations supported by this addon (yet).
+    _run_ops = {
+        'pre-up': _up,
+        'post-down': _down,
+        'query-checkcurr': _query_check,
+        'query-running': _query_running,
+    }
+
+    def get_ops(self):
+        return self._run_ops.keys()
+
+    def _init_command_handlers(self):
+        if not self.ipcmd:
+            self.ipcmd = LinkUtils()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+        op_handler = self._run_ops.get(operation)
+
+        if not op_handler:
+            return
+
+        if operation != 'query-running' and not self._is_my_interface(ifaceobj):
+            return
+
+        self._init_command_handlers()
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj)
+        else:
+            op_handler(self, ifaceobj)
index f5aef0f7ce5dadf685095d40833fdb1e041dc3f2..099208b1fe48cf4c5ecebedc404a9e9929070f1b 100644 (file)
@@ -64,7 +64,8 @@ class Netlink(utilsBase):
                 'sit': self._link_dump_info_data_iptun_tunnel,
                 'ip6tnl': self._link_dump_info_data_iptun_tunnel,
                 'vti': self._link_dump_info_data_vti_tunnel,
-                'vti6': self._link_dump_info_data_vti_tunnel
+                'vti6': self._link_dump_info_data_vti_tunnel,
+                'xfrm': self._link_dump_info_data_xfrm
             }
 
         except Exception as e:
@@ -221,6 +222,17 @@ class Netlink(utilsBase):
         except Exception as e:
             raise Exception('netlink: %s: cannot create macvlan %s: %s'
                             % (ifacename, macvlan_ifacename, str(e)))
+    
+    def link_add_xfrm(self, ifacename, xfrm_ifacename, xfrm_id):
+        self.logger.info('%s: netlink: ip link add %s type xfrm dev %s if_id %s'
+                         % (xfrm_ifacename, xfrm_ifacename, ifacename, xfrm_id))
+        if ifupdownflags.flags.DRYRUN: return
+        ifindex = self.get_iface_index(ifacename)
+        try:
+            return self._nlmanager_api.link_add_xfrm(ifindex, xfrm_ifacename, xfrm_id)
+        except Exception as e:
+            raise Exception('netlink: %s: cannot create xfrm %s id %s: %s'
+                            % (ifacename, xfrm_ifacename, xfrm_id, str(e)))
 
     def link_set_updown(self, ifacename, state):
         self.logger.info('%s: netlink: ip link set dev %s %s'
@@ -544,6 +556,14 @@ class Netlink(utilsBase):
             "tunnel-physdev": self.get_iface_name(tunnel_link_ifindex) if tunnel_link_ifindex else ""
         }
 
+    def _link_dump_info_data_xfrm(self, ifname, linkdata):
+        xfrm_physdev_link_ifindex = linkdata.get(Link.IFLA_XFRM_LINK)
+
+        return {
+            'xfrm-id': str(linkdata.get(Link.IFLA_XFRM_IF_ID, '')),
+            'xfrm-physdev': self.get_iface_name(xfrm_physdev_link_ifindex) if xfrm_physdev_link_ifindex else ""
+        }
+
     def _link_dump_linkinfo(self, link, dump):
         linkinfo = link.attributes[Link.IFLA_LINKINFO].get_pretty_value(dict)
 
index d902e686073b86df974838367ff3df7ca231ccd1..d973cbc2ba8219740bbb67abb2eb6982aeefee70 100644 (file)
@@ -586,6 +586,8 @@ class LinkUtils(utilsBase):
                         break
                     elif citems[i] == 'macvlan' and citems[i + 1] == 'mode':
                         linkattrs['kind'] = 'macvlan'
+                    elif citems[i] == 'xfrm':
+                        linkattrs['kind'] = 'xfrm'
                 except Exception as e:
                     if warn:
                         self.logger.debug('%s: parsing error: id, mtu, state, '
@@ -1377,6 +1379,10 @@ class LinkUtils(utilsBase):
     def link_add_macvlan(ifname, macvlan_ifacename, mode):
         utils.exec_commandl(['ip', 'link', 'add',  'link', ifname, 'name', macvlan_ifacename, 'type', 'macvlan', 'mode', mode])
 
+    @staticmethod
+    def link_add_xfrm(ifname, xfrm_name, xfrm_id):
+        utils.exec_commandl(['ip', 'link', 'add', xfrm_name, 'type', 'xfrm', 'dev', ifname, 'if_id', xfrm_id])
+
     @staticmethod
     def route_add(route):
         utils.exec_command('%s route add %s' % (utils.ip_cmd,
index c6279969eaa4c47d149a22006d758dbb8933a864..ba9453d4488a5e288bc4aa0fae21ccaafcf02750 100644 (file)
@@ -699,6 +699,18 @@ class NetlinkManager(object):
         """
         return self._link_add(ifindex, ifname, 'macvlan', {Link.IFLA_MACVLAN_MODE: Link.MACVLAN_MODE_PRIVATE})
 
+    def link_add_xfrm(self, physdev, xfrm_ifname, xfrm_id):
+        """
+        ifindex is the index of the parent interface that this sub-interface
+        is being added to
+        """
+        ifla_info_data = {
+            Link.IFLA_XFRM_IF_ID: int(xfrm_id),
+            Link.IFLA_XFRM_LINK: int(physdev)
+        }
+        
+        return self._link_add(ifindex=None, ifname=xfrm_ifname, kind='xfrm', ifla_info_data=ifla_info_data)
+
     def vlan_get(self, filter_ifindex=None, filter_vlanid=None, compress_vlans=True):
         """
         filter_ifindex should be a tuple if interface indexes, this is a whitelist filter
index 8335dae90dbc1d2e9221daea283f2d8ca10381c6..cd0d332d8a5ddf4e8a2294814b692cba3f0453f7 100644 (file)
@@ -1105,7 +1105,7 @@ class AttributeIFLA_LINKINFO(Attribute):
         kind        = self.value.get(Link.IFLA_INFO_KIND)
         slave_kind  = self.value.get(Link.IFLA_INFO_SLAVE_KIND)
 
-        if not slave_kind and kind not in ('vlan', 'macvlan', 'vxlan', 'bond', 'bridge'):
+        if not slave_kind and kind not in ('vlan', 'macvlan', 'vxlan', 'bond', 'bridge', 'xfrm'):
             raise Exception('Unsupported IFLA_INFO_KIND %s' % kind)
         elif not kind and slave_kind != 'bridge':
             # only support brport for now.
@@ -1177,6 +1177,15 @@ class AttributeIFLA_LINKINFO(Attribute):
                         else:
                             self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_DATA macvlan sub-attribute type %d' % info_data_type)
 
+                    elif kind == 'xfrm':
+                        if info_data_type in (Link.IFLA_XFRM_IF_ID, Link.IFLA_XFRM_LINK):
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(8)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            sub_attr_pack_layout.append('L')
+                            sub_attr_payload.append(info_data_value)
+
                     elif kind == 'vxlan':
                         if info_data_type in (Link.IFLA_VXLAN_ID,
                                               Link.IFLA_VXLAN_LINK,
@@ -1703,6 +1712,11 @@ class AttributeIFLA_LINKINFO(Attribute):
                                     self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_INFO_KIND macvlan type %s (%d), length %d, padded to %d' %
                                                 (parent_msg.get_ifla_macvlan_string(info_data_type), info_data_type, info_data_length, info_data_end))
 
+                            elif ifla_info_kind == 'xfrm':
+                                # 4-byte int
+                                if info_data_type in (Link.IFLA_XFRM_IF_ID, Link.IFLA_XFRM_LINK):
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
+
                             elif ifla_info_kind == 'vxlan':
 
                                 # IPv4Address
@@ -2031,7 +2045,8 @@ class AttributeIFLA_LINKINFO(Attribute):
             'vti6':         Link.ifla_vti_to_string,
             'ipip':         Link.ifla_iptun_to_string,
             'sit':          Link.ifla_iptun_to_string,
-            'ip6tnl':       Link.ifla_iptun_to_string
+            'ip6tnl':       Link.ifla_iptun_to_string,
+            'xfrm':       Link.ifla_xfrm_to_string
         }.get(ifla_info_kind, {})
 
         kind_dict[Link.IFLA_INFO_SLAVE_DATA] = {
@@ -3240,6 +3255,19 @@ class Link(NetlinkPacket):
         MACVLAN_MODE_PASSTHRU : 'MACVLAN_MODE_PASSTHRU'
     }
 
+    # =========================================
+    # IFLA_INFO_DATA attributes for xfrm
+    # =========================================
+    IFLA_XFRM_UNSPEC = 0
+    IFLA_XFRM_LINK   = 1
+    IFLA_XFRM_IF_ID  = 2
+
+    ifla_xfrm_to_string = {
+        IFLA_XFRM_UNSPEC : 'IFLA_XFRM_UNSPEC',
+        IFLA_XFRM_LINK   : 'IFLA_XFRM_LINK',
+        IFLA_XFRM_IF_ID   : 'IFLA_XFRM_IF_ID'
+    }
+
     # =========================================
     # IFLA_INFO_DATA attributes for vxlan
     # =========================================
@@ -3910,6 +3938,9 @@ class Link(NetlinkPacket):
     def get_ifla_macvlan_string(self, index):
         return self.get_string(self.ifla_macvlan_to_string, index)
 
+    def get_ifla_xfrm_string(self, index):
+        return self.get_string(self.ifla_xfrm_to_string, index)
+
     def get_macvlan_mode_string(self, index):
         return self.get_string(self.macvlan_mode_to_string, index)