Skip to main content

netget/server/bfd/
session.rs

1//! One BFD session in Asynchronous mode (RFC 5880 §6.8): the state machine, the timers, the
2//! Poll Sequence and the authentication sequence numbers. Pure apart from the clock it is
3//! handed, so the server (passive role) and the client (active role) run the same code.
4//! Demand mode and the Echo function are not implemented: NetGet never sets the D bit and
5//! always advertises a Required Min Echo RX Interval of zero.
6use super::packet::{self, AuthConfig, ControlPacket, State};
7use anyhow::{bail, ensure, Result};
8use serde_json::{json, Value};
9use std::time::Duration;
10use tokio::time::Instant;
11
12/// §6.8.3: while the session is not Up, Desired Min TX is at least one second.
13pub const SLOW_TX_US: u32 = 1_000_000;
14pub const DEFAULT_DESIRED_MIN_TX_MS: u32 = 300;
15pub const DEFAULT_REQUIRED_MIN_RX_MS: u32 = 300;
16pub const DEFAULT_DETECT_MULT: u8 = 3;
17/// The range NetGet accepts for an interval, in milliseconds.
18pub const MIN_INTERVAL_MS: u32 = 10;
19pub const MAX_INTERVAL_MS: u32 = 60_000;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct Timers {
23    pub desired_min_tx_us: u32,
24    pub required_min_rx_us: u32,
25    pub detect_mult: u8,
26}
27
28impl Default for Timers {
29    fn default() -> Self {
30        Self {
31            desired_min_tx_us: DEFAULT_DESIRED_MIN_TX_MS * 1000,
32            required_min_rx_us: DEFAULT_REQUIRED_MIN_RX_MS * 1000,
33            detect_mult: DEFAULT_DETECT_MULT,
34        }
35    }
36}
37
38impl Timers {
39    /// From an action or startup parameters: `desired_min_tx_ms`, `required_min_rx_ms`,
40    /// `detect_mult`, each optional, falling back to `base`.
41    pub fn from_json(v: &Value, base: Timers) -> Result<Self> {
42        let ms = |key: &str, fallback: u32| -> Result<u32> {
43            match v.get(key) {
44                None | Some(Value::Null) => Ok(fallback),
45                Some(x) => {
46                    let n = x
47                        .as_u64()
48                        .filter(|n| (MIN_INTERVAL_MS as u64..=MAX_INTERVAL_MS as u64).contains(n));
49                    match n {
50                        Some(n) => Ok(n as u32 * 1000),
51                        None => bail!(
52                            "{key} must be a whole number of milliseconds, {MIN_INTERVAL_MS}-{MAX_INTERVAL_MS}"
53                        ),
54                    }
55                }
56            }
57        };
58        let detect_mult = match v.get("detect_mult") {
59            None | Some(Value::Null) => base.detect_mult,
60            Some(x) => {
61                let n = x.as_u64().unwrap_or(0);
62                ensure!((1..=255).contains(&n), "detect_mult must be 1-255");
63                n as u8
64            }
65        };
66        Ok(Self {
67            desired_min_tx_us: ms("desired_min_tx_ms", base.desired_min_tx_us / 1000)?,
68            required_min_rx_us: ms("required_min_rx_ms", base.required_min_rx_us / 1000)?,
69            detect_mult,
70        })
71    }
72}
73
74/// What one received packet did to the session.
75#[derive(Debug, Default)]
76pub struct Received {
77    /// The state before and after, when it changed.
78    pub transition: Option<(State, State)>,
79    /// The packet had Poll set: answer at once with Final.
80    pub send_final: bool,
81}
82
83pub struct Session {
84    pub local_discr: u32,
85    pub remote_discr: u32,
86    pub state: State,
87    pub remote_state: State,
88    pub local_diag: u8,
89    /// What the operator or the model asked for.
90    pub configured: Timers,
91    /// What the packets say now (Desired Min TX raised to a second while not Up).
92    advertised: Timers,
93    /// The values the local timing actually uses: a Poll Sequence defers an increase of the
94    /// transmit interval and a decrease of the receive interval until the Final (§6.8.3).
95    tx_in_force_us: u32,
96    rx_in_force_us: u32,
97    pub poll: bool,
98    pub remote_desired_min_tx_us: u32,
99    pub remote_min_rx_us: u32,
100    pub remote_detect_mult: u8,
101    pub passive: bool,
102    auth: Option<AuthConfig>,
103    xmit_seq: u32,
104    rcv_seq: Option<u32>,
105    pub last_rx: Option<Instant>,
106}
107
108impl Session {
109    pub fn new(
110        local_discr: u32,
111        configured: Timers,
112        auth: Option<AuthConfig>,
113        passive: bool,
114    ) -> Self {
115        let advertised = Timers {
116            desired_min_tx_us: configured.desired_min_tx_us.max(SLOW_TX_US),
117            ..configured
118        };
119        Self {
120            local_discr,
121            remote_discr: 0,
122            state: State::Down,
123            remote_state: State::Down,
124            local_diag: 0,
125            configured,
126            advertised,
127            tx_in_force_us: advertised.desired_min_tx_us,
128            rx_in_force_us: advertised.required_min_rx_us,
129            poll: false,
130            remote_desired_min_tx_us: 0,
131            // §6.8.1: initialized to 1 so the first packets go out.
132            remote_min_rx_us: 1,
133            remote_detect_mult: 0,
134            passive,
135            auth,
136            xmit_seq: rand::random(),
137            rcv_seq: None,
138            last_rx: None,
139        }
140    }
141
142    /// Advertise new values. While Up, a change starts a Poll Sequence; an increase of the
143    /// transmit interval or a decrease of the receive interval takes effect at the Final.
144    fn advertise(&mut self) {
145        let next = Timers {
146            desired_min_tx_us: if self.state == State::Up {
147                self.configured.desired_min_tx_us
148            } else {
149                self.configured.desired_min_tx_us.max(SLOW_TX_US)
150            },
151            ..self.configured
152        };
153        if next == self.advertised {
154            return;
155        }
156        if self.state == State::Up {
157            self.poll = true;
158            if next.desired_min_tx_us < self.tx_in_force_us {
159                self.tx_in_force_us = next.desired_min_tx_us;
160            }
161            if next.required_min_rx_us > self.rx_in_force_us {
162                self.rx_in_force_us = next.required_min_rx_us;
163            }
164        } else {
165            self.tx_in_force_us = next.desired_min_tx_us;
166            self.rx_in_force_us = next.required_min_rx_us;
167        }
168        self.advertised = next;
169    }
170
171    pub fn set_timers(&mut self, timers: Timers) {
172        self.configured = timers;
173        self.advertise();
174    }
175
176    fn set_state(&mut self, next: State, diag: u8) -> Option<(State, State)> {
177        if next == self.state {
178            return None;
179        }
180        let previous = self.state;
181        self.state = next;
182        self.local_diag = diag;
183        self.advertise();
184        Some((previous, next))
185    }
186
187    pub fn admin_down(&mut self, diag: u8) -> Option<(State, State)> {
188        self.set_state(State::AdminDown, diag)
189    }
190
191    pub fn admin_up(&mut self) -> Option<(State, State)> {
192        if self.state != State::AdminDown {
193            return None;
194        }
195        self.set_state(State::Down, 0)
196    }
197
198    /// §6.8.6, after the packet has been decoded and matched to this session.
199    pub fn receive(&mut self, bytes: &[u8], p: &ControlPacket, now: Instant) -> Result<Received> {
200        packet::verify(bytes, p, self.auth.as_ref())?;
201        if let (Some(config), Some(seq)) = (&self.auth, p.auth.as_ref().and_then(|a| a.sequence)) {
202            if let Some(last) = self.rcv_seq {
203                let window = 3 * p.detect_mult as u32;
204                let ahead = seq.wrapping_sub(last);
205                let ok = if config.kind.meticulous() {
206                    (1..=window).contains(&ahead)
207                } else {
208                    ahead <= window
209                };
210                ensure!(
211                    ok,
212                    "sequence number {seq} is outside the window after {last}"
213                );
214            }
215            self.rcv_seq = Some(seq);
216        }
217        let mut out = Received {
218            send_final: p.poll,
219            ..Default::default()
220        };
221        self.last_rx = Some(now);
222        self.remote_discr = p.my_discriminator;
223        self.remote_state = p.state;
224        self.remote_desired_min_tx_us = p.desired_min_tx_us;
225        self.remote_min_rx_us = p.required_min_rx_us;
226        self.remote_detect_mult = p.detect_mult;
227        if p.final_ && self.poll {
228            self.poll = false;
229            self.tx_in_force_us = self.advertised.desired_min_tx_us;
230            self.rx_in_force_us = self.advertised.required_min_rx_us;
231        }
232        if self.state == State::AdminDown {
233            return Ok(out);
234        }
235        out.transition = match (p.state, self.state) {
236            (State::AdminDown, local) if local != State::Down => self.set_state(State::Down, 3),
237            (State::AdminDown, _) => None,
238            (State::Down, State::Down) => self.set_state(State::Init, 0),
239            (State::Init, State::Down) => self.set_state(State::Up, 0),
240            (State::Init | State::Up, State::Init) => self.set_state(State::Up, 0),
241            (State::Down, State::Up) => self.set_state(State::Down, 3),
242            _ => None,
243        };
244        Ok(out)
245    }
246
247    /// The packet to send now: `final_` answers a Poll.
248    pub fn packet(&mut self, final_: bool) -> Vec<u8> {
249        let p = ControlPacket {
250            diag: self.local_diag,
251            state: self.state,
252            poll: self.poll && !final_,
253            final_,
254            control_plane_independent: false,
255            demand: false,
256            detect_mult: self.advertised.detect_mult,
257            my_discriminator: self.local_discr,
258            your_discriminator: self.remote_discr,
259            desired_min_tx_us: self.advertised.desired_min_tx_us,
260            required_min_rx_us: self.advertised.required_min_rx_us,
261            required_min_echo_rx_us: 0,
262            auth: None,
263        };
264        let auth = self.auth.as_ref().map(|a| {
265            if a.kind != packet::AuthType::SimplePassword {
266                self.xmit_seq = self.xmit_seq.wrapping_add(1);
267            }
268            (a, self.xmit_seq)
269        });
270        p.encode(auth)
271    }
272
273    /// §6.8.7: a passive system is silent until it knows the remote discriminator, and nobody
274    /// sends to a peer asking for no packets.
275    pub fn may_transmit(&self) -> bool {
276        !(self.passive && self.remote_discr == 0) && self.remote_min_rx_us != 0
277    }
278
279    /// The interval to the next periodic packet, with the §6.8.7 jitter: 75–100% of the
280    /// interval, or 75–90% with a Detect Mult of one.
281    pub fn next_interval(&self) -> Duration {
282        let base = self.tx_in_force_us.max(self.remote_min_rx_us) as u64;
283        let top = if self.advertised.detect_mult == 1 {
284            90
285        } else {
286            100
287        };
288        let pct = rand::random::<u64>() % (top - 75 + 1) + 75;
289        Duration::from_micros(base * pct / 100)
290    }
291
292    pub fn tx_interval_us(&self) -> u32 {
293        self.tx_in_force_us.max(self.remote_min_rx_us)
294    }
295
296    /// §6.8.4, Asynchronous mode.
297    pub fn detection_time(&self) -> Duration {
298        Duration::from_micros(
299            self.remote_detect_mult as u64
300                * self.rx_in_force_us.max(self.remote_desired_min_tx_us) as u64,
301        )
302    }
303
304    /// When the session goes Down for silence, if it is waiting on the peer at all.
305    pub fn detection_deadline(&self) -> Option<Instant> {
306        match self.state {
307            State::Init | State::Up => self.last_rx.map(|t| t + self.detection_time()),
308            _ => None,
309        }
310    }
311
312    /// The Detection Time passed: Down with diagnostic 1, and the remote discriminator is
313    /// forgotten (§6.8.1).
314    pub fn detection_expired(&mut self) -> Option<(State, State)> {
315        self.remote_discr = 0;
316        self.rcv_seq = None;
317        self.set_state(State::Down, 1)
318    }
319
320    /// The session as the model sees it.
321    pub fn snapshot(&self) -> Value {
322        json!({
323            "state": self.state.name(),
324            "diag": packet::diag_name(self.local_diag),
325            "remote_state": self.remote_state.name(),
326            "local_discriminator": self.local_discr,
327            "remote_discriminator": self.remote_discr,
328            "desired_min_tx_ms": self.configured.desired_min_tx_us / 1000,
329            "required_min_rx_ms": self.configured.required_min_rx_us / 1000,
330            "detect_mult": self.configured.detect_mult,
331            "remote_desired_min_tx_ms": self.remote_desired_min_tx_us / 1000,
332            "remote_required_min_rx_ms": self.remote_min_rx_us / 1000,
333            "remote_detect_mult": self.remote_detect_mult,
334            "tx_interval_ms": self.tx_interval_us() / 1000,
335            "detection_time_ms": self.detection_time().as_millis() as u64,
336            "poll_in_progress": self.poll,
337            "authentication": self.auth.as_ref().map(|a| a.kind.name()),
338        })
339    }
340}