diff --git a/.github/workflows/firefox.yml b/.github/workflows/firefox.yml index 17ab97cdac..a5ef3247f2 100644 --- a/.github/workflows/firefox.yml +++ b/.github/workflows/firefox.yml @@ -87,9 +87,10 @@ jobs: - name: Plumb in Neqo run: | - # Get qlog version used by neqo + # Get qlog and mtu version used by neqo cargo generate-lockfile QLOG_VERSION=$(cargo pkgid qlog | cut -d@ -f2) + MTU_VERSION=$(cargo pkgid mtu | cut -d@ -f2) rm Cargo.lock cd mozilla-unified { @@ -97,6 +98,10 @@ jobs: echo 'who = "CI"' echo 'criteria = "safe-to-deploy"' echo "version = \"$QLOG_VERSION\"" + echo '[[audits.mtu]]' + echo 'who = "CI"' + echo 'criteria = "safe-to-deploy"' + echo "version = \"$MTU_VERSION\"" } >> supply-chain/audits.toml sed -i'' -e "s/qlog =.*/qlog = \"$QLOG_VERSION\"/" netwerk/socket/neqo_glue/Cargo.toml { @@ -107,6 +112,18 @@ jobs: echo 'neqo-qpack = { path = "../neqo-qpack" }' echo 'neqo-crypto = { path = "../neqo-crypto" }' } >> Cargo.toml + { + echo '[[trusted.windows-bindgen]]' + echo 'criteria = "safe-to-deploy"' + echo 'user-id = 64539 # Kenny Kerr (kennykerr)' + echo 'start = "2021-11-15"' + echo 'end = "2024-09-12"' + echo '[[trusted.windows-metadata]]' + echo 'criteria = "safe-to-deploy"' + echo 'user-id = 64539 # Kenny Kerr (kennykerr)' + echo 'start = "2021-11-15"' + echo 'end = "2024-09-12"' + } >> supply-chain/audits.toml cargo update neqo-http3 neqo-transport neqo-common neqo-qpack neqo-crypto ./mach vendor rust --ignore-modified diff --git a/Cargo.lock b/Cargo.lock index 914f9b7ffe..c104e7a109 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,6 +257,36 @@ dependencies = [ "itertools", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "crunchy" version = "0.2.2" @@ -762,6 +792,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -795,6 +834,22 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "903970ae2f248d7275214cf8f387f8ba0c4ea7e3d87a320e85493db60ce28616" +[[package]] +name = "mtu" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e76af2be1c20282d2b7290590552a58bcfad00937fa8ac6f240202e76a0dae" +dependencies = [ + "bindgen", + "cfg_aliases", + "libc", + "mozbuild", + "static_assertions", + "windows-bindgen", + "windows-core", + "windows-targets", +] + [[package]] name = "neqo-bin" version = "0.11.0" @@ -886,6 +941,7 @@ dependencies = [ "enum-map", "indexmap", "log", + "mtu", "neqo-common", "neqo-crypto", "neqo-transport", @@ -1007,6 +1063,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "regex" version = "1.9.4" @@ -1073,6 +1149,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "semver" version = "1.0.16" @@ -1363,6 +1445,20 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-bindgen" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cd28d93c692351f3a6e5615567c56756e330bee1c99c6bdd57bfc5ab15f589" +dependencies = [ + "proc-macro2", + "rayon", + "serde", + "serde_json", + "syn", + "windows-metadata", +] + [[package]] name = "windows-core" version = "0.58.0" @@ -1398,6 +1494,12 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-metadata" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e837f3c3012cfe9e7086302a93f441a7999439be1ad4c530d55d2f6d2921809" + [[package]] name = "windows-result" version = "0.2.0" diff --git a/neqo-transport/Cargo.toml b/neqo-transport/Cargo.toml index 80deb22cca..c0ed73d743 100644 --- a/neqo-transport/Cargo.toml +++ b/neqo-transport/Cargo.toml @@ -22,6 +22,7 @@ indexmap = { version = "2.2", default-features = false } # See https://github.co log = { workspace = true } neqo-common = { path = "../neqo-common" } neqo-crypto = { path = "../neqo-crypto" } +mtu = { version = "0.2.3", default-features = false } # neqo is only user currently, can bump freely qlog = { workspace = true } smallvec = { version = "1.13", default-features = false } static_assertions = { workspace = true } @@ -41,6 +42,7 @@ build-fuzzing-corpus = [ ] disable-encryption = ["neqo-crypto/disable-encryption"] draft-29 = [] +gecko = ["mtu/gecko"] [lib] # See https://github.com/bheisler/criterion.rs/blob/master/book/src/faq.md#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options diff --git a/neqo-transport/src/cc/classic_cc.rs b/neqo-transport/src/cc/classic_cc.rs index 5dc7356a19..57e7a2b1f4 100644 --- a/neqo-transport/src/cc/classic_cc.rs +++ b/neqo-transport/src/cc/classic_cc.rs @@ -12,6 +12,9 @@ use std::{ time::{Duration, Instant}, }; +use ::qlog::events::{quic::CongestionStateUpdated, EventData}; +use neqo_common::{const_max, const_min, qdebug, qinfo, qlog::NeqoQlog, qtrace}; + use super::CongestionControl; use crate::{ packet::PacketNumber, @@ -21,9 +24,6 @@ use crate::{ sender::PACING_BURST_SIZE, Pmtud, }; -#[rustfmt::skip] // to keep `::` and thus prevent conflict with `crate::qlog` -use ::qlog::events::{quic::CongestionStateUpdated, EventData}; -use neqo_common::{const_max, const_min, qdebug, qinfo, qlog::NeqoQlog, qtrace}; pub const CWND_INITIAL_PKTS: usize = 10; const PERSISTENT_CONG_THRESH: u32 = 3; @@ -604,10 +604,7 @@ impl ClassicCongestionControl { #[cfg(test)] mod tests { - use std::{ - net::{IpAddr, Ipv4Addr}, - time::{Duration, Instant}, - }; + use std::time::{Duration, Instant}; use neqo_common::{qinfo, IpTosEcn}; use test_fixture::now; @@ -618,6 +615,7 @@ mod tests { classic_cc::State, cubic::{Cubic, CUBIC_BETA_USIZE_DIVIDEND, CUBIC_BETA_USIZE_DIVISOR}, new_reno::NewReno, + tests::{IP_ADDR, MTU, RTT}, CongestionControl, CongestionControlAlgorithm, CWND_INITIAL_PKTS, }, packet::{PacketNumber, PacketType}, @@ -626,10 +624,8 @@ mod tests { Pmtud, }; - const IP_ADDR: IpAddr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); - const PTO: Duration = Duration::from_millis(100); - const RTT: Duration = Duration::from_millis(98); - const RTT_ESTIMATE: RttEstimate = RttEstimate::from_duration(Duration::from_millis(98)); + const PTO: Duration = RTT; + const RTT_ESTIMATE: RttEstimate = RttEstimate::from_duration(RTT); const ZERO: Duration = Duration::from_secs(0); const EPSILON: Duration = Duration::from_nanos(1); const GAP: Duration = Duration::from_secs(1); @@ -665,11 +661,11 @@ mod tests { match cc { CongestionControlAlgorithm::NewReno => Box::new(ClassicCongestionControl::new( NewReno::default(), - Pmtud::new(IP_ADDR), + Pmtud::new(IP_ADDR, MTU), )), CongestionControlAlgorithm::Cubic => Box::new(ClassicCongestionControl::new( Cubic::default(), - Pmtud::new(IP_ADDR), + Pmtud::new(IP_ADDR, MTU), )), } } @@ -908,13 +904,13 @@ mod tests { fn persistent_congestion_no_lost() { let lost = make_lost(&[]); assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR, MTU)), 0, 0, &lost )); assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)), 0, 0, &lost @@ -926,13 +922,13 @@ mod tests { fn persistent_congestion_one_lost() { let lost = make_lost(&[1]); assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR, MTU)), 0, 0, &lost )); assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)), 0, 0, &lost @@ -946,37 +942,37 @@ mod tests { // sample are not considered. So 0 is ignored. let lost = make_lost(&[0, PERSISTENT_CONG_THRESH + 1, PERSISTENT_CONG_THRESH + 2]); assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR, MTU)), 1, 1, &lost )); assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR, MTU)), 0, 1, &lost )); assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR, MTU)), 1, 0, &lost )); assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)), 1, 1, &lost )); assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)), 0, 1, &lost )); assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)), 1, 0, &lost @@ -997,13 +993,13 @@ mod tests { lost[0].len(), ); assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR, MTU)), 0, 0, &lost )); assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)), 0, 0, &lost @@ -1017,13 +1013,13 @@ mod tests { fn persistent_congestion_min() { let lost = make_lost(&[1, PERSISTENT_CONG_THRESH + 2]); assert!(persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR, MTU)), 0, 0, &lost )); assert!(persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)), 0, 0, &lost @@ -1036,7 +1032,7 @@ mod tests { #[test] fn persistent_congestion_no_prev_ack_newreno() { let lost = make_lost(&[1, PERSISTENT_CONG_THRESH + 2]); - let mut cc = ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR)); + let mut cc = ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR, MTU)); cc.detect_persistent_congestion(Some(by_pto(0)), None, PTO, lost.iter(), Instant::now()); assert_eq!(cc.cwnd(), cc.cwnd_min()); } @@ -1044,7 +1040,7 @@ mod tests { #[test] fn persistent_congestion_no_prev_ack_cubic() { let lost = make_lost(&[1, PERSISTENT_CONG_THRESH + 2]); - let mut cc = ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)); + let mut cc = ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)); cc.detect_persistent_congestion(Some(by_pto(0)), None, PTO, lost.iter(), Instant::now()); assert_eq!(cc.cwnd(), cc.cwnd_min()); } @@ -1055,7 +1051,7 @@ mod tests { fn persistent_congestion_unsorted_newreno() { let lost = make_lost(&[PERSISTENT_CONG_THRESH + 2, 1]); assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR, MTU)), 0, 0, &lost @@ -1068,7 +1064,7 @@ mod tests { fn persistent_congestion_unsorted_cubic() { let lost = make_lost(&[PERSISTENT_CONG_THRESH + 2, 1]); assert!(!persistent_congestion_by_pto( - ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)), + ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)), 0, 0, &lost @@ -1079,7 +1075,7 @@ mod tests { fn app_limited_slow_start() { const BELOW_APP_LIMIT_PKTS: usize = 5; const ABOVE_APP_LIMIT_PKTS: usize = BELOW_APP_LIMIT_PKTS + 1; - let mut cc = ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR)); + let mut cc = ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR, MTU)); let cwnd = cc.congestion_window; let mut now = now(); let mut next_pn = 0; @@ -1163,7 +1159,7 @@ mod tests { const BELOW_APP_LIMIT_PKTS: usize = CWND_PKTS_CA - 2; const ABOVE_APP_LIMIT_PKTS: usize = BELOW_APP_LIMIT_PKTS + 1; - let mut cc = ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR)); + let mut cc = ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR, MTU)); let mut now = now(); // Change state to congestion avoidance by introducing loss. @@ -1279,7 +1275,7 @@ mod tests { #[test] fn ecn_ce() { let now = now(); - let mut cc = ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR)); + let mut cc = ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR, MTU)); let p_ce = SentPacket::new( PacketType::Short, 1, diff --git a/neqo-transport/src/cc/tests/cubic.rs b/neqo-transport/src/cc/tests/cubic.rs index e62c063b90..18a4f39a37 100644 --- a/neqo-transport/src/cc/tests/cubic.rs +++ b/neqo-transport/src/cc/tests/cubic.rs @@ -8,7 +8,6 @@ #![allow(clippy::cast_sign_loss)] use std::{ - net::{IpAddr, Ipv4Addr}, ops::Sub, time::{Duration, Instant}, }; @@ -16,6 +15,7 @@ use std::{ use neqo_common::IpTosEcn; use test_fixture::now; +use super::{IP_ADDR, MTU, RTT}; use crate::{ cc::{ classic_cc::ClassicCongestionControl, @@ -31,9 +31,6 @@ use crate::{ rtt::RttEstimate, }; -const IP_ADDR: IpAddr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); -const RTT: Duration = Duration::from_millis(100); - const fn cwnd_after_loss(cwnd: usize) -> usize { cwnd * CUBIC_BETA_USIZE_DIVIDEND / CUBIC_BETA_USIZE_DIVISOR } @@ -95,7 +92,7 @@ fn expected_tcp_acks(cwnd_rtt_start: usize, mtu: usize) -> u64 { #[test] fn tcp_phase() { - let mut cubic = ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)); + let mut cubic = ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)); // change to congestion avoidance state. cubic.set_ssthresh(1); @@ -202,7 +199,7 @@ fn tcp_phase() { #[test] fn cubic_phase() { - let mut cubic = ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)); + let mut cubic = ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)); let cwnd_initial_f64: f64 = convert_to_f64(cubic.cwnd_initial()); // Set last_max_cwnd to a higher number make sure that cc is the cubic phase (cwnd is calculated // by the cubic equation). @@ -257,7 +254,7 @@ fn assert_within + PartialOrd + Copy>(value: T, expected: T, #[test] fn congestion_event_slow_start() { - let mut cubic = ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)); + let mut cubic = ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)); _ = fill_cwnd(&mut cubic, 0, now()); ack_packet(&mut cubic, 0, now()); @@ -288,7 +285,7 @@ fn congestion_event_slow_start() { #[test] fn congestion_event_congestion_avoidance() { - let mut cubic = ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)); + let mut cubic = ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)); // Set ssthresh to something small to make sure that cc is in the congection avoidance phase. cubic.set_ssthresh(1); @@ -312,7 +309,7 @@ fn congestion_event_congestion_avoidance() { #[test] fn congestion_event_congestion_avoidance_2() { - let mut cubic = ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)); + let mut cubic = ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)); // Set ssthresh to something small to make sure that cc is in the congection avoidance phase. cubic.set_ssthresh(1); @@ -341,7 +338,7 @@ fn congestion_event_congestion_avoidance_2() { #[test] fn congestion_event_congestion_avoidance_no_overflow() { const PTO: Duration = Duration::from_millis(120); - let mut cubic = ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR)); + let mut cubic = ClassicCongestionControl::new(Cubic::default(), Pmtud::new(IP_ADDR, MTU)); // Set ssthresh to something small to make sure that cc is in the congection avoidance phase. cubic.set_ssthresh(1); diff --git a/neqo-transport/src/cc/tests/mod.rs b/neqo-transport/src/cc/tests/mod.rs index 879693fb24..45bed8fd88 100644 --- a/neqo-transport/src/cc/tests/mod.rs +++ b/neqo-transport/src/cc/tests/mod.rs @@ -4,5 +4,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{ + net::{IpAddr, Ipv4Addr}, + time::Duration, +}; + mod cubic; mod new_reno; + +pub const IP_ADDR: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED); +pub const MTU: Option = Some(1_500); +pub const RTT: Duration = Duration::from_millis(100); diff --git a/neqo-transport/src/cc/tests/new_reno.rs b/neqo-transport/src/cc/tests/new_reno.rs index 0b4e64af55..d629d26b32 100644 --- a/neqo-transport/src/cc/tests/new_reno.rs +++ b/neqo-transport/src/cc/tests/new_reno.rs @@ -6,14 +6,12 @@ // Congestion control -use std::{ - net::{IpAddr, Ipv4Addr}, - time::Duration, -}; +use std::time::Duration; use neqo_common::IpTosEcn; use test_fixture::now; +use super::{IP_ADDR, MTU, RTT}; use crate::{ cc::{new_reno::NewReno, ClassicCongestionControl, CongestionControl}, packet::PacketType, @@ -22,9 +20,7 @@ use crate::{ rtt::RttEstimate, }; -const IP_ADDR: IpAddr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); -const PTO: Duration = Duration::from_millis(100); -const RTT: Duration = Duration::from_millis(98); +const PTO: Duration = RTT; const RTT_ESTIMATE: RttEstimate = RttEstimate::from_duration(RTT); fn cwnd_is_default(cc: &ClassicCongestionControl) { @@ -40,7 +36,7 @@ fn cwnd_is_halved(cc: &ClassicCongestionControl) { #[test] #[allow(clippy::too_many_lines)] fn issue_876() { - let mut cc = ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR)); + let mut cc = ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR, MTU)); let now = now(); let before = now.checked_sub(Duration::from_millis(100)).unwrap(); let after = now + Duration::from_millis(150); @@ -151,7 +147,7 @@ fn issue_876() { #[test] // https://github.com/mozilla/neqo/pull/1465 fn issue_1465() { - let mut cc = ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR)); + let mut cc = ClassicCongestionControl::new(NewReno::default(), Pmtud::new(IP_ADDR, MTU)); let mut pn = 0; let mut now = now(); let max_datagram_size = cc.max_datagram_size(); diff --git a/neqo-transport/src/connection/mod.rs b/neqo-transport/src/connection/mod.rs index 6233746c22..9cbb9ce7e0 100644 --- a/neqo-transport/src/connection/mod.rs +++ b/neqo-transport/src/connection/mod.rs @@ -337,6 +337,7 @@ impl Connection { c.conn_params.pacing_enabled(), NeqoQlog::default(), now, + &mut c.stats.borrow_mut(), ); c.setup_handshake_path(&Rc::new(RefCell::new(path)), now); Ok(c) @@ -1554,6 +1555,7 @@ impl Connection { self.conn_params.get_cc_algorithm(), self.conn_params.pacing_enabled(), now, + &mut self.stats.borrow_mut(), ); path.borrow_mut().add_received(d.len()); let res = self.input_path(&path, d, received); @@ -1925,6 +1927,7 @@ impl Connection { self.conn_params.get_cc_algorithm(), self.conn_params.pacing_enabled(), now, + &mut self.stats.borrow_mut(), ); self.ensure_permanent(&path, now)?; qinfo!( @@ -2885,7 +2888,7 @@ impl Connection { .ok_or(Error::InternalError)? .borrow_mut() .pmtud_mut() - .start(); + .start(now, &mut self.stats.borrow_mut()); } Ok(()) } diff --git a/neqo-transport/src/connection/tests/mod.rs b/neqo-transport/src/connection/tests/mod.rs index e897b7082b..5accfab36d 100644 --- a/neqo-transport/src/connection/tests/mod.rs +++ b/neqo-transport/src/connection/tests/mod.rs @@ -690,6 +690,7 @@ fn assert_path_challenge_min_len(c: &Connection, d: &Datagram, now: Instant) { c.conn_params.get_cc_algorithm(), c.conn_params.pacing_enabled(), now, + &mut c.stats.borrow_mut(), ); if path.borrow().amplification_limit() < path.borrow().plpmtu() { // If the amplification limit is less than the PLPMTU, then the path diff --git a/neqo-transport/src/path.rs b/neqo-transport/src/path.rs index 7ab36d8f3b..c6fe207375 100644 --- a/neqo-transport/src/path.rs +++ b/neqo-transport/src/path.rs @@ -15,7 +15,9 @@ use std::{ time::{Duration, Instant}, }; -use neqo_common::{hex, qdebug, qinfo, qlog::NeqoQlog, qtrace, Datagram, Encoder, IpTos, IpTosEcn}; +use neqo_common::{ + hex, qdebug, qinfo, qlog::NeqoQlog, qtrace, qwarn, Datagram, Encoder, IpTos, IpTosEcn, +}; use neqo_crypto::random; use crate::{ @@ -79,6 +81,7 @@ impl Paths { cc: CongestionControlAlgorithm, pacing: bool, now: Instant, + stats: &mut Stats, ) -> PathRef { self.paths .iter() @@ -90,7 +93,8 @@ impl Paths { } }) .unwrap_or_else(|| { - let mut p = Path::temporary(local, remote, cc, pacing, self.qlog.clone(), now); + let mut p = + Path::temporary(local, remote, cc, pacing, self.qlog.clone(), now, stats); if let Some(primary) = self.primary.as_ref() { p.prime_rtt(primary.borrow().rtt()); } @@ -253,7 +257,9 @@ impl Paths { } else { // See if the PMTUD raise timer wants to fire. if let Some(path) = self.primary() { - path.borrow_mut().pmtud_mut().maybe_fire_raise_timer(now); + path.borrow_mut() + .pmtud_mut() + .maybe_fire_raise_timer(now, stats); } true } @@ -528,8 +534,20 @@ impl Path { pacing: bool, qlog: NeqoQlog, now: Instant, + stats: &mut Stats, ) -> Self { - let mut sender = PacketSender::new(cc, pacing, Pmtud::new(remote.ip()), now); + let iface_mtu = match mtu::interface_and_mtu(remote.ip()) { + Ok((name, mtu)) => { + qdebug!("Outbound interface {name} has MTU {mtu}"); + stats.pmtud_iface_mtu = mtu; + Some(mtu) + } + Err(e) => { + qwarn!("Failed to determine outbound interface: {e}"); + None + } + }; + let mut sender = PacketSender::new(cc, pacing, Pmtud::new(remote.ip(), iface_mtu), now); sender.set_qlog(qlog.clone()); Self { local, diff --git a/neqo-transport/src/pmtud.rs b/neqo-transport/src/pmtud.rs index 880ef70dfc..fd40f92699 100644 --- a/neqo-transport/src/pmtud.rs +++ b/neqo-transport/src/pmtud.rs @@ -46,6 +46,7 @@ pub struct Pmtud { search_table: &'static [usize], header_size: usize, mtu: usize, + iface_mtu: usize, probe_index: usize, probe_count: usize, probe_state: Probe, @@ -71,13 +72,14 @@ impl Pmtud { } #[must_use] - pub const fn new(remote_ip: IpAddr) -> Self { + pub fn new(remote_ip: IpAddr, iface_mtu: Option) -> Self { let search_table = Self::search_table(remote_ip); let probe_index = 0; Self { search_table, header_size: Self::header_size(remote_ip), mtu: search_table[probe_index], + iface_mtu: iface_mtu.unwrap_or(usize::MAX), probe_index, probe_count: 0, probe_state: Probe::NotNeeded, @@ -87,11 +89,11 @@ impl Pmtud { } /// Checks whether the PMTUD raise timer should be fired, and does so if needed. - pub fn maybe_fire_raise_timer(&mut self, now: Instant) { + pub fn maybe_fire_raise_timer(&mut self, now: Instant, stats: &mut Stats) { if self.probe_state == Probe::NotNeeded && self.raise_timer.is_some_and(|t| now >= t) { qdebug!("PMTUD raise timer fired"); self.raise_timer = None; - self.start(); + self.start(now, stats); } } @@ -108,12 +110,6 @@ impl Pmtud { self.probe_state == Probe::Needed } - /// Returns true if a PMTUD probe was sent. - #[must_use] - pub fn probe_sent(&self) -> bool { - self.probe_state == Probe::Sent - } - /// Returns the size of the current PMTUD probe. #[must_use] pub const fn probe_size(&self) -> usize { @@ -160,7 +156,7 @@ impl Pmtud { /// Checks whether a PMTUD probe has been acknowledged, and if so, updates the PMTUD state. /// May also initiate a new probe process for a larger MTU. - pub fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], stats: &mut Stats) { + pub fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], now: Instant, stats: &mut Stats) { // Reset the loss counts for all packets sizes <= the size of the largest ACKed packet. let max_len = acked_pkts.iter().map(SentPacket::len).max().unwrap_or(0); if max_len == 0 { @@ -186,15 +182,17 @@ impl Pmtud { // total number of successful probes. stats.pmtud_ack += acked; self.mtu = self.search_table[self.probe_index]; + stats.pmtud_pmtu = self.mtu; qdebug!("PMTUD probe of size {} succeeded", self.mtu); - self.start(); + self.start(now, stats); } /// Stops the PMTUD process, setting the MTU to the largest successful probe size. - fn stop(&mut self, idx: usize, now: Instant) { + fn stop(&mut self, idx: usize, now: Instant, stats: &mut Stats) { self.probe_state = Probe::NotNeeded; // We don't need to send any more probes self.probe_index = idx; // Index of the last successful probe self.mtu = self.search_table[idx]; // Leading to this MTU + stats.pmtud_pmtu = self.mtu; self.probe_count = 0; // Reset the count self.loss_counts.fill(0); // Reset the loss counts self.raise_timer = Some(now + PMTU_RAISE_TIMER); @@ -283,27 +281,32 @@ impl Pmtud { // TODO: If we are declaring losses, that means that we're getting packets through. // The size of those will put a floor on the MTU. We're currently conservative and // start from scratch, but we don't strictly need to do that. - self.restart(stats); + self.reset(stats); + qdebug!("PMTUD reset and restarting, PLPMTU is now {}", self.mtu); + self.start(now, stats); } else { // We saw multiple losses of packets > the current MTU during PMTU discovery, so // we're done. - self.stop(last_ok, now); + self.stop(last_ok, now, stats); } } - fn restart(&mut self, stats: &mut Stats) { + /// Resets the PMTUD process, starting from the smallest probe size. + fn reset(&mut self, stats: &mut Stats) { self.probe_index = 0; self.mtu = self.search_table[self.probe_index]; + stats.pmtud_pmtu = self.mtu; self.loss_counts.fill(0); self.raise_timer = None; stats.pmtud_change += 1; - qdebug!("PMTUD restarted, PLPMTU is now {}", self.mtu); - self.start(); } /// Starts the next upward PMTUD probe. - pub fn start(&mut self) { - if self.probe_index < SEARCH_TABLE_LEN - 1 { + pub fn start(&mut self, now: Instant, stats: &mut Stats) { + if self.probe_index < SEARCH_TABLE_LEN - 1 // Not at the end of the search table + // Next size is <= iface MTU + && self.search_table[self.probe_index + 1] <= self.iface_mtu + { self.probe_state = Probe::Needed; // We need to send a probe self.probe_count = 0; // For the first time self.probe_index += 1; // At this size @@ -312,8 +315,8 @@ impl Pmtud { self.search_table[self.probe_index], ); } else { - // If we're at the end of the search table, we're done. - self.probe_state = Probe::NotNeeded; + // If we're at the end of the search table or hit the local interface MTU, we're done. + self.stop(self.probe_index, now, stats); } } @@ -344,8 +347,8 @@ mod tests { Pmtud, Stats, }; - const V4: IpAddr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); - const V6: IpAddr = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); + const V4: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED); + const V6: IpAddr = IpAddr::V6(Ipv6Addr::UNSPECIFIED); fn make_sentpacket(pn: u64, now: Instant, len: usize) -> SentPacket { SentPacket::new( @@ -396,7 +399,7 @@ mod tests { let packet = make_sentpacket(pn, now, encoder.len()); if encoder.len() + Pmtud::header_size(addr) <= mtu { - pmtud.on_packets_acked(&[packet], stats); + pmtud.on_packets_acked(&[packet], now, stats); assert_eq!(stats_before.pmtud_ack + 1, stats.pmtud_ack); } else { pmtud.on_packets_lost(&[packet], stats, now); @@ -407,11 +410,11 @@ mod tests { fn find_pmtu(addr: IpAddr, mtu: usize) { fixture_init(); let now = now(); - let mut pmtud = Pmtud::new(addr); + let mut pmtud = Pmtud::new(addr, Some(mtu)); let mut stats = Stats::default(); let mut prot = CryptoDxState::test_default(); - pmtud.start(); + pmtud.start(now, &mut stats); assert!(pmtud.needs_probe()); while pmtud.needs_probe() { @@ -445,12 +448,12 @@ mod tests { fixture_init(); let now = now(); - let mut pmtud = Pmtud::new(addr); + let mut pmtud = Pmtud::new(addr, Some(mtu)); let mut stats = Stats::default(); let mut prot = CryptoDxState::test_default(); assert!(smaller_mtu >= pmtud.search_table[0]); - pmtud.start(); + pmtud.start(now, &mut stats); assert!(pmtud.needs_probe()); while pmtud.needs_probe() { @@ -498,12 +501,12 @@ mod tests { fixture_init(); let now = now(); - let mut pmtud = Pmtud::new(addr); + let mut pmtud = Pmtud::new(addr, Some(larger_mtu)); let mut stats = Stats::default(); let mut prot = CryptoDxState::test_default(); assert!(larger_mtu >= pmtud.search_table[0]); - pmtud.start(); + pmtud.start(now, &mut stats); assert!(pmtud.needs_probe()); while pmtud.needs_probe() { @@ -513,7 +516,7 @@ mod tests { qdebug!("Increasing MTU to {}", larger_mtu); let now = now + PMTU_RAISE_TIMER; - pmtud.maybe_fire_raise_timer(now); + pmtud.maybe_fire_raise_timer(now, &mut stats); while pmtud.needs_probe() { pmtud_step(&mut pmtud, &mut stats, &mut prot, addr, larger_mtu, now); } @@ -570,7 +573,7 @@ mod tests { #[test] fn pmtud_on_packets_lost() { let now = now(); - let mut pmtud = Pmtud::new(V4); + let mut pmtud = Pmtud::new(V4, Some(1500)); let mut stats = Stats::default(); // No packets lost, nothing should change. @@ -638,23 +641,23 @@ mod tests { #[test] fn pmtud_on_packets_lost_and_acked() { let now = now(); - let mut pmtud = Pmtud::new(V4); + let mut pmtud = Pmtud::new(V4, Some(1500)); let mut stats = Stats::default(); // A packet of size 100 was ACKed, which is smaller than all probe sizes. // Loss counts should be unchanged. - pmtud.on_packets_acked(&[make_sentpacket(0, now, 100)], &mut stats); + pmtud.on_packets_acked(&[make_sentpacket(0, now, 100)], now, &mut stats); assert_eq!([0; SEARCH_TABLE_LEN], pmtud.loss_counts); // A packet of size 100_000 was ACKed, which is larger than all probe sizes. // Loss counts should be unchanged. - pmtud.on_packets_acked(&[make_sentpacket(0, now, 100_000)], &mut stats); + pmtud.on_packets_acked(&[make_sentpacket(0, now, 100_000)], now, &mut stats); assert_eq!([0; SEARCH_TABLE_LEN], pmtud.loss_counts); pmtud.loss_counts.fill(0); // Reset the loss counts. // No packets ACKed, nothing should change. - pmtud.on_packets_acked(&[], &mut stats); + pmtud.on_packets_acked(&[], now, &mut stats); assert_eq!([0; SEARCH_TABLE_LEN], pmtud.loss_counts); // One packet of size 4000 was lost, which should increase loss counts >= 4000 by one. @@ -663,7 +666,7 @@ mod tests { assert_eq!(expected_lc, pmtud.loss_counts); // Now a packet of size 5000 is ACKed, which should reset all loss counts <= 5000. - pmtud.on_packets_acked(&[make_sentpacket(0, now, 5000)], &mut stats); + pmtud.on_packets_acked(&[make_sentpacket(0, now, 5000)], now, &mut stats); let expected_lc = search_table_zero(&pmtud, &pmtud.loss_counts, 5000); assert_eq!(expected_lc, pmtud.loss_counts); @@ -674,7 +677,7 @@ mod tests { assert_eq!(expected_lc, pmtud.loss_counts); // Now a packet of size 8000 is ACKed, which should reset all loss counts <= 8000. - pmtud.on_packets_acked(&[make_sentpacket(0, now, 8000)], &mut stats); + pmtud.on_packets_acked(&[make_sentpacket(0, now, 8000)], now, &mut stats); let expected_lc = search_table_zero(&pmtud, &pmtud.loss_counts, 8000); assert_eq!(expected_lc, pmtud.loss_counts); diff --git a/neqo-transport/src/recovery/mod.rs b/neqo-transport/src/recovery/mod.rs index 8f89b1cfcd..4879750a70 100644 --- a/neqo-transport/src/recovery/mod.rs +++ b/neqo-transport/src/recovery/mod.rs @@ -1050,6 +1050,7 @@ mod tests { impl Default for Fixture { fn default() -> Self { const CC: CongestionControlAlgorithm = CongestionControlAlgorithm::NewReno; + let stats = StatsCell::default(); let mut path = Path::temporary( DEFAULT_ADDR, DEFAULT_ADDR, @@ -1057,6 +1058,7 @@ mod tests { true, NeqoQlog::default(), now(), + &mut stats.borrow_mut(), ); path.make_permanent( None, @@ -1065,7 +1067,7 @@ mod tests { path.set_primary(true, now()); path.rtt_mut().set_initial(TEST_RTT); Self { - lr: LossRecovery::new(StatsCell::default(), FAST_PTO_SCALE), + lr: LossRecovery::new(stats, FAST_PTO_SCALE), path: Rc::new(RefCell::new(path)), } } diff --git a/neqo-transport/src/sender.rs b/neqo-transport/src/sender.rs index e6075ee327..f2f8bcb3ce 100644 --- a/neqo-transport/src/sender.rs +++ b/neqo-transport/src/sender.rs @@ -109,7 +109,7 @@ impl PacketSender { stats: &mut Stats, ) { self.cc.on_packets_acked(acked_pkts, rtt_est, now); - self.pmtud_mut().on_packets_acked(acked_pkts, stats); + self.pmtud_mut().on_packets_acked(acked_pkts, now, stats); self.maybe_update_pacer_mtu(); } diff --git a/neqo-transport/src/stats.rs b/neqo-transport/src/stats.rs index af8ae69a70..33fdc10c2e 100644 --- a/neqo-transport/src/stats.rs +++ b/neqo-transport/src/stats.rs @@ -171,6 +171,10 @@ pub struct Stats { pub pmtud_lost: usize, /// Number of times a path MTU changed unexpectedly. pub pmtud_change: usize, + /// MTU of the local interface used for the most recent path. + pub pmtud_iface_mtu: usize, + /// Probed PMTU of the current path. + pub pmtud_pmtu: usize, /// Whether the connection was resumed successfully. pub resumed: bool, @@ -262,8 +266,13 @@ impl Debug for Stats { )?; writeln!( f, - " pmtud: {} sent {} acked {} lost {} change", - self.pmtud_tx, self.pmtud_ack, self.pmtud_lost, self.pmtud_change + " pmtud: {} sent {} acked {} lost {} change {} iface_mtu {} pmtu", + self.pmtud_tx, + self.pmtud_ack, + self.pmtud_lost, + self.pmtud_change, + self.pmtud_iface_mtu, + self.pmtud_pmtu )?; writeln!(f, " resumed: {}", self.resumed)?; writeln!(f, " frames rx:")?;