#![allow(unused_imports)]
#![allow(dead_code)]
#![allow(unexpected_cfgs)]
use std::cmp;
use std::collections::VecDeque;
use std::fmt;
use std::net::IpAddr;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time;
use std::time::Duration;
use std::time::Instant;
use bytes::Buf;
use bytes::BufMut;
use rand::RngCore;
use ring::aead;
use ring::aead::LessSafeKey;
use ring::aead::UnboundKey;
use ring::hmac;
use rustc_hash::FxHashSet;
use crate::codec::VINT_MAX;
use crate::connection::stream;
use crate::tls::TlsSession;
use crate::token::ResetToken;
use crate::trans_param::TransportParams;
pub const QUIC_VERSION: u32 = QUIC_VERSION_V1;
pub const QUIC_VERSION_V1: u32 = 0x0000_0001;
pub const MAX_CID_LEN: usize = 20;
const MAX_CID_LIMIT: u64 = 8;
const RESET_TOKEN_LEN: usize = 16;
const MIN_RESET_PACKET_LEN: usize = 21;
const MAX_RESET_PACKET_LEN: usize = 42;
const LENGTH_FIELD_LEN: usize = 2;
pub const MIN_CLIENT_INITIAL_LEN: usize = 1200;
const MIN_PAYLOAD_LEN: usize = 4;
const MAX_ACK_RANGES: usize = 68;
const DEFAULT_SEND_UDP_PAYLOAD_SIZE: usize = 1200;
const ANTI_AMPLIFICATION_FACTOR: usize = 3;
pub const TIMER_GRANULARITY: Duration = Duration::from_millis(1);
const MAX_STREAMS_PER_TYPE: u64 = 1 << 60;
const CONNECTION_WINDOW_FACTOR: f64 = 1.5;
const INITIAL_RTT: Duration = Duration::from_millis(333);
const DEFAULT_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(30);
const DEFAULT_PTO_LINEAR_FACTOR: u64 = 0;
const MAX_PTO: Duration = Duration::MAX;
pub type Result<T> = std::result::Result<T, Error>;
#[repr(C)]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub struct ConnectionId {
len: u8,
data: [u8; MAX_CID_LEN],
}
impl ConnectionId {
pub fn new(bytes: &[u8]) -> Self {
let len = cmp::min(bytes.len(), MAX_CID_LEN);
let mut cid = Self {
len: len as u8,
data: [0; MAX_CID_LEN],
};
cid.data[..len].copy_from_slice(&bytes[..len]);
cid
}
pub fn random() -> Self {
Self {
len: MAX_CID_LEN as u8,
data: rand::random::<[u8; MAX_CID_LEN]>(),
}
}
}
impl std::ops::Deref for ConnectionId {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.data[0..self.len as usize]
}
}
impl fmt::Debug for ConnectionId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for b in self.iter() {
write!(f, "{b:02x}")?;
}
Ok(())
}
}
impl fmt::Display for ConnectionId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
pub trait ConnectionIdGenerator {
fn generate(&mut self) -> ConnectionId;
fn cid_len(&self) -> usize;
fn generate_cid_and_token(&mut self, reset_token_key: &hmac::Key) -> (ConnectionId, u128) {
let scid = self.generate();
let reset_token = ResetToken::generate(reset_token_key, &scid);
(scid, reset_token.to_u128())
}
}
#[derive(Debug, Clone, Copy)]
pub struct RandomConnectionIdGenerator {
cid_len: usize,
}
impl RandomConnectionIdGenerator {
pub fn new(cid_len: usize) -> Self {
Self {
cid_len: cmp::min(cid_len, MAX_CID_LEN),
}
}
}
impl ConnectionIdGenerator for RandomConnectionIdGenerator {
fn generate(&mut self) -> ConnectionId {
let mut bytes = [0; MAX_CID_LEN];
rand::thread_rng().fill_bytes(&mut bytes[..self.cid_len]);
ConnectionId::new(&bytes[..self.cid_len])
}
fn cid_len(&self) -> usize {
self.cid_len
}
}
#[derive(Clone, Copy, Debug)]
pub struct PacketInfo {
pub src: SocketAddr,
pub dst: SocketAddr,
pub time: time::Instant,
}
#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)]
pub struct FourTuple {
pub local: SocketAddr,
pub remote: SocketAddr,
}
#[derive(Default)]
pub struct FourTupleIter {
pub(crate) addrs: Vec<FourTuple>,
}
impl Iterator for FourTupleIter {
type Item = FourTuple;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.addrs.pop()
}
}
impl ExactSizeIterator for FourTupleIter {
#[inline]
fn len(&self) -> usize {
self.addrs.len()
}
}
fn version_is_supported(version: u32) -> bool {
matches!(version, QUIC_VERSION_V1)
}
#[derive(Clone)]
pub struct Config {
local_transport_params: TransportParams,
max_handshake_timeout: time::Duration,
max_concurrent_conns: u32,
max_connection_window: u64,
max_stream_window: u64,
retry: bool,
stateless_reset: bool,
address_token_lifetime: Duration,
address_token_key: Vec<LessSafeKey>,
reset_token_key: hmac::Key,
cid_len: usize,
anti_amplification_factor: usize,
send_batch_size: usize,
zerortt_buffer_size: usize,
max_undecryptable_packets: usize,
recovery: RecoveryConfig,
multipath: MultipathConfig,
tls_config_selector: Option<Arc<dyn tls::TlsConfigSelector>>,
}
impl Config {
pub fn new() -> Result<Self> {
let local_transport_params = TransportParams {
initial_max_data: 10485760,
initial_max_stream_data_bidi_local: 5242880,
initial_max_stream_data_bidi_remote: 2097152,
initial_max_stream_data_uni: 1048576,
initial_max_streams_bidi: 200,
initial_max_streams_uni: 100,
..TransportParams::default()
};
let reset_token_key = hmac::Key::new(hmac::HMAC_SHA256, &[]);
Ok(Self {
local_transport_params,
max_handshake_timeout: DEFAULT_HANDSHAKE_TIMEOUT,
max_concurrent_conns: 1000000,
max_connection_window: stream::MAX_CONNECTION_WINDOW,
max_stream_window: stream::MAX_STREAM_WINDOW,
retry: false,
stateless_reset: true,
address_token_lifetime: Duration::from_secs(86400),
address_token_key: Self::rand_address_token_key()?,
reset_token_key,
cid_len: 8,
anti_amplification_factor: ANTI_AMPLIFICATION_FACTOR,
send_batch_size: 64,
zerortt_buffer_size: 1000,
max_undecryptable_packets: 10,
recovery: RecoveryConfig::default(),
multipath: MultipathConfig::default(),
tls_config_selector: None,
})
}
pub fn set_max_idle_timeout(&mut self, v: u64) {
self.local_transport_params.max_idle_timeout = cmp::min(v, VINT_MAX);
}
pub fn set_max_handshake_timeout(&mut self, v: u64) {
self.max_handshake_timeout = time::Duration::from_millis(v);
}
pub fn set_recv_udp_payload_size(&mut self, v: u16) {
self.local_transport_params.max_udp_payload_size = cmp::min(v as u64, VINT_MAX);
}
pub fn enable_dplpmtud(&mut self, v: bool) {
self.recovery.enable_dplpmtud = v;
}
pub fn set_send_udp_payload_size(&mut self, v: usize) {
self.recovery.max_datagram_size = cmp::max(v, DEFAULT_SEND_UDP_PAYLOAD_SIZE);
}
pub fn set_initial_max_data(&mut self, v: u64) {
self.local_transport_params.initial_max_data = cmp::min(v, self.max_connection_window);
}
pub fn set_initial_max_stream_data_bidi_local(&mut self, v: u64) {
self.local_transport_params
.initial_max_stream_data_bidi_local = cmp::min(v, self.max_stream_window);
}
pub fn set_initial_max_stream_data_bidi_remote(&mut self, v: u64) {
self.local_transport_params
.initial_max_stream_data_bidi_remote = cmp::min(v, self.max_stream_window);
}
pub fn set_initial_max_stream_data_uni(&mut self, v: u64) {
self.local_transport_params.initial_max_stream_data_uni =
cmp::min(v, self.max_stream_window);
}
pub fn set_initial_max_streams_bidi(&mut self, v: u64) {
self.local_transport_params.initial_max_streams_bidi = cmp::min(v, VINT_MAX);
}
pub fn set_initial_max_streams_uni(&mut self, v: u64) {
self.local_transport_params.initial_max_streams_uni = cmp::min(v, VINT_MAX);
}
pub fn set_ack_delay_exponent(&mut self, v: u64) {
self.local_transport_params.ack_delay_exponent = cmp::min(v, VINT_MAX);
}
pub fn set_max_ack_delay(&mut self, v: u64) {
self.local_transport_params.max_ack_delay = cmp::min(v, VINT_MAX);
}
pub fn set_ack_eliciting_threshold(&mut self, v: u64) {
self.recovery.ack_eliciting_threshold = v;
}
pub fn set_congestion_control_algorithm(&mut self, cca: CongestionControlAlgorithm) {
self.recovery.congestion_control_algorithm = cca;
}
pub fn set_initial_congestion_window(&mut self, packets: u64) {
self.recovery.initial_congestion_window = packets;
}
pub fn set_min_congestion_window(&mut self, packets: u64) {
self.recovery.min_congestion_window = packets
}
pub fn set_slow_start_thresh(&mut self, packets: u64) {
self.recovery.slow_start_thresh = packets
}
pub fn set_bbr_probe_rtt_duration(&mut self, millis: u64) {
self.recovery.bbr_probe_rtt_duration =
cmp::max(Duration::from_millis(millis), TIMER_GRANULARITY);
}
pub fn enable_bbr_probe_rtt_based_on_bdp(&mut self, v: bool) {
self.recovery.bbr_probe_rtt_based_on_bdp = v;
}
pub fn set_bbr_probe_rtt_cwnd_gain(&mut self, v: f64) {
self.recovery.bbr_probe_rtt_cwnd_gain = v;
}
pub fn set_bbr_rtprop_filter_len(&mut self, millis: u64) {
self.recovery.bbr_rtprop_filter_len =
cmp::max(Duration::from_millis(millis), TIMER_GRANULARITY);
}
pub fn set_bbr_probe_bw_cwnd_gain(&mut self, v: f64) {
self.recovery.bbr_probe_bw_cwnd_gain = v;
}
pub fn set_copa_slow_start_delta(&mut self, v: f64) {
self.recovery.copa_slow_start_delta = v;
}
pub fn set_copa_steady_delta(&mut self, v: f64) {
self.recovery.copa_steady_delta = v;
}
pub fn enable_copa_use_standing_rtt(&mut self, v: bool) {
self.recovery.copa_use_standing_rtt = v;
}
pub fn set_initial_rtt(&mut self, millis: u64) {
self.recovery.initial_rtt = cmp::max(Duration::from_millis(millis), TIMER_GRANULARITY);
}
pub fn enable_pacing(&mut self, v: bool) {
self.recovery.enable_pacing = v;
}
pub fn set_pacing_granularity(&mut self, millis: u64) {
self.recovery.pacing_granularity =
cmp::max(Duration::from_millis(millis), TIMER_GRANULARITY);
}
pub fn set_pto_linear_factor(&mut self, v: u64) {
self.recovery.pto_linear_factor = v;
}
pub fn set_max_pto(&mut self, millis: u64) {
self.recovery.max_pto = cmp::max(Duration::from_millis(millis), TIMER_GRANULARITY);
}
pub fn set_active_connection_id_limit(&mut self, v: u64) {
if v >= 2 {
self.local_transport_params.active_conn_id_limit = cmp::min(v, VINT_MAX);
}
}
pub fn enable_multipath(&mut self, v: bool) {
self.local_transport_params.enable_multipath = v;
}
pub fn set_multipath_algorithm(&mut self, v: MultipathAlgorithm) {
self.multipath.multipath_algorithm = v;
}
pub fn set_max_connection_window(&mut self, v: u64) {
self.max_connection_window = cmp::min(v, VINT_MAX);
}
pub fn set_max_stream_window(&mut self, v: u64) {
self.max_stream_window = cmp::min(v, VINT_MAX);
}
pub fn set_max_concurrent_conns(&mut self, v: u32) {
self.max_concurrent_conns = v;
}
pub fn set_reset_token_key(&mut self, v: [u8; 64]) {
self.reset_token_key = hmac::Key::new(hmac::HMAC_SHA256, &v);
}
pub fn set_address_token_lifetime(&mut self, seconds: u64) {
self.address_token_lifetime = Duration::from_secs(seconds);
}
pub fn set_address_token_key(&mut self, keys: Vec<[u8; 16]>) -> Result<()> {
if keys.is_empty() {
return Err(Error::InvalidConfig("address token key empty".into()));
}
let mut address_token_key = vec![];
for key in keys {
let key = UnboundKey::new(&aead::AES_128_GCM, &key).map_err(|_| Error::CryptoFail)?;
let key = LessSafeKey::new(key);
address_token_key.push(key);
}
self.address_token_key = address_token_key;
Ok(())
}
pub fn enable_retry(&mut self, enable_retry: bool) {
self.retry = enable_retry;
}
pub fn enable_stateless_reset(&mut self, enable_stateless_reset: bool) {
self.stateless_reset = enable_stateless_reset;
}
pub fn set_cid_len(&mut self, v: usize) {
self.cid_len = cmp::min(v, MAX_CID_LEN);
}
pub fn set_anti_amplification_factor(&mut self, v: usize) {
self.anti_amplification_factor = cmp::max(v, ANTI_AMPLIFICATION_FACTOR);
}
pub fn set_send_batch_size(&mut self, v: usize) {
self.send_batch_size = cmp::max(v, 1);
}
pub fn set_zerortt_buffer_size(&mut self, v: usize) {
if v > 0 {
self.zerortt_buffer_size = v;
} else {
self.zerortt_buffer_size = 1000;
}
}
pub fn set_max_undecryptable_packets(&mut self, v: usize) {
if v > 0 {
self.max_undecryptable_packets = v;
} else {
self.max_undecryptable_packets = 10;
}
}
pub fn enable_encryption(&mut self, v: bool) {
self.local_transport_params.disable_encryption = !v;
}
pub fn set_tls_config(&mut self, tls_config: tls::TlsConfig) {
self.set_tls_config_selector(Arc::new(tls::DefaultTlsConfigSelector {
tls_config: Arc::new(tls_config),
}));
}
pub fn set_tls_config_selector(
&mut self,
tls_config_selector: Arc<dyn tls::TlsConfigSelector>,
) {
self.tls_config_selector = Some(tls_config_selector);
}
fn rand_address_token_key() -> Result<Vec<LessSafeKey>> {
let mut key = [0_u8; 16];
rand::thread_rng().fill_bytes(&mut key);
Ok(vec![LessSafeKey::new(
UnboundKey::new(&aead::AES_128_GCM, &key).map_err(|_| Error::CryptoFail)?,
)])
}
fn new_tls_session(&self, server_name: Option<&str>, is_server: bool) -> Result<TlsSession> {
if self.tls_config_selector.is_none() {
return Err(Error::TlsFail("tls config selector is not set".into()));
}
match self.tls_config_selector.as_ref().unwrap().get_default() {
Some(tls_config) => tls_config.new_session(server_name, is_server),
None => Err(Error::TlsFail("get tls config failed".into())),
}
}
}
#[doc(hidden)]
#[derive(Debug, Clone)]
pub struct RecoveryConfig {
pub enable_dplpmtud: bool,
pub max_datagram_size: usize,
max_ack_delay: Duration,
ack_eliciting_threshold: u64,
pub congestion_control_algorithm: CongestionControlAlgorithm,
pub min_congestion_window: u64,
pub initial_congestion_window: u64,
pub slow_start_thresh: u64,
pub bbr_probe_rtt_duration: Duration,
pub bbr_probe_rtt_based_on_bdp: bool,
pub bbr_probe_rtt_cwnd_gain: f64,
pub bbr_rtprop_filter_len: Duration,
pub bbr_probe_bw_cwnd_gain: f64,
pub copa_slow_start_delta: f64,
pub copa_steady_delta: f64,
pub copa_use_standing_rtt: bool,
pub initial_rtt: Duration,
pub enable_pacing: bool,
pub pacing_granularity: Duration,
pub pto_linear_factor: u64,
pub max_pto: Duration,
}
impl Default for RecoveryConfig {
fn default() -> RecoveryConfig {
RecoveryConfig {
enable_dplpmtud: true,
max_datagram_size: DEFAULT_SEND_UDP_PAYLOAD_SIZE, max_ack_delay: time::Duration::from_millis(0),
ack_eliciting_threshold: 2,
congestion_control_algorithm: CongestionControlAlgorithm::Bbr,
min_congestion_window: 2_u64,
initial_congestion_window: 10_u64,
slow_start_thresh: u64::MAX,
bbr_probe_rtt_duration: Duration::from_millis(200),
bbr_probe_rtt_based_on_bdp: false,
bbr_probe_rtt_cwnd_gain: 0.75,
bbr_rtprop_filter_len: Duration::from_secs(10),
bbr_probe_bw_cwnd_gain: 2.0,
copa_slow_start_delta: congestion_control::COPA_DELTA,
copa_steady_delta: congestion_control::COPA_DELTA,
copa_use_standing_rtt: true,
initial_rtt: INITIAL_RTT,
enable_pacing: true,
pacing_granularity: time::Duration::from_millis(1),
pto_linear_factor: DEFAULT_PTO_LINEAR_FACTOR,
max_pto: MAX_PTO,
}
}
}
#[doc(hidden)]
#[derive(Debug, Clone)]
pub struct MultipathConfig {
multipath_algorithm: MultipathAlgorithm,
}
impl Default for MultipathConfig {
fn default() -> MultipathConfig {
MultipathConfig {
multipath_algorithm: MultipathAlgorithm::MinRtt,
}
}
}
enum Event {
ConnectionEstablished,
NewToken(Vec<u8>),
ScidToAdvertise(u8),
ScidRetired(ConnectionId),
DcidAdvertised(ResetToken),
DcidRetired(ResetToken),
ResetTokenAdvertised(ResetToken),
StreamCreated(u64),
StreamClosed(u64),
}
#[derive(Default)]
struct EventQueue(Option<VecDeque<Event>>);
impl EventQueue {
fn enable(&mut self) {
self.0 = Some(VecDeque::new());
}
fn add(&mut self, e: Event) -> bool {
if let Some(events) = &mut self.0 {
events.push_back(e);
return true;
}
false
}
fn poll(&mut self) -> Option<Event> {
if let Some(events) = &mut self.0 {
return events.pop_front();
}
None
}
fn is_empty(&self) -> bool {
if let Some(events) = &self.0 {
return events.is_empty();
}
true
}
}
struct ConnectionQueues {
tickable: FxHashSet<u64>,
sendable: FxHashSet<u64>,
}
impl ConnectionQueues {
fn new() -> Self {
Self {
tickable: FxHashSet::default(),
sendable: FxHashSet::default(),
}
}
fn is_empty(&self) -> bool {
self.tickable.is_empty() && self.sendable.is_empty()
}
fn tickable_next(&self) -> Option<u64> {
self.tickable.iter().next().copied()
}
fn sendable_next(&self) -> Option<u64> {
self.sendable.iter().next().copied()
}
}
pub trait TransportHandler {
fn on_conn_created(&mut self, conn: &mut Connection);
fn on_conn_established(&mut self, conn: &mut Connection);
fn on_conn_closed(&mut self, conn: &mut Connection);
fn on_stream_created(&mut self, conn: &mut Connection, stream_id: u64);
fn on_stream_readable(&mut self, conn: &mut Connection, stream_id: u64);
fn on_stream_writable(&mut self, conn: &mut Connection, stream_id: u64);
fn on_stream_closed(&mut self, conn: &mut Connection, stream_id: u64);
fn on_new_token(&mut self, conn: &mut Connection, token: Vec<u8>);
}
pub trait PacketSendHandler {
fn on_packets_send(&self, pkts: &[(Vec<u8>, PacketInfo)]) -> Result<usize>;
}
#[repr(C)]
#[derive(PartialEq, Eq)]
pub enum Shutdown {
Read = 0,
Write = 1,
}
pub enum PathEvent {
Validated(usize),
Abandoned(usize),
}
#[repr(C)]
#[derive(Default)]
pub struct PathStats {
pub recv_count: u64,
pub recv_bytes: u64,
pub sent_count: u64,
pub sent_bytes: u64,
pub lost_count: u64,
pub lost_bytes: u64,
pub acked_count: u64,
pub acked_bytes: u64,
pub init_cwnd: u64,
pub final_cwnd: u64,
pub max_cwnd: u64,
pub min_cwnd: u64,
pub max_inflight: u64,
pub loss_event_count: u64,
pub cwnd_limited_count: u64,
pub cwnd_limited_duration: u64,
pub min_rtt: u64,
pub max_rtt: u64,
pub srtt: u64,
pub rttvar: u64,
pub in_slow_start: bool,
pub pacing_rate: u64,
}
#[cfg(test)]
mod tests {
use super::*;
#[ctor::ctor]
fn init() {
env_logger::builder()
.filter_level(log::LevelFilter::Trace)
.format_timestamp_millis()
.is_test(true)
.init();
}
#[test]
fn connection_id() {
let mut cid_gen = RandomConnectionIdGenerator::new(8);
let cid = cid_gen.generate();
assert_eq!(cid.len(), cid_gen.cid_len());
let cid = ConnectionId {
len: 4,
data: [0xa8; 20],
};
assert_eq!(format!("{}", cid), "a8a8a8a8");
}
#[test]
fn initial_rtt() -> Result<()> {
let mut config = Config::new()?;
config.set_initial_rtt(0);
assert_eq!(config.recovery.initial_rtt, TIMER_GRANULARITY);
config.set_initial_rtt(100);
assert_eq!(config.recovery.initial_rtt, Duration::from_millis(100));
Ok(())
}
#[test]
fn pto_linear_factor() -> Result<()> {
let mut config = Config::new()?;
assert_eq!(config.recovery.pto_linear_factor, DEFAULT_PTO_LINEAR_FACTOR);
config.set_pto_linear_factor(0);
assert_eq!(config.recovery.pto_linear_factor, 0);
config.set_pto_linear_factor(100);
assert_eq!(config.recovery.pto_linear_factor, 100);
Ok(())
}
#[test]
fn max_pto() -> Result<()> {
let mut config = Config::new()?;
assert_eq!(config.recovery.max_pto, MAX_PTO);
config.set_max_pto(0);
assert_eq!(config.recovery.max_pto, TIMER_GRANULARITY);
config.set_max_pto(300000);
assert_eq!(config.recovery.max_pto, Duration::from_millis(300000));
Ok(())
}
#[test]
fn initial_max_streams_bidi() -> Result<()> {
let mut config = Config::new()?;
config.set_initial_max_streams_bidi(u64::MAX);
assert_eq!(
config.local_transport_params.initial_max_streams_bidi,
VINT_MAX
);
Ok(())
}
}
pub use crate::congestion_control::CongestionControlAlgorithm;
pub use crate::connection::path::Path;
pub use crate::connection::Connection;
pub use crate::endpoint::Endpoint;
pub use crate::error::Error;
pub use crate::multipath_scheduler::MultipathAlgorithm;
pub use crate::packet::PacketHeader;
pub use crate::tls::TlsConfig;
pub use crate::tls::TlsConfigSelector;
#[path = "connection/connection.rs"]
pub mod connection;
#[path = "congestion_control/congestion_control.rs"]
mod congestion_control;
#[path = "multipath_scheduler/multipath_scheduler.rs"]
mod multipath_scheduler;
#[path = "tls/tls.rs"]
mod tls;
#[cfg(feature = "h3")]
#[path = "h3/h3.rs"]
pub mod h3;
#[cfg(feature = "qlog")]
#[path = "qlog/qlog.rs"]
mod qlog;
#[cfg(feature = "ffi")]
mod ffi;
#[cfg(feature = "cbindgen")]
#[path = "h3/connection.rs"]
mod h3_connection;
mod codec;
pub mod endpoint;
pub mod error;
mod frame;
mod packet;
mod ranges;
#[doc(hidden)]
pub mod timer_queue;
mod token;
mod trans_param;
mod window;