Skip to main content

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;