Files

6.0 KiB
Raw Permalink Blame History

VM validation lab (libvirt / Alpine)

Português: docs/pt-BR/vm-lab.md.

All commands below were read from the scripts in scripts/ (they are the source of truth); the lab has been created and run during M0–M2 — a full-suite green run is the M2+ gate and is still in progress (see §6).

1. Where it lives

Host Role
laptop (this repo) builds kernel/module/tools; drives everything over ssh
deb — root@192.168.1.242 libvirt hypervisor; runs the 4 pftest-* Alpine VMs

The lab never needs NAT to the outside; the only external access is the libvirt default network (192.168.122.0/24, mgmt+DHCP) used for cloud-init package installs and test ssh. The existing sms-vm on deb is never touched.

2. Topology (single source: lab/ips.env)

VM Role NICs Kernel
pftest-pf0 filter under test (runs pf.ko) mgmt (dhcp, default net), lan 10.0.1.1/24, wan 10.0.2.1/24, wan2 10.0.3.1/24 stock Alpine via image-extracted direct boot; or the self-built 7.3.4 bzImage with --pf0-kernel= (root=/dev/vda2 ro console=ttyS0,115200n8, no initrd)
pftest-cli client mgmt dhcp (routes disabled), lan 10.0.1.10/24, gw 10.0.1.1 (pf0) Alpine stock
pftest-srv server mgmt dhcp (routes disabled), wan 10.0.2.20/24, gw 10.0.2.1 (pf0), 10.0.3.0/24 via gw2 Alpine stock
pftest-gw2 second-path gateway (ip_forward=1) wan2 10.0.3.2/24, wan 10.0.2.2/24 (no mgmt NIC) Alpine stock

All VMs get net.ipv4.ip_forward=1 and rp_filter=0 via cloud-init, root password pftest plus an injected lab keypair (/root/.ssh/pflab_ed on deb, copy on the laptop for ProxyJump), and packages iperf3 tcpdump python3 scapy bash iptables (Alpine name is scapy; the image also needs iptables for pf0 masquerade experiments).

libvirt networks: default (kept as-is, started+autostarted for packages) and isolated bridges pf-lan 10.0.1.0/24, pf-wan 10.0.2.0/24, pf-wan2 10.0.3.0/24 — forward="none", no DHCP, plus host-side bridge IPs 10.0.x.254 on deb so deb can ssh into every guest's data NIC. VM sizing: 2 vCPU / 1 GB each. Disks are per-VM qcow2 backing a shared immutable /var/lib/libvirt/images/alpine-base.qcow2 (synced from _vendor/images/alpine-3.22-x86_64-cloud.qcow2 by sha256).

Boot note: the Alpine cloud image is not BIOS/UEFI bootable in QEMU as delivered (empty ESP), so all VMs use direct kernel boot from the image's own extracted kernel; --pf0-kernel= only swaps pf0's kernel for the built one.

3. Recreate / deploy / destroy

./scripts/vm-create.sh                       # idempotent: tears down and rebuilds pftest-* domains + seeds
./scripts/vm-create.sh --pf0-kernel=out/linux-7.3.4-bzImage   # pf0 boots the built kernel (T6)
./scripts/vm-create.sh --recreate-base       # force base-image re-sync
./scripts/vm-deploy.sh                       # laptop→deb ($DEPLOY_DIR)→pf0:/opt/pf:
                                             #   out/*bzImage, module/*.ko, tools/out/*
./scripts/vm-destroy.sh                      # domains, disks, seeds, pf-* nets, ssh fragments
./scripts/vm-destroy.sh --purge-base --purge-keys   # + base image + lab keypair

vm-create.sh ends by validating ssh pf0 'ip -br a' and leaves all VMs running. Laptop ssh aliases (pf0|cli|srv|gw2, ProxyJump via deb) are generated into ~/.ssh/config.d/pflab.

4. Running the suite

./scripts/vm-run-tests.sh        # or: make test

Deterministic by design: before every gate the runner destroys and restarts pftest-pf0 (fresh disk delta + no leftover module), waits for ssh on the mgmt DHCP address 192.168.122.8, re-deploys pf.ko/pfctl/*.conf into the guest, then runs the gate script from the laptop (gates talk to the VMs through deb with nested ssh — tests/vm/lib.sh). Per-gate output is captured to out/test-logs/<gate>.sh.log; the runner prints PASS <gate> / FAIL <gate> (rc=...) per test, a summary PASS=n FAIL=m, and exits non-zero if anything failed.

5. What each gate proves today

Gate Scenario (asserted with real traffic, not exit codes)
01-state.sh pass in/out quick proto icmp keep state → cli pings srv through pf0 (transit); pf0 then lists an icmp state via pfctl -s states
02-block.sh block drop in quick proto icmp → cli ping fails; block return-rst in quick proto tcp … port 8080 → cli's nc to srv:8080 is refused (RST), checked via nc exit
03-tables.sh table <blk> { 10.0.1.10 } + block in quick from <blk> → pfctl -t blk -T show lists it; cli ping blocked; live -T delete unblocks the same flow
04-anchors.sh anchor rule (-a restricted -f) passing icmp is evaluated before a later main-ruleset block (quick/top-placement); pfctl -s Anchors lists the anchor

Planned gates per the plan document (T11–T18): 05-nat, 06-rdr, 07-binat, 08-scrub (scapy), 09-routeto, 10-dup-fail, 11-divert, 12-queues (iperf rate tolerance ±15%), 13-pflog, 14-soak (reload under traffic + rmmod cycle + kmemleak). IPv6 is smoke-only for state/block/nat66/scrub. bench-basic.sh (planned, not in the tree yet) is informational; there is no numeric perf gate.

6. Current suite status (2026-11-11, honestly)

The M2 gates 01–04 exist and run, but are not all green yet — the data path (netisr workqueue model) and state visibility are being fixed in the M2 gate work. The latest captured run (out/test-logs/) shows the mixed state: transit ping OK / state listing failing on 01, icmp-blocked OK on 02, partial results on 03, 04 aborted. Treat "VERIFIED-in-VM" in the feature matrix as reserved for gates that pass in a full clean run; nothing beyond M1 is claimed VERIFIED until then.

7. Manual poking

ssh pf0                       # via laptop alias (ProxyJump deb)
insmod /opt/pf/pf.ko
/opt/pf/pfctl -e
/opt/pf/pfctl -f /tmp/g1.conf
/opt/pf/pfctl -s info ; /opt/pf/pfctl -s states -v
dmesg | grep pfk

Artifacts of future full runs (log + pcaps) are meant to ship with the Gitea release (spec §7).