ndn_discovery/
protocol.rs

1//! `DiscoveryProtocol` trait, `ProtocolId`, and `InboundMeta` types.
2
3use std::net::SocketAddr;
4use std::time::{Duration, Instant};
5
6use bytes::Bytes;
7use ndn_packet::Name;
8use ndn_transport::FaceId;
9
10use crate::{DiscoveryContext, MacAddr};
11
12/// Link-layer source address of an inbound packet.
13///
14/// Populated by the engine when the face layer can provide a sender address
15/// (multicast faces via `recv_with_source`). `None` for unicast faces where
16/// the sender identity is already implicit in the face itself.
17#[derive(Clone, Debug)]
18pub enum LinkAddr {
19    /// Source MAC extracted from the Ethernet frame header.
20    Ether(MacAddr),
21    /// Source IP:port extracted from the UDP socket (`recvfrom`).
22    Udp(SocketAddr),
23}
24
25/// Per-packet metadata passed to [`DiscoveryProtocol::on_inbound`].
26///
27/// Carries side-channel information that does not appear in the NDN wire
28/// bytes — primarily the link-layer source address needed to create a
29/// unicast reply face without embedding addresses in the Interest payload.
30#[derive(Clone, Debug, Default)]
31pub struct InboundMeta {
32    /// Source address of the sender, if the face layer exposed it.
33    pub source: Option<LinkAddr>,
34}
35
36impl InboundMeta {
37    /// Metadata with no source address (unicast face or unknown sender).
38    pub const fn none() -> Self {
39        Self { source: None }
40    }
41
42    /// Metadata carrying an Ethernet source MAC.
43    pub fn ether(mac: MacAddr) -> Self {
44        Self {
45            source: Some(LinkAddr::Ether(mac)),
46        }
47    }
48
49    /// Metadata carrying a UDP source address.
50    pub fn udp(addr: SocketAddr) -> Self {
51        Self {
52            source: Some(LinkAddr::Udp(addr)),
53        }
54    }
55}
56
57/// Stable identifier for a discovery protocol instance.
58///
59/// Used to tag FIB entries so they can be bulk-removed when the protocol
60/// stops or reconfigures, and to route inbound packets in `CompositeDiscovery`
61/// without ambiguity.
62///
63/// Implementations should use a short, descriptive ASCII string such as
64/// `"ether-nd"`, `"swim"`, or `"sd-browser"`.
65#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
66pub struct ProtocolId(pub &'static str);
67
68impl std::fmt::Display for ProtocolId {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        f.write_str(self.0)
71    }
72}
73
74/// A pluggable discovery protocol.
75///
76/// Implementations observe face lifecycle events and inbound packets;
77/// they mutate engine state exclusively through the [`DiscoveryContext`]
78/// interface, making them decoupled from the engine and independently
79/// testable.
80///
81/// # Namespace isolation
82///
83/// Each protocol declares which NDN name prefixes it uses via
84/// [`claimed_prefixes`].  [`CompositeDiscovery`] checks at construction
85/// time that no two protocols claim overlapping prefixes.  All discovery
86/// prefixes live under the reserved `/ndn/local/` sub-tree.
87///
88/// [`CompositeDiscovery`]: crate::CompositeDiscovery
89pub trait DiscoveryProtocol: Send + Sync + 'static {
90    /// Unique protocol identifier.
91    fn protocol_id(&self) -> ProtocolId;
92
93    /// NDN name prefixes this protocol reserves.
94    ///
95    /// Typically sub-prefixes of `/ndn/local/nd/` (neighbor discovery) or
96    /// `/ndn/local/sd/` (service discovery).  Used for namespace conflict
97    /// detection and inbound routing in `CompositeDiscovery`.
98    fn claimed_prefixes(&self) -> &[Name];
99
100    /// Called when a new face comes up (after `FaceTable::insert`).
101    fn on_face_up(&self, face_id: FaceId, ctx: &dyn DiscoveryContext);
102
103    /// Called when a face goes down (before `FaceTable::remove`).
104    fn on_face_down(&self, face_id: FaceId, ctx: &dyn DiscoveryContext);
105
106    /// Called for every inbound raw packet before it enters the pipeline.
107    ///
108    /// Returns `true` if the packet was consumed by this protocol and should
109    /// **not** be forwarded through the NDN pipeline.  Return `false` to let
110    /// the packet continue normally.
111    ///
112    /// `meta` carries the link-layer source address when the face layer
113    /// exposes it (multicast faces). Discovery protocols use `meta.source`
114    /// to create unicast reply faces without embedding addresses in the
115    /// Interest payload.
116    fn on_inbound(
117        &self,
118        raw: &Bytes,
119        incoming_face: FaceId,
120        meta: &InboundMeta,
121        ctx: &dyn DiscoveryContext,
122    ) -> bool;
123
124    /// Periodic tick, called by the engine's tick task at `tick_interval`.
125    ///
126    /// Use this to send hellos, check timeouts, rotate probes, and update
127    /// SWIM gossip state.
128    fn on_tick(&self, now: Instant, ctx: &dyn DiscoveryContext);
129
130    /// How often the engine should call `on_tick`.
131    ///
132    /// The default (100 ms) works for most deployments.  High-mobility
133    /// profiles may use 20–50 ms; static deployments may use 1 s.
134    fn tick_interval(&self) -> Duration {
135        Duration::from_millis(100)
136    }
137}