$ bpfstatsd [options...] <command> [arguments...]
bpfstatsd
is a daemon that runs an arbitrary command for every N packets
received on a network interface, using statistics gathered from the Berkeley
Packet Filter, which on
BSD systems is the /dev/bpf
device.
For example, to output a message for every 100 packets on em0
:
$ bpfstatsd -c 100 -i em0 /bin/sh -c 'echo "Hello, World!"'
The number of packets required to trigger the command and the interface to
measure statistics for are configurable using command-line arguments but, if
omitted, default to 1 and pflog0
, respectively. For full documentation, run:
$ bpfstatsd -h
Although this software is a general-purpose packet counter, the author
anticipates that it will be most useful when combined with dedicated pflog(4)
interfaces that log specific firewall rules. For example, this software could
be used to run a command when connections are blocked or established through a
firewall using a particular address, protocol, or port. More comprehensive
examples are documented in later sections.
This software depends on the /dev/bpf
interface and is currently only
supported on OpenBSD, although it may be ported to
other operating systems in the future. See the bpf(4)
manual page for more
information.
The bpfstatsd
binary requires, and is therefore installed with, root
setuid
privileges which are necessary to open the /dev/bpf
device. These privileges
are dropped at the earliest opportunity to mitigate any vulnerabilities.
Additional security measures employed include the pledge(2)
and unveil(2)
system calls to limit system call and filesystem access, respectively, although
these mitigations are not implemented for the command run by bpfstatsd
which
runs in a separate child process via execve(2)
.
To build and install the binary to /usr/local/bin/bpfstatsd
, run:
$ make && doas make install
The program runs in the foreground by default, but can be daemonized with a
suitable rc.d(8)
configuration. An example file is provided in the rc
directory of this distribution.
In this example, bpfstatsd
is used to automatically turn on an unattended
device (for example, an IP camera) when clients attempt to connect to it.
Connections to the device must be made through a pf(4)
firewall so they can
be logged to a pflog(4)
interface that this software uses to measure
statistics. It is technically possible to use the bpf(4)
device to detect the
connections directly on the receiving interface, without any pflog(4)
involvement, but that is beyond the intended scope of this software, which is
to be a simple packet counter for a particular interface.
This example assumes the device is connected to a smart plug and controllable via Zigbee2MQTT, although this example should, in theory, work with any home automation system that exposes a suitable interface. The cURL binary must be installed to send the message to the MQTT broker to turn on the device. The actual command depends on the home automation system used.
Create a dedicated pflog(4)
interface, e.g.
# echo "up" > /etc/hostname.pflog1
# sh /etc/netstat pflog1
Configure pf(4)
to log packets that create state to this interface (refer to
the pf.conf(5)
manual page for full details), e.g.
pass in log (to pflog1) quick on lan inet proto tcp to $camera port 9000
Run bpfstatsd
to send an MQTT message for each packet on pflog1
, e.g.
$ bpfstatsd -i pflog1 /usr/local/bin/curl \
-d '{"state": "ON"}' \
mqtt://localhost:1883/zigbee2mqtt/plug1/set
If this is all working as intended, then a connection to the device will turn the device on if it was previously turned off. Automatically turning off the device is out of scope for this documentation and is left as an exercise for the reader.