bacnet_network/layer_network_message.rs
1//! The network-layer messages a non-routing node's user sends and reads
2//! (Clause 6.4): a send for a local network message, and the reports the
3//! user acts on, read from a received control with the one parser each
4//! payload has (#1794, #1818).
5
6use super::*;
7use crate::network_number::NumberControl;
8use bacnet_npdu::{NetworkMessageType, RejectMessageToNetwork};
9use bacnet_router_core::message::{NetworkNumberIs, Networks};
10use bacnet_router_core::table::is_reserved;
11
12/// The network messages [`NetworkLayer::send_network_message`] sends: the
13/// two questions a non-routing node asks. The rest change routing tables,
14/// flow control or connections, or announce routes this node doesn't
15/// have, so each joins the list only deliberately, with its own review.
16pub const SENDABLE_NETWORK_MESSAGES: [NetworkMessageType; 2] = [
17 NetworkMessageType::WHO_IS_ROUTER_TO_NETWORK,
18 NetworkMessageType::WHAT_IS_NETWORK_NUMBER,
19];
20
21/// A network message that tells a non-routing node something about the
22/// internetwork, read from a [`ReceivedNetworkControl`].
23#[derive(Debug, Clone, PartialEq, Eq)]
24#[non_exhaustive]
25pub enum NetworkReport {
26 /// I-Am-Router-To-Network (Clause 6.4.2): the sender routes to these
27 /// networks, in the order it listed them.
28 IAmRouterToNetwork(Vec<u16>),
29 /// Network-Number-Is (Clause 6.4.20): the number of the local network,
30 /// and whether the sender had it configured rather than learned.
31 NetworkNumberIs {
32 /// The local network's number, in 1..=65534.
33 network: u16,
34 /// Whether the sender's number was configured (flag 1).
35 configured: bool,
36 },
37 /// Reject-Message-To-Network (Clause 6.4.4): a router refused a message
38 /// this node sent, for the reason and network it names.
39 RejectMessageToNetwork(RejectMessageToNetwork),
40}
41
42/// A [`NetworkReport`] and where it came from.
43#[derive(Debug, Clone, PartialEq, Eq)]
44pub struct ReceivedNetworkReport {
45 /// The link-layer peer that sent it: for an I-Am-Router-To-Network, the
46 /// router to send through to reach the networks it lists.
47 pub source_mac: MacAddr,
48 /// SNET/SADR, when a router passed the message on from another network,
49 /// as a reject from a router further along the path is.
50 pub source_network: Option<NpduAddress>,
51 /// What the message reports.
52 pub report: NetworkReport,
53}
54
55impl ReceivedNetworkReport {
56 /// Read `control` as a report, or `None` for any other message type or
57 /// a payload its parser refuses.
58 ///
59 /// - I-Am-Router-To-Network: router-core's [`Networks`] view. Entries
60 /// 0 and 0xFFFF name no network a router can reach and are dropped,
61 /// as router-core's table drops them ([`is_reserved`]); the message is
62 /// refused only when no other entry is left.
63 /// - Network-Number-Is: [`NumberControl::parse`], the reader the local
64 /// Number controls use, so a routed, unicast or malformed one is
65 /// ignored here too (Clause 6.4.20).
66 /// - Reject-Message-To-Network: [`RejectMessageToNetwork::decode`],
67 /// exactly three octets.
68 pub fn parse(control: &ReceivedNetworkControl) -> Option<Self> {
69 Self::parse_with(control, NumberControl::parse(control))
70 }
71
72 /// [`Self::parse`] for a caller that already read `control` with
73 /// [`NumberControl::parse`] and passes that result as `number`, so a
74 /// Network-Number-Is is read once.
75 pub fn parse_with(
76 control: &ReceivedNetworkControl,
77 number: Option<NumberControl>,
78 ) -> Option<Self> {
79 let npdu = &control.npdu;
80 let report = match npdu.message_type? {
81 NetworkMessageType::I_AM_ROUTER_TO_NETWORK => {
82 let networks = Networks::parse(&npdu.payload).ok()?;
83 let listed: Vec<u16> = networks.iter().filter(|&n| !is_reserved(n)).collect();
84 if listed.is_empty() {
85 return None;
86 }
87 NetworkReport::IAmRouterToNetwork(listed)
88 }
89 NetworkMessageType::NETWORK_NUMBER_IS => match number? {
90 NumberControl::NumberIs { number, flag } => NetworkReport::NetworkNumberIs {
91 network: number,
92 configured: flag == NetworkNumberIs::CONFIGURED,
93 },
94 NumberControl::WhatIs => return None,
95 },
96 NetworkMessageType::REJECT_MESSAGE_TO_NETWORK => NetworkReport::RejectMessageToNetwork(
97 RejectMessageToNetwork::decode(&npdu.payload).ok()?,
98 ),
99 _ => return None,
100 };
101 Some(Self {
102 source_mac: control.source_mac.clone(),
103 source_network: npdu.source.clone(),
104 report,
105 })
106 }
107}
108
109impl<T: TransportPort + 'static> NetworkLayer<T> {
110 /// Send a network-layer message on the local network, with no DNET:
111 /// broadcast when `destination_mac` is `None`, else to that peer.
112 ///
113 /// The NPDU names no destination network, so routers act on it
114 /// themselves rather than pass it on, as Who-Is-Router-To-Network and
115 /// What-Is-Network-Number need (Clauses 6.4.1 and 6.4.19). A
116 /// `destination_mac` that is one of the link's local broadcast
117 /// destinations goes out as the local broadcast, as
118 /// [`Self::send_apdu`] does. Only the types in
119 /// [`SENDABLE_NETWORK_MESSAGES`] are sent; any other is refused before
120 /// anything is sent.
121 pub async fn send_network_message(
122 &self,
123 message_type: NetworkMessageType,
124 payload: &[u8],
125 destination_mac: Option<&[u8]>,
126 ) -> Result<(), Error> {
127 if !SENDABLE_NETWORK_MESSAGES.contains(&message_type) {
128 return Err(Error::Encoding(format!(
129 "network message type {message_type} is not one a non-routing node sends; \
130 only Who-Is-Router-To-Network and What-Is-Network-Number are"
131 )));
132 }
133 let npdu = Npdu {
134 message_type: Some(message_type),
135 payload: Bytes::copy_from_slice(payload),
136 ..Npdu::default()
137 };
138 let mut buf = BytesMut::with_capacity(3 + payload.len());
139 encode_npdu(&mut buf, &npdu)?;
140 match destination_mac {
141 Some(mac) if !self.local_broadcasts.contains(mac) => {
142 self.transport.send_unicast(&buf, mac).await
143 }
144 _ => self.transport.send_broadcast(&buf).await,
145 }
146 }
147}
148
149#[cfg(test)]
150#[path = "layer_network_message_tests.rs"]
151mod tests;