ergot_base/interface_manager/profiles/direct_edge/
mod.rs

1//! "Edge" device profile
2//!
3//! Edge devices are the second simplest device profile, and are intended for devices
4//! that are on the "edge" of a network, e.g. they have a single upstream connection
5//! to a bridge or seed router.
6//!
7//! These devices use as many tricks as possible to be as simple as possible. They
8//! initially start not knowing their network ID, and if a packet is sent to them,
9//! they assume the destination net ID is their net ID. They will also blindly send
10//! any outgoing packets, rather than trying to determine whether that packet is
11//! actually routable to a node on the network.
12
13use log::{debug, trace};
14use serde::Serialize;
15
16#[cfg(feature = "embedded-io-async-v0_6")]
17pub mod eio_0_6;
18
19#[cfg(feature = "embassy-usb-v0_4")]
20pub mod eusb_0_4;
21
22#[cfg(feature = "embassy-usb-v0_5")]
23pub mod eusb_0_5;
24
25#[cfg(feature = "std")]
26pub mod std_tcp;
27
28use crate::{
29    Header, ProtocolError,
30    interface_manager::{
31        Interface, InterfaceSendError, InterfaceSink, InterfaceState, Profile, SetStateError,
32    },
33    wire_frames::CommonHeader,
34};
35
36pub const CENTRAL_NODE_ID: u8 = 1;
37pub const EDGE_NODE_ID: u8 = 2;
38
39pub enum SetNetIdError {
40    CantSetZero,
41    NoActiveSink,
42}
43
44// TODO: call this something like "point to point edge"
45pub struct DirectEdge<I: Interface> {
46    sink: I::Sink,
47    seq_no: u16,
48    state: InterfaceState,
49    own_node_id: u8,
50    other_node_id: u8,
51}
52
53impl<I: Interface> DirectEdge<I> {
54    pub const fn new_target(sink: I::Sink) -> Self {
55        Self {
56            sink,
57            seq_no: 0,
58            state: InterfaceState::Down,
59            own_node_id: EDGE_NODE_ID,
60            other_node_id: CENTRAL_NODE_ID,
61        }
62    }
63
64    pub const fn new_controller(sink: I::Sink, state: InterfaceState) -> Self {
65        Self {
66            sink,
67            seq_no: 0,
68            state,
69            own_node_id: CENTRAL_NODE_ID,
70            other_node_id: EDGE_NODE_ID,
71        }
72    }
73}
74
75impl<I: Interface> DirectEdge<I> {
76    fn common_send<'b>(
77        &'b mut self,
78        ihdr: &Header,
79    ) -> Result<(&'b mut I::Sink, CommonHeader), InterfaceSendError> {
80        let net_id = match &self.state {
81            InterfaceState::Down | InterfaceState::Inactive => {
82                return Err(InterfaceSendError::NoRouteToDest);
83            }
84            InterfaceState::ActiveLocal { .. } => {
85                // TODO: maybe also handle this?
86                return Err(InterfaceSendError::NoRouteToDest);
87            }
88            InterfaceState::Active { net_id, node_id: _ } => *net_id,
89        };
90
91        trace!("common_send header: {:?}", ihdr);
92
93        if net_id == 0 {
94            debug!("Attempted to send via interface before we have been assigned a net ID");
95            // No net_id yet, don't allow routing (todo: maybe broadcast?)
96            return Err(InterfaceSendError::NoRouteToDest);
97        }
98        // todo: we could probably keep a routing table of some kind, but for
99        // now, we treat this as a "default" route, all packets go
100
101        // TODO: a LOT of this is copy/pasted from the router, can we make this
102        // shared logic, or handled by the stack somehow?
103        if ihdr.dst.network_id == net_id && ihdr.dst.node_id == self.own_node_id {
104            return Err(InterfaceSendError::DestinationLocal);
105        }
106
107        // Now that we've filtered out "dest local" checks, see if there is
108        // any TTL left before we send to the next hop
109        let mut hdr = ihdr.clone();
110        hdr.decrement_ttl()?;
111
112        // If the source is local, rewrite the source using this interface's
113        // information so responses can find their way back here
114        if hdr.src.net_node_any() {
115            // todo: if we know the destination is EXACTLY this network,
116            // we could leave the network_id local to allow for shorter
117            // addresses
118            hdr.src.network_id = net_id;
119            hdr.src.node_id = self.own_node_id;
120        }
121
122        // If this is a broadcast message, update the destination, ignoring
123        // whatever was there before
124        if hdr.dst.port_id == 255 {
125            hdr.dst.network_id = net_id;
126            hdr.dst.node_id = self.other_node_id;
127        }
128
129        let seq_no = self.seq_no;
130        self.seq_no = self.seq_no.wrapping_add(1);
131
132        let header = CommonHeader {
133            src: hdr.src,
134            dst: hdr.dst,
135            seq_no,
136            kind: hdr.kind,
137            ttl: hdr.ttl,
138        };
139        if [0, 255].contains(&hdr.dst.port_id) && ihdr.any_all.is_none() {
140            return Err(InterfaceSendError::AnyPortMissingKey);
141        }
142
143        Ok((&mut self.sink, header))
144    }
145}
146
147impl<I: Interface> Profile for DirectEdge<I> {
148    type InterfaceIdent = ();
149
150    fn send<T: Serialize>(&mut self, hdr: &Header, data: &T) -> Result<(), InterfaceSendError> {
151        let (intfc, header) = self.common_send(hdr)?;
152
153        let res = intfc.send_ty(&header, hdr.any_all.as_ref(), data);
154
155        match res {
156            Ok(()) => Ok(()),
157            Err(()) => Err(InterfaceSendError::InterfaceFull),
158        }
159    }
160
161    fn send_err(&mut self, hdr: &Header, err: ProtocolError) -> Result<(), InterfaceSendError> {
162        let (intfc, header) = self.common_send(hdr)?;
163
164        let res = intfc.send_err(&header, err);
165
166        match res {
167            Ok(()) => Ok(()),
168            Err(()) => Err(InterfaceSendError::InterfaceFull),
169        }
170    }
171
172    fn send_raw(
173        &mut self,
174        hdr: &Header,
175        hdr_raw: &[u8],
176        data: &[u8],
177    ) -> Result<(), InterfaceSendError> {
178        let (intfc, header) = self.common_send(hdr)?;
179
180        let res = intfc.send_raw(&header, hdr_raw, data);
181
182        match res {
183            Ok(()) => Ok(()),
184            Err(()) => Err(InterfaceSendError::InterfaceFull),
185        }
186    }
187
188    fn interface_state(&mut self, _ident: ()) -> Option<InterfaceState> {
189        Some(self.state)
190    }
191
192    fn set_interface_state(
193        &mut self,
194        _ident: (),
195        state: InterfaceState,
196    ) -> Result<(), SetStateError> {
197        match state {
198            InterfaceState::Down => {
199                self.state = InterfaceState::Down;
200            }
201            InterfaceState::Inactive => {
202                self.state = InterfaceState::Inactive;
203            }
204            InterfaceState::ActiveLocal { node_id } => {
205                if node_id != self.own_node_id {
206                    return Err(SetStateError::InvalidNodeId);
207                }
208                self.state = InterfaceState::ActiveLocal { node_id };
209            }
210            InterfaceState::Active { net_id, node_id } => {
211                if node_id != self.own_node_id {
212                    return Err(SetStateError::InvalidNodeId);
213                }
214                self.state = InterfaceState::Active { net_id, node_id };
215            }
216        }
217        Ok(())
218    }
219}