pingerd pings every host of the IP plan and says how long the answers
took, for Prometheus.
- Whom it pings: every host in
/etc/ipplan.db(tablehost) that has an IPv4 address. The database is read again for every round, so new hosts are picked up without a restart; a host that has left the plan is pinged until the next restart. - One site only:
--site SITE(several times, or--site A,B) keeps it to the hosts of those sites of the plan. A host's site is the part of its network's name before the@-EVENTinEVENT@TECH-SRV-A; upper or lower case is the same. Without the switch it pings every site's hosts, as it always has. - Another database:
--database FILE. - How often: once a second (
--interval). - What it says: the histogram
icmp_rtt_seconds, one per host (labelhost), onhttp://<server>:13110/metrics(--port). A host that does not answer simply gets no new observations: compareicmp_rtt_seconds_countover time. - Rights: it starts as root and continues as
nobody- see "Privileges" below.
The sending and receiving is a small C module, dhmonpinger
(src/pinger/dhmonpinger.c); the daemon is src/pinger/pingerd.
pingerd must be started as root, and is root only while it starts:
- as root it opens the one raw ICMP socket, sets that socket's receive buffer (see below) and raises its own priority (nice -15);
- then it changes to the group
nogroupand the usernobody. With that it also loses every capability; the socket, already open, keeps working.
Everything else is done as nobody: reading the plan database,
sending the pings, receiving the answers, and the web server on port
13110. /etc/ipplan.db must therefore be readable by everyone.
Two things it does not do:
- It does not clear root's supplementary groups. Started by systemd there are none (a root service carries none on Debian); started by hand from a root shell, it keeps the groups of that shell.
- It has no account of its own: it shares
nobodywith whatever else runs as that user on the machine.
Every package is tried for this before it is released: the running
daemon's user must be nobody (.github/try.sh).
The answers of a round arrive all at once and wait in the socket's
receive buffer until pingerd has read them; what does not fit is
lost, and looks like hosts that did not answer. The module therefore
asks for a large buffer (3670016 bytes, which the kernel counts as
twice that).
-
Started as root - as the unit does -
pingerdgets that buffer whatever the kernel's limit is (it hasCAP_NET_ADMINand forces the size). Nothing needs to be set. -
Started without
CAP_NET_ADMIN(in a container, say), the kernel silently cuts the buffer down tonet.core.rmem_max- 212992 on a stock Debian 13, a seventeenth of what is asked for.pingerdsays so when it starts (WARNING: the receive buffer is ...). Then raise the limit, in/etc/sysctl.d/:net.core.rmem_max = 8388608
A release is a Debian package, built for Debian 13 - nothing is compiled on the server:
pinger_X.Y.Z_amd64.deb
/usr/sbin/pingerd
/usr/lib/python3/dist-packages/dhmonpinger.cpython-313-x86_64-linux-gnu.so
/usr/lib/systemd/system/pingerd.service
apt install ./pinger_X.Y.Z_amd64.deb
systemctl enable --now pingerd
apt fetches what it depends on: Debian's python3-prometheus-client.
The package depends on exactly the Python it was built for (Debian
13's, 3.13) - the module is tied to it. Installing it starts nothing
(pingerd needs /etc/ipplan.db); an upgrade restarts a pingerd
that is running.
- Set the version in
src/pinger/setup.py. - Push; the
ciworkflow builds the package and tries it. - Tag that commit
vX.Y.Z(the same version) and push the tag: thereleaseworkflow builds, tries and publishes the package andSHA256SUMSas a GitHub release.
Building is .github/build.sh. Trying is .github/try.sh, in a new
Debian 13 container that has no compiler: the package installs with
apt, the module pings 127.0.0.1 (tests/loopback.py), pingerd reads
its hosts from a plan database and answers on its web port with what it
measured, as nobody, and the package is removed again.
docker run --rm -v "$PWD:/src" -w /src debian:13 sh .github/build.sh
docker run --rm --cap-add NET_RAW --cap-add SYS_NICE \
--cap-add NET_ADMIN -e PINGER_FULL_BUFFER=1 \
-v "$PWD:/src:ro" -w /src debian:13 sh .github/try.sh
The first leaves the package in dist/. (With rootless podman the
second stops where pingerd raises its priority, and the buffer
cannot be forced: both need real root.)
The old src/pinger/Makefile (build and install straight onto the
machine it runs on, under /opt/pinger) is unchanged, and so is
pingerd.service in the repository, which names that path; the
package's unit names /usr/sbin/pingerd.