Skip to content

Commit

Permalink
quinn-rs#2057: Store IDs of transport parameters in enum.
Browse files Browse the repository at this point in the history
  • Loading branch information
mstyura committed Nov 25, 2024
1 parent a750a82 commit 1f78a7b
Showing 1 changed file with 114 additions and 52 deletions.
166 changes: 114 additions & 52 deletions quinn-proto/src/transport_parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
MinAckDelayDraft07 = 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
Expand All @@ -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(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(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(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(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(InitialMaxStreamDataBidiRemote) = 0,
/// Initial flow control limit for unidirectional streams
initial_max_stream_data_uni(0x0007) = 0,
initial_max_stream_data_uni(InitialMaxStreamDataUni) = 0,

/// Initial maximum number of bidirectional streams the peer may initiate
initial_max_streams_bidi(0x0008) = 0,
initial_max_streams_bidi(InitialMaxStreamsBidi) = 0,
/// Initial maximum number of unidirectional streams the peer may initiate
initial_max_streams_uni(0x0009) = 0,
initial_max_streams_uni(InitialMaxStreamsUni) = 0,

/// Exponent used to decode the ACK Delay field in the ACK frame
ack_delay_exponent(0x000a) = 3,
ack_delay_exponent(AckDelayExponent) = 3,
/// Maximum amount of time in milliseconds by which the endpoint will delay sending
/// acknowledgments
max_ack_delay(0x000b) = 25,
max_ack_delay(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(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::MinAckDelayDraft07,
];

macro_rules! make_struct {
{$($(#[$doc:meta])* $name:ident ($code:expr) = $default:expr,)*} => {
{$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
/// Transport parameters used to negotiate connection-level preferences between peers
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct TransportParameters {
Expand Down Expand Up @@ -111,7 +165,7 @@ macro_rules! make_struct {
pub(crate) grease_transport_parameter: Option<ReservedTransportParameter>,

/// 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
Expand Down Expand Up @@ -309,91 +363,91 @@ impl From<UnexpectedEnd> for Error {
impl TransportParameters {
/// Encode `TransportParameters` into buffer
pub fn write<W: BufMut>(&self, w: &mut W) {
for id in self
for &id in self
.write_order
.as_ref()
.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(id 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(id 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(id 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(id 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(id 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(id 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(id 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(id as u64);
w.write_var(0);
}
}
0xff04de1b => {
TransportParameterId::MinAckDelayDraft07 => {
if let Some(x) = self.min_ack_delay {
w.write_var(0xff04de1b);
w.write_var(id 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:ident) = $default:expr,)*} => {
match id {
$($code => {
$(TransportParameterId::$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:?}");
}
}
}
Expand All @@ -411,7 +465,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:ident) = $default:expr,)*} => {{
struct ParamState {
$($name: bool,)*
}
Expand All @@ -432,45 +486,53 @@ 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);
}
let mut tok = [0; RESET_TOKEN_SIZE];
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::MinAckDelayDraft07 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:ident) = $default:expr,)*} => {
match id {
$($code => {
$(id if TransportParameterId::$id as u64 == id => {
let value = r.get::<VarInt>()?;
if len != value.size() || got.$name { return Err(Error::Malformed); }
params.$name = value.into();
Expand Down

0 comments on commit 1f78a7b

Please sign in to comment.