macula_nat_detector (macula v0.14.3)
View SourceNAT Type Detector using NATCracker Methodology.
Detects the local peer's NAT characteristics by probing external observers (gateways/peers with public IPs) and analyzing reflexive addresses. Classification follows NATCracker's 27 NAT type model.
NAT Policy Detection: 1. Mapping Policy (m): How NAT maps internal to external addresses - EI (Endpoint-Independent): Same external addr for all destinations - HD (Host-Dependent): Different external addr per destination host - PD (Port-Dependent): Different external addr per destination host:port
2. Filtering Policy (f): What incoming packets NAT accepts - EI: Accepts from any source - HD: Accepts from hosts we've contacted - PD: Accepts from host:port we've contacted
3. Allocation Policy (a): How NAT chooses external ports - PP (Port-Preservation): external_port = local_port - PC (Port-Contiguity): external_port = last_port + delta - RD (Random): No predictable pattern
Detection Algorithm (Fast - 200-400ms): 1. Send NAT_PROBE to primary observer (100ms RTT) 2. Send NAT_PROBE to secondary observer (parallel, 100ms RTT) 3. Compare reflexive addresses to classify NAT type
Most Common NAT Types (per NATCracker): - (EI, PD, PP): 37% of consumer NATs - (EI, EI, PP): 15% (Full Cone) - (PD, PD, RD): 12% (Symmetric)
Summary
Functions
Add an observation from an external observer. Called when we receive a NAT_PROBE_REPLY with our reflexive address.
Detect local NAT type (async, returns immediately). Results are cached and available via get_local_profile/0.
Detect NAT type using specific observer endpoint.
Get the cached local NAT profile.
Handle NAT_PROBE_REPLY message from an observer. Called when we receive a reply containing our reflexive address.
Trigger NAT type refresh (re-detection).
Start the NAT detector server.
Types
-type observation() :: #{observer_id := binary(), reflexive_address := {inet:ip_address(), inet:port_number()}, local_address := {inet:ip_address(), inet:port_number()}, observed_at := integer()}.
-type pending_probe() :: #{observer_endpoint := binary(), sent_at := integer(), local_port := inet:port_number()}.
Functions
-spec add_observation(binary(), {inet:ip_address(), inet:port_number()}) -> ok.
Add an observation from an external observer. Called when we receive a NAT_PROBE_REPLY with our reflexive address.
-spec detect() -> ok.
Detect local NAT type (async, returns immediately). Results are cached and available via get_local_profile/0.
-spec detect(binary()) -> ok.
Detect NAT type using specific observer endpoint.
-spec get_local_profile() -> {ok, macula_nat_cache:nat_profile()} | not_detected.
Get the cached local NAT profile.
-spec handle_probe_reply(map()) -> ok.
Handle NAT_PROBE_REPLY message from an observer. Called when we receive a reply containing our reflexive address.
-spec refresh() -> ok.
Trigger NAT type refresh (re-detection).
Start the NAT detector server.