diff --git a/.lgtm.yml b/.lgtm.yml index b39788c9e..4747d33a9 100644 --- a/.lgtm.yml +++ b/.lgtm.yml @@ -120,7 +120,7 @@ extraction: - lxml - gssapi - netaddr - - netifaces + - ifaddr - polib - requests - python-augeas diff --git a/doc/requirements.txt b/doc/requirements.txt index cdaa42658..8d91b893f 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -11,6 +11,7 @@ m2r2 ## ipa dependencies dnspython jwcrypto +ifaddr netaddr qrcode six diff --git a/freeipa.spec.in b/freeipa.spec.in index 2a220b41d..698932e55 100755 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -403,7 +403,7 @@ BuildRequires: python3-libipa_hbac BuildRequires: python3-libsss_nss_idmap BuildRequires: python3-lxml BuildRequires: python3-netaddr >= %{python_netaddr_version} -BuildRequires: python3-netifaces +BuildRequires: python3-ifaddr BuildRequires: python3-pki >= %{pki_version} BuildRequires: python3-polib BuildRequires: python3-pyasn1 @@ -885,7 +885,7 @@ Requires: python3-gssapi >= 1.2.0 Requires: python3-jwcrypto >= 0.4.2 Requires: python3-libipa_hbac Requires: python3-netaddr >= %{python_netaddr_version} -Requires: python3-netifaces >= 0.10.4 +Requires: python3-ifaddr Requires: python3-pyasn1 >= 0.3.2-2 Requires: python3-pyasn1-modules >= 0.3.2-2 Requires: python3-pyusb diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py index 83cfefb87..4ba82025a 100644 --- a/ipaclient/install/client.py +++ b/ipaclient/install/client.py @@ -18,7 +18,7 @@ import logging import dns import getpass import gssapi -import netifaces +import ifaddr import os import re import SSSDConfig @@ -1351,31 +1351,36 @@ def unconfigure_nisdomain(statestore): def get_iface_from_ip(ip_addr): - for interface in netifaces.interfaces(): - if_addrs = netifaces.ifaddresses(interface) - for family in [netifaces.AF_INET, netifaces.AF_INET6]: - for ip in if_addrs.get(family, []): - if ip['addr'] == ip_addr: - return interface + for adapter in ifaddr.get_adapters(): + for ips in adapter.ips: + # IPv6 is reported as a tuple, IPv4 is reported as str + if ip_addr in (ips.ip[0], ips.ip): + return adapter.name raise RuntimeError("IP %s not assigned to any interface." % ip_addr) -def get_local_ipaddresses(iface=None): +def __get_ifaddr_adapters(iface=None): if iface: - interfaces = [iface] + interfaces = set(iface if isinstance(iface, (list, tuple)) else [iface]) else: - interfaces = netifaces.interfaces() + interfaces = set(adapter.name for adapter in ifaddr.get_adapters()) + return [ + adapter + for adapter in ifaddr.get_adapters() + if adapter.name in interfaces or adapter.nice_name in interfaces + ] + +def get_local_ipaddresses(iface=None): ips = [] - for interface in interfaces: - if_addrs = netifaces.ifaddresses(interface) - for family in [netifaces.AF_INET, netifaces.AF_INET6]: - for ip in if_addrs.get(family, []): - try: - ips.append(ipautil.CheckedIPAddress(ip['addr'])) - logger.debug('IP check successful: %s', ip['addr']) - except ValueError as e: - logger.debug('IP check failed: %s', e) + for adapter in __get_ifaddr_adapters(iface): + for ifip in adapter.ips: + try: + ip_addr = ifip.ip[0] if isinstance(ifip.ip, tuple) else ifip.ip + ips.append(ipautil.CheckedIPAddress(ip_addr)) + logger.debug('IP check successful: %s', ip_addr) + except ValueError as e: + logger.debug('IP check failed: %s', e) return ips diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py index 6802dde57..b02d58839 100644 --- a/ipapython/ipautil.py +++ b/ipapython/ipautil.py @@ -48,9 +48,9 @@ import six from six.moves import input try: - import netifaces + import ifaddr except ImportError: - netifaces = None + ifaddr = None from ipapython.dn import DN from ipaplatform.paths import paths @@ -203,42 +203,37 @@ class CheckedIPAddress(UnsafeIPAddress): :return: InterfaceDetails named tuple or None if no interface has this address """ - if netifaces is None: - raise ImportError("netifaces") + if ifaddr is None: + raise ImportError("ifaddr") logger.debug("Searching for an interface of IP address: %s", self) + if self.version == 4: - family = netifaces.AF_INET + family_ips = ( + (ip.ip, ip.network_prefix, ip.nice_name) + for ips in [a.ips for a in ifaddr.get_adapters()] + for ip in ips if not isinstance(ip.ip, tuple) + ) elif self.version == 6: - family = netifaces.AF_INET6 + family_ips = ( + (ip.ip[0], ip.network_prefix, ip.nice_name) + for ips in [a.ips for a in ifaddr.get_adapters()] + for ip in ips if isinstance(ip.ip, tuple) + ) else: raise ValueError( "Unsupported address family ({})".format(self.version) ) - for interface in netifaces.interfaces(): - for ifdata in netifaces.ifaddresses(interface).get(family, []): + for ip, prefix, ifname in family_ips: + ifaddrmask = "{ip}/{prefix}".format(ip=ip, prefix=prefix) + logger.debug( + "Testing local IP address: %s (interface: %s)", + ifaddrmask, ifname) + ifnet = netaddr.IPNetwork(ifaddrmask) - # link-local addresses contain '%suffix' that causes parse - # errors in IPNetwork - ifaddr = ifdata['addr'].split(u'%', 1)[0] + if ifnet.ip == self: + return InterfaceDetails(ifname, ifnet) - # newer versions of netifaces provide IPv6 netmask in format - # 'ffff:ffff:ffff:ffff::/64'. We have to split and use prefix - # or the netmask with older versions - ifmask = ifdata['netmask'].split(u'/')[-1] - - ifaddrmask = '{addr}/{netmask}'.format( - addr=ifaddr, - netmask=ifmask - ) - logger.debug( - "Testing local IP address: %s (interface: %s)", - ifaddrmask, interface) - - ifnet = netaddr.IPNetwork(ifaddrmask) - - if ifnet.ip == self: - return InterfaceDetails(interface, ifnet) return None def set_ip_net(self, ifnet): diff --git a/ipapython/setup.py b/ipapython/setup.py index ea55f5c72..b7b25c8b4 100644 --- a/ipapython/setup.py +++ b/ipapython/setup.py @@ -48,6 +48,6 @@ if __name__ == '__main__': extras_require={ "ldap": ["python-ldap"], # ipapython.ipaldap # CheckedIPAddress.get_matching_interface - "netifaces": ["netifaces"], + "ifaddr": ["ifaddr"], }, ) diff --git a/ipasetup.py.in b/ipasetup.py.in index 25eac3b21..56a1f3b06 100644 --- a/ipasetup.py.in +++ b/ipasetup.py.in @@ -75,7 +75,7 @@ PACKAGE_VERSION = { 'ipaserver': 'ipaserver == {}'.format(VERSION), 'jwcrypto': 'jwcrypto >= 0.4.2', 'kdcproxy': 'kdcproxy >= 0.3', - 'netifaces': 'netifaces >= 0.10.4', + 'ifaddr': 'ifaddr >= 0.1.7', 'python-ldap': 'python-ldap >= 3.0.0', 'python-yubico': 'python-yubico >= 1.2.3', 'qrcode': 'qrcode >= 5.0', diff --git a/pylintrc b/pylintrc index 22053a9b5..50278cc76 100644 --- a/pylintrc +++ b/pylintrc @@ -13,7 +13,6 @@ extension-pkg-allow-list= _ldap, cryptography, gssapi, - netifaces, lxml.etree, pysss_murmur,