Files
pf-linux-port/docs/pt-BR/vm-lab.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

163 lines
8.1 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.
# Bancada de validação em VMs (libvirt / Alpine)
English: [docs/en/vm-lab.md](../en/vm-lab.md).
Todos os comandos abaixo foram lidos dos scripts em `scripts/` (eles são a
fonte de verdade); a bancada já foi criada e executada durante M0–M2 — uma
rodada completa e verde da suíte é o gate do M2+ e ainda está em andamento
(ver §6).
## 1. Onde ela mora
| Host | Papel |
|---|---|
| laptop (este repo) | compila kernel/módulo/ferramentas; comanda tudo por ssh |
| **deb** — `root@192.168.1.242` | hipervisor libvirt; roda as 4 VMs Alpine `pftest-*` |
A bancada não precisa de NAT para a internet; o único acesso externo é a
rede libvirt `default` (192.168.122.0/24, mgmt+DHCP) usada para o cloud-init
instalar pacotes e para o ssh dos testes. A `sms-vm` existente no deb nunca é
tocada.
## 2. Topologia (fonte única: `lab/ips.env`)
| VM | Papel | NICs | Kernel |
|---|---|---|---|
| `pftest-pf0` | filtro sob teste (roda o `pf.ko`) | `mgmt` (dhcp, rede default), `lan` 10.0.1.1/24, `wan` 10.0.2.1/24, `wan2` 10.0.3.1/24 | Alpine padrão via boot direto do kernel extraído da imagem; **ou** o `bzImage` 7.3.4 compilado com `--pf0-kernel=` (`root=/dev/vda2 ro console=ttyS0,115200n8`, sem initrd) |
| `pftest-cli` | cliente | `mgmt` dhcp (rotas desabilitadas), `lan` 10.0.1.10/24, gw 10.0.1.1 (pf0) | Alpine padrão |
| `pftest-srv` | servidor | `mgmt` dhcp (rotas desabilitadas), `wan` 10.0.2.20/24, gw 10.0.2.1 (pf0), 10.0.3.0/24 via gw2 | Alpine padrão |
| `pftest-gw2` | gateway do segundo caminho (ip_forward=1) | `wan2` 10.0.3.2/24, `wan` 10.0.2.2/24 (sem NIC mgmt) | Alpine padrão |
Todas as VMs recebem `net.ipv4.ip_forward=1` e `rp_filter=0` via cloud-init,
senha de root `pftest` mais um par de chaves de laboratório injetado
(`/root/.ssh/pflab_ed` no deb, cópia no laptop para ProxyJump), e os pacotes
`iperf3 tcpdump python3 scapy bash iptables` (o nome no Alpine é `scapy`; a
imagem também precisa de `iptables` para os experimentos de masquerade do
pf0).
Redes libvirt: `default` (mantida como está, iniciada+autostart para os
pacotes) e bridges isoladas `pf-lan` 10.0.1.0/24, `pf-wan` 10.0.2.0/24,
`pf-wan2` 10.0.3.0/24 — `forward="none"`, sem DHCP, mais IPs de bridge no
host `10.0.x.254` no deb para que o deb consiga ssh/icmp na NIC de dados de
cada guest. Dimensionamento: 2 vCPU / 1 GB por VM. Os discos são qcow2 por
VM com **backing** na imagem compartilhada imutável
`/var/lib/libvirt/images/alpine-base.qcow2` (sincronizada de
`_vendor/images/alpine-3.22-x86_64-cloud.qcow2` por sha256).
> Nota de boot: a imagem cloud do Alpine não é inicializável em BIOS/UEFI no
> QEMU como entregue (ESP vazia), então *todas* as VMs usam boot direto do
> kernel extraído da própria imagem; `--pf0-kernel=` apenas troca o kernel
> do pf0 pelo compilado.
## 3. Recriar / deployar / destruir
```sh
./scripts/vm-create.sh # idempotente: derruba e recria domínios + seeds pftest-*
./scripts/vm-create.sh --pf0-kernel=out/linux-7.3.4-bzImage # pf0 sobe com o kernel compilado (T6)
./scripts/vm-create.sh --recreate-base # força re-sync da imagem base
./scripts/vm-deploy.sh # laptop→deb ($DEPLOY_DIR)→pf0:/opt/pf:
# out/*bzImage, module/*.ko, tools/out/*
./scripts/vm-destroy.sh # domínios, discos, seeds, redes pf-* e fragmentos ssh
./scripts/vm-destroy.sh --purge-base --purge-keys # + imagem base + chaves do lab
```
O `vm-create.sh` termina validando `ssh pf0 'ip -br a'` e deixa todas as VMs
ligadas. Os aliases de ssh do laptop (`pf0|cli|srv|gw2`, ProxyJump via deb)
são gerados em `~/.ssh/config.d/pflab`.
**Do pf0 para as outras VMs.** O firewall tem seu próprio par de chaves e os
pares confiam nele, então uma sessão no pf0 pode pilotar o cliente e o
servidor diretamente — é isso que os screencasts fazem
(`ssh cli 'ping …'`):
```sh
# uma vez por lab, a partir do hypervisor
ssh pf0 'ssh-keygen -t ed25519 -N "" -C pf0 -f /root/.ssh/id_ed25519'
# depois autorizar essa chave pública no cli, no srv e no gw2
```
O `/root/.ssh/config` do pf0 mapeia os nomes curtos `cli`, `srv` e `gw2`
para 10.0.1.10, 10.0.2.20 e 10.0.3.2, e o `/etc/hosts` traz o mesmo
mapeamento.
## 4. Rodando a suíte
```sh
./scripts/vm-run-tests.sh # ou: make test
```
Determinístico por projeto: **antes de cada gate** o runner destrói e
reinicia o `pftest-pf0` (delta de disco e módulo zerados), espera o ssh no
endereço DHCP de mgmt `192.168.122.8`, re-deploya `pf.ko`/`pfctl`/`*.conf` no
guest e então roda o script do gate a partir do **laptop** (os gates falam
com as VMs através do deb via ssh aninhado — `tests/vm/lib.sh`). A saída de
cada gate é gravada em `out/test-logs/<gate>.sh.log`; o runner imprime
`PASS <gate>` / `FAIL <gate> (rc=...)` por teste, um resumo `PASS=n FAIL=m`
e sai com código diferente de zero se algo falhou.
## 5. O que cada gate prova hoje
Cada gate afirma **tráfego real e estado observável**, nunca apenas um código
de saída: o pacote chega, é descartado ou é traduzido, e a prova é lida de
volta nos contadores do próprio firewall, na tabela de estados, em capturas de
pacote ou no par.
A lista completa está na §6 (15 gates, todos verdes). Cada gate é um script
shell em `tests/vm/` que fala com as VMs através do deb com ssh aninhado,
usando os helpers de `tests/vm/lib.sh` (`pf0`, `cli`, `srv`, `gw2`, `pfput`,
`pfload`, `ok`, `bad`). O `scripts/vm-run-tests.sh` roda tudo em ordem e
guarda `out/test-logs/<gate>.sh.log`.
O que **não** tem gate (lacunas honestas): o caminho IPv6, o fingerprint de
OS, as opções `set` (`state-policy`, `allow-opts`) e os syncookies — o código
compila dentro do `pf.ko`, mas nenhum gate os exercita. O FQ-CoDel compila e
compartilha o header de estatísticas com o `pfctl`, mas só a disciplina
**hfsc** é exercitada.
**Não existe gate numérico de performance**: o `bench-basic.sh` não está na
árvore e nenhuma meta de vazão é reivindicada.
## 6. Status atual da suíte (2026-11-12)
**15/15 gates verdes.** Uma rodada completa e limpa
(`./scripts/vm-run-tests.sh`, ~9 min) recria e reinicia o `pftest-pf0` antes
de cada gate, reimplanta o módulo e as ferramentas, e termina com
`PASS=15 FAIL=0`. Os logs capturados ficam em
`out/test-logs/<gate>.sh.log`.
| Gate | O que prova hoje |
|---|---|
| `01-state.sh` | `keep state` em um ping de trânsito; o estado aparece no `pfctl -ss` |
| `02-block.sh` | `block drop` mata o ping; `block return-rst` devolve RST ao cliente |
| `03-tables.sh` | uma tabela `pfr` bloqueia um host; um `-T delete` ao vivo libera o mesmo fluxo |
| `04-anchors.sh` | a regra de um anchor é avaliada antes de um `block` posterior do ruleset principal |
| `05-nat.sh` | NAT de origem: o pacote sai com o endereço do pool e a resposta é des-NATeada |
| `06-rdr.sh` | redirect de porta de ponta a ponta, mais 2 MB passando com integridade byte a byte |
| `07-binat.sh` | `binat-to`: o par vê o endereço mapeado e o original nunca vaza |
| `08-routeto.sh` | `route-to` sai pelo link do gateway; `dup-to` copia e mantém o fluxo |
| `09-scrub.sh` | `min-ttl` e `max-mss` medidos no egresso do próprio firewall |
| `10-pools.sh` | seleção do pool de NAT (`round-robin`, `source-hash`) |
| `11-divert.sh` | `divert-to` para o relay `pdivert`: o pacote vai para o userspace e volta |
| `12-pflog.sh` | os pacotes logados chegam a `/dev/pflog` e viram um pcap DLT_PFLOG |
| `13-replyto.sh` | `reply-to` força a resposta para o link do gateway |
| `14-soak.sh` | reload de ruleset sob tráfego, ciclo de `rmmod`, checagem de vazamento |
| `15-queue.sh` | filas hfsc: anexação, estatísticas da classe, um fluxo de 2 MB contado pela classe |
Os screencasts ([screencast.md](screencast.md)) mostram boa parte desses
gates sendo executados, com o diagrama de rede do lab desenhado sobre o
terminal.
## 7. Inspeção manual
```sh
ssh pf0 # via alias do laptop (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
```
Artefatos de rodadas completas futuras (log + pcaps) devem ir junto com o
release no Gitea (spec §7).