natsense

package
v0.2.7 Latest Latest
Warning

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

Go to latest
Published: May 30, 2026 License: MPL-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package natsense implements observed address consensus (spec Phase 2a: ≥3 distinct reporters). For tests or small networks, set MinAgreeingPeers to 1.

Index

Constants

View Source
const (
	NATUnknown      uint8 = 0
	NATFullCone     uint8 = 1
	NATRestricted   uint8 = 2
	NATPortRestrict uint8 = 3
	NATSymmetric    uint8 = 4
)

NAT type constants (mirror protocol.NAT* for convenience).

Variables

This section is empty.

Functions

This section is empty.

Types

type Sense

type Sense struct {
	// contains filtered or unexported fields
}

Sense tracks distinct peers that reported the same reflected UDP endpoint (wire bytes) and infers NAT type from both passive mapping observation and active probe results.

v4 and v6 state are maintained independently. InferNATType covers v4 only; use InferV6Reach for v6 reachability classification.

func NewSense

func NewSense(minAgreeingPeers int) *Sense

NewSense returns a consensus tracker. If minAgreeingPeers <= 0, default 3 is used.

func (*Sense) ClearProbeResult added in v0.1.4

func (s *Sense) ClearProbeResult()

ClearProbeResult discards cached active-probe classification so the next probe result is used immediately after network changes. Deprecated: use ClearV4ProbeResult / ClearV6ProbeResult as needed.

func (*Sense) ClearV4ProbeResult added in v0.2.6

func (s *Sense) ClearV4ProbeResult()

ClearV4ProbeResult discards the cached v4 probe result.

func (*Sense) ClearV6ProbeResult added in v0.2.6

func (s *Sense) ClearV6ProbeResult()

ClearV6ProbeResult discards the cached v6 probe result.

func (*Sense) HasMultiPortEvidence added in v0.2.1

func (s *Sense) HasMultiPortEvidence() bool

HasMultiPortEvidence reports whether any single public IPv4 address has been observed with ≥2 distinct ports in the live (non-expired) vote window. Used to overrule probe-based FullCone classification when there is live evidence that a single NAT device is assigning different external ports per destination (Symmetric NAT behavior).

Ports are only compared within the same public IP: a multi-homed host whose two NICs have different public IPs will each show one stable port and must NOT be treated as Symmetric. v6 GUA ports are also excluded.

func (*Sense) InferNATType

func (s *Sense) InferNATType() uint8

InferNATType returns the best NAT classification for local operational use (punch strategy, session signalling). It is intentionally conservative: when the active probe has not yet run it returns NATRestricted so that punch and ICE paths are prepared even before confirmation.

Do NOT use this for publishing to the DHT — call PublishNatType() instead, which returns NATUnknown in the unconfirmed case rather than NATRestricted, so that connecting peers treat us optimistically until we have real evidence.

Decision flow:

  1. Public bind (QUIC socket on a WAN IPv4) → NATFullCone.
  2. Symmetric mapping: ≥2 well-supported ports under the same public IPv4 → NATSymmetric. Ports across different public IPs (multi-homed host) are NOT counted together; that would be a false positive.
  3. Insufficient passive evidence → NATUnknown.
  4. Active probe result (RecordV4ProbeResult): reachable=true → NATFullCone (any peer can initiate; Full Cone or cloud NAT) reachable=false → NATRestricted
  5. No probe result yet → NATRestricted (conservative default).

func (*Sense) InferV6Reach added in v0.2.6

func (s *Sense) InferV6Reach() V6Reach

InferV6Reach returns the current best classification of local IPv6 reachability. Decision flow:

  1. GUA bind confirmed → Direct (globally routable, no NAT).
  2. Active v6 probe result available → Direct or Firewalled.
  3. No information → Unknown.

func (*Sense) InvalidateObservations added in v0.1.4

func (s *Sense) InvalidateObservations()

InvalidateObservations clears passive observed_addr votes after confirmed network changes so old mappings do not bias the next consensus cycle.

func (*Sense) IsBindPublic deprecated added in v0.1.4

func (s *Sense) IsBindPublic() bool

Deprecated: use IsV4BindPublic.

func (*Sense) IsV4BindPublic added in v0.2.6

func (s *Sense) IsV4BindPublic() bool

IsV4BindPublic reports whether the local socket is bound to a public WAN IPv4 address.

func (*Sense) IsV6GUABind added in v0.2.6

func (s *Sense) IsV6GUABind() bool

IsV6GUABind reports whether a local GUA was detected.

func (*Sense) MinAgreeing

func (s *Sense) MinAgreeing() int

MinAgreeing returns the configured threshold.

func (*Sense) PublishNatType added in v0.2.6

func (s *Sense) PublishNatType() uint8

PublishNatType returns the NAT capability value to embed in a published DHT endpoint record. It differs from InferNATType in exactly one case:

  • When passive evidence is sufficient (stable single-port mapping) but the active probe has not yet completed, InferNATType returns NATRestricted as a conservative local hint; PublishNatType returns NATUnknown instead.

The rationale: a published NATRestricted causes connecting peers to skip v4 direct-dial and go straight to ICE, which is wasteful if we later confirm full-cone reachability. NATUnknown instructs peers to try direct optimistically while keeping ICE ready — the correct behaviour when we simply don't know yet.

All other cases (public bind, Symmetric, confirmed probe results, insufficient evidence) return the same value as InferNATType.

func (*Sense) Record

func (s *Sense) Record(reporter a2al.NodeID, observed []byte)

Record adds one vote: reporter saw our endpoint as observed (wire encoding).

func (*Sense) RecordBindPublic deprecated added in v0.1.4

func (s *Sense) RecordBindPublic(isPublic bool)

Deprecated: use RecordV4BindPublic.

func (*Sense) RecordProbeResult deprecated added in v0.1.4

func (s *Sense) RecordProbeResult(reachable bool)

Deprecated: use RecordV4ProbeResult.

func (*Sense) RecordV4BindPublic added in v0.2.6

func (s *Sense) RecordV4BindPublic(isPublic bool)

RecordV4BindPublic records whether the local UDP socket is bound to a public WAN IPv4 address. Called by host.RunNATProbe after inspecting the QUIC listen address.

func (*Sense) RecordV4ProbeResult added in v0.2.6

func (s *Sense) RecordV4ProbeResult(reachable bool)

RecordV4ProbeResult records the outcome of an active v4 reachability probe. reachable=true means ≥1 remote peer successfully echoed our claimed v4 address.

func (*Sense) RecordV6GUABind added in v0.2.6

func (s *Sense) RecordV6GUABind(hasGUA bool)

RecordV6GUABind records whether the local interface has an IPv6 Globally Unique Address (GUA). A GUA bind implies v6 direct reachability absent a stateful firewall. Called by host.RunNATProbe.

func (*Sense) RecordV6ProbeResult added in v0.2.6

func (s *Sense) RecordV6ProbeResult(reachable bool)

RecordV6ProbeResult records the outcome of an active v6 reachability probe. reachable=true means ≥1 remote peer echoed our v6 address (Direct); false means a stateful firewall is blocking unsolicited inbound v6.

func (*Sense) SetMinAgreeing

func (s *Sense) SetMinAgreeing(n int)

SetMinAgreeing updates the threshold (e.g. 1 for integration tests).

func (*Sense) TrustedUDP

func (s *Sense) TrustedUDP() (host string, port uint16, ok bool)

TrustedUDP returns host and port if some observed key has >= min distinct reporters whose votes are younger than voteTTL.

func (*Sense) TrustedUDPAll added in v0.2.6

func (s *Sense) TrustedUDPAll() []string

TrustedUDPAll returns all observed keys (host:port strings) that have reached the minimum-agreement threshold. Unlike TrustedUDP it does not stop at the first match, so callers receive both IPv4 and IPv6 consensus entries when a dual-stack node is observed from multiple reporters.

func (*Sense) TrustedWire

func (s *Sense) TrustedWire() ([]byte, bool)

TrustedWire returns the first trusted observed_addr wire form (6 or 18 bytes), for any address family. Deprecated: prefer TrustedWireV4 / TrustedWireV6 for family-specific probing.

func (*Sense) TrustedWireV4 added in v0.2.6

func (s *Sense) TrustedWireV4() ([]byte, bool)

TrustedWireV4 returns the wire-encoded trusted IPv4 external address, if any. Only v4 consensus entries are considered.

func (*Sense) TrustedWireV6 added in v0.2.6

func (s *Sense) TrustedWireV6() ([]byte, bool)

TrustedWireV6 returns the wire-encoded trusted IPv6 external address, if any. Only v6 consensus entries are considered.

type V6Reach added in v0.2.6

type V6Reach uint8

V6Reach classifies the local node's IPv6 reachability. The state space is simpler than v4 NAT types: v6 has globally routable addresses (Direct), is behind a stateful firewall (Firewalled), or we have not determined this yet (Unknown).

const (
	V6Unknown    V6Reach = 0
	V6Direct     V6Reach = 1 // GUA bind confirmed, or v6 probe echo received
	V6Firewalled V6Reach = 2 // v6 probe attempted but echo blocked
)

Jump to

Keyboard shortcuts

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