Files
pf-linux-port/docs/en/guide.md
T
lgcosta dff2185b20 Docs: bring the documentation in line with the code
- Demo docs replaced by the screencast docs (single source of truth): the
  legacy demo-session.sh/demo-record.py and the v0.9 renderer were removed
  instead of being kept as a second convention.
- feature-matrix: stale 'M2-in-progress' vocabulary gone; the rows the 15
  gates prove now say VERIFIED-in-VM and name the gate; af-to is now an
  explicit non-goal row (INET6 0 in this build).
- guide: the queues/divert/pflog sections described a state that no longer
  exists (priq-only, divert dropping packets, pflog logging nothing); they
  now describe the shipped behaviour.
- vm-lab: the 15 real gates with what each proves, 15/15 green, and the
  pf0 -> cli/srv/gw2 ssh setup the screencasts rely on.
- install/build: pdivert and pflogd are built and ported (docs said
  otherwise); module object list now includes hfsc.c, fq_codel.c, pfk_pflog.c;
  the screencast pipeline is an optional build stage.
- pf-guide: the 'M2 scope only' framing is gone; honest gaps listed.
- caveats: three new sections about the screencast pipeline (MiniMax video
  generation blocked by the account tier, narration as a separate track, a
  video frame is not a verdict).
- porting-notes: what the recording taught us.
- README/README.pt-BR: status, milestones, layout and video section updated.

All internal doc links verified.
2026-11-12 11:51:25 -03:00

110 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Architecture guide — one page
Português: [docs/pt-BR/guide.md](../pt-BR/guide.md).
## What this port is
A faithful port of the **OpenBSD 8.0 PF engine** running inside **Linux
7.3.4** as one out-of-tree module, `pf.ko`. Nothing is faked: the matching,
state, table, normalization and queueing code *is* OpenBSD's, compiled under a
compatibility layer; control comes from the real `pfctl` speaking the real
OpenBSD ioctl ABI through `/dev/pf`. There is **one global PF instance bound
to `init_net`** — exactly like OpenBSD has one kernel; per-netns instances are
out of scope.
Build targets: OpenBSD 8.0 sources (sha256-pinned, `VERSIONS`), Linux 7.3.4
(`linux-7.3.4`), `gcc >= 15`, bison. `pfvar.h` v1.548 is shared bit-for-bit
between kernel and userland (ioctl encoding per `module/include/pfk.h`).
## Components
| Path | Role |
|---|---|
| `module/pfsrc/` | Ported OpenBSD files: `pf.c` (engine), `pf_ioctl.c` (DIOC* ABI), `pf_norm.c` (scrub/fragments), `pf_table.c`, `pf_ruleset.c`, `pf_if.c`, `pf_lb.c` (pools/NAT ports), `pf_osfp.c`, `pf_syncookies.c`, `ifq.c`/`hfsc.c`/`fq_codel.c` (staged for M5), `if_pflog.c` + `ip_divert.c` (staged references), `divert_sock.c` (divert entry stub), `pfk_mod.c` (load/exit, `/dev/pf`), `pfk_hooks.c` (netfilter data path) |
| `module/compat/` | Linux plumbing under the BSD-shaped sources: mbuf-over-`sk_buff` shim (`pfk_mbuf`), kernel glue (malloc/pools/timeouts/locks, `pfk_kern`), interface+address views (`pfk_if`), BSD-semantics IP output/forward (`pfk_ip4`, `pfk_ip6`), socket lookups (`pfk_pcb`), ifq API with priq semantics (`pfk_ifq`), radix/route-table/crypto ports |
| `module/include/` | `pfk.h` (master compat header + OpenBSD-style `_IO*` encoding), staged `net/pfvar.h`, `pfk_mbuf.h`, `pfk_net.h`, `pfk_if.h` |
| `tools/` | Linux build of OpenBSD 8.0 `pfctl` (single binary), `pdivert` relay, ABI parity harness (`abitest` + `make abi`), offline parse tests (`make test`). Deltas vs upstream: `tools/UPSTREAM.md` |
| `scripts/` | `fetch-sources.sh`, `vendorize.sh`, `stage-port.sh` (deterministic staging), `build-kernel.sh`, `build-module.sh`, `vm-*.sh` lab automation, `screencast/` (video pipeline) |
| `tests/vm/` | VM suite: 15 gates `01-state.sh … 15-queue.sh` + `lib.sh`; `tests/confs/` rulesets deployed to the lab |
| `patches/kernel/` | Core patch set — currently **empty** (`p1-needs.txt` records why no exports were needed); see [patches.md](patches.md) |
## Data path (M2, as implemented today)
OpenBSD runs PF at `IPL_NET` in process-ish context; our compat locks are
sleepable, so the hook **never calls `pf_test()` in softirq**. Packets are
stolen onto an ordered workqueue (`pfk_netisr`) and the verdict is applied by
re-injection, tagged per direction via `skb->mark` so the survivor is not
re-queued by our own hook.
```
Linux 7.3.4, module pf.ko
NIC ──► netfilter (softirq — enqueue only)
PRE_ROUTING PF_IN ─┐ ┌─ LOCAL_OUT PF_OUT
POST_ROUTING PF_OUT ─┤ NF_STOLEN ├─ (runs AFTER conntrack)
LOCAL_IN/FORWARD: pass-through ┘ POST_ROUTING: NF_IP_PRI_LAST-1
│ (skb ref + kmalloc pfk_pkt;
│ alloc failure ⇒ NF_ACCEPT = fail-open)
▼
pfk_netisr workqueue (process context)
│
pfk_m_wrap_skb(skb)──► struct mbuf shim
│
pf_test(af, dir, ifp, &m) ◄── the real OpenBSD 8.0 engine:
│ scrub/normalize → state lookup
│ → rules/anchors/tables/quick
│ → state insert → verdict
▼
pass ──► mark |= PFK_DONE_IN/OUT ──► reinject: PF_IN: netif_rx() (survivor
of reassembly allowed through)
PF_OUT: skb_dst()->output()
block/drop ──► kfree_skb() (+ PF's own return-rst/ICMP generation)
fragment-buffered ──► our reference dropped; pf keeps the survivor (m == NULL)
```
Control plane: `/dev/pf` (miscdev, mode 0600, write gated on `CAP_NET_ADMIN`)
→ `pfk_ioctl_io()` copies the parameter struct sized from the OpenBSD `_IOR/
_IOW` encoding, dispatches into the ported `pfioctl()` (`pf_ioctl.c`) — the
full DIOC* ABI as upstream. `pfctl -e/-f/-s/-t` are that same OpenBSD binary,
rebuilt.
## Queues, divert, pflog — today's observable behavior
- **Queues**: `pfctl` parses the OpenBSD 8.0 `queue`/`queue on` syntax and the
kernel side implements it in the BSD shape: `compat/pfk_ifq.c` provides the
`ifq_ops`/`pfq_ops` interfaces and the OpenBSD back ends (`pfsrc/hfsc.c`,
`pfsrc/fq_codel.c`) plug into them; **priq** is the default discipline.
Gate `15-queue.sh` proves the **hfsc** discipline end to end: the queue
config loads, the discipline attaches to the interface, `pfctl -s queue -v`
lists both classes with statistics, a 2 MB queued flow completes and the
class's packet counter advances. The class handles and the stats structs
come from OpenBSD's own `net/hfsc.h`/`net/fq_codel.h`, vendored so the
kernel and `pfctl` cannot drift apart.
- **Divert**: `divert-to`/`divert` tag evaluate to PF's `PF_DIVERT` verdict
and the module registers an `AF_DIVERT` socket family (`PF_DIVERT=32`) at
runtime — no kernel-tree patch. With no listener the packet is dropped
(`divs_noport`, BSD parity); with the `pdivert` relay running the packet is
delivered to userspace, rewritten and reinjected. Gate `11-divert.sh`.
- **pflog**: the engine queues logged packets, `pfsrc/pfk_pflog.c` exposes
them through `/dev/pflog`, and `tools/pflogd` writes a DLT_PFLOG pcap whose
record layout is byte-compatible with OpenBSD `pflog(4)`. Gate
`12-pflog.sh`. The deviation from upstream is the transport (a chardev
instead of an `if_pflog`/bpf interface), not the format.
## Milestones (see feature-matrix.md for per-feature status)
| Milestone | Scope | Status |
|---|---|---|
| M0 | repo, scripts, kernel build, VM boots custom kernel | done |
| M1 | compat layer, `pf.ko` loads, `/dev/pf`, `pfctl -f`/`-s info` | done |
| M2 | filter core: rules/states/tables/anchors VM gates | **done** — gates 01–04 green |
| M3 | NAT (`nat-to`/`rdr-to`/`binat-to`), scrub, pflog | **done** — gates 05/06/07/09 (incl. 2 MB integrity through a redirect) and pflog gate 12 |
| M4 | route-to/dup-to/reply-to, pools, divert e2e | **done** — gates 08/10/13 and divert gate 11 |
| M5 | queues: ifq back ends (hfsc/fq_codel) | **done (mechanical)** — gate 15: hfsc attaches, `pfctl -s queue -v` reports class stats, a 2 MB queued flow is counted by the class. `bandwidth` = HFSC linkshare (a guarantee under contention, not a cap) |
| M6 | docs complete, patchset tidy, full suite green, tagged releases | **done** — docs EN+PT-BR, 15/15 suite green, tagged (`v1.0.0`) |
## Next reads
[build.md](build.md) → [vm-lab.md](vm-lab.md) → [pf-guide.md](pf-guide.md) →
[feature-matrix.md](feature-matrix.md) → [screencast.md](screencast.md) →
[caveats.md](caveats.md).