summaryrefslogtreecommitdiffstats
path: root/third_party/rust/neqo-transport/src/connection/params.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/neqo-transport/src/connection/params.rs')
-rw-r--r--third_party/rust/neqo-transport/src/connection/params.rs348
1 files changed, 348 insertions, 0 deletions
diff --git a/third_party/rust/neqo-transport/src/connection/params.rs b/third_party/rust/neqo-transport/src/connection/params.rs
new file mode 100644
index 0000000000..bb4979fcf1
--- /dev/null
+++ b/third_party/rust/neqo-transport/src/connection/params.rs
@@ -0,0 +1,348 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use crate::connection::{ConnectionIdManager, Role, LOCAL_ACTIVE_CID_LIMIT};
+pub use crate::recovery::FAST_PTO_SCALE;
+use crate::recv_stream::RECV_BUFFER_SIZE;
+use crate::rtt::GRANULARITY;
+use crate::stream_id::StreamType;
+use crate::tparams::{self, PreferredAddress, TransportParameter, TransportParametersHandler};
+use crate::tracking::DEFAULT_ACK_DELAY;
+use crate::version::{Version, VersionConfig};
+use crate::{CongestionControlAlgorithm, Res};
+use std::cmp::max;
+use std::convert::TryFrom;
+use std::time::Duration;
+
+const LOCAL_MAX_DATA: u64 = 0x3FFF_FFFF_FFFF_FFFF; // 2^62-1
+const LOCAL_STREAM_LIMIT_BIDI: u64 = 16;
+const LOCAL_STREAM_LIMIT_UNI: u64 = 16;
+/// See `ConnectionParameters.ack_ratio` for a discussion of this value.
+pub const ACK_RATIO_SCALE: u8 = 10;
+/// By default, aim to have the peer acknowledge 4 times per round trip time.
+/// See `ConnectionParameters.ack_ratio` for more.
+const DEFAULT_ACK_RATIO: u8 = 4 * ACK_RATIO_SCALE;
+/// The local value for the idle timeout period.
+const DEFAULT_IDLE_TIMEOUT: Duration = Duration::from_secs(30);
+const MAX_QUEUED_DATAGRAMS_DEFAULT: usize = 10;
+
+/// What to do with preferred addresses.
+#[derive(Debug, Clone, Copy)]
+pub enum PreferredAddressConfig {
+ /// Disabled, whether for client or server.
+ Disabled,
+ /// Enabled at a client, disabled at a server.
+ Default,
+ /// Enabled at both client and server.
+ Address(PreferredAddress),
+}
+
+/// ConnectionParameters use for setting intitial value for QUIC parameters.
+/// This collects configuration like initial limits, protocol version, and
+/// congestion control algorithm.
+#[derive(Debug, Clone)]
+pub struct ConnectionParameters {
+ versions: VersionConfig,
+ cc_algorithm: CongestionControlAlgorithm,
+ /// Initial connection-level flow control limit.
+ max_data: u64,
+ /// Initial flow control limit for receiving data on bidirectional streams that the peer creates.
+ max_stream_data_bidi_remote: u64,
+ /// Initial flow control limit for receiving data on bidirectional streams that this endpoint creates.
+ max_stream_data_bidi_local: u64,
+ /// Initial flow control limit for receiving data on unidirectional streams that the peer creates.
+ max_stream_data_uni: u64,
+ /// Initial limit on bidirectional streams that the peer creates.
+ max_streams_bidi: u64,
+ /// Initial limit on unidirectional streams that this endpoint creates.
+ max_streams_uni: u64,
+ /// The ACK ratio determines how many acknowledgements we will request as a
+ /// fraction of both the current congestion window (expressed in packets) and
+ /// as a fraction of the current round trip time. This value is scaled by
+ /// `ACK_RATIO_SCALE`; that is, if the goal is to have at least five
+ /// acknowledgments every round trip, set the value to `5 * ACK_RATIO_SCALE`.
+ /// Values less than `ACK_RATIO_SCALE` are clamped to `ACK_RATIO_SCALE`.
+ ack_ratio: u8,
+ /// The duration of the idle timeout for the connection.
+ idle_timeout: Duration,
+ preferred_address: PreferredAddressConfig,
+ datagram_size: u64,
+ outgoing_datagram_queue: usize,
+ incoming_datagram_queue: usize,
+ fast_pto: u8,
+}
+
+impl Default for ConnectionParameters {
+ fn default() -> Self {
+ Self {
+ versions: VersionConfig::default(),
+ cc_algorithm: CongestionControlAlgorithm::NewReno,
+ max_data: LOCAL_MAX_DATA,
+ max_stream_data_bidi_remote: u64::try_from(RECV_BUFFER_SIZE).unwrap(),
+ max_stream_data_bidi_local: u64::try_from(RECV_BUFFER_SIZE).unwrap(),
+ max_stream_data_uni: u64::try_from(RECV_BUFFER_SIZE).unwrap(),
+ max_streams_bidi: LOCAL_STREAM_LIMIT_BIDI,
+ max_streams_uni: LOCAL_STREAM_LIMIT_UNI,
+ ack_ratio: DEFAULT_ACK_RATIO,
+ idle_timeout: DEFAULT_IDLE_TIMEOUT,
+ preferred_address: PreferredAddressConfig::Default,
+ datagram_size: 0,
+ outgoing_datagram_queue: MAX_QUEUED_DATAGRAMS_DEFAULT,
+ incoming_datagram_queue: MAX_QUEUED_DATAGRAMS_DEFAULT,
+ fast_pto: FAST_PTO_SCALE,
+ }
+ }
+}
+
+impl ConnectionParameters {
+ pub fn get_versions(&self) -> &VersionConfig {
+ &self.versions
+ }
+
+ pub(crate) fn get_versions_mut(&mut self) -> &mut VersionConfig {
+ &mut self.versions
+ }
+
+ /// Describe the initial version that should be attempted and all the
+ /// versions that should be enabled. This list should contain the initial
+ /// version and be in order of preference, with more preferred versions
+ /// before less preferred.
+ pub fn versions(mut self, initial: Version, all: Vec<Version>) -> Self {
+ self.versions = VersionConfig::new(initial, all);
+ self
+ }
+
+ pub fn get_cc_algorithm(&self) -> CongestionControlAlgorithm {
+ self.cc_algorithm
+ }
+
+ pub fn cc_algorithm(mut self, v: CongestionControlAlgorithm) -> Self {
+ self.cc_algorithm = v;
+ self
+ }
+
+ pub fn get_max_data(&self) -> u64 {
+ self.max_data
+ }
+
+ pub fn max_data(mut self, v: u64) -> Self {
+ self.max_data = v;
+ self
+ }
+
+ pub fn get_max_streams(&self, stream_type: StreamType) -> u64 {
+ match stream_type {
+ StreamType::BiDi => self.max_streams_bidi,
+ StreamType::UniDi => self.max_streams_uni,
+ }
+ }
+
+ /// # Panics
+ /// If v > 2^60 (the maximum allowed by the protocol).
+ pub fn max_streams(mut self, stream_type: StreamType, v: u64) -> Self {
+ assert!(v <= (1 << 60), "max_streams is too large");
+ match stream_type {
+ StreamType::BiDi => {
+ self.max_streams_bidi = v;
+ }
+ StreamType::UniDi => {
+ self.max_streams_uni = v;
+ }
+ }
+ self
+ }
+
+ /// Get the maximum stream data that we will accept on different types of streams.
+ /// # Panics
+ /// If `StreamType::UniDi` and `false` are passed as that is not a valid combination.
+ pub fn get_max_stream_data(&self, stream_type: StreamType, remote: bool) -> u64 {
+ match (stream_type, remote) {
+ (StreamType::BiDi, false) => self.max_stream_data_bidi_local,
+ (StreamType::BiDi, true) => self.max_stream_data_bidi_remote,
+ (StreamType::UniDi, false) => {
+ panic!("Can't get receive limit on a stream that can only be sent.")
+ }
+ (StreamType::UniDi, true) => self.max_stream_data_uni,
+ }
+ }
+
+ /// Set the maximum stream data that we will accept on different types of streams.
+ /// # Panics
+ /// If `StreamType::UniDi` and `false` are passed as that is not a valid combination
+ /// or if v >= 62 (the maximum allowed by the protocol).
+ pub fn max_stream_data(mut self, stream_type: StreamType, remote: bool, v: u64) -> Self {
+ assert!(v < (1 << 62), "max stream data is too large");
+ match (stream_type, remote) {
+ (StreamType::BiDi, false) => {
+ self.max_stream_data_bidi_local = v;
+ }
+ (StreamType::BiDi, true) => {
+ self.max_stream_data_bidi_remote = v;
+ }
+ (StreamType::UniDi, false) => {
+ panic!("Can't set receive limit on a stream that can only be sent.")
+ }
+ (StreamType::UniDi, true) => {
+ self.max_stream_data_uni = v;
+ }
+ }
+ self
+ }
+
+ /// Set a preferred address (which only has an effect for a server).
+ pub fn preferred_address(mut self, preferred: PreferredAddress) -> Self {
+ self.preferred_address = PreferredAddressConfig::Address(preferred);
+ self
+ }
+
+ /// Disable the use of preferred addresses.
+ pub fn disable_preferred_address(mut self) -> Self {
+ self.preferred_address = PreferredAddressConfig::Disabled;
+ self
+ }
+
+ pub fn get_preferred_address(&self) -> &PreferredAddressConfig {
+ &self.preferred_address
+ }
+
+ pub fn ack_ratio(mut self, ack_ratio: u8) -> Self {
+ self.ack_ratio = ack_ratio;
+ self
+ }
+
+ pub fn get_ack_ratio(&self) -> u8 {
+ self.ack_ratio
+ }
+
+ /// # Panics
+ /// If `timeout` is 2^62 milliseconds or more.
+ pub fn idle_timeout(mut self, timeout: Duration) -> Self {
+ assert!(timeout.as_millis() < (1 << 62), "idle timeout is too long");
+ self.idle_timeout = timeout;
+ self
+ }
+
+ pub fn get_idle_timeout(&self) -> Duration {
+ self.idle_timeout
+ }
+
+ pub fn get_datagram_size(&self) -> u64 {
+ self.datagram_size
+ }
+
+ pub fn datagram_size(mut self, v: u64) -> Self {
+ self.datagram_size = v;
+ self
+ }
+
+ pub fn get_outgoing_datagram_queue(&self) -> usize {
+ self.outgoing_datagram_queue
+ }
+
+ pub fn outgoing_datagram_queue(mut self, v: usize) -> Self {
+ // The max queue length must be at least 1.
+ self.outgoing_datagram_queue = max(v, 1);
+ self
+ }
+
+ pub fn get_incoming_datagram_queue(&self) -> usize {
+ self.incoming_datagram_queue
+ }
+
+ pub fn incoming_datagram_queue(mut self, v: usize) -> Self {
+ // The max queue length must be at least 1.
+ self.incoming_datagram_queue = max(v, 1);
+ self
+ }
+
+ pub fn get_fast_pto(&self) -> u8 {
+ self.fast_pto
+ }
+
+ /// Scale the PTO timer. A value of `FAST_PTO_SCALE` follows the spec, a smaller
+ /// value does not, but produces more probes with the intent of ensuring lower
+ /// latency in the event of tail loss. A value of `FAST_PTO_SCALE/4` is quite
+ /// aggressive. Smaller values (other than zero) are not rejected, but could be
+ /// very wasteful. Values greater than `FAST_PTO_SCALE` delay probes and could
+ /// reduce performance. It should not be possible to increase the PTO timer by
+ /// too much based on the range of valid values, but a maximum value of 255 will
+ /// result in very poor performance.
+ /// Scaling PTO this way does not affect when persistent congestion is declared,
+ /// but may change how many retransmissions are sent before declaring persistent
+ /// congestion.
+ ///
+ /// # Panics
+ /// A value of 0 is invalid and will cause a panic.
+ pub fn fast_pto(mut self, scale: u8) -> Self {
+ assert_ne!(scale, 0);
+ self.fast_pto = scale;
+ self
+ }
+
+ pub fn create_transport_parameter(
+ &self,
+ role: Role,
+ cid_manager: &mut ConnectionIdManager,
+ ) -> Res<TransportParametersHandler> {
+ let mut tps = TransportParametersHandler::new(role, self.versions.clone());
+ // default parameters
+ tps.local.set_integer(
+ tparams::ACTIVE_CONNECTION_ID_LIMIT,
+ u64::try_from(LOCAL_ACTIVE_CID_LIMIT).unwrap(),
+ );
+ tps.local.set_empty(tparams::DISABLE_MIGRATION);
+ tps.local.set_empty(tparams::GREASE_QUIC_BIT);
+ tps.local.set_integer(
+ tparams::MAX_ACK_DELAY,
+ u64::try_from(DEFAULT_ACK_DELAY.as_millis()).unwrap(),
+ );
+ tps.local.set_integer(
+ tparams::MIN_ACK_DELAY,
+ u64::try_from(GRANULARITY.as_micros()).unwrap(),
+ );
+
+ // set configurable parameters
+ tps.local
+ .set_integer(tparams::INITIAL_MAX_DATA, self.max_data);
+ tps.local.set_integer(
+ tparams::INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
+ self.max_stream_data_bidi_local,
+ );
+ tps.local.set_integer(
+ tparams::INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
+ self.max_stream_data_bidi_remote,
+ );
+ tps.local.set_integer(
+ tparams::INITIAL_MAX_STREAM_DATA_UNI,
+ self.max_stream_data_uni,
+ );
+ tps.local
+ .set_integer(tparams::INITIAL_MAX_STREAMS_BIDI, self.max_streams_bidi);
+ tps.local
+ .set_integer(tparams::INITIAL_MAX_STREAMS_UNI, self.max_streams_uni);
+ tps.local.set_integer(
+ tparams::IDLE_TIMEOUT,
+ u64::try_from(self.idle_timeout.as_millis()).unwrap_or(0),
+ );
+ if let PreferredAddressConfig::Address(preferred) = &self.preferred_address {
+ if role == Role::Server {
+ let (cid, srt) = cid_manager.preferred_address_cid()?;
+ tps.local.set(
+ tparams::PREFERRED_ADDRESS,
+ TransportParameter::PreferredAddress {
+ v4: preferred.ipv4(),
+ v6: preferred.ipv6(),
+ cid,
+ srt,
+ },
+ );
+ }
+ }
+ tps.local
+ .set_integer(tparams::MAX_DATAGRAM_FRAME_SIZE, self.datagram_size);
+ Ok(tps)
+ }
+}