QUIC Transport
QUIC-based transport for modern, secure, NAT-friendly communication with built-in TLS 1.3 and connection migration.
QUIC transport is experimental and requires the quic feature flag.
The API may change in future releases. It is HDDS-to-HDDS only (not standardized for DDS interop).
Overview
QUIC provides a modern alternative to TCP for DDS communication. Built on UDP with mandatory TLS 1.3, it offers connection migration, multiplexed streams, and 0-RTT reconnection -- features particularly useful for mobile/IoT devices and cloud deployments.
When to use QUIC:
- Mobile/roaming devices (IP changes frequently)
- Cloud deployments behind NAT
- IoT devices on cellular networks
- Cross-datacenter communication
- Firewall-restricted environments
When NOT to use QUIC:
- Multicast discovery (use UDP)
- Cross-vendor DDS interop (QUIC not standardized for DDS)
- Ultra-low latency requirements (< 100 us)
Feature Flag
QUIC transport requires the quic feature:
[dependencies]
hdds = { version = "0.1", features = ["quic"] }
Without this feature, QUIC types are not available at compile time.
Configuration
QuicConfig
use hdds::transport::quic::QuicConfig;
use std::time::Duration;
let config = QuicConfig::builder()
.bind_addr("0.0.0.0:7400".parse().unwrap())
.server_name("my-dds-cluster.local")
.enable_0rtt(true)
.idle_timeout(Duration::from_secs(30))
.max_concurrent_streams(100)
.enable_migration(true)
.build();
Configuration Options
| Option | Default | Description |
|---|---|---|
bind_addr | 0.0.0.0:0 | Local bind address (0 = auto-assign port) |
server_name | "hdds.local" | TLS SNI server name |
enable_0rtt | true | Fast reconnection to known peers |
keep_alive_interval | 15s | NAT binding maintenance interval |
idle_timeout | 30s | Connection idle timeout |
max_concurrent_streams | 100 | Streams per connection |
max_recv_window | 1 MB | Maximum receive window size |
enable_migration | true | Allow connection migration on IP change |
dangerous_skip_verify | false | Skip TLS certificate verification |
Reconnection Settings
QUIC supports automatic reconnection with exponential backoff.
| Option | Default | Description |
|---|---|---|
reconnect_enabled | true | Auto-reconnect on connection drop |
reconnect_max_attempts | None (infinite) | Max retry attempts |
reconnect_base_delay | 1s | Initial backoff delay |
reconnect_max_delay | 60s | Maximum backoff delay cap |
use hdds::transport::quic::QuicConfig;
use std::time::Duration;
let config = QuicConfig::builder()
.reconnect_enabled(true)
.reconnect_max_attempts(Some(10))
.reconnect_base_delay(Duration::from_millis(500))
.reconnect_max_delay(Duration::from_secs(30))
.build();
TLS Certificates
By default, QUIC generates self-signed certificates automatically. For production deployments, provide custom certificates.
use hdds::transport::quic::QuicConfig;
let config = QuicConfig::builder()
.certificate(include_str!("cert.pem"))
.private_key(include_str!("key.pem"))
.build();
dangerous_skip_verify() disables all TLS certificate verification. Never use in production.
Connection Migration
QUIC automatically handles IP address changes without dropping the connection. This is transparent to the application.
Device moves from WiFi to cellular:
IP changes from 192.168.1.50 to 10.0.0.50
QUIC connection continues without interruption
Connection migration is enabled by default (enable_migration: true).
0-RTT Reconnection
When enable_0rtt is enabled, QUIC caches session tickets for known peers. Subsequent connections complete instantly (0-RTT) instead of requiring a full handshake.
0-RTT data can be replayed by attackers. HDDS only uses 0-RTT for idempotent discovery messages, not user data.
Wire Format
QUIC uses the same length-prefixed wire format as TCP:
+----------------+-------------------+
| Length (4B BE) | RTPS Message |
+----------------+-------------------+
Maximum message size: 16 MB (MAX_QUIC_MESSAGE_SIZE).
Connection States
QUIC connections go through the following states:
| State | Description |
|---|---|
Connecting | TLS handshake in progress |
Connected | Active, sending/receiving data |
Draining | Graceful shutdown in progress |
Closed | Connection terminated |
QUIC vs TCP vs UDP
| Feature | UDP | TCP | QUIC |
|---|---|---|---|
| NAT traversal | Good | Poor | Excellent |
| Firewall friendly | Varies | Good | Good (UDP 443) |
| Connection migration | No | No | Yes |
| Built-in encryption | No | Optional | Mandatory (TLS 1.3) |
| 0-RTT reconnection | N/A | No | Yes |
| Head-of-line blocking | No | Yes | No |
| Multicast | Yes | No | No |
| Multiplexed streams | N/A | No | Yes |
Hybrid Configuration
Combine UDP for multicast discovery with QUIC for data exchange:
use hdds::{Participant, TransportMode};
use hdds::transport::quic::QuicConfig;
let quic_config = QuicConfig::builder()
.bind_addr("0.0.0.0:7401".parse().unwrap())
.enable_0rtt(true)
.build();
let participant = Participant::builder("app")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast) // Discovery
.with_quic_transport(quic_config) // User data
.build()?;
Known Limitations
| Limitation | Description |
|---|---|
| Experimental | API may change in future releases |
| Feature-gated | Requires quic feature flag at compile time |
| No multicast | Cannot do multicast discovery; combine with UDP |
| HDDS-only | Not cross-vendor interoperable |
| No C FFI | QUIC is not yet exposed in the C API |
| quinn dependency | Adds the quinn crate and its transitive dependencies |
Next Steps
- TCP Transport -- Traditional TCP alternative
- Shared Memory Transport -- Zero-copy local IPC
- Advanced Transport Features -- DSCP, filtering, TSN