Use netifaces module instead of 'ip' command

Netifaces allows to get addresses from local interfaces of the host in
safer way than parsing output of the ip command.

https://fedorahosted.org/freeipa/ticket/5591

Reviewed-By: David Kupka <dkupka@redhat.com>
This commit is contained in:
Martin Basti 2016-04-13 16:14:42 +02:00
parent 62bb478e11
commit 70fd78928c
4 changed files with 39 additions and 45 deletions

View File

@ -33,6 +33,7 @@ try:
from optparse import SUPPRESS_HELP, OptionGroup, OptionValueError
import dns
import gssapi
import netifaces
import nss.nss as nss
import SSSDConfig
@ -1526,39 +1527,31 @@ def unconfigure_nisdomain():
def get_iface_from_ip(ip_addr):
result = ipautil.run([paths.IP, '-oneline', 'address', 'show'],
capture_output=True)
for line in result.output.split('\n'):
fields = line.split()
if len(fields) < 6:
continue
if fields[2] not in ['inet', 'inet6']:
continue
(ip, mask) = fields[3].rsplit('/', 1)
if ip == ip_addr:
return fields[1]
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
else:
raise RuntimeError("IP %s not assigned to any interface." % ip_addr)
def get_local_ipaddresses(iface=None):
args = [paths.IP, '-oneline', 'address', 'show']
if iface:
args += ['dev', iface]
result = ipautil.run(args, capture_output=True)
lines = result.output.split('\n')
interfaces = [iface]
else:
interfaces = netifaces.interfaces()
ips = []
for line in lines:
fields = line.split()
if len(fields) < 6:
continue
if fields[2] not in ['inet', 'inet6']:
continue
(ip, mask) = fields[3].rsplit('/', 1)
try:
ips.append(ipautil.CheckedIPAddress(ip))
except ValueError:
continue
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']))
except ValueError:
continue
return ips

View File

@ -103,6 +103,7 @@ BuildRequires: python-jwcrypto
BuildRequires: custodia
BuildRequires: libini_config-devel >= 1.2.0
BuildRequires: dbus-python
BuildRequires: python-netifaces >= 0.10.4
# Build dependencies for unit tests
BuildRequires: libcmocka-devel
@ -478,7 +479,6 @@ Provides: python2-ipaplatform = %{version}-%{release}
Requires: %{name}-common = %{version}-%{release}
Requires: python-gssapi >= 1.1.2
Requires: gnupg
Requires: iproute
Requires: keyutils
Requires: pyOpenSSL
Requires: python-nss >= 0.16
@ -500,6 +500,7 @@ Requires: python-ldap >= 2.4.15
Requires: python-requests
Requires: python-custodia
Requires: python-dns >= 1.11.1
Requires: python-netifaces >= 0.10.4
Conflicts: %{alt_name}-python < %{version}
@ -526,7 +527,6 @@ Provides: python3-ipaplatform = %{version}-%{release}
Requires: %{name}-common = %{version}-%{release}
Requires: python3-gssapi >= 1.1.2
Requires: gnupg
Requires: iproute
Requires: keyutils
Requires: python3-pyOpenSSL
Requires: python3-nss >= 0.16
@ -548,6 +548,7 @@ Requires: python3-pyldap >= 2.4.15
Requires: python3-custodia
Requires: python3-requests
Requires: python3-dns >= 1.11.1
Requires: python3-netifaces >= 0.10.4
%description -n python3-ipalib
IPA is an integrated solution to provide centrally managed Identity (users,

View File

@ -140,7 +140,6 @@ class BasePathNamespace(object):
CACERT_P12 = "/root/cacert.p12"
ROOT_IPA_CSR = "/root/ipa.csr"
NAMED_PID = "/run/named/named.pid"
IP = "/sbin/ip"
NOLOGIN = "/sbin/nologin"
SBIN_REBOOT = "/sbin/reboot"
SBIN_RESTORECON = "/sbin/restorecon"

View File

@ -32,6 +32,7 @@ import socket
import re
import datetime
import netaddr
import netifaces
import time
import gssapi
import pwd
@ -151,24 +152,24 @@ class CheckedIPAddress(netaddr.IPAddress):
if match_local:
if addr.version == 4:
family = 'inet'
family = netifaces.AF_INET
elif addr.version == 6:
family = 'inet6'
family = netifaces.AF_INET6
else:
raise ValueError(
"Unsupported address family ({})".format(addr.version)
)
result = run(
[paths.IP, '-family', family, '-oneline', 'address', 'show'],
capture_output=True)
lines = result.output.split('\n')
for line in lines:
fields = line.split()
if len(fields) < 4:
continue
ifnet = netaddr.IPNetwork(fields[3])
if ifnet == net or (net is None and ifnet.ip == addr):
net = ifnet
iface = fields[1]
break
for interface in netifaces.interfaces():
for ifdata in netifaces.ifaddresses(interface).get(family, []):
ifnet = netaddr.IPNetwork('{addr}/{netmask}'.format(
addr=ifdata['addr'],
netmask=ifdata['netmask']
))
if ifnet == net or (net is None and ifnet.ip == addr):
net = ifnet
iface = interface
break
if iface is None:
raise ValueError('No network interface matches the provided IP address and netmask')