classify

package
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 11, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package classify turns probe results into a NAT-type verdict.

Classification follows RFC 5780 mapping-behavior categories (Endpoint-Independent, Address-Dependent, Address and Port-Dependent) and emits legacy RFC 3489 terms ("cone", "symmetric") for human readers. Optional filtering data (RFC 5780 §4.4) refines the WebRTC forecast when the target server supports CHANGE-REQUEST. See docs/design.md.

Index

Constants

View Source
const (
	WarnAllProbesFailed                 = "all_probes_failed"
	WarnADMOrStricter                   = "adm_or_stricter"
	WarnCGNATDetected                   = "cgnat_detected"
	WarnInsufficientProbes              = "insufficient_probes"
	WarnFilteringBehaviorNotTested      = "filtering_behavior_not_tested"
	WarnFilteringSkippedNoChangeRequest = "filtering_skipped_no_change_request"
	WarnMixedAddressFamilyProbes        = "mixed_address_family_probes"
	WarnHairpinUntested                 = "hairpin_untested"
)

Stable warning vocabulary for the JSON API.

Variables

This section is empty.

Functions

This section is empty.

Types

type FilteringBehavior added in v0.1.2

type FilteringBehavior int

FilteringBehavior is the RFC 5780 §4.4 filtering category.

const (
	// FilteringUntested is the zero value: no §4.4 sequence ran (server didn't
	// support OTHER-ADDRESS, or filtering wasn't attempted).
	FilteringUntested FilteringBehavior = iota
	// FilteringEndpointIndependent: replies arrive from any source the server
	// is asked to use (Test 2 + Test 3 both received).
	FilteringEndpointIndependent
	// FilteringAddressDependent: replies arrive when source IP matches a peer
	// the client has communicated with (Test 2 dropped, Test 3 received).
	FilteringAddressDependent
	// FilteringAddressAndPortDependent: replies arrive only when both source
	// IP and port match (Test 2 + Test 3 both dropped).
	FilteringAddressAndPortDependent
)

func (FilteringBehavior) String added in v0.1.2

func (b FilteringBehavior) String() string

String returns the canonical wire name (matches the JSON enum).

type Forecast

type Forecast struct {
	DirectP2P    string // "likely" | "possible" | "unlikely" | "unknown"
	TURNRequired bool
}

Forecast is the WebRTC direct-P2P prediction.

type NATType

type NATType int

NATType is the RFC 5780 mapping-behavior category.

const (
	// Unknown indicates classification could not be determined from the
	// available probes (typically only one successful probe — no comparison
	// point for mapping behavior).
	Unknown NATType = iota

	// EndpointIndependentMapping: same mapped endpoint across servers.
	// RFC 5780. Legacy term "cone".
	EndpointIndependentMapping

	// AddressDependentMapping: mapped endpoint varies by destination address.
	// RFC 5780. v0.1 reports this for any case where mapped endpoints differ
	// across servers; ADM vs APDM cannot be distinguished without
	// CHANGE-REQUEST.
	AddressDependentMapping

	// AddressPortDependentMapping: mapped endpoint varies by destination
	// address and port. RFC 5780. Legacy term "symmetric". v0.1 does not
	// emit this category directly (see AddressDependentMapping note).
	AddressPortDependentMapping

	// Blocked: no probe succeeded. Network rejects outbound STUN, all target
	// servers are unreachable, or the caller's timeout fired too early.
	Blocked
)

func (NATType) String

func (t NATType) String() string

String returns the canonical RFC 5780 name.

type Verdict

type Verdict struct {
	Type           NATType
	LegacyName     string // "cone", "symmetric", "" when unknown/blocked
	PublicEndpoint netip.AddrPort
	CGNAT          bool
	// Filtering is the RFC 5780 §4.4 outcome; FilteringUntested when the
	// §4.4 sequence did not run (no OTHER-ADDRESS support, or not attempted).
	Filtering FilteringBehavior
	// FilteringTestedAgainst is the server the §4.4 sequence ran against.
	// Zero-value (Server{}) when Filtering == FilteringUntested, which
	// happens in any of these cases:
	//   - no FilteringResult was supplied (filtering not attempted);
	//   - the server did not advertise OTHER-ADDRESS (ErrFilteringNotSupported);
	//   - the initial Test 1 binding probe failed (ErrTest1Failed);
	//   - the (T2=true, T3=false) RFC-impossible state was observed.
	FilteringTestedAgainst probe.Server
	// Hairpinning is tri-state per docs/design.md:361:
	//   - nil:    not tested (probe didn't run / socket setup failed)
	//   - &true:  tagged loopback packet arrived
	//   - &false: listen window elapsed without the packet (may be a true
	//             negative OR a per-NAT filtering false-negative on the
	//             hairpin path; see docs/design.md:428)
	// Carried through Classify unchanged in v0.1.4; forecast logic does NOT
	// shift on this value yet. Hairpinning only matters for same-NAT (LAN)
	// peers, while the natcheck user's question is about inter-NAT WebRTC.
	// Future releases may revisit if real-world data justifies a shift.
	Hairpinning *bool
	Warnings    []string
	Forecast    Forecast
}

Verdict is the final classification output.

func Classify

func Classify(results []probe.Result, filtering *probe.FilteringResult, hairpinning *probe.HairpinningResult) Verdict

Classify turns probe results into a Verdict. Pure function: no I/O, no goroutines, deterministic for a given input.

A probe.Result is treated as successful only when Err == nil AND Mapped.IsValid(). This guards against buggy Prober implementations that might report nil-error with a zero mapped endpoint.

filtering may be nil; in that case Verdict.Filtering stays Untested and the existing WarnFilteringBehaviorNotTested warning is emitted. When filtering is non-nil, its Test2/Test3 booleans drive the FilteringBehavior and the WebRTC forecast for EIM mappings (RFC 5780 §4.4 outcomes).

hairpinning may be nil; in that case Verdict.Hairpinning stays nil and WarnHairpinUntested is emitted. When hairpinning is non-nil, the value is carried through to Verdict.Hairpinning unchanged. v0.1.4 does NOT shift the forecast based on this value — hairpinning only matters for same-NAT (LAN) peers, which is not the natcheck user's question. See the Verdict field doc and docs/design.md:478.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL