]> git.proxmox.com Git - mirror_ifupdown2.git/blobdiff - ifupdown2/ifupdownaddons/LinkUtils.py
LinkUtils: tunnel_change: do not purge the cache on tunnel change
[mirror_ifupdown2.git] / ifupdown2 / ifupdownaddons / LinkUtils.py
index ab93f774615835febf0a07c093770f476bf45683..d05c0103535e2fdd61b9de72712498d8e0493b3d 100644 (file)
@@ -10,6 +10,7 @@ import re
 import glob
 import shlex
 import signal
+import socket
 import subprocess
 
 from ipaddr import IPNetwork, IPv6Network
@@ -55,6 +56,9 @@ class LinkUtils(utilsBase):
     bridge_utils_is_installed = os.path.exists(utils.brctl_cmd)
     bridge_utils_missing_warning = True
 
+    DEFAULT_IP_METRIC = 1024
+    ADDR_METRIC_SUPPORT = None
+
     def __init__(self, *args, **kargs):
         utilsBase.__init__(self, *args, **kargs)
 
@@ -68,6 +72,27 @@ class LinkUtils(utilsBase):
         if not ifupdownflags.flags.PERFMODE and not LinkUtils._CACHE_FILL_DONE:
             self._fill_cache()
 
+        if LinkUtils.ADDR_METRIC_SUPPORT is None:
+            try:
+                cmd = [utils.ip_cmd, 'addr', 'help']
+                self.logger.info('executing %s addr help' % utils.ip_cmd)
+
+                process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+                stdout, stderr = process.communicate()
+                LinkUtils.ADDR_METRIC_SUPPORT = '[ metric METRIC ]' in stderr or ''
+                self.logger.info('address metric support: %s' % ('OK' if LinkUtils.ADDR_METRIC_SUPPORT else 'KO'))
+            except Exception:
+                LinkUtils.ADDR_METRIC_SUPPORT = False
+                self.logger.info('address metric support: KO')
+
+    @classmethod
+    def addr_metric_support(cls):
+        return cls.ADDR_METRIC_SUPPORT
+
+    @classmethod
+    def get_default_ip_metric(cls):
+        return cls.DEFAULT_IP_METRIC
+
     @classmethod
     def reset(cls):
         LinkUtils._CACHE_FILL_DONE = False
@@ -504,6 +529,28 @@ class LinkUtils(utilsBase):
                         linkattrs['state'] = citems[i + 1]
                     elif citems[i] == 'link/ether':
                         linkattrs['hwaddress'] = citems[i + 1]
+                    elif citems[i] in ['link/gre', 'link/ipip', 'link/sit', 'link/gre6', 'link/tunnel6', 'gretap']:
+                        linkattrs['kind'] = 'tunnel'
+                        tunattrs = {'mode': citems[i].split('/')[-1],
+                                    'endpoint' : None,
+                                    'local' : None,
+                                    'ttl' : None,
+                                    'physdev' : None}
+                        for j in range(i, len(citems)):
+                            if citems[j] == 'local':
+                                tunattrs['local'] = citems[j + 1]
+                            elif citems[j] == 'remote':
+                                tunattrs['endpoint'] = citems[j + 1]
+                            elif citems[j] == 'ttl':
+                                tunattrs['ttl'] = citems[j + 1]
+                            elif citems[j] == 'dev':
+                                tunattrs['physdev'] = citems[j + 1]
+                            elif citems[j] in ['vti', 'vti6', 'ip6gre', 'ipip6', 'ip6ip6']:
+                                tunattrs['mode'] = citems[j]
+                        linkattrs['linkinfo'] = tunattrs
+                        break
+                    elif citems[i] == 'link/ppp':
+                        linkattrs['kind'] = 'ppp'
                     elif citems[i] == 'vlan':
                         vlanid = self._get_vland_id(citems, i, warn)
                         if vlanid:
@@ -527,6 +574,8 @@ class LinkUtils(utilsBase):
                                 vattrs['ageing'] = citems[j + 1]
                             elif citems[j] == 'nolearning':
                                 vattrs['learning'] = 'off'
+                            elif citems[j] == 'dev':
+                                vattrs['physdev'] = citems[j + 1]
                         linkattrs['linkinfo'] = vattrs
                         break
                     elif citems[i] == 'vrf' and citems[i + 1] == 'table':
@@ -789,7 +838,7 @@ class LinkUtils(utilsBase):
                                         '-o', '-d', 'link', 'show'])
 
     def addr_add(self, ifacename, address, broadcast=None,
-                 peer=None, scope=None, preferred_lifetime=None):
+                 peer=None, scope=None, preferred_lifetime=None, metric=None):
         if not address:
             return
         cmd = 'addr add %s' % address
@@ -802,6 +851,10 @@ class LinkUtils(utilsBase):
         if preferred_lifetime:
             cmd += ' preferred_lft %s' % preferred_lifetime
         cmd += ' dev %s' % ifacename
+
+        if metric:
+            cmd += ' metric %s' % metric
+
         if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
             self.add_to_batch(cmd)
         else:
@@ -954,7 +1007,7 @@ class LinkUtils(utilsBase):
 
         return running_ipobj == (ip4 + ip6)
 
-    def addr_add_multiple(self, ifaceobj, ifacename, addrs, purge_existing=False):
+    def addr_add_multiple(self, ifaceobj, ifacename, addrs, purge_existing=False, metric=None):
         # purges address
         if purge_existing:
             # if perfmode is not set and also if iface has no sibling
@@ -981,7 +1034,7 @@ class LinkUtils(utilsBase):
                 self.logger.warning('%s: %s' % (ifacename, str(e)))
         for a in addrs:
             try:
-                self.addr_add(ifacename, a)
+                self.addr_add(ifacename, a, metric=metric)
             except Exception, e:
                 self.logger.error(str(e))
 
@@ -1062,7 +1115,7 @@ class LinkUtils(utilsBase):
         return self._cache_get('link', [ifacename, 'ifflag'], refresh=True)
 
     @staticmethod
-    def route_add_gateway(ifacename, gateway, vrf=None, metric=None):
+    def route_add_gateway(ifacename, gateway, vrf=None, metric=None, onlink=True):
         if not gateway:
             return
         if not vrf:
@@ -1075,6 +1128,10 @@ class LinkUtils(utilsBase):
         if metric:
             cmd += 'metric %s' % metric
         cmd += ' dev %s' % ifacename
+
+        if onlink:
+            cmd += " onlink"
+
         utils.exec_command(cmd)
 
     @staticmethod
@@ -1206,6 +1263,47 @@ class LinkUtils(utilsBase):
 
         return cur_peers
 
+    def tunnel_create(self, tunnelname, mode, attrs={}):
+        """ generic link_create function """
+        if self.link_exists(tunnelname):
+            return
+
+        cmd = ''
+        if '6' in mode:
+            cmd = ' -6 '
+
+        if mode in ['gretap']:
+            cmd += 'link add %s type %s' % (tunnelname, mode)
+        else:
+            cmd += 'tunnel add %s mode %s' % (tunnelname, mode)
+
+        if attrs:
+            for k, v in attrs.iteritems():
+                cmd += ' %s' % k
+                if v:
+                    cmd += ' %s' % v
+        if self.ipbatch and not self.ipbatch_pause:
+            self.add_to_batch(cmd)
+        else:
+            utils.exec_command('ip %s' % cmd)
+        self._cache_update([tunnelname], {})
+
+    def tunnel_change(self, tunnelname, attrs={}):
+        """ tunnel change function """
+        if not self.link_exists(tunnelname):
+            return
+        cmd = 'tunnel change'
+        cmd += ' %s' %(tunnelname)
+        if attrs:
+            for k, v in attrs.iteritems():
+                cmd += ' %s' %k
+                if v:
+                    cmd += ' %s' %v
+        if self.ipbatch and not self.ipbatch_pause:
+            self.add_to_batch(cmd)
+        else:
+            utils.exec_command('ip %s' % cmd)
+
     def link_create_vxlan(self, name, vxlanid,
                           localtunnelip=None,
                           svcnodeip=None,
@@ -1256,6 +1354,10 @@ class LinkUtils(utilsBase):
             return True
         return os.path.exists('/sys/class/net/%s' % ifacename)
 
+    @staticmethod
+    def link_exists_nodryrun(ifname):
+        return os.path.exists('/sys/class/net/%s' % ifname)
+
     def link_get_ifindex(self, ifacename):
         if ifupdownflags.flags.DRYRUN:
             return True
@@ -1449,11 +1551,40 @@ class LinkUtils(utilsBase):
 
         if not bridgeout: return brvlaninfo
         try:
-            vlan_json_dict = json.loads(bridgeout, encoding="utf-8")
+            vlan_json = json.loads(bridgeout, encoding="utf-8")
         except Exception, e:
             self.logger.info('json loads failed with (%s)' % str(e))
             return {}
-        return vlan_json_dict
+
+        try:
+            if isinstance(vlan_json, list):
+                # newer iproute2 version changed the bridge vlan show output
+                # ifupdown2 relies on the previous format, we have the convert
+                # data into old format
+                bridge_port_vids = dict()
+
+                for intf in vlan_json:
+                    bridge_port_vids[intf["ifname"]] = intf["vlans"]
+
+                return bridge_port_vids
+            else:
+                # older iproute2 version have different ways to dump vlans
+                # ifupdown2 prefers the following syntax:
+                # {
+                #    "vx-1002": [{
+                #        "vlan": 1002,
+                #        "flags": ["PVID", "Egress Untagged"]
+                #    }
+                #    ],
+                #    "vx-1004": [{
+                #        "vlan": 1004,
+                #        "flags": ["PVID", "Egress Untagged"]
+                #    }]
+                # }
+                return vlan_json
+        except Exception as e:
+            self.logger.debug("bridge vlan show: Unknown json output: %s" % str(e))
+            return vlan_json
 
     @staticmethod
     def get_bridge_vlan_nojson():
@@ -1687,10 +1818,14 @@ class LinkUtils(utilsBase):
             ret = False
         return ret
 
-    def ip_route_get_dev(self, prefix):
+    def ip_route_get_dev(self, prefix, vrf_master=None):
         try:
-            output = utils.exec_command('%s route get %s' %
-                                        (utils.ip_cmd, prefix))
+            if vrf_master:
+                cmd = '%s route get %s vrf %s' % (utils.ip_cmd, prefix, vrf_master)
+            else:
+                cmd = '%s route get %s' % (utils.ip_cmd, prefix)
+
+            output = utils.exec_command(cmd)
             if output:
                 rline = output.splitlines()[0]
                 if rline:
@@ -2497,6 +2632,31 @@ class LinkUtils(utilsBase):
             return mcqv4src.get(vlan)
         return mcqv4src
 
+    def bridge_get_mcqv4src_sysfs(self, bridge, vlan=None):
+        if not LinkUtils.bridge_utils_is_installed:
+            return {}
+        if not self.supported_command['showmcqv4src']:
+            return {}
+        if ifupdownflags.flags.PERFMODE:
+            return {}
+        mcqv4src = {}
+        try:
+            filename = '/sys/class/net/%s/bridge/multicast_v4_queriers' % bridge
+            if os.path.exists(filename):
+                for line in self.read_file(filename) or []:
+                    vlan_id, ip = line.split('=')
+                    mcqv4src[vlan_id] = ip.strip()
+        except Exception as e:
+            s = str(e).lower()
+            msg = ('%s showmcqv4src: skipping unsupported command'
+                   % utils.brctl_cmd)
+            self.logger.info(msg)
+            self.supported_command['showmcqv4src'] = False
+            return {}
+        if vlan:
+            return mcqv4src.get(vlan)
+        return mcqv4src
+
     @staticmethod
     def bridge_set_mclmi(bridge, mclmi):
         if not LinkUtils.bridge_utils_is_installed:
@@ -2540,3 +2700,63 @@ class LinkUtils(utilsBase):
             return os.listdir('/sys/class/net/%s/brif/' % bridgename)
         except:
             return []
+
+    def reset_addr_cache(self, ifname):
+        try:
+            linkCache.links[ifname]['addrs'] = {}
+            self.logger.debug('%s: reset address cache' % ifname)
+        except:
+            pass
+
+    def get_ipv6_addrgen_mode(self, ifname):
+        try:
+            return self._cache_get('link', [ifname, 'af_spec', socket.AF_INET6])[Link.IFLA_INET6_ADDR_GEN_MODE]
+        except:
+            # default to 0 (eui64)
+            return 0
+
+    def ipv6_addrgen(self, ifname, addrgen, link_created):
+        try:
+            # IFLA_INET6_ADDR_GEN_MODE values:
+            # 0 = eui64
+            # 1 = none
+            if self._link_cache_get([ifname, 'af_spec', socket.AF_INET6])[Link.IFLA_INET6_ADDR_GEN_MODE] == addrgen:
+                self.logger.debug('%s: ipv6 addrgen already %s' % (ifname, 'off' if addrgen else 'on'))
+                return
+
+            disabled_ipv6 = self.read_file_oneline('/proc/sys/net/ipv6/conf/%s/disable_ipv6' % ifname)
+            if not disabled_ipv6 or int(disabled_ipv6) == 1:
+                self.logger.info('%s: cannot set addrgen: ipv6 is disabled on this device' % ifname)
+                return
+
+            if int(self._link_cache_get([ifname, 'mtu'])) < 1280:
+                self.logger.info('%s: ipv6 addrgen is disabled on device with MTU '
+                                 'lower than 1280: cannot set addrgen %s' % (ifname, 'off' if addrgen else 'on'))
+                return
+        except (KeyError, TypeError):
+            self.logger.debug('%s: ipv6 addrgen probably not supported or disabled on this device' % ifname)
+            return
+        except Exception:
+            pass
+
+        if not link_created:
+            # When setting addrgenmode it is necessary to flap the macvlan
+            # device. After flapping the device we also need to re-add all
+            # the user configuration. The best way to add the user config
+            # is to flush our internal address cache
+            self.reset_addr_cache(ifname)
+
+        cmd = 'link set dev %s addrgenmode %s' % (ifname, 'none' if addrgen else 'eui64')
+
+        is_link_up = self.is_link_up(ifname)
+
+        if is_link_up:
+            self.link_down(ifname)
+
+        if LinkUtils.ipbatch:
+            self.add_to_batch(cmd)
+        else:
+            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
+
+        if is_link_up:
+            self.link_up(ifname)