ndn_discovery/strategy/
mod.rs

1//! Probe-scheduling strategies for neighbor discovery.
2//!
3//! A [`NeighborProbeStrategy`] controls **when** hellos and liveness probes are
4//! sent.  The state machine (face creation, FIB wiring, neighbor table
5//! mutations) lives in the protocol implementation (`EtherND`, `UdpND`, …) and
6//! is completely independent.
7//!
8//! ## Strategy types
9//!
10//! | Module | Type | Profile |
11//! |--------|------|---------|
12//! | [`backoff`] | [`BackoffScheduler`] | LAN, Campus, Static |
13//! | [`reactive`] | [`ReactiveScheduler`] | Mobile, low-traffic nodes |
14//! | [`passive`] | [`PassiveScheduler`] | HighMobility, dense mesh |
15//!
16//! ## Factory
17//!
18//! [`build_strategy`] constructs the right scheduler from a [`DiscoveryConfig`].
19//! Pass the result as `Box<dyn NeighborProbeStrategy>` into the protocol
20//! constructor; swap it at runtime without restarting the protocol.
21
22pub mod backoff;
23pub mod composite;
24pub mod passive;
25pub mod reactive;
26pub mod swim;
27
28pub use backoff::BackoffScheduler;
29pub use composite::CompositeStrategy;
30pub use passive::PassiveScheduler;
31pub use reactive::ReactiveScheduler;
32pub use swim::SwimScheduler;
33
34use std::time::{Duration, Instant};
35
36use ndn_transport::FaceId;
37
38use crate::config::{DiscoveryConfig, HelloStrategyKind};
39
40// ─── ProbeRequest ────────────────────────────────────────────────────────────
41
42/// An action returned by [`NeighborProbeStrategy::on_tick`].
43#[derive(Clone, Debug, PartialEq, Eq)]
44pub enum ProbeRequest {
45    /// Send a hello Interest on the multicast / broadcast face.
46    Broadcast,
47    /// Send a unicast hello Interest directly to this face.
48    Unicast(FaceId),
49}
50
51// ─── TriggerEvent ────────────────────────────────────────────────────────────
52
53/// An out-of-band event that informs the strategy and may cause an early probe.
54#[derive(Clone, Debug, PartialEq, Eq)]
55pub enum TriggerEvent {
56    /// A new face came up; send a hello immediately to bootstrap the neighbor
57    /// table.
58    FaceUp,
59    /// A forwarding failure occurred (Nack received or no FIB match); re-probe
60    /// to verify the affected face is still alive.
61    ForwardingFailure,
62    /// A neighbor's liveness deadline expired without a hello response; switch
63    /// to fast re-probing (`ESTABLISHED → STALE`).
64    NeighborStale,
65    /// A packet was passively observed from an unknown source MAC.  Only
66    /// meaningful for [`PassiveScheduler`].
67    PassiveDetection,
68}
69
70// ─── NeighborProbeStrategy ───────────────────────────────────────────────────
71
72/// Controls the *when* of hello/probe scheduling.
73///
74/// # Contract
75///
76/// - [`on_tick`] is called at a regular interval set by
77///   [`DiscoveryConfig::hello_interval_base`].  It returns zero or more
78///   [`ProbeRequest`]s to execute this tick.
79/// - [`on_probe_success`] is called when a probe response is received.  The
80///   strategy may use this to reset its backoff interval or annotate quality
81///   measurements.
82/// - [`on_probe_timeout`] is called when a pending probe times out.  The
83///   strategy advances its failure counter and may escalate the probe rate.
84/// - [`trigger`] is called for out-of-band topology events.  The strategy may
85///   schedule an immediate probe on the next [`on_tick`] call.
86///
87/// All methods take `&mut self`; the protocol wraps the strategy in
88/// `Mutex<Box<dyn NeighborProbeStrategy>>` so it can be replaced at runtime.
89pub trait NeighborProbeStrategy: Send + 'static {
90    /// Advance the scheduler's clock to `now`.
91    ///
92    /// Returns the set of probes that should be sent this tick.  An empty
93    /// `Vec` means "nothing to do yet"; the protocol should call this again
94    /// on the next tick interval.
95    fn on_tick(&mut self, now: Instant) -> Vec<ProbeRequest>;
96
97    /// A probe response was received with the given round-trip time.
98    ///
99    /// Reset failure counters and back-off intervals.  `rtt` may be used by
100    /// adaptive schedulers to tune the next probe interval.
101    fn on_probe_success(&mut self, rtt: Duration);
102
103    /// A probe timed out (no response within `probe_timeout`).
104    ///
105    /// Advance failure counters; escalate probe rate if appropriate.
106    fn on_probe_timeout(&mut self);
107
108    /// An external topology event occurred.
109    ///
110    /// The strategy should arrange for a probe to be scheduled (typically on
111    /// the next [`on_tick`]) unless rate-limiting applies.
112    fn trigger(&mut self, event: TriggerEvent);
113}
114
115// ─── Factory ─────────────────────────────────────────────────────────────────
116
117/// Construct the appropriate [`NeighborProbeStrategy`] for the given config.
118///
119/// The mapping follows [`HelloStrategyKind`]:
120///
121/// | Kind | Scheduler |
122/// |------|-----------|
123/// | `Backoff` | [`BackoffScheduler`] |
124/// | `Reactive` | [`ReactiveScheduler`] |
125/// | `Passive` | [`PassiveScheduler`] (falls back to backoff when idle) |
126/// | `Swim` | [`BackoffScheduler`] (SWIM strategy not yet implemented) |
127pub fn build_strategy(cfg: &DiscoveryConfig) -> Box<dyn NeighborProbeStrategy> {
128    match cfg.hello_strategy {
129        HelloStrategyKind::Backoff => Box::new(BackoffScheduler::from_discovery_config(cfg)),
130        HelloStrategyKind::Swim => Box::new(SwimScheduler::from_discovery_config(cfg)),
131        HelloStrategyKind::Reactive => Box::new(ReactiveScheduler::from_discovery_config(cfg)),
132        HelloStrategyKind::Passive => Box::new(PassiveScheduler::from_discovery_config(cfg)),
133    }
134}