Skip to main content

bacnet_router_core/router/
host.rs

1//! What the router asks of its caller during a call: the [`Host`] hooks.
2
3use crate::message::{MessageType, Networks, RejectReason, RoutingEntries};
4use crate::table::RejectEffect;
5use crate::PortId;
6
7/// The caller's side of a [`receive`](super::Router::receive) or
8/// [`on_deadline`](super::Router::on_deadline) call.
9///
10/// Every method has a default, so `impl Host for MyHost {}` gives a router
11/// that knows no group addresses, admits every protected message and
12/// reports nothing: what [`Permissive`] is.
13///
14/// The router calls these with its state borrowed, and a hosted router
15/// calls them under the lock it keeps the router in: an implementation
16/// must not block, and must never call into the router or take that lock.
17pub trait Host {
18    /// Whether `mac` is a group address on `port`, and of which kind: asked
19    /// only for a DADR the router would hand to a node on that directly
20    /// attached port (#1504, #1677).
21    fn group(&self, port: PortId, mac: &[u8]) -> Group {
22        let _ = (port, mac);
23        Group::No
24    }
25
26    /// Whether to act on a protected network message (RB-09). The router
27    /// asks after the message's data has been checked and before anything
28    /// changes. `false` drops it silently: no table change, relay, answer
29    /// or reject.
30    fn authorize(&mut self, claim: &Claim<'_>) -> bool {
31        let _ = claim;
32        true
33    }
34
35    /// Something the hosted router logs today happened.
36    fn observe(&mut self, event: Event) {
37        let _ = event;
38    }
39}
40
41/// What a DADR is on its delivery port, as [`Host::group`] says.
42///
43/// The router sends nothing to a group address as a unicast. An
44/// Unconfirmed-Request for a [`Broadcast`](Self::Broadcast) one goes out as
45/// that port's local broadcast instead, and reaches the router's own
46/// application where a DLEN 0 broadcast would: when it came from that
47/// network. Any other APDU for one, anything for a [`Refuse`](Self::Refuse)
48/// one, and every network message for either, is dropped and counted in
49/// [`Counters::group_dadr_drops`](super::Counters::group_dadr_drops), with
50/// no reject (#1677, decisions L19, L23 and L24).
51#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
52pub enum Group {
53    /// Not a group address there: sent to it as addressed.
54    #[default]
55    No,
56    /// A group address of the port's own: on B/IP, a broadcast or
57    /// multicast address at the link's own UDP port.
58    Broadcast,
59    /// A group address outside the port's own: on B/IP, a broadcast or
60    /// multicast address at another UDP port, which the link's broadcast
61    /// would not reach.
62    Refuse,
63}
64
65/// A [`Host`] with every default: no group addresses, every protected
66/// message admitted, nothing observed. For firmware and tests.
67#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
68pub struct Permissive;
69
70impl Host for Permissive {}
71
72/// The protected classes of network message, one per kind of change a
73/// message can make to the routing table.
74///
75/// It is `#[non_exhaustive]`, so a new class isn't a breaking change: a
76/// `match` needs a wildcard arm.
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
78#[non_exhaustive]
79pub enum ControlClass {
80    /// I-Am-Router learning.
81    IAm,
82    /// Initialize-Routing-Table-Ack learning.
83    InitAck,
84    /// Initialize-Routing-Table updates and removals (not queries).
85    InitMgmt,
86    /// Router-Busy marks.
87    Busy,
88    /// Router-Available marks.
89    Available,
90    /// Changes a received reject makes.
91    Reject,
92    /// Routes stored from I-Could-Be-Router.
93    ICouldBe,
94}
95
96/// The networks a protected message names.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum Targets<'c> {
99    /// Networks by number, as I-Am-Router, Router-Busy and
100    /// Router-Available carry them, or the one network a reject or an
101    /// I-Could-Be names.
102    Networks(Networks<'c>),
103    /// The routing entries an Initialize-Routing-Table, or an Ack to one,
104    /// carries.
105    Entries(RoutingEntries<'c>),
106}
107
108impl<'c> Targets<'c> {
109    /// The networks, in the order the message lists them.
110    pub fn iter(&self) -> impl Iterator<Item = u16> + 'c {
111        let (list, entries) = match *self {
112            Self::Networks(list) => (Some(list.iter()), None),
113            Self::Entries(entries) => (None, Some(entries.iter().map(|entry| entry.network))),
114        };
115        list.into_iter()
116            .flatten()
117            .chain(entries.into_iter().flatten())
118    }
119
120    /// Whether it names none.
121    #[must_use]
122    pub fn is_empty(&self) -> bool {
123        self.iter().next().is_none()
124    }
125}
126
127/// What [`Host::authorize`] is told about a protected message. It carries
128/// what the message claims, none of it verified.
129///
130/// The router builds it. It is `#[non_exhaustive]`, so a new field isn't a
131/// breaking change: a pattern needs `..`.
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133#[non_exhaustive]
134pub struct Claim<'c> {
135    /// The class of change it would make.
136    pub class: ControlClass,
137    /// The port it arrived on.
138    pub port: PortId,
139    /// That port's network number.
140    pub port_network: u16,
141    /// The link-layer address of the node that sent it.
142    pub source: &'c [u8],
143    /// Its message type.
144    pub message_type: MessageType,
145    /// Its vendor ID, for a proprietary type.
146    pub vendor_id: Option<u16>,
147    /// The SNET it claims, if any.
148    pub snet: Option<u16>,
149    /// The DNET it names, if any.
150    pub dnet: Option<u16>,
151    /// The networks it names.
152    pub targets: Targets<'c>,
153    /// A Router-Busy or Router-Available that lists no network: it covers
154    /// every network the sender serves.
155    pub targets_omitted: bool,
156}
157
158/// What [`Host::observe`] hears about: what the hosted router logs today
159/// that the call's result and actions don't show.
160///
161/// It is `#[non_exhaustive]`, so a new event isn't a breaking change: a
162/// `match` needs a wildcard arm.
163#[derive(Debug, Clone, Copy, PartialEq, Eq)]
164#[non_exhaustive]
165pub enum Event {
166    /// A learning claim installed or refreshed a route.
167    Learned {
168        /// The network.
169        network: u16,
170        /// The port it is reached through.
171        port: PortId,
172        /// I-Am-Router or Initialize-Routing-Table-Ack.
173        class: ControlClass,
174    },
175    /// A route moved port often enough to count as flapping.
176    Flapping {
177        /// The network.
178        network: u16,
179        /// Moves within 60 s of the one before, this one included.
180        flaps: u8,
181    },
182    /// The route cap ended a message's learning or update, or turned an
183    /// I-Could-Be-Router offer away.
184    RouteCap {
185        /// The class of the message.
186        class: ControlClass,
187    },
188    /// A reject arrived and was applied to the table.
189    RejectReceived {
190        /// The network it names.
191        network: u16,
192        /// Its reason.
193        reason: RejectReason,
194        /// What it did to the route.
195        effect: RejectEffect,
196    },
197    /// Router-Busy or Router-Available covered a network.
198    Marked {
199        /// The network.
200        network: u16,
201        /// Busy, or available.
202        busy: bool,
203    },
204    /// An Initialize-Routing-Table entry installed a route.
205    UpdateApplied {
206        /// The network.
207        network: u16,
208        /// The port it now goes through.
209        port: PortId,
210    },
211    /// An Initialize-Routing-Table entry with Port ID 0 removed a route.
212    UpdateRemoved {
213        /// The network.
214        network: u16,
215    },
216    /// An Initialize-Routing-Table entry named no port of this router, and
217    /// was skipped.
218    UnknownPortId {
219        /// The network.
220        network: u16,
221        /// The Port ID it named.
222        port_id: u8,
223    },
224    /// An I-Could-Be-Router arrived.
225    ICouldBe {
226        /// The network on offer.
227        network: u16,
228        /// The sender's rating of the connection.
229        performance_index: u8,
230        /// Whether it was stored: only where the table had no route, and
231        /// was under the route cap.
232        stored: bool,
233    },
234    /// An Establish-Connection arrived; this router opens no connections.
235    EstablishConnection {
236        /// The network.
237        network: u16,
238        /// The termination time it asked for.
239        termination_time: u8,
240    },
241    /// A Disconnect-Connection arrived; with no connections, nothing is
242    /// removed.
243    DisconnectIgnored {
244        /// The network.
245        network: u16,
246    },
247    /// A message of the removed security types arrived; it is not
248    /// rejected.
249    SecurityMessage {
250        /// Its type.
251        message_type: MessageType,
252    },
253    /// A message of a type this router doesn't handle arrived for it; it
254    /// draws reject reason 3.
255    UnknownMessage {
256        /// Its type.
257        message_type: MessageType,
258    },
259    /// A Network-Number-Is reported a number other than the port's.
260    NumberConflict {
261        /// The port it arrived on.
262        port: PortId,
263        /// The port's configured number.
264        local: u16,
265        /// The number reported.
266        reported: u16,
267        /// Whether the sender's number was configured rather than learned.
268        configured: bool,
269    },
270    /// A relay went nowhere because its hop count had run out. It comes
271    /// for every such relay, including one whose frame was also delivered
272    /// locally (a global broadcast, or a DLEN 0 broadcast on the arrival
273    /// network), which [`Received`](super::Received) doesn't show.
274    HopCountSpent {
275        /// The network the relay was for: 0xFFFF for a global broadcast.
276        network: u16,
277    },
278    /// An NPDU too long for an egress port's limit for its kind of send
279    /// (fix R1). One routed toward a network drew reject reason 4 naming
280    /// it; a fan-out leaves that port out when it is polled, where the skip
281    /// is counted. Both count in
282    /// [`Counters::egress_too_long`](super::Counters::egress_too_long).
283    TooLong {
284        /// The egress port.
285        port: PortId,
286        /// The NPDU's length, as rewritten for that port.
287        octets: usize,
288        /// The port's limit for that kind of send.
289        limit: usize,
290        /// The network the reject named; `None` for a fan-out.
291        rejected: Option<u16>,
292    },
293    /// A refused NPDU named this router as its origin, so no reject went.
294    OwnOrigin {
295        /// The network the reject would have named.
296        network: u16,
297    },
298    /// The sweep removed a learned route not confirmed for too long.
299    Purged {
300        /// The network.
301        network: u16,
302    },
303    /// The sweep dropped a hardened-mode claim that waited too long.
304    PendingExpired {
305        /// The network.
306        network: u16,
307    },
308    /// The sweep forgot a search for an unknown network.
309    DiscoveryExpired {
310        /// The network.
311        network: u16,
312    },
313}