瀏覽代碼

feat(socket/tcp): add congestion controllers

- add CongestionController trait
- implement Cubic and NoControl

Signed-off-by: Yuuki Takano <[email protected]>

feat(socket/tcp): add congestion_controller.rs

Signed-off-by: Yuuki Takano <[email protected]>

feat(cubic): add test for TCP Cubic

Signed-off-by: Yuuki Takano <[email protected]>

feat(cubic): do not update congestion as much as possible

If the last update time of the cwnd was less than 100ms ago,
don't update the congestion window.

Signed-off-by: Yuuki Takano <[email protected]>

feat(cubic): implement slow start for TCP Cubic

Signed-off-by: Yuuki Takano <[email protected]>

fix(cubic): use MSS as the minimum cwnd

Signed-off-by: Yuuki Takano <[email protected]>

feat(tcp): add "socket-tcp-cubic" and "socket-tcp-reno" features

- "socket-tcp-cubic": use Cubic as a default congestion controller
- "socket-tcp-reno": use Reno as a default congestion controller

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): prepare new() for Socket<'a, DefaultCC>

All tests are passed because of this fix.

$ cargo test
$ cargo test --features socket-tcp-cubic

Signed-off-by: Yuuki Takano <[email protected]>

feat(tcp, reno): add Reno of a congestion controller

Signed-off-by: Yuuki Takano <[email protected]>

feat(cubic): do not use cbrt() and powi() for no_std

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): update for coverage test

Signed-off-by: Yuuki Takano <[email protected]>

fix(cubic): do not use `abs()` for no_std

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): make some types regading congestion control private

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): make `CongestionController` `pub(super)`

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): make modules of congestion controllers private

- `cubic`, `reno` and `no_control` modules are now private.
- `ActiveCC` is now `pub(super)`

Signed-off-by: Yuuki Takano <[email protected]>

fix: typo

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): use `defmt::Format` if `defmt` feature is specified

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): introduce `GenericController` for congestion control

Remove `socket-tcp-cubic` and `socket-tcp-reno` features
for congestion control.
Instead of the features, `set_congestion_control()`
is defined to set an algorithm of congestion control.

Signed-off-by: Yuuki Takano <[email protected]>

feat(tcp): add a test for `(set|get)_congestion_control()`

Signed-off-by: Yuuki Takano <[email protected]>

fix: restore unessesory change

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): rename congestion_controller to congestion

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): update the document about `Cubic`

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): do not use `Default` trait for congestion controller

Because `Default` trait requires `Size`,
returning `&dyn Trait` is rejected by the Rust compiler.

Signed-off-by: Yuuki Takano <[email protected]>

chore(tcp): `#[allow(unused_variables)]` is moved on the trait

Signed-off-by: Yuuki Takano <[email protected]>

feat(tcp): add `socket-tcp-cubic` feature for Cubic

Cubic uses f64 and it is inefficient on some platforms like STM32.
So, Cubic is disabled by default.

Signed-off-by: Yuuki Takano <[email protected]>

fix: typo

Signed-off-by: Yuuki Takano <[email protected]>

Update src/socket/tcp.rs

Co-authored-by: Catherine <[email protected]>

feat(tcp): add `socket-tcp-reno` feature for Reno

Signed-off-by: Yuuki Takano <[email protected]>

feat(tcp): use GenericController::inner_*() methods

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): choose the best congestion controller

If Cubic is enabled, it is used as a default controller.
If Reno is enabled, it is used as a default controller.

If Cubic and Reno are eanabled, Cubic is a default controlerr,
but it can be specified by `set_congestion_control()` method.

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): rename CongestionController to Controller

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): rename GenericController to AnyController

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): add a document to `AnyController::new()`

Signed-off-by: Yuuki Takano <[email protected]>

fix(tcp): use Controller instead of CongestionController

CongestionController has been renamed.

Signed-off-by: Yuuki Takano <[email protected]>
Yuuki Takano 1 年之前
父節點
當前提交
833399f124
共有 7 個文件被更改,包括 693 次插入4 次删除
  1. 16 1
      Cargo.toml
  2. 3 3
      README.md
  3. 120 0
      src/socket/tcp.rs
  4. 101 0
      src/socket/tcp/congestion.rs
  5. 312 0
      src/socket/tcp/congestion/cubic.rs
  6. 11 0
      src/socket/tcp/congestion/no_control.rs
  7. 130 0
      src/socket/tcp/congestion/reno.rs

+ 16 - 1
Cargo.toml

@@ -70,6 +70,21 @@ defmt = ["dep:defmt", "heapless/defmt-03"]
 "socket-dns" = ["socket", "proto-dns"]
 "socket-mdns" = ["socket-dns"]
 
+# Enable Cubic TCP congestion control algorithm, and it is used as a default congestion controller.
+#
+# Cubic relies on double precision (`f64`) floating point operations, which may cause issues in some contexts:
+#
+# * Small embedded processors (such as Cortex-M0, Cortex-M1, and Cortex-M3) do not have an FPU,
+#   and floating point operations consume significant amounts of CPU time and Flash space.
+# * Interrupt handlers should almost always avoid floating-point operations.
+# * Kernel-mode code on desktop processors usually avoids FPU operations to reduce the penalty of saving and restoring FPU registers.
+#
+# In all these cases, `CongestionControl::Reno` is a better choice of congestion control algorithm.
+"socket-tcp-cubic" = []
+
+# Enable Reno TCP congestion control algorithm, and it is used as a default congestion controller.
+"socket-tcp-reno" = []
+
 "packetmeta-id" = []
 
 "async" = []
@@ -85,7 +100,7 @@ default = [
 ]
 
 # Private features
-# Features starting with "_" are considered private. They should not be enabled by 
+# Features starting with "_" are considered private. They should not be enabled by
 # other crates, and they are not considered semver-stable.
 
 "_proto-fragmentation" = []

+ 3 - 3
README.md

@@ -62,7 +62,7 @@ There are 3 supported mediums.
     hop-by-hop option.
 
 #### 6LoWPAN
-  
+
   * Implementation of [RFC6282](https://tools.ietf.org/rfc/rfc6282.txt).
   * Fragmentation is supported, as defined in [RFC4944](https://tools.ietf.org/rfc/rfc4944.txt).
   * UDP header compression/decompression is supported.
@@ -229,8 +229,8 @@ They can be set in two ways:
 - Via Cargo features: enable a feature like `<name>-<value>`. `name` must be in lowercase and
 use dashes instead of underscores. For example. `iface-max-addr-count-3`. Only a selection of values
 is available, check `Cargo.toml` for the list.
-- Via environment variables at build time: set the variable named `SMOLTCP_<value>`. For example 
-`SMOLTCP_IFACE_MAX_ADDR_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`. 
+- Via environment variables at build time: set the variable named `SMOLTCP_<value>`. For example
+`SMOLTCP_IFACE_MAX_ADDR_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`.
 Any value can be set, unlike with Cargo features.
 
 Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting

+ 120 - 0
src/socket/tcp.rs

@@ -17,6 +17,8 @@ use crate::wire::{
     TCP_HEADER_LEN,
 };
 
+mod congestion;
+
 macro_rules! tcp_trace {
     ($($arg:expr),*) => (net_log!(trace, $($arg),*));
 }
@@ -390,6 +392,19 @@ impl Display for Tuple {
     }
 }
 
+/// A congestion control algorithm.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum CongestionControl {
+    None,
+
+    #[cfg(feature = "socket-tcp-reno")]
+    Reno,
+
+    #[cfg(feature = "socket-tcp-cubic")]
+    Cubic,
+}
+
 /// A Transmission Control Protocol socket.
 ///
 /// A TCP socket may passively listen for connections or actively connect to another endpoint.
@@ -464,6 +479,9 @@ pub struct Socket<'a> {
     /// Nagle's Algorithm enabled.
     nagle: bool,
 
+    /// The congestion control algorithm.
+    congestion_controller: congestion::AnyController,
+
     #[cfg(feature = "async")]
     rx_waker: WakerRegistration,
     #[cfg(feature = "async")]
@@ -522,6 +540,7 @@ impl<'a> Socket<'a> {
             ack_delay_timer: AckDelayTimer::Idle,
             challenge_ack_timer: Instant::from_secs(0),
             nagle: true,
+            congestion_controller: congestion::AnyController::new(),
 
             #[cfg(feature = "async")]
             rx_waker: WakerRegistration::new(),
@@ -530,6 +549,54 @@ impl<'a> Socket<'a> {
         }
     }
 
+    /// Set an algorithm for congestion control.
+    ///
+    /// `CongestionControl::None` indicates that no congestion control is applied.
+    /// Options `CongestionControl::Cubic` and `CongestionControl::Reno` are also available.
+    /// To use Reno and Cubic, please enable the `socket-tcp-reno` and `socket-tcp-cubic` features
+    /// in the `smoltcp` crate, respectively.
+    ///
+    /// `CongestionControl::Reno` is a classic congestion control algorithm valued for its simplicity.
+    /// Despite having a lower algorithmic complexity than `Cubic`,
+    /// it is less efficient in terms of bandwidth usage.
+    ///
+    /// `CongestionControl::Cubic` represents a modern congestion control algorithm designed to
+    /// be more efficient and fair compared to `CongestionControl::Reno`.
+    /// It is the default choice for Linux, Windows, and macOS.
+    /// `CongestionControl::Cubic` relies on double precision (`f64`) floating point operations, which may cause issues in some contexts:
+    /// * Small embedded processors (such as Cortex-M0, Cortex-M1, and Cortex-M3) do not have an FPU, and floating point operations consume significant amounts of CPU time and Flash space.
+    /// * Interrupt handlers should almost always avoid floating-point operations.
+    /// * Kernel-mode code on desktop processors usually avoids FPU operations to reduce the penalty of saving and restoring FPU registers.
+    /// In all these cases, `CongestionControl::Reno` is a better choice of congestion control algorithm.
+    pub fn set_congestion_control(&mut self, congestion_control: CongestionControl) {
+        use congestion::*;
+
+        self.congestion_controller = match congestion_control {
+            CongestionControl::None => AnyController::None(no_control::NoControl),
+
+            #[cfg(feature = "socket-tcp-reno")]
+            CongestionControl::Reno => AnyController::Reno(reno::Reno::new()),
+
+            #[cfg(feature = "socket-tcp-cubic")]
+            CongestionControl::Cubic => AnyController::Cubic(cubic::Cubic::new()),
+        }
+    }
+
+    /// Return the current congestion control algorithm.
+    pub fn congestion_control(&self) -> CongestionControl {
+        use congestion::*;
+
+        match self.congestion_controller {
+            AnyController::None(_) => CongestionControl::None,
+
+            #[cfg(feature = "socket-tcp-reno")]
+            AnyController::Reno(_) => CongestionControl::Reno,
+
+            #[cfg(feature = "socket-tcp-cubic")]
+            AnyController::Cubic(_) => CongestionControl::Cubic,
+        }
+    }
+
     /// Register a waker for receive operations.
     ///
     /// The waker is woken on state changes that might affect the return value
@@ -1593,6 +1660,9 @@ impl<'a> Socket<'a> {
                 }
 
                 self.rtte.on_ack(cx.now(), ack_number);
+                self.congestion_controller
+                    .inner_mut()
+                    .on_ack(cx.now(), ack_len, &self.rtte);
             }
         }
 
@@ -1636,6 +1706,9 @@ impl<'a> Socket<'a> {
                         tcp_trace!("received SYNACK with zero MSS, ignoring");
                         return None;
                     }
+                    self.congestion_controller
+                        .inner_mut()
+                        .set_mss(max_seg_size as usize);
                     self.remote_mss = max_seg_size as usize
                 }
 
@@ -1681,6 +1754,9 @@ impl<'a> Socket<'a> {
                         return None;
                     }
                     self.remote_mss = max_seg_size as usize;
+                    self.congestion_controller
+                        .inner_mut()
+                        .set_mss(self.remote_mss);
                 }
 
                 self.remote_seq_no = repr.seq_number + 1;
@@ -1795,6 +1871,10 @@ impl<'a> Socket<'a> {
         let is_window_update = new_remote_win_len != self.remote_win_len;
         self.remote_win_len = new_remote_win_len;
 
+        self.congestion_controller
+            .inner_mut()
+            .set_remote_window(new_remote_win_len);
+
         if ack_len > 0 {
             // Dequeue acknowledged octets.
             debug_assert!(self.tx_buffer.len() >= ack_len);
@@ -1831,6 +1911,11 @@ impl<'a> Socket<'a> {
                     // Increment duplicate ACK count
                     self.local_rx_dup_acks = self.local_rx_dup_acks.saturating_add(1);
 
+                    // Inform congestion controller of duplicate ACK
+                    self.congestion_controller
+                        .inner_mut()
+                        .on_duplicate_ack(cx.now());
+
                     net_debug!(
                         "received duplicate ACK for seq {} (duplicate nr {}{})",
                         ack_number,
@@ -1995,6 +2080,9 @@ impl<'a> Socket<'a> {
             0
         };
 
+        // Compare max_send with the congestion window.
+        let max_send = max_send.min(self.congestion_controller.inner().window());
+
         // Can we send at least 1 octet?
         let mut can_send = max_send != 0;
         // Can we send at least 1 full segment?
@@ -2072,6 +2160,10 @@ impl<'a> Socket<'a> {
             self.remote_last_ts = Some(cx.now());
         }
 
+        self.congestion_controller
+            .inner_mut()
+            .pre_transmit(cx.now());
+
         // Check if any state needs to be changed because of a timer.
         if self.timed_out(cx.now()) {
             // If a timeout expires, we should abort the connection.
@@ -2095,6 +2187,11 @@ impl<'a> Socket<'a> {
 
                 // Inform RTTE, so that it can avoid bogus measurements.
                 self.rtte.on_retransmit();
+
+                // Inform the congestion controller that we're retransmitting.
+                self.congestion_controller
+                    .inner_mut()
+                    .on_retransmit(cx.now());
             }
         }
 
@@ -2315,6 +2412,9 @@ impl<'a> Socket<'a> {
         if repr.segment_len() > 0 {
             self.rtte
                 .on_send(cx.now(), repr.seq_number + repr.segment_len());
+            self.congestion_controller
+                .inner_mut()
+                .post_transmit(cx.now(), repr.segment_len());
         }
 
         if !self.seq_to_transmit(cx) && repr.segment_len() > 0 {
@@ -7309,4 +7409,24 @@ mod test {
             assert_eq!(r.retransmission_timeout(), Duration::from_millis(rto));
         }
     }
+
+    #[test]
+    fn test_set_get_congestion_control() {
+        let mut s = socket_established();
+
+        #[cfg(feature = "socket-tcp-reno")]
+        {
+            s.set_congestion_control(CongestionControl::Reno);
+            assert_eq!(s.congestion_control(), CongestionControl::Reno);
+        }
+
+        #[cfg(feature = "socket-tcp-cubic")]
+        {
+            s.set_congestion_control(CongestionControl::Cubic);
+            assert_eq!(s.congestion_control(), CongestionControl::Cubic);
+        }
+
+        s.set_congestion_control(CongestionControl::None);
+        assert_eq!(s.congestion_control(), CongestionControl::None);
+    }
 }

+ 101 - 0
src/socket/tcp/congestion.rs

@@ -0,0 +1,101 @@
+use crate::time::Instant;
+
+use super::RttEstimator;
+
+pub(super) mod no_control;
+
+#[cfg(feature = "socket-tcp-cubic")]
+pub(super) mod cubic;
+
+#[cfg(feature = "socket-tcp-reno")]
+pub(super) mod reno;
+
+#[allow(unused_variables)]
+pub(super) trait Controller {
+    /// Returns the number of bytes that can be sent.
+    fn window(&self) -> usize;
+
+    /// Set the remote window size.
+    fn set_remote_window(&mut self, remote_window: usize) {}
+
+    fn on_ack(&mut self, now: Instant, len: usize, rtt: &RttEstimator) {}
+
+    fn on_retransmit(&mut self, now: Instant) {}
+
+    fn on_duplicate_ack(&mut self, now: Instant) {}
+
+    fn pre_transmit(&mut self, now: Instant) {}
+
+    fn post_transmit(&mut self, now: Instant, len: usize) {}
+
+    /// Set the maximum segment size.
+    fn set_mss(&mut self, mss: usize) {}
+}
+
+#[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub(super) enum AnyController {
+    None(no_control::NoControl),
+
+    #[cfg(feature = "socket-tcp-reno")]
+    Reno(reno::Reno),
+
+    #[cfg(feature = "socket-tcp-cubic")]
+    Cubic(cubic::Cubic),
+}
+
+impl AnyController {
+    /// Create a new congestion controller.
+    /// `AnyController::new()` selects the best congestion controller based on the features.
+    ///
+    /// - If `socket-tcp-cubic` feature is enabled, it will use `Cubic`.
+    /// - If `socket-tcp-reno` feature is enabled, it will use `Reno`.
+    /// - If both `socket-tcp-cubic` and `socket-tcp-reno` features are enabled, it will use `Cubic`.
+    ///    - `Cubic` is more efficient regarding throughput.
+    ///    - `Reno` is more conservative and is suitable for low-power devices.
+    /// - If no congestion controller is available, it will use `NoControl`.
+    ///
+    /// Users can also select a congestion controller manually by [`super::Socket::set_congestion_control()`]
+    /// method at run-time.
+    #[allow(unreachable_code)]
+    #[inline]
+    pub fn new() -> Self {
+        #[cfg(feature = "socket-tcp-cubic")]
+        {
+            return AnyController::Cubic(cubic::Cubic::new());
+        }
+
+        #[cfg(feature = "socket-tcp-reno")]
+        {
+            return AnyController::Reno(reno::Reno::new());
+        }
+
+        AnyController::None(no_control::NoControl)
+    }
+
+    #[inline]
+    pub fn inner_mut(&mut self) -> &mut dyn Controller {
+        match self {
+            AnyController::None(n) => n,
+
+            #[cfg(feature = "socket-tcp-reno")]
+            AnyController::Reno(r) => r,
+
+            #[cfg(feature = "socket-tcp-cubic")]
+            AnyController::Cubic(c) => c,
+        }
+    }
+
+    #[inline]
+    pub fn inner(&self) -> &dyn Controller {
+        match self {
+            AnyController::None(n) => n,
+
+            #[cfg(feature = "socket-tcp-reno")]
+            AnyController::Reno(r) => r,
+
+            #[cfg(feature = "socket-tcp-cubic")]
+            AnyController::Cubic(c) => c,
+        }
+    }
+}

+ 312 - 0
src/socket/tcp/congestion/cubic.rs

@@ -0,0 +1,312 @@
+use crate::time::Instant;
+
+use super::Controller;
+
+// Constants for the Cubic congestion control algorithm.
+// See RFC 8312.
+const BETA_CUBIC: f64 = 0.7;
+const C: f64 = 0.4;
+
+#[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct Cubic {
+    cwnd: usize,     // Congestion window
+    min_cwnd: usize, // The minimum size of congestion window
+    w_max: usize,    // Window size just before congestion
+    recovery_start: Option<Instant>,
+    rwnd: usize, // Remote window
+    last_update: Instant,
+    ssthresh: usize,
+}
+
+impl Cubic {
+    pub fn new() -> Cubic {
+        Cubic {
+            cwnd: 1024 * 2,
+            min_cwnd: 1024 * 2,
+            w_max: 1024 * 2,
+            recovery_start: None,
+            rwnd: 64 * 1024,
+            last_update: Instant::from_millis(0),
+            ssthresh: usize::MAX,
+        }
+    }
+}
+
+impl Controller for Cubic {
+    fn window(&self) -> usize {
+        self.cwnd
+    }
+
+    fn on_retransmit(&mut self, now: Instant) {
+        self.w_max = self.cwnd;
+        self.ssthresh = self.cwnd >> 1;
+        self.recovery_start = Some(now);
+    }
+
+    fn on_duplicate_ack(&mut self, now: Instant) {
+        self.w_max = self.cwnd;
+        self.ssthresh = self.cwnd >> 1;
+        self.recovery_start = Some(now);
+    }
+
+    fn set_remote_window(&mut self, remote_window: usize) {
+        if self.rwnd < remote_window {
+            self.rwnd = remote_window;
+        }
+    }
+
+    fn on_ack(&mut self, _now: Instant, len: usize, _rtt: &crate::socket::tcp::RttEstimator) {
+        // Slow start.
+        if self.cwnd < self.ssthresh {
+            self.cwnd = self
+                .cwnd
+                .saturating_add(len)
+                .min(self.rwnd)
+                .max(self.min_cwnd);
+        }
+    }
+
+    fn pre_transmit(&mut self, now: Instant) {
+        let Some(recovery_start) = self.recovery_start else {
+            self.recovery_start = Some(now);
+            return;
+        };
+
+        let now_millis = now.total_millis();
+
+        // If the last update was less than 100ms ago, don't update the congestion window.
+        if self.last_update > recovery_start && now_millis - self.last_update.total_millis() < 100 {
+            return;
+        }
+
+        // Elapsed time since the start of the recovery phase.
+        let t = now_millis - recovery_start.total_millis();
+        if t < 0 {
+            return;
+        }
+
+        // K = (w_max * (1 - beta) / C)^(1/3)
+        let k3 = ((self.w_max as f64) * (1.0 - BETA_CUBIC)) / C;
+        let k = if let Some(k) = cube_root(k3) {
+            k
+        } else {
+            return;
+        };
+
+        // cwnd = C(T - K)^3 + w_max
+        let s = t as f64 / 1000.0 - k;
+        let s = s * s * s;
+        let cwnd = C * s + self.w_max as f64;
+
+        self.last_update = now;
+
+        self.cwnd = (cwnd as usize).max(self.min_cwnd).min(self.rwnd);
+    }
+
+    fn set_mss(&mut self, mss: usize) {
+        self.min_cwnd = mss;
+    }
+}
+
+#[inline]
+fn abs(a: f64) -> f64 {
+    if a < 0.0 {
+        -a
+    } else {
+        a
+    }
+}
+
+/// Calculate cube root by using the Newton-Raphson method.
+fn cube_root(a: f64) -> Option<f64> {
+    if a <= 0.0 {
+        return None;
+    }
+
+    let (tolerance, init) = if a < 1_000.0 {
+        (1.0, 8.879040017426005) // cube_root(700.0)
+    } else if a < 1_000_000.0 {
+        (5.0, 88.79040017426004) // cube_root(700_000.0)
+    } else if a < 1_000_000_000.0 {
+        (50.0, 887.9040017426004) // cube_root(700_000_000.0)
+    } else if a < 1_000_000_000_000.0 {
+        (500.0, 8879.040017426003) // cube_root(700_000_000_000.0)
+    } else if a < 1_000_000_000_000_000.0 {
+        (5000.0, 88790.40017426001) // cube_root(700_000_000_000.0)
+    } else {
+        (50000.0, 887904.0017426) // cube_root(700_000_000_000_000.0)
+    };
+
+    let mut x = init; // initial value
+    let mut n = 20; // The maximum iteration
+    loop {
+        let next_x = (2.0 * x + a / (x * x)) / 3.0;
+        if abs(next_x - x) < tolerance {
+            return Some(next_x);
+        }
+        x = next_x;
+
+        if n == 0 {
+            return Some(next_x);
+        }
+
+        n -= 1;
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{socket::tcp::RttEstimator, time::Instant};
+
+    use super::*;
+
+    #[test]
+    fn test_cubic() {
+        let remote_window = 64 * 1024 * 1024;
+        let now = Instant::from_millis(0);
+
+        for i in 0..10 {
+            for j in 0..9 {
+                let mut cubic = Cubic::new();
+                // Set remote window.
+                cubic.set_remote_window(remote_window);
+
+                cubic.set_mss(1480);
+
+                if i & 1 == 0 {
+                    cubic.on_retransmit(now);
+                } else {
+                    cubic.on_duplicate_ack(now);
+                }
+
+                cubic.pre_transmit(now);
+
+                let mut n = i;
+                for _ in 0..j {
+                    n *= i;
+                }
+
+                let elapsed = Instant::from_millis(n);
+                cubic.pre_transmit(elapsed);
+
+                let cwnd = cubic.window();
+                println!("Cubic: elapsed = {}, cwnd = {}", elapsed, cwnd);
+
+                assert!(cwnd >= cubic.min_cwnd);
+                assert!(cubic.window() <= remote_window);
+            }
+        }
+    }
+
+    #[test]
+    fn cubic_time_inversion() {
+        let mut cubic = Cubic::new();
+
+        let t1 = Instant::from_micros(0);
+        let t2 = Instant::from_micros(i64::MAX);
+
+        cubic.on_retransmit(t2);
+        cubic.pre_transmit(t1);
+
+        let cwnd = cubic.window();
+        println!("Cubic:time_inversion: cwnd: {}, cubic: {cubic:?}", cwnd);
+
+        assert!(cwnd >= cubic.min_cwnd);
+        assert!(cwnd <= cubic.rwnd);
+    }
+
+    #[test]
+    fn cubic_long_elapsed_time() {
+        let mut cubic = Cubic::new();
+
+        let t1 = Instant::from_millis(0);
+        let t2 = Instant::from_micros(i64::MAX);
+
+        cubic.on_retransmit(t1);
+        cubic.pre_transmit(t2);
+
+        let cwnd = cubic.window();
+        println!("Cubic:long_elapsed_time: cwnd: {}", cwnd);
+
+        assert!(cwnd >= cubic.min_cwnd);
+        assert!(cwnd <= cubic.rwnd);
+    }
+
+    #[test]
+    fn cubic_last_update() {
+        let mut cubic = Cubic::new();
+
+        let t1 = Instant::from_millis(0);
+        let t2 = Instant::from_millis(100);
+        let t3 = Instant::from_millis(199);
+        let t4 = Instant::from_millis(20000);
+
+        cubic.on_retransmit(t1);
+
+        cubic.pre_transmit(t2);
+        let cwnd2 = cubic.window();
+
+        cubic.pre_transmit(t3);
+        let cwnd3 = cubic.window();
+
+        cubic.pre_transmit(t4);
+        let cwnd4 = cubic.window();
+
+        println!(
+            "Cubic:last_update: cwnd2: {}, cwnd3: {}, cwnd4: {}",
+            cwnd2, cwnd3, cwnd4
+        );
+
+        assert_eq!(cwnd2, cwnd3);
+        assert_ne!(cwnd2, cwnd4);
+    }
+
+    #[test]
+    fn cubic_slow_start() {
+        let mut cubic = Cubic::new();
+
+        let t1 = Instant::from_micros(0);
+
+        let cwnd = cubic.window();
+        let ack_len = 1024;
+
+        cubic.on_ack(t1, ack_len, &RttEstimator::default());
+
+        assert!(cubic.window() > cwnd);
+
+        for i in 1..1000 {
+            let t2 = Instant::from_micros(i);
+            cubic.on_ack(t2, ack_len * 100, &RttEstimator::default());
+            assert!(cubic.window() <= cubic.rwnd);
+        }
+
+        let t3 = Instant::from_micros(2000);
+
+        let cwnd = cubic.window();
+        cubic.on_retransmit(t3);
+        assert_eq!(cwnd >> 1, cubic.ssthresh);
+    }
+
+    #[test]
+    fn cubic_pre_transmit() {
+        let mut cubic = Cubic::new();
+        cubic.pre_transmit(Instant::from_micros(2000));
+    }
+
+    #[test]
+    fn test_cube_root() {
+        for n in (1..1000000).step_by(99) {
+            let a = n as f64;
+            let a = a * a * a;
+            let result = cube_root(a);
+            println!("cube_root({a}) = {}", result.unwrap());
+        }
+    }
+
+    #[test]
+    #[should_panic]
+    fn cube_root_zero() {
+        cube_root(0.0).unwrap();
+    }
+}

+ 11 - 0
src/socket/tcp/congestion/no_control.rs

@@ -0,0 +1,11 @@
+use super::Controller;
+
+#[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct NoControl;
+
+impl Controller for NoControl {
+    fn window(&self) -> usize {
+        usize::MAX
+    }
+}

+ 130 - 0
src/socket/tcp/congestion/reno.rs

@@ -0,0 +1,130 @@
+use crate::{socket::tcp::RttEstimator, time::Instant};
+
+use super::Controller;
+
+#[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct Reno {
+    cwnd: usize,
+    min_cwnd: usize,
+    ssthresh: usize,
+    rwnd: usize,
+}
+
+impl Reno {
+    pub fn new() -> Self {
+        Reno {
+            cwnd: 1024 * 2,
+            min_cwnd: 1024 * 2,
+            ssthresh: usize::MAX,
+            rwnd: 64 * 1024,
+        }
+    }
+}
+
+impl Controller for Reno {
+    fn window(&self) -> usize {
+        self.cwnd
+    }
+
+    fn on_ack(&mut self, _now: Instant, len: usize, _rtt: &RttEstimator) {
+        let len = if self.cwnd < self.ssthresh {
+            // Slow start.
+            len
+        } else {
+            self.ssthresh = self.cwnd;
+            self.min_cwnd
+        };
+
+        self.cwnd = self
+            .cwnd
+            .saturating_add(len)
+            .min(self.rwnd)
+            .max(self.min_cwnd);
+    }
+
+    fn on_duplicate_ack(&mut self, _now: Instant) {
+        self.ssthresh = (self.cwnd >> 1).max(self.min_cwnd);
+    }
+
+    fn on_retransmit(&mut self, _now: Instant) {
+        self.cwnd = (self.cwnd >> 1).max(self.min_cwnd);
+    }
+
+    fn set_mss(&mut self, mss: usize) {
+        self.min_cwnd = mss;
+    }
+
+    fn set_remote_window(&mut self, remote_window: usize) {
+        if self.rwnd < remote_window {
+            self.rwnd = remote_window;
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::time::Instant;
+
+    use super::*;
+
+    #[test]
+    fn test_reno() {
+        let remote_window = 64 * 1024;
+        let now = Instant::from_millis(0);
+
+        for i in 0..10 {
+            for j in 0..9 {
+                let mut reno = Reno::new();
+                reno.set_mss(1480);
+
+                // Set remote window.
+                reno.set_remote_window(remote_window);
+
+                reno.on_ack(now, 4096, &RttEstimator::default());
+
+                let mut n = i;
+                for _ in 0..j {
+                    n *= i;
+                }
+
+                if i & 1 == 0 {
+                    reno.on_retransmit(now);
+                } else {
+                    reno.on_duplicate_ack(now);
+                }
+
+                let elapsed = Instant::from_millis(1000);
+                reno.on_ack(elapsed, n, &RttEstimator::default());
+
+                let cwnd = reno.window();
+                println!("Reno: elapsed = {}, cwnd = {}", elapsed, cwnd);
+
+                assert!(cwnd >= reno.min_cwnd);
+                assert!(reno.window() <= remote_window);
+            }
+        }
+    }
+
+    #[test]
+    fn reno_min_cwnd() {
+        let remote_window = 64 * 1024;
+        let now = Instant::from_millis(0);
+
+        let mut reno = Reno::new();
+        reno.set_remote_window(remote_window);
+
+        for _ in 0..100 {
+            reno.on_retransmit(now);
+            assert!(reno.window() >= reno.min_cwnd);
+        }
+    }
+
+    #[test]
+    fn reno_set_rwnd() {
+        let mut reno = Reno::new();
+        reno.set_remote_window(64 * 1024 * 1024);
+
+        println!("{reno:?}");
+    }
+}