Skip to content

feat(probe): RFC 5780 §4.3 hairpinning detection#23

Merged
vnykmshr merged 2 commits into
mainfrom
v0.1.4-phase4-hairpinning
May 11, 2026
Merged

feat(probe): RFC 5780 §4.3 hairpinning detection#23
vnykmshr merged 2 commits into
mainfrom
v0.1.4-phase4-hairpinning

Conversation

@vnykmshr

Copy link
Copy Markdown
Contributor

Implements the v0.1.4 hairpinning feature per docs/design.md:413-428. New internal/probe/hairpin.go allocates two unconnected UDP sockets, STUN-probes each in parallel against the first --server entry, then sends a tagged loopback packet from A to mapped-B; listen window default 1s, parallel cost ≤200ms in the common case. The send/listen step is abstracted via injectable hairpinSender/hairpinReceiver types so the port-restricted-filter false-negative case (spec-named at design.md:428) can be tested without a real NAT.

JSON gains "hairpinning": true | false | null (additive, null = not tested). classify.Classify signature gains *probe.HairpinningResult; nil result emits hairpin_untested warning. Forecast unchanged in v0.1.4 — hairpinning only matters for same-NAT peers, which isn't natcheck's audience question. Per huddle decision recorded in todos/releases/v0.1.4/00-master-tracker.md.

docs/design.md:415 updated to drop the aspirational "no wall-clock cost" claim. CHANGELOG entry under [Unreleased].

Verified: go test -race ./..., golangci-lint run ./..., gofmt -l ., go vet ./... all clean.

vnykmshr added 2 commits May 11, 2026 14:52
Two dedicated unconnected UDP sockets, STUN-probed in parallel against the
first --server entry, then a tagged loopback packet from A to mapped-B.
JSON gains "hairpinning": true | false | null per docs/design.md:361.
classify.Classify signature gains *probe.HairpinningResult; nil → emits
hairpin_untested warning. Forecast unchanged in v0.1.4. False-negative test
case (port-restricted filter on hairpin path) uses an injectable send/recv
oracle so no real NAT is required in localhost tests.
Mirrors stun.go:43-55 so a cancel-without-deadline context unblocks in-flight
reads/writes promptly instead of waiting for the full T_hairpin window.
@vnykmshr

Copy link
Copy Markdown
Contributor Author

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@vnykmshr vnykmshr merged commit 36a687f into main May 11, 2026
7 checks passed
@vnykmshr vnykmshr deleted the v0.1.4-phase4-hairpinning branch May 11, 2026 09:14
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.

1 participant