1use alloc::fmt::Debug;
8use alloc::sync::Arc;
9use core::hash::Hash;
10use core::sync::atomic::Ordering;
11use derivative::Derivative;
12use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr, Ipv6Scope};
13use net_types::{
14 MulticastAddr, MulticastAddress as _, NonMappedAddr, ScopeableAddress as _, SpecifiedAddr,
15 SpecifiedAddress as _, UnicastAddr,
16};
17use netstack3_base::{
18 AtomicInstant, Inspectable, InspectableValue, Inspector, InspectorDeviceExt,
19 InstantBindingsTypes, IpExt, StrongDeviceIdentifier,
20};
21
22#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
32pub struct Ipv4SourceAddr {
33 addr: Ipv4Addr,
34}
35
36impl Ipv4SourceAddr {
37 fn new(addr: Ipv4Addr) -> Option<Self> {
41 if addr.is_specified()
42 && !addr.is_multicast()
43 && !Ipv4::LINK_LOCAL_UNICAST_SUBNET.contains(&addr)
44 {
45 Some(Ipv4SourceAddr { addr })
46 } else {
47 None
48 }
49 }
50}
51
52impl From<Ipv4SourceAddr> for Ipv4Addr {
53 fn from(addr: Ipv4SourceAddr) -> Self {
54 addr.addr
55 }
56}
57
58#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
62pub struct Ipv4DestinationAddr {
63 addr: MulticastAddr<Ipv4Addr>,
64}
65
66impl Ipv4DestinationAddr {
67 fn new(addr: Ipv4Addr) -> Option<Self> {
71 if Ipv4::LINK_LOCAL_MULTICAST_SUBNET.contains(&addr) {
75 None
76 } else {
77 Some(Ipv4DestinationAddr { addr: MulticastAddr::new(addr)? })
78 }
79 }
80}
81
82impl From<Ipv4DestinationAddr> for SpecifiedAddr<Ipv4Addr> {
83 fn from(addr: Ipv4DestinationAddr) -> Self {
84 addr.addr.into_specified()
85 }
86}
87
88#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
93pub struct Ipv6SourceAddr {
94 addr: NonMappedAddr<UnicastAddr<Ipv6Addr>>,
95}
96
97impl Ipv6SourceAddr {
98 fn new(addr: Ipv6Addr) -> Option<Self> {
102 let addr = NonMappedAddr::new(UnicastAddr::new(addr)?)?;
103 match addr.scope() {
104 Ipv6Scope::InterfaceLocal | Ipv6Scope::LinkLocal => None,
105 Ipv6Scope::Reserved(_) | Ipv6Scope::Unassigned(_) => None,
106 Ipv6Scope::AdminLocal
107 | Ipv6Scope::SiteLocal
108 | Ipv6Scope::OrganizationLocal
109 | Ipv6Scope::Global => Some(Ipv6SourceAddr { addr }),
110 }
111 }
112}
113
114impl From<Ipv6SourceAddr> for net_types::ip::Ipv6SourceAddr {
115 fn from(addr: Ipv6SourceAddr) -> Self {
116 net_types::ip::Ipv6SourceAddr::Unicast(addr.addr)
117 }
118}
119
120#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
124pub struct Ipv6DestinationAddr {
125 addr: MulticastAddr<Ipv6Addr>,
126}
127
128impl Ipv6DestinationAddr {
129 fn new(addr: Ipv6Addr) -> Option<Self> {
133 if addr.scope().multicast_scope_id() <= Ipv6Scope::MULTICAST_SCOPE_ID_LINK_LOCAL {
137 None
138 } else {
139 Some(Ipv6DestinationAddr { addr: MulticastAddr::new(addr)? })
140 }
141 }
142}
143
144impl From<Ipv6DestinationAddr> for SpecifiedAddr<Ipv6Addr> {
145 fn from(addr: Ipv6DestinationAddr) -> Self {
146 addr.addr.into_specified()
147 }
148}
149
150pub trait MulticastRouteIpExt: IpExt {
152 type SourceAddress: Clone
154 + Debug
155 + Eq
156 + Hash
157 + Ord
158 + PartialEq
159 + PartialOrd
160 + Into<Self::RecvSrcAddr>;
161 type DestinationAddress: Clone
163 + Debug
164 + Eq
165 + Hash
166 + Ord
167 + PartialEq
168 + PartialOrd
169 + Into<SpecifiedAddr<Self::Addr>>;
170}
171
172impl MulticastRouteIpExt for Ipv4 {
173 type SourceAddress = Ipv4SourceAddr;
174 type DestinationAddress = Ipv4DestinationAddr;
175}
176
177impl MulticastRouteIpExt for Ipv6 {
178 type SourceAddress = Ipv6SourceAddr;
179 type DestinationAddress = Ipv6DestinationAddr;
180}
181
182#[derive(Clone, Debug, Eq, GenericOverIp, Hash, Ord, PartialEq, PartialOrd)]
184#[generic_over_ip(I, Ip)]
185pub struct MulticastRouteKey<I: MulticastRouteIpExt> {
186 pub(crate) src_addr: I::SourceAddress,
188 pub(crate) dst_addr: I::DestinationAddress,
190}
191
192impl<I: MulticastRouteIpExt> MulticastRouteKey<I> {
193 pub fn new(src_addr: I::Addr, dst_addr: I::Addr) -> Option<MulticastRouteKey<I>> {
197 I::map_ip(
198 (src_addr, dst_addr),
199 |(src_addr, dst_addr)| {
200 Some(MulticastRouteKey {
201 src_addr: Ipv4SourceAddr::new(src_addr)?,
202 dst_addr: Ipv4DestinationAddr::new(dst_addr)?,
203 })
204 },
205 |(src_addr, dst_addr)| {
206 Some(MulticastRouteKey {
207 src_addr: Ipv6SourceAddr::new(src_addr)?,
208 dst_addr: Ipv6DestinationAddr::new(dst_addr)?,
209 })
210 },
211 )
212 }
213
214 pub fn src_addr(&self) -> I::Addr {
216 I::map_ip(self, |key| key.src_addr.addr, |key| **key.src_addr.addr)
217 }
218
219 pub fn dst_addr(&self) -> I::Addr {
221 I::map_ip(self, |key| *key.dst_addr.addr, |key| *key.dst_addr.addr)
222 }
223}
224
225impl<I: MulticastRouteIpExt> Inspectable for MulticastRouteKey<I> {
226 fn record<II: Inspector>(&self, inspector: &mut II) {
227 inspector.record_ip_addr("SourceAddress", self.src_addr());
228 inspector.record_ip_addr("DestinationAddress", self.dst_addr());
229 }
230}
231
232#[derive(Derivative)]
234#[derivative(Debug(bound = ""))]
235pub struct MulticastRouteEntry<D: StrongDeviceIdentifier, BT: InstantBindingsTypes> {
236 pub(crate) route: MulticastRoute<D>,
237 pub(crate) stats: MulticastRouteStats<BT::AtomicInstant>,
240}
241
242impl<D: StrongDeviceIdentifier, BT: InstantBindingsTypes> MulticastRouteEntry<D, BT> {
243 pub(crate) fn inspect<I: Inspector, II: InspectorDeviceExt<D>>(&self, inspector: &mut I) {
247 let MulticastRouteEntry {
248 route: MulticastRoute { input_interface, action },
249 stats: MulticastRouteStats { last_used },
250 } = self;
251 II::record_device(inspector, "InputInterface", input_interface);
252 let Action::Forward(targets) = action;
253 inspector.record_child("ForwardingTargets", |inspector| {
254 for MulticastRouteTarget { output_interface, min_ttl } in targets.iter() {
255 inspector.record_unnamed_child(|inspector| {
256 II::record_device(inspector, "OutputInterface", output_interface);
257 inspector.record_uint("MinTTL", *min_ttl);
258 });
259 }
260 });
261 inspector.record_child("Statistics", |inspector| {
262 last_used.load(Ordering::Relaxed).record("LastUsed", inspector);
263 });
264 }
265}
266
267#[derive(Clone, Debug, Eq, PartialEq)]
271pub struct MulticastRoute<D: StrongDeviceIdentifier> {
272 pub(crate) input_interface: D,
274 pub(crate) action: Action<D>,
276}
277
278#[derive(Clone, Debug, Eq, PartialEq)]
280pub(crate) enum Action<D: StrongDeviceIdentifier> {
281 Forward(MulticastRouteTargets<D>),
283}
284
285pub type MulticastRouteTargets<D> = Arc<[MulticastRouteTarget<D>]>;
297
298#[derive(Clone, Debug, Eq, Hash, PartialEq)]
300pub struct MulticastRouteTarget<D: StrongDeviceIdentifier> {
301 pub output_interface: D,
303 pub min_ttl: u8,
307}
308
309#[derive(Debug, Eq, PartialEq)]
311pub enum ForwardMulticastRouteError {
312 EmptyTargetList,
314 InputInterfaceIsTarget,
317 DuplicateTarget,
319}
320
321impl<D: StrongDeviceIdentifier> MulticastRoute<D> {
322 pub fn new_forward(
324 input_interface: D,
325 targets: MulticastRouteTargets<D>,
326 ) -> Result<Self, ForwardMulticastRouteError> {
327 if targets.is_empty() {
328 return Err(ForwardMulticastRouteError::EmptyTargetList);
329 }
330 if targets.iter().any(|MulticastRouteTarget { output_interface, min_ttl: _ }| {
331 output_interface == &input_interface
332 }) {
333 return Err(ForwardMulticastRouteError::InputInterfaceIsTarget);
334 }
335
336 for (index, target_a) in targets.iter().enumerate() {
341 if targets[index + 1..]
345 .iter()
346 .any(|target_b| target_a.output_interface == target_b.output_interface)
347 {
348 return Err(ForwardMulticastRouteError::DuplicateTarget);
349 }
350 }
351
352 Ok(MulticastRoute { input_interface, action: Action::Forward(targets) })
353 }
354}
355
356#[derive(Debug, Eq, PartialEq)]
358pub struct MulticastRouteStats<Instant> {
359 pub last_used: Instant,
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373
374 use alloc::vec;
375 use alloc::vec::Vec;
376 use net_declare::{net_ip_v4, net_ip_v6};
377 use netstack3_base::testutil::MultipleDevicesId;
378 use test_case::test_case;
379
380 const UNICAST_V4: Ipv4Addr = net_ip_v4!("192.0.2.1");
381 const MULTICAST_V4: Ipv4Addr = net_ip_v4!("224.0.1.1");
382 const LL_UNICAST_V4: Ipv4Addr = net_ip_v4!("169.254.0.1");
383 const LL_MULTICAST_V4: Ipv4Addr = net_ip_v4!("224.0.0.1");
384 const UNICAST_V6: Ipv6Addr = net_ip_v6!("2001:0DB8::1");
385 const MULTICAST_V6: Ipv6Addr = net_ip_v6!("ff0e::1");
386 const LL_UNICAST_V6: Ipv6Addr = net_ip_v6!("fe80::1");
387 const LL_MULTICAST_V6: Ipv6Addr = net_ip_v6!("ff02::1");
388 const V4_MAPPED_V6: Ipv6Addr = net_ip_v6!("::FFFF:192.0.2.1");
389
390 #[test_case(UNICAST_V4, MULTICAST_V4 => true; "success")]
391 #[test_case(UNICAST_V4, UNICAST_V4 => false; "unicast_dst")]
392 #[test_case(UNICAST_V4, Ipv4::UNSPECIFIED_ADDRESS => false; "unspecified_dst")]
393 #[test_case(MULTICAST_V4, MULTICAST_V4 => false; "multicast_src")]
394 #[test_case(Ipv4::UNSPECIFIED_ADDRESS, MULTICAST_V4 => false; "unspecified_src")]
395 #[test_case(LL_UNICAST_V4, MULTICAST_V4 => false; "ll_unicast_src")]
396 #[test_case(UNICAST_V4, LL_MULTICAST_V4 => false; "ll_multicast_dst")]
397 fn new_ipv4_route_key(src_addr: Ipv4Addr, dst_addr: Ipv4Addr) -> bool {
398 MulticastRouteKey::<Ipv4>::new(src_addr, dst_addr).is_some()
399 }
400
401 #[test_case(UNICAST_V6, MULTICAST_V6 => true; "success")]
402 #[test_case(UNICAST_V6, UNICAST_V6 => false; "unicast_dst")]
403 #[test_case(UNICAST_V6, Ipv6::UNSPECIFIED_ADDRESS => false; "unspecified_dst")]
404 #[test_case(MULTICAST_V6, MULTICAST_V6 => false; "multicast_src")]
405 #[test_case(Ipv6::UNSPECIFIED_ADDRESS, MULTICAST_V6 => false; "unspecified_src")]
406 #[test_case(LL_UNICAST_V6, MULTICAST_V6 => false; "ll_unicast_src")]
407 #[test_case(UNICAST_V6, LL_MULTICAST_V6 => false; "ll_multicast_dst")]
408 #[test_case(V4_MAPPED_V6, LL_MULTICAST_V6 => false; "mapped_src")]
409 fn new_ipv6_route_key(src_addr: Ipv6Addr, dst_addr: Ipv6Addr) -> bool {
410 MulticastRouteKey::<Ipv6>::new(src_addr, dst_addr).is_some()
411 }
412
413 #[test_case(MultipleDevicesId::A, vec![] =>
414 Some(ForwardMulticastRouteError::EmptyTargetList); "empty_target_list")]
415 #[test_case(MultipleDevicesId::A, vec![MultipleDevicesId::A] =>
416 Some(ForwardMulticastRouteError::InputInterfaceIsTarget); "input_interface_is_target")]
417 #[test_case(MultipleDevicesId::A, vec![MultipleDevicesId::B, MultipleDevicesId::B] =>
418 Some(ForwardMulticastRouteError::DuplicateTarget); "duplicate_target")]
419 #[test_case(MultipleDevicesId::A, vec![MultipleDevicesId::B, MultipleDevicesId::C] =>
420 None; "valid_route")]
421 fn new_forward(
422 input_interface: MultipleDevicesId,
423 output_interfaces: Vec<MultipleDevicesId>,
424 ) -> Option<ForwardMulticastRouteError> {
425 let targets = output_interfaces
426 .into_iter()
427 .map(|output_interface| MulticastRouteTarget { output_interface, min_ttl: 0 })
428 .collect();
429 MulticastRoute::new_forward(input_interface, targets).err()
430 }
431}