X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ifupdown2%2Fifupdownaddons%2FLinkUtils.py;h=d05c0103535e2fdd61b9de72712498d8e0493b3d;hb=df106c519e61fb6989f7a7c722ae2ea85607c2d7;hp=ab93f774615835febf0a07c093770f476bf45683;hpb=d486dd0df02969a9706027f8cba650e3e5ac2045;p=mirror_ifupdown2.git diff --git a/ifupdown2/ifupdownaddons/LinkUtils.py b/ifupdown2/ifupdownaddons/LinkUtils.py index ab93f77..d05c010 100644 --- a/ifupdown2/ifupdownaddons/LinkUtils.py +++ b/ifupdown2/ifupdownaddons/LinkUtils.py @@ -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)