# DF-0571 — Verdict: REPRODUCED (panic / DoS)

## Verdict
REPRODUCED. The two `panic("ipfw3: unsupported proto %u", ip->ip_p)`
sites at sys/net/ipfw3_nat/ip_fw3_nat.c:219 (inbound) and :254
(outbound) are both reachable as soon as any ipfw3 NAT rule matches an
IP packet whose protocol is not TCP/UDP/ICMP. Reproduced by sending a
single IP packet with `ip_p = 132` (SCTP) outbound through a NAT'd
interface.

## Mechanism (trigger → primitive → effect)
1. `ipfw3 nat 1 config ip <ifaddr>` — install NAT configuration.
2. `ipfw3 add 100 nat 1 ip from any to any out` — install a NAT rule
   that matches outbound IPv4 packets.
3. Any outbound IPv4 packet with proto ∉ {TCP, UDP, ICMP} matches the
   rule.
4. PFIL hook `ip_fw3_check_out` (sys/netinet/ip_output.c:529 →
   sys/net/ipfw3/ip_fw3.c:1255) calls `ip_fw3_chk` (sys/net/ipfw3/
   ip_fw3.c:318) which dispatches to `check_nat` (ip_fw3_nat.c:143).
5. `check_nat` calls `ip_fw3_nat()` (ip_fw3_nat.c:173). The outbound
   branch at line 224 enters `switch (ip->ip_p)` at line 228.
6. For proto=132 (SCTP), no case matches; control falls to
   `default: panic("ipfw3: unsupported proto %u", ip->ip_p)` at
   line 254.
7. Kernel panics. Confirmed stack:

   ```
   panic: ipfw3: unsupported proto 132
   ip_fw3_nat() at ip_fw3_nat+0x7f
   check_nat() at check_nat+0x60
   ip_fw3_chk() at ip_fw3_chk+0x1aa
   ip_fw3_check_out() at ip_fw3_check_out+0x44
   pfil_run_hooks() at pfil_run_hooks+0x6f
   ```

## PoC
- `raw_trigger.c` — opens `IPPROTO_RAW` socket, IP_HDRINCL, builds a
  minimal IPv4 header with `ip_p = 132`, sends via `sendto`. Panic is
  immediate. Requires `SYSCAP_NONET_RAW` (root) to open the raw socket.
- `trigger.c` — alternative unprivileged angle via UDP multicast join;
  on this guest the kernel-routed IGMP report does not reach the NAT
  path because the only outbound path it takes is multicast-specific
  and bypasses ipfw3 NAT for our test setup. (Left as a starting point
  for unpriv-reachable variants on routers where NAT matches all
  multicast traffic.)

## Threat model / reachability
- **Router scenario (realistic)**: a DFly router running ipfw3 NAT
  with a NAT rule that matches outbound traffic. Any host on the
  NAT'd subnet emitting SCTP/GRE/ESP/AH/IGMP/UDPLite packets crashes
  the router. The attacker is the *sender* of the packet, not a local
  user on the router.
- **Local-user scenario**: limited. Unprivileged users on the router
  host itself can cause panic only if their traffic matches a NAT rule
  with a non-TCP/UDP/ICMP protocol they can emit. Standard sockets
  only emit TCP/UDP/ICMP, so this requires either a raw socket
  (root-only) or a kernel-emitted packet (e.g. IGMP membership report)
  that matches a NAT rule covering multicast destinations.

Net: **DoS (panic) of any ipfw3-NAT-enabled router from any peer that
can route an unsupported-protocol IPv4 packet into the NAT'd path.**
No memory corruption — the primitive is the panic itself, which is
sufficient for DoS.

## PoC changes
- `raw_trigger.c` and `trigger.c` written from scratch (the poc dir
  was empty when this verification started).

## Fix validation (Phase 8)
Built `fix.diff` (replaces `panic(...)` at lines 219 and 254 with
`goto oops;`, matching the existing inner-switch behavior at line
328). Built the single-module change in `/usr/src/sys/net/ipfw3_nat`
with `make`, installed the rebuilt `ipfw3_nat.ko` to `/boot/kernel/`,
rebooted.

- **Baseline (#0, unpatched module)**: PoC panics instantly:
  `panic: ipfw3: unsupported proto 132` at `ip_fw3_nat+0x7f`.
- **Patched (rebuilt module)**: same PoC returns cleanly. NAT rule
  counter still increments (proving the packet matched and went
  through check_nat), but instead of panicking the kernel returns
  IP_FW_DENY via `goto oops`, and sendto returns EACCES. Guest
  stays up.

The fix is consistent with the inner switch's existing handling
(`default: goto oops;` at line 328) — this just brings the two outer
switches in line with that.

## Kernel references
- sys/net/ipfw3_nat/ip_fw3_nat.c:219 — inbound panic site (confirmed)
- sys/net/ipfw3_nat/ip_fw3_nat.c:254 — outbound panic site (confirmed)
- sys/net/ipfw3_nat/ip_fw3_nat.c:328 — existing `goto oops` for same
  condition in the inner switch (precedent for the fix)
- sys/net/ipfw3_nat/ip_fw3_nat.c:430 — `oops:` label returns IP_FW_DENY
- sys/netinet/ip_output.c:529 — PFIL hook that drives the path
