Automatic Repeat Request (ARQ) Implementation in MasterDnsVPN: Technical Deep Dive

MasterDnsVPN implements a custom ARQ layer over DNS/UDP that provides reliable, ordered data delivery through adaptive RTO calculation, selective NACKs, sliding window flow control, and three concurrent processing loops.

MasterDnsVPN uses a sophisticated Automatic Repeat Request (ARQ) mechanism to transform unreliable DNS/UDP transport into a robust streaming protocol. The implementation resides primarily in internal/arq/arq.go and draws architectural inspiration from QUIC, featuring packet-numbered sequencing, explicit ACK/NACK handling, and Jacobson/Karels adaptive timeout calculation. This analysis examines how the masterking32/MasterDnsVPN repository implements reliable transport atop latency-heavy DNS infrastructure.

ARQ Architecture and Core Components

The ARQ subsystem centers around the *ARQ type, instantiated via arq.NewARQ for each VPN stream. Three background goroutines manage concurrent operations under a shared sync.RWMutex:

  • ioLoop: Reads from local TCP/Unix sockets, buffers application data, and enqueues outbound STREAM_DATA packets.
  • writeLoop: Consumes inbound packets from the network, reassembles them in sequence, and writes the reconstructed payload to the local socket.
  • retransmitLoop: Monitors send buffers for expired RTO timers and schedules retransmissions, including NACK-triggered events.

These loops coordinate through synchronized access to send/receive windows, sequence numbers, and connection state flags.

Key Data Structures and Buffers

The implementation maintains separate buffers for data and control plane traffic to ensure critical management packets receive differentiated treatment.

Send Buffers (sndBuf and controlSndBuf)

Located in internal/arq/arq.go (lines 31-41), these buffers store outbound packets awaiting acknowledgment. Each entry tracks creation timestamp, last-sent time, retry count, current RTO value, and eligibility for RTT sampling. The separation between sndBuf (data) and controlSndBuf (management) allows independent RTO calculation and retry policies.

Receive Buffers (rcvBuf)

Indexed by packet number (lines 35-38), rcvBuf holds out-of-order data until the next expected sequence (rcvNxt) arrives. This buffering strategy enables ordered delivery to the application despite UDP's inherent packet reordering.

Configuration (Config)

The Config struct holds tunable parameters including WindowSize, RTO values, DataNackMaxGap, and TTLs. These values populate from the VPN session policy parsed in internal/vpnproto/session_accept.go, allowing per-session customization of ARQ behavior.

Adaptive Retransmission and Flow Control

The ARQ layer implements sophisticated mechanisms to handle packet loss and congestion without manual intervention.

Jacobson/Karels RTO Algorithm

The updateAdaptiveRTO function (lines 26-41) adjusts the retransmission timeout dynamically based on observed RTT samples. This classic algorithm smooths Round-Trip Time variations to prevent premature retransmission while reacting quickly to actual congestion. Separate RTO trackers exist for data and control planes, allowing differentiated treatment of user traffic versus connection management.

Selective NACK Generation

When maybeSendDataNacks detects sequence gaps larger than DataNackMaxGap (lines 122-173), the receiver transmits selective negative acknowledgments after DataNackInitialDelay, repeating at DataNackRepeatInterval until the missing packets arrive. This mechanism avoids the latency penalty of waiting for full RTO expiration when the receiver knows exactly which packets are missing.

Sliding Window Flow Control

The sender respects a windowSize limit (lines 84-92) and monitors windowNotFull signals to apply back-pressure. When buffers grow beyond configured limits, the writer pauses to prevent memory exhaustion, creating explicit flow control between the ARQ layer and local application.

Packet Lifecycle: From Transmission to ACK

The ARQ state machine processes every packet through distinct phases:

  1. Packetization: ioLoop reads from the local connection, assigns sequence number sndNxt, stores the payload in sndBuf with currentDataBaseRTO, and emits via PacketEnqueuer.PushTXPacket.

  2. Reception: ReceiveData validates packet numbers against the receive window, buffers in rcvBuf, and transmits immediate STREAM_DATA_ACK responses.

  3. ACK Processing: ReceiveAck removes entries from sndBuf, updates RTT statistics via noteSuccessfulDataSample, and signals windowNotFull to resume writing.

  4. Timeout Detection: checkRetransmits (lines 2330-2365) scans buffers for expired RTOs. For each expired packet, it increments retry counters, inflates RTO by dataRetransmitRTOGrowthFactor or controlRetransmitRTOGrowthFactor, and re-queues packets via enqueuer.PushTXPacket.

  5. NACK Handling: HandleDataNack retransmits specific missing packets once without RTO inflation, providing faster recovery than timeout-based retransmission alone.

Graceful Stream Shutdown

The ARQ layer tracks close-read/write states through MarkCloseReadSent, MarkCloseWriteSent, and MarkRstSent methods (lines 511-560). It acknowledges termination packets and enters a draining phase, flushing pending data before emitting final close packets. If the TerminalDrainTimeout expires before acknowledgment, the system falls back to RST transmission to prevent resource leakage.

Integration with the UDP Server Layer

Server-side streams instantiate ARQ through the pattern shown in internal/udpserver/stream_server.go. The server implements the PacketEnqueuer interface to bridge ARQ packets to the DNS/UDP transport:

func (s *Stream_server) initARQ(streamID uint16, sessionID uint8, conn io.ReadWriteCloser) {
    cfg := arq.Config{
        WindowSize:                  s.arqPolicy.MaxARQWindowSize,
        RTO:                         s.arqPolicy.MinARQInitialRTOSeconds,
        MaxRTO:                      s.arqPolicy.ARQ_MAX_RTO_SECONDS,
        EnableControlReliability:    s.arqPolicy.EnableControlReliability,
        ControlRTO:                  s.arqPolicy.ARQ_CONTROL_INITIAL_RTO_SECONDS,
        ControlMaxRTO:               s.arqPolicy.ARQ_CONTROL_MAX_RTO_SECONDS,
        ControlMaxRetries:           s.arqPolicy.ARQ_MAX_CONTROL_RETRIES,
        InactivityTimeout:           s.arqPolicy.ARQ_INACTIVITY_TIMEOUT_SECONDS,
        DataPacketTTL:               s.arqPolicy.ARQ_DATA_PACKET_TTL_SECONDS,
        MaxDataRetries:              s.arqPolicy.ARQ_MAX_DATA_RETRIES,
        ControlPacketTTL:            s.arqPolicy.ARQ_CONTROL_PACKET_TTL_SECONDS,
        DataNackMaxGap:              s.arqPolicy.ARQ_DATA_NACK_MAX_GAP,
        DataNackInitialDelaySeconds: s.arqPolicy.ARQ_DATA_NACK_INITIAL_DELAY_SECONDS,
        DataNackRepeatSeconds:       s.arqPolicy.ARQ_DATA_NACK_REPEAT_SECONDS,
        TerminalDrainTimeout:        s.arqPolicy.ARQ_TERMINAL_DRAIN_TIMEOUT_SECONDS,
        TerminalAckWaitTimeout:      s.arqPolicy.ARQ_TERMINAL_ACK_WAIT_TIMEOUT_SECONDS,
        CompressionType:             s.compression,
        IsClient:                    false,
    }

    s.ARQ = arq.NewARQ(streamID, sessionID, s, conn, s.mtu, s.log, cfg)
    s.ARQ.Start()
}

Application layers write to the conn provided to NewARQ, while the ARQ layer handles packetization, retransmission, and ordering transparently.

Summary

  • MasterDnsVPN implements Automatic Repeat Request (ARQ) over DNS/UDP through a custom protocol layer inspired by QUIC, located in internal/arq/arq.go.
  • Three concurrent loops (ioLoop, writeLoop, retransmitLoop) handle transmission, reception, and recovery under mutex protection.
  • Adaptive RTO calculation uses the Jacobson/Karels algorithm (lines 26-41) to optimize retransmission timing based on network conditions.
  • Selective NACKs enable rapid recovery from packet loss via maybeSendDataNacks without waiting for full timeouts.
  • Flow control via sliding windows prevents buffer overflow and manages back-pressure through windowSize limits.
  • Graceful shutdown tracks close states and drains pending data before connection termination, falling back to RST if timeouts expire.

Frequently Asked Questions

How does MasterDnsVPN's ARQ differ from standard TCP retransmission?

MasterDnsVPN's ARQ operates over DNS/UDP rather than raw IP, implementing packet-numbered sequencing and selective NACKs similar to QUIC rather than TCP's cumulative acknowledgment model. It maintains separate RTO calculations for data and control planes in updateAdaptiveRTO, allowing independent timeout adjustment for user traffic versus connection management packets.

What triggers a retransmission in the ARQ layer?

Retransmissions occur through two mechanisms: timeout-based via checkRetransmits (lines 2330-2365) when CurrentRTO expires without acknowledgment, and NACK-based via HandleDataNack when the receiver explicitly requests missing packets. Timeout-based retries inflate the RTO using dataRetransmitRTOGrowthFactor, while NACK retransmissions preserve the current timeout value for faster recovery.

How does the ARQ implementation handle packet ordering?

Out-of-order packets are stored in rcvBuf indexed by packet number until the expected sequence rcvNxt arrives. The writeLoop reassembles contiguous sequences before delivering them to the local socket, ensuring ordered delivery despite UDP's inherent unordered nature and potential packet loss.

What happens when the retry limit is exceeded?

When MaxDataRetries or ControlMaxRetries is exceeded as tracked in checkRetransmits, the ARQ layer terminates the stream by sending an RST packet via MarkRstSent and transitions to StateClosed. This prevents infinite retransmission attempts and forces connection teardown when the network path appears permanently failed.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →