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;