Skip to content
File

Blob: firmware/vendor/sctp-proto/src/association/timer.rs

rust244 lines
1use core::time::Duration;
2use std::time::Instant;
3 
4use crate::config::{RTO_INITIAL, RTO_MAX, RTO_MIN};
5 
6pub(crate) const ACK_INTERVAL: u64 = 200;
7const TIMER_COUNT: usize = 6;
8 
9#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
10pub(crate) enum Timer {
11 T1Init = 0,
12 T1Cookie = 1,
13 T2Shutdown = 2,
14 T3RTX = 3,
15 Reconfig = 4,
16 Ack = 5,
17}
18 
19impl 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)]
32pub(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 
43impl 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 
55impl 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 
141const RTO_ALPHA: u64 = 1;
142const RTO_BETA: u64 = 2;
143const 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)]
148pub(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 
158impl 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 
172impl 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 
232fn 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}