ant_quic/
transport_parameters.rs

1//! QUIC connection transport parameters
2//!
3//! The `TransportParameters` type is used to represent the transport parameters
4//! negotiated by peers while establishing a QUIC connection. This process
5//! happens as part of the establishment of the TLS session. As such, the types
6//! contained in this modules should generally only be referred to by custom
7//! implementations of the `crypto::Session` trait.
8
9use std::{
10    convert::TryFrom,
11    net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
12};
13
14use bytes::{Buf, BufMut};
15use rand::{Rng as _, RngCore, seq::SliceRandom as _};
16use thiserror::Error;
17
18use crate::{
19    LOC_CID_COUNT, MAX_CID_SIZE, MAX_STREAM_COUNT, RESET_TOKEN_SIZE, ResetToken, Side,
20    TIMER_GRANULARITY, TransportError, VarInt,
21    cid_generator::ConnectionIdGenerator,
22    cid_queue::CidQueue,
23    coding::{BufExt, BufMutExt, UnexpectedEnd},
24    config::{EndpointConfig, ServerConfig, TransportConfig},
25    shared::ConnectionId,
26};
27
28// Apply a given macro to a list of all the transport parameters having integer types, along with
29// their codes and default values. Using this helps us avoid error-prone duplication of the
30// contained information across decoding, encoding, and the `Default` impl. Whenever we want to do
31// something with transport parameters, we'll handle the bulk of cases by writing a macro that
32// takes a list of arguments in this form, then passing it to this macro.
33macro_rules! apply_params {
34    ($macro:ident) => {
35        $macro! {
36            // #[doc] name (id) = default,
37            /// Milliseconds, disabled if zero
38            max_idle_timeout(MaxIdleTimeout) = 0,
39            /// Limits the size of UDP payloads that the endpoint is willing to receive
40            max_udp_payload_size(MaxUdpPayloadSize) = 65527,
41
42            /// Initial value for the maximum amount of data that can be sent on the connection
43            initial_max_data(InitialMaxData) = 0,
44            /// Initial flow control limit for locally-initiated bidirectional streams
45            initial_max_stream_data_bidi_local(InitialMaxStreamDataBidiLocal) = 0,
46            /// Initial flow control limit for peer-initiated bidirectional streams
47            initial_max_stream_data_bidi_remote(InitialMaxStreamDataBidiRemote) = 0,
48            /// Initial flow control limit for unidirectional streams
49            initial_max_stream_data_uni(InitialMaxStreamDataUni) = 0,
50
51            /// Initial maximum number of bidirectional streams the peer may initiate
52            initial_max_streams_bidi(InitialMaxStreamsBidi) = 0,
53            /// Initial maximum number of unidirectional streams the peer may initiate
54            initial_max_streams_uni(InitialMaxStreamsUni) = 0,
55
56            /// Exponent used to decode the ACK Delay field in the ACK frame
57            ack_delay_exponent(AckDelayExponent) = 3,
58            /// Maximum amount of time in milliseconds by which the endpoint will delay sending
59            /// acknowledgments
60            max_ack_delay(MaxAckDelay) = 25,
61            /// Maximum number of connection IDs from the peer that an endpoint is willing to store
62            active_connection_id_limit(ActiveConnectionIdLimit) = 2,
63        }
64    };
65}
66
67macro_rules! make_struct {
68    {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
69        /// Transport parameters used to negotiate connection-level preferences between peers
70        #[derive(Debug, Copy, Clone, Eq, PartialEq)]
71        pub struct TransportParameters {
72            $($(#[$doc])* pub(crate) $name : VarInt,)*
73
74            /// Does the endpoint support active connection migration
75            pub(crate) disable_active_migration: bool,
76            /// Maximum size for datagram frames
77            pub(crate) max_datagram_frame_size: Option<VarInt>,
78            /// The value that the endpoint included in the Source Connection ID field of the first
79            /// Initial packet it sends for the connection
80            pub(crate) initial_src_cid: Option<ConnectionId>,
81            /// The endpoint is willing to receive QUIC packets containing any value for the fixed
82            /// bit
83            pub(crate) grease_quic_bit: bool,
84
85            /// Minimum amount of time in microseconds by which the endpoint is able to delay
86            /// sending acknowledgments
87            ///
88            /// If a value is provided, it implies that the endpoint supports QUIC Acknowledgement
89            /// Frequency
90            pub(crate) min_ack_delay: Option<VarInt>,
91
92            /// NAT traversal configuration for this connection
93            ///
94            /// When present, indicates support for QUIC NAT traversal extension
95            pub(crate) nat_traversal: Option<NatTraversalConfig>,
96
97            // Server-only
98            /// The value of the Destination Connection ID field from the first Initial packet sent
99            /// by the client
100            pub(crate) original_dst_cid: Option<ConnectionId>,
101            /// The value that the server included in the Source Connection ID field of a Retry
102            /// packet
103            pub(crate) retry_src_cid: Option<ConnectionId>,
104            /// Token used by the client to verify a stateless reset from the server
105            pub(crate) stateless_reset_token: Option<ResetToken>,
106            /// The server's preferred address for communication after handshake completion
107            pub(crate) preferred_address: Option<PreferredAddress>,
108            /// The randomly generated reserved transport parameter to sustain future extensibility
109            /// of transport parameter extensions.
110            /// When present, it is included during serialization but ignored during deserialization.
111            pub(crate) grease_transport_parameter: Option<ReservedTransportParameter>,
112
113            /// Defines the order in which transport parameters are serialized.
114            ///
115            /// This field is initialized only for outgoing `TransportParameters` instances and
116            /// is set to `None` for `TransportParameters` received from a peer.
117            pub(crate) write_order: Option<[u8; TransportParameterId::SUPPORTED.len()]>,
118        }
119
120        // We deliberately don't implement the `Default` trait, since that would be public, and
121        // downstream crates should never construct `TransportParameters` except by decoding those
122        // supplied by a peer.
123        impl TransportParameters {
124            /// Standard defaults, used if the peer does not supply a given parameter.
125            pub(crate) fn default() -> Self {
126                Self {
127                    $($name: VarInt::from_u32($default),)*
128
129                    disable_active_migration: false,
130                    max_datagram_frame_size: None,
131                    initial_src_cid: None,
132                    grease_quic_bit: false,
133                    min_ack_delay: None,
134                    nat_traversal: None,
135
136                    original_dst_cid: None,
137                    retry_src_cid: None,
138                    stateless_reset_token: None,
139                    preferred_address: None,
140                    grease_transport_parameter: None,
141                    write_order: None,
142                }
143            }
144        }
145    }
146}
147
148apply_params!(make_struct);
149
150impl TransportParameters {
151    pub(crate) fn new(
152        config: &TransportConfig,
153        endpoint_config: &EndpointConfig,
154        cid_gen: &dyn ConnectionIdGenerator,
155        initial_src_cid: ConnectionId,
156        server_config: Option<&ServerConfig>,
157        rng: &mut impl RngCore,
158    ) -> Self {
159        Self {
160            initial_src_cid: Some(initial_src_cid),
161            initial_max_streams_bidi: config.max_concurrent_bidi_streams,
162            initial_max_streams_uni: config.max_concurrent_uni_streams,
163            initial_max_data: config.receive_window,
164            initial_max_stream_data_bidi_local: config.stream_receive_window,
165            initial_max_stream_data_bidi_remote: config.stream_receive_window,
166            initial_max_stream_data_uni: config.stream_receive_window,
167            max_udp_payload_size: endpoint_config.max_udp_payload_size,
168            max_idle_timeout: config.max_idle_timeout.unwrap_or(VarInt(0)),
169            disable_active_migration: server_config.is_some_and(|c| !c.migration),
170            active_connection_id_limit: if cid_gen.cid_len() == 0 {
171                2 // i.e. default, i.e. unsent
172            } else {
173                CidQueue::LEN as u32
174            }
175            .into(),
176            max_datagram_frame_size: config
177                .datagram_receive_buffer_size
178                .map(|x| (x.min(u16::MAX.into()) as u16).into()),
179            grease_quic_bit: endpoint_config.grease_quic_bit,
180            min_ack_delay: Some(
181                VarInt::from_u64(u64::try_from(TIMER_GRANULARITY.as_micros()).unwrap()).unwrap(),
182            ),
183            grease_transport_parameter: Some(ReservedTransportParameter::random(rng)),
184            write_order: Some({
185                let mut order = std::array::from_fn(|i| i as u8);
186                order.shuffle(rng);
187                order
188            }),
189            ..Self::default()
190        }
191    }
192
193    /// Check that these parameters are legal when resuming from
194    /// certain cached parameters
195    pub(crate) fn validate_resumption_from(&self, cached: &Self) -> Result<(), TransportError> {
196        if cached.active_connection_id_limit > self.active_connection_id_limit
197            || cached.initial_max_data > self.initial_max_data
198            || cached.initial_max_stream_data_bidi_local > self.initial_max_stream_data_bidi_local
199            || cached.initial_max_stream_data_bidi_remote > self.initial_max_stream_data_bidi_remote
200            || cached.initial_max_stream_data_uni > self.initial_max_stream_data_uni
201            || cached.initial_max_streams_bidi > self.initial_max_streams_bidi
202            || cached.initial_max_streams_uni > self.initial_max_streams_uni
203            || cached.max_datagram_frame_size > self.max_datagram_frame_size
204            || cached.grease_quic_bit && !self.grease_quic_bit
205        {
206            return Err(TransportError::PROTOCOL_VIOLATION(
207                "0-RTT accepted with incompatible transport parameters",
208            ));
209        }
210        Ok(())
211    }
212
213    /// Maximum number of CIDs to issue to this peer
214    ///
215    /// Consider both a) the active_connection_id_limit from the other end; and
216    /// b) LOC_CID_COUNT used locally
217    pub(crate) fn issue_cids_limit(&self) -> u64 {
218        self.active_connection_id_limit.0.min(LOC_CID_COUNT)
219    }
220}
221
222/// NAT traversal configuration for a QUIC connection
223///
224/// This configuration is negotiated as part of the transport parameters and
225/// enables QUIC NAT traversal extension functionality.
226#[derive(Debug, Copy, Clone, Eq, PartialEq)]
227pub struct NatTraversalConfig {
228    /// Role of this endpoint in NAT traversal coordination
229    pub(crate) role: NatTraversalRole,
230    /// Maximum number of candidate addresses to exchange
231    pub(crate) max_candidates: VarInt,
232    /// Timeout for coordination protocol in milliseconds
233    pub(crate) coordination_timeout: VarInt,
234    /// Maximum number of concurrent traversal attempts
235    pub(crate) max_concurrent_attempts: VarInt,
236}
237
238impl NatTraversalConfig {
239    fn wire_size(&self) -> u16 {
240        1 + // role discriminant
241        self.max_candidates.size() as u16 +
242        self.coordination_timeout.size() as u16 +
243        self.max_concurrent_attempts.size() as u16 +
244        match self.role {
245            NatTraversalRole::Server { .. } => 1, // can_relay boolean
246            _ => 0,
247        }
248    }
249
250    fn write<W: BufMut>(&self, w: &mut W) {
251        match self.role {
252            NatTraversalRole::Client => w.put_u8(0),
253            NatTraversalRole::Server { can_relay } => {
254                w.put_u8(1);
255                w.put_u8(if can_relay { 1 } else { 0 });
256            }
257            NatTraversalRole::Bootstrap => w.put_u8(2),
258        }
259        w.write(self.max_candidates);
260        w.write(self.coordination_timeout);
261        w.write(self.max_concurrent_attempts);
262    }
263
264    fn read<R: Buf>(r: &mut R) -> Result<Self, Error> {
265        let role_byte = r.get::<u8>()?;
266        let role = match role_byte {
267            0 => NatTraversalRole::Client,
268            1 => {
269                let can_relay = r.get::<u8>()? != 0;
270                NatTraversalRole::Server { can_relay }
271            }
272            2 => NatTraversalRole::Bootstrap,
273            _ => return Err(Error::IllegalValue),
274        };
275        
276        let max_candidates = r.get()?;
277        let coordination_timeout = r.get()?;
278        let max_concurrent_attempts = r.get()?;
279        
280        Ok(Self {
281            role,
282            max_candidates,
283            coordination_timeout,
284            max_concurrent_attempts,
285        })
286    }
287}
288
289/// Role of an endpoint in NAT traversal coordination
290#[derive(Debug, Copy, Clone, Eq, PartialEq)]
291pub(crate) enum NatTraversalRole {
292    /// Client endpoint (initiates connections, on-demand)
293    Client,
294    /// Server endpoint (accepts connections, always reachable)
295    Server { 
296        /// Whether this server can act as a relay for other connections
297        can_relay: bool,
298    },
299    /// Bootstrap/relay endpoint (publicly reachable, coordinates traversal)
300    Bootstrap,
301}
302
303/// A server's preferred address
304///
305/// This is communicated as a transport parameter during TLS session establishment.
306#[derive(Debug, Copy, Clone, Eq, PartialEq)]
307pub(crate) struct PreferredAddress {
308    pub(crate) address_v4: Option<SocketAddrV4>,
309    pub(crate) address_v6: Option<SocketAddrV6>,
310    pub(crate) connection_id: ConnectionId,
311    pub(crate) stateless_reset_token: ResetToken,
312}
313
314impl PreferredAddress {
315    fn wire_size(&self) -> u16 {
316        4 + 2 + 16 + 2 + 1 + self.connection_id.len() as u16 + 16
317    }
318
319    fn write<W: BufMut>(&self, w: &mut W) {
320        w.write(self.address_v4.map_or(Ipv4Addr::UNSPECIFIED, |x| *x.ip()));
321        w.write::<u16>(self.address_v4.map_or(0, |x| x.port()));
322        w.write(self.address_v6.map_or(Ipv6Addr::UNSPECIFIED, |x| *x.ip()));
323        w.write::<u16>(self.address_v6.map_or(0, |x| x.port()));
324        w.write::<u8>(self.connection_id.len() as u8);
325        w.put_slice(&self.connection_id);
326        w.put_slice(&self.stateless_reset_token);
327    }
328
329    fn read<R: Buf>(r: &mut R) -> Result<Self, Error> {
330        let ip_v4 = r.get::<Ipv4Addr>()?;
331        let port_v4 = r.get::<u16>()?;
332        let ip_v6 = r.get::<Ipv6Addr>()?;
333        let port_v6 = r.get::<u16>()?;
334        let cid_len = r.get::<u8>()?;
335        if r.remaining() < cid_len as usize || cid_len > MAX_CID_SIZE as u8 {
336            return Err(Error::Malformed);
337        }
338        let mut stage = [0; MAX_CID_SIZE];
339        r.copy_to_slice(&mut stage[0..cid_len as usize]);
340        let cid = ConnectionId::new(&stage[0..cid_len as usize]);
341        if r.remaining() < 16 {
342            return Err(Error::Malformed);
343        }
344        let mut token = [0; RESET_TOKEN_SIZE];
345        r.copy_to_slice(&mut token);
346        let address_v4 = if ip_v4.is_unspecified() && port_v4 == 0 {
347            None
348        } else {
349            Some(SocketAddrV4::new(ip_v4, port_v4))
350        };
351        let address_v6 = if ip_v6.is_unspecified() && port_v6 == 0 {
352            None
353        } else {
354            Some(SocketAddrV6::new(ip_v6, port_v6, 0, 0))
355        };
356        if address_v4.is_none() && address_v6.is_none() {
357            return Err(Error::IllegalValue);
358        }
359        Ok(Self {
360            address_v4,
361            address_v6,
362            connection_id: cid,
363            stateless_reset_token: token.into(),
364        })
365    }
366}
367
368/// Errors encountered while decoding `TransportParameters`
369#[derive(Debug, Copy, Clone, Eq, PartialEq, Error)]
370pub enum Error {
371    /// Parameters that are semantically invalid
372    #[error("parameter had illegal value")]
373    IllegalValue,
374    /// Catch-all error for problems while decoding transport parameters
375    #[error("parameters were malformed")]
376    Malformed,
377}
378
379impl From<Error> for TransportError {
380    fn from(e: Error) -> Self {
381        match e {
382            Error::IllegalValue => Self::TRANSPORT_PARAMETER_ERROR("illegal value"),
383            Error::Malformed => Self::TRANSPORT_PARAMETER_ERROR("malformed"),
384        }
385    }
386}
387
388impl From<UnexpectedEnd> for Error {
389    fn from(_: UnexpectedEnd) -> Self {
390        Self::Malformed
391    }
392}
393
394impl TransportParameters {
395    /// Encode `TransportParameters` into buffer
396    pub fn write<W: BufMut>(&self, w: &mut W) {
397        for idx in self
398            .write_order
399            .as_ref()
400            .unwrap_or(&std::array::from_fn(|i| i as u8))
401        {
402            let id = TransportParameterId::SUPPORTED[*idx as usize];
403            match id {
404                TransportParameterId::ReservedTransportParameter => {
405                    if let Some(param) = self.grease_transport_parameter {
406                        param.write(w);
407                    }
408                }
409                TransportParameterId::StatelessResetToken => {
410                    if let Some(ref x) = self.stateless_reset_token {
411                        w.write_var(id as u64);
412                        w.write_var(16);
413                        w.put_slice(x);
414                    }
415                }
416                TransportParameterId::DisableActiveMigration => {
417                    if self.disable_active_migration {
418                        w.write_var(id as u64);
419                        w.write_var(0);
420                    }
421                }
422                TransportParameterId::MaxDatagramFrameSize => {
423                    if let Some(x) = self.max_datagram_frame_size {
424                        w.write_var(id as u64);
425                        w.write_var(x.size() as u64);
426                        w.write(x);
427                    }
428                }
429                TransportParameterId::PreferredAddress => {
430                    if let Some(ref x) = self.preferred_address {
431                        w.write_var(id as u64);
432                        w.write_var(x.wire_size() as u64);
433                        x.write(w);
434                    }
435                }
436                TransportParameterId::OriginalDestinationConnectionId => {
437                    if let Some(ref cid) = self.original_dst_cid {
438                        w.write_var(id as u64);
439                        w.write_var(cid.len() as u64);
440                        w.put_slice(cid);
441                    }
442                }
443                TransportParameterId::InitialSourceConnectionId => {
444                    if let Some(ref cid) = self.initial_src_cid {
445                        w.write_var(id as u64);
446                        w.write_var(cid.len() as u64);
447                        w.put_slice(cid);
448                    }
449                }
450                TransportParameterId::RetrySourceConnectionId => {
451                    if let Some(ref cid) = self.retry_src_cid {
452                        w.write_var(id as u64);
453                        w.write_var(cid.len() as u64);
454                        w.put_slice(cid);
455                    }
456                }
457                TransportParameterId::GreaseQuicBit => {
458                    if self.grease_quic_bit {
459                        w.write_var(id as u64);
460                        w.write_var(0);
461                    }
462                }
463                TransportParameterId::MinAckDelayDraft07 => {
464                    if let Some(x) = self.min_ack_delay {
465                        w.write_var(id as u64);
466                        w.write_var(x.size() as u64);
467                        w.write(x);
468                    }
469                }
470                TransportParameterId::NatTraversal => {
471                    if let Some(ref x) = self.nat_traversal {
472                        w.write_var(id as u64);
473                        w.write_var(x.wire_size() as u64);
474                        x.write(w);
475                    }
476                }
477                id => {
478                    macro_rules! write_params {
479                        {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
480                            match id {
481                                $(TransportParameterId::$id => {
482                                    if self.$name.0 != $default {
483                                        w.write_var(id as u64);
484                                        w.write(VarInt::try_from(self.$name.size()).unwrap());
485                                        w.write(self.$name);
486                                    }
487                                })*,
488                                _ => {
489                                    unimplemented!("Missing implementation of write for transport parameter with code {id:?}");
490                                }
491                            }
492                        }
493                    }
494                    apply_params!(write_params);
495                }
496            }
497        }
498    }
499
500    /// Decode `TransportParameters` from buffer
501    pub fn read<R: Buf>(side: Side, r: &mut R) -> Result<Self, Error> {
502        // Initialize to protocol-specified defaults
503        let mut params = Self::default();
504
505        // State to check for duplicate transport parameters.
506        macro_rules! param_state {
507            {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {{
508                struct ParamState {
509                    $($name: bool,)*
510                }
511
512                ParamState {
513                    $($name: false,)*
514                }
515            }}
516        }
517        let mut got = apply_params!(param_state);
518
519        while r.has_remaining() {
520            let id = r.get_var()?;
521            let len = r.get_var()?;
522            if (r.remaining() as u64) < len {
523                return Err(Error::Malformed);
524            }
525            let len = len as usize;
526            let Ok(id) = TransportParameterId::try_from(id) else {
527                // unknown transport parameters are ignored
528                r.advance(len);
529                continue;
530            };
531
532            match id {
533                TransportParameterId::OriginalDestinationConnectionId => {
534                    decode_cid(len, &mut params.original_dst_cid, r)?
535                }
536                TransportParameterId::StatelessResetToken => {
537                    if len != 16 || params.stateless_reset_token.is_some() {
538                        return Err(Error::Malformed);
539                    }
540                    let mut tok = [0; RESET_TOKEN_SIZE];
541                    r.copy_to_slice(&mut tok);
542                    params.stateless_reset_token = Some(tok.into());
543                }
544                TransportParameterId::DisableActiveMigration => {
545                    if len != 0 || params.disable_active_migration {
546                        return Err(Error::Malformed);
547                    }
548                    params.disable_active_migration = true;
549                }
550                TransportParameterId::PreferredAddress => {
551                    if params.preferred_address.is_some() {
552                        return Err(Error::Malformed);
553                    }
554                    params.preferred_address = Some(PreferredAddress::read(&mut r.take(len))?);
555                }
556                TransportParameterId::InitialSourceConnectionId => {
557                    decode_cid(len, &mut params.initial_src_cid, r)?
558                }
559                TransportParameterId::RetrySourceConnectionId => {
560                    decode_cid(len, &mut params.retry_src_cid, r)?
561                }
562                TransportParameterId::MaxDatagramFrameSize => {
563                    if len > 8 || params.max_datagram_frame_size.is_some() {
564                        return Err(Error::Malformed);
565                    }
566                    params.max_datagram_frame_size = Some(r.get().unwrap());
567                }
568                TransportParameterId::GreaseQuicBit => match len {
569                    0 => params.grease_quic_bit = true,
570                    _ => return Err(Error::Malformed),
571                },
572                TransportParameterId::MinAckDelayDraft07 => {
573                    params.min_ack_delay = Some(r.get().unwrap())
574                }
575                TransportParameterId::NatTraversal => {
576                    if params.nat_traversal.is_some() {
577                        return Err(Error::Malformed);
578                    }
579                    params.nat_traversal = Some(NatTraversalConfig::read(&mut r.take(len))?);
580                }
581                _ => {
582                    macro_rules! parse {
583                        {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
584                            match id {
585                                $(TransportParameterId::$id => {
586                                    let value = r.get::<VarInt>()?;
587                                    if len != value.size() || got.$name { return Err(Error::Malformed); }
588                                    params.$name = value.into();
589                                    got.$name = true;
590                                })*
591                                _ => r.advance(len),
592                            }
593                        }
594                    }
595                    apply_params!(parse);
596                }
597            }
598        }
599
600        // Semantic validation
601
602        // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.26.1
603        if params.ack_delay_exponent.0 > 20
604            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.28.1
605            || params.max_ack_delay.0 >= 1 << 14
606            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-6.2.1
607            || params.active_connection_id_limit.0 < 2
608            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.10.1
609            || params.max_udp_payload_size.0 < 1200
610            // https://www.rfc-editor.org/rfc/rfc9000.html#section-4.6-2
611            || params.initial_max_streams_bidi.0 > MAX_STREAM_COUNT
612            || params.initial_max_streams_uni.0 > MAX_STREAM_COUNT
613            // https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-08.html#section-3-4
614            || params.min_ack_delay.is_some_and(|min_ack_delay| {
615                // min_ack_delay uses microseconds, whereas max_ack_delay uses milliseconds
616                min_ack_delay.0 > params.max_ack_delay.0 * 1_000
617            })
618            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-8
619            || (side.is_server()
620                && (params.original_dst_cid.is_some()
621                    || params.preferred_address.is_some()
622                    || params.retry_src_cid.is_some()
623                    || params.stateless_reset_token.is_some()))
624            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.38.1
625            || params
626                .preferred_address.is_some_and(|x| x.connection_id.is_empty())
627        {
628            return Err(Error::IllegalValue);
629        }
630
631        Ok(params)
632    }
633}
634
635/// A reserved transport parameter.
636///
637/// It has an identifier of the form 31 * N + 27 for the integer value of N.
638/// Such identifiers are reserved to exercise the requirement that unknown transport parameters be ignored.
639/// The reserved transport parameter has no semantics and can carry arbitrary values.
640/// It may be included in transport parameters sent to the peer, and should be ignored when received.
641///
642/// See spec: <https://www.rfc-editor.org/rfc/rfc9000.html#section-18.1>
643#[derive(Debug, Copy, Clone, Eq, PartialEq)]
644pub(crate) struct ReservedTransportParameter {
645    /// The reserved identifier of the transport parameter
646    id: VarInt,
647
648    /// Buffer to store the parameter payload
649    payload: [u8; Self::MAX_PAYLOAD_LEN],
650
651    /// The number of bytes to include in the wire format from the `payload` buffer
652    payload_len: usize,
653}
654
655impl ReservedTransportParameter {
656    /// Generates a transport parameter with a random payload and a reserved ID.
657    ///
658    /// The implementation is inspired by quic-go and quiche:
659    /// 1. <https://github.com/quic-go/quic-go/blob/3e0a67b2476e1819752f04d75968de042b197b56/internal/wire/transport_parameters.go#L338-L344>
660    /// 2. <https://github.com/google/quiche/blob/cb1090b20c40e2f0815107857324e99acf6ec567/quiche/quic/core/crypto/transport_parameters.cc#L843-L860>
661    fn random(rng: &mut impl RngCore) -> Self {
662        let id = Self::generate_reserved_id(rng);
663
664        let payload_len = rng.random_range(0..Self::MAX_PAYLOAD_LEN);
665
666        let payload = {
667            let mut slice = [0u8; Self::MAX_PAYLOAD_LEN];
668            rng.fill_bytes(&mut slice[..payload_len]);
669            slice
670        };
671
672        Self {
673            id,
674            payload,
675            payload_len,
676        }
677    }
678
679    fn write(&self, w: &mut impl BufMut) {
680        w.write_var(self.id.0);
681        w.write_var(self.payload_len as u64);
682        w.put_slice(&self.payload[..self.payload_len]);
683    }
684
685    /// Generates a random reserved identifier of the form `31 * N + 27`, as required by RFC 9000.
686    /// Reserved transport parameter identifiers are used to test compliance with the requirement
687    /// that unknown transport parameters must be ignored by peers.
688    /// See: <https://www.rfc-editor.org/rfc/rfc9000.html#section-18.1> and <https://www.rfc-editor.org/rfc/rfc9000.html#section-22.3>
689    fn generate_reserved_id(rng: &mut impl RngCore) -> VarInt {
690        let id = {
691            let rand = rng.random_range(0u64..(1 << 62) - 27);
692            let n = rand / 31;
693            31 * n + 27
694        };
695        debug_assert!(
696            id % 31 == 27,
697            "generated id does not have the form of 31 * N + 27"
698        );
699        VarInt::from_u64(id).expect(
700            "generated id does fit into range of allowed transport parameter IDs: [0; 2^62)",
701        )
702    }
703
704    /// The maximum length of the payload to include as the parameter payload.
705    /// This value is not a specification-imposed limit but is chosen to match
706    /// the limit used by other implementations of QUIC, e.g., quic-go and quiche.
707    const MAX_PAYLOAD_LEN: usize = 16;
708}
709
710#[repr(u64)]
711#[derive(Debug, Clone, Copy, PartialEq, Eq)]
712pub(crate) enum TransportParameterId {
713    // https://www.rfc-editor.org/rfc/rfc9000.html#iana-tp-table
714    OriginalDestinationConnectionId = 0x00,
715    MaxIdleTimeout = 0x01,
716    StatelessResetToken = 0x02,
717    MaxUdpPayloadSize = 0x03,
718    InitialMaxData = 0x04,
719    InitialMaxStreamDataBidiLocal = 0x05,
720    InitialMaxStreamDataBidiRemote = 0x06,
721    InitialMaxStreamDataUni = 0x07,
722    InitialMaxStreamsBidi = 0x08,
723    InitialMaxStreamsUni = 0x09,
724    AckDelayExponent = 0x0A,
725    MaxAckDelay = 0x0B,
726    DisableActiveMigration = 0x0C,
727    PreferredAddress = 0x0D,
728    ActiveConnectionIdLimit = 0x0E,
729    InitialSourceConnectionId = 0x0F,
730    RetrySourceConnectionId = 0x10,
731
732    // Smallest possible ID of reserved transport parameter https://datatracker.ietf.org/doc/html/rfc9000#section-22.3
733    ReservedTransportParameter = 0x1B,
734
735    // https://www.rfc-editor.org/rfc/rfc9221.html#section-3
736    MaxDatagramFrameSize = 0x20,
737
738    // https://www.rfc-editor.org/rfc/rfc9287.html#section-3
739    GreaseQuicBit = 0x2AB2,
740
741    // https://datatracker.ietf.org/doc/html/draft-ietf-quic-ack-frequency#section-10.1
742    MinAckDelayDraft07 = 0xFF04DE1B,
743
744    // NAT Traversal Extension - using reserved parameter space (31*N + 27)
745    // This follows RFC 9000 reserved parameter pattern for experimental extensions
746    NatTraversal = 0x58, // 31*1 + 27 = 58
747}
748
749impl TransportParameterId {
750    /// Array with all supported transport parameter IDs
751    const SUPPORTED: [Self; 22] = [
752        Self::MaxIdleTimeout,
753        Self::MaxUdpPayloadSize,
754        Self::InitialMaxData,
755        Self::InitialMaxStreamDataBidiLocal,
756        Self::InitialMaxStreamDataBidiRemote,
757        Self::InitialMaxStreamDataUni,
758        Self::InitialMaxStreamsBidi,
759        Self::InitialMaxStreamsUni,
760        Self::AckDelayExponent,
761        Self::MaxAckDelay,
762        Self::ActiveConnectionIdLimit,
763        Self::ReservedTransportParameter,
764        Self::StatelessResetToken,
765        Self::DisableActiveMigration,
766        Self::MaxDatagramFrameSize,
767        Self::PreferredAddress,
768        Self::OriginalDestinationConnectionId,
769        Self::InitialSourceConnectionId,
770        Self::RetrySourceConnectionId,
771        Self::GreaseQuicBit,
772        Self::MinAckDelayDraft07,
773        Self::NatTraversal,
774    ];
775}
776
777impl std::cmp::PartialEq<u64> for TransportParameterId {
778    fn eq(&self, other: &u64) -> bool {
779        *other == (*self as u64)
780    }
781}
782
783impl TryFrom<u64> for TransportParameterId {
784    type Error = ();
785
786    fn try_from(value: u64) -> Result<Self, Self::Error> {
787        let param = match value {
788            id if Self::MaxIdleTimeout == id => Self::MaxIdleTimeout,
789            id if Self::MaxUdpPayloadSize == id => Self::MaxUdpPayloadSize,
790            id if Self::InitialMaxData == id => Self::InitialMaxData,
791            id if Self::InitialMaxStreamDataBidiLocal == id => Self::InitialMaxStreamDataBidiLocal,
792            id if Self::InitialMaxStreamDataBidiRemote == id => {
793                Self::InitialMaxStreamDataBidiRemote
794            }
795            id if Self::InitialMaxStreamDataUni == id => Self::InitialMaxStreamDataUni,
796            id if Self::InitialMaxStreamsBidi == id => Self::InitialMaxStreamsBidi,
797            id if Self::InitialMaxStreamsUni == id => Self::InitialMaxStreamsUni,
798            id if Self::AckDelayExponent == id => Self::AckDelayExponent,
799            id if Self::MaxAckDelay == id => Self::MaxAckDelay,
800            id if Self::ActiveConnectionIdLimit == id => Self::ActiveConnectionIdLimit,
801            id if Self::ReservedTransportParameter == id => Self::ReservedTransportParameter,
802            id if Self::StatelessResetToken == id => Self::StatelessResetToken,
803            id if Self::DisableActiveMigration == id => Self::DisableActiveMigration,
804            id if Self::MaxDatagramFrameSize == id => Self::MaxDatagramFrameSize,
805            id if Self::PreferredAddress == id => Self::PreferredAddress,
806            id if Self::OriginalDestinationConnectionId == id => {
807                Self::OriginalDestinationConnectionId
808            }
809            id if Self::InitialSourceConnectionId == id => Self::InitialSourceConnectionId,
810            id if Self::RetrySourceConnectionId == id => Self::RetrySourceConnectionId,
811            id if Self::GreaseQuicBit == id => Self::GreaseQuicBit,
812            id if Self::MinAckDelayDraft07 == id => Self::MinAckDelayDraft07,
813            id if Self::NatTraversal == id => Self::NatTraversal,
814            _ => return Err(()),
815        };
816        Ok(param)
817    }
818}
819
820fn decode_cid(len: usize, value: &mut Option<ConnectionId>, r: &mut impl Buf) -> Result<(), Error> {
821    if len > MAX_CID_SIZE || value.is_some() || r.remaining() < len {
822        return Err(Error::Malformed);
823    }
824
825    *value = Some(ConnectionId::from_buf(r, len));
826    Ok(())
827}
828
829#[cfg(test)]
830mod test {
831    use super::*;
832
833    #[test]
834    fn coding() {
835        let mut buf = Vec::new();
836        let params = TransportParameters {
837            initial_src_cid: Some(ConnectionId::new(&[])),
838            original_dst_cid: Some(ConnectionId::new(&[])),
839            initial_max_streams_bidi: 16u32.into(),
840            initial_max_streams_uni: 16u32.into(),
841            ack_delay_exponent: 2u32.into(),
842            max_udp_payload_size: 1200u32.into(),
843            preferred_address: Some(PreferredAddress {
844                address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
845                address_v6: Some(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 24, 0, 0)),
846                connection_id: ConnectionId::new(&[0x42]),
847                stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
848            }),
849            grease_quic_bit: true,
850            min_ack_delay: Some(2_000u32.into()),
851            ..TransportParameters::default()
852        };
853        params.write(&mut buf);
854        assert_eq!(
855            TransportParameters::read(Side::Client, &mut buf.as_slice()).unwrap(),
856            params
857        );
858    }
859
860    #[test]
861    fn reserved_transport_parameter_generate_reserved_id() {
862        use rand::rngs::mock::StepRng;
863        let mut rngs = [
864            StepRng::new(0, 1),
865            StepRng::new(1, 1),
866            StepRng::new(27, 1),
867            StepRng::new(31, 1),
868            StepRng::new(u32::MAX as u64, 1),
869            StepRng::new(u32::MAX as u64 - 1, 1),
870            StepRng::new(u32::MAX as u64 + 1, 1),
871            StepRng::new(u32::MAX as u64 - 27, 1),
872            StepRng::new(u32::MAX as u64 + 27, 1),
873            StepRng::new(u32::MAX as u64 - 31, 1),
874            StepRng::new(u32::MAX as u64 + 31, 1),
875            StepRng::new(u64::MAX, 1),
876            StepRng::new(u64::MAX - 1, 1),
877            StepRng::new(u64::MAX - 27, 1),
878            StepRng::new(u64::MAX - 31, 1),
879            StepRng::new(1 << 62, 1),
880            StepRng::new((1 << 62) - 1, 1),
881            StepRng::new((1 << 62) + 1, 1),
882            StepRng::new((1 << 62) - 27, 1),
883            StepRng::new((1 << 62) + 27, 1),
884            StepRng::new((1 << 62) - 31, 1),
885            StepRng::new((1 << 62) + 31, 1),
886        ];
887        for rng in &mut rngs {
888            let id = ReservedTransportParameter::generate_reserved_id(rng);
889            assert!(id.0 % 31 == 27)
890        }
891    }
892
893    #[test]
894    fn reserved_transport_parameter_ignored_when_read() {
895        let mut buf = Vec::new();
896        let reserved_parameter = ReservedTransportParameter::random(&mut rand::rng());
897        assert!(reserved_parameter.payload_len < ReservedTransportParameter::MAX_PAYLOAD_LEN);
898        assert!(reserved_parameter.id.0 % 31 == 27);
899
900        reserved_parameter.write(&mut buf);
901        assert!(!buf.is_empty());
902        let read_params = TransportParameters::read(Side::Server, &mut buf.as_slice()).unwrap();
903        assert_eq!(read_params, TransportParameters::default());
904    }
905
906    #[test]
907    fn read_semantic_validation() {
908        #[allow(clippy::type_complexity)]
909        let illegal_params_builders: Vec<Box<dyn FnMut(&mut TransportParameters)>> = vec![
910            Box::new(|t| {
911                // This min_ack_delay is bigger than max_ack_delay!
912                let min_ack_delay = t.max_ack_delay.0 * 1_000 + 1;
913                t.min_ack_delay = Some(VarInt::from_u64(min_ack_delay).unwrap())
914            }),
915            Box::new(|t| {
916                // Preferred address can only be sent by senders (and we are reading the transport
917                // params as a client)
918                t.preferred_address = Some(PreferredAddress {
919                    address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
920                    address_v6: None,
921                    connection_id: ConnectionId::new(&[]),
922                    stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
923                })
924            }),
925        ];
926
927        for mut builder in illegal_params_builders {
928            let mut buf = Vec::new();
929            let mut params = TransportParameters::default();
930            builder(&mut params);
931            params.write(&mut buf);
932
933            assert_eq!(
934                TransportParameters::read(Side::Server, &mut buf.as_slice()),
935                Err(Error::IllegalValue)
936            );
937        }
938    }
939
940    #[test]
941    fn resumption_params_validation() {
942        let high_limit = TransportParameters {
943            initial_max_streams_uni: 32u32.into(),
944            ..TransportParameters::default()
945        };
946        let low_limit = TransportParameters {
947            initial_max_streams_uni: 16u32.into(),
948            ..TransportParameters::default()
949        };
950        high_limit.validate_resumption_from(&low_limit).unwrap();
951        low_limit.validate_resumption_from(&high_limit).unwrap_err();
952    }
953}