ergot_base/interface_manager/
mod.rs

1//! The Interface Manager
2//!
3//! The [`NetStack`] is generic over an "Interface Manager", which is
4//! responsible for handling any external interfaces of the current program
5//! or device.
6//!
7//! Different interface managers may support a various number of external
8//! interfaces. The simplest interface manager is a "Null Interface Manager",
9//! Which supports no external interfaces, meaning that messages may only be
10//! routed locally.
11//!
12//! The next simplest interface manager is one that only supports zero or one
13//! active interfaces, for example if a device is directly connected to a PC
14//! using USB. In this case, routing is again simple: if messages are not
15//! intended for the local device, they should be routed out of the one external
16//! interface. Similarly, if we support an interface, but it is not connected
17//! (e.g. the USB cable is unplugged), all packets with external destinations
18//! will fail to send.
19//!
20//! For more complex devices, an interface manager with multiple (bounded or
21//! unbounded) interfaces, and more complex routing capabilities, may be
22//! selected.
23//!
24//! Unlike Sockets, which might be various and diverse on all systems, a system
25//! is expected to have one statically-known interface manager, which may
26//! manage various and diverse interfaces. Therefore, the interface manager is
27//! a generic type (unlike sockets), while the interfaces owned by an interface
28//! manager use similar "trick"s like the socket list to handle different
29//! kinds of interfaces (for example, USB on one interface, and RS-485 on
30//! another).
31//!
32//! In general when sending a message, the [`NetStack`] will check if the
33//! message is definitively for the local device (e.g. Net ID = 0, Node ID = 0),
34//! and if not the NetStack will pass the message to the Interface Manager. If
35//! the interface manager can route this packet, it informs the NetStack it has
36//! done so. If the Interface Manager realizes that the packet is still for us
37//! (e.g. matching a Net ID and Node ID of the local device), it may bounce the
38//! message back to the NetStack to locally route.
39//!
40//! [`NetStack`]: crate::NetStack
41
42use crate::{Header, HeaderSeq, ProtocolError};
43use serde::Serialize;
44
45pub mod cobs_stream;
46pub mod framed_stream;
47pub mod null;
48
49#[cfg(feature = "embassy-usb-v0_4")]
50pub mod eusb_0_4_client;
51
52#[cfg(feature = "nusb-v0_1")]
53pub mod nusb_0_1_router;
54
55#[cfg(feature = "std")]
56pub mod std_tcp_client;
57#[cfg(feature = "std")]
58pub mod std_tcp_router;
59#[cfg(feature = "std")]
60pub mod std_utils;
61
62#[derive(Debug, PartialEq, Eq)]
63#[non_exhaustive]
64pub enum InterfaceSendError {
65    /// Refusing to send local destination remotely
66    DestinationLocal,
67    /// Interface Manager does not know how to route to requested destination
68    NoRouteToDest,
69    /// Interface Manager found a destination interface, but that interface
70    /// was full in space/slots
71    InterfaceFull,
72    /// TODO: Remove
73    PlaceholderOhNo,
74    /// Destination was an "any" port, but a key was not provided
75    AnyPortMissingKey,
76    /// TTL has reached the terminal value
77    TtlExpired,
78}
79
80pub trait ConstInit {
81    const INIT: Self;
82}
83
84// An interface send is very similar to a socket send, with the exception
85// that interface sends are ALWAYS a serializing operation (or requires
86// serialization has already been done), which means we don't need to
87// differentiate between "send owned" and "send borrowed". The exception
88// to this is "send raw", where serialization has already been done, e.g.
89// if we are routing a packet.
90pub trait InterfaceManager {
91    fn send<T: Serialize>(&mut self, hdr: &Header, data: &T) -> Result<(), InterfaceSendError>;
92    fn send_err(&mut self, hdr: &Header, err: ProtocolError) -> Result<(), InterfaceSendError>;
93    fn send_raw(
94        &mut self,
95        hdr: &Header,
96        hdr_raw: &[u8],
97        data: &[u8],
98    ) -> Result<(), InterfaceSendError>;
99}
100
101impl InterfaceSendError {
102    pub fn to_error(&self) -> ProtocolError {
103        match self {
104            InterfaceSendError::DestinationLocal => ProtocolError::ISE_DESTINATION_LOCAL,
105            InterfaceSendError::NoRouteToDest => ProtocolError::ISE_NO_ROUTE_TO_DEST,
106            InterfaceSendError::InterfaceFull => ProtocolError::ISE_INTERFACE_FULL,
107            InterfaceSendError::PlaceholderOhNo => ProtocolError::ISE_PLACEHOLDER_OH_NO,
108            InterfaceSendError::AnyPortMissingKey => ProtocolError::ISE_ANY_PORT_MISSING_KEY,
109            InterfaceSendError::TtlExpired => ProtocolError::ISE_TTL_EXPIRED,
110        }
111    }
112}
113
114pub mod wire_frames {
115    use log::warn;
116    use postcard::{Serializer, ser_flavors};
117    use serde::{Deserialize, Serialize};
118
119    use crate::{
120        Address, AnyAllAppendix, FrameKind, HeaderSeq, Key, ProtocolError, nash::NameHash,
121    };
122
123    use super::BorrowedFrame;
124
125    #[derive(Serialize, Deserialize, Debug)]
126    pub struct CommonHeader {
127        pub src: u32,
128        pub dst: u32,
129        pub seq_no: u16,
130        pub kind: u8,
131        pub ttl: u8,
132    }
133
134    pub enum PartialDecodeTail<'a> {
135        Specific(&'a [u8]),
136        AnyAll {
137            apdx: AnyAllAppendix,
138            body: &'a [u8],
139        },
140        Err(ProtocolError),
141    }
142
143    pub struct PartialDecode<'a> {
144        pub hdr: CommonHeader,
145        pub hdr_raw: &'a [u8],
146        pub tail: PartialDecodeTail<'a>,
147    }
148
149    pub(crate) fn decode_frame_partial(data: &[u8]) -> Option<PartialDecode<'_>> {
150        let (common, remain) = postcard::take_from_bytes::<CommonHeader>(data).ok()?;
151        let is_err = common.kind == FrameKind::PROTOCOL_ERROR.0;
152        let any_all = [0, 255].contains(&Address::from_word(common.dst).port_id);
153
154        match (is_err, any_all) {
155            // Not allowed: any/all AND is err
156            (true, true) => {
157                warn!("Rejecting any/all protocol error message");
158                None
159            }
160            (true, false) => {
161                let hdr_raw_len = data.len() - remain.len();
162                let hdr_raw = &data[..hdr_raw_len];
163                // err
164                let (err, remain) = postcard::take_from_bytes::<ProtocolError>(remain).ok()?;
165                if !remain.is_empty() {
166                    warn!("Excess data, rejecting");
167                    return None;
168                }
169                Some(PartialDecode {
170                    hdr: common,
171                    tail: PartialDecodeTail::Err(err),
172                    hdr_raw,
173                })
174            }
175            (false, true) => {
176                let (key, remain) = postcard::take_from_bytes::<Key>(remain).ok()?;
177                let (nash, remain) = postcard::take_from_bytes::<u32>(remain).ok()?;
178                let hdr_raw_len = data.len() - remain.len();
179                let hdr_raw = &data[..hdr_raw_len];
180
181                Some(PartialDecode {
182                    hdr: common,
183                    tail: PartialDecodeTail::AnyAll {
184                        apdx: AnyAllAppendix {
185                            key,
186                            nash: NameHash::from_u32(nash),
187                        },
188                        body: remain,
189                    },
190                    hdr_raw,
191                })
192            }
193            (false, false) => {
194                let hdr_raw_len = data.len() - remain.len();
195                let hdr_raw = &data[..hdr_raw_len];
196
197                Some(PartialDecode {
198                    hdr: common,
199                    tail: PartialDecodeTail::Specific(remain),
200                    hdr_raw,
201                })
202            }
203        }
204    }
205
206    // must not be error
207    // doesn't check if dest is actually any/all
208    pub fn encode_frame_ty<F, T>(
209        flav: F,
210        hdr: &CommonHeader,
211        apdx: Option<&AnyAllAppendix>,
212        body: &T,
213    ) -> Result<F::Output, ()>
214    where
215        F: ser_flavors::Flavor,
216        T: Serialize,
217    {
218        let mut serializer = Serializer { output: flav };
219        hdr.serialize(&mut serializer).map_err(drop)?;
220
221        if let Some(app) = apdx {
222            serializer.output.try_extend(&app.key.0).map_err(drop)?;
223            let val: u32 = app.nash.as_ref().map(NameHash::to_u32).unwrap_or(0);
224            val.serialize(&mut serializer).map_err(drop)?;
225        }
226
227        body.serialize(&mut serializer).map_err(drop)?;
228        serializer.output.finalize().map_err(drop)
229    }
230
231    // must not be error
232    // doesn't check if dest is actually any/all
233    pub fn encode_frame_raw<F>(
234        flav: F,
235        hdr: &CommonHeader,
236        key: Option<&Key>,
237        body: &[u8],
238    ) -> Result<F::Output, ()>
239    where
240        F: ser_flavors::Flavor,
241    {
242        let mut serializer = Serializer { output: flav };
243        hdr.serialize(&mut serializer).map_err(drop)?;
244
245        if let Some(key) = key {
246            serializer.output.try_extend(&key.0).map_err(drop)?;
247        }
248
249        serializer.output.try_extend(body).map_err(drop)?;
250        serializer.output.finalize().map_err(drop)
251    }
252
253    pub fn encode_frame_err<F>(
254        flav: F,
255        hdr: &CommonHeader,
256        err: ProtocolError,
257    ) -> Result<F::Output, ()>
258    where
259        F: ser_flavors::Flavor,
260    {
261        let mut serializer = Serializer { output: flav };
262        hdr.serialize(&mut serializer).map_err(drop)?;
263        err.serialize(&mut serializer).map_err(drop)?;
264        serializer.output.finalize().map_err(drop)
265    }
266
267    pub fn de_frame(remain: &[u8]) -> Option<BorrowedFrame<'_>> {
268        let res = decode_frame_partial(remain)?;
269
270        let app;
271        let body = match res.tail {
272            PartialDecodeTail::Specific(body) => {
273                app = None;
274                Ok(body)
275            }
276            PartialDecodeTail::AnyAll { apdx, body } => {
277                app = Some(apdx);
278                Ok(body)
279            }
280            PartialDecodeTail::Err(protocol_error) => {
281                app = None;
282                Err(protocol_error)
283            }
284        };
285
286        let CommonHeader {
287            src,
288            dst,
289            seq_no,
290            kind,
291            ttl,
292        } = res.hdr;
293
294        Some(BorrowedFrame {
295            hdr: HeaderSeq {
296                src: Address::from_word(src),
297                dst: Address::from_word(dst),
298                seq_no,
299                any_all: app,
300                kind: FrameKind(kind),
301                ttl,
302            },
303            body,
304            hdr_raw: res.hdr_raw,
305        })
306    }
307}
308
309#[allow(dead_code)]
310pub struct BorrowedFrame<'a> {
311    pub hdr: HeaderSeq,
312    pub hdr_raw: &'a [u8],
313    pub body: Result<&'a [u8], ProtocolError>,
314}