Skip to content

Add UDP/datagram transport: wire codec, replay window, reassembler, endpoint routing#38

Merged
pzverkov merged 5 commits into
mainfrom
feat/udp-datagram-transport
May 30, 2026
Merged

Add UDP/datagram transport: wire codec, replay window, reassembler, endpoint routing#38
pzverkov merged 5 commits into
mainfrom
feat/udp-datagram-transport

Conversation

@pzverkov
Copy link
Copy Markdown
Member

Draft. First slice of the UDP/datagram transport. This PR lands the wire format, the anti-replay window, the handshake reassembler, and the connectionless endpoint with its connection-index routing. The reliable handshake, the encrypted data path, the performance pass, and the DoS/amplification hardening land in follow-up PRs; seams and TODOs in the code mark where they plug in.

The datagram transport is a separate, connectionless transport from the TCP/stream Transport. It does not reuse the stream framing: length-prefixed io.ReadFull does not survive datagram loss/reorder, and the ~1.6 KB post-quantum Hellos exceed a 1500 B MTU. The crypto core (chkem, crypto, and Session secret derivation) is reused unchanged. There is no TCP/UDP interop, by design.

What this PR contains:

  • pkg/protocol/datagram_codec.go: self-contained per-datagram wire format. A 14 B common header [type|epoch|recvIndex|seq] plus a handshake fragment extension. No cross-datagram length prefix and no transmitted nonce (the nonce is derived as sessionPrefix || seq); the full header is the AEAD AAD, so the epoch and connection index are authenticated. A cookie/retry field is reserved so the later anti-DoS work needs no wire change.
  • pkg/tunnel/replay.go: a multi-word sliding-bitmap anti-replay window (1024 bits) over the monotonic sequence, never reset across rekey.
  • pkg/tunnel/reassembly.go: a bounded, handshake-only fragment reassembler (per-source cap, per-message size cap, timeout, and a global source cap).
  • pkg/tunnel/datagram.go: the endpoint shell and a connection-index registry. CSPRNG-random indices demuxed by index rather than source address, so a session survives NAT rebind/roaming, one address can host many sessions, and the index space resists off-path guessing (regenerated on collision). Plus per-source half-open accounting and the frame-routing seam.
  • internal/constants/constants.go: datagram parameters (1200 B MTU, payload cap, nonce prefix, reassembly/handshake/idle/half-open bounds).
  • docs/: design notes.

Security review (pre-merge): parsing, the replay window, the connection-index registry, and the nonce/AAD design were reviewed in depth and are clean (bounds-checked parsing, a correct sliding bitmap, CSPRNG indices allocated under a lock). One finding is fixed here: the reassembler now has a global cap across all sources, because UDP source addresses are spoofable and a per-source cap alone did not bound total memory. Two enforcement items are recorded as TODOs at their seams for the handshake work: gating new half-open sessions through the per-source admission check, and authenticating a CLOSE frame before teardown. Neither is reachable yet (there is no live receive loop).

Planned follow-ups (separate PRs):

  • Reliable bilateral handshake over datagrams: retransmission with backoff, fragmentation, duplicate detection, and a responder flight cache that replays its ServerHello verbatim instead of re-running the randomized encapsulation.
  • Epoch-keyed cipher selection on Session and the encrypted data send/receive path.
  • A zero-allocation, batched-I/O performance pass once a correctness baseline exists.
  • Stateless cookie / anti-amplification hardening (using the reserved cookie field) and authenticated source-address rebinding for roaming, with a threat-model writeup and fuzzing.

Verification: go build ./..., go vet, and gofmt are clean; the full module is green under -race; the codec, replay window, reassembler (including the global cap), and connection-index registry are covered by unit tests.

@pzverkov pzverkov force-pushed the feat/udp-datagram-transport branch from dc11326 to e25d9c3 Compare May 30, 2026 18:35
A separate connectionless datagram transport from the TCP/stream Transport, with its own self-contained wire format: no cross-datagram length prefix and no transmitted nonce (the nonce derives from a per-session prefix and the sequence number), and the full frame header is the AEAD AAD so the AEAD authenticates the epoch and connection index.

Adds the per-datagram codec and handshake fragment format (pkg/protocol/datagram_codec.go), a multi-word sliding-bitmap anti-replay window that survives rekeys without reset (pkg/tunnel/replay.go), a bounded handshake-only reassembler with per-source, size, timeout, and global source caps (pkg/tunnel/reassembly.go), and the endpoint with a CSPRNG connection-index registry that demuxes by index rather than source address plus per-source half-open accounting (pkg/tunnel/datagram.go). Datagram parameters live in internal/constants and design notes in docs.

Reuses the crypto core (chkem, crypto, and Session secret derivation) unchanged. The reliable handshake, the encrypted data path, the performance pass, and the anti-DoS hardening follow in separate changes. Build, vet, gofmt, and golangci-lint are clean; the module is green under -race; unit tests cover the codec, replay window, reassembler, connection-index registry, and frame routing.
@pzverkov pzverkov force-pushed the feat/udp-datagram-transport branch from e25d9c3 to b5c34e8 Compare May 30, 2026 19:30
pzverkov added 4 commits May 30, 2026 22:39
The older pinned Codacy CLI crashed internally during SARIF generation (a Scala exception, exit 1), failing the Codacy job on every run even though the workflow sets max-allowed-issues to its maximum to keep findings non-blocking. v4.4.7 is the current release; this re-pins to its commit SHA. No change to our code.
@pzverkov pzverkov marked this pull request as ready for review May 30, 2026 22:44
@pzverkov pzverkov self-assigned this May 30, 2026
@pzverkov pzverkov merged commit 8811f13 into main May 30, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant