use bacnet_encoding::npdu::{Npdu, NpduAddress};
use bacnet_router_core::node::{self, Node};
use bacnet_router_core::router::Discard;
use bacnet_transport::warn_throttle::WarnThrottle;
use bytes::{Buf, Bytes};
use tracing::{debug, warn};
use crate::totals::totals;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct Counters {
pub address_length_drops: u64,
pub global_broadcast_dadr_drops: u64,
pub broadcast_pdu_type_drops: u64,
pub malformed: u64,
pub routed_drops: u64,
}
totals!(
NodeTotals: node::Counters => Counters {
address_length_drops,
global_broadcast_dadr_drops,
broadcast_pdu_type_drops,
malformed,
routed_drops,
}
);
pub(crate) enum Inbound {
Apdu {
apdu: Bytes,
source_network: Option<NpduAddress>,
is_group: bool,
global_broadcast: bool,
},
Control(Option<Npdu>),
Dropped,
}
pub(crate) fn admit(
node: &mut Node,
totals: &NodeTotals,
drop_warning: &WarnThrottle,
source_mac: &[u8],
link_group: bool,
frame: Bytes,
controls: bool,
) -> Inbound {
let (offset, inbound) = match node.receive(source_mac, link_group, &frame) {
node::Received::Apdu(apdu) => (
apdu.npdu.payload_offset,
Inbound::Apdu {
apdu: Bytes::new(),
source_network: apdu.npdu.source.map(NpduAddress::from),
is_group: apdu.group,
global_broadcast: apdu.global,
},
),
node::Received::Message(npdu) if controls => (
npdu.payload_offset,
Inbound::Control(Some(Npdu::header_from(&npdu))),
),
node::Received::Message(npdu) => {
debug!(
message_type = ?npdu.message_type,
"Ignoring network layer message (non-router mode)"
);
return Inbound::Control(None);
}
node::Received::Discarded(discard) => {
totals.add(&node.take_counters());
if discard == Discard::NotRouted {
debug!("Discarding routed message (non-router)");
} else if let Some(dropped) = drop_warning.note() {
warn!(
?discard,
dropped, "Dropping an inbound NPDU (drops since the last warning)"
);
} else {
debug!(?discard, "Dropping an inbound NPDU");
}
return Inbound::Dropped;
}
};
let mut payload = frame;
payload.advance(offset);
match inbound {
Inbound::Apdu {
source_network,
is_group,
global_broadcast,
..
} => Inbound::Apdu {
apdu: payload,
source_network,
is_group,
global_broadcast,
},
Inbound::Control(Some(mut npdu)) => {
npdu.payload = payload;
Inbound::Control(Some(npdu))
}
other => other,
}
}