Skip to content

fix(orig_dest): pf_state_xport union fixes macOS DIOCNATLOOK struct size (rebased #2)#45

Merged
madeye merged 1 commit into
mainfrom
fix/macos-pfioc-natlook-struct-layout
Jun 30, 2026
Merged

fix(orig_dest): pf_state_xport union fixes macOS DIOCNATLOOK struct size (rebased #2)#45
madeye merged 1 commit into
mainfrom
fix/macos-pfioc-natlook-struct-layout

Conversation

@madeye

@madeye madeye commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Rebase of #2 onto current main (which has since gained IPv6 support), resolving the conflict and keeping @Abica's original analysis and tests. Credit to @Abica for the root-cause investigation.

The bug

struct pfioc_natlook uses union pf_state_xport for its four port fields, not bare u16. That union includes a u32 spi member, so it is 4 bytes, making the struct 84 bytes — not the 76 the old layout produced.

Because the DIOCNATLOOK ioctl number is computed at compile time from size_of::<PfiocNatlook>(), the wrong size produced ioctl 0xC04C4417 instead of 0xC0544417. The kernel rejects the unrecognized number with ENOTTY, so every NAT lookup silently fell through to the getsockname fallback heuristic.

This was masked in e2e tests because DIOCNATLOOK failure was logged only at debug and the loopback getsockname fallback happened to work. The old tail field order (direction, log) was also wrong — the kernel struct is proto_variant, direction.

Changes vs. #2

Same struct/union fix and the same compile-time + unit-test coverage (size_of, offset_of!, ioctl-number, round-trip, zero-init), re-applied on top of the IPv6-aware get_original_dest_pf. The DIOCNATLOOK failure log is also bumped debugwarn so future regressions are visible.

Verification

cargo fmt --check, cargo clippy --all-targets -- -D warnings, and cargo test all pass on macOS — the 7 new pf-layout tests included (160 passed, 0 failed).

Closes #2.

🤖 Generated with Claude Code

…uct size

`struct pfioc_natlook` uses `union pf_state_xport` for its four port
fields, not bare `u16`. The union carries a `u32 spi` member, so it is
4 bytes (not 2), making the struct 84 bytes instead of 76.

Because the DIOCNATLOOK ioctl number is computed at compile time from
`size_of::<PfiocNatlook>()`, the wrong size yielded ioctl 0xC04C4417
instead of 0xC0544417. The kernel rejected the unrecognized number with
ENOTTY, so every NAT lookup silently fell back to the getsockname
heuristic. The old tail field order (`direction, log`) was also wrong;
the kernel struct is `proto_variant, direction`.

Adds size_of, offset_of, ioctl-number, and round-trip unit tests so the
layout can't silently regress again.

Co-authored-by: Nicholas Wright <Abica>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@madeye
madeye force-pushed the fix/macos-pfioc-natlook-struct-layout branch from 818025d to 94aaaa2 Compare June 30, 2026 06:41
@madeye
madeye merged commit 67a1f5a into main Jun 30, 2026
6 checks passed
@madeye
madeye deleted the fix/macos-pfioc-natlook-struct-layout branch June 30, 2026 06:45
@Abica

Abica commented Jun 30, 2026

Copy link
Copy Markdown

Glad it was useful! Thanks for carrying it across the line and keeping the test suite intact.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants