Module l4

Module l4 

Source
Expand description

L4 TCP/UDP proxying with SNI routing reuse (DW-103, DP-01 / #234: UDP datagram forwarding).

L4 proxying is a LISTENER TYPE, not a route action: a protocol: tcp (or protocol: udp) listener accepts raw L4 connections and splices them byte-for-byte to an upstream endpoint, never running the HTTP pipeline. This is the same byte-splice model the DW-008 TLS passthrough path uses – and when sni_routing is true, the TCP dispatcher reuses the EXACT SNI extraction from passthrough (security::tls::sni_from_client_hello + the peek loop) to select the upstream from the listener’s tls.sni_routes.

§TCP dispatcher

L4Dispatcher (built from L4ProxyConfig) accepts a TCP connection and:

  1. If sni_routing is true, peeks the TLS ClientHello SNI (reusing security::tls::handle_passthrough’s peek + sni_from_client_hello extraction – the same bounded reassembly, the same 64 KiB budget, the same 10s peek timeout). The SNI is matched against the listener’s tls.sni_routes to select the upstream; the configured upstream is the fallback for no-SNI / unmatched names (absent = close).
  2. If sni_routing is false, the configured upstream receives every connection.
  3. The selected upstream’s endpoint is picked through the CURRENT generation’s balancers (no hash key – a byte splice has no client-IP semantics), so L4 picks follow config reloads.
  4. The client and upstream connections are spliced with tokio::io::copy_bidirectional until either side closes (the same tunnel the passthrough path and the 101 upgrade path use). An optional idle timeout closes the splice when neither side sends data for the configured duration.

Peeking (never reading) keeps the ClientHello bytes available for the upstream once splicing starts: the entire hello is still in the socket buffer and is replayed to the upstream by the splice.

§UDP dispatcher (DP-01, #234)

UdpDispatcher implements UDP datagram forwarding with per-client session tracking. Each unique client SocketAddr gets a dedicated upstream UdpSocket; datagrams from the client are forwarded to the upstream, and responses from the upstream are forwarded back to the client. Sessions expire after a configurable idle timeout (default 30s) to avoid leaking NAT state. The dispatcher uses a bounded session table (max_sessions, default 4096) to prevent resource exhaustion under adversarial traffic.

§Availability

The entire module is compiled into the OSS build. The config schema (ListenerProtocol::Tcp/Udp + L4Config) is always present so configs round-trip; L4 proxying is always available.

Structs§

L4Dispatcher
TCP L4 dispatcher: accept one TCP connection, optionally peek SNI, select the upstream, and splice byte-for-byte.
L4ProxyConfig
Compiled L4 proxying configuration (from L4Config).
UdpDispatcher
UDP L4 dispatcher (DP-01, #234): per-client session tracking with datagram forwarding and idle-session eviction.

Enums§

L4DispatchAction
The outcome of an L4 TCP dispatch: which upstream endpoint the connection was spliced to (for metrics/logging), or that it was closed (no upstream, no endpoint, or SNI routing with no match).
L4Error
L4 proxying error. Kept simple: the dispatcher logs and closes on any error – there is no client-facing HTTP envelope at L4.