netstack3_ip/multicast_forwarding/
route.rs1use 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, NonMappedAddr, NonMulticastAddr, ScopeableAddress as _, SpecifiedAddr,
15 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: NonMulticastAddr<SpecifiedAddr<Ipv4Addr>>,
34}
35
36impl Ipv4SourceAddr {
37 fn new(addr: Ipv4Addr) -> Option<Self> {
41 if Ipv4::LINK_LOCAL_UNICAST_SUBNET.contains(&addr) {
42 return None;
43 }
44
45 Some(Ipv4SourceAddr { addr: NonMulticastAddr::new(SpecifiedAddr::new(addr)?)? })
46 }
47}
48
49impl From<Ipv4SourceAddr> for net_types::ip::Ipv4SourceAddr {
50 fn from(addr: Ipv4SourceAddr) -> Self {
51 net_types::ip::Ipv4SourceAddr::Specified(addr.addr)
52 }
53}
54
55#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
59pub struct Ipv4DestinationAddr {
60 addr: MulticastAddr<Ipv4Addr>,
61}
62
63impl Ipv4DestinationAddr {
64 fn new(addr: Ipv4Addr) -> Option<Self> {
68 if Ipv4::LINK_LOCAL_MULTICAST_SUBNET.contains(&addr) {
72 None
73 } else {
74 Some(Ipv4DestinationAddr { addr: MulticastAddr::new(addr)? })
75 }
76 }
77}
78
79impl From<Ipv4DestinationAddr> for SpecifiedAddr<Ipv4Addr> {
80 fn from(addr: Ipv4DestinationAddr) -> Self {
81 addr.addr.into_specified()
82 }
83}
84
85#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
90pub struct Ipv6SourceAddr {
91 addr: NonMappedAddr<UnicastAddr<Ipv6Addr>>,
92}
93
94impl Ipv6SourceAddr {
95 fn new(addr: Ipv6Addr) -> Option<Self> {
99 let addr = NonMappedAddr::new(UnicastAddr::new(addr)?)?;
100 match addr.scope() {
101 Ipv6Scope::InterfaceLocal | Ipv6Scope::LinkLocal => None,
102 Ipv6Scope::Reserved(_) | Ipv6Scope::Unassigned(_) => None,
103 Ipv6Scope::AdminLocal
104 | Ipv6Scope::SiteLocal
105 | Ipv6Scope::OrganizationLocal
106 | Ipv6Scope::Global => Some(Ipv6SourceAddr { addr }),
107 }
108 }
109}
110
111impl From<Ipv6SourceAddr> for net_types::ip::Ipv6SourceAddr {
112 fn from(addr: Ipv6SourceAddr) -> Self {
113 net_types::ip::Ipv6SourceAddr::Unicast(addr.addr)
114 }
115}
116
117#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
121pub struct Ipv6DestinationAddr {
122 addr: MulticastAddr<Ipv6Addr>,
123}
124
125impl Ipv6DestinationAddr {
126 fn new(addr: Ipv6Addr) -> Option<Self> {
130 if addr.scope().multicast_scope_id() <= Ipv6Scope::MULTICAST_SCOPE_ID_LINK_LOCAL {
134 None
135 } else {
136 Some(Ipv6DestinationAddr { addr: MulticastAddr::new(addr)? })
137 }
138 }
139}
140
141impl From<Ipv6DestinationAddr> for SpecifiedAddr<Ipv6Addr> {
142 fn from(addr: Ipv6DestinationAddr) -> Self {
143 addr.addr.into_specified()
144 }
145}
146
147pub trait MulticastRouteIpExt: IpExt {
149 type SourceAddress: Clone
151 + Debug
152 + Eq
153 + Hash
154 + Ord
155 + PartialEq
156 + PartialOrd
157 + Into<Self::RecvSrcAddr>;
158 type DestinationAddress: Clone
160 + Debug
161 + Eq
162 + Hash
163 + Ord
164 + PartialEq
165 + PartialOrd
166 + Into<SpecifiedAddr<Self::Addr>>;
167}
168
169impl MulticastRouteIpExt for Ipv4 {
170 type SourceAddress = Ipv4SourceAddr;
171 type DestinationAddress = Ipv4DestinationAddr;
172}
173
174impl MulticastRouteIpExt for Ipv6 {
175 type SourceAddress = Ipv6SourceAddr;
176 type DestinationAddress = Ipv6DestinationAddr;
177}
178
179#[derive(Clone, Debug, Eq, GenericOverIp, Hash, Ord, PartialEq, PartialOrd)]
181#[generic_over_ip(I, Ip)]
182pub struct MulticastRouteKey<I: MulticastRouteIpExt> {
183 pub(crate) src_addr: I::SourceAddress,
185 pub(crate) dst_addr: I::DestinationAddress,
187}
188
189impl<I: MulticastRouteIpExt> MulticastRouteKey<I> {
190 pub fn new(src_addr: I::Addr, dst_addr: I::Addr) -> Option<MulticastRouteKey<I>> {
194 I::map_ip(
195 (src_addr, dst_addr),
196 |(src_addr, dst_addr)| {
197 Some(MulticastRouteKey {
198 src_addr: Ipv4SourceAddr::new(src_addr)?,
199 dst_addr: Ipv4DestinationAddr::new(dst_addr)?,
200 })
201 },
202 |(src_addr, dst_addr)| {
203 Some(MulticastRouteKey {
204 src_addr: Ipv6SourceAddr::new(src_addr)?,
205 dst_addr: Ipv6DestinationAddr::new(dst_addr)?,
206 })
207 },
208 )
209 }
210
211 pub fn src_addr(&self) -> I::Addr {
213 I::map_ip(self, |key| **key.src_addr.addr, |key| **key.src_addr.addr)
214 }
215
216 pub fn dst_addr(&self) -> I::Addr {
218 I::map_ip(self, |key| *key.dst_addr.addr, |key| *key.dst_addr.addr)
219 }
220}
221
222impl<I: MulticastRouteIpExt> Inspectable for MulticastRouteKey<I> {
223 fn record<II: Inspector>(&self, inspector: &mut II) {
224 inspector.record_ip_addr("SourceAddress", self.src_addr());
225 inspector.record_ip_addr("DestinationAddress", self.dst_addr());
226 }
227}
228
229#[derive(Derivative)]
231#[derivative(Debug(bound = ""))]
232pub struct MulticastRouteEntry<D: StrongDeviceIdentifier, BT: InstantBindingsTypes> {
233 pub(crate) route: MulticastRoute<D>,
234 pub(crate) stats: MulticastRouteStats<BT::AtomicInstant>,
237}
238
239impl<D: StrongDeviceIdentifier, BT: InstantBindingsTypes> MulticastRouteEntry<D, BT> {
240 pub(crate) fn inspect<I: Inspector, II: InspectorDeviceExt<D>>(&self, inspector: &mut I) {
244 let MulticastRouteEntry {
245 route: MulticastRoute { input_interface, action },
246 stats: MulticastRouteStats { last_used },
247 } = self;
248 II::record_device(inspector, "InputInterface", input_interface);
249 let Action::Forward(targets) = action;
250 inspector.record_child("ForwardingTargets", |inspector| {
251 for MulticastRouteTarget { output_interface, min_ttl } in targets.iter() {
252 inspector.record_unnamed_child(|inspector| {
253 II::record_device(inspector, "OutputInterface", output_interface);
254 inspector.record_uint("MinTTL", *min_ttl);
255 });
256 }
257 });
258 inspector.record_child("Statistics", |inspector| {
259 last_used.load(Ordering::Relaxed).record("LastUsed", inspector);
260 });
261 }
262}
263
264#[derive(Clone, Debug, Eq, PartialEq)]
268pub struct MulticastRoute<D: StrongDeviceIdentifier> {
269 pub(crate) input_interface: D,
271 pub(crate) action: Action<D>,
273}
274
275#[derive(Clone, Debug, Eq, PartialEq)]
277pub(crate) enum Action<D: StrongDeviceIdentifier> {
278 Forward(MulticastRouteTargets<D>),
280}
281
282pub type MulticastRouteTargets<D> = Arc<[MulticastRouteTarget<D>]>;
294
295#[derive(Clone, Debug, Eq, Hash, PartialEq)]
297pub struct MulticastRouteTarget<D: StrongDeviceIdentifier> {
298 pub output_interface: D,
300 pub min_ttl: u8,
304}
305
306#[derive(Debug, Eq, PartialEq)]
308pub enum ForwardMulticastRouteError {
309 EmptyTargetList,
311 InputInterfaceIsTarget,
314 DuplicateTarget,
316}
317
318impl<D: StrongDeviceIdentifier> MulticastRoute<D> {
319 pub fn new_forward(
321 input_interface: D,
322 targets: MulticastRouteTargets<D>,
323 ) -> Result<Self, ForwardMulticastRouteError> {
324 if targets.is_empty() {
325 return Err(ForwardMulticastRouteError::EmptyTargetList);
326 }
327 if targets.iter().any(|MulticastRouteTarget { output_interface, min_ttl: _ }| {
328 output_interface == &input_interface
329 }) {
330 return Err(ForwardMulticastRouteError::InputInterfaceIsTarget);
331 }
332
333 for (index, target_a) in targets.iter().enumerate() {
338 if targets[index + 1..]
342 .iter()
343 .any(|target_b| target_a.output_interface == target_b.output_interface)
344 {
345 return Err(ForwardMulticastRouteError::DuplicateTarget);
346 }
347 }
348
349 Ok(MulticastRoute { input_interface, action: Action::Forward(targets) })
350 }
351}
352
353#[derive(Debug, Eq, PartialEq)]
355pub struct MulticastRouteStats<Instant> {
356 pub last_used: Instant,
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370
371 use alloc::vec;
372 use alloc::vec::Vec;
373 use net_declare::{net_ip_v4, net_ip_v6};
374 use netstack3_base::testutil::MultipleDevicesId;
375 use test_case::test_case;
376
377 const UNICAST_V4: Ipv4Addr = net_ip_v4!("192.0.2.1");
378 const MULTICAST_V4: Ipv4Addr = net_ip_v4!("224.0.1.1");
379 const LL_UNICAST_V4: Ipv4Addr = net_ip_v4!("169.254.0.1");
380 const LL_MULTICAST_V4: Ipv4Addr = net_ip_v4!("224.0.0.1");
381 const UNICAST_V6: Ipv6Addr = net_ip_v6!("2001:0DB8::1");
382 const MULTICAST_V6: Ipv6Addr = net_ip_v6!("ff0e::1");
383 const LL_UNICAST_V6: Ipv6Addr = net_ip_v6!("fe80::1");
384 const LL_MULTICAST_V6: Ipv6Addr = net_ip_v6!("ff02::1");
385 const V4_MAPPED_V6: Ipv6Addr = net_ip_v6!("::FFFF:192.0.2.1");
386
387 #[test_case(UNICAST_V4, MULTICAST_V4 => true; "success")]
388 #[test_case(UNICAST_V4, UNICAST_V4 => false; "unicast_dst")]
389 #[test_case(UNICAST_V4, Ipv4::UNSPECIFIED_ADDRESS => false; "unspecified_dst")]
390 #[test_case(MULTICAST_V4, MULTICAST_V4 => false; "multicast_src")]
391 #[test_case(Ipv4::UNSPECIFIED_ADDRESS, MULTICAST_V4 => false; "unspecified_src")]
392 #[test_case(LL_UNICAST_V4, MULTICAST_V4 => false; "ll_unicast_src")]
393 #[test_case(UNICAST_V4, LL_MULTICAST_V4 => false; "ll_multicast_dst")]
394 fn new_ipv4_route_key(src_addr: Ipv4Addr, dst_addr: Ipv4Addr) -> bool {
395 MulticastRouteKey::<Ipv4>::new(src_addr, dst_addr).is_some()
396 }
397
398 #[test_case(UNICAST_V6, MULTICAST_V6 => true; "success")]
399 #[test_case(UNICAST_V6, UNICAST_V6 => false; "unicast_dst")]
400 #[test_case(UNICAST_V6, Ipv6::UNSPECIFIED_ADDRESS => false; "unspecified_dst")]
401 #[test_case(MULTICAST_V6, MULTICAST_V6 => false; "multicast_src")]
402 #[test_case(Ipv6::UNSPECIFIED_ADDRESS, MULTICAST_V6 => false; "unspecified_src")]
403 #[test_case(LL_UNICAST_V6, MULTICAST_V6 => false; "ll_unicast_src")]
404 #[test_case(UNICAST_V6, LL_MULTICAST_V6 => false; "ll_multicast_dst")]
405 #[test_case(V4_MAPPED_V6, LL_MULTICAST_V6 => false; "mapped_src")]
406 fn new_ipv6_route_key(src_addr: Ipv6Addr, dst_addr: Ipv6Addr) -> bool {
407 MulticastRouteKey::<Ipv6>::new(src_addr, dst_addr).is_some()
408 }
409
410 #[test_case(MultipleDevicesId::A, vec![] =>
411 Some(ForwardMulticastRouteError::EmptyTargetList); "empty_target_list")]
412 #[test_case(MultipleDevicesId::A, vec![MultipleDevicesId::A] =>
413 Some(ForwardMulticastRouteError::InputInterfaceIsTarget); "input_interface_is_target")]
414 #[test_case(MultipleDevicesId::A, vec![MultipleDevicesId::B, MultipleDevicesId::B] =>
415 Some(ForwardMulticastRouteError::DuplicateTarget); "duplicate_target")]
416 #[test_case(MultipleDevicesId::A, vec![MultipleDevicesId::B, MultipleDevicesId::C] =>
417 None; "valid_route")]
418 fn new_forward(
419 input_interface: MultipleDevicesId,
420 output_interfaces: Vec<MultipleDevicesId>,
421 ) -> Option<ForwardMulticastRouteError> {
422 let targets = output_interfaces
423 .into_iter()
424 .map(|output_interface| MulticastRouteTarget { output_interface, min_ttl: 0 })
425 .collect();
426 MulticastRoute::new_forward(input_interface, targets).err()
427 }
428}