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..21ef3fc94 100644 --- a/psutil/_psutil_posix.c +++ b/psutil/_psutil_posix.c @@ -13,6 +13,7 @@ #include #include #include +#include #ifdef __linux #include @@ -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,34 @@ 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); -#else - py_broadcast = psutil_convert_ipaddr(ifa->ifa_broadaddr, family); -#endif - if (py_broadcast == NULL) + + 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; + } + + 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 +225,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 +239,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"):