# DF-3379 — pfsync sync engine is dead code (`pfsyncif` never initialized; RX dispatch never compiled)

## Impact
On any DragonFly system where an operator configures pfsync (the pf state-synchronization
interface used with CARP for HA firewall failover), **no state synchronization ever occurs**:
no pfsync packet is ever built or transmitted, and no pfsync packet can ever be received.
`ifconfig`-equivalent configuration (SIOCSETPFSYNC) succeeds, the interface exists, counters
exist — but the engine behind it cannot run. An HA pair silently carries **zero** shared
state: on failover every connection/NAT state is lost (re-authentication storms, black-holed
sessions, asymmetric-state assumptions broken), and the operator has no error, no warning,
and no counter to tell them.

## Root cause (three independent defects, each alone fatal to the engine)
1. **`pfsyncif` is never assigned** — `sys/net/pf/if_pfsync.c:90` declares
   `struct pfsync_softc *pfsyncif = NULL;` and `pfsync_clone_create()`
   (if_pfsync.c:131-192) never stores the new softc into it (upstream OpenBSD
   pfsync_clone_create does `pfsyncif = sc`). Every engine entry point takes its
   `sc == NULL` early-out:
   - `pfsync_input()`  — drops every packet at if_pfsync.c:489 (`if (!sc || ...) goto done`)
   - `pfsync_pack_state()` — if_pfsync.c:1286-1287 (`if (sc == NULL) return (0)`)
   - `pfsync_request_update()` — if_pfsync.c:1449-1450
   - `pfsync_clear_states()` — if_pfsync.c:1492-1493
2. **The RX dispatch is never compiled** — `sys/netinet/in_proto.c:281-296` guards the
   `.pr_input = pfsync_input` protosw entry with `#ifdef NPFSYNC`, but nothing in the
   tree defines `NPFSYNC` (no `use_pfsync.h` include exists; `sys/conf/files:1574` builds
   `if_pfsync.c` only as part of `pf`; no kernel config names a pfsync device). The stale
   comment at if_pfsync.c:482 ("This function is not yet called from anywhere") is
   therefore accurate despite in_proto.c's apparent registration.
3. **The bulk updater is never scheduled and is mis-wired** — `pfsync_bulk_update()`
   (if_pfsync.c:1545) is referenced by no callout anywhere; the only
   `callout_reset(&sc->sc_bulk_tmo, ...)` in the file (if_pfsync.c:1604) passes
   `pfsync_timeout` as the handler instead of `pfsync_bulk_update` (upstream OpenBSD
   passes pfsync_bulk_update). Additionally `SIOCSETPFSYNC` resets the default peer in
   **host** byte order (`sc->sc_sync_peer.s_addr = INADDR_PFSYNC_GROUP;` at
   if_pfsync.c:1076-1077, no `htonl`, vs. the correct `htonl(...)` at if_pfsync.c:150-151),
   which would send "multicast" pfsync to 240.0.0.224 (class E) instead of 224.0.0.240
   if the engine were live.

## Security consequences
- Silent loss of the HA state-sync security control (failover drops all state).
- TriReachability correction for this audit: known findings DF-0428, DF-0429, DF-0430,
  DF-0431, DF-0432 (the unauthenticated pfsync protocol family) are **not reachable** on
  this tree as shipped — the entire RX parser is behind two dead switches (`NPFSYNC`
  compile guard and the `!sc` check). They remain latent: any future commit that assigns
  `pfsyncif` and defines `NPFSYNC` re-arms all five without any further code change.

## Reproduce
See `build.sh` / `run.sh`. Summary:
1. `kldload pf; ifconfig pfsync0 create`
2. `./trigger_df3379`  (SIOCSETPFSYNC syncdev=lo0 syncpeer=10.0.2.2 maxupd=128 —
   a unicast peer defeats the "no listener configured" early-out at if_pfsync.c:1294,
   so a live engine *must* transmit)
3. enable pf with `pass out all keep state`; generate states (ping/ssh)
4. `tcpdump -i vtnet0 -n proto 240` and `tcpdump -i lo0 -n proto 240` — **zero packets**,
   while `pfctl -s state` shows live, updating states.

## Expected result (bug present)
- `pfctl -s state` non-empty; states created and updated during the capture window.
- Zero proto-240 (pfsync) packets on any interface, ever.
- Static: `grep -n "pfsyncif" sys/net/pf/if_pfsync.c` shows only the NULL initializer and
  reads — no assignment. `grep -rn NPFSYNC sys/` shows only the `#ifdef/#endif` pair in
  in_proto.c.

## Fix direction
Wire the engine up deliberately (assign `pfsyncif = sc` in pfsync_clone_create / clear on
destroy; compile-time or runtime registration of the RX hook; fix the bulk-update callout
handler and the htonl on the default peer) — **but only together with the authentication
fix for the DF-0428..0432 family**, since a working engine immediately exposes all five.
Absent that, the honest fix is to refuse SIOCSETPFSYNC configuration (or refuse creating
the clone) so operators get an error instead of silent non-synchronization.
