macula_hole_punch (macula v0.20.5)

View Source

QUIC Hole Punch Executor with Cancellation and Adaptive Timing.

Implements the simultaneous open (SYN-SYN) pattern for QUIC to establish direct connections through NAT devices.

Features: - Proper cancellation of in-progress punch attempts - Adaptive timing based on NAT type and previous attempts - Tracks active punches via gen_server state - Supports both sync and async execution

QUIC Hole Punching Approach: Unlike TCP's explicit SYN packets, QUIC uses encrypted handshakes. The hole punching strategy is:

1. Both peers start QUIC connect() at the same coordinated time 2. Initial packets "punch" holes in both NATs 3. One peer's connection will succeed (race condition) 4. The other peer retries connecting through the opened hole

NAT Behavior Considerations: - EI mapping: External address is consistent - easy hole punch - HD mapping: Must target specific host - coordinate addresses - PP allocation: Same port - single target port - PC allocation: Sequential ports - try predicted range - RD allocation: Random ports - harder to predict, try range

Adaptive Timing: - Symmetric NAT: Longer timeouts, more port attempts - Restricted NAT: Standard timeouts - Full Cone: Fast timeouts, single port

Summary

Functions

Cancel an ongoing hole punch attempt.

Execute a hole punch attempt synchronously. Blocks until connection established or timeout.

Execute hole punch asynchronously. Returns immediately, caller receives result via message.

Get list of active punch attempts (for debugging/monitoring).

Start the hole punch executor.

Types

nat_type/0

-type nat_type() :: full_cone | restricted | port_restricted | symmetric | unknown.

punch_opts/0

-type punch_opts() ::
          #{target_host := binary() | string(),
            target_ports := [inet:port_number()],
            local_port => inet:port_number(),
            session_id := binary(),
            punch_time => integer(),
            role => initiator | target,
            local_nat_type => nat_type(),
            remote_nat_type => nat_type()}.

punch_result/0

-type punch_result() ::
          {ok, quicer:connection_handle()} |
          {error, timeout | unreachable | all_ports_failed | cancelled}.

Functions

cancel(Ref)

-spec cancel(reference()) -> ok | {error, not_found}.

Cancel an ongoing hole punch attempt.

execute(TargetNodeId, Opts, Timeout)

-spec execute(binary(), punch_opts(), timeout()) -> punch_result().

Execute a hole punch attempt synchronously. Blocks until connection established or timeout.

execute_async(TargetNodeId, Opts, ReplyTo)

-spec execute_async(binary(), punch_opts(), pid()) -> reference().

Execute hole punch asynchronously. Returns immediately, caller receives result via message.

get_active_punches()

-spec get_active_punches() -> [{reference(), map()}].

Get list of active punch attempts (for debugging/monitoring).

handle_call(Request, From, State)

handle_cast(Msg, State)

handle_info(Info, State)

init(_)

start_link()

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

Start the hole punch executor.

terminate(Reason, State)