From a9befcc34cda0dbf34be2dc5302d18b6e163cc5e Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 20 Dec 2023 19:24:07 +0100 Subject: [PATCH 1/7] FreeBSD: implement socket filtering in C --- psutil/_psbsd.py | 5 +-- psutil/arch/freebsd/sys_socks.c | 68 ++++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index 04b615ff9..fc03d1114 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -428,13 +428,10 @@ def net_connections(kind): elif NETBSD: rawlist = cext.net_connections(-1, kind) else: # FreeBSD - rawlist = cext.net_connections() + rawlist = cext.net_connections(families, types) for item in rawlist: fd, fam, type, laddr, raddr, status, pid = item - if FREEBSD: - if (fam not in families) or (type not in types): - continue nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, TCP_STATUSES, pid) ret.add(nt) diff --git a/psutil/arch/freebsd/sys_socks.c b/psutil/arch/freebsd/sys_socks.c index 48d3fb4f5..218292900 100644 --- a/psutil/arch/freebsd/sys_socks.c +++ b/psutil/arch/freebsd/sys_socks.c @@ -76,7 +76,9 @@ psutil_get_file_from_sock(kvaddr_t sock) { // Reference: // https://github.com/freebsd/freebsd/blob/master/usr.bin/sockstat/sockstat.c -int psutil_gather_inet(int proto, PyObject *py_retlist) { +int psutil_gather_inet( + int proto, int include_v4, int include_v6, PyObject *py_retlist) +{ struct xinpgen *xig, *exig; struct xinpcb *xip; struct xtcpcb *xtp; @@ -186,11 +188,15 @@ int psutil_gather_inet(int proto, PyObject *py_retlist) { rport = ntohs(inp->inp_fport); if (inp->inp_vflag & INP_IPV4) { + if (include_v4 == 0) + continue; family = AF_INET; inet_ntop(AF_INET, &inp->inp_laddr.s_addr, lip, sizeof(lip)); inet_ntop(AF_INET, &inp->inp_faddr.s_addr, rip, sizeof(rip)); } else if (inp->inp_vflag & INP_IPV6) { + if (include_v6 == 0) + continue; family = AF_INET6; inet_ntop(AF_INET6, &inp->in6p_laddr.s6_addr, lip, sizeof(lip)); inet_ntop(AF_INET6, &inp->in6p_faddr.s6_addr, rip, sizeof(rip)); @@ -339,24 +345,68 @@ int psutil_gather_unix(int proto, PyObject *py_retlist) { } +int +psutil_value_in_seq(PyObject *py_seq, int value) { + int inseq; + PyObject *py_value; + + py_value = PyLong_FromLong((long)value); + inseq = PySequence_Contains(py_seq, py_value); + Py_DECREF(py_value); + return inseq; +} + + PyObject* psutil_net_connections(PyObject* self, PyObject* args) { - // Return system-wide open connections. + int include_v4, include_v6, include_unix, include_tcp, include_udp; + PyObject *py_af_filter = NULL; + PyObject *py_type_filter = NULL; PyObject *py_retlist = PyList_New(0); if (py_retlist == NULL) return NULL; - if (psutil_populate_xfiles() != 1) + if (! PyArg_ParseTuple(args, "OO", &py_af_filter, &py_type_filter)) { goto error; - if (psutil_gather_inet(IPPROTO_TCP, py_retlist) == 0) - goto error; - if (psutil_gather_inet(IPPROTO_UDP, py_retlist) == 0) + } + if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) { + PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); goto error; - if (psutil_gather_unix(SOCK_STREAM, py_retlist) == 0) - goto error; - if (psutil_gather_unix(SOCK_DGRAM, py_retlist) == 0) + } + + include_v4 = psutil_value_in_seq(py_af_filter, AF_INET); + include_v6 = psutil_value_in_seq(py_af_filter, AF_INET6); + include_unix = psutil_value_in_seq(py_af_filter, AF_UNIX); + include_tcp = psutil_value_in_seq(py_type_filter, SOCK_STREAM); + include_udp = psutil_value_in_seq(py_type_filter, SOCK_DGRAM); + + if (psutil_populate_xfiles() != 1) goto error; + // TCP + if (include_tcp == 1) { + if (psutil_gather_inet( + IPPROTO_TCP, include_v4, include_v6, py_retlist) == 0) + { + goto error; + } + } + // UDP + if (include_udp == 1) { + if (psutil_gather_inet( + IPPROTO_UDP, include_v4, include_v6, py_retlist) == 0) + { + goto error; + } + } + // UNIX + if (include_unix == 1) { + if (psutil_gather_unix(SOCK_STREAM, py_retlist) == 0) + goto error; + if (psutil_gather_unix(SOCK_DGRAM, py_retlist) == 0) + goto error; + } + free(psutil_xfiles); return py_retlist; From 659f8ebf2ec4c83e416a68239b76c0de29f89ee8 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 20 Dec 2023 19:30:42 +0100 Subject: [PATCH 2/7] check return code for Py* calls --- psutil/arch/freebsd/sys_socks.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/psutil/arch/freebsd/sys_socks.c b/psutil/arch/freebsd/sys_socks.c index 218292900..5a38e94c3 100644 --- a/psutil/arch/freebsd/sys_socks.c +++ b/psutil/arch/freebsd/sys_socks.c @@ -351,7 +351,9 @@ psutil_value_in_seq(PyObject *py_seq, int value) { PyObject *py_value; py_value = PyLong_FromLong((long)value); - inseq = PySequence_Contains(py_seq, py_value); + if (py_value == NULL) + return -1; + inseq = PySequence_Contains(py_seq, py_value); // return -1 on failure Py_DECREF(py_value); return inseq; } @@ -374,11 +376,16 @@ psutil_net_connections(PyObject* self, PyObject* args) { goto error; } - include_v4 = psutil_value_in_seq(py_af_filter, AF_INET); - include_v6 = psutil_value_in_seq(py_af_filter, AF_INET6); - include_unix = psutil_value_in_seq(py_af_filter, AF_UNIX); - include_tcp = psutil_value_in_seq(py_type_filter, SOCK_STREAM); - include_udp = psutil_value_in_seq(py_type_filter, SOCK_DGRAM); + if ((include_v4 = psutil_value_in_seq(py_af_filter, AF_INET)) == -1) + goto error; + if ((include_v6 = psutil_value_in_seq(py_af_filter, AF_INET6)) == -1) + goto error; + if ((include_unix = psutil_value_in_seq(py_af_filter, AF_UNIX)) == -1) + goto error; + if ((include_tcp = psutil_value_in_seq(py_type_filter, SOCK_STREAM)) == -1) + goto error; + if ((include_udp = psutil_value_in_seq(py_type_filter, SOCK_DGRAM)) == -1) + goto error; if (psutil_populate_xfiles() != 1) goto error; From 7781f2eedb1e1575ab4d01d879624443c7722c38 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 20 Dec 2023 19:31:39 +0100 Subject: [PATCH 3/7] change fun signature --- psutil/arch/freebsd/sys_socks.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/psutil/arch/freebsd/sys_socks.c b/psutil/arch/freebsd/sys_socks.c index 5a38e94c3..268799e61 100644 --- a/psutil/arch/freebsd/sys_socks.c +++ b/psutil/arch/freebsd/sys_socks.c @@ -346,7 +346,7 @@ int psutil_gather_unix(int proto, PyObject *py_retlist) { int -psutil_value_in_seq(PyObject *py_seq, int value) { +psutil_int_in_seq(int value, PyObject *py_seq) { int inseq; PyObject *py_value; @@ -376,15 +376,15 @@ psutil_net_connections(PyObject* self, PyObject* args) { goto error; } - if ((include_v4 = psutil_value_in_seq(py_af_filter, AF_INET)) == -1) + if ((include_v4 = psutil_int_in_seq(AF_INET, py_af_filter)) == -1) goto error; - if ((include_v6 = psutil_value_in_seq(py_af_filter, AF_INET6)) == -1) + if ((include_v6 = psutil_int_in_seq(AF_INET6, py_af_filter)) == -1) goto error; - if ((include_unix = psutil_value_in_seq(py_af_filter, AF_UNIX)) == -1) + if ((include_unix = psutil_int_in_seq(AF_UNIX, py_af_filter)) == -1) goto error; - if ((include_tcp = psutil_value_in_seq(py_type_filter, SOCK_STREAM)) == -1) + if ((include_tcp = psutil_int_in_seq(SOCK_STREAM, py_type_filter)) == -1) goto error; - if ((include_udp = psutil_value_in_seq(py_type_filter, SOCK_DGRAM)) == -1) + if ((include_udp = psutil_int_in_seq(SOCK_DGRAM, py_type_filter)) == -1) goto error; if (psutil_populate_xfiles() != 1) From 4ca5ad450e49256f640eaf5a3f15306a61035c2f Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 20 Dec 2023 22:18:07 +0100 Subject: [PATCH 4/7] update HISTORY --- HISTORY.rst | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 932ac21b9..909fad4cb 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -5,17 +5,12 @@ **Enhancements** -- 2342_, [NetBSD]: filter `net_connections()`_ returned list in C instead of +- 2343_, [FreeBSD]: filter `net_connections()`_ returned list in C instead of Python, and avoid to retrieve unnecessary connection types unless explicitly asked. E.g., on an IDLE system with few IPv6 connections this will run around - 170% faster. Before all connection types (TCP, UDP, UNIX) were retrived - internally, even if they were not returned.:: - - import psutil, time - started = time.monotonic() - for x in range(1000): - psutil.net_connections("tcp6") - print(f"completed in {(time.monotonic() - started):.4f} secs") + 4 times faster. Before all connection types (TCP, UDP, UNIX) were retrived + internally, even if only a portion was returned. +- 2342_, [NetBSD]: same as above (#2343) but for NetBSD. **Bug fixes** From ada83e2a027e192ac414dd154655997cd27d5611 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 20 Dec 2023 22:19:46 +0100 Subject: [PATCH 5/7] fix typo --- HISTORY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index 909fad4cb..0f84d59f4 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -8,7 +8,7 @@ - 2343_, [FreeBSD]: filter `net_connections()`_ returned list in C instead of Python, and avoid to retrieve unnecessary connection types unless explicitly asked. E.g., on an IDLE system with few IPv6 connections this will run around - 4 times faster. Before all connection types (TCP, UDP, UNIX) were retrived + 4 times faster. Before all connection types (TCP, UDP, UNIX) were retrieved internally, even if only a portion was returned. - 2342_, [NetBSD]: same as above (#2343) but for NetBSD. From 1896a4acce496bdae5e3d48ce91e4e68baf11796 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 20 Dec 2023 22:26:13 +0100 Subject: [PATCH 6/7] further speedup --- psutil/arch/freebsd/sys_socks.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/psutil/arch/freebsd/sys_socks.c b/psutil/arch/freebsd/sys_socks.c index 268799e61..358efd08c 100644 --- a/psutil/arch/freebsd/sys_socks.c +++ b/psutil/arch/freebsd/sys_socks.c @@ -179,6 +179,12 @@ int psutil_gather_inet( goto error; } + // filter + if ((inp->inp_vflag & INP_IPV4) && (include_v4 == 0)) + continue; + if ((inp->inp_vflag & INP_IPV6) && (include_v6 == 0)) + continue; + char lip[200], rip[200]; xf = psutil_get_file_from_sock(so->xso_so); @@ -188,15 +194,11 @@ int psutil_gather_inet( rport = ntohs(inp->inp_fport); if (inp->inp_vflag & INP_IPV4) { - if (include_v4 == 0) - continue; family = AF_INET; inet_ntop(AF_INET, &inp->inp_laddr.s_addr, lip, sizeof(lip)); inet_ntop(AF_INET, &inp->inp_faddr.s_addr, rip, sizeof(rip)); } else if (inp->inp_vflag & INP_IPV6) { - if (include_v6 == 0) - continue; family = AF_INET6; inet_ntop(AF_INET6, &inp->in6p_laddr.s6_addr, lip, sizeof(lip)); inet_ntop(AF_INET6, &inp->in6p_faddr.s6_addr, rip, sizeof(rip)); From 1a03a5a14d65d730b6a75c362761c3f937658d57 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 20 Dec 2023 22:29:26 +0100 Subject: [PATCH 7/7] define static/private functions --- psutil/arch/freebsd/sys_socks.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/psutil/arch/freebsd/sys_socks.c b/psutil/arch/freebsd/sys_socks.c index 358efd08c..3887bd403 100644 --- a/psutil/arch/freebsd/sys_socks.c +++ b/psutil/arch/freebsd/sys_socks.c @@ -33,7 +33,7 @@ static struct xfile *psutil_xfiles; static int psutil_nxfiles; -int +static int psutil_populate_xfiles(void) { size_t len; @@ -61,7 +61,7 @@ psutil_populate_xfiles(void) { } -struct xfile * +static struct xfile * psutil_get_file_from_sock(kvaddr_t sock) { struct xfile *xf; int n; @@ -76,7 +76,8 @@ psutil_get_file_from_sock(kvaddr_t sock) { // Reference: // https://github.com/freebsd/freebsd/blob/master/usr.bin/sockstat/sockstat.c -int psutil_gather_inet( +static int +psutil_gather_inet( int proto, int include_v4, int include_v6, PyObject *py_retlist) { struct xinpgen *xig, *exig; @@ -243,7 +244,8 @@ int psutil_gather_inet( } -int psutil_gather_unix(int proto, PyObject *py_retlist) { +static int +psutil_gather_unix(int proto, PyObject *py_retlist) { struct xunpgen *xug, *exug; struct xunpcb *xup; const char *varname = NULL; @@ -347,7 +349,7 @@ int psutil_gather_unix(int proto, PyObject *py_retlist) { } -int +static int psutil_int_in_seq(int value, PyObject *py_seq) { int inseq; PyObject *py_value;