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}