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}