use core::net::{Ipv4Addr, SocketAddrV4};
use bacnet_bip_codec::BvlcFunction;
use super::config::Link;
use crate::Arrival;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Addressed {
pub node: bool,
pub broadcast: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct LocalAddresses<'a> {
pub own: Ipv4Addr,
pub broadcast: Ipv4Addr,
pub wildcard: Option<&'a [Ipv4Addr]>,
}
impl LocalAddresses<'_> {
#[must_use]
pub const fn new(own: Ipv4Addr, broadcast: Ipv4Addr) -> Self {
Self {
own,
broadcast,
wildcard: None,
}
}
}
impl Addressed {
#[must_use]
pub fn classify(
destination: Ipv4Addr,
os_group: Option<bool>,
local: &LocalAddresses<'_>,
) -> Self {
let to_broadcast = destination == local.broadcast || destination.is_broadcast();
let node = match local.wildcard {
Some(own_ips) => {
!to_broadcast && !destination.is_multicast() && own_ips.contains(&destination)
}
None => destination == local.own,
} && os_group != Some(true);
Self {
node,
broadcast: to_broadcast && os_group != Some(false),
}
}
pub(crate) fn arrival(self, function: BvlcFunction) -> Option<Arrival> {
let unicast = self.node.then_some(Arrival::Unicast);
let broadcast = self.broadcast.then_some(Arrival::Broadcast);
match function {
BvlcFunction::ORIGINAL_UNICAST_NPDU => unicast,
BvlcFunction::ORIGINAL_BROADCAST_NPDU => broadcast,
BvlcFunction::FORWARDED_NPDU => broadcast.or(unicast),
_ => unicast,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct Groups {
pub broadcast: SocketAddrV4,
pub interface: Ipv4Addr,
}
impl Groups {
#[must_use]
pub const fn new(broadcast: SocketAddrV4, interface: Ipv4Addr) -> Self {
Self {
broadcast,
interface,
}
}
#[must_use]
pub const fn contains(&self, address: SocketAddrV4) -> bool {
let broadcast = &self.broadcast;
(address.ip().to_bits() == broadcast.ip().to_bits() && address.port() == broadcast.port())
|| self.contains_ip(*address.ip())
}
#[must_use]
pub const fn contains_ip(&self, ip: Ipv4Addr) -> bool {
let (ip_bits, broadcast) = (ip.to_bits(), self.broadcast.ip().to_bits());
ip.is_broadcast()
|| ip.is_multicast()
|| (ip_bits == broadcast && !ip.is_loopback() && ip_bits != self.interface.to_bits())
}
}
impl Link {
#[must_use]
pub const fn groups(&self) -> Groups {
Groups {
broadcast: self.subnet.broadcast,
interface: *self.address.ip(),
}
}
#[must_use]
pub fn is_own(&self, address: SocketAddrV4) -> bool {
address == self.address
}
#[must_use]
pub fn is_answerable(&self, source: SocketAddrV4) -> bool {
!source.ip().is_unspecified()
&& source.port() != 0
&& !self.groups().contains_ip(*source.ip())
&& !self.is_own(source)
}
}