ndn_transport/face.rs
1use bytes::Bytes;
2use thiserror::Error;
3
4/// Link-layer source address returned by multicast/broadcast faces.
5///
6/// Using a dedicated enum (rather than re-exporting the `ndn-discovery`
7/// `LinkAddr`) keeps `ndn-transport` free of a dependency on `ndn-discovery`.
8/// The engine converts `FaceAddr` into `ndn_discovery::InboundMeta` in the
9/// face reader, which *does* depend on `ndn-discovery`.
10#[derive(Clone, Debug)]
11pub enum FaceAddr {
12 /// Source UDP `SocketAddr` from `recvfrom` on a multicast socket.
13 Udp(std::net::SocketAddr),
14 /// Source MAC address extracted from the Ethernet frame header.
15 Ether([u8; 6]),
16}
17
18/// Opaque identifier for a face. Cheap to copy; safe to use across tasks.
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
20pub struct FaceId(pub u32);
21
22impl FaceId {
23 pub const INVALID: FaceId = FaceId(u32::MAX);
24}
25
26impl core::fmt::Display for FaceId {
27 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
28 write!(f, "face#{}", self.0)
29 }
30}
31
32/// Classifies a face by its transport type (informational; not used for routing).
33#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
35#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
36pub enum FaceKind {
37 Udp,
38 Tcp,
39 Unix,
40 Ethernet,
41 EtherMulticast,
42 App,
43 Shm,
44 Serial,
45 Bluetooth,
46 Wfb,
47 Compute,
48 Internal,
49 Multicast,
50 WebSocket,
51 /// Management socket face (Unix domain, operator-level trust).
52 ///
53 /// Faces of this kind are created by the router's NFD face listener for
54 /// connections to the management socket. The Unix socket's filesystem
55 /// permissions (`0600`, owned by the router user) serve as the
56 /// authentication boundary — commands from `Management` faces are granted
57 /// operator-level access without requiring signed Interests.
58 Management,
59}
60
61impl FaceKind {
62 /// Whether this face is local (in-process / same-host IPC) or non-local (network).
63 pub fn scope(&self) -> FaceScope {
64 match self {
65 FaceKind::Unix
66 | FaceKind::App
67 | FaceKind::Shm
68 | FaceKind::Internal
69 | FaceKind::Management
70 | FaceKind::WebSocket => FaceScope::Local,
71 FaceKind::Udp
72 | FaceKind::Tcp
73 | FaceKind::Ethernet
74 | FaceKind::EtherMulticast
75 | FaceKind::Serial
76 | FaceKind::Bluetooth
77 | FaceKind::Wfb
78 | FaceKind::Compute
79 | FaceKind::Multicast => FaceScope::NonLocal,
80 }
81 }
82
83 /// Whether this face has operator-level implicit trust (management socket).
84 pub fn is_management(&self) -> bool {
85 matches!(self, FaceKind::Management)
86 }
87}
88
89impl core::fmt::Display for FaceKind {
90 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
91 f.write_str(match self {
92 Self::Udp => "udp",
93 Self::Tcp => "tcp",
94 Self::Unix => "unix",
95 Self::Ethernet => "ethernet",
96 Self::EtherMulticast => "ether-multicast",
97 Self::App => "app",
98 Self::Shm => "shm",
99 Self::Serial => "serial",
100 Self::Bluetooth => "bluetooth",
101 Self::Wfb => "wfb",
102 Self::Compute => "compute",
103 Self::Internal => "internal",
104 Self::Multicast => "multicast",
105 Self::WebSocket => "web-socket",
106 Self::Management => "management",
107 })
108 }
109}
110
111impl core::str::FromStr for FaceKind {
112 type Err = ();
113
114 fn from_str(s: &str) -> Result<Self, Self::Err> {
115 match s {
116 "udp" => Ok(Self::Udp),
117 "tcp" => Ok(Self::Tcp),
118 "unix" => Ok(Self::Unix),
119 "ethernet" => Ok(Self::Ethernet),
120 "ether-multicast" => Ok(Self::EtherMulticast),
121 "app" => Ok(Self::App),
122 "shm" => Ok(Self::Shm),
123 "serial" => Ok(Self::Serial),
124 "bluetooth" => Ok(Self::Bluetooth),
125 "wfb" => Ok(Self::Wfb),
126 "compute" => Ok(Self::Compute),
127 "internal" => Ok(Self::Internal),
128 "multicast" => Ok(Self::Multicast),
129 "web-socket" => Ok(Self::WebSocket),
130 "management" => Ok(Self::Management),
131 _ => Err(()),
132 }
133 }
134}
135
136/// Whether a face is local (same-host IPC) or non-local (network).
137///
138/// NFD uses this to enforce that `/localhost` prefixes never cross non-local
139/// faces — a security boundary preventing management Interests from leaking
140/// onto the network.
141#[derive(Clone, Copy, Debug, PartialEq, Eq)]
142pub enum FaceScope {
143 Local,
144 NonLocal,
145}
146
147/// Face persistence level (NFD semantics).
148///
149/// - `OnDemand` (0): created by a listener, destroyed on idle timeout or I/O error.
150/// - `Persistent` (1): created by management command, survives I/O errors.
151/// - `Permanent` (2): never destroyed, even on I/O errors (multicast, always-on links).
152#[derive(Clone, Copy, Debug, PartialEq, Eq)]
153pub enum FacePersistency {
154 OnDemand = 0,
155 Persistent = 1,
156 Permanent = 2,
157}
158
159/// Link type for a face — indicates the connectivity model of the underlying link.
160///
161/// Forwarding strategies use this to decide whether to suppress duplicate Interests
162/// on the same face (multi-access suppression) and to select appropriate forwarding
163/// algorithms for partially-connected topologies.
164///
165/// Matches the NFD link-type model:
166/// - `PointToPoint`: single-peer link (unicast TCP, UDP, serial, Unix socket).
167/// - `MultiAccess`: all nodes on the link receive every frame (Ethernet multicast,
168/// UDP multicast on a wired LAN or Wi-Fi infrastructure AP).
169/// - `AdHoc`: partially-connected wireless (Wi-Fi IBSS, MANET) — not all nodes
170/// hear every frame, so multi-access suppression should be disabled.
171#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
172#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
173#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
174pub enum LinkType {
175 #[default]
176 PointToPoint,
177 MultiAccess,
178 AdHoc,
179}
180
181impl core::fmt::Display for LinkType {
182 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
183 f.write_str(match self {
184 Self::PointToPoint => "point-to-point",
185 Self::MultiAccess => "multi-access",
186 Self::AdHoc => "ad-hoc",
187 })
188 }
189}
190
191impl FacePersistency {
192 pub fn from_u64(v: u64) -> Option<Self> {
193 match v {
194 0 => Some(Self::OnDemand),
195 1 => Some(Self::Persistent),
196 2 => Some(Self::Permanent),
197 _ => None,
198 }
199 }
200}
201
202#[derive(Debug, Error)]
203pub enum FaceError {
204 #[error("face closed")]
205 Closed,
206 #[error("I/O error: {0}")]
207 Io(#[from] std::io::Error),
208 #[error("send buffer full")]
209 Full,
210}
211
212/// The core face abstraction.
213///
214/// `recv` is called only from the face's own task (single consumer).
215/// `send` may be called concurrently from multiple pipeline tasks (must be `&self`
216/// and internally synchronised where the underlying transport requires it).
217pub trait Face: Send + Sync + 'static {
218 fn id(&self) -> FaceId;
219 fn kind(&self) -> FaceKind;
220
221 /// Remote URI (e.g. `udp4://192.168.1.1:6363`). Returns `None` for face
222 /// types that don't have a meaningful remote endpoint.
223 fn remote_uri(&self) -> Option<String> {
224 None
225 }
226
227 /// Local URI (e.g. `unix:///run/nfd/nfd.sock`). Returns `None` for face
228 /// types that don't expose local binding info.
229 fn local_uri(&self) -> Option<String> {
230 None
231 }
232
233 /// Receive the next packet. Blocks until a packet arrives or the face closes.
234 fn recv(&self) -> impl Future<Output = Result<Bytes, FaceError>> + Send;
235
236 /// Receive the next packet together with the link-layer sender address.
237 ///
238 /// The default implementation returns `None` for the source address.
239 /// Multicast and broadcast faces override this to return the link-layer
240 /// source, enabling discovery to create unicast reply faces without
241 /// embedding addresses in NDN payloads.
242 fn recv_with_addr(
243 &self,
244 ) -> impl Future<Output = Result<(Bytes, Option<FaceAddr>), FaceError>> + Send {
245 async { self.recv().await.map(|b| (b, None)) }
246 }
247
248 /// Send a packet. Must not block the caller; use internal buffering.
249 ///
250 /// # LP encoding convention
251 ///
252 /// Network-facing transports (UDP, TCP, Serial, Ethernet) wrap the raw NDN
253 /// packet in an NDNLPv2 `LpPacket` envelope before writing to the wire.
254 /// Local transports (Unix, App, SHM) send the raw packet as-is — the
255 /// pipeline already strips LP framing before forwarding.
256 ///
257 /// [`StreamFace`](crate::StreamFace) makes this explicit via the `lp_encode`
258 /// constructor parameter. Custom face implementations should follow the
259 /// same convention based on [`FaceKind::scope()`].
260 fn send(&self, pkt: Bytes) -> impl Future<Output = Result<(), FaceError>> + Send;
261
262 /// Link type of this face.
263 ///
264 /// The default is [`LinkType::PointToPoint`] for all unicast transports.
265 /// Multicast and broadcast faces override this to [`LinkType::MultiAccess`].
266 /// Wi-Fi ad-hoc / MANET faces should return [`LinkType::AdHoc`].
267 fn link_type(&self) -> LinkType {
268 LinkType::PointToPoint
269 }
270}
271
272use std::future::Future;