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
12 changes: 11 additions & 1 deletion neqo-bin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@
}

#[derive(Clone, Debug, Parser)]
#[expect(
clippy::struct_excessive_bools,
reason = "A config struct is not a good use of that lint."
)]
pub struct QuicParameters {
#[arg(
short = 'Q',
Expand Down Expand Up @@ -165,6 +169,10 @@
/// Whether to slice the SNI.
pub no_sni_slicing: bool,

#[arg(long)]
/// Whether to disable Alternative Backoff with ECN (RFC 8511).
pub no_abe: bool,

#[arg(name = "preferred-address-v4", long)]
/// An IPv4 address for the server preferred address.
pub preferred_address_v4: Option<String>,
Expand All @@ -191,6 +199,7 @@
preferred_address_v4: None,
preferred_address_v6: None,
no_sni_slicing: false,
no_abe: false,
}
}
}
Expand Down Expand Up @@ -257,16 +266,17 @@

#[must_use]
pub fn get(&self, alpn: &str) -> ConnectionParameters {
let mut params = ConnectionParameters::default()
.max_streams(StreamType::BiDi, self.max_streams_bidi)
.max_streams(StreamType::UniDi, self.max_streams_uni)
.idle_timeout(Duration::from_secs(self.idle_timeout))
.initial_rtt(Duration::from_millis(self.initial_rtt_ms))
.congestion_control(self.congestion_control)
.slow_start(self.slow_start)
.pacing(!self.no_pacing)
.pmtud(!self.no_pmtud)

Check warning on line 277 in neqo-bin/src/lib.rs

View workflow job for this annotation

GitHub Actions / Find mutants

Missed mutant

delete ! in QuicParameters::get
.sni_slicing(!self.no_sni_slicing);
.sni_slicing(!self.no_sni_slicing)

Check warning on line 278 in neqo-bin/src/lib.rs

View workflow job for this annotation

GitHub Actions / Find mutants

Missed mutant

delete ! in QuicParameters::get
.abe(!self.no_abe);

Check warning on line 279 in neqo-bin/src/lib.rs

View workflow job for this annotation

GitHub Actions / Find mutants

Missed mutant

delete ! in QuicParameters::get
params = if let Some(pa) = self.preferred_address() {
params.preferred_address(pa)
} else {
Expand Down
24 changes: 22 additions & 2 deletions neqo-transport/src/cc/cubic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,26 @@ impl Display for State {
}
}

#[derive(Debug, Default)]
#[derive(Debug)]
pub struct Cubic {
/// Current CUBIC parameters.
current: State,
/// CUBIC parameters that have been stored on a congestion event to restore later in case it
/// turns out to have been spurious.
stored: Option<State>,
/// Whether to apply the alternative backoff factor [`Self::BETA_USIZE_DIVIDEND_ECN`] on ECN-CE
/// congestion events (RFC 8511).
abe: bool,
}

impl Default for Cubic {
fn default() -> Self {
Self {
current: State::default(),
stored: None,
abe: true,
}
}
}

impl Display for Cubic {
Expand Down Expand Up @@ -186,6 +199,13 @@ impl Cubic {
/// construct the beta value from `85/100`.
pub const BETA_USIZE_DIVIDEND_ECN: usize = 85;

/// Enable or disable Alternative Backoff with ECN (RFC 8511). Enabled by default.
#[must_use]
pub const fn abe(mut self, abe: bool) -> Self {
self.abe = abe;
self
}

/// This is the factor that is used by fast convergence to further reduce the next `W_max` when
/// a congestion event occurs while `cwnd < W_max`. This speeds up the bandwidth release for
/// when a new flow joins the network.
Expand Down Expand Up @@ -458,7 +478,7 @@ impl WindowAdjustment for Cubic {

// Reducing the congestion window and resetting time
self.current.t_epoch = None;
let beta_dividend = if congestion_trigger == Ecn {
let beta_dividend = if self.abe && congestion_trigger == Ecn {
Self::BETA_USIZE_DIVIDEND_ECN
} else {
Self::BETA_USIZE_DIVIDEND
Expand Down
53 changes: 41 additions & 12 deletions neqo-transport/src/cc/tests/cubic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::{
use neqo_common::to_u64;
use test_fixture::now;

use super::{RTT, make_cc_cubic};
use super::{RTT, make_cc_cubic, make_cc_cubic_with};
use crate::{
cc::{
ClassicSlowStart, CongestionController as _,
Expand Down Expand Up @@ -48,7 +48,18 @@ fn setup_congestion_avoidance(
ClassicCongestionController<ClassicSlowStart, Cubic>,
CongestionControlStats,
) {
let mut cc = make_cc_cubic();
setup_congestion_avoidance_with(Cubic::default(), fast_convergence)
}

/// Like [`setup_congestion_avoidance`], but with the given Cubic instance.
fn setup_congestion_avoidance_with(
cubic: Cubic,
fast_convergence: bool,
) -> (
ClassicCongestionController<ClassicSlowStart, Cubic>,
CongestionControlStats,
) {
let mut cc = make_cc_cubic_with(cubic);
let mut cc_stats = CongestionControlStats::default();
// Enter congestion avoidance phase.
cc.set_ssthresh(1);
Expand Down Expand Up @@ -335,16 +346,17 @@ fn congestion_event_congestion_avoidance() {
assert_eq!(cc_stats.congestion_events.loss, 1);
}

/// Verify that `acked_bytes` is correctly reduced on a congestion event.
fn acked_bytes_reduced_on_congestion_event(
/// Verify that `cwnd` and `acked_bytes` are correctly reduced on a congestion event.
fn cwnd_and_acked_bytes_reduced_on_congestion_event(
cubic: Cubic,
trigger: impl FnOnce(
&mut ClassicCongestionController<ClassicSlowStart, Cubic>,
Instant,
&mut CongestionControlStats,
),
beta: usize,
) {
let (mut cubic, mut cc_stats) = setup_congestion_avoidance(false);
let (mut cubic, mut cc_stats) = setup_congestion_avoidance_with(cubic, false);

// The helper acked packet 0. Ack one more to accumulate acked_bytes.
let now = now() + RTT / 10;
Expand All @@ -360,27 +372,44 @@ fn acked_bytes_reduced_on_congestion_event(
// Trigger the congestion event.
trigger(&mut cubic, now, &mut cc_stats);

// Verify acked_bytes was reduced by the correct factor.
let expected = acked_bytes_before * beta / Cubic::BETA_USIZE_DIVISOR;
assert_eq!(cubic.acked_bytes(), expected);
// Verify cwnd and acked_bytes were reduced by the correct factor.
assert_eq!(
cubic.cwnd(),
cubic.cwnd_initial() * beta / Cubic::BETA_USIZE_DIVISOR
);
assert_eq!(
cubic.acked_bytes(),
acked_bytes_before * beta / Cubic::BETA_USIZE_DIVISOR
);
}

#[test]
fn acked_bytes_reduced_on_loss() {
acked_bytes_reduced_on_congestion_event(
fn cwnd_and_acked_bytes_reduced_on_loss() {
cwnd_and_acked_bytes_reduced_on_congestion_event(
Cubic::default(),
|cc, _, stats| packet_lost(cc, 2, stats),
Cubic::BETA_USIZE_DIVIDEND,
);
}

#[test]
fn acked_bytes_reduced_on_ecn_ce() {
acked_bytes_reduced_on_congestion_event(
fn cwnd_and_acked_bytes_reduced_on_ecn_ce() {
cwnd_and_acked_bytes_reduced_on_congestion_event(
Cubic::default(),
|cc, now, stats| ecn_ce(cc, 2, now, stats),
Cubic::BETA_USIZE_DIVIDEND_ECN,
);
}

#[test]
fn cwnd_and_acked_bytes_reduced_on_ecn_ce_without_abe() {
cwnd_and_acked_bytes_reduced_on_congestion_event(
Cubic::default().abe(false),
|cc, now, stats| ecn_ce(cc, 2, now, stats),
Cubic::BETA_USIZE_DIVIDEND,
);
}

#[test]
fn congestion_event_congestion_avoidance_fast_convergence() {
let (mut cubic, mut cc_stats) = setup_congestion_avoidance(true);
Expand Down
7 changes: 6 additions & 1 deletion neqo-transport/src/cc/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,14 @@ pub fn make_cc_newreno() -> ClassicCongestionController<ClassicSlowStart, NewRen

/// Helper to create `ClassicCongestionController` with Cubic for tests.
pub fn make_cc_cubic() -> ClassicCongestionController<ClassicSlowStart, Cubic> {
make_cc_cubic_with(Cubic::default())
}

/// Helper to create `ClassicCongestionController` with the given Cubic instance for tests.
pub fn make_cc_cubic_with(cubic: Cubic) -> ClassicCongestionController<ClassicSlowStart, Cubic> {
ClassicCongestionController::new(
ClassicSlowStart::default(),
Cubic::default(),
cubic,
Pmtud::new(IP_ADDR, MTU),
true,
)
Expand Down
22 changes: 22 additions & 0 deletions neqo-transport/src/connection/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ pub struct ConnectionParameters {
/// Whether to recover from spurious congestion events by restoring prior Congestion Controller
/// state. Detection and metrics are always active regardless of this setting.
spurious_recovery: bool,
/// Whether to use Alternative Backoff with ECN (RFC 8511), i.e. a smaller multiplicative
/// decrease on ECN-CE congestion events than on loss. Only affects CUBIC.
abe: bool,
}

impl Default for ConnectionParameters {
Expand Down Expand Up @@ -209,6 +212,7 @@ impl Default for ConnectionParameters {
scone: false,
reliable_stream_reset: true,
spurious_recovery: true,
abe: true,
}
}
}
Expand Down Expand Up @@ -541,6 +545,17 @@ impl ConnectionParameters {
self
}

#[must_use]
pub const fn abe_enabled(&self) -> bool {
self.abe
}

#[must_use]
pub const fn abe(mut self, abe: bool) -> Self {
self.abe = abe;
self
}

/// # Errors
/// When a connection ID cannot be obtained.
/// # Panics
Expand Down Expand Up @@ -669,6 +684,13 @@ mod tests {
);
}

#[test]
fn abe_enabled() {
// Default is true; verify builder can toggle it.
assert!(ConnectionParameters::default().abe_enabled());
assert!(!ConnectionParameters::default().abe(false).abe_enabled());
}

#[test]
fn max_stream_data_transport_parameters() {
use std::{cell::RefCell, rc::Rc};
Expand Down
7 changes: 4 additions & 3 deletions neqo-transport/src/sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ impl PacketSender {
pub fn new(conn_params: &ConnectionParameters, pmtud: Pmtud, now: Instant) -> Self {
let mtu = pmtud.plpmtu();
let spurious_recovery = conn_params.spurious_recovery_enabled();
let abe = conn_params.abe_enabled();
Self {
cc: match (
conn_params.get_congestion_control(),
Expand Down Expand Up @@ -81,7 +82,7 @@ impl PacketSender {
(CongestionControl::Cubic, SlowStart::Classic) => {
CongestionControlImplementation::ClassicCubic(ClassicCongestionController::new(
ClassicSlowStart::default(),
Cubic::default(),
Cubic::default().abe(abe),
pmtud,
spurious_recovery,
))
Expand All @@ -92,15 +93,15 @@ impl PacketSender {
conn_params.pacing_enabled(),
conn_params.get_hystart_css_baseline(),
),
Cubic::default(),
Cubic::default().abe(abe),
pmtud,
spurious_recovery,
))
}
(CongestionControl::Cubic, SlowStart::Search) => {
CongestionControlImplementation::SearchCubic(ClassicCongestionController::new(
Search::new(),
Cubic::default(),
Cubic::default().abe(abe),
pmtud,
spurious_recovery,
))
Expand Down
Loading