feat(probe): RFC 5780 §4.3 hairpinning detection#23
Merged
Conversation
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.
Contributor
Author
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the v0.1.4 hairpinning feature per
docs/design.md:413-428. Newinternal/probe/hairpin.goallocates two unconnected UDP sockets, STUN-probes each in parallel against the first--serverentry, 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 injectablehairpinSender/hairpinReceivertypes 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.Classifysignature gains*probe.HairpinningResult; nil result emitshairpin_untestedwarning. Forecast unchanged in v0.1.4 — hairpinning only matters for same-NAT peers, which isn't natcheck's audience question. Per huddle decision recorded intodos/releases/v0.1.4/00-master-tracker.md.docs/design.md:415updated 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.