From f63fd2b811c292ed8b237bed2bf49ffef68fbd33 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Sat, 30 Mar 2019 12:20:20 +0100 Subject: [PATCH] Introduce shared module for widely used types This removes the dependency from connection on endpoint which I always found ugly, and generally shrinks some of our larger modules by extracting types that are not tightly coupled to their those modules' other contents. --- quinn-proto/src/connection.rs | 42 +--- quinn-proto/src/crypto.rs | 3 +- quinn-proto/src/endpoint.rs | 182 +------------- quinn-proto/src/frame.rs | 2 +- quinn-proto/src/lib.rs | 15 +- quinn-proto/src/packet.rs | 107 +------- quinn-proto/src/shared.rs | 313 ++++++++++++++++++++++++ quinn-proto/src/transport_parameters.rs | 2 +- 8 files changed, 342 insertions(+), 324 deletions(-) create mode 100644 quinn-proto/src/shared.rs diff --git a/quinn-proto/src/connection.rs b/quinn-proto/src/connection.rs index 28d45aaf0..6598c382d 100644 --- a/quinn-proto/src/connection.rs +++ b/quinn-proto/src/connection.rs @@ -13,17 +13,19 @@ use slog::Logger; use crate::assembler::Assembler; use crate::coding::BufMutExt; use crate::crypto::{ - self, reset_token_for, Crypto, CryptoClientConfig, CryptoSession, HeaderCrypto, - RingHeaderCrypto, TlsSession, ACK_DELAY_EXPONENT, + reset_token_for, Crypto, CryptoClientConfig, CryptoSession, HeaderCrypto, RingHeaderCrypto, + TlsSession, ACK_DELAY_EXPONENT, }; use crate::dedup::Dedup; -use crate::endpoint::TransportConfig; use crate::frame::FrameStruct; use crate::packet::{ - set_payload_length, ConnectionId, EcnCodepoint, Header, LongType, Packet, PacketNumber, - PartialDecode, SpaceId, LONG_RESERVED_BITS, SHORT_RESERVED_BITS, + set_payload_length, Header, LongType, Packet, PacketNumber, PartialDecode, SpaceId, + LONG_RESERVED_BITS, SHORT_RESERVED_BITS, }; use crate::range_set::RangeSet; +use crate::shared::{ + ClientConfig, ConnectionEvent, ConnectionId, EcnCodepoint, EndpointEvent, TransportConfig, +}; use crate::stream::{self, ReadError, Streams, WriteError}; use crate::transport_parameters::{self, TransportParameters}; use crate::{ @@ -3106,12 +3108,6 @@ mod state { } } -#[derive(Clone)] -pub struct ClientConfig { - pub server_name: String, - pub tls_config: Arc, -} - /// Represents one or more packets subject to retransmission #[derive(Debug, Clone)] struct SentPacket { @@ -3134,30 +3130,6 @@ struct SentPacket { /// Ensures we can always fit all our ACKs in a single minimum-MTU packet with room to spare const MAX_ACK_BLOCKS: usize = 64; -/// Events to be sent to the Connection -pub enum ConnectionEvent { - Datagram { - now: Instant, - remote: SocketAddr, - ecn: Option, - first_decode: PartialDecode, - remaining: Option, - }, - NewIdentifiers(Vec<(u64, ConnectionId)>), - Timer(Instant, Timer), -} - -/// Events to be sent to the Endpoint -#[derive(Clone, Debug)] -pub enum EndpointEvent { - Closed { - remote: SocketAddr, - }, - NeedIdentifiers, - /// Stop routing connection ID for this sequence number to this `Connection` - RetireConnectionId(u64), -} - /// Encoding of I/O operations to emit on upcoming `poll_io` calls #[derive(Debug)] struct IoQueue { diff --git a/quinn-proto/src/crypto.rs b/quinn-proto/src/crypto.rs index 645624d40..e97513ec5 100644 --- a/quinn-proto/src/crypto.rs +++ b/quinn-proto/src/crypto.rs @@ -17,7 +17,8 @@ pub use rustls::{ClientConfig, ClientSession, ServerConfig, ServerSession, Sessi use webpki::DNSNameRef; use crate::coding::{BufExt, BufMutExt}; -use crate::packet::{ConnectionId, PacketNumber, LONG_HEADER_FORM}; +use crate::packet::{PacketNumber, LONG_HEADER_FORM}; +use crate::shared::ConnectionId; use crate::transport_parameters::TransportParameters; use crate::{ConnectError, Side, TransportError, MAX_CID_SIZE, MIN_CID_SIZE, RESET_TOKEN_SIZE}; diff --git a/quinn-proto/src/endpoint.rs b/quinn-proto/src/endpoint.rs index 43a195459..238c0d198 100644 --- a/quinn-proto/src/endpoint.rs +++ b/quinn-proto/src/endpoint.rs @@ -1,4 +1,3 @@ -use std::cmp; use std::collections::{HashMap, VecDeque}; use std::net::SocketAddr; use std::ops::{Index, IndexMut}; @@ -15,16 +14,20 @@ use slab::Slab; use slog::{self, Logger}; use crate::coding::BufMutExt; -use crate::connection::{initial_close, ClientConfig, Connection, ConnectionEvent, EndpointEvent}; +use crate::connection::{initial_close, Connection}; use crate::crypto::{ self, reset_token_for, Crypto, CryptoClientConfig, CryptoServerConfig, RingHeaderCrypto, TokenKey, }; -use crate::packet::{ConnectionId, EcnCodepoint, Header, Packet, PacketDecodeError, PartialDecode}; +use crate::packet::{Header, Packet, PacketDecodeError, PartialDecode}; +use crate::shared::{ + ClientConfig, ConfigError, ConnectionEvent, ConnectionId, EcnCodepoint, EndpointEvent, + TransportConfig, +}; use crate::transport_parameters::TransportParameters; use crate::{ - varint, Side, Transmit, TransportError, LOC_CID_COUNT, MAX_CID_SIZE, MIN_CID_SIZE, - MIN_INITIAL_SIZE, RESET_TOKEN_SIZE, VERSION, + Side, Transmit, TransportError, LOC_CID_COUNT, MAX_CID_SIZE, MIN_CID_SIZE, MIN_INITIAL_SIZE, + RESET_TOKEN_SIZE, VERSION, }; /// The main entry point to the library @@ -592,163 +595,6 @@ pub(crate) struct ConnectionMeta { loc_cids: HashMap, } -/// Parameters governing the core QUIC state machine -/// -/// This should be tuned to suit the application. In particular, window sizes for streams, stream -/// data, and overall connection data should be set differently depending on the expected round trip -/// time, link capacity, memory availability, and rate of stream creation. Tuning for higher -/// bandwidths and latencies increases worst-case memory consumption, but does not impair -/// performance at lower bandwidths and latencies. The default configuration is tuned for a 100Mbps -/// link with a 100ms round trip time, with remote endpoints opening at most 320 new streams per -/// second. Applications which do not require remotely-initiated streams should set the stream -/// windows to zero. -pub struct TransportConfig { - /// Maximum number of bidirectional streams that may be initiated by the peer but not yet - /// accepted locally - /// - /// Must be nonzero for the peer to open any bidirectional streams. - /// - /// Any number of streams may be in flight concurrently. However, to ensure predictable resource - /// use, the number of streams which the peer has initiated but which the local application has - /// not yet accepted will be kept below this threshold. - /// - /// Because it takes at least one round trip for an endpoint to open a new stream and be - /// notified of its peer's flow control updates, this imposes a hard upper bound on the number - /// of streams that may be opened per round-trip. In other words, this should be set to at least - /// the desired number of streams opened per unit time, multiplied by the round trip time. - /// - /// Note that worst-case memory use is directly proportional to `stream_window_bidi * - /// stream_receive_window`, with an upper bound proportional to `receive_window`. - pub stream_window_bidi: u64, - /// Variant of `stream_window_bidi` affecting unidirectional streams - pub stream_window_uni: u64, - /// Maximum duration of inactivity to accept before timing out the connection (ms). - /// - /// The actual value used is the minimum of this and the peer's own idle timeout. 0 for none. - pub idle_timeout: u64, - /// Maximum number of bytes the peer may transmit without acknowledgement on any one stream - /// before becoming blocked. - /// - /// This should be set to at least the expected connection latency multiplied by the maximum - /// desired throughput. Setting this smaller than `receive_window` helps ensure that a single - /// stream doesn't monopolize receive buffers, which may otherwise occur if the application - /// chooses not to read from a large stream for a time while still requiring data on other - /// streams. - pub stream_receive_window: u64, - /// Maximum number of bytes the peer may transmit across all streams of a connection before - /// becoming blocked. - /// - /// This should be set to at least the expected connection latency multiplied by the maximum - /// desired throughput. Larger values can be useful to allow maximum throughput within a - /// stream while another is blocked. - pub receive_window: u64, - /// Maximum number of bytes to transmit to a peer without acknowledgment - /// - /// Provides an upper bound on memory when communicating with peers that issue large amounts of - /// flow control credit. Endpoints that wish to handle large numbers of connections robustly - /// should take care to set this low enough to guarantee memory exhaustion does not occur if - /// every connection uses the entire window. - pub send_window: u64, - - /// Maximum number of tail loss probes before an RTO fires. - pub max_tlps: u32, - /// Maximum reordering in packet number space before FACK style loss detection considers a - /// packet lost. - pub packet_threshold: u32, - /// Maximum reordering in time space before time based loss detection considers a packet lost. - /// 0.16 format, added to 1 - pub time_threshold: u16, - /// The length of the peer’s delayed ack timer (μs). - pub delayed_ack_timeout: u64, - /// The RTT used before an RTT sample is taken (μs) - pub initial_rtt: u64, - - /// The max packet size that was used for calculating default and minimum congestion windows. - pub max_datagram_size: u64, - /// Default limit on the amount of outstanding data in bytes. - /// - /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14600))` - pub initial_window: u64, - /// Default minimum congestion window. - /// - /// Recommended value: `2 * max_datagram_size`. - pub minimum_window: u64, - /// Reduction in congestion window when a new loss event is detected. 0.16 format - pub loss_reduction_factor: u16, - /// Number of consecutive PTOs after which network is considered to be experiencing persistent congestion. - pub persistent_congestion_threshold: u32, - /// Number of milliseconds of inactivity before sending a keep-alive packet - /// - /// Keep-alive packets prevent an inactive but otherwise healthy connection from timing out. - /// - /// 0 to disable, which is the default. Only one side of any given connection needs keep-alive - /// enabled for the connection to be preserved. Must be set lower than the idle_timeout of both - /// peers to be effective. - pub keep_alive_interval: u32, -} - -impl Default for TransportConfig { - fn default() -> Self { - const EXPECTED_RTT: u64 = 100; // ms - const MAX_STREAM_BANDWIDTH: u64 = 12500 * 1000; // bytes/s - // Window size needed to avoid pipeline - // stalls - const STREAM_RWND: u64 = MAX_STREAM_BANDWIDTH / 1000 * EXPECTED_RTT; - const MAX_DATAGRAM_SIZE: u64 = 1200; - - TransportConfig { - stream_window_bidi: 32, - stream_window_uni: 32, - idle_timeout: 10_000, - stream_receive_window: STREAM_RWND, - receive_window: 8 * STREAM_RWND, - send_window: 8 * STREAM_RWND, - - max_tlps: 2, - packet_threshold: 3, - time_threshold: 0x2000, // 1/8 - delayed_ack_timeout: 25 * 1000, - initial_rtt: EXPECTED_RTT as u64 * 1000, - - max_datagram_size: MAX_DATAGRAM_SIZE, - initial_window: cmp::min( - 10 * MAX_DATAGRAM_SIZE, - cmp::max(2 * MAX_DATAGRAM_SIZE, 14600), - ), - minimum_window: 2 * MAX_DATAGRAM_SIZE, - loss_reduction_factor: 0x8000, // 1/2 - persistent_congestion_threshold: 2, - keep_alive_interval: 0, - } - } -} - -impl TransportConfig { - fn validate(&self, log: &Logger) -> Result<(), ConfigError> { - if let Some((name, _)) = [ - ("stream_window_bidi", self.stream_window_bidi), - ("stream_window_uni", self.stream_window_uni), - ("receive_window", self.receive_window), - ("stream_receive_window", self.stream_receive_window), - ("idle_timeout", self.idle_timeout), - ] - .iter() - .find(|&&(_, x)| x > varint::MAX_VALUE) - { - return Err(ConfigError::VarIntBounds(name)); - } - if self.idle_timeout != 0 && self.keep_alive_interval as u64 >= self.idle_timeout { - warn!( - log, - "keep-alive interval {} is ineffective due to lower idle timeout {}", - self.keep_alive_interval, - self.idle_timeout - ); - } - Ok(()) - } -} - /// Global configuration for the endpoint, affecting all connections pub struct EndpointConfig { /// Length of connection IDs for the endpoint. @@ -834,18 +680,6 @@ impl Default for ServerConfig { } } -/// Errors in the configuration of an endpoint -#[derive(Debug, Error)] -pub enum ConfigError { - /// The supplied configuration contained an invalid value - #[error(display = "illegal configuration value: {}", _0)] - IllegalValue(&'static str), - /// A configuration field that will be encoded as a variable-length integer exceeds the 0..2^62 - /// range - #[error(display = "{} must be at most 2^62-1", _0)] - VarIntBounds(&'static str), -} - #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct ConnectionHandle(pub usize); diff --git a/quinn-proto/src/frame.rs b/quinn-proto/src/frame.rs index 5ada82999..df913c482 100644 --- a/quinn-proto/src/frame.rs +++ b/quinn-proto/src/frame.rs @@ -4,8 +4,8 @@ use std::{fmt, io, mem}; use bytes::{Buf, BufMut, Bytes}; use crate::coding::{self, BufExt, BufMutExt, UnexpectedEnd}; -use crate::packet::EcnCodepoint; use crate::range_set::RangeSet; +use crate::shared::EcnCodepoint; use crate::{ varint, ConnectionId, Directionality, StreamId, TransportError, TransportErrorCode, MAX_CID_SIZE, MIN_CID_SIZE, RESET_TOKEN_SIZE, diff --git a/quinn-proto/src/lib.rs b/quinn-proto/src/lib.rs index 4925fe239..baed01188 100644 --- a/quinn-proto/src/lib.rs +++ b/quinn-proto/src/lib.rs @@ -18,6 +18,7 @@ use std::time::Duration; mod assembler; pub mod coding; mod dedup; +mod packet; mod range_set; #[cfg(test)] mod tests; @@ -25,10 +26,7 @@ mod transport_parameters; pub mod varint; mod connection; -pub use crate::connection::{ - Connection, ConnectionError, ConnectionEvent, EndpointEvent, Event, Timer, TimerSetting, - TimerUpdate, -}; +pub use crate::connection::{Connection, ConnectionError, Event, Timer, TimerSetting, TimerUpdate}; mod crypto; pub use crate::crypto::{ClientConfig, TokenKey}; @@ -39,12 +37,13 @@ pub use crate::frame::{ApplicationClose, ConnectionClose}; mod endpoint; pub use crate::endpoint::{ - ConfigError, ConnectError, ConnectionHandle, DatagramEvent, Endpoint, EndpointConfig, - ServerConfig, TransportConfig, + ConnectError, ConnectionHandle, DatagramEvent, Endpoint, EndpointConfig, ServerConfig, }; -mod packet; -pub use crate::packet::{ConnectionId, EcnCodepoint}; +mod shared; +pub use crate::shared::{ + ConfigError, ConnectionEvent, ConnectionId, EcnCodepoint, EndpointEvent, TransportConfig, +}; mod stream; pub use crate::stream::{ReadError, WriteError}; diff --git a/quinn-proto/src/packet.rs b/quinn-proto/src/packet.rs index ec09d56dc..eb2d77872 100644 --- a/quinn-proto/src/packet.rs +++ b/quinn-proto/src/packet.rs @@ -1,14 +1,14 @@ -use std::{cmp::Ordering, fmt, io, ops::Range, str}; +use std::{cmp::Ordering, io, ops::Range, str}; use bytes::{BigEndian, Buf, BufMut, ByteOrder, Bytes, BytesMut}; use err_derive::Error; -use rand::Rng; use slog; use crate::coding::{self, BufExt, BufMutExt}; use crate::crypto::{HeaderCrypto, RingHeaderCrypto}; +use crate::shared::ConnectionId; use crate::varint; -use crate::{MAX_CID_SIZE, MIN_CID_SIZE, VERSION}; +use crate::VERSION; // Due to packet number encryption, it is impossible to fully decode a header // (which includes a variable-length packet number) without crypto context. @@ -770,80 +770,6 @@ impl From for PacketDecodeError { } } -/// Protocol-level identifier for a connection. -/// -/// Mainly useful for identifying this connection's packets on the wire with tools like Wireshark. -#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct ConnectionId { - pub len: u8, - pub bytes: [u8; MAX_CID_SIZE], -} - -impl ConnectionId { - pub fn new(bytes: &[u8]) -> Self { - debug_assert!( - bytes.is_empty() || (bytes.len() >= MIN_CID_SIZE && bytes.len() <= MAX_CID_SIZE) - ); - let mut res = Self { - len: bytes.len() as u8, - bytes: [0; MAX_CID_SIZE], - }; - res.bytes[..bytes.len()].clone_from_slice(&bytes); - res - } - - pub fn random(rng: &mut R, len: usize) -> Self { - debug_assert!(len <= MAX_CID_SIZE); - let mut res = Self { - len: len as u8, - bytes: [0; MAX_CID_SIZE], - }; - let mut rng_bytes = [0; MAX_CID_SIZE]; - rng.fill_bytes(&mut rng_bytes); - res.bytes[..len].clone_from_slice(&rng_bytes[..len]); - res - } -} - -impl ::std::ops::Deref for ConnectionId { - type Target = [u8]; - fn deref(&self) -> &[u8] { - &self.bytes[0..self.len as usize] - } -} - -impl ::std::ops::DerefMut for ConnectionId { - fn deref_mut(&mut self) -> &mut [u8] { - &mut self.bytes[0..self.len as usize] - } -} - -impl fmt::Debug for ConnectionId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.bytes[0..self.len as usize].fmt(f) - } -} - -impl fmt::Display for ConnectionId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for byte in self.iter() { - write!(f, "{:02x}", byte)?; - } - Ok(()) - } -} - -impl slog::Value for ConnectionId { - fn serialize( - &self, - _: &slog::Record<'_>, - key: slog::Key, - serializer: &mut dyn slog::Serializer, - ) -> slog::Result { - serializer.emit_arguments(key, &format_args!("{}", self)) - } -} - pub fn set_payload_length(packet: &mut [u8], header_len: usize, pn_len: usize, tag_len: usize) { let len = packet.len() - header_len + pn_len + tag_len; assert!(len < 2usize.pow(14)); // Fits in reserved space @@ -860,33 +786,6 @@ pub const SHORT_RESERVED_BITS: u8 = 0x18; pub const LONG_RESERVED_BITS: u8 = 0x0c; const KEY_PHASE_BIT: u8 = 0x04; -/// Explicit congestion notification codepoint -#[repr(u8)] -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum EcnCodepoint { - ECT0 = 0b10, - ECT1 = 0b01, - CE = 0b11, -} - -impl EcnCodepoint { - pub fn from_bits(x: u8) -> Option { - use self::EcnCodepoint::*; - Some(match x & 0b11 { - 0b10 => ECT0, - 0b01 => ECT1, - 0b11 => CE, - _ => { - return None; - } - }) - } - - pub fn bits(self) -> u8 { - self as u8 - } -} - /// Packet number space identifiers #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] pub enum SpaceId { diff --git a/quinn-proto/src/shared.rs b/quinn-proto/src/shared.rs new file mode 100644 index 000000000..a485010e3 --- /dev/null +++ b/quinn-proto/src/shared.rs @@ -0,0 +1,313 @@ +use std::net::SocketAddr; +use std::sync::Arc; +use std::time::Instant; +use std::{cmp, fmt}; + +use bytes::BytesMut; +use err_derive::Error; +use rand::Rng; +use slog::Logger; + +use crate::connection::Timer; +use crate::packet::PartialDecode; +use crate::{crypto, varint, MAX_CID_SIZE, MIN_CID_SIZE}; + +/// Parameters governing the core QUIC state machine +/// +/// This should be tuned to suit the application. In particular, window sizes for streams, stream +/// data, and overall connection data should be set differently depending on the expected round trip +/// time, link capacity, memory availability, and rate of stream creation. Tuning for higher +/// bandwidths and latencies increases worst-case memory consumption, but does not impair +/// performance at lower bandwidths and latencies. The default configuration is tuned for a 100Mbps +/// link with a 100ms round trip time, with remote endpoints opening at most 320 new streams per +/// second. Applications which do not require remotely-initiated streams should set the stream +/// windows to zero. +pub struct TransportConfig { + /// Maximum number of bidirectional streams that may be initiated by the peer but not yet + /// accepted locally + /// + /// Must be nonzero for the peer to open any bidirectional streams. + /// + /// Any number of streams may be in flight concurrently. However, to ensure predictable resource + /// use, the number of streams which the peer has initiated but which the local application has + /// not yet accepted will be kept below this threshold. + /// + /// Because it takes at least one round trip for an endpoint to open a new stream and be + /// notified of its peer's flow control updates, this imposes a hard upper bound on the number + /// of streams that may be opened per round-trip. In other words, this should be set to at least + /// the desired number of streams opened per unit time, multiplied by the round trip time. + /// + /// Note that worst-case memory use is directly proportional to `stream_window_bidi * + /// stream_receive_window`, with an upper bound proportional to `receive_window`. + pub stream_window_bidi: u64, + /// Variant of `stream_window_bidi` affecting unidirectional streams + pub stream_window_uni: u64, + /// Maximum duration of inactivity to accept before timing out the connection (ms). + /// + /// The actual value used is the minimum of this and the peer's own idle timeout. 0 for none. + pub idle_timeout: u64, + /// Maximum number of bytes the peer may transmit without acknowledgement on any one stream + /// before becoming blocked. + /// + /// This should be set to at least the expected connection latency multiplied by the maximum + /// desired throughput. Setting this smaller than `receive_window` helps ensure that a single + /// stream doesn't monopolize receive buffers, which may otherwise occur if the application + /// chooses not to read from a large stream for a time while still requiring data on other + /// streams. + pub stream_receive_window: u64, + /// Maximum number of bytes the peer may transmit across all streams of a connection before + /// becoming blocked. + /// + /// This should be set to at least the expected connection latency multiplied by the maximum + /// desired throughput. Larger values can be useful to allow maximum throughput within a + /// stream while another is blocked. + pub receive_window: u64, + /// Maximum number of bytes to transmit to a peer without acknowledgment + /// + /// Provides an upper bound on memory when communicating with peers that issue large amounts of + /// flow control credit. Endpoints that wish to handle large numbers of connections robustly + /// should take care to set this low enough to guarantee memory exhaustion does not occur if + /// every connection uses the entire window. + pub send_window: u64, + + /// Maximum number of tail loss probes before an RTO fires. + pub max_tlps: u32, + /// Maximum reordering in packet number space before FACK style loss detection considers a + /// packet lost. + pub packet_threshold: u32, + /// Maximum reordering in time space before time based loss detection considers a packet lost. + /// 0.16 format, added to 1 + pub time_threshold: u16, + /// The length of the peer’s delayed ack timer (μs). + pub delayed_ack_timeout: u64, + /// The RTT used before an RTT sample is taken (μs) + pub initial_rtt: u64, + + /// The max packet size that was used for calculating default and minimum congestion windows. + pub max_datagram_size: u64, + /// Default limit on the amount of outstanding data in bytes. + /// + /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14600))` + pub initial_window: u64, + /// Default minimum congestion window. + /// + /// Recommended value: `2 * max_datagram_size`. + pub minimum_window: u64, + /// Reduction in congestion window when a new loss event is detected. 0.16 format + pub loss_reduction_factor: u16, + /// Number of consecutive PTOs after which network is considered to be experiencing persistent congestion. + pub persistent_congestion_threshold: u32, + /// Number of milliseconds of inactivity before sending a keep-alive packet + /// + /// Keep-alive packets prevent an inactive but otherwise healthy connection from timing out. + /// + /// 0 to disable, which is the default. Only one side of any given connection needs keep-alive + /// enabled for the connection to be preserved. Must be set lower than the idle_timeout of both + /// peers to be effective. + pub keep_alive_interval: u32, +} + +impl Default for TransportConfig { + fn default() -> Self { + const EXPECTED_RTT: u64 = 100; // ms + const MAX_STREAM_BANDWIDTH: u64 = 12500 * 1000; // bytes/s + // Window size needed to avoid pipeline + // stalls + const STREAM_RWND: u64 = MAX_STREAM_BANDWIDTH / 1000 * EXPECTED_RTT; + const MAX_DATAGRAM_SIZE: u64 = 1200; + + TransportConfig { + stream_window_bidi: 32, + stream_window_uni: 32, + idle_timeout: 10_000, + stream_receive_window: STREAM_RWND, + receive_window: 8 * STREAM_RWND, + send_window: 8 * STREAM_RWND, + + max_tlps: 2, + packet_threshold: 3, + time_threshold: 0x2000, // 1/8 + delayed_ack_timeout: 25 * 1000, + initial_rtt: EXPECTED_RTT as u64 * 1000, + + max_datagram_size: MAX_DATAGRAM_SIZE, + initial_window: cmp::min( + 10 * MAX_DATAGRAM_SIZE, + cmp::max(2 * MAX_DATAGRAM_SIZE, 14600), + ), + minimum_window: 2 * MAX_DATAGRAM_SIZE, + loss_reduction_factor: 0x8000, // 1/2 + persistent_congestion_threshold: 2, + keep_alive_interval: 0, + } + } +} + +impl TransportConfig { + pub(crate) fn validate(&self, log: &Logger) -> Result<(), ConfigError> { + if let Some((name, _)) = [ + ("stream_window_bidi", self.stream_window_bidi), + ("stream_window_uni", self.stream_window_uni), + ("receive_window", self.receive_window), + ("stream_receive_window", self.stream_receive_window), + ("idle_timeout", self.idle_timeout), + ] + .iter() + .find(|&&(_, x)| x > varint::MAX_VALUE) + { + return Err(ConfigError::VarIntBounds(name)); + } + if self.idle_timeout != 0 && self.keep_alive_interval as u64 >= self.idle_timeout { + warn!( + log, + "keep-alive interval {} is ineffective due to lower idle timeout {}", + self.keep_alive_interval, + self.idle_timeout + ); + } + Ok(()) + } +} + +/// Errors in the configuration of an endpoint +#[derive(Debug, Error)] +pub enum ConfigError { + /// The supplied configuration contained an invalid value + #[error(display = "illegal configuration value: {}", _0)] + IllegalValue(&'static str), + /// A configuration field that will be encoded as a variable-length integer exceeds the 0..2^62 + /// range + #[error(display = "{} must be at most 2^62-1", _0)] + VarIntBounds(&'static str), +} + +/// Events to be sent to the Connection +pub enum ConnectionEvent { + Datagram { + now: Instant, + remote: SocketAddr, + ecn: Option, + first_decode: PartialDecode, + remaining: Option, + }, + NewIdentifiers(Vec<(u64, ConnectionId)>), + Timer(Instant, Timer), +} + +/// Events to be sent to the Endpoint +#[derive(Clone, Debug)] +pub enum EndpointEvent { + Closed { + remote: SocketAddr, + }, + NeedIdentifiers, + /// Stop routing connection ID for this sequence number to this `Connection` + RetireConnectionId(u64), +} + +/// Protocol-level identifier for a connection. +/// +/// Mainly useful for identifying this connection's packets on the wire with tools like Wireshark. +#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct ConnectionId { + pub len: u8, + pub bytes: [u8; MAX_CID_SIZE], +} + +impl ConnectionId { + pub fn new(bytes: &[u8]) -> Self { + debug_assert!( + bytes.is_empty() || (bytes.len() >= MIN_CID_SIZE && bytes.len() <= MAX_CID_SIZE) + ); + let mut res = Self { + len: bytes.len() as u8, + bytes: [0; MAX_CID_SIZE], + }; + res.bytes[..bytes.len()].clone_from_slice(&bytes); + res + } + + pub fn random(rng: &mut R, len: usize) -> Self { + debug_assert!(len <= MAX_CID_SIZE); + let mut res = Self { + len: len as u8, + bytes: [0; MAX_CID_SIZE], + }; + let mut rng_bytes = [0; MAX_CID_SIZE]; + rng.fill_bytes(&mut rng_bytes); + res.bytes[..len].clone_from_slice(&rng_bytes[..len]); + res + } +} + +impl ::std::ops::Deref for ConnectionId { + type Target = [u8]; + fn deref(&self) -> &[u8] { + &self.bytes[0..self.len as usize] + } +} + +impl ::std::ops::DerefMut for ConnectionId { + fn deref_mut(&mut self) -> &mut [u8] { + &mut self.bytes[0..self.len as usize] + } +} + +impl fmt::Debug for ConnectionId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.bytes[0..self.len as usize].fmt(f) + } +} + +impl fmt::Display for ConnectionId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for byte in self.iter() { + write!(f, "{:02x}", byte)?; + } + Ok(()) + } +} + +impl slog::Value for ConnectionId { + fn serialize( + &self, + _: &slog::Record<'_>, + key: slog::Key, + serializer: &mut dyn slog::Serializer, + ) -> slog::Result { + serializer.emit_arguments(key, &format_args!("{}", self)) + } +} + +/// Explicit congestion notification codepoint +#[repr(u8)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum EcnCodepoint { + ECT0 = 0b10, + ECT1 = 0b01, + CE = 0b11, +} + +impl EcnCodepoint { + pub fn from_bits(x: u8) -> Option { + use self::EcnCodepoint::*; + Some(match x & 0b11 { + 0b10 => ECT0, + 0b01 => ECT1, + 0b11 => CE, + _ => { + return None; + } + }) + } + + pub fn bits(self) -> u8 { + self as u8 + } +} + +#[derive(Clone)] +pub struct ClientConfig { + pub server_name: String, + pub tls_config: Arc, +} diff --git a/quinn-proto/src/transport_parameters.rs b/quinn-proto/src/transport_parameters.rs index 8f007be6f..2a0ec9d81 100644 --- a/quinn-proto/src/transport_parameters.rs +++ b/quinn-proto/src/transport_parameters.rs @@ -4,7 +4,7 @@ use bytes::{Buf, BufMut}; use err_derive::Error; use crate::coding::{BufExt, BufMutExt, UnexpectedEnd}; -use crate::packet::ConnectionId; +use crate::shared::ConnectionId; use crate::{ varint, Side, TransportConfig, TransportError, MAX_CID_SIZE, MIN_CID_SIZE, RESET_TOKEN_SIZE, VERSION,