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 Some(Self {
80 source_mac: control.source_mac.clone(),
81 source_network: control.npdu.source.clone(),
82 report: NetworkReport::parse_with(&control.npdu, number)?,
83 })
84 }
85}
86
87impl NetworkReport {
88 /// Parse an overheard NPDU using its logical broadcast/unicast delivery,
89 /// without manufacturing transport provenance or starting a network layer.
90 pub fn parse_npdu(npdu: &Npdu, link_layer_group: bool) -> Option<Self> {
91 Self::parse_with(npdu, NumberControl::parse_npdu(npdu, link_layer_group))
92 }
93
94 fn parse_with(npdu: &Npdu, number: Option<NumberControl>) -> Option<Self> {
95 Some(match npdu.message_type? {
96 NetworkMessageType::I_AM_ROUTER_TO_NETWORK => {
97 let networks = Networks::parse(&npdu.payload).ok()?;
98 let listed: Vec<u16> = networks.iter().filter(|&n| !is_reserved(n)).collect();
99 if listed.is_empty() {
100 return None;
101 }
102 NetworkReport::IAmRouterToNetwork(listed)
103 }
104 NetworkMessageType::NETWORK_NUMBER_IS => match number? {
105 NumberControl::NumberIs { number, flag } => NetworkReport::NetworkNumberIs {
106 network: number,
107 configured: flag == NetworkNumberIs::CONFIGURED,
108 },
109 NumberControl::WhatIs => return None,
110 },
111 NetworkMessageType::REJECT_MESSAGE_TO_NETWORK => NetworkReport::RejectMessageToNetwork(
112 RejectMessageToNetwork::decode(&npdu.payload).ok()?,
113 ),
114 _ => return None,
115 })
116 }
117}
118
119impl<T: TransportPort + 'static> NetworkLayer<T> {
120 /// Send a network-layer message on the local network, with no DNET:
121 /// broadcast when `destination_mac` is `None`, else to that peer.
122 ///
123 /// The NPDU names no destination network, so routers act on it
124 /// themselves rather than pass it on, as Who-Is-Router-To-Network and
125 /// What-Is-Network-Number need (Clauses 6.4.1 and 6.4.19). A
126 /// `destination_mac` that is one of the link's local broadcast
127 /// destinations goes out as the local broadcast, as
128 /// [`Self::send_apdu`] does. Only the types in
129 /// [`SENDABLE_NETWORK_MESSAGES`] are sent; any other is refused before
130 /// anything is sent.
131 pub async fn send_network_message(
132 &self,
133 message_type: NetworkMessageType,
134 payload: &[u8],
135 destination_mac: Option<&[u8]>,
136 ) -> Result<(), Error> {
137 if !SENDABLE_NETWORK_MESSAGES.contains(&message_type) {
138 return Err(Error::Encoding(format!(
139 "network message type {message_type} is not one a non-routing node sends; \
140 only Who-Is-Router-To-Network and What-Is-Network-Number are"
141 )));
142 }
143 let npdu = Npdu {
144 message_type: Some(message_type),
145 payload: Bytes::copy_from_slice(payload),
146 ..Npdu::default()
147 };
148 let mut buf = BytesMut::with_capacity(3 + payload.len());
149 encode_npdu(&mut buf, &npdu)?;
150 match destination_mac {
151 Some(mac) if !self.local_broadcasts.contains(mac) => {
152 self.transport.send_unicast(&buf, mac).await
153 }
154 _ => self.transport.send_broadcast(&buf).await,
155 }
156 }
157}
158
159#[cfg(test)]
160#[path = "layer_network_message_tests.rs"]
161mod tests;