netstack3_ip/multicast_forwarding/
route.rsuse alloc::fmt::Debug;
use alloc::sync::Arc;
use core::hash::Hash;
use core::sync::atomic::Ordering;
use derivative::Derivative;
use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr, Ipv6Scope};
use net_types::{
MulticastAddr, MulticastAddress as _, NonMappedAddr, ScopeableAddress as _, SpecifiedAddr,
SpecifiedAddress as _, UnicastAddr,
};
use netstack3_base::{
AtomicInstant, Inspectable, InspectableValue, Inspector, InspectorDeviceExt,
InstantBindingsTypes, IpExt, StrongDeviceIdentifier,
};
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Ipv4SourceAddr {
addr: Ipv4Addr,
}
impl Ipv4SourceAddr {
fn new(addr: Ipv4Addr) -> Option<Self> {
if addr.is_specified()
&& !addr.is_multicast()
&& !Ipv4::LINK_LOCAL_UNICAST_SUBNET.contains(&addr)
{
Some(Ipv4SourceAddr { addr })
} else {
None
}
}
}
impl From<Ipv4SourceAddr> for Ipv4Addr {
fn from(addr: Ipv4SourceAddr) -> Self {
addr.addr
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Ipv4DestinationAddr {
addr: MulticastAddr<Ipv4Addr>,
}
impl Ipv4DestinationAddr {
fn new(addr: Ipv4Addr) -> Option<Self> {
if Ipv4::LINK_LOCAL_MULTICAST_SUBNET.contains(&addr) {
None
} else {
Some(Ipv4DestinationAddr { addr: MulticastAddr::new(addr)? })
}
}
}
impl From<Ipv4DestinationAddr> for SpecifiedAddr<Ipv4Addr> {
fn from(addr: Ipv4DestinationAddr) -> Self {
addr.addr.into_specified()
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Ipv6SourceAddr {
addr: NonMappedAddr<UnicastAddr<Ipv6Addr>>,
}
impl Ipv6SourceAddr {
fn new(addr: Ipv6Addr) -> Option<Self> {
let addr = NonMappedAddr::new(UnicastAddr::new(addr)?)?;
match addr.scope() {
Ipv6Scope::InterfaceLocal | Ipv6Scope::LinkLocal => None,
Ipv6Scope::Reserved(_) | Ipv6Scope::Unassigned(_) => None,
Ipv6Scope::AdminLocal
| Ipv6Scope::SiteLocal
| Ipv6Scope::OrganizationLocal
| Ipv6Scope::Global => Some(Ipv6SourceAddr { addr }),
}
}
}
impl From<Ipv6SourceAddr> for net_types::ip::Ipv6SourceAddr {
fn from(addr: Ipv6SourceAddr) -> Self {
net_types::ip::Ipv6SourceAddr::Unicast(addr.addr)
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Ipv6DestinationAddr {
addr: MulticastAddr<Ipv6Addr>,
}
impl Ipv6DestinationAddr {
fn new(addr: Ipv6Addr) -> Option<Self> {
if addr.scope().multicast_scope_id() <= Ipv6Scope::MULTICAST_SCOPE_ID_LINK_LOCAL {
None
} else {
Some(Ipv6DestinationAddr { addr: MulticastAddr::new(addr)? })
}
}
}
impl From<Ipv6DestinationAddr> for SpecifiedAddr<Ipv6Addr> {
fn from(addr: Ipv6DestinationAddr) -> Self {
addr.addr.into_specified()
}
}
pub trait MulticastRouteIpExt: IpExt {
type SourceAddress: Clone
+ Debug
+ Eq
+ Hash
+ Ord
+ PartialEq
+ PartialOrd
+ Into<Self::RecvSrcAddr>;
type DestinationAddress: Clone
+ Debug
+ Eq
+ Hash
+ Ord
+ PartialEq
+ PartialOrd
+ Into<SpecifiedAddr<Self::Addr>>;
}
impl MulticastRouteIpExt for Ipv4 {
type SourceAddress = Ipv4SourceAddr;
type DestinationAddress = Ipv4DestinationAddr;
}
impl MulticastRouteIpExt for Ipv6 {
type SourceAddress = Ipv6SourceAddr;
type DestinationAddress = Ipv6DestinationAddr;
}
#[derive(Clone, Debug, Eq, GenericOverIp, Hash, Ord, PartialEq, PartialOrd)]
#[generic_over_ip(I, Ip)]
pub struct MulticastRouteKey<I: MulticastRouteIpExt> {
pub(crate) src_addr: I::SourceAddress,
pub(crate) dst_addr: I::DestinationAddress,
}
impl<I: MulticastRouteIpExt> MulticastRouteKey<I> {
pub fn new(src_addr: I::Addr, dst_addr: I::Addr) -> Option<MulticastRouteKey<I>> {
I::map_ip(
(src_addr, dst_addr),
|(src_addr, dst_addr)| {
Some(MulticastRouteKey {
src_addr: Ipv4SourceAddr::new(src_addr)?,
dst_addr: Ipv4DestinationAddr::new(dst_addr)?,
})
},
|(src_addr, dst_addr)| {
Some(MulticastRouteKey {
src_addr: Ipv6SourceAddr::new(src_addr)?,
dst_addr: Ipv6DestinationAddr::new(dst_addr)?,
})
},
)
}
pub fn src_addr(&self) -> I::Addr {
I::map_ip(self, |key| key.src_addr.addr, |key| **key.src_addr.addr)
}
pub fn dst_addr(&self) -> I::Addr {
I::map_ip(self, |key| *key.dst_addr.addr, |key| *key.dst_addr.addr)
}
}
impl<I: MulticastRouteIpExt> Inspectable for MulticastRouteKey<I> {
fn record<II: Inspector>(&self, inspector: &mut II) {
inspector.record_ip_addr("SourceAddress", self.src_addr());
inspector.record_ip_addr("DestinationAddress", self.dst_addr());
}
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
pub struct MulticastRouteEntry<D: StrongDeviceIdentifier, BT: InstantBindingsTypes> {
pub(crate) route: MulticastRoute<D>,
pub(crate) stats: MulticastRouteStats<BT::AtomicInstant>,
}
impl<D: StrongDeviceIdentifier, BT: InstantBindingsTypes> MulticastRouteEntry<D, BT> {
pub(crate) fn inspect<I: Inspector, II: InspectorDeviceExt<D>>(&self, inspector: &mut I) {
let MulticastRouteEntry {
route: MulticastRoute { input_interface, action },
stats: MulticastRouteStats { last_used },
} = self;
II::record_device(inspector, "InputInterface", input_interface);
let Action::Forward(targets) = action;
inspector.record_child("ForwardingTargets", |inspector| {
for MulticastRouteTarget { output_interface, min_ttl } in targets.iter() {
inspector.record_unnamed_child(|inspector| {
II::record_device(inspector, "OutputInterface", output_interface);
inspector.record_uint("MinTTL", *min_ttl);
});
}
});
inspector.record_child("Statistics", |inspector| {
last_used.load(Ordering::Relaxed).record("LastUsed", inspector);
});
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MulticastRoute<D: StrongDeviceIdentifier> {
pub(crate) input_interface: D,
pub(crate) action: Action<D>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum Action<D: StrongDeviceIdentifier> {
Forward(MulticastRouteTargets<D>),
}
pub type MulticastRouteTargets<D> = Arc<[MulticastRouteTarget<D>]>;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct MulticastRouteTarget<D: StrongDeviceIdentifier> {
pub output_interface: D,
pub min_ttl: u8,
}
#[derive(Debug, Eq, PartialEq)]
pub enum ForwardMulticastRouteError {
EmptyTargetList,
InputInterfaceIsTarget,
DuplicateTarget,
}
impl<D: StrongDeviceIdentifier> MulticastRoute<D> {
pub fn new_forward(
input_interface: D,
targets: MulticastRouteTargets<D>,
) -> Result<Self, ForwardMulticastRouteError> {
if targets.is_empty() {
return Err(ForwardMulticastRouteError::EmptyTargetList);
}
if targets.iter().any(|MulticastRouteTarget { output_interface, min_ttl: _ }| {
output_interface == &input_interface
}) {
return Err(ForwardMulticastRouteError::InputInterfaceIsTarget);
}
for (index, target_a) in targets.iter().enumerate() {
if targets[index + 1..]
.iter()
.any(|target_b| target_a.output_interface == target_b.output_interface)
{
return Err(ForwardMulticastRouteError::DuplicateTarget);
}
}
Ok(MulticastRoute { input_interface, action: Action::Forward(targets) })
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct MulticastRouteStats<Instant> {
pub last_used: Instant,
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
use alloc::vec::Vec;
use net_declare::{net_ip_v4, net_ip_v6};
use netstack3_base::testutil::MultipleDevicesId;
use test_case::test_case;
const UNICAST_V4: Ipv4Addr = net_ip_v4!("192.0.2.1");
const MULTICAST_V4: Ipv4Addr = net_ip_v4!("224.0.1.1");
const LL_UNICAST_V4: Ipv4Addr = net_ip_v4!("169.254.0.1");
const LL_MULTICAST_V4: Ipv4Addr = net_ip_v4!("224.0.0.1");
const UNICAST_V6: Ipv6Addr = net_ip_v6!("2001:0DB8::1");
const MULTICAST_V6: Ipv6Addr = net_ip_v6!("ff0e::1");
const LL_UNICAST_V6: Ipv6Addr = net_ip_v6!("fe80::1");
const LL_MULTICAST_V6: Ipv6Addr = net_ip_v6!("ff02::1");
const V4_MAPPED_V6: Ipv6Addr = net_ip_v6!("::FFFF:192.0.2.1");
#[test_case(UNICAST_V4, MULTICAST_V4 => true; "success")]
#[test_case(UNICAST_V4, UNICAST_V4 => false; "unicast_dst")]
#[test_case(UNICAST_V4, Ipv4::UNSPECIFIED_ADDRESS => false; "unspecified_dst")]
#[test_case(MULTICAST_V4, MULTICAST_V4 => false; "multicast_src")]
#[test_case(Ipv4::UNSPECIFIED_ADDRESS, MULTICAST_V4 => false; "unspecified_src")]
#[test_case(LL_UNICAST_V4, MULTICAST_V4 => false; "ll_unicast_src")]
#[test_case(UNICAST_V4, LL_MULTICAST_V4 => false; "ll_multicast_dst")]
fn new_ipv4_route_key(src_addr: Ipv4Addr, dst_addr: Ipv4Addr) -> bool {
MulticastRouteKey::<Ipv4>::new(src_addr, dst_addr).is_some()
}
#[test_case(UNICAST_V6, MULTICAST_V6 => true; "success")]
#[test_case(UNICAST_V6, UNICAST_V6 => false; "unicast_dst")]
#[test_case(UNICAST_V6, Ipv6::UNSPECIFIED_ADDRESS => false; "unspecified_dst")]
#[test_case(MULTICAST_V6, MULTICAST_V6 => false; "multicast_src")]
#[test_case(Ipv6::UNSPECIFIED_ADDRESS, MULTICAST_V6 => false; "unspecified_src")]
#[test_case(LL_UNICAST_V6, MULTICAST_V6 => false; "ll_unicast_src")]
#[test_case(UNICAST_V6, LL_MULTICAST_V6 => false; "ll_multicast_dst")]
#[test_case(V4_MAPPED_V6, LL_MULTICAST_V6 => false; "mapped_src")]
fn new_ipv6_route_key(src_addr: Ipv6Addr, dst_addr: Ipv6Addr) -> bool {
MulticastRouteKey::<Ipv6>::new(src_addr, dst_addr).is_some()
}
#[test_case(MultipleDevicesId::A, vec![] =>
Some(ForwardMulticastRouteError::EmptyTargetList); "empty_target_list")]
#[test_case(MultipleDevicesId::A, vec![MultipleDevicesId::A] =>
Some(ForwardMulticastRouteError::InputInterfaceIsTarget); "input_interface_is_target")]
#[test_case(MultipleDevicesId::A, vec![MultipleDevicesId::B, MultipleDevicesId::B] =>
Some(ForwardMulticastRouteError::DuplicateTarget); "duplicate_target")]
#[test_case(MultipleDevicesId::A, vec![MultipleDevicesId::B, MultipleDevicesId::C] =>
None; "valid_route")]
fn new_forward(
input_interface: MultipleDevicesId,
output_interfaces: Vec<MultipleDevicesId>,
) -> Option<ForwardMulticastRouteError> {
let targets = output_interfaces
.into_iter()
.map(|output_interface| MulticastRouteTarget { output_interface, min_ttl: 0 })
.collect();
MulticastRoute::new_forward(input_interface, targets).err()
}
}