netstack3_ip/multicast_forwarding/
route.rs1use alloc::fmt::Debug;
8use alloc::sync::Arc;
9use core::hash::Hash;
10use core::num::NonZeroU8;
11use core::sync::atomic::Ordering;
12use derivative::Derivative;
13use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr, Ipv6Scope};
14use net_types::{
15 MulticastAddr, NonMappedAddr, NonMulticastAddr, ScopeableAddress as _, SpecifiedAddr,
16 UnicastAddr,
17};
18use netstack3_base::{
19 AtomicInstant, Inspectable, InspectableValue, Inspector, InspectorDeviceExt,
20 InstantBindingsTypes, IpExt, StrongDeviceIdentifier,
21};
22
23#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
33pub struct Ipv4SourceAddr {
34 addr: NonMulticastAddr<SpecifiedAddr<Ipv4Addr>>,
35}
36
37impl Ipv4SourceAddr {
38 fn new(addr: Ipv4Addr) -> Option<Self> {
42 if Ipv4::LINK_LOCAL_UNICAST_SUBNET.contains(&addr) {
43 return None;
44 }
45
46 Some(Ipv4SourceAddr { addr: NonMulticastAddr::new(SpecifiedAddr::new(addr)?)? })
47 }
48}
49
50impl From<Ipv4SourceAddr> for net_types::ip::Ipv4SourceAddr {
51 fn from(addr: Ipv4SourceAddr) -> Self {
52 net_types::ip::Ipv4SourceAddr::Specified(addr.addr)
53 }
54}
55
56#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
60pub struct Ipv4DestinationAddr {
61 addr: MulticastAddr<Ipv4Addr>,
62}
63
64impl Ipv4DestinationAddr {
65 fn new(addr: Ipv4Addr) -> Option<Self> {
69 if Ipv4::LINK_LOCAL_MULTICAST_SUBNET.contains(&addr) {
73 None
74 } else {
75 Some(Ipv4DestinationAddr { addr: MulticastAddr::new(addr)? })
76 }
77 }
78}
79
80impl From<Ipv4DestinationAddr> for SpecifiedAddr<Ipv4Addr> {
81 fn from(addr: Ipv4DestinationAddr) -> Self {
82 addr.addr.into_specified()
83 }
84}
85
86#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
91pub struct Ipv6SourceAddr {
92 addr: NonMappedAddr<UnicastAddr<Ipv6Addr>>,
93}
94
95impl Ipv6SourceAddr {
96 fn new(addr: Ipv6Addr) -> Option<Self> {
100 let addr = NonMappedAddr::new(UnicastAddr::new(addr)?)?;
101 match addr.scope() {
102 Ipv6Scope::InterfaceLocal | Ipv6Scope::LinkLocal => None,
103 Ipv6Scope::Reserved(_) | Ipv6Scope::Unassigned(_) => None,
104 Ipv6Scope::RealmLocal | Ipv6Scope::AdminLocal | Ipv6Scope::OrganizationLocal => {
105 unreachable!("Observed a multicast scope ID on a known unicast address");
106 }
107 Ipv6Scope::SiteLocal | Ipv6Scope::Global => Some(Ipv6SourceAddr { addr }),
108 }
109 }
110}
111
112impl From<Ipv6SourceAddr> for net_types::ip::Ipv6SourceAddr {
113 fn from(addr: Ipv6SourceAddr) -> Self {
114 net_types::ip::Ipv6SourceAddr::Unicast(addr.addr)
115 }
116}
117
118#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
122pub struct Ipv6DestinationAddr {
123 addr: MulticastAddr<Ipv6Addr>,
124}
125
126impl Ipv6DestinationAddr {
127 fn new(addr: Ipv6Addr) -> Option<Self> {
131 if addr.scope().multicast_scope_id() <= Ipv6Scope::MULTICAST_SCOPE_ID_REALM_LOCAL {
135 None
136 } else {
137 Some(Ipv6DestinationAddr { addr: MulticastAddr::new(addr)? })
138 }
139 }
140}
141
142impl From<Ipv6DestinationAddr> for SpecifiedAddr<Ipv6Addr> {
143 fn from(addr: Ipv6DestinationAddr) -> Self {
144 addr.addr.into_specified()
145 }
146}
147
148pub trait MulticastRouteIpExt: IpExt {
150 type SourceAddress: Clone
152 + Debug
153 + Eq
154 + Hash
155 + Ord
156 + PartialEq
157 + PartialOrd
158 + Into<Self::RecvSrcAddr>;
159 type DestinationAddress: Clone
161 + Debug
162 + Eq
163 + Hash
164 + Ord
165 + PartialEq
166 + PartialOrd
167 + Into<SpecifiedAddr<Self::Addr>>;
168}
169
170impl MulticastRouteIpExt for Ipv4 {
171 type SourceAddress = Ipv4SourceAddr;
172 type DestinationAddress = Ipv4DestinationAddr;
173}
174
175impl MulticastRouteIpExt for Ipv6 {
176 type SourceAddress = Ipv6SourceAddr;
177 type DestinationAddress = Ipv6DestinationAddr;
178}
179
180#[derive(Clone, Debug, Eq, GenericOverIp, Hash, Ord, PartialEq, PartialOrd)]
182#[generic_over_ip(I, Ip)]
183pub struct MulticastRouteKey<I: MulticastRouteIpExt> {
184 pub(crate) src_addr: I::SourceAddress,
186 pub(crate) dst_addr: I::DestinationAddress,
188}
189
190impl<I: MulticastRouteIpExt> MulticastRouteKey<I> {
191 pub fn new(src_addr: I::Addr, dst_addr: I::Addr) -> Option<MulticastRouteKey<I>> {
195 I::map_ip(
196 (src_addr, dst_addr),
197 |(src_addr, dst_addr)| {
198 Some(MulticastRouteKey {
199 src_addr: Ipv4SourceAddr::new(src_addr)?,
200 dst_addr: Ipv4DestinationAddr::new(dst_addr)?,
201 })
202 },
203 |(src_addr, dst_addr)| {
204 Some(MulticastRouteKey {
205 src_addr: Ipv6SourceAddr::new(src_addr)?,
206 dst_addr: Ipv6DestinationAddr::new(dst_addr)?,
207 })
208 },
209 )
210 }
211
212 pub fn src_addr(&self) -> I::Addr {
214 I::map_ip(self, |key| **key.src_addr.addr, |key| **key.src_addr.addr)
215 }
216
217 pub fn dst_addr(&self) -> I::Addr {
219 I::map_ip(self, |key| *key.dst_addr.addr, |key| *key.dst_addr.addr)
220 }
221}
222
223impl<I: MulticastRouteIpExt> Inspectable for MulticastRouteKey<I> {
224 fn record<II: Inspector>(&self, inspector: &mut II) {
225 inspector.record_ip_addr("SourceAddress", self.src_addr());
226 inspector.record_ip_addr("DestinationAddress", self.dst_addr());
227 }
228}
229
230#[derive(Derivative)]
232#[derivative(Debug(bound = ""))]
233pub struct MulticastRouteEntry<D: StrongDeviceIdentifier, BT: InstantBindingsTypes> {
234 pub(crate) route: MulticastRoute<D>,
235 pub(crate) stats: MulticastRouteStats<BT::AtomicInstant>,
238}
239
240impl<D: StrongDeviceIdentifier, BT: InstantBindingsTypes> MulticastRouteEntry<D, BT> {
241 pub(crate) fn inspect<I: Inspector, II: InspectorDeviceExt<D>>(&self, inspector: &mut I) {
245 let MulticastRouteEntry {
246 route: MulticastRoute { input_interface, action },
247 stats: MulticastRouteStats { last_used },
248 } = self;
249 II::record_device(inspector, "InputInterface", input_interface);
250 let Action::Forward(targets) = action;
251 inspector.record_child("ForwardingTargets", |inspector| {
252 for MulticastRouteTarget { output_interface, min_ttl } in targets.iter() {
253 inspector.record_unnamed_child(|inspector| {
254 II::record_device(inspector, "OutputInterface", output_interface);
255 inspector.record_uint("MinTTL", min_ttl.get());
256 });
257 }
258 });
259 inspector.record_child("Statistics", |inspector| {
260 last_used.load(Ordering::Relaxed).record("LastUsed", inspector);
261 });
262 }
263}
264
265#[derive(Clone, Debug, Eq, PartialEq)]
269pub struct MulticastRoute<D: StrongDeviceIdentifier> {
270 pub(crate) input_interface: D,
272 pub(crate) action: Action<D>,
274}
275
276#[derive(Clone, Debug, Eq, PartialEq)]
278pub(crate) enum Action<D: StrongDeviceIdentifier> {
279 Forward(MulticastRouteTargets<D>),
281}
282
283pub type MulticastRouteTargets<D> = Arc<[MulticastRouteTarget<D>]>;
295
296#[derive(Clone, Debug, Eq, Hash, PartialEq)]
298pub struct MulticastRouteTarget<D: StrongDeviceIdentifier> {
299 pub output_interface: D,
301 pub min_ttl: NonZeroU8,
305}
306
307#[derive(Debug, Eq, PartialEq)]
309pub enum ForwardMulticastRouteError {
310 EmptyTargetList,
312 InputInterfaceIsTarget,
315 DuplicateTarget,
317}
318
319impl<D: StrongDeviceIdentifier> MulticastRoute<D> {
320 pub fn new_forward(
322 input_interface: D,
323 targets: MulticastRouteTargets<D>,
324 ) -> Result<Self, ForwardMulticastRouteError> {
325 if targets.is_empty() {
326 return Err(ForwardMulticastRouteError::EmptyTargetList);
327 }
328 if targets.iter().any(|MulticastRouteTarget { output_interface, min_ttl: _ }| {
329 output_interface == &input_interface
330 }) {
331 return Err(ForwardMulticastRouteError::InputInterfaceIsTarget);
332 }
333
334 for (index, target_a) in targets.iter().enumerate() {
339 if targets[index + 1..]
343 .iter()
344 .any(|target_b| target_a.output_interface == target_b.output_interface)
345 {
346 return Err(ForwardMulticastRouteError::DuplicateTarget);
347 }
348 }
349
350 Ok(MulticastRoute { input_interface, action: Action::Forward(targets) })
351 }
352}
353
354#[derive(Debug, Eq, PartialEq)]
356pub struct MulticastRouteStats<Instant> {
357 pub last_used: Instant,
366}
367
368#[cfg(test)]
369mod tests {
370 use super::*;
371
372 use alloc::vec;
373 use alloc::vec::Vec;
374 use net_declare::{net_ip_v4, net_ip_v6};
375 use netstack3_base::testutil::MultipleDevicesId;
376 use test_case::test_case;
377
378 const UNICAST_V4: Ipv4Addr = net_ip_v4!("192.0.2.1");
379 const MULTICAST_V4: Ipv4Addr = net_ip_v4!("224.0.1.1");
380 const LL_UNICAST_V4: Ipv4Addr = net_ip_v4!("169.254.0.1");
381 const LL_MULTICAST_V4: Ipv4Addr = net_ip_v4!("224.0.0.1");
382 const UNICAST_V6: Ipv6Addr = net_ip_v6!("2001:0DB8::1");
383 const MULTICAST_V6: Ipv6Addr = net_ip_v6!("ff0e::1");
384 const LL_UNICAST_V6: Ipv6Addr = net_ip_v6!("fe80::1");
385 const LL_MULTICAST_V6: Ipv6Addr = net_ip_v6!("ff02::1");
386 const V4_MAPPED_V6: Ipv6Addr = net_ip_v6!("::FFFF:192.0.2.1");
387 const RL_MULTICAST_V6: Ipv6Addr = net_ip_v6!("ff03::1");
388
389 #[test_case(UNICAST_V4, MULTICAST_V4 => true; "success")]
390 #[test_case(UNICAST_V4, UNICAST_V4 => false; "unicast_dst")]
391 #[test_case(UNICAST_V4, Ipv4::UNSPECIFIED_ADDRESS => false; "unspecified_dst")]
392 #[test_case(MULTICAST_V4, MULTICAST_V4 => false; "multicast_src")]
393 #[test_case(Ipv4::UNSPECIFIED_ADDRESS, MULTICAST_V4 => false; "unspecified_src")]
394 #[test_case(LL_UNICAST_V4, MULTICAST_V4 => false; "ll_unicast_src")]
395 #[test_case(UNICAST_V4, LL_MULTICAST_V4 => false; "ll_multicast_dst")]
396 fn new_ipv4_route_key(src_addr: Ipv4Addr, dst_addr: Ipv4Addr) -> bool {
397 MulticastRouteKey::<Ipv4>::new(src_addr, dst_addr).is_some()
398 }
399
400 #[test_case(UNICAST_V6, MULTICAST_V6 => true; "success")]
401 #[test_case(UNICAST_V6, UNICAST_V6 => false; "unicast_dst")]
402 #[test_case(UNICAST_V6, Ipv6::UNSPECIFIED_ADDRESS => false; "unspecified_dst")]
403 #[test_case(MULTICAST_V6, MULTICAST_V6 => false; "multicast_src")]
404 #[test_case(Ipv6::UNSPECIFIED_ADDRESS, MULTICAST_V6 => false; "unspecified_src")]
405 #[test_case(LL_UNICAST_V6, MULTICAST_V6 => false; "ll_unicast_src")]
406 #[test_case(UNICAST_V6, LL_MULTICAST_V6 => false; "ll_multicast_dst")]
407 #[test_case(V4_MAPPED_V6, LL_MULTICAST_V6 => false; "mapped_src")]
408 #[test_case(UNICAST_V6, RL_MULTICAST_V6 => false; "rl_multicast_dst")]
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 {
428 output_interface,
429 min_ttl: NonZeroU8::new(1).unwrap(),
430 })
431 .collect();
432 MulticastRoute::new_forward(input_interface, targets).err()
433 }
434}