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