Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/tuic-server/src/wind_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,9 @@ async fn create_quiche_inbound(ctx: &Arc<TuicAppContext>) -> eyre::Result<Tuiche
send_window: quiche.send_window,
receive_window: quiche.receive_window,
congestion_control,
// This (legacy) adapter does not expose per-algorithm CC tuning; keep
// quiche's defaults.
cc: Default::default(),
udp_relay_mode: wind_tuic::quiche::UdpRelayMode::Datagram,
enable_0rtt: quiche.zero_rtt,
};
Expand Down
9 changes: 7 additions & 2 deletions crates/wind-quic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ eyre = "0.6"
thiserror = "2"
tracing = "0.1"
pin-project = "1"
# Congestion-control tuning structs in `config` derive serde so config
# front-ends (e.g. the TUIC server) can deserialize them directly.
serde = { version = "1", features = ["derive"] }

# QUIC with quinn
quinn = { workspace = true, default-features = false, features = ["runtime-tokio"], optional = true }
Expand All @@ -66,8 +69,10 @@ aws-lc-rs = { version = "*", default-features = false, optional = true }
# HTTP/3 (masquerade) — transport-agnostic h3 over the `QuicConnection` adapter.
h3 = { version = "0.0.8", optional = true }

# QUIC with quiche / tokio-quiche
tokio-quiche = { version = "0.19", optional = true }
# QUIC with quiche / tokio-quiche. `quiche_internal` unlocks
# `quiche::Config::set_custom_bbr_params` (gated behind quiche's `internal`
# feature) so the quiche backend can apply per-connection BBR tuning.
tokio-quiche = { version = "0.19", optional = true, features = ["quiche_internal"] }
boring = { version = "4", default-features = false, optional = true }
boring-sys = { version = "4", default-features = false, optional = true }
foreign-types-shared = { version = "0.3", optional = true }
Expand Down
116 changes: 116 additions & 0 deletions crates/wind-quic/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,118 @@

use std::time::Duration;

use serde::{Deserialize, Serialize};
pub use wind_core::quic::QuicCongestionControl;

/// BBR bandwidth-`lo` reduction strategy on a congestion event.
///
/// Backend-neutral mirror of quiche's experimental `BbrBwLoReductionStrategy`;
/// the quiche backend maps it onto the native enum.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BbrBwLoReductionStrategy {
/// Default strategy based on `BBRBeta`.
Default,
/// Use min-rtt to estimate bandwidth reduction.
MinRtt,
/// Use inflight data to estimate bandwidth reduction.
Inflight,
/// Use cwnd to estimate bandwidth reduction.
Cwnd,
}

/// Custom BBR (`bbr2_gcongestion`) tuning, the full surface of quiche's
/// experimental `BbrParams`.
///
/// Every field is optional; `None` leaves quiche's built-in default in place.
/// These knobs are *experimental* (the upstream type is `#[doc(hidden)]` and
/// may be removed) and only take effect on the quiche backend with the BBR
/// congestion controller. Lives in this backend-neutral layer because it must
/// travel through [`TransportConfig`] to reach the quiche backend — the only
/// backend that consumes it.
#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields, rename_all = "snake_case")]
pub struct Bbr2gcConfig {
/// BBR startup cwnd gain.
pub startup_cwnd_gain: Option<f32>,
/// BBR startup pacing gain.
pub startup_pacing_gain: Option<f32>,
/// BBR full-bandwidth threshold.
pub full_bw_threshold: Option<f32>,
/// Rounds to stay in STARTUP before exiting on a bandwidth plateau.
pub startup_full_bw_rounds: Option<usize>,
/// Loss count needed to exit STARTUP.
pub startup_full_loss_count: Option<usize>,
/// BBR drain cwnd gain.
pub drain_cwnd_gain: Option<f32>,
/// BBR drain pacing gain.
pub drain_pacing_gain: Option<f32>,
/// Respect Reno coexistence.
pub enable_reno_coexistence: Option<bool>,
/// Avoid overestimating bandwidth on ack compression.
pub enable_overestimate_avoidance: Option<bool>,
/// Enable the `a0` point fix in the bandwidth sampler.
pub choose_a0_point_fix: Option<bool>,
/// PROBE_BW up-phase pacing gain.
pub probe_bw_probe_up_pacing_gain: Option<f32>,
/// PROBE_BW down-phase pacing gain.
pub probe_bw_probe_down_pacing_gain: Option<f32>,
/// PROBE_BW DOWN/CRUISE/REFILL cwnd gain.
pub probe_bw_cwnd_gain: Option<f32>,
/// PROBE_BW UP cwnd gain.
pub probe_bw_up_cwnd_gain: Option<f32>,
/// PROBE_RTT pacing gain.
pub probe_rtt_pacing_gain: Option<f32>,
/// PROBE_RTT cwnd gain.
pub probe_rtt_cwnd_gain: Option<f32>,
/// Rounds to stay in PROBE_BW up if bytes-in-flight doesn't drop below
/// target.
pub max_probe_up_queue_rounds: Option<usize>,
/// BBR loss threshold.
pub loss_threshold: Option<f32>,
/// Use bytes-delivered as the estimate for `inflight_hi`.
pub use_bytes_delivered_for_inflight_hi: Option<bool>,
/// Decrease startup pacing at round end.
pub decrease_startup_pacing_at_end_of_round: Option<bool>,
/// Bandwidth-`lo` reduction strategy.
pub bw_lo_reduction_strategy: Option<BbrBwLoReductionStrategy>,
/// Count app-limited rounds with no bandwidth growth toward the
/// exit-startup rounds threshold.
pub ignore_app_limited_for_no_bandwidth_growth: Option<bool>,
/// Initial pacing rate (bytes/sec) before an RTT estimate is available.
pub initial_pacing_rate_bytes_per_second: Option<u64>,
/// Scale the pacing rate when the MSS changes during PMTUD.
pub scale_pacing_rate_by_mss: Option<bool>,
/// Disable the `has_stayed_long_enough_in_probe_down` early exit.
pub disable_probe_down_early_exit: Option<bool>,
/// Set the expected packet send time to `now` instead of the computed
/// next-release time.
pub time_sent_set_to_now: Option<bool>,
}

/// Backend-neutral congestion-control tuning carried by [`TransportConfig`],
/// alongside the algorithm selector ([`TransportConfig::congestion`]).
///
/// Each backend applies the subset it understands. Today only the quiche
/// backend consumes these; the quinn backend reads
/// [`TransportConfig::initial_window`] and ignores the rest.
#[derive(Clone, Debug, Default)]
pub struct CongestionTuning {
/// Initial congestion window, in packets (quiche). `None` = backend
/// default.
pub initial_cwnd_packets: Option<usize>,
/// Enable pacing of outgoing packets (quiche).
pub pacing: Option<bool>,
/// Maximum pacing rate, in bytes/sec (quiche). `None` = unlimited.
pub max_pacing_rate: Option<u64>,
/// Enable HyStart++ — only with cubic/reno (quiche).
pub hystart: Option<bool>,
/// Enable the CUBIC idle-restart fix — only with cubic (quiche).
pub cubic_idle_restart_fix: Option<bool>,
/// Custom BBR parameters — only with the BBR controller (quiche).
pub bbr: Option<Bbr2gcConfig>,
}

/// QUIC transport tuning, shared by both backends.
#[derive(Clone, Debug)]
pub struct TransportConfig {
Expand All @@ -31,7 +141,12 @@ pub struct TransportConfig {
/// Congestion-control algorithm.
pub congestion: QuicCongestionControl,
/// Initial congestion window in bytes. `None` uses the backend default.
/// (quinn; the quiche backend uses
/// [`CongestionTuning::initial_cwnd_packets`]).
pub initial_window: Option<u64>,
/// Per-algorithm congestion-control tuning. Each backend applies the subset
/// it supports.
pub cc: CongestionTuning,
/// Advertise QUIC DATAGRAM (RFC 9221) support.
pub enable_datagram: bool,
/// Allow 0-RTT early data (resumption).
Expand All @@ -53,6 +168,7 @@ impl Default for TransportConfig {
gso: false,
congestion: QuicCongestionControl::default(),
initial_window: None,
cc: CongestionTuning::default(),
enable_datagram: true,
enable_0rtt: false,
alpn: vec![b"h3".to_vec()],
Expand Down
4 changes: 3 additions & 1 deletion crates/wind-quic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ pub mod error;
pub mod prefixed;
pub mod traits;

pub use config::{CertSource, ClientTlsConfig, ServerTlsConfig, TransportConfig};
pub use config::{
Bbr2gcConfig, BbrBwLoReductionStrategy, CertSource, ClientTlsConfig, CongestionTuning, ServerTlsConfig, TransportConfig,
};
pub use error::{QuicError, Result};
pub use prefixed::PrefixedRecv;
pub use traits::{QuicConnection, QuicRecvStream, QuicSendStream};
Expand Down
64 changes: 63 additions & 1 deletion crates/wind-quic/src/quiche/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use tokio_quiche::{
ConnectionParams,
metrics::DefaultMetrics,
quic::connect_with_config,
settings::{CertificateKind, Hooks, QuicSettings, TlsCertificatePaths},
settings::{BbrParamsField, CertificateKind, Hooks, QuicSettings, TlsCertificatePaths},
socket::Socket,
};
use tracing::warn;
Expand All @@ -46,6 +46,47 @@ fn cc_name(cc: crate::config::QuicCongestionControl) -> &'static str {
}
}

/// Convert the backend-neutral [`Bbr2gcConfig`](crate::config::Bbr2gcConfig)
/// onto quiche's native experimental `BbrParams`.
fn quiche_bbr_params(c: &crate::config::Bbr2gcConfig) -> tokio_quiche::quiche::BbrParams {
use tokio_quiche::quiche::BbrBwLoReductionStrategy as QuicheStrategy;

use crate::config::BbrBwLoReductionStrategy as Strategy;
tokio_quiche::quiche::BbrParams {
startup_cwnd_gain: c.startup_cwnd_gain,
startup_pacing_gain: c.startup_pacing_gain,
full_bw_threshold: c.full_bw_threshold,
startup_full_bw_rounds: c.startup_full_bw_rounds,
startup_full_loss_count: c.startup_full_loss_count,
drain_cwnd_gain: c.drain_cwnd_gain,
drain_pacing_gain: c.drain_pacing_gain,
enable_reno_coexistence: c.enable_reno_coexistence,
enable_overestimate_avoidance: c.enable_overestimate_avoidance,
choose_a0_point_fix: c.choose_a0_point_fix,
probe_bw_probe_up_pacing_gain: c.probe_bw_probe_up_pacing_gain,
probe_bw_probe_down_pacing_gain: c.probe_bw_probe_down_pacing_gain,
probe_bw_cwnd_gain: c.probe_bw_cwnd_gain,
probe_bw_up_cwnd_gain: c.probe_bw_up_cwnd_gain,
probe_rtt_pacing_gain: c.probe_rtt_pacing_gain,
probe_rtt_cwnd_gain: c.probe_rtt_cwnd_gain,
max_probe_up_queue_rounds: c.max_probe_up_queue_rounds,
loss_threshold: c.loss_threshold,
use_bytes_delivered_for_inflight_hi: c.use_bytes_delivered_for_inflight_hi,
decrease_startup_pacing_at_end_of_round: c.decrease_startup_pacing_at_end_of_round,
bw_lo_reduction_strategy: c.bw_lo_reduction_strategy.map(|s| match s {
Strategy::Default => QuicheStrategy::Default,
Strategy::MinRtt => QuicheStrategy::MinRttReduction,
Strategy::Inflight => QuicheStrategy::InflightReduction,
Strategy::Cwnd => QuicheStrategy::CwndReduction,
}),
ignore_app_limited_for_no_bandwidth_growth: c.ignore_app_limited_for_no_bandwidth_growth,
initial_pacing_rate_bytes_per_second: c.initial_pacing_rate_bytes_per_second,
scale_pacing_rate_by_mss: c.scale_pacing_rate_by_mss,
disable_probe_down_early_exit: c.disable_probe_down_early_exit,
time_sent_set_to_now: c.time_sent_set_to_now,
}
}

/// Translate the backend-neutral [`TransportConfig`] into a [`QuicSettings`].
fn quic_settings(t: &TransportConfig) -> QuicSettings {
let mut s = QuicSettings::default();
Expand All @@ -62,6 +103,27 @@ fn quic_settings(t: &TransportConfig) -> QuicSettings {
s.enable_dgram = t.enable_datagram;
s.enable_early_data = t.enable_0rtt;
s.alpn = t.alpn.clone();

// Per-algorithm congestion-control tuning. `None` leaves quiche's default.
let cc = &t.cc;
if let Some(packets) = cc.initial_cwnd_packets {
s.initial_congestion_window_packets = packets;
}
if let Some(pacing) = cc.pacing {
s.enable_pacing = pacing;
}
if let Some(rate) = cc.max_pacing_rate {
s.max_pacing_rate = Some(rate);
}
if let Some(hystart) = cc.hystart {
s.enable_hystart = hystart;
}
if let Some(fix) = cc.cubic_idle_restart_fix {
s.enable_cubic_idle_restart_fix = fix;
}
if let Some(bbr) = &cc.bbr {
s.custom_bbr_params = BbrParamsField(Some(quiche_bbr_params(bbr)));
}
s
}

Expand Down
6 changes: 6 additions & 0 deletions crates/wind-tuic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@

pub mod proto;

// Backend-neutral congestion-control config types (defined in `wind-quic`),
// re-exported so config front-ends (e.g. the TUIC server) can build a
// [`CongestionTuning`] and per-algorithm tuning without depending on
// `wind-quic` directly. Available regardless of which backend feature is on.
pub use wind_quic::{Bbr2gcConfig, BbrBwLoReductionStrategy, CongestionTuning};

#[cfg(feature = "server")]
pub mod active;
#[cfg(feature = "server")]
Expand Down
7 changes: 6 additions & 1 deletion crates/wind-tuic/src/quiche/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
time::Duration,
};

use wind_quic::QuicCongestionControl;
use wind_quic::{CongestionTuning, QuicCongestionControl};

/// Congestion control algorithm.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
Expand Down Expand Up @@ -79,6 +79,9 @@ pub struct ConnectionOpts {
pub receive_window: u64,
/// Congestion control algorithm.
pub congestion_control: CongestionControl,
/// Per-algorithm congestion-control tuning (initial window, pacing,
/// HyStart++, CUBIC idle-restart-fix, custom BBR params).
pub cc: CongestionTuning,
/// UDP relay mode.
pub udp_relay_mode: UdpRelayMode,
/// Enable 0-RTT.
Expand All @@ -94,6 +97,7 @@ impl Default for ConnectionOpts {
send_window: 8 * 1024 * 1024, // 8 MB
receive_window: 8 * 1024 * 1024, // 8 MB
congestion_control: CongestionControl::default(),
cc: CongestionTuning::default(),
udp_relay_mode: UdpRelayMode::default(),
enable_0rtt: true,
}
Expand All @@ -114,6 +118,7 @@ impl ConnectionOpts {
enable_datagram: matches!(self.udp_relay_mode, UdpRelayMode::Datagram),
enable_0rtt: self.enable_0rtt,
alpn: vec![b"h3".to_vec()],
cc: self.cc.clone(),
..Default::default()
}
}
Expand Down
8 changes: 8 additions & 0 deletions crates/wind-tuic/src/quinn/inbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ pub struct TuicInboundOpts {
/// of slow-start faster instead of trickling the first few round trips.
pub initial_window: u64,

/// NewReno loss-reduction factor (β). Only applies to the `newreno`
/// controller; `None` keeps quinn's default. Other controllers ignore it.
pub newreno_loss_reduction_factor: Option<f32>,

/// HTTP/3 masquerade. When `Some`, connections that aren't TUIC (their
/// first stream byte isn't `0x05`) are served as a reverse-proxy HTTP/3
/// web server instead of being dropped.
Expand Down Expand Up @@ -106,6 +110,7 @@ impl Default for TuicInboundOpts {
gso: true,
congestion_control: CongestionControl::Bbr,
initial_window: 1024 * 1024,
newreno_loss_reduction_factor: None,
masquerade: None,
hooks: InboundHooks::default(),
active: None,
Expand Down Expand Up @@ -200,6 +205,9 @@ impl TuicInbound {
CongestionControl::NewReno => {
let mut cfg = quinn::congestion::NewRenoConfig::default();
cfg.initial_window(iw);
if let Some(factor) = self.opts.newreno_loss_reduction_factor {
cfg.loss_reduction_factor(factor);
}
Arc::new(cfg)
}
}
Expand Down
11 changes: 11 additions & 0 deletions patches/tokio-quiche/src/settings/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,17 @@ fn make_quiche_config(
config.discover_pmtu(quic_settings.discover_path_mtu);
config.set_pmtud_max_probes(quic_settings.pmtud_max_probes);
config.enable_hystart(quic_settings.enable_hystart);
config.set_enable_cubic_idle_restart_fix(
quic_settings.enable_cubic_idle_restart_fix,
);

// Custom BBR (gcongestion) tuning. `set_custom_bbr_params` is gated behind
// quiche's `internal` feature (our `quiche_internal`); when that feature is
// off the params are simply ignored so the crate still builds.
#[cfg(feature = "quiche_internal")]
if let Some(bbr_params) = quic_settings.custom_bbr_params.0 {
config.set_custom_bbr_params(bbr_params);
}

config.enable_pacing(quic_settings.enable_pacing);
if let Some(max_pacing_rate) = quic_settings.max_pacing_rate {
Expand Down
Loading
Loading