Linx.Netfilter is the kernel's modern firewall — nf_tables — modelled as plain
Elixir values, applied atomically, and (optionally) kept live by reconciling the
value you want against the rules the kernel actually holds.
nf_tables is a single coherent kernel surface — packet filtering, NAT,
connection tracking, and packet-event streams — reached over the
NETLINK_NETFILTER family. Linx.Netfilter represents a whole ruleset
(%Ruleset{}: tables → chains → ordered rules, plus sets, maps, vmaps and named
objects) as ordinary, inspectable data, with four verbs over it: build a
ruleset (pipeline DSL or ~NFT sigil), pull the kernel's current state into
a value, diff two rulesets into a patch, and push a ruleset to the
kernel. Every mutation rides a mandatory BATCH_BEGIN/BATCH_END transaction:
the kernel applies the whole batch or rejects it whole. Kernel state lives in the
kernel; the Elixir value is just a value.
Where it fits
Netfilter is a concept module peer to Linx.Process, Linx.Cgroup,
Linx.User, Linx.Seccomp and the rest — the firewall mental model made
explicit. It rides Linx.Netlink.Nfnl (mirroring how Rtnl rides the same wire
core), so a ruleset can be pushed into the host's netns or, via
Nfnl.open({:pid, n}), into a child's fully isolated nftables instance. Each
netns has its own tables, generation counter, and commit mutex. Its consumers are
firewall appliances and container orchestrators; its pull/diff/push/
subscribe shape is the reference template that Linx.Reconcile generalises.
Flow
Two properties stand out. First, the reconcile cycle: pull current state, diff
against desired, push only the minimal patch as one batch — guarded by the
kernel's generation counter (BATCH_GENID) so a concurrent nft/firewalld
writer is detected and the push retried rather than clobbering. Second, the
owner table: by default a table is destroyed when the socket that created it
closes, so the firewall's lifetime is the owning supervisor's lifetime.
flowchart LR
desired["desired Ruleset<br/>(build / ~NFT)"] --> diff["diff"]
pull["pull<br/>(kernel state)"] --> diff
diff -->|"minimal patch"| push["push(mode: :reconcile)<br/>one atomic batch + GENID CAS"]
push --> kernel["kernel nftables"]
kernel -.->|"NFNLGRP_NFTABLES<br/>multicast"| sub["subscribe (Monitor)"]
sub -.->|"look now"| pull
owner["owning socket closes"] -.->|"NFT_TABLE_F_OWNER"| gone["table vanishes"]Learn more
- API —
Linx.Netfilter(withLinx.Netfilter.Ruleset,Table,Chain,Rule,Set/Map/Vmap,Object,Monitor, andLogfor NFLOG) - Examples — netfilter-examples.md: building, pushing, pulling, diffing, monitoring, and NFLOG capture
- References — netfilter-references.md: the nfnetlink protocol and nft kernel surface