diff --git a/quinn-proto/src/transport_parameters.rs b/quinn-proto/src/transport_parameters.rs index f39e260f1..8229b8cd5 100644 --- a/quinn-proto/src/transport_parameters.rs +++ b/quinn-proto/src/transport_parameters.rs @@ -25,6 +25,41 @@ use crate::{ RESET_TOKEN_SIZE, TIMER_GRANULARITY, }; +#[repr(u32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum TransportParameterId { + // https://www.rfc-editor.org/rfc/rfc9000.html#iana-tp-table + OriginalDestinationConnectionId = 0x00, + MaxIdleTimeout = 0x01, + StatelessResetToken = 0x02, + MaxUdpPayloadSize = 0x03, + InitialMaxData = 0x04, + InitialMaxStreamDataBidiLocal = 0x05, + InitialMaxStreamDataBidiRemote = 0x06, + InitialMaxStreamDataUni = 0x07, + InitialMaxStreamsBidi = 0x08, + InitialMaxStreamsUni = 0x09, + AckDelayExponent = 0x0A, + MaxAckDelay = 0x0B, + DisableActiveMigration = 0x0C, + PreferredAddress = 0x0D, + ActiveConnectionIdLimit = 0x0E, + InitialSourceConnectionId = 0x0F, + RetrySourceConnectionId = 0x10, + + // Smallest possible ID of reserved transport parameter https://datatracker.ietf.org/doc/html/rfc9000#section-22.3 + ReservedTransportParameter = 0x1B, + + // https://www.rfc-editor.org/rfc/rfc9221.html#section-3 + MaxDatagramFrameSize = 0x20, + + // https://www.rfc-editor.org/rfc/rfc9287.html#section-3 + GreaseQuicBit = 0x2AB2, + + // https://datatracker.ietf.org/doc/html/draft-ietf-quic-ack-frequency#section-10.1 + MinAckDelayDraftIetfQuicAckFrequency07 = 0xFF04DE1B, +} + // Apply a given macro to a list of all the transport parameters having integer types, along with // their codes and default values. Using this helps us avoid error-prone duplication of the // contained information across decoding, encoding, and the `Default` impl. Whenever we want to do @@ -35,42 +70,61 @@ macro_rules! apply_params { $macro! { // #[doc] name (id) = default, /// Milliseconds, disabled if zero - max_idle_timeout(0x0001) = 0, + max_idle_timeout(TransportParameterId::MaxIdleTimeout) = 0, /// Limits the size of UDP payloads that the endpoint is willing to receive - max_udp_payload_size(0x0003) = 65527, + max_udp_payload_size(TransportParameterId::MaxUdpPayloadSize) = 65527, /// Initial value for the maximum amount of data that can be sent on the connection - initial_max_data(0x0004) = 0, + initial_max_data(TransportParameterId::InitialMaxData) = 0, /// Initial flow control limit for locally-initiated bidirectional streams - initial_max_stream_data_bidi_local(0x0005) = 0, + initial_max_stream_data_bidi_local(TransportParameterId::InitialMaxStreamDataBidiLocal) = 0, /// Initial flow control limit for peer-initiated bidirectional streams - initial_max_stream_data_bidi_remote(0x0006) = 0, + initial_max_stream_data_bidi_remote(TransportParameterId::InitialMaxStreamDataBidiRemote) = 0, /// Initial flow control limit for unidirectional streams - initial_max_stream_data_uni(0x0007) = 0, + initial_max_stream_data_uni(TransportParameterId::InitialMaxStreamDataUni) = 0, /// Initial maximum number of bidirectional streams the peer may initiate - initial_max_streams_bidi(0x0008) = 0, + initial_max_streams_bidi(TransportParameterId::InitialMaxStreamsBidi) = 0, /// Initial maximum number of unidirectional streams the peer may initiate - initial_max_streams_uni(0x0009) = 0, + initial_max_streams_uni(TransportParameterId::InitialMaxStreamsUni) = 0, /// Exponent used to decode the ACK Delay field in the ACK frame - ack_delay_exponent(0x000a) = 3, + ack_delay_exponent(TransportParameterId::AckDelayExponent) = 3, /// Maximum amount of time in milliseconds by which the endpoint will delay sending /// acknowledgments - max_ack_delay(0x000b) = 25, + max_ack_delay(TransportParameterId::MaxAckDelay) = 25, /// Maximum number of connection IDs from the peer that an endpoint is willing to store - active_connection_id_limit(0x000e) = 2, + active_connection_id_limit(TransportParameterId::ActiveConnectionIdLimit) = 2, } }; } -const DEFAULT_TRANSPORT_PARAMETERS_ORDER: [u32; 21] = [ - 0x0001, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000e, 27, - 0x02, 0x0c, 0x20, 0x000d, 0x00, 0x0f, 0x10, 0x2ab2, 0xff04de1b, +const DEFAULT_TRANSPORT_PARAMETERS_ORDER: [TransportParameterId; 21] = [ + TransportParameterId::MaxIdleTimeout, + TransportParameterId::MaxUdpPayloadSize, + TransportParameterId::InitialMaxData, + TransportParameterId::InitialMaxStreamDataBidiLocal, + TransportParameterId::InitialMaxStreamDataBidiRemote, + TransportParameterId::InitialMaxStreamDataUni, + TransportParameterId::InitialMaxStreamsBidi, + TransportParameterId::InitialMaxStreamsUni, + TransportParameterId::AckDelayExponent, + TransportParameterId::MaxAckDelay, + TransportParameterId::ActiveConnectionIdLimit, + TransportParameterId::ReservedTransportParameter, + TransportParameterId::StatelessResetToken, + TransportParameterId::DisableActiveMigration, + TransportParameterId::MaxDatagramFrameSize, + TransportParameterId::PreferredAddress, + TransportParameterId::OriginalDestinationConnectionId, + TransportParameterId::InitialSourceConnectionId, + TransportParameterId::RetrySourceConnectionId, + TransportParameterId::GreaseQuicBit, + TransportParameterId::MinAckDelayDraftIetfQuicAckFrequency07, ]; macro_rules! make_struct { - {$($(#[$doc:meta])* $name:ident ($code:expr) = $default:expr,)*} => { + {$($(#[$doc:meta])* $name:ident ($id:path) = $default:expr,)*} => { /// Transport parameters used to negotiate connection-level preferences between peers #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct TransportParameters { @@ -111,7 +165,7 @@ macro_rules! make_struct { pub(crate) grease_transport_parameter: Option, /// The order in which transport parameters are serialized - pub(crate) write_order: Option<[u32; 21]>, + pub(crate) write_order: Option<[TransportParameterId; 21]>, } // We deliberately don't implement the `Default` trait, since that would be public, and @@ -315,85 +369,87 @@ impl TransportParameters { .unwrap_or(&DEFAULT_TRANSPORT_PARAMETERS_ORDER) { match id { - 27 => { + TransportParameterId::ReservedTransportParameter => { if let Some(param) = self.grease_transport_parameter { param.write(w); } } - 0x02 => { + TransportParameterId::StatelessResetToken => { if let Some(ref x) = self.stateless_reset_token { - w.write_var(0x02); + w.write_var(TransportParameterId::StatelessResetToken as u64); w.write_var(16); w.put_slice(x); } } - 0x0c => { + TransportParameterId::DisableActiveMigration => { if self.disable_active_migration { - w.write_var(0x0c); + w.write_var(TransportParameterId::DisableActiveMigration as u64); w.write_var(0); } } - 0x20 => { + TransportParameterId::MaxDatagramFrameSize => { if let Some(x) = self.max_datagram_frame_size { - w.write_var(0x20); + w.write_var(TransportParameterId::MaxDatagramFrameSize as u64); w.write_var(x.size() as u64); w.write(x); } } - 0x0d => { + TransportParameterId::PreferredAddress => { if let Some(ref x) = self.preferred_address { - w.write_var(0x000d); + w.write_var(TransportParameterId::PreferredAddress as u64); w.write_var(x.wire_size() as u64); x.write(w); } } - 0x00 => { + TransportParameterId::OriginalDestinationConnectionId => { if let Some(ref cid) = self.original_dst_cid { - w.write_var(0x00); + w.write_var(TransportParameterId::OriginalDestinationConnectionId as u64); w.write_var(cid.len() as u64); w.put_slice(cid); } } - 0x0f => { + TransportParameterId::InitialSourceConnectionId => { if let Some(ref cid) = self.initial_src_cid { - w.write_var(0x0f); + w.write_var(TransportParameterId::InitialSourceConnectionId as u64); w.write_var(cid.len() as u64); w.put_slice(cid); } } - 0x10 => { + TransportParameterId::RetrySourceConnectionId => { if let Some(ref cid) = self.retry_src_cid { - w.write_var(0x10); + w.write_var(TransportParameterId::RetrySourceConnectionId as u64); w.write_var(cid.len() as u64); w.put_slice(cid); } } - 0x2ab2 => { + TransportParameterId::GreaseQuicBit => { if self.grease_quic_bit { - w.write_var(0x2ab2); + w.write_var(TransportParameterId::GreaseQuicBit as u64); w.write_var(0); } } - 0xff04de1b => { + TransportParameterId::MinAckDelayDraftIetfQuicAckFrequency07 => { if let Some(x) = self.min_ack_delay { - w.write_var(0xff04de1b); + w.write_var( + TransportParameterId::MinAckDelayDraftIetfQuicAckFrequency07 as u64, + ); w.write_var(x.size() as u64); w.write(x); } } id => { macro_rules! write_params { - {$($(#[$doc:meta])* $name:ident ($code:expr) = $default:expr,)*} => { + {$($(#[$doc:meta])* $name:ident ($id:path) = $default:expr,)*} => { match id { - $($code => { + $($id => { if self.$name.0 != $default { - w.write_var($code); + w.write_var($id as u64); w.write(VarInt::try_from(self.$name.size()).unwrap()); w.write(self.$name); } })*, _ => { - unreachable!("Missing implementation of write for transport parameter with code {id:X}"); + unimplemented!("Missing implementation of write for transport parameter with code {id:?}"); } } } @@ -411,7 +467,7 @@ impl TransportParameters { // State to check for duplicate transport parameters. macro_rules! param_state { - {$($(#[$doc:meta])* $name:ident ($code:expr) = $default:expr,)*} => {{ + {$($(#[$doc:meta])* $name:ident ($id:path) = $default:expr,)*} => {{ struct ParamState { $($name: bool,)* } @@ -432,8 +488,10 @@ impl TransportParameters { let len = len as usize; match id { - 0x00 => decode_cid(len, &mut params.original_dst_cid, r)?, - 0x02 => { + id if id == TransportParameterId::OriginalDestinationConnectionId as u64 => { + decode_cid(len, &mut params.original_dst_cid, r)? + } + id if id == TransportParameterId::StatelessResetToken as u64 => { if len != 16 || params.stateless_reset_token.is_some() { return Err(Error::Malformed); } @@ -441,36 +499,42 @@ impl TransportParameters { r.copy_to_slice(&mut tok); params.stateless_reset_token = Some(tok.into()); } - 0x0c => { + id if id == TransportParameterId::DisableActiveMigration as u64 => { if len != 0 || params.disable_active_migration { return Err(Error::Malformed); } params.disable_active_migration = true; } - 0x0d => { + id if id == TransportParameterId::PreferredAddress as u64 => { if params.preferred_address.is_some() { return Err(Error::Malformed); } params.preferred_address = Some(PreferredAddress::read(&mut r.take(len))?); } - 0x0f => decode_cid(len, &mut params.initial_src_cid, r)?, - 0x10 => decode_cid(len, &mut params.retry_src_cid, r)?, - 0x20 => { + id if id == TransportParameterId::InitialSourceConnectionId as u64 => { + decode_cid(len, &mut params.initial_src_cid, r)? + } + id if id == TransportParameterId::RetrySourceConnectionId as u64 => { + decode_cid(len, &mut params.retry_src_cid, r)? + } + id if id == TransportParameterId::MaxDatagramFrameSize as u64 => { if len > 8 || params.max_datagram_frame_size.is_some() { return Err(Error::Malformed); } params.max_datagram_frame_size = Some(r.get().unwrap()); } - 0x2ab2 => match len { + id if id == TransportParameterId::GreaseQuicBit as u64 => match len { 0 => params.grease_quic_bit = true, _ => return Err(Error::Malformed), }, - 0xff04de1b => params.min_ack_delay = Some(r.get().unwrap()), + id if id == TransportParameterId::MinAckDelayDraftIetfQuicAckFrequency07 as u64 => { + params.min_ack_delay = Some(r.get().unwrap()) + } _ => { macro_rules! parse { - {$($(#[$doc:meta])* $name:ident ($code:expr) = $default:expr,)*} => { + {$($(#[$doc:meta])* $name:ident ($id:path) = $default:expr,)*} => { match id { - $($code => { + $(id if $id as u64 == id => { let value = r.get::()?; if len != value.size() || got.$name { return Err(Error::Malformed); } params.$name = value.into();