Skip to main content

QUIC transport parameters — RFC 9000 §18

Transport parameters are exchanged inside the TLS 1.3 quic_transport_parameters extension (ID 57). Each parameter is:

Transport Parameter {
Parameter ID (i),
Parameter Length (i), ← byte length of value
Parameter Value (..),
}

Scalar parameters encode their value as a QUIC varint inside the value region; flag parameters have a zero-length value; complex parameters (preferred_address) carry a sub-structure.

Parameter catalogue

IDNameTypeDefaultCarrier
0x00original_destination_connection_idCID (≤ 20 bytes)absentserver only
0x01max_idle_timeoutvarint ms0 (disabled)both
0x02stateless_reset_token16 bytesabsentserver only
0x03max_udp_payload_sizevarint65527both (≥ 1200)
0x04initial_max_datavarint0both
0x05initial_max_stream_data_bidi_localvarint0both
0x06initial_max_stream_data_bidi_remotevarint0both
0x07initial_max_stream_data_univarint0both
0x08initial_max_streams_bidivarint0both (≤ 2⁶⁰)
0x09initial_max_streams_univarint0both (≤ 2⁶⁰)
0x0Aack_delay_exponentvarint u83both (≤ 20)
0x0Bmax_ack_delayvarint ms25both
0x0Cdisable_active_migrationflagabsentboth
0x0Dpreferred_addressstructabsentserver only
0x0Eactive_connection_id_limitvarint2both (≥ 2)
0x0Finitial_source_connection_idCIDrequiredboth
0x10retry_source_connection_idCIDabsentserver only after Retry
0x20max_datagram_frame_sizevarintabsentboth (RFC 9221)
0x2AB2grease_quic_bitflagabsentboth (RFC 9287)

The type is core.net.quic.transport_params.TransportParams:

public type TransportParams is {
original_destination_connection_id: Maybe<ConnectionId>,
max_idle_timeout_ms: UInt64,
stateless_reset_token: Maybe<[Byte; 16]>,
max_udp_payload_size: UInt64,
initial_max_data: UInt64,
initial_max_stream_data_bidi_local: UInt64,
initial_max_stream_data_bidi_remote: UInt64,
initial_max_stream_data_uni: UInt64,
initial_max_streams_bidi: UInt64,
initial_max_streams_uni: UInt64,
ack_delay_exponent: UInt8,
max_ack_delay_ms: UInt64,
disable_active_migration: Bool,
preferred_address: Maybe<PreferredAddress>,
active_connection_id_limit: UInt64,
initial_source_connection_id: Maybe<ConnectionId>,
retry_source_connection_id: Maybe<ConnectionId>,
max_datagram_frame_size: Maybe<UInt64>,
grease_quic_bit: Bool,
};

TransportParams.defaults() returns RFC 9000 §18.2 defaults.

Bounds theorem (V9)

core.net.quic.transport_params.TransportParams.bounds_ok() enforces the §18.2 constraints that MUST hold for every valid parameter set:

initial_max_streams_bidi ≤ 2^60 (MAX_STREAMS_LIMIT)
initial_max_streams_uni ≤ 2^60
ack_delay_exponent ≤ 20 (MAX_ACK_DELAY_EXPONENT)
active_connection_id_limit ≥ 2 (MIN_ACTIVE_CID_LIMIT)
max_udp_payload_size ≥ 1200 (MIN_MAX_UDP_PAYLOAD_SIZE)

The call site treats a false return as fatal (TransportError.ProtocolViolation). The SMT layer discharges V9 at compile time (v9_transport_params_theorem).

Wire encoding

Each scalar parameter is three nested varints: id, length, value. Byte-exact example for a minimum server parameter block with max_idle_timeout=0 ms, max_udp_payload_size=1200 B, and initial_max_data=5:

0x01 0x01 0x00 ← max_idle_timeout = 0
0x03 0x02 0x44 0xB0 ← max_udp_payload_size = 1200
0x04 0x01 0x05 ← initial_max_data = 5

The 1200-byte max_udp_payload_size spills into a 2-byte varint: top 2 bits 01 (14-bit prefix) + 14-bit value 0x4B0 → wire bytes 0x44 0xB0 (0x4000 | 0x04B0).

Empty-body parameters (disable_active_migration, grease_quic_bit) emit [id_varint, 0x00]:

0x0C 0x00 ← disable_active_migration present
0x6A 0xB2 0x00 ← grease_quic_bit (id 0x2AB2 → 2-byte varint)

The greased grease_quic_bit ID (0x2AB2) encodes as a 2-byte QUIC varint: top 2 bits 01 (14-bit prefix) plus value 0x2AB2 → wire bytes 0x6A 0xB2. See transport_params_encode_kat and rfc9000_transport_params_roundtrip.

Preferred address

Server-only parameter 0x0D carrying a migration hint:

PreferredAddress {
ipv4_addr: [Byte; 4],
ipv4_port: UInt16,
ipv6_addr: [Byte; 16],
ipv6_port: UInt16,
connection_id: ConnectionId, ← length byte + CID bytes
stateless_reset_token: [Byte; 16],
}

Either IPv4 or IPv6 may be all-zero meaning "not offered". The CID length is a single byte followed by the CID bytes. See preferred_address_roundtrip.

API

mount core.net.quic.transport_params.{TransportParams, encode, decode};

let mut tp = TransportParams.defaults();
tp.max_udp_payload_size = 1200_u64;
tp.initial_max_data = 1_048_576_u64;
tp.initial_max_streams_bidi = 100_u64;

// Encode into scratch buffer.
let mut wire: List<Byte> = [];
encode(&tp, &mut wire);

// Decode + validate bounds (callers treat `!bounds_ok()` as fatal).
let parsed = decode(wire.as_slice()).unwrap();
assert(parsed.bounds_ok());

References

  • vcs/specs/L2-standard/net/quic/transport_params_defaults.vr
  • vcs/specs/L2-standard/net/quic/transport_params_bounds.vr
  • vcs/specs/L2-standard/net/quic/transport_params_encode_kat.vr
  • vcs/specs/L2-standard/net/quic/transport_params_optionals_roundtrip.vr
  • vcs/specs/L2-standard/net/quic/rfc9000_transport_params_roundtrip.vr
  • vcs/specs/L2-standard/net/quic/preferred_address_roundtrip.vr
  • vcs/specs/L2-standard/net/quic/v9_transport_params_theorem.vr