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