The SO_BINDTODEVICE → IP_PKTINFO switch in the previous commit was a semantic change: the module no longer touches outgoing routing at all, and several places in the docs and the module's top-of-file comment still described the old mechanism. - README.md and debian/control now describe attribution as reading the ingress ifindex per connection from the kernel's IP_PKTINFO / IPV6_PKTINFO cmsg, and explicitly call out that the DSR / maglev return-path constraint is what makes the change necessary. - docs/design.md FR-1.1 / FR-1.5 / FR-1.6 are rewritten to forbid SO_BINDTODEVICE and to describe the cmsg-based lookup. NFR-6.1 notes these are ordinary unprivileged socket options. The "Components" / "Composes With" sections and the "Alternatives Considered" entry are brought in line — and a new entry records SO_BINDTODEVICE as a rejected alternative with the exact failure mode seen on an IPng production box. - docs/config-guide.md already carried the new description; unchanged here. - src/ngx_http_ipng_stats_module.c's top-level block comment is rewritten to match; the section header above init_module goes from "rebind listen sockets with SO_BINDTODEVICE" to "enable IP_PKTINFO on listen sockets, resolve ifindexes". Three SO_BINDTODEVICE mentions deliberately remain in the source and one in the design doc's alternatives table — all of them explain that the module *avoids* the option, which is itself load-bearing documentation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2.0 KiB
nginx-ipng-stats-plugin
Per-VIP, per-device traffic counters for nginx. Ships as a dynamic nginx module and a Debian package that loads into stock upstream nginx on Debian Trixie.
The module attributes every HTTP request to the interface it arrived on, reading the ingress ifindex per connection from the
kernel's IP_PKTINFO / IPV6_PKTINFO cmsg. Listening sockets stay plain wildcards, so outgoing packets follow the normal
routing table — which is what makes this safe for DSR / maglev deployments where the SYN arrives via a GRE tunnel and the
SYN-ACK must leave via the default route. Counters — requests, status codes, bytes, latency histograms — are exposed as
Prometheus text or JSON from a single HTTP scrape endpoint, filtered per-source. This is useful for any deployment where
traffic arrives on distinct interfaces — GRE tunnels, VLANs, bonded links, or plain ethernet — and per-interface observability
is needed.
Without any device=/ipng_source_tag= parameters, the module still counts and exposes per-VIP traffic under the configurable
default source tag (direct), which makes it a useful plain observability module for any nginx host.
See docs/design.md for the full design, including the attribution model, data flow, and requirements.
Quick start
make install-deps # install build and test dependencies (apt)
make build # build the .so out-of-tree
make pkg-deb # build a .deb package
make robot-test # run end-to-end tests via containerlab
Installing
sudo dpkg -i build/*.deb
The package installs the .so into /usr/lib/nginx/modules, drops a load_module stanza into /etc/nginx/modules-enabled/, and runs
nginx -t before completing.
Configuring
See docs/user-guide.md for an end-to-end walkthrough and docs/config-guide.md for the
directive and listen parameter reference.
License
Apache-2.0. See LICENSE.