File
Blob: firmware/vendor/sctp-proto/src/association/timer.rs
| 1 | use core::time::Duration; |
| 2 | use std::time::Instant; |
| 3 | |
| 4 | use crate::config::{RTO_INITIAL, RTO_MAX, RTO_MIN}; |
| 5 | |
| 6 | pub(crate) const ACK_INTERVAL: u64 = 200; |
| 7 | const TIMER_COUNT: usize = 6; |
| 8 | |
| 9 | #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] |
| 10 | pub(crate) enum Timer { |
| 11 | T1Init = 0, |
| 12 | T1Cookie = 1, |
| 13 | T2Shutdown = 2, |
| 14 | T3RTX = 3, |
| 15 | Reconfig = 4, |
| 16 | Ack = 5, |
| 17 | } |
| 18 | |
| 19 | impl Timer { |
| 20 | pub(crate) const VALUES: [Self; TIMER_COUNT] = [ |
| 21 | Timer::T1Init, |
| 22 | Timer::T1Cookie, |
| 23 | Timer::T2Shutdown, |
| 24 | Timer::T3RTX, |
| 25 | Timer::Reconfig, |
| 26 | Timer::Ack, |
| 27 | ]; |
| 28 | } |
| 29 | |
| 30 | /// A table of data associated with each distinct kind of `Timer` |
| 31 | #[derive(Debug, Copy, Clone)] |
| 32 | pub(crate) struct TimerTable { |
| 33 | data: [Option<Instant>; TIMER_COUNT], |
| 34 | retrans: [usize; TIMER_COUNT], |
| 35 | /// Maximum retransmissions for each timer. `None` means unlimited. |
| 36 | max_retrans: [Option<usize>; TIMER_COUNT], |
| 37 | /// Timers whose next expiry must not increment the error counter |
| 38 | no_error_count: [bool; TIMER_COUNT], |
| 39 | /// Maximum RTO value for exponential backoff. |
| 40 | rto_max: u64, |
| 41 | } |
| 42 | |
| 43 | impl Default for TimerTable { |
| 44 | fn default() -> Self { |
| 45 | TimerTable { |
| 46 | data: [None; TIMER_COUNT], |
| 47 | retrans: [0; TIMER_COUNT], |
| 48 | max_retrans: [None; TIMER_COUNT], |
| 49 | no_error_count: [false; TIMER_COUNT], |
| 50 | rto_max: 60000, // Default RTO_MAX |
| 51 | } |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | impl TimerTable { |
| 56 | pub fn new( |
| 57 | max_init_retransmits: Option<usize>, |
| 58 | max_data_retransmits: Option<usize>, |
| 59 | rto_max: u64, |
| 60 | ) -> Self { |
| 61 | TimerTable { |
| 62 | max_retrans: [ |
| 63 | max_init_retransmits, //T1Init |
| 64 | max_init_retransmits, //T1Cookie |
| 65 | None, //T2Shutdown (unlimited) |
| 66 | max_data_retransmits, //T3RTX |
| 67 | max_init_retransmits, //Reconfig |
| 68 | None, //Ack (unlimited) |
| 69 | ], |
| 70 | rto_max, |
| 71 | ..Default::default() |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | pub fn set(&mut self, timer: Timer, time: Option<Instant>) { |
| 76 | self.data[timer as usize] = time; |
| 77 | } |
| 78 | |
| 79 | pub fn get(&self, timer: Timer) -> Option<Instant> { |
| 80 | self.data[timer as usize] |
| 81 | } |
| 82 | |
| 83 | pub fn next_timeout(&self) -> Option<Instant> { |
| 84 | self.data.iter().filter_map(|&x| x).min() |
| 85 | } |
| 86 | |
| 87 | pub fn start(&mut self, timer: Timer, now: Instant, interval: u64) { |
| 88 | let interval = if timer == Timer::Ack { |
| 89 | interval |
| 90 | } else { |
| 91 | calculate_next_timeout(interval, self.retrans[timer as usize], self.rto_max) |
| 92 | }; |
| 93 | |
| 94 | let time = now + Duration::from_millis(interval); |
| 95 | self.data[timer as usize] = Some(time); |
| 96 | } |
| 97 | |
| 98 | /// Restarts the timer if the current instant is none or elapsed. |
| 99 | pub fn restart_if_stale(&mut self, timer: Timer, now: Instant, interval: u64) { |
| 100 | if let Some(current) = self.data[timer as usize] { |
| 101 | if current >= now { |
| 102 | return; |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | self.start(timer, now, interval); |
| 107 | } |
| 108 | |
| 109 | pub fn stop(&mut self, timer: Timer) { |
| 110 | self.data[timer as usize] = None; |
| 111 | self.retrans[timer as usize] = 0; |
| 112 | self.no_error_count[timer as usize] = false; |
| 113 | } |
| 114 | |
| 115 | /// Exempt the timer's next expiry from the `max_retrans` accounting. |
| 116 | pub fn suppress_error_count(&mut self, timer: Timer) { |
| 117 | self.no_error_count[timer as usize] = true; |
| 118 | } |
| 119 | |
| 120 | pub fn is_expired(&mut self, timer: Timer, after: Instant) -> (bool, bool, usize) { |
| 121 | let expired = self.data[timer as usize].is_some_and(|x| x <= after); |
| 122 | let mut failure = false; |
| 123 | if expired { |
| 124 | if self.no_error_count[timer as usize] { |
| 125 | self.no_error_count[timer as usize] = false; |
| 126 | } else { |
| 127 | self.retrans[timer as usize] += 1; |
| 128 | if let Some(max) = self.max_retrans[timer as usize] { |
| 129 | if self.retrans[timer as usize] > max { |
| 130 | failure = true; |
| 131 | } |
| 132 | } |
| 133 | // If max_retrans is None, failure stays false (unlimited) |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | (expired, failure, self.retrans[timer as usize]) |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | const RTO_ALPHA: u64 = 1; |
| 142 | const RTO_BETA: u64 = 2; |
| 143 | const RTO_BASE: u64 = 8; |
| 144 | |
| 145 | /// rtoManager manages Rtx timeout values. |
| 146 | /// This is an implementation of RFC 4960 sec 6.3.1. |
| 147 | #[derive(Debug)] |
| 148 | pub(crate) struct RtoManager { |
| 149 | pub(crate) srtt: u64, |
| 150 | pub(crate) rttvar: f64, |
| 151 | pub(crate) rto: u64, |
| 152 | pub(crate) no_update: bool, |
| 153 | pub(crate) rto_initial: u64, |
| 154 | pub(crate) rto_min: u64, |
| 155 | pub(crate) rto_max: u64, |
| 156 | } |
| 157 | |
| 158 | impl Default for RtoManager { |
| 159 | fn default() -> Self { |
| 160 | RtoManager { |
| 161 | srtt: 0, |
| 162 | rttvar: 0.0, |
| 163 | rto: RTO_INITIAL, |
| 164 | no_update: false, |
| 165 | rto_initial: RTO_INITIAL, |
| 166 | rto_min: RTO_MIN, |
| 167 | rto_max: RTO_MAX, |
| 168 | } |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | impl RtoManager { |
| 173 | /// Creates a new RtoManager with configurable RTO values. |
| 174 | pub(crate) fn new(rto_initial: u64, rto_min: u64, rto_max: u64) -> Self { |
| 175 | RtoManager { |
| 176 | srtt: 0, |
| 177 | rttvar: 0.0, |
| 178 | rto: rto_initial, |
| 179 | no_update: false, |
| 180 | rto_initial, |
| 181 | rto_min, |
| 182 | rto_max, |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | /// set_new_rtt takes a newly measured RTT then adjust the RTO in msec. |
| 187 | pub(crate) fn set_new_rtt(&mut self, rtt: u64) -> u64 { |
| 188 | if self.no_update { |
| 189 | return self.srtt; |
| 190 | } |
| 191 | |
| 192 | if self.srtt == 0 { |
| 193 | // First measurement |
| 194 | self.srtt = rtt; |
| 195 | self.rttvar = rtt as f64 / 2.0; |
| 196 | } else { |
| 197 | // Subsequent rtt measurement |
| 198 | self.rttvar = ((RTO_BASE - RTO_BETA) as f64 * self.rttvar |
| 199 | + RTO_BETA as f64 * (self.srtt as i64 - rtt as i64).abs() as f64) |
| 200 | / RTO_BASE as f64; |
| 201 | self.srtt = ((RTO_BASE - RTO_ALPHA) * self.srtt + RTO_ALPHA * rtt) / RTO_BASE; |
| 202 | } |
| 203 | |
| 204 | self.rto = (self.srtt + (4.0 * self.rttvar) as u64).clamp(self.rto_min, self.rto_max); |
| 205 | |
| 206 | self.srtt |
| 207 | } |
| 208 | |
| 209 | /// get_rto simply returns the current RTO in msec. |
| 210 | pub(crate) fn get_rto(&self) -> u64 { |
| 211 | self.rto |
| 212 | } |
| 213 | |
| 214 | /// reset resets the RTO variables to the initial values. |
| 215 | pub(crate) fn reset(&mut self) { |
| 216 | if self.no_update { |
| 217 | return; |
| 218 | } |
| 219 | |
| 220 | self.srtt = 0; |
| 221 | self.rttvar = 0.0; |
| 222 | self.rto = self.rto_initial; |
| 223 | } |
| 224 | |
| 225 | /// set RTO value for testing |
| 226 | pub(crate) fn set_rto(&mut self, rto: u64, no_update: bool) { |
| 227 | self.rto = rto; |
| 228 | self.no_update = no_update; |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | fn calculate_next_timeout(rto: u64, n_rtos: usize, rto_max: u64) -> u64 { |
| 233 | // RFC 4096 sec 6.3.3. Handle T3-rtx Expiration |
| 234 | // E2) For the destination address for which the timer expires, set RTO |
| 235 | // <- RTO * 2 ("back off the timer"). The maximum value discussed |
| 236 | // in rule C7 above (RTO.max) may be used to provide an upper bound |
| 237 | // to this doubling operation. |
| 238 | if n_rtos < 31 { |
| 239 | core::cmp::min(rto << n_rtos, rto_max) |
| 240 | } else { |
| 241 | rto_max |
| 242 | } |
| 243 | } |