use bacnet_npdu::{NetworkMessageType, RejectMessageReason};
use bacnet_transport::port::{ReceivedNpdu, TransportPort};
use bacnet_types::MacAddr;
use bacnet_types::error::Error;
use bytes::Bytes;
use tokio::sync::mpsc;
use crate::layer::NetworkLayer;
use crate::loopback_fixture::{
FromRouter, LONGEST, REMOTE, TOO_LONG, next_from_router, pump, recv, reject_of, wire,
};
use crate::router::{BACnetRouter, RouterOptions, RouterPort, StartedRouter};
struct Injector {
received: Option<mpsc::Receiver<ReceivedNpdu>>,
sent: mpsc::Sender<ReceivedNpdu>,
unicast_macs: mpsc::UnboundedSender<MacAddr>,
mac: [u8; 1],
}
struct Link {
deliver: mpsc::Sender<ReceivedNpdu>,
sent: FromRouter,
}
impl Link {
async fn from(&self, source: &[u8], npdu: Vec<u8>) {
let frame = ReceivedNpdu::unverified(
Bytes::from(npdu),
MacAddr::from_slice(source),
false,
Vec::new(),
None,
);
self.deliver.send(frame).await.unwrap();
}
}
fn injector(mac: u8) -> (Injector, Link) {
let (deliver, received) = mpsc::channel(16);
let (sent_tx, sent) = mpsc::channel(64);
let (unicast_macs, macs) = mpsc::unbounded_channel();
let port = Injector {
received: Some(received),
sent: sent_tx,
unicast_macs,
mac: [mac],
};
let sent = FromRouter::new(sent, macs);
(port, Link { deliver, sent })
}
impl Injector {
fn record(&self, npdu: &[u8], unicast: Option<&[u8]>) {
let frame = ReceivedNpdu::unverified(
Bytes::copy_from_slice(npdu),
MacAddr::from_slice(&self.mac),
unicast.is_none(),
Vec::new(),
None,
);
if self.sent.try_send(frame).is_ok()
&& let Some(mac) = unicast
{
let _ = self.unicast_macs.send(MacAddr::from_slice(mac));
}
}
}
impl TransportPort for Injector {
async fn start(&mut self) -> Result<mpsc::Receiver<ReceivedNpdu>, Error> {
self.received
.take()
.ok_or_else(|| Error::Encoding("already started".into()))
}
async fn stop(&mut self) -> Result<(), Error> {
Ok(())
}
async fn send_unicast(&self, npdu: &[u8], mac: &[u8]) -> Result<(), Error> {
self.record(npdu, Some(mac));
Ok(())
}
async fn send_broadcast(&self, npdu: &[u8]) -> Result<(), Error> {
self.record(npdu, None);
Ok(())
}
fn local_receive_apdu_capacity(&self) -> u16 {
1476
}
fn local_mac(&self) -> &[u8] {
&self.mac
}
}
fn mac(length: u8) -> MacAddr {
(0..length).map(|i| 0xA0u8.wrapping_add(i)).collect()
}
const I_AM: Option<NetworkMessageType> = Some(NetworkMessageType::I_AM_ROUTER_TO_NETWORK);
#[tokio::test]
async fn non_router_drops_and_counts_a_frame_from_an_over_long_source_mac() {
let (port, link) = injector(0x01);
let mut network = NetworkLayer::new(port);
let mut controls = network.enable_network_control_receiver().unwrap();
let mut apdus = network.start().await.unwrap();
for length in [TOO_LONG, 255, LONGEST] {
link.from(&mac(length), wire(None, None, None)).await;
link.from(&mac(length), wire(None, None, I_AM)).await;
}
let apdu = recv(&mut apdus).await;
assert_eq!(apdu.source_mac, mac(LONGEST));
pump(&mut apdus);
let control = recv(&mut controls).await;
assert_eq!(control.source_mac, mac(LONGEST));
assert_eq!(network.address_length_drops(), 4);
assert!(apdus.try_recv().is_err() && controls.try_recv().is_err());
network.stop().await.unwrap();
}
#[tokio::test]
async fn router_drops_and_counts_a_frame_from_an_over_long_source_mac() {
let (port_a, mut link_a) = injector(0x01);
let (port_b, mut link_b) = injector(0x02);
let ports = vec![
RouterPort {
transport: port_a,
network_number: 1000,
},
RouterPort {
transport: port_b,
network_number: 2000,
},
];
let StartedRouter {
mut router,
apdus: mut local,
..
} = BACnetRouter::start(ports, RouterOptions::new())
.await
.unwrap();
let long = mac(TOO_LONG);
link_a.from(&long, wire(None, None, None)).await;
link_a.from(&long, wire(None, None, I_AM)).await;
link_a.from(&long, wire(Some((2000, 1)), None, None)).await;
link_a.from(&mac(LONGEST), wire(None, None, None)).await;
let delivered = recv(&mut local).await;
assert_eq!(delivered.source_mac, mac(LONGEST));
assert_eq!(delivered.ingress_network, Some(1000));
assert_eq!(router.address_length_drops(), 3);
link_b
.from(&[0x0B], wire(Some((REMOTE, 1)), None, None))
.await;
let (reject, _) = next_from_router(&mut link_b.sent).await;
assert_eq!(
reject_of(&reject).reason,
RejectMessageReason::NOT_DIRECTLY_CONNECTED
);
let (solicit, to) = next_from_router(&mut link_a.sent).await;
assert!(to.is_none(), "the solicitation is a broadcast");
assert_eq!(
solicit.message_type,
Some(NetworkMessageType::WHO_IS_ROUTER_TO_NETWORK)
);
assert!(local.try_recv().is_err());
assert_eq!(router.address_length_drops(), 3);
router.stop().await.unwrap();
}