]> git.proxmox.com Git - mirror_ifupdown2.git/blobdiff - ifupdown2/addons/vxlan.py
addons: vxlan: fix vxlan-port example type
[mirror_ifupdown2.git] / ifupdown2 / addons / vxlan.py
index a367e99ede169e115b21a119716e279854af307b..abbad44c4b5160daec8278b73d16a4514fab1e94 100644 (file)
 #!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
 
-from ifupdown.iface import *
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.iproute2 import iproute2
-import ifupdown.rtnetlink_api as rtnetlink_api
-import logging
 from sets import Set
+from ipaddr import IPNetwork, IPv4Address, IPv4Network, AddressValueError
+
+try:
+    import ifupdown2.ifupdown.policymanager as policymanager
+
+    from ifupdown2.nlmanager.nlmanager import Link
+
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdown.netlink import netlink
+    from ifupdown2.ifupdownaddons.cache import *
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+except ImportError:
+    import ifupdown.policymanager as policymanager
+
+    from nlmanager.nlmanager import Link
+
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+    from ifupdown.netlink import netlink
+
+    from ifupdownaddons.cache import *
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+
 
 class vxlan(moduleBase):
     _modinfo = {'mhelp' : 'vxlan module configures vxlan interfaces.',
                 'attrs' : {
                         'vxlan-id' :
                             {'help' : 'vxlan id',
+                             'validrange' : ['1', '16777214'],
                              'required' : True,
                              'example': ['vxlan-id 100']},
                         'vxlan-local-tunnelip' :
                             {'help' : 'vxlan local tunnel ip',
+                             'validvals' : ['<ipv4>'],
                              'example': ['vxlan-local-tunnelip 172.16.20.103']},
                         'vxlan-svcnodeip' :
                             {'help' : 'vxlan id',
+                             'validvals' : ['<ipv4>'],
                              'example': ['vxlan-svcnodeip 172.16.22.125']},
                         'vxlan-remoteip' :
                             {'help' : 'vxlan remote ip',
-                             'example': ['vxlan-remoteip 172.16.22.127']},
+                             'validvals' : ['<ipv4>'],
+                             'example': ['vxlan-remoteip 172.16.22.127'],
+                             'multiline': True},
                         'vxlan-learning' :
-                            {'help' : 'vxlan learning on/off',
-                             'example': ['vxlan-learning off'],
-                             'default': 'on'},
+                            {'help' : 'vxlan learning yes/no',
+                             'validvals' : ['yes', 'no', 'on', 'off'],
+                             'example': ['vxlan-learning no'],
+                             'default': 'yes'},
+                        'vxlan-ageing' :
+                            {'help' : 'vxlan aging timer',
+                             'validrange' : ['0', '4096'],
+                             'example': ['vxlan-ageing 300'],
+                             'default': '300'},
+                        'vxlan-purge-remotes' :
+                            {'help' : 'vxlan purge existing remote entries',
+                             'validvals' : ['yes', 'no'],
+                             'example': ['vxlan-purge-remotes yes'],},
+                    'vxlan-port': {
+                        'help': 'vxlan UDP port (transmitted to vxlan driver)',
+                        'validvals': ['<number>'],
+                        'example': ['vxlan-port 4789'],
+                        'validrange': ['1', '65536'],
+                        'default': '4789',
+                    },
+                    'vxlan-physdev':
+                        {'help': 'vxlan physical device',
+                         'example': ['vxlan-physdev eth1']},
+
                 }}
+    _clagd_vxlan_anycast_ip = ""
+    _vxlan_local_tunnelip = None
 
     def __init__(self, *args, **kargs):
         moduleBase.__init__(self, *args, **kargs)
         self.ipcmd = None
+        purge_remotes = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vxlan-purge-remotes')
+        if purge_remotes:
+            self._purge_remotes = utils.get_boolean_from_string(purge_remotes)
+        else:
+            self._purge_remotes = False
+
+    def syntax_check(self, ifaceobj, ifaceobj_getfunc):
+        if self._is_vxlan_device(ifaceobj):
+            if not ifaceobj.get_attr_value_first('vxlan-local-tunnelip') and not vxlan._vxlan_local_tunnelip:
+                self.logger.warning('%s: missing vxlan-local-tunnelip' % ifaceobj.name)
+                return False
+            return self.syntax_check_localip_anycastip_equal(
+                ifaceobj.name,
+                ifaceobj.get_attr_value_first('vxlan-local-tunnelip') or vxlan._vxlan_local_tunnelip,
+                vxlan._clagd_vxlan_anycast_ip
+            )
+        return True
+
+    def syntax_check_localip_anycastip_equal(self, ifname, local_ip, anycast_ip):
+        try:
+            if IPNetwork(local_ip) == IPNetwork(anycast_ip):
+                self.logger.warning('%s: vxlan-local-tunnelip and clagd-vxlan-anycast-ip are identical (%s)'
+                                    % (ifname, local_ip))
+                return False
+        except:
+            pass
+        return True
 
     def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None):
-        if not self._is_vxlan_device(ifaceobj):
-            return None
-        ifaceobj.link_kind |= ifaceLinkKind.VXLAN
+        if self._is_vxlan_device(ifaceobj):
+            ifaceobj.link_kind |= ifaceLinkKind.VXLAN
+            self._set_global_local_ip(ifaceobj)
+        elif ifaceobj.name == 'lo':
+            clagd_vxlan_list = ifaceobj.get_attr_value('clagd-vxlan-anycast-ip')
+            if clagd_vxlan_list:
+                if len(clagd_vxlan_list) != 1:
+                    self.log_warn('%s: multiple clagd-vxlan-anycast-ip lines, using first one'
+                                  % (ifaceobj.name,))
+                vxlan._clagd_vxlan_anycast_ip = clagd_vxlan_list[0]
+
+            self._set_global_local_ip(ifaceobj)
+
+        # If we should use a specific underlay device for the VXLAN
+        # tunnel make sure this device is set up before the VXLAN iface.
+        physdev = ifaceobj.get_attr_value_first('vxlan-physdev')
+
+        if physdev:
+            return [physdev]
+
         return None
 
+    def _set_global_local_ip(self, ifaceobj):
+        vxlan_local_tunnel_ip = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
+        if vxlan_local_tunnel_ip and not vxlan._vxlan_local_tunnelip:
+            vxlan._vxlan_local_tunnelip = vxlan_local_tunnel_ip
+
     def _is_vxlan_device(self, ifaceobj):
         if ifaceobj.get_attr_value_first('vxlan-id'):
             return True
         return False
 
-    def _up(self, ifaceobj):
+    def _get_purge_remotes(self, ifaceobj):
+        if not ifaceobj:
+            return self._purge_remotes
+        purge_remotes = ifaceobj.get_attr_value_first('vxlan-purge-remotes')
+        if purge_remotes:
+            purge_remotes = utils.get_boolean_from_string(purge_remotes)
+        else:
+            purge_remotes = self._purge_remotes
+        return purge_remotes
+
+    def should_create_set_vxlan(self, link_exists, ifname, vxlan_id, local, learning, ageing, group):
+        """
+            should we issue a netlink: ip link add dev %ifname type vxlan ...?
+            checking each attribute against the cache
+        """
+        if not link_exists:
+            return True
+
+        try:
+            if ageing:
+                ageing = int(ageing)
+        except:
+            pass
+
+        for attr_list, value in (
+            ((ifname, 'linkinfo', Link.IFLA_VXLAN_ID), vxlan_id),
+            ((ifname, 'linkinfo', Link.IFLA_VXLAN_AGEING), ageing),
+            ((ifname, 'linkinfo', Link.IFLA_VXLAN_LOCAL), local),
+            ((ifname, 'linkinfo', Link.IFLA_VXLAN_LEARNING), learning),
+            ((ifname, 'linkinfo', Link.IFLA_VXLAN_GROUP), group),
+        ):
+            if value and not self.ipcmd.cache_check(attr_list, value):
+                return True
+        return False
+
+    def _vxlan_create(self, ifaceobj):
         vxlanid = ifaceobj.get_attr_value_first('vxlan-id')
         if vxlanid:
-            self.ipcmd.link_create_vxlan(ifaceobj.name, vxlanid,
-            localtunnelip=ifaceobj.get_attr_value_first('vxlan-local-tunnelip'),
-            svcnodeips=ifaceobj.get_attr_value('vxlan-svcnodeip'),
-            remoteips=ifaceobj.get_attr_value('vxlan-remoteip'),
-            learning=ifaceobj.get_attr_value_first('vxlan-learning'),
-            ageing=ifaceobj.get_attr_value_first('vxlan-ageing'))
-            if ifaceobj.addr_method == 'manual':
-               rtnetlink_api.rtnl_api.link_set(ifaceobj.name, "up")
+            ifname = ifaceobj.name
+            anycastip = self._clagd_vxlan_anycast_ip
+            group = ifaceobj.get_attr_value_first('vxlan-svcnodeip')
+
+            local = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
+            if not local and vxlan._vxlan_local_tunnelip:
+                local = vxlan._vxlan_local_tunnelip
+
+            self.syntax_check_localip_anycastip_equal(ifname, local, anycastip)
+            # if both local-ip and anycast-ip are identical the function prints a warning
+
+            ageing = ifaceobj.get_attr_value_first('vxlan-ageing')
+            vxlan_port = ifaceobj.get_attr_value_first('vxlan-port')
+            physdev = ifaceobj.get_attr_value_first('vxlan-physdev')
+            purge_remotes = self._get_purge_remotes(ifaceobj)
+
+            link_exists = self.ipcmd.link_exists(ifname)
+
+            if (not link_exists or
+                not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
+                vxlan_learning = ifaceobj.get_attr_value_first('vxlan-learning')
+                if not vxlan_learning:
+                    vxlan_learning = self.get_attr_default_value('vxlan-learning')
+                learning = utils.get_boolean_from_string(vxlan_learning)
+            else:
+                learning = utils.get_boolean_from_string(
+                                self.ipcmd.get_vxlandev_learning(ifname))
+
+            if link_exists:
+                vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifname)
+                # on ifreload do not overwrite anycast_ip to individual ip
+                # if clagd has modified
+                if vxlanattrs:
+                    running_localtunnelip = vxlanattrs.get('local')
+                    if (anycastip and running_localtunnelip and
+                                anycastip == running_localtunnelip):
+                        local = running_localtunnelip
+                    if vxlanattrs.get('vxlanid') != vxlanid:
+                        self.log_error('%s: Cannot change running vxlan id: '
+                                       'Operation not supported' % ifname, ifaceobj)
+            try:
+                vxlanid = int(vxlanid)
+            except:
+                self.log_error('%s: invalid vxlan-id \'%s\'' % (ifname, vxlanid), ifaceobj)
+
+            if not group:
+                group = policymanager.policymanager_api.get_attr_default(
+                    module_name=self.__class__.__name__,
+                    attr='vxlan-svcnodeip'
+                )
+
+            if group:
+                try:
+                    group = IPv4Address(group)
+                except AddressValueError:
+                    try:
+                        group_ip = IPv4Network(group).ip
+                        self.logger.warning('%s: vxlan-svcnodeip %s: netmask ignored' % (ifname, group))
+                        group = group_ip
+                    except:
+                        raise Exception('%s: invalid vxlan-svcnodeip %s: must be in ipv4 format' % (ifname, group))
+
+            if not local:
+                local = policymanager.policymanager_api.get_attr_default(
+                    module_name=self.__class__.__name__,
+                    attr='vxlan-local-tunnelip'
+                )
+
+            if local:
+                try:
+                    local = IPv4Address(local)
+                except AddressValueError:
+                    try:
+                        local_ip = IPv4Network(local).ip
+                        self.logger.warning('%s: vxlan-local-tunnelip %s: netmask ignored' % (ifname, local))
+                        local = local_ip
+                    except:
+                        raise Exception('%s: invalid vxlan-local-tunnelip %s: must be in ipv4 format' % (ifname, local))
+
+            if not ageing:
+                ageing = policymanager.policymanager_api.get_attr_default(
+                    module_name=self.__class__.__name__,
+                    attr='vxlan-ageing'
+                )
+
+                if not ageing and link_exists:
+                    # if link doesn't exist we let the kernel define ageing
+                    ageing = self.get_attr_default_value('vxlan-ageing')
+
+            if not vxlan_port:
+                vxlan_port = policymanager.policymanager_api.get_attr_default(
+                    module_name=self.__class__.__name__,
+                    attr='vxlan-port'
+                )
+
+            try:
+                vxlan_port = int(vxlan_port)
+            except TypeError:
+                # TypeError means vxlan_port was None
+                # ie: not provided by the user or the policy
+                vxlan_port = netlink.VXLAN_UDP_PORT
+            except ValueError as e:
+                self.logger.warning('%s: vxlan-port: using default %s: invalid configured value %s' % (ifname, netlink.VXLAN_UDP_PORT, str(e)))
+                vxlan_port = netlink.VXLAN_UDP_PORT
+
+            if link_exists:
+                cache_port = vxlanattrs.get(Link.IFLA_VXLAN_PORT)
+                if vxlan_port != cache_port:
+                    self.logger.warning('%s: vxlan-port (%s) cannot be changed - to apply the desired change please run: ifdown %s && ifup %s'
+                                        % (ifname, cache_port, ifname, ifname))
+                    vxlan_port = cache_port
+
+            if self.should_create_set_vxlan(link_exists, ifname, vxlanid, local, learning, ageing, group):
+                try:
+                    netlink.link_add_vxlan(ifname, vxlanid,
+                                           local=local,
+                                           learning=learning,
+                                           ageing=ageing,
+                                           group=group,
+                                           dstport=vxlan_port,
+                                           physdev=physdev)
+                except Exception as e_netlink:
+                    self.logger.debug('%s: vxlan netlink: %s' % (ifname, str(e_netlink)))
+                    try:
+                        self.ipcmd.link_create_vxlan(ifname, vxlanid,
+                                                     localtunnelip=local,
+                                                     svcnodeip=group,
+                                                     remoteips=ifaceobj.get_attr_value('vxlan-remoteip'),
+                                                     learning='on' if learning else 'off',
+                                                     ageing=ageing)
+                    except Exception as e_iproute2:
+                        self.logger.warning('%s: vxlan add/set failed: %s' % (ifname, str(e_iproute2)))
+                        return
+
+                try:
+                    # manually adding an entry to the caching after creating/updating the vxlan
+                    if not ifname in linkCache.links:
+                        linkCache.links[ifname] = {'linkinfo': {}}
+                    linkCache.links[ifname]['linkinfo'].update({
+                        'learning': learning,
+                        Link.IFLA_VXLAN_LEARNING: learning,
+                        'vxlanid': str(vxlanid),
+                        Link.IFLA_VXLAN_ID: vxlanid
+                    })
+                    if ageing:
+                        linkCache.links[ifname]['linkinfo'].update({
+                            'ageing': ageing,
+                            Link.IFLA_VXLAN_AGEING: int(ageing)
+                        })
+                except:
+                    pass
+            else:
+                self.logger.info('%s: vxlan already exists' % ifname)
+                # if the vxlan already exists it's already cached
+
+            remoteips = ifaceobj.get_attr_value('vxlan-remoteip')
+            if remoteips:
+                try:
+                    for remoteip in remoteips:
+                        IPv4Address(remoteip)
+                except Exception as e:
+                    self.log_error('%s: vxlan-remoteip: %s' %(ifaceobj.name, str(e)))
+
+            if purge_remotes or remoteips:
+                # figure out the diff for remotes and do the bridge fdb updates
+                # only if provisioned by user and not by an vxlan external
+                # controller.
+                peers = self.ipcmd.get_vxlan_peers(ifaceobj.name, group)
+                if local and remoteips and local in remoteips:
+                    remoteips.remove(local)
+                cur_peers = set(peers)
+                if remoteips:
+                    new_peers = set(remoteips)
+                    del_list = cur_peers.difference(new_peers)
+                    add_list = new_peers.difference(cur_peers)
+                else:
+                    del_list = cur_peers
+                    add_list = []
+
+                for addr in del_list:
+                    try:
+                        self.ipcmd.bridge_fdb_del(ifaceobj.name,
+                                                  '00:00:00:00:00:00',
+                                                  None, True, addr)
+                    except:
+                        pass
+
+                for addr in add_list:
+                    try:
+                        self.ipcmd.bridge_fdb_append(ifaceobj.name,
+                                                     '00:00:00:00:00:00',
+                                                     None, True, addr)
+                    except:
+                        pass
+
+    def _up(self, ifaceobj):
+        self._vxlan_create(ifaceobj)
 
     def _down(self, ifaceobj):
         try:
@@ -62,8 +389,10 @@ class vxlan(moduleBase):
         except Exception, e:
             self.log_warn(str(e))
 
-    def _query_check_n_update(self, ifaceobjcurr, attrname, attrval,
+    def _query_check_n_update(self, ifaceobj, ifaceobjcurr, attrname, attrval,
                               running_attrval):
+        if not ifaceobj.get_attr_value_first(attrname):
+            return
         if running_attrval and attrval == running_attrval:
            ifaceobjcurr.update_config_with_status(attrname, attrval, 0)
         else:
@@ -72,7 +401,7 @@ class vxlan(moduleBase):
     def _query_check_n_update_addresses(self, ifaceobjcurr, attrname,
                                         addresses, running_addresses):
         if addresses:
-            for a in addresses: 
+            for a in addresses:
                 if a in running_addresses:
                     ifaceobjcurr.update_config_with_status(attrname, a, 0)
                 else:
@@ -91,32 +420,79 @@ class vxlan(moduleBase):
             ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
                     self.get_mod_attrs(), -1)
             return
-        self._query_check_n_update(ifaceobjcurr, 'vxlan-id',
-                       ifaceobj.get_attr_value_first('vxlan-id'), 
+        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-id',
+                       ifaceobj.get_attr_value_first('vxlan-id'),
                        vxlanattrs.get('vxlanid'))
 
-        self._query_check_n_update(ifaceobjcurr, 'vxlan-local-tunnelip',
-                       ifaceobj.get_attr_value_first('vxlan-local-tunnelip'), 
-                       vxlanattrs.get('local'))
+        self._query_check_n_update(
+            ifaceobj,
+            ifaceobjcurr,
+            'vxlan-port',
+            ifaceobj.get_attr_value_first('vxlan-port'),
+            str(vxlanattrs.get(Link.IFLA_VXLAN_PORT))
+        )
+
+        running_attrval = vxlanattrs.get('local')
+        attrval = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
+        if not attrval:
+            attrval = vxlan._vxlan_local_tunnelip
+            ifaceobj.update_config('vxlan-local-tunnelip', attrval)
 
-        self._query_check_n_update_addresses(ifaceobjcurr, 'vxlan-svcnodeip',
-                       ifaceobj.get_attr_value('vxlan-svcnodeip'), 
-                       vxlanattrs.get('svcnode', []))
+        if running_attrval == self._clagd_vxlan_anycast_ip:
+            # if local ip is anycast_ip, then let query_check to go through
+            attrval = self._clagd_vxlan_anycast_ip
+        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-local-tunnelip',
+                                   attrval, running_attrval)
 
-        self._query_check_n_update_addresses(ifaceobjcurr, 'vxlan-remoteip',
-                       ifaceobj.get_attr_value('vxlan-remoteip'), 
-                       vxlanattrs.get('remote', []))
+        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-svcnodeip',
+                       ifaceobj.get_attr_value_first('vxlan-svcnodeip'),
+                       vxlanattrs.get('svcnode'))
+
+        purge_remotes = self._get_purge_remotes(ifaceobj)
+        if purge_remotes or ifaceobj.get_attr_value('vxlan-remoteip'):
+            # If purge remotes or if vxlan-remoteip's are set
+            # in the config file, we are owners of the installed
+            # remote-ip's, lets check and report any remote ips we don't
+            # understand
+            self._query_check_n_update_addresses(ifaceobjcurr, 'vxlan-remoteip',
+                           ifaceobj.get_attr_value('vxlan-remoteip'),
+                                                 self.ipcmd.get_vxlan_peers(ifaceobj.name, vxlanattrs.get('svcnode')))
 
         learning = ifaceobj.get_attr_value_first('vxlan-learning')
-        if not learning:
-            learning = 'on'
-        running_learning = vxlanattrs.get('learning')
-        if learning == running_learning:
-           ifaceobjcurr.update_config_with_status('vxlan-learning',
-                                                  running_learning, 0)
-        else:
-           ifaceobjcurr.update_config_with_status('vxlan-learning',
-                                                  running_learning, 1)
+        if learning:
+            running_learning = vxlanattrs.get('learning')
+            if learning == 'yes' and running_learning == 'on':
+                running_learning = 'yes'
+            elif learning == 'no' and running_learning == 'off':
+                running_learning = 'no'
+            if learning == running_learning:
+                ifaceobjcurr.update_config_with_status('vxlan-learning',
+                                                        running_learning, 0)
+            else:
+                ifaceobjcurr.update_config_with_status('vxlan-learning',
+                                                        running_learning, 1)
+        ageing = ifaceobj.get_attr_value_first('vxlan-ageing')
+        if not ageing:
+            ageing = self.get_mod_subattr('vxlan-ageing', 'default')
+        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-ageing',
+                       ageing, vxlanattrs.get('ageing'))
+
+        physdev = ifaceobj.get_attr_value_first('vxlan-physdev')
+
+        if physdev:
+            ifla_vxlan_link = vxlanattrs.get(Link.IFLA_VXLAN_LINK)
+
+            if ifla_vxlan_link:
+                self._query_check_n_update(
+                    ifaceobj,
+                    ifaceobjcurr,
+                    'vxlan-physdev',
+                    physdev,
+                    netlink.get_iface_name(ifla_vxlan_link)
+                )
+            else:
+                ifaceobjcurr.update_config_with_status('vxlan-physdev', physdev, 1)
+
 
     def _query_running(self, ifaceobjrunning):
         vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifaceobjrunning.name)
@@ -125,21 +501,39 @@ class vxlan(moduleBase):
         attrval = vxlanattrs.get('vxlanid')
         if attrval:
             ifaceobjrunning.update_config('vxlan-id', vxlanattrs.get('vxlanid'))
+        else:
+            # if there is no vxlan id, this is not a vxlan port
+            return
+
+        ifaceobjrunning.update_config('vxlan-port', vxlanattrs.get(Link.IFLA_VXLAN_PORT))
+
         attrval = vxlanattrs.get('local')
         if attrval:
             ifaceobjrunning.update_config('vxlan-local-tunnelip', attrval)
         attrval = vxlanattrs.get('svcnode')
         if attrval:
-            [ifaceobjrunning.update_config('vxlan-svcnode', a)
-                        for a in attrval]
-        attrval = vxlanattrs.get('remote')
-        if attrval:
-            [ifaceobjrunning.update_config('vxlan-remoteip', a)
-                        for a in attrval]
+            ifaceobjrunning.update_config('vxlan-svcnode', attrval)
+        purge_remotes = self._get_purge_remotes(None)
+        if purge_remotes:
+            # if purge_remotes is on, it means we own the
+            # remote ips. Query them and add it to the running config
+            attrval = self.ipcmd.get_vxlan_peers(ifaceobjrunning.name, vxlanattrs.get('svcnode'))
+            if attrval:
+                [ifaceobjrunning.update_config('vxlan-remoteip', a)
+                            for a in attrval]
         attrval = vxlanattrs.get('learning')
         if attrval and attrval == 'on':
             ifaceobjrunning.update_config('vxlan-learning', 'on')
+        attrval = vxlanattrs.get('ageing')
+        if attrval:
+            ifaceobjrunning.update_config('vxlan-ageing', vxlanattrs.get('ageing'))
 
+        ifla_vxlan_link = vxlanattrs.get(Link.IFLA_VXLAN_LINK)
+        if ifla_vxlan_link:
+            ifaceobjrunning.update_config(
+                'vxlan-physdev',
+                netlink.get_iface_name(ifla_vxlan_link)
+            )
 
     _run_ops = {'pre-up' : _up,
                'post-down' : _down,
@@ -151,7 +545,7 @@ class vxlan(moduleBase):
 
     def _init_command_handlers(self):
         if not self.ipcmd:
-            self.ipcmd = iproute2(**self.get_flags())
+            self.ipcmd = LinkUtils()
 
     def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
         op_handler = self._run_ops.get(operation)