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