Skip to main content

quiche/
transport_params.rs

1// Copyright (C) 2018-2025, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27//! Transport parameters handling as per RFC 9000 Section 7.4
28//! Part of the Cryptographic and Transport Handshake
29
30use std::collections::HashSet;
31use std::mem::size_of;
32
33use crate::ConnectionId;
34use crate::Error;
35use crate::Result;
36use crate::MAX_STREAM_ID;
37
38#[cfg(feature = "qlog")]
39use crate::crypto;
40#[cfg(feature = "qlog")]
41use qlog::events::quic::TransportInitiator;
42#[cfg(feature = "qlog")]
43use qlog::events::EventData;
44
45/// Maximum permitted value of the `ack_delay_exponent` transport parameter,
46/// as mandated by RFC 9000 Section 18.2.
47pub const MAX_ACK_DELAY_EXPONENT: u64 = 20;
48
49/// QUIC Unknown Transport Parameter.
50///
51/// A QUIC transport parameter that is not specifically recognized
52/// by this implementation.
53#[derive(Clone, Debug, PartialEq)]
54pub struct UnknownTransportParameter<T> {
55    /// The ID of the unknown transport parameter.
56    pub id: u64,
57
58    /// Original data representing the value of the unknown transport parameter.
59    pub value: T,
60}
61
62impl<T> UnknownTransportParameter<T> {
63    /// Checks whether an unknown Transport Parameter's ID is in the reserved
64    /// space.
65    ///
66    /// See Section 18.1 in [RFC9000](https://datatracker.ietf.org/doc/html/rfc9000#name-reserved-transport-paramete).
67    pub fn is_reserved(&self) -> bool {
68        let n = (self.id - 27) / 31;
69        self.id == 31 * n + 27
70    }
71}
72
73#[cfg(feature = "qlog")]
74impl From<UnknownTransportParameter<Vec<u8>>>
75    for qlog::events::quic::UnknownTransportParameter
76{
77    fn from(value: UnknownTransportParameter<Vec<u8>>) -> Self {
78        Self {
79            id: value.id,
80            value: qlog::HexSlice::maybe_string(Some(value.value.as_slice()))
81                .unwrap_or_default(),
82        }
83    }
84}
85
86impl From<UnknownTransportParameter<&[u8]>>
87    for UnknownTransportParameter<Vec<u8>>
88{
89    // When an instance of an UnknownTransportParameter is actually
90    // stored in UnknownTransportParameters, then we make a copy
91    // of the bytes if the source is an instance of an UnknownTransportParameter
92    // whose value is not owned.
93    fn from(value: UnknownTransportParameter<&[u8]>) -> Self {
94        Self {
95            id: value.id,
96            value: value.value.to_vec(),
97        }
98    }
99}
100
101/// Track unknown transport parameters, up to a limit.
102#[derive(Clone, Debug, PartialEq, Default)]
103pub struct UnknownTransportParameters {
104    /// The space remaining for storing unknown transport parameters.
105    pub capacity: usize,
106    /// The unknown transport parameters.
107    pub parameters: Vec<UnknownTransportParameter<Vec<u8>>>,
108}
109
110impl UnknownTransportParameters {
111    /// Pushes an unknown transport parameter into storage if there is space
112    /// remaining.
113    pub fn push(&mut self, new: UnknownTransportParameter<&[u8]>) -> Result<()> {
114        let new_unknown_tp_size = new.value.len() + size_of::<u64>();
115        if new_unknown_tp_size < self.capacity {
116            self.capacity -= new_unknown_tp_size;
117            self.parameters.push(new.into());
118            Ok(())
119        } else {
120            Err(octets::BufferTooShortError.into())
121        }
122    }
123}
124
125/// An Iterator over unknown transport parameters.
126pub struct UnknownTransportParameterIterator<'a> {
127    index: usize,
128    parameters: &'a Vec<UnknownTransportParameter<Vec<u8>>>,
129}
130
131impl<'a> IntoIterator for &'a UnknownTransportParameters {
132    type IntoIter = UnknownTransportParameterIterator<'a>;
133    type Item = &'a UnknownTransportParameter<Vec<u8>>;
134
135    fn into_iter(self) -> Self::IntoIter {
136        UnknownTransportParameterIterator {
137            index: 0,
138            parameters: &self.parameters,
139        }
140    }
141}
142
143impl<'a> Iterator for UnknownTransportParameterIterator<'a> {
144    type Item = &'a UnknownTransportParameter<Vec<u8>>;
145
146    fn next(&mut self) -> Option<Self::Item> {
147        let result = self.parameters.get(self.index);
148        self.index += 1;
149        result
150    }
151}
152
153/// QUIC Transport Parameters
154#[derive(Clone, Debug, PartialEq)]
155pub struct TransportParams {
156    /// Value of Destination CID field from first Initial packet sent by client
157    pub original_destination_connection_id: Option<ConnectionId<'static>>,
158    /// The maximum idle timeout.
159    pub max_idle_timeout: u64,
160    /// Token used for verifying stateless resets
161    pub stateless_reset_token: Option<u128>,
162    /// The maximum UDP payload size.
163    pub max_udp_payload_size: u64,
164    /// The initial flow control maximum data for the connection.
165    pub initial_max_data: u64,
166    /// The initial flow control maximum data for local bidirectional streams.
167    pub initial_max_stream_data_bidi_local: u64,
168    /// The initial flow control maximum data for remote bidirectional streams.
169    pub initial_max_stream_data_bidi_remote: u64,
170    /// The initial flow control maximum data for unidirectional streams.
171    pub initial_max_stream_data_uni: u64,
172    /// The initial maximum bidirectional streams.
173    pub initial_max_streams_bidi: u64,
174    /// The initial maximum unidirectional streams.
175    pub initial_max_streams_uni: u64,
176    /// The ACK delay exponent.
177    pub ack_delay_exponent: u64,
178    /// The max ACK delay.
179    pub max_ack_delay: u64,
180    /// Whether active migration is disabled.
181    pub disable_active_migration: bool,
182    /// The active connection ID limit.
183    pub active_conn_id_limit: u64,
184    /// The value that the endpoint included in the Source CID field of a Retry
185    /// Packet.
186    pub initial_source_connection_id: Option<ConnectionId<'static>>,
187    /// The value that the server included in the Source CID field of a Retry
188    /// Packet.
189    pub retry_source_connection_id: Option<ConnectionId<'static>>,
190    /// DATAGRAM frame extension parameter, if any.
191    pub max_datagram_frame_size: Option<u64>,
192    /// Unknown peer transport parameters and values, if any.
193    pub unknown_params: Option<UnknownTransportParameters>,
194    // pub preferred_address: ...,
195}
196
197impl Default for TransportParams {
198    fn default() -> TransportParams {
199        TransportParams {
200            original_destination_connection_id: None,
201            max_idle_timeout: 0,
202            stateless_reset_token: None,
203            max_udp_payload_size: 65527,
204            initial_max_data: 0,
205            initial_max_stream_data_bidi_local: 0,
206            initial_max_stream_data_bidi_remote: 0,
207            initial_max_stream_data_uni: 0,
208            initial_max_streams_bidi: 0,
209            initial_max_streams_uni: 0,
210            ack_delay_exponent: 3,
211            max_ack_delay: 25,
212            disable_active_migration: false,
213            active_conn_id_limit: 2,
214            initial_source_connection_id: None,
215            retry_source_connection_id: None,
216            max_datagram_frame_size: None,
217            unknown_params: Default::default(),
218        }
219    }
220}
221
222impl TransportParams {
223    pub(crate) fn decode(
224        buf: &[u8], is_server: bool, unknown_size: Option<usize>,
225    ) -> Result<TransportParams> {
226        let mut params = octets::Octets::with_slice(buf);
227        let mut seen_params = HashSet::new();
228
229        let mut tp = TransportParams::default();
230
231        if let Some(unknown_transport_param_tracking_size) = unknown_size {
232            tp.unknown_params = Some(UnknownTransportParameters {
233                capacity: unknown_transport_param_tracking_size,
234                parameters: vec![],
235            });
236        }
237
238        while params.cap() > 0 {
239            let id = params.get_varint()?;
240
241            if seen_params.contains(&id) {
242                return Err(Error::InvalidTransportParam);
243            }
244            seen_params.insert(id);
245
246            let mut val = params.get_bytes_with_varint_length()?;
247
248            match id {
249                0x0000 => {
250                    if is_server {
251                        return Err(Error::InvalidTransportParam);
252                    }
253
254                    tp.original_destination_connection_id =
255                        Some(val.to_vec().into());
256                },
257
258                0x0001 => {
259                    tp.max_idle_timeout = val.get_varint()?;
260                },
261
262                0x0002 => {
263                    if is_server {
264                        return Err(Error::InvalidTransportParam);
265                    }
266
267                    tp.stateless_reset_token = Some(u128::from_be_bytes(
268                        val.get_bytes(16)?
269                            .to_vec()
270                            .try_into()
271                            .map_err(|_| Error::BufferTooShort)?,
272                    ));
273                },
274
275                0x0003 => {
276                    tp.max_udp_payload_size = val.get_varint()?;
277
278                    if tp.max_udp_payload_size < 1200 {
279                        return Err(Error::InvalidTransportParam);
280                    }
281                },
282
283                0x0004 => {
284                    tp.initial_max_data = val.get_varint()?;
285                },
286
287                0x0005 => {
288                    tp.initial_max_stream_data_bidi_local = val.get_varint()?;
289                },
290
291                0x0006 => {
292                    tp.initial_max_stream_data_bidi_remote = val.get_varint()?;
293                },
294
295                0x0007 => {
296                    tp.initial_max_stream_data_uni = val.get_varint()?;
297                },
298
299                0x0008 => {
300                    let max = val.get_varint()?;
301
302                    if max > MAX_STREAM_ID {
303                        return Err(Error::InvalidTransportParam);
304                    }
305
306                    tp.initial_max_streams_bidi = max;
307                },
308
309                0x0009 => {
310                    let max = val.get_varint()?;
311
312                    if max > MAX_STREAM_ID {
313                        return Err(Error::InvalidTransportParam);
314                    }
315
316                    tp.initial_max_streams_uni = max;
317                },
318
319                0x000a => {
320                    let ack_delay_exponent = val.get_varint()?;
321
322                    if ack_delay_exponent > MAX_ACK_DELAY_EXPONENT {
323                        return Err(Error::InvalidTransportParam);
324                    }
325
326                    tp.ack_delay_exponent = ack_delay_exponent;
327                },
328
329                0x000b => {
330                    let max_ack_delay = val.get_varint()?;
331
332                    if max_ack_delay >= 2_u64.pow(14) {
333                        return Err(Error::InvalidTransportParam);
334                    }
335
336                    tp.max_ack_delay = max_ack_delay;
337                },
338
339                0x000c => {
340                    tp.disable_active_migration = true;
341                },
342
343                0x000d => {
344                    if is_server {
345                        return Err(Error::InvalidTransportParam);
346                    }
347
348                    // TODO: decode preferred_address
349                },
350
351                0x000e => {
352                    let limit = val.get_varint()?;
353
354                    if limit < 2 {
355                        return Err(Error::InvalidTransportParam);
356                    }
357
358                    tp.active_conn_id_limit = limit;
359                },
360
361                0x000f => {
362                    tp.initial_source_connection_id = Some(val.to_vec().into());
363                },
364
365                0x00010 => {
366                    if is_server {
367                        return Err(Error::InvalidTransportParam);
368                    }
369
370                    tp.retry_source_connection_id = Some(val.to_vec().into());
371                },
372
373                0x0020 => {
374                    tp.max_datagram_frame_size = Some(val.get_varint()?);
375                },
376
377                // Track unknown transport parameters specially.
378                unknown_tp_id => {
379                    if let Some(unknown_params) = &mut tp.unknown_params {
380                        // It is _not_ an error not to have space enough to track
381                        // an unknown parameter.
382                        let _ = unknown_params.push(UnknownTransportParameter {
383                            id: unknown_tp_id,
384                            value: val.buf(),
385                        });
386                    }
387                },
388            }
389        }
390
391        Ok(tp)
392    }
393
394    pub(crate) fn encode_param(
395        b: &mut octets::OctetsMut, ty: u64, len: usize,
396    ) -> Result<()> {
397        b.put_varint(ty)?;
398        b.put_varint(len as u64)?;
399
400        Ok(())
401    }
402
403    pub(crate) fn encode<'a>(
404        tp: &TransportParams, is_server: bool, out: &'a mut [u8],
405    ) -> Result<&'a mut [u8]> {
406        let mut b = octets::OctetsMut::with_slice(out);
407
408        if is_server {
409            if let Some(ref odcid) = tp.original_destination_connection_id {
410                TransportParams::encode_param(&mut b, 0x0000, odcid.len())?;
411                b.put_bytes(odcid)?;
412            }
413        };
414
415        if tp.max_idle_timeout != 0 {
416            assert!(tp.max_idle_timeout <= octets::MAX_VAR_INT);
417            TransportParams::encode_param(
418                &mut b,
419                0x0001,
420                octets::varint_len(tp.max_idle_timeout),
421            )?;
422            b.put_varint(tp.max_idle_timeout)?;
423        }
424
425        if is_server {
426            if let Some(ref token) = tp.stateless_reset_token {
427                TransportParams::encode_param(&mut b, 0x0002, 16)?;
428                b.put_bytes(&token.to_be_bytes())?;
429            }
430        }
431
432        if tp.max_udp_payload_size != 0 {
433            assert!(tp.max_udp_payload_size <= octets::MAX_VAR_INT);
434            TransportParams::encode_param(
435                &mut b,
436                0x0003,
437                octets::varint_len(tp.max_udp_payload_size),
438            )?;
439            b.put_varint(tp.max_udp_payload_size)?;
440        }
441
442        if tp.initial_max_data != 0 {
443            assert!(tp.initial_max_data <= octets::MAX_VAR_INT);
444            TransportParams::encode_param(
445                &mut b,
446                0x0004,
447                octets::varint_len(tp.initial_max_data),
448            )?;
449            b.put_varint(tp.initial_max_data)?;
450        }
451
452        if tp.initial_max_stream_data_bidi_local != 0 {
453            assert!(tp.initial_max_stream_data_bidi_local <= octets::MAX_VAR_INT);
454            TransportParams::encode_param(
455                &mut b,
456                0x0005,
457                octets::varint_len(tp.initial_max_stream_data_bidi_local),
458            )?;
459            b.put_varint(tp.initial_max_stream_data_bidi_local)?;
460        }
461
462        if tp.initial_max_stream_data_bidi_remote != 0 {
463            assert!(
464                tp.initial_max_stream_data_bidi_remote <= octets::MAX_VAR_INT
465            );
466            TransportParams::encode_param(
467                &mut b,
468                0x0006,
469                octets::varint_len(tp.initial_max_stream_data_bidi_remote),
470            )?;
471            b.put_varint(tp.initial_max_stream_data_bidi_remote)?;
472        }
473
474        if tp.initial_max_stream_data_uni != 0 {
475            assert!(tp.initial_max_stream_data_uni <= octets::MAX_VAR_INT);
476            TransportParams::encode_param(
477                &mut b,
478                0x0007,
479                octets::varint_len(tp.initial_max_stream_data_uni),
480            )?;
481            b.put_varint(tp.initial_max_stream_data_uni)?;
482        }
483
484        if tp.initial_max_streams_bidi != 0 {
485            assert!(tp.initial_max_streams_bidi <= octets::MAX_VAR_INT);
486            TransportParams::encode_param(
487                &mut b,
488                0x0008,
489                octets::varint_len(tp.initial_max_streams_bidi),
490            )?;
491            b.put_varint(tp.initial_max_streams_bidi)?;
492        }
493
494        if tp.initial_max_streams_uni != 0 {
495            assert!(tp.initial_max_streams_uni <= octets::MAX_VAR_INT);
496            TransportParams::encode_param(
497                &mut b,
498                0x0009,
499                octets::varint_len(tp.initial_max_streams_uni),
500            )?;
501            b.put_varint(tp.initial_max_streams_uni)?;
502        }
503
504        if tp.ack_delay_exponent != 0 {
505            assert!(tp.ack_delay_exponent <= MAX_ACK_DELAY_EXPONENT);
506            TransportParams::encode_param(
507                &mut b,
508                0x000a,
509                octets::varint_len(tp.ack_delay_exponent),
510            )?;
511            b.put_varint(tp.ack_delay_exponent)?;
512        }
513
514        if tp.max_ack_delay != 0 {
515            assert!(tp.max_ack_delay <= octets::MAX_VAR_INT);
516            TransportParams::encode_param(
517                &mut b,
518                0x000b,
519                octets::varint_len(tp.max_ack_delay),
520            )?;
521            b.put_varint(tp.max_ack_delay)?;
522        }
523
524        if tp.disable_active_migration {
525            TransportParams::encode_param(&mut b, 0x000c, 0)?;
526        }
527
528        // TODO: encode preferred_address
529
530        if tp.active_conn_id_limit != 2 {
531            assert!(tp.active_conn_id_limit <= octets::MAX_VAR_INT);
532            TransportParams::encode_param(
533                &mut b,
534                0x000e,
535                octets::varint_len(tp.active_conn_id_limit),
536            )?;
537            b.put_varint(tp.active_conn_id_limit)?;
538        }
539
540        if let Some(scid) = &tp.initial_source_connection_id {
541            TransportParams::encode_param(&mut b, 0x000f, scid.len())?;
542            b.put_bytes(scid)?;
543        }
544
545        if is_server {
546            if let Some(scid) = &tp.retry_source_connection_id {
547                TransportParams::encode_param(&mut b, 0x0010, scid.len())?;
548                b.put_bytes(scid)?;
549            }
550        }
551
552        if let Some(max_datagram_frame_size) = tp.max_datagram_frame_size {
553            assert!(max_datagram_frame_size <= octets::MAX_VAR_INT);
554            TransportParams::encode_param(
555                &mut b,
556                0x0020,
557                octets::varint_len(max_datagram_frame_size),
558            )?;
559            b.put_varint(max_datagram_frame_size)?;
560        }
561
562        let out_len = b.off();
563
564        Ok(&mut out[..out_len])
565    }
566
567    /// Creates a qlog event for connection transport parameters and TLS fields
568    #[cfg(feature = "qlog")]
569    pub fn to_qlog(
570        &self, initiator: TransportInitiator, cipher: Option<crypto::Algorithm>,
571    ) -> EventData {
572        let original_destination_connection_id = qlog::HexSlice::maybe_string(
573            self.original_destination_connection_id.as_ref(),
574        );
575
576        let stateless_reset_token = qlog::HexSlice::maybe_string(
577            self.stateless_reset_token.map(|s| s.to_be_bytes()).as_ref(),
578        );
579
580        let tls_cipher: Option<String> = cipher.map(|f| format!("{f:?}"));
581
582        EventData::QuicParametersSet(Box::new(
583            qlog::events::quic::ParametersSet {
584                initiator: Some(initiator),
585                tls_cipher,
586                original_destination_connection_id,
587                stateless_reset_token,
588                disable_active_migration: Some(self.disable_active_migration),
589                max_idle_timeout: Some(self.max_idle_timeout),
590                max_udp_payload_size: Some(self.max_udp_payload_size),
591                ack_delay_exponent: Some(self.ack_delay_exponent),
592                max_ack_delay: Some(self.max_ack_delay),
593                active_connection_id_limit: Some(self.active_conn_id_limit),
594
595                initial_max_data: Some(self.initial_max_data),
596                initial_max_stream_data_bidi_local: Some(
597                    self.initial_max_stream_data_bidi_local,
598                ),
599                initial_max_stream_data_bidi_remote: Some(
600                    self.initial_max_stream_data_bidi_remote,
601                ),
602                initial_max_stream_data_uni: Some(
603                    self.initial_max_stream_data_uni,
604                ),
605                initial_max_streams_bidi: Some(self.initial_max_streams_bidi),
606                initial_max_streams_uni: Some(self.initial_max_streams_uni),
607
608                unknown_parameters: self
609                    .unknown_params
610                    .as_ref()
611                    .map(|unknown_params| {
612                        unknown_params
613                            .into_iter()
614                            .cloned()
615                            .map(
616                                Into::<
617                                    qlog::events::quic::UnknownTransportParameter,
618                                >::into,
619                            )
620                            .collect()
621                    })
622                    .unwrap_or_default(),
623
624                ..Default::default()
625            },
626        ))
627    }
628}