Skip to content

Commit

Permalink
Add in support for network interface flags. (#2037)
Browse files Browse the repository at this point in the history
Signed-off-by: Chris Lalancette <[email protected]>
  • Loading branch information
clalancette authored Sep 6, 2022
1 parent 9f9a82d commit 70eecaf
Show file tree
Hide file tree
Showing 13 changed files with 246 additions and 16 deletions.
4 changes: 4 additions & 0 deletions CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -789,3 +789,7 @@ I: 2099

N: Torsten Blum
I: 2114

N: Chris Lalancette
W: https://github.com/clalancette
I: 2037
1 change: 1 addition & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

- 1053_: drop Python 2.6 support. (patches by Matthieu Darbois and Hugo van
Kemenade)
- 2037_: Add additional flags to net_if_stats.
- 2050_, [Linux]: increase ``read(2)`` buffer size from 1k to 32k when reading
``/proc`` pseudo files line by line. This should help having more consistent
results.
Expand Down
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,8 @@ Network
snicaddr(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}
>>>
>>> psutil.net_if_stats()
{'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536),
'wlan0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500)}
{'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536, flags='up,loopback,running'),
'wlan0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500, flags='up,broadcast,running,multicast')}
>>>
Sensors
Expand Down
14 changes: 12 additions & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -735,20 +735,30 @@ Network
- **speed**: the NIC speed expressed in mega bits (MB), if it can't be
determined (e.g. 'localhost') it will be set to ``0``.
- **mtu**: NIC's maximum transmission unit expressed in bytes.
- **flags**: a string of comma-separated flags on the interface (may be an empty string).
Possible flags are: ``up``, ``broadcast``, ``debug``, ``loopback``,
``pointopoint``, ``notrailers``, ``running``, ``noarp``, ``promisc``,
``allmulti``, ``master``, ``slave``, ``multicast``, ``portsel``,
``dynamic``, ``oactive``, ``simplex``, ``link0``, ``link1``, ``link2``,
and ``d2`` (some flags are only available on certain platforms).

Availability: UNIX

Example:

>>> import psutil
>>> psutil.net_if_stats()
{'eth0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500),
'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536)}
{'eth0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500, flags='up,broadcast,running,multicast'),
'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536, flags='up,loopback,running')}

Also see `nettop.py`_ and `ifconfig.py`_ for an example application.

.. versionadded:: 3.0.0

.. versionchanged:: 5.7.3 `isup` on UNIX also checks whether the NIC is running.

.. versionchanged:: 5.9.3 *flags* field was added on POSIX.

Sensors
-------

Expand Down
3 changes: 2 additions & 1 deletion psutil/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ class BatteryTime(enum.IntEnum):
snicaddr = namedtuple('snicaddr',
['family', 'address', 'netmask', 'broadcast', 'ptp'])
# psutil.net_if_stats()
snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu'])
snicstats = namedtuple('snicstats',
['isup', 'duplex', 'speed', 'mtu', 'flags'])
# psutil.cpu_stats()
scpustats = namedtuple(
'scpustats', ['ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls'])
Expand Down
7 changes: 5 additions & 2 deletions psutil/_psaix.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,8 @@ def net_if_stats():
names = set([x[0] for x in net_if_addrs()])
ret = {}
for name in names:
isup, mtu = cext.net_if_stats(name)
mtu = cext_posix.net_if_mtu(name)
flags = cext_posix.net_if_flags(name)

# try to get speed and duplex
# TODO: rewrite this in C (entstat forks, so use truss -f to follow.
Expand All @@ -257,8 +258,10 @@ def net_if_stats():
speed = int(re_result.group(1))
duplex = re_result.group(2)

output_flags = ','.join(flags)
isup = 'running' in flags
duplex = duplex_map.get(duplex, NIC_DUPLEX_UNKNOWN)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
ret[name] = _common.snicstats(isup, duplex, speed, mtu, output_flags)
return ret


Expand Down
7 changes: 5 additions & 2 deletions psutil/_psbsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ def net_if_stats():
for name in names:
try:
mtu = cext_posix.net_if_mtu(name)
isup = cext_posix.net_if_is_running(name)
flags = cext_posix.net_if_flags(name)
duplex, speed = cext_posix.net_if_duplex_speed(name)
except OSError as err:
# https://github.com/giampaolo/psutil/issues/1279
Expand All @@ -392,7 +392,10 @@ def net_if_stats():
else:
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
output_flags = ','.join(flags)
isup = 'running' in flags
ret[name] = _common.snicstats(isup, duplex, speed, mtu,
output_flags)
return ret


Expand Down
7 changes: 5 additions & 2 deletions psutil/_pslinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -1060,7 +1060,7 @@ def net_if_stats():
for name in names:
try:
mtu = cext_posix.net_if_mtu(name)
isup = cext_posix.net_if_is_running(name)
flags = cext_posix.net_if_flags(name)
duplex, speed = cext.net_if_duplex_speed(name)
except OSError as err:
# https://github.com/giampaolo/psutil/issues/1279
Expand All @@ -1069,7 +1069,10 @@ def net_if_stats():
else:
debug(err)
else:
ret[name] = _common.snicstats(isup, duplex_map[duplex], speed, mtu)
output_flags = ','.join(flags)
isup = 'running' in flags
ret[name] = _common.snicstats(isup, duplex_map[duplex], speed, mtu,
output_flags)
return ret


Expand Down
7 changes: 5 additions & 2 deletions psutil/_psosx.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ def net_if_stats():
for name in names:
try:
mtu = cext_posix.net_if_mtu(name)
isup = cext_posix.net_if_is_running(name)
flags = cext_posix.net_if_flags(name)
duplex, speed = cext_posix.net_if_duplex_speed(name)
except OSError as err:
# https://github.com/giampaolo/psutil/issues/1279
Expand All @@ -272,7 +272,10 @@ def net_if_stats():
else:
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
output_flags = ','.join(flags)
isup = 'running' in flags
ret[name] = _common.snicstats(isup, duplex, speed, mtu,
output_flags)
return ret


Expand Down
2 changes: 1 addition & 1 deletion psutil/_pssunos.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ def net_if_stats():
isup, duplex, speed, mtu = items
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
ret[name] = _common.snicstats(isup, duplex, speed, mtu, '')
return ret


Expand Down
201 changes: 201 additions & 0 deletions psutil/_psutil_posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,206 @@ psutil_net_if_mtu(PyObject *self, PyObject *args) {
return PyErr_SetFromErrno(PyExc_OSError);
}

static int
append_flag(PyObject *py_retlist, const char * flag_name)
{
PyObject *py_str = NULL;

py_str = PyUnicode_DecodeFSDefault(flag_name);
if (! py_str)
return 0;
if (PyList_Append(py_retlist, py_str)) {
Py_DECREF(py_str);
return 0;
}
Py_CLEAR(py_str);

return 1;
}

/*
* Get all of the NIC flags and return them.
*/
static PyObject *
psutil_net_if_flags(PyObject *self, PyObject *args) {
char *nic_name;
int sock = -1;
int ret;
struct ifreq ifr;
PyObject *py_retlist = PyList_New(0);
short int flags;

if (py_retlist == NULL)
return NULL;

if (! PyArg_ParseTuple(args, "s", &nic_name))
goto error;

sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
PyErr_SetFromOSErrnoWithSyscall("socket(SOCK_DGRAM)");
goto error;
}

PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
if (ret == -1) {
PyErr_SetFromOSErrnoWithSyscall("ioctl(SIOCGIFFLAGS)");
goto error;
}

close(sock);
sock = -1;

flags = ifr.ifr_flags & 0xFFFF;

// Linux/glibc IFF flags: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/gnu/net/if.h;h=251418f82331c0426e58707fe4473d454893b132;hb=HEAD
// macOS IFF flags: https://opensource.apple.com/source/xnu/xnu-792/bsd/net/if.h.auto.html
// AIX IFF flags: https://www.ibm.com/support/pages/how-hexadecimal-flags-displayed-ifconfig-are-calculated
// FreeBSD IFF flags: https://www.freebsd.org/cgi/man.cgi?query=if_allmulti&apropos=0&sektion=0&manpath=FreeBSD+10-current&format=html

#ifdef IFF_UP
// Available in (at least) Linux, macOS, AIX, BSD
if (flags & IFF_UP)
if (!append_flag(py_retlist, "up"))
goto error;
#endif
#ifdef IFF_BROADCAST
// Available in (at least) Linux, macOS, AIX, BSD
if (flags & IFF_BROADCAST)
if (!append_flag(py_retlist, "broadcast"))
goto error;
#endif
#ifdef IFF_DEBUG
// Available in (at least) Linux, macOS, BSD
if (flags & IFF_DEBUG)
if (!append_flag(py_retlist, "debug"))
goto error;
#endif
#ifdef IFF_LOOPBACK
// Available in (at least) Linux, macOS, BSD
if (flags & IFF_LOOPBACK)
if (!append_flag(py_retlist, "loopback"))
goto error;
#endif
#ifdef IFF_POINTOPOINT
// Available in (at least) Linux, macOS, BSD
if (flags & IFF_POINTOPOINT)
if (!append_flag(py_retlist, "pointopoint"))
goto error;
#endif
#ifdef IFF_NOTRAILERS
// Available in (at least) Linux, macOS, AIX
if (flags & IFF_NOTRAILERS)
if (!append_flag(py_retlist, "notrailers"))
goto error;
#endif
#ifdef IFF_RUNNING
// Available in (at least) Linux, macOS, AIX, BSD
if (flags & IFF_RUNNING)
if (!append_flag(py_retlist, "running"))
goto error;
#endif
#ifdef IFF_NOARP
// Available in (at least) Linux, macOS, BSD
if (flags & IFF_NOARP)
if (!append_flag(py_retlist, "noarp"))
goto error;
#endif
#ifdef IFF_PROMISC
// Available in (at least) Linux, macOS, BSD
if (flags & IFF_PROMISC)
if (!append_flag(py_retlist, "promisc"))
goto error;
#endif
#ifdef IFF_ALLMULTI
// Available in (at least) Linux, macOS, BSD
if (flags & IFF_ALLMULTI)
if (!append_flag(py_retlist, "allmulti"))
goto error;
#endif
#ifdef IFF_MASTER
// Available in (at least) Linux
if (flags & IFF_MASTER)
if (!append_flag(py_retlist, "master"))
goto error;
#endif
#ifdef IFF_SLAVE
// Available in (at least) Linux
if (flags & IFF_SLAVE)
if (!append_flag(py_retlist, "slave"))
goto error;
#endif
#ifdef IFF_MULTICAST
// Available in (at least) Linux, macOS, BSD
if (flags & IFF_MULTICAST)
if (!append_flag(py_retlist, "multicast"))
goto error;
#endif
#ifdef IFF_PORTSEL
// Available in (at least) Linux
if (flags & IFF_PORTSEL)
if (!append_flag(py_retlist, "portsel"))
goto error;
#endif
#ifdef IFF_AUTOMEDIA
// Available in (at least) Linux
if (flags & IFF_AUTOMEDIA)
if (!append_flag(py_retlist, "automedia"))
goto error;
#endif
#ifdef IFF_DYNAMIC
// Available in (at least) Linux
if (flags & IFF_DYNAMIC)
if (!append_flag(py_retlist, "dynamic"))
goto error;
#endif
#ifdef IFF_OACTIVE
// Available in (at least) macOS, BSD
if (flags & IFF_OACTIVE)
if (!append_flag(py_retlist, "oactive"))
goto error;
#endif
#ifdef IFF_SIMPLEX
// Available in (at least) macOS, AIX, BSD
if (flags & IFF_SIMPLEX)
if (!append_flag(py_retlist, "simplex"))
goto error;
#endif
#ifdef IFF_LINK0
// Available in (at least) macOS, BSD
if (flags & IFF_LINK0)
if (!append_flag(py_retlist, "link0"))
goto error;
#endif
#ifdef IFF_LINK1
// Available in (at least) macOS, BSD
if (flags & IFF_LINK1)
if (!append_flag(py_retlist, "link1"))
goto error;
#endif
#ifdef IFF_LINK2
// Available in (at least) macOS, BSD
if (flags & IFF_LINK2)
if (!append_flag(py_retlist, "link2"))
goto error;
#endif
#ifdef IFF_D2
// Available in (at least) AIX
if (flags & IFF_D2)
if (!append_flag(py_retlist, "d2"))
goto error;
#endif

return py_retlist;

error:
Py_DECREF(py_retlist);
if (sock != -1)
close(sock);
return NULL;
}


/*
* Inspect NIC flags, returns a bool indicating whether the NIC is
Expand Down Expand Up @@ -667,6 +867,7 @@ static PyMethodDef mod_methods[] = {
{"getpagesize", psutil_getpagesize_pywrapper, METH_VARARGS},
{"getpriority", psutil_posix_getpriority, METH_VARARGS},
{"net_if_addrs", psutil_net_if_addrs, METH_VARARGS},
{"net_if_flags", psutil_net_if_flags, METH_VARARGS},
{"net_if_is_running", psutil_net_if_is_running, METH_VARARGS},
{"net_if_mtu", psutil_net_if_mtu, METH_VARARGS},
{"setpriority", psutil_posix_setpriority, METH_VARARGS},
Expand Down
2 changes: 1 addition & 1 deletion psutil/_pswindows.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ def net_if_stats():
isup, duplex, speed, mtu = items
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
ret[name] = _common.snicstats(isup, duplex, speed, mtu, '')
return ret


Expand Down
Loading

0 comments on commit 70eecaf

Please sign in to comment.