Manipulate Linux netfilter over netlink: the connection tracking table and the nftables ruleset.
This is the high-level crate. It sits on
netlink-packet-netfilter the way
rtnetlink sits on netlink-packet-route.
Every dump returns a Stream. The kernel answers with one message per
object followed by NLMSG_DONE, and a conntrack table or a large ruleset
spans many of them; consuming only the first item truncates the result
without any error.
nftables applies changes atomically, and the kernel requires an entire
batch to arrive in one datagram -- nfnetlink_rcv dispatches on the first
message in the buffer it is handed and consumes the rest as the batch.
Transaction serialises the whole thing and writes it in one call, which
is why it takes a socket of its own rather than going through Handle.
An nftables rule is a program over registers: matching a TCP destination
port means loading two bytes from the transport header into a register,
comparing that register against the port, then emitting a verdict.
nftables::expr::Rule keeps that shape but allocates the registers, so no
caller has to keep two expressions agreeing about one.
Rule::new()
.meta_eq_u8(MetaKey::L4Proto, 6)
.payload_eq(PayloadBase::Transport, 2, &22u16.to_be_bytes())
.counter()
.accept()
.build()NfLog receives logged packets and NfQueue intercepts them; both own their own socket. A queue is not request and response: the
kernel pushes a packet and blocks it until a verdict quoting its id comes
back, so its traffic is unsolicited, continuous, and must not queue behind
anything else.
Most tests check message construction against bytes. The ones in
tests/kernel.rs need an actual kernel, CAP_NET_ADMIN, and they change
netfilter state:
NETFILTER_TEST_IN_NETNS=1 cargo testThat routes the test binary through scripts/netns-test-runner.sh, which
runs it in a container with --net=none. nftables tables, the conntrack
table and nfqueue bindings are all per network namespace, so anything the
tests create dies with the container instead of landing on the machine
running them. /nix is mounted read-only because the binary's ELF
interpreter lives there.
Without the variable the runner execs the binary directly, so a plain
cargo test needs neither Docker nor privileges and the kernel tests skip
themselves.