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}