docs: fail-to is not an OpenBSD 8.0 keyword (removed upstream) - correct all references; reply-to gate added
This commit is contained in:
1 parent
3235af7f0b
commit
c2d1fc4c53
8 files changed
+61
-9
No files matched your search
@@ -9,7 +9,7 @@ real OpenBSD `pfctl` over the `/dev/pf` ioctl ABI. (A second module for the
|
||||
**Status:** the filter core, stateful filtering, tables, anchors, NAT
|
||||
(`nat-to`/`rdr-to`/`binat-to`), forced routing (`route-to`/`dup-to`), `divert`
|
||||
and `pflog` are **verified in a real libvirt lab** (13/13 gates green). The
|
||||
remaining features (ALTQ/pfq, af-to, `fail-to`/`reply-to`) are still pending — honest per-feature status:
|
||||
remaining features (ALTQ/pfq, af-to, `reply-to`) are still pending — honest per-feature status:
|
||||
[`docs/en/feature-matrix.md`](docs/en/feature-matrix.md).
|
||||
Demo (asciinema): [`docs/en/demo.md`](docs/en/demo.md).
|
||||
Design: [`docs/superpowers/specs/2026-11-11-pf-linux-port-design.md`](docs/superpowers/specs/2026-11-11-pf-linux-port-design.md)
|
||||
@@ -23,7 +23,7 @@ Design: [`docs/superpowers/specs/2026-11-11-pf-linux-port-design.md`](docs/super
|
||||
| M1 | compat layer, `pf.ko` loads, `/dev/pf`, `pfctl -f` / `-s info` | done |
|
||||
| M2 | filter core: rules/states/tables/anchors, VM gates 01–04 | **done** — gates 01/02/03/04 green |
|
||||
| M3 | NAT (`nat-to`/`rdr-to`/`binat-to`), scrub | **done** — gates 05/06/07/09 (incl. 2 MB integrity through a redirect, min-ttl/max-mss measured) |
|
||||
| M4 | route-to/dup-to/fail-to, divert e2e, pflog | **partial** — divert (11), pflog (12), route-to/dup-to (08), pools rr/source-hash (10) verified; `fail-to`/`reply-to` pending |
|
||||
| M4 | route-to/dup-to, divert e2e, pflog | **partial** — divert (11), pflog (12), route-to/dup-to (08), pools rr/source-hash (10) verified; `reply-to` pending |
|
||||
| M5 | queues: HFSC/FQ-CoDel back ends + `pfq` qdisc | pending |
|
||||
| M6 | docs complete, patchset tidy, full suite green, tagged release | **done** — docs EN+PT-BR, 11/11 suite green, tagged (v0.5.0) |
|
||||
|
||||
@@ -37,7 +37,7 @@ Design: [`docs/superpowers/specs/2026-11-11-pf-linux-port-design.md`](docs/super
|
||||
| Scrub | fragment reassembly, overlap drop, `min-ttl`/`max-mss`/`df`/`no-df`, `set reassemble` |
|
||||
| QoS (ALTQ successor) | `queue`/`queue on` with OpenBSD 8.0 `ifq` schedulers: **default (priq), HFSC, FQ-CoDel**, attached via the `pfq` qdisc; `pfctl -s queue -v` stats |
|
||||
| Divert | `divert-to` / `divert` tag + `pdivert` relay over an in-module `PF_DIVERT` socket family (verified: packet to userspace and back) |
|
||||
| Route | `route-to`/`dup-to` + pools (`round-robin`, `source-hash`) verified; `fail-to`, `reply-to` pending |
|
||||
| Route | `route-to`/`dup-to` + pools (`round-robin`, `source-hash`) verified; `reply-to` pending (`fail-to` no longer exists in OpenBSD 8.0) |
|
||||
| Logging | `pflogd` via `/dev/pflog` (DLT_PFLOG pcap, OpenBSD record layout) — verified |
|
||||
| Control | `/dev/pf` (0600), OpenBSD 8.0 ioctl ABI, Linux build of `pfctl` (single binary) |
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ As of 2026-11-11; source of per-row evidence: `out/test-logs/`,
|
||||
|
||||
| Feature | Upstream syntax | Milestone | Status |
|
||||
|---|---|---|---|
|
||||
| `route-to` / `dup-to` / `fail-to` / `reply-to` | `pass in … route-to { 10.0.3.2 } keep state` | M4 | **`route-to` and `dup-to` VERIFIED in lab** (gate `08-routeto.sh`): the packet leaves through the gateway's link instead of the normal egress (proved on the firewall's own interfaces), and `dup-to` keeps the original path while emitting the copy. `fail-to`/`reply-to` parse and are stored; no gate yet. Deviation: the forced nexthop is installed by setting `rt_gw4`/`rt_gw_family` on the looked-up route (Linux only ARPs the gateway when the route declares one). |
|
||||
| `route-to` / `dup-to` / `reply-to` | `pass in … route-to { 10.0.3.2 } keep state` | M4 | **`route-to` and `dup-to` VERIFIED in lab** (gate `08-routeto.sh`): the packet leaves through the gateway's link instead of the normal egress (proved on the firewall's own interfaces), and `dup-to` keeps the original path while emitting the copy. `reply-to` parses and is stored; gate pending. `fail-to` is **not a keyword in OpenBSD 8.0** (removed upstream), so it is out of scope here. Deviation: the forced nexthop is installed by setting `rt_gw4`/`rt_gw_family` on the looked-up route (Linux only ARPs the gateway when the route declares one). |
|
||||
| `divert-to` / `divert` tag | `pass in … divert-to <host> port <n>` | VERIFIED in lab | **Works end-to-end.** The module registers an `AF_DIVERT` socket family (`PF_DIVERT=32`) at runtime — no kernel-tree patch. A `divert-to` rule hands the packet to that family; with no listener the packet is dropped (`divs_noport`, BSD parity); with `tools/pdivert` running the packet is delivered to userspace, rewritten and reinjected (verified with a payload mutation + checksum fix, ping 0% loss). |
|
||||
| Queues, default scheduler (priq) | `queue root on wan`, `pfctl -s queue -v` | M5 | COMPILES-untested — `compat/pfk_ifq.c` implements the ifq API with **priq fallback semantics**; `queue` rules parse and load, but packets are not actually shunted through a classful qdisc until M5 |
|
||||
| HFSC | `queue on wan { hfsc … }` | M5 | COMPILES-untested — **priq fallback until M5**; `pfsrc/hfsc.c` staged (real OpenBSD 8.0 code) but **not in the module object list** |
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ rebuilt.
|
||||
| M1 | compat layer, `pf.ko` loads, `/dev/pf`, `pfctl -f`/`-s info` | done |
|
||||
| M2 | filter core: rules/states/tables/anchors VM gates | **in progress** — data path implemented, gates 01–04 not all green yet |
|
||||
| M3 | NAT (nat/rdr/binat), scrub e2e, pflog | pending |
|
||||
| M4 | route-to/dup-to/fail-to, divert e2e | pending |
|
||||
| M4 | route-to/dup-to, divert e2e | pending |
|
||||
| M5 | queues: ifq back ends (hfsc/fq_codel) + `pfq` qdisc | pending |
|
||||
| M6 | docs complete, patchset tidy, full suite green, tagged releases | in progress (this doc set) |
|
||||
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ treat them as load-and-pray until the matrix moves them.
|
||||
|---|---|---|
|
||||
| `nat-to`, `rdr-to`, `binat-to` | — | **Verified in the lab** (gates `05-nat`, `06-rdr`, the latter pulls 2 MB through a redirect and compares it byte for byte). `nat-to (egress)` expands the OpenBSD interface *group*; on a multi-NIC host prefer the explicit `nat-to (<iface>)` form. `binat-to` is expanded by pfctl into a `nat-to … static-port` + `rdr-to` pair (gate `07-binat`). |
|
||||
| `scrub (min-ttl, max-mss, df, random-id)`, `set reassemble` | — | **Verified in the lab** (gate `09-scrub.sh`): `min-ttl` and `max-mss` measurably rewrite transit packets. || `divert-to`, `divert` tag | — | **Verified in the lab** (gate `11-divert`): the packet is delivered to an `AF_DIVERT` socket (`PF_DIVERT=32`) served by `tools/pdivert`; with no listener it is dropped, exactly like BSD's `divs_noport`. |
|
||||
| `route-to`, `dup-to` | — | **Verified in the lab** (gate `08-routeto.sh`): the forced gateway is used instead of the normal egress, and `dup-to` copies while keeping the flow. `fail-to`/`reply-to` load but have no gate yet. |
|
||||
| `route-to`, `dup-to` | — | **Verified in the lab** (gate `08-routeto.sh`): the forced gateway is used instead of the normal egress, and `dup-to` copies while keeping the flow. `reply-to` loads but has no gate yet. `fail-to` is not a keyword in OpenBSD 8.0 (removed upstream). |
|
||||
| `queue` / `queue on` / `pfctl -s queue` | M5 | Parse and load. Queuing falls back to the priq-shaped compat `ifq` layer; **no bandwidth enforcement happens until the `pfq` qdisc lands**. |
|
||||
| `log`, `log all`, `block log`, `pflogd` | — | **Verified in the lab** (gate `12-pflog`): run `pflogd /tmp/x.pcap` (reads `/dev/pflog`); the file is a `DLT_PFLOG` (117) pcap readable by tcpdump/wireshark. |
|
||||
| `route-label …` | never (non-goal) | Rejected at the ioctl layer. |
|
||||
|
||||
@@ -58,7 +58,7 @@ Estado em 2026-11-11; evidência por linha: `out/test-logs/`,
|
||||
|
||||
| Feature | Sintaxe de upstream | Milestone | Status |
|
||||
|---|---|---|---|
|
||||
| Pools de `route-to` (`rr`, `round-robin`, `source-hash`, `hash`), `reply-to`, `dup-to`, `fail-to` | `pass in … route-to { 10.0.3.2, 10.0.2.2 } round-robin keep state` | M4 | **`round-robin` e `source-hash` VERIFICADOS em lab** (gate `10-pools.sh`): com dois gateways em dois enlaces, o round-robin usa ambos e o source-hash fixa todo fluxo de uma origem em um único enlace. `dup-to` verificado no gate 08; `fail-to`/`reply-to` ainda sem gate. |
|
||||
| Pools de `route-to` (`rr`, `round-robin`, `source-hash`, `hash`), `reply-to`, `dup-to` | `pass in … route-to { 10.0.3.2, 10.0.2.2 } round-robin keep state` | M4 | **`round-robin` e `source-hash` VERIFICADOS em lab** (gate `10-pools.sh`): com dois gateways em dois enlaces, o round-robin usa ambos e o source-hash fixa todo fluxo de uma origem em um único enlace. `dup-to` verificado no gate 08; `reply-to` ainda sem gate. `fail-to` **não existe no OpenBSD 8.0** (removido upstream), então está fora de escopo. |
|
||||
| `divert-to` / tag `divert` | `pass in … divert-to <host> port <n>` | VERIFICADO em lab | **Funciona ponta a ponta.** O módulo registra em runtime a família de sockets `AF_DIVERT` (`PF_DIVERT=32`) — sem patch no kernel. A regra `divert-to` entrega o pacote a essa família; sem listener o pacote é descartado (`divs_noport`, paridade BSD); com `tools/pdivert` rodando o pacote vai ao userspace, é reescrito e reinjetado (verificado com mutação de payload + correção de checksum, ping 0% loss). |
|
||||
| Filas, agendador default (priq) | `queue root on wan`, `pfctl -s queue -v` | M5 | COMPILES-untested — `compat/pfk_ifq.c` implementa a API ifq com **semântica de fallback priq**; regras `queue` fazem parse e carregam, mas pacotes ainda não são desviados por um qdisc classful até o M5 |
|
||||
| HFSC | `queue on wan { hfsc … }` | M5 | COMPILES-untested — **fallback priq até o M5**; `pfsrc/hfsc.c` staged (código real do OpenBSD 8.0) mas **fora da lista de objetos** do módulo |
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ tamanho embutido na codificação `_IOR/_IOW` OpenBSD e despacha para o
|
||||
| M1 | camada de compat, `pf.ko` carrega, `/dev/pf`, `pfctl -f`/`-s info` | concluído |
|
||||
| M2 | núcleo de filtragem: gates VM de regras/estados/tabelas/anchors | **em andamento** — caminho de dados implementado, gates 01–04 ainda não todos verdes |
|
||||
| M3 | NAT (nat/rdr/binat), scrub e2e, pflog | pendente |
|
||||
| M4 | route-to/dup-to/fail-to, divert e2e | pendente |
|
||||
| M4 | route-to/dup-to, divert e2e | pendente |
|
||||
| M5 | filas: back ends ifq (hfsc/fq_codel) + qdisc `pfq` | pendente |
|
||||
| M6 | docs bilíngues completas, patchset arrumado, suíte inteira verde, tags de release | em andamento (este conjunto de docs) |
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ matriz mudar.
|
||||
|---|---|---|
|
||||
| `nat-to`, `rdr-to` | — | **Verificado em lab** (gates `05-nat`, `06-rdr`; este último baixa 2 MB por um redirect e compara byte a byte). `nat-to (egress)` expande o *grupo* de interfaces do OpenBSD; num host multi-NIC prefira a forma explícita `nat-to (<iface>)`. O `binat-to` é expandido pelo pfctl num par `nat-to … static-port` + `rdr-to` (gate `07-binat`). |
|
||||
| `scrub (min-ttl, max-mss, df, random-id)`, `set reassemble` | — | **Verificado em lab** (gate `09-scrub.sh`): `min-ttl` e `max-mss` reescrevem de fato pacotes em trânsito. || `divert-to`, tag `divert` | — | **Verificado em lab** (gate `11-divert`): o pacote é entregue a um socket `AF_DIVERT` (`PF_DIVERT=32`) atendido pelo `tools/pdivert`; sem listener é descartado, exatamente como o `divs_noport` do BSD. |
|
||||
| `route-to`, `dup-to` | — | **Verificado em lab** (gate `08-routeto.sh`): o gateway forçado é usado em vez do egress normal, e `dup-to` copia mantendo o fluxo. `fail-to`/`reply-to` carregam mas ainda sem gate. |
|
||||
| `route-to`, `dup-to` | — | **Verificado em lab** (gate `08-routeto.sh`): o gateway forçado é usado em vez do egress normal, e `dup-to` copia mantendo o fluxo. `reply-to` carrega mas ainda sem gate. `fail-to` não é palavra-chave no OpenBSD 8.0 (removido upstream). |
|
||||
| `queue` / `queue on` / `pfctl -s queue` | M5 | Faz parse e carrega. A fila cai no fallback priq da camada `ifq` de compat; **nenhuma limitação de banda acontece até o qdisc `pfq` chegar**. |
|
||||
| `log`, `log all`, `block log`, `pflogd` | — | **Verificado em lab** (gate `12-pflog`): rode `pflogd /tmp/x.pcap` (lê `/dev/pflog`); o arquivo é um pcap `DLT_PFLOG` (117) legível por tcpdump/wireshark. |
|
||||
| `route-label …` | nunca (não-objetivo) | Rejeitado na camada de ioctl. |
|
||||
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
# M4 gate: reply-to - force the next hop of the *reply* direction.
|
||||
#
|
||||
# reply-to is the asymmetric-routing tool: a flow whose request arrives on one
|
||||
# link can have its answer pushed out through a different gateway. Proof is
|
||||
# taken on pf0's own interfaces (the lab's host bridge filters forwarded frames
|
||||
# that cross networks, so the peer side is not observable):
|
||||
#
|
||||
# request cli -> srv leaves via $IF_SRV (normal path, untouched)
|
||||
# reply srv -> cli leaves via $IF_GW (forced by reply-to), not $IF_CLI
|
||||
set -uo pipefail; cd "$(dirname "$0")"; . ./lib.sh
|
||||
|
||||
pf0 'insmod /opt/pf/pf.ko 2>/dev/null || insmod /tmp/pf.ko; true'
|
||||
pf0 '/opt/pf/pfctl -e; /opt/pf/pfctl -f /dev/null' >/dev/null
|
||||
|
||||
IF_SRV=$(pf0 "ip route get $SRV_IP | grep -o 'dev [a-z0-9]*' | cut -d' ' -f2")
|
||||
IF_GW=$(pf0 "ip route get $GW2_IP | grep -o 'dev [a-z0-9]*' | cut -d' ' -f2")
|
||||
IF_CLI=$(pf0 "ip route get $CLI_IP | grep -o 'dev [a-z0-9]*' | cut -d' ' -f2")
|
||||
[ -n "$IF_SRV" ] && [ -n "$IF_GW" ] && [ -n "$IF_CLI" ] || { bad "interface resolution"; finish; }
|
||||
[ "$IF_GW" != "$IF_CLI" ] || { bad "gateway and client share a link"; finish; }
|
||||
ok "reply would normally leave via $IF_CLI, forced via $IF_GW"
|
||||
|
||||
cat > /tmp/g13.conf <<C
|
||||
pass in quick proto icmp from $CLI_IP to $SRV_IP reply-to { $GW2_IP } keep state
|
||||
pass out quick keep state
|
||||
C
|
||||
pfload /tmp/g13.conf || { bad "reply-to rule load"; finish; }
|
||||
pf0 '/opt/pf/pfctl -sr' | grep -q 'reply-to' && ok "reply-to rule listed" || bad "reply-to not listed"
|
||||
|
||||
pf0 "pkill tcpdump; (tcpdump -ni $IF_GW -c 8 -e icmp > /tmp/rt_gw.td 2>&1 &); (tcpdump -ni $IF_CLI -c 8 -e icmp > /tmp/rt_cli.td 2>&1 &); (tcpdump -ni $IF_SRV -c 8 -e icmp > /tmp/rt_srv.td 2>&1 &); sleep 1" >/dev/null
|
||||
cli "ping -c2 -W2 $SRV_IP >/dev/null 2>&1"
|
||||
sleep 2
|
||||
|
||||
cnt() {
|
||||
local n
|
||||
n=$(pf0 "grep -c -- '$2' /tmp/rt_$1.td 2>/dev/null; true" | tr -dc '0-9')
|
||||
[ -n "$n" ] || n=0
|
||||
printf '%s' "$n"
|
||||
}
|
||||
|
||||
# the request path must be unchanged
|
||||
N=$(cnt srv "$CLI_IP > $SRV_IP"); [ "$N" -gt 0 ] && ok "request left via $IF_SRV ($N)" \
|
||||
|| bad "request did not leave via $IF_SRV"
|
||||
|
||||
# the reply must be forced to the gateway link
|
||||
N=$(cnt gw "$SRV_IP > $CLI_IP"); [ "$N" -gt 0 ] && ok "reply forced onto $IF_GW ($N)" \
|
||||
|| bad "reply not seen on $IF_GW"
|
||||
N=$(cnt cli "$SRV_IP > $CLI_IP"); [ "$N" -eq 0 ] && ok "reply did not use $IF_CLI" \
|
||||
|| bad "reply still left via $IF_CLI ($N)"
|
||||
|
||||
pf0 'pkill tcpdump; /opt/pf/pfctl -d' >/dev/null
|
||||
finish
|
||||
Reference in new issue
Block a user