feat: report listening sockets
CI / test (3.10) (push) Successful in 27s
CI / test (3.11) (push) Successful in 27s
CI / test (3.12) (push) Successful in 37s
CI / test (3.10) (pull_request) Successful in 42s
CI / test (3.11) (pull_request) Successful in 42s
CI / test (3.12) (pull_request) Successful in 38s
CI / test (3.10) (push) Successful in 27s
CI / test (3.11) (push) Successful in 27s
CI / test (3.12) (push) Successful in 37s
CI / test (3.10) (pull_request) Successful in 42s
CI / test (3.11) (pull_request) Successful in 42s
CI / test (3.12) (pull_request) Successful in 38s
get_listening_sockets() in the shape of napalm-device-types' ListeningSocketsMixin, whose ss/cgroup reading is Linux's, and with its rule: read as root first, and without root when that brings nothing back, which the reading reports as `attributed: False`. - FreeBSD: `sockstat -46lq -P tcp,udp`, which sees every socket either way. - OpenBSD: `fstat -n` as root (the sockets nothing is connected to; port 0 is unbound), `netstat -an` without root, as fstat shows a user only their own processes; those sockets come without a process. - Addresses as ss prints them: `*` becomes 0.0.0.0 or :: by family, IPv6 without brackets, a zone (`fe80::1%lo0`) becomes the interface. One entry per socket, the first process holding it. No get_kernel_facts on purpose: KernelFactsMixin reports a Linux kernel's modules and CONFIG_ options, which a BSD kernel does not have. Fixtures recorded on the FreeBSD 15.1 and OpenBSD 7.9 VMs with test listeners on 127.0.0.1 and ::1 (NetOrk/netork#798).
This commit is contained in:
@@ -213,3 +213,59 @@ class TestBoottime:
|
||||
|
||||
def test_garbage(self):
|
||||
assert parse.boottime("sysctl: unknown oid") is None
|
||||
|
||||
|
||||
class TestListeningSockets:
|
||||
"""In the shape of napalm-device-types' ListeningSocketDict: ``0.0.0.0`` and
|
||||
``::`` are every address of their family, as ``ss`` prints them."""
|
||||
|
||||
@staticmethod
|
||||
def _keys(sockets):
|
||||
return {(s["proto"], s["address"], s["port"], s["interface"]) for s in sockets}
|
||||
|
||||
def test_freebsd_sockstat(self):
|
||||
sockets = parse.sockstat(fixture("freebsd-vm", "sudo_sockstat_46lq_P_tcp_udp.txt"))
|
||||
assert self._keys(sockets) == {
|
||||
("udp", "127.0.0.1", 7779, None),
|
||||
("tcp", "::1", 7777, None),
|
||||
("tcp", "127.0.0.1", 7778, None),
|
||||
("tcp", "::", 22, None),
|
||||
("tcp", "0.0.0.0", 22, None),
|
||||
("udp", "::", 514, None),
|
||||
("udp", "0.0.0.0", 514, None),
|
||||
}
|
||||
sshd = next(s for s in sockets if s["port"] == 22 and s["address"] == "0.0.0.0")
|
||||
assert (sshd["process"], sshd["pid"], sshd["unit"], sshd["container_id"]) == (
|
||||
"sshd",
|
||||
1282,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
|
||||
def test_openbsd_fstat_keeps_only_listening_and_bound_sockets(self):
|
||||
sockets = parse.fstat_sockets(fixture("openbsd-vm", "sudo_fstat_n.txt"))
|
||||
keys = self._keys(sockets)
|
||||
assert ("tcp", "0.0.0.0", 22, None) in keys
|
||||
assert ("tcp", "::", 22, None) in keys
|
||||
assert ("tcp", "::1", 25, None) in keys
|
||||
assert ("tcp", "fe80::1", 25, "lo0") in keys
|
||||
assert ("udp", "10.0.2.15", 68, None) in keys
|
||||
assert ("udp", "127.0.0.1", 7779, None) in keys
|
||||
# connected (ntpd, ssh sessions) and unbound (*:0) sockets are not listening
|
||||
assert not any(s["port"] in (0, 123) or s["process"] == "sshd-session" for s in sockets)
|
||||
smtpd = next(s for s in sockets if s["port"] == 25 and s["address"] == "127.0.0.1")
|
||||
assert (smtpd["process"], smtpd["pid"]) == ("smtpd", 69044)
|
||||
|
||||
def test_openbsd_netstat_without_root_names_no_process(self):
|
||||
sockets = parse.netstat_listening(fixture("openbsd-vm", "netstat_an.txt"))
|
||||
keys = self._keys(sockets)
|
||||
assert ("tcp", "0.0.0.0", 22, None) in keys
|
||||
assert ("tcp", "::", 22, None) in keys
|
||||
assert ("tcp", "fe80::1", 25, "lo0") in keys
|
||||
assert ("udp", "127.0.0.1", 7779, None) in keys
|
||||
assert all(s["process"] is None and s["pid"] is None for s in sockets)
|
||||
assert not any(s["port"] == 123 for s in sockets) # ntpd's connected sockets
|
||||
|
||||
def test_a_socket_held_by_two_processes_is_listed_once(self):
|
||||
text = "www nginx 901 6 tcp4 *:80 *:*\nwww nginx 900 6 tcp4 *:80 *:*\n"
|
||||
assert [(s["pid"]) for s in parse.sockstat(text)] == [901]
|
||||
|
||||
Reference in New Issue
Block a user