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        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;