macula_nat_detector (macula v0.14.3)

View Source

NAT 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

observation/0

-type observation() ::
          #{observer_id := binary(),
            reflexive_address := {inet:ip_address(), inet:port_number()},
            local_address := {inet:ip_address(), inet:port_number()},
            observed_at := integer()}.

pending_probe/0

-type pending_probe() ::
          #{observer_endpoint := binary(), sent_at := integer(), local_port := inet:port_number()}.

Functions

add_observation(ObserverId, ReflexiveAddress)

-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.

detect()

-spec detect() -> ok.

Detect local NAT type (async, returns immediately). Results are cached and available via get_local_profile/0.

detect(ObserverEndpoint)

-spec detect(binary()) -> ok.

Detect NAT type using specific observer endpoint.

get_local_profile()

-spec get_local_profile() -> {ok, macula_nat_cache:nat_profile()} | not_detected.

Get the cached local NAT profile.

handle_call(Request, From, State)

handle_cast(Msg, State)

handle_info(Info, State)

handle_probe_reply(ReplyMsg)

-spec handle_probe_reply(map()) -> ok.

Handle NAT_PROBE_REPLY message from an observer. Called when we receive a reply containing our reflexive address.

init(Opts)

refresh()

-spec refresh() -> ok.

Trigger NAT type refresh (re-detection).

start_link(Opts)

-spec start_link(map()) -> {ok, pid()} | {error, term()}.

Start the NAT detector server.

terminate(Reason, State)