Skip to main content

QUIC Transport

QUIC-based transport for modern, secure, NAT-friendly communication with built-in TLS 1.3 and connection migration.

Experimental

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​

OptionDefaultDescription
bind_addr0.0.0.0:0Local bind address (0 = auto-assign port)
server_name"hdds.local"TLS SNI server name
enable_0rtttrueFast reconnection to known peers
keep_alive_interval15sNAT binding maintenance interval
idle_timeout30sConnection idle timeout
max_concurrent_streams100Streams per connection
max_recv_window1 MBMaximum receive window size
enable_migrationtrueAllow connection migration on IP change
dangerous_skip_verifyfalseSkip TLS certificate verification

Reconnection Settings​

QUIC supports automatic reconnection with exponential backoff.

OptionDefaultDescription
reconnect_enabledtrueAuto-reconnect on connection drop
reconnect_max_attemptsNone (infinite)Max retry attempts
reconnect_base_delay1sInitial backoff delay
reconnect_max_delay60sMaximum 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();
Testing Only

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 Security

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:

StateDescription
ConnectingTLS handshake in progress
ConnectedActive, sending/receiving data
DrainingGraceful shutdown in progress
ClosedConnection terminated

QUIC vs TCP vs UDP​

FeatureUDPTCPQUIC
NAT traversalGoodPoorExcellent
Firewall friendlyVariesGoodGood (UDP 443)
Connection migrationNoNoYes
Built-in encryptionNoOptionalMandatory (TLS 1.3)
0-RTT reconnectionN/ANoYes
Head-of-line blockingNoYesNo
MulticastYesNoNo
Multiplexed streamsN/ANoYes

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​

LimitationDescription
ExperimentalAPI may change in future releases
Feature-gatedRequires quic feature flag at compile time
No multicastCannot do multicast discovery; combine with UDP
HDDS-onlyNot cross-vendor interoperable
No C FFIQUIC is not yet exposed in the C API
quinn dependencyAdds the quinn crate and its transitive dependencies

Next Steps​