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

> Explore the MasterDnsVPN implementation of Automatic Repeat Request ARQ. Discover how its custom ARQ layer ensures reliable, ordered data delivery with adaptive RTO calculation and sliding window flow control.

- Repository: [Amin Mahmoudi/MasterDnsVPN](https://github.com/masterking32/MasterDnsVPN)
- Tags: deep-dive
- Published: 2026-05-10

---

**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`](https://github.com/masterking32/MasterDnsVPN/blob/main/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`](https://github.com/masterking32/MasterDnsVPN/blob/main/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`](https://github.com/masterking32/MasterDnsVPN/blob/main/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`](https://github.com/masterking32/MasterDnsVPN/blob/main/internal/udpserver/stream_server.go). The server implements the `PacketEnqueuer` interface to bridge ARQ packets to the DNS/UDP transport:

```go
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`](https://github.com/masterking32/MasterDnsVPN/blob/main/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.