rtc_sctp/association/
timer.rs

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