From 725dfb4faa3240fe5730e5abc72dfbb74efe66e9 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 5 Aug 2015 02:54:04 +0200 Subject: [PATCH] #663: ptp addresses linux implementation --- HISTORY.rst | 1 + README.rst | 12 ++++++------ docs/index.rst | 23 +++++++++++++++-------- examples/ifconfig.py | 2 ++ psutil/__init__.py | 15 ++++++++++----- psutil/_common.py | 2 +- psutil/_psutil_posix.c | 33 +++++++++++++++++++++++++++++---- test/test_psutil.py | 8 +++++++- 8 files changed, 71 insertions(+), 25 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index e1d94a288..780cef27c 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -6,6 +6,7 @@ Bug tracker at https://github.com/giampaolo/psutil/issues **Enhancements** - #648: CI test integration for OSX. (patch by Jeff Tang) +- #663: net_if_addrs() now returns point-to-point addresses (for VPNs). **Bug fixes** diff --git a/README.rst b/README.rst index 564656146..3979924d1 100644 --- a/README.rst +++ b/README.rst @@ -166,12 +166,12 @@ Network ...] >>> >>> psutil.net_if_addrs() - {'lo': [snic(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'), - snic(family=, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None), - snic(family=, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')], - 'wlan0': [snic(family=, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'), - snic(family=, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None), - snic(family=, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]} + {'lo': [snic(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None), + snic(family=, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None), + snic(family=, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)], + 'wlan0': [snic(family=, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None), + snic(family=, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None), + snic(family=, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]} >>> >>> psutil.net_if_stats() {'eth0': snicstats(isup=True, duplex=, speed=100, mtu=1500), diff --git a/docs/index.rst b/docs/index.rst index 443019226..43f445938 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -410,28 +410,33 @@ Network Return the addresses associated to each NIC (network interface card) installed on the system as a dictionary whose keys are the NIC names and value is a list of namedtuples for each address assigned to the NIC. - Each namedtuple includes 4 fields: + Each namedtuple includes 5 fields: - **family** - **address** - **netmask** - **broadcast** + - **ptp** *family* can be either `AF_INET `__, `AF_INET6 `__ or :const:`psutil.AF_LINK`, which refers to a MAC address. - *address* is the primary address, *netmask* and *broadcast* may be ``None``. + *address* is the primary address and it is always set. + *netmask*, *broadcast* and *ptp* may be ``None``. + *ptp* stands for "point to point" and references the destination address on a + point to point interface (tipically a VPN). + *broadcast* and *ptp* are mutually exclusive. Example:: >>> import psutil >>> psutil.net_if_addrs() - {'lo': [snic(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'), - snic(family=, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None), - snic(family=, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')], - 'wlan0': [snic(family=, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'), - snic(family=, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None), - snic(family=, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]} + {'lo': [snic(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None), + snic(family=, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None), + snic(family=, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)], + 'wlan0': [snic(family=, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None), + snic(family=, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None), + snic(family=, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]} >>> See also `examples/ifconfig.py `__ @@ -446,6 +451,8 @@ Network *New in 3.0.0* + *Changed in 3.1.2:* *ptp* field was added. + .. function:: net_if_stats() Return information about each NIC (network interface card) installed on the diff --git a/examples/ifconfig.py b/examples/ifconfig.py index e7a436cc0..1f1ee985d 100644 --- a/examples/ifconfig.py +++ b/examples/ifconfig.py @@ -71,6 +71,8 @@ def main(): print(" broadcast : %s" % addr.broadcast) if addr.netmask: print(" netmask : %s" % addr.netmask) + if addr.ptp: + print(" p2p : %s" % addr.ptp) print("") diff --git a/psutil/__init__.py b/psutil/__init__.py index 79b369c99..385b80a4a 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -1749,17 +1749,22 @@ def net_if_addrs(): """Return the addresses associated to each NIC (network interface card) installed on the system as a dictionary whose keys are the NIC names and value is a list of namedtuples for each address - assigned to the NIC. Each namedtuple includes 4 fields: + assigned to the NIC. Each namedtuple includes 5 fields: - family - address - netmask - broadcast + - ptp 'family' can be either socket.AF_INET, socket.AF_INET6 or psutil.AF_LINK, which refers to a MAC address. - 'address' is the primary address, 'netmask' and 'broadcast' - may be None. + 'address' is the primary address and it is always set. + 'netmask' and 'broadcast' and 'ptp' may be None. + 'ptp' stands for "point to point" and references the destination + address on a point to point interface (tipically a VPN). + 'broadcast' and 'ptp' are mutually exclusive. + Note: you can have more than one address of the same family associated with each interface. """ @@ -1769,7 +1774,7 @@ def net_if_addrs(): rawlist = _psplatform.net_if_addrs() rawlist.sort(key=lambda x: x[1]) # sort by family ret = collections.defaultdict(list) - for name, fam, addr, mask, broadcast in rawlist: + for name, fam, addr, mask, broadcast, ptp in rawlist: if has_enums: try: fam = socket.AddressFamily(fam) @@ -1782,7 +1787,7 @@ def net_if_addrs(): # We re-set the family here so that repr(family) # will show AF_LINK rather than AF_PACKET fam = _psplatform.AF_LINK - ret[name].append(_common.snic(fam, addr, mask, broadcast)) + ret[name].append(_common.snic(fam, addr, mask, broadcast, ptp)) return dict(ret) diff --git a/psutil/_common.py b/psutil/_common.py index e9acf595d..9f5c06f2e 100644 --- a/psutil/_common.py +++ b/psutil/_common.py @@ -215,7 +215,7 @@ def socktype_to_enum(num): sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr', 'status', 'pid']) # psutil.net_if_addrs() -snic = namedtuple('snic', ['family', 'address', 'netmask', 'broadcast']) +snic = namedtuple('snic', ['family', 'address', 'netmask', 'broadcast', 'ptp']) # psutil.net_if_stats() snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu']) diff --git a/psutil/_psutil_posix.c b/psutil/_psutil_posix.c index 183dab0e1..5967df0c2 100644 --- a/psutil/_psutil_posix.c +++ b/psutil/_psutil_posix.c @@ -17,6 +17,7 @@ #ifdef __linux #include #include +#include #endif // end linux #if defined(__FreeBSD__) || defined(__APPLE__) @@ -163,6 +164,7 @@ psutil_net_if_addrs(PyObject* self, PyObject* args) PyObject *py_address = NULL; PyObject *py_netmask = NULL; PyObject *py_broadcast = NULL; + PyObject *py_ptp = NULL; if (py_retlist == NULL) return NULL; @@ -185,20 +187,41 @@ psutil_net_if_addrs(PyObject* self, PyObject* args) py_netmask = psutil_convert_ipaddr(ifa->ifa_netmask, family); if (py_netmask == NULL) goto error; + #ifdef __linux - py_broadcast = psutil_convert_ipaddr(ifa->ifa_ifu.ifu_broadaddr, family); + if (ifa->ifa_flags & IFF_BROADCAST) { + py_broadcast = psutil_convert_ipaddr(ifa->ifa_broadaddr, family); + Py_INCREF(Py_None); + py_ptp = Py_None; + } + else if (ifa->ifa_flags & IFF_POINTOPOINT) { + py_ptp = psutil_convert_ipaddr(ifa->ifa_dstaddr, family); + Py_INCREF(Py_None); + py_broadcast = Py_None; + } + else { + Py_INCREF(Py_None); + Py_INCREF(Py_None); + py_broadcast = Py_None; + py_ptp = Py_None; + } #else + // TODO py_broadcast = psutil_convert_ipaddr(ifa->ifa_broadaddr, family); + Py_INCREF(Py_None); + py_ptp = Py_None; #endif - if (py_broadcast == NULL) + + if ((py_broadcast == NULL) || (py_ptp == NULL)) goto error; py_tuple = Py_BuildValue( - "(siOOO)", + "(siOOOO)", ifa->ifa_name, family, py_address, py_netmask, - py_broadcast + py_broadcast, + py_ptp ); if (! py_tuple) @@ -209,6 +232,7 @@ psutil_net_if_addrs(PyObject* self, PyObject* args) Py_DECREF(py_address); Py_DECREF(py_netmask); Py_DECREF(py_broadcast); + Py_DECREF(py_ptp); } freeifaddrs(ifaddr); @@ -222,6 +246,7 @@ psutil_net_if_addrs(PyObject* self, PyObject* args) Py_XDECREF(py_address); Py_XDECREF(py_netmask); Py_XDECREF(py_broadcast); + Py_XDECREF(py_ptp); return NULL; } diff --git a/test/test_psutil.py b/test/test_psutil.py index 27806f37f..27ffd7164 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -1069,13 +1069,19 @@ def test_net_if_addrs(self): s = socket.socket(af, socktype, proto) with contextlib.closing(s): s.bind(sa) - for ip in (addr.address, addr.netmask, addr.broadcast): + for ip in (addr.address, addr.netmask, addr.broadcast, + addr.ptp): if ip is not None: # TODO: skip AF_INET6 for now because I get: # AddressValueError: Only hex digits permitted in # u'c6f3%lxcbr0' in u'fe80::c8e0:fff:fe54:c6f3%lxcbr0' if addr.family != AF_INET6: check_ip_address(ip, addr.family) + # broadcast and ptp addresses are mutually exclusive + if addr.broadcast: + self.assertIsNone(addr.ptp) + elif addr.ptp: + self.assertIsNone(addr.broadcast) if BSD or OSX or SUNOS: if hasattr(socket, "AF_LINK"):