From 8e69291bb42812c9e57fae14b6f079e4ebc1719c Mon Sep 17 00:00:00 2001 From: Will Hawkins Date: Wed, 16 Oct 2024 10:00:47 -0400 Subject: [PATCH] lib, qlog: Track and Log Unknown Transport Parameters Track (and make available to qlog for logging) transport parameters and their values that this implementation does not specifically recognize. --- qlog/src/events/quic.rs | 9 +++ quiche/src/lib.rs | 165 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 171 insertions(+), 3 deletions(-) diff --git a/qlog/src/events/quic.rs b/qlog/src/events/quic.rs index e504125a1a..5c7d836870 100644 --- a/qlog/src/events/quic.rs +++ b/qlog/src/events/quic.rs @@ -571,6 +571,15 @@ pub struct TransportParametersSet { pub initial_max_streams_uni: Option, pub preferred_address: Option, + + pub unknown_parameters: Vec, +} + +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] +pub struct UnknownTransportParameter { + pub id: u64, + pub value: Vec, } #[serde_with::skip_serializing_none] diff --git a/quiche/src/lib.rs b/quiche/src/lib.rs index bf9c27824e..1661eb190c 100644 --- a/quiche/src/lib.rs +++ b/quiche/src/lib.rs @@ -382,6 +382,7 @@ #[macro_use] extern crate log; +use octets::BufferTooShortError; #[cfg(feature = "qlog")] use qlog::events::connectivity::ConnectivityEventType; #[cfg(feature = "qlog")] @@ -7984,6 +7985,94 @@ impl std::fmt::Debug for Stats { } } +/// QUIC Unknown Transport Parameter +/// +/// A QUIC transport parameter that is not specifically recognized +/// by this implementation. +#[derive(Clone, Debug, PartialEq)] +pub struct UnknownTransportParameter { + /// ID of an unknown transport parameter + pub id: u64, + /// Original data representing the value of an unknown transport parameter + pub value: Vec, +} + +impl From + for qlog::events::quic::UnknownTransportParameter +{ + fn from(value: UnknownTransportParameter) -> Self { + Self { + id: value.id, + value: value.value, + } + } +} + +/// Track unknown transport parameters, up to a limit. +#[derive(Clone, Debug, PartialEq)] +pub struct UnknownTransportParameters { + // The maximum number of bytes available to store the values of unknown + // transport parameters. + /// The space remaining for storing unknown transport parameters. + pub capacity: usize, + /// The unknown transport parameters. + pub parameters: Vec, +} + +impl<'a> Default for UnknownTransportParameters { + fn default() -> Self { + Self { + capacity: Self::MAX_UNKNOWN_TRANSPORT_PARAM_SIZE, + parameters: vec![], + } + } +} + +impl UnknownTransportParameters { + const MAX_UNKNOWN_TRANSPORT_PARAM_SIZE: usize = 256; + + /// Push an unknown transport parameter into storage if there is space + /// remaining. + pub fn safe_push(&mut self, new: UnknownTransportParameter) -> Result<()> { + let new_unknown_tp_size = new.value.len(); + if new_unknown_tp_size < self.capacity { + self.capacity -= new.value.len(); + self.parameters.push(new); + Ok(()) + } else { + Err(BufferTooShortError.into()) + } + } +} + +/// An Iterator over unknown transport parameters. +pub struct UnknownTransportParameterIterator<'a> { + index: usize, + parameters: &'a Vec, +} + +impl<'a> IntoIterator for &'a UnknownTransportParameters { + type IntoIter = UnknownTransportParameterIterator<'a>; + type Item = &'a UnknownTransportParameter; + + fn into_iter(self) -> Self::IntoIter { + UnknownTransportParameterIterator { + index: 0, + parameters: &self.parameters, + } + } +} + +impl<'a> Iterator for UnknownTransportParameterIterator<'a> { + type Item = &'a UnknownTransportParameter; + + fn next(&mut self) -> Option { + let result = self.parameters.get(self.index); + self.index += 1; + result + } +} + /// QUIC Transport Parameters #[derive(Clone, Debug, PartialEq)] pub struct TransportParams { @@ -8023,7 +8112,9 @@ pub struct TransportParams { pub retry_source_connection_id: Option>, /// DATAGRAM frame extension parameter, if any. pub max_datagram_frame_size: Option, - // pub preferred_address: ..., + /// Unknown peer transport parameters and values, if any. + pub unknown_params: UnknownTransportParameters, /* pub preferred_address: + * ..., */ } impl Default for TransportParams { @@ -8046,6 +8137,7 @@ impl Default for TransportParams { initial_source_connection_id: None, retry_source_connection_id: None, max_datagram_frame_size: None, + unknown_params: Default::default(), } } } @@ -8196,8 +8288,15 @@ impl TransportParams { tp.max_datagram_frame_size = Some(val.get_varint()?); }, - // Ignore unknown parameters. - _ => (), + // Track unknown transport parameters specially. + unknown_tp_id => { + tp.unknown_params + .safe_push(UnknownTransportParameter { + id: unknown_tp_id, + value: val.to_vec(), + }) + .unwrap(); + }, } } @@ -8410,6 +8509,12 @@ impl TransportParams { initial_max_streams_uni: Some(self.initial_max_streams_uni), preferred_address: None, + + unknown_parameters: self.unknown_params + .into_iter() + .cloned() + .map(Into::::into) + .collect(), }, ) } @@ -8952,6 +9057,7 @@ mod tests { initial_source_connection_id: Some(b"woot woot".to_vec().into()), retry_source_connection_id: Some(b"retry".to_vec().into()), max_datagram_frame_size: Some(32), + unknown_params: Default::default(), }; let mut raw_params = [42; 256]; @@ -8982,6 +9088,7 @@ mod tests { initial_source_connection_id: Some(b"woot woot".to_vec().into()), retry_source_connection_id: None, max_datagram_frame_size: Some(32), + unknown_params: Default::default(), }; let mut raw_params = [42; 256]; @@ -9029,6 +9136,58 @@ mod tests { ); } + #[test] + fn transport_params_unknown_max_space_respected() { + let mut unknown_params: UnknownTransportParameters = Default::default(); + + let massive_unknown_param = UnknownTransportParameter { + id: 5, + value: vec![0xau8; 280], + }; + let big_unknown_param = UnknownTransportParameter { + id: 5, + value: vec![0xau8; 240], + }; + let little_unknown_param = UnknownTransportParameter { + id: 6, + value: vec![0xau8; 15], + }; + + assert!(unknown_params.safe_push(massive_unknown_param).is_err()); + assert!( + unknown_params.capacity == + UnknownTransportParameters::MAX_UNKNOWN_TRANSPORT_PARAM_SIZE + ); + + unknown_params.safe_push(big_unknown_param).unwrap(); + assert!(unknown_params.capacity == 16); + + unknown_params + .safe_push(little_unknown_param.clone()) + .unwrap(); + assert!(unknown_params.capacity == 1); + + assert!(unknown_params.safe_push(little_unknown_param).is_err()); + + let mut unknown_params_iter = unknown_params.into_iter(); + + let unknown_params_first = unknown_params_iter + .next() + .expect("Should have a 0th element."); + assert!( + unknown_params_first.id == 5 && + unknown_params_first.value == vec![0xau8; 240] + ); + + let unknown_params_second = unknown_params_iter + .next() + .expect("Should have a 1th element."); + assert!( + unknown_params_second.id == 6 && + unknown_params_second.value == vec![0xau8; 15] + ); + } + #[test] fn unknown_version() { let mut config = Config::new(0xbabababa).unwrap();