1use alloc::vec::Vec;
8use arrayvec::ArrayVec;
9use core::num::NonZeroUsize;
10use core::time::Duration;
11use derivative::Derivative;
12use lru::LruCache;
13use net_types::ip::{Ip, IpVersionMarker};
14use netstack3_base::{
15 CoreTimerContext, Inspectable, Inspector, Instant as _, LocalFrameDestination, Marks,
16 StrongDeviceIdentifier as _, WeakDeviceIdentifier,
17};
18use packet::{Buf, ParseBufferMut};
19use packet_formats::ip::IpPacket;
20use zerocopy::SplitByteSlice;
21
22use crate::IpLayerIpExt;
23use crate::internal::multicast_forwarding::{
24 MulticastForwardingBindingsContext, MulticastForwardingBindingsTypes,
25 MulticastForwardingTimerId,
26};
27use crate::multicast_forwarding::MulticastRouteKey;
28
29pub(crate) const PACKET_QUEUE_LEN: usize = 3;
34
35const MAX_PENDING_ROUTES: NonZeroUsize = NonZeroUsize::new(1000).unwrap();
41
42const PENDING_ROUTE_EXPIRATION: Duration = Duration::from_secs(10);
47
48const PENDING_ROUTE_GC_PERIOD: Duration = Duration::from_secs(10);
54
55#[derive(Derivative)]
61#[derivative(Debug(bound = ""))]
62pub struct MulticastForwardingPendingPackets<
63 I: IpLayerIpExt,
64 D: WeakDeviceIdentifier,
65 BT: MulticastForwardingBindingsTypes,
66> {
67 table: LruCache<MulticastRouteKey<I>, PacketQueue<I, D, BT>>,
68 gc_timer: BT::Timer,
78}
79
80impl<I: IpLayerIpExt, D: WeakDeviceIdentifier, BC: MulticastForwardingBindingsContext<I, D::Strong>>
81 MulticastForwardingPendingPackets<I, D, BC>
82{
83 pub(crate) fn new<CC>(bindings_ctx: &mut BC) -> Self
84 where
85 CC: CoreTimerContext<MulticastForwardingTimerId<I>, BC>,
86 {
87 Self {
88 table: LruCache::new(MAX_PENDING_ROUTES),
89 gc_timer: CC::new_timer(
90 bindings_ctx,
91 MulticastForwardingTimerId::PendingPacketsGc(IpVersionMarker::<I>::new()),
92 ),
93 }
94 }
95
96 pub(crate) fn try_queue_packet<B>(
100 &mut self,
101 bindings_ctx: &mut BC,
102 key: MulticastRouteKey<I>,
103 packet: &I::Packet<B>,
104 dev: &D::Strong,
105 frame_dst: Option<LocalFrameDestination>,
106 max_fragment_len: Option<usize>,
107 marks: Marks,
108 ) -> QueuePacketOutcome
109 where
110 B: SplitByteSlice,
111 {
112 let was_empty = self.table.is_empty();
113 let outcome = if let Some(queue) = self.table.get_mut(&key) {
114 match queue
115 .try_push(|| QueuedPacket::new(dev, packet, frame_dst, max_fragment_len, marks))
116 {
117 Ok(()) => QueuePacketOutcome::QueuedInExistingQueue,
118 Err(PacketQueueFullError) => QueuePacketOutcome::ExistingQueueFull,
119 }
120 } else {
121 let mut queue = PacketQueue::new(bindings_ctx);
122 queue
123 .try_push(|| QueuedPacket::new(dev, packet, frame_dst, max_fragment_len, marks))
124 .expect("newly instantiated queue must have capacity");
125
126 let prev = self.table.put(key, queue);
127 debug_assert!(prev.is_none());
128 QueuePacketOutcome::QueuedInNewQueue
129 };
130
131 if was_empty && !self.table.is_empty() {
134 let prev = bindings_ctx.schedule_timer(PENDING_ROUTE_GC_PERIOD, &mut self.gc_timer);
135 debug_assert!(prev.is_none());
136 }
137
138 outcome
139 }
140
141 #[cfg(any(debug_assertions, test))]
142 pub(crate) fn contains(&self, key: &MulticastRouteKey<I>) -> bool {
143 self.table.contains(key)
144 }
145
146 pub(crate) fn remove(
150 &mut self,
151 key: &MulticastRouteKey<I>,
152 bindings_ctx: &mut BC,
153 ) -> Option<PacketQueue<I, D, BC>> {
154 let was_empty = self.table.is_empty();
155 let queue = self.table.pop(key);
156
157 if !was_empty && self.table.is_empty() {
161 let _: Option<BC::Instant> = bindings_ctx.cancel_timer(&mut self.gc_timer);
162 }
163
164 queue
165 }
166
167 pub(crate) fn run_garbage_collection(&mut self, bindings_ctx: &mut BC) -> u64 {
171 let now = bindings_ctx.now();
172 let mut removed_count = 0u64;
173 let expired_keys: Vec<_> = self
174 .table
175 .iter()
176 .filter_map(
177 |(key, queue)| if queue.expires_at <= now { Some(key.clone()) } else { None },
178 )
179 .collect();
180
181 for key in expired_keys {
182 let queue = self.table.pop(&key).expect("expired key must be present");
183 removed_count += queue.queue.len() as u64;
186 }
187
188 if !self.table.is_empty() {
192 let _: Option<BC::Instant> =
193 bindings_ctx.schedule_timer(PENDING_ROUTE_GC_PERIOD, &mut self.gc_timer);
194 }
195
196 removed_count
197 }
198}
199
200impl<I: IpLayerIpExt, D: WeakDeviceIdentifier, BT: MulticastForwardingBindingsTypes> Inspectable
201 for MulticastForwardingPendingPackets<I, D, BT>
202{
203 fn record<II: Inspector>(&self, inspector: &mut II) {
204 let MulticastForwardingPendingPackets { table, gc_timer: _ } = self;
205 inspector.record_usize("NumRoutes", table.len())
208 }
209}
210
211#[derive(Debug, PartialEq)]
213pub(crate) enum QueuePacketOutcome {
214 QueuedInNewQueue,
217 QueuedInExistingQueue,
220 ExistingQueueFull,
223}
224
225#[derive(Derivative)]
227#[derivative(Debug(bound = ""))]
228pub struct PacketQueue<I: Ip, D: WeakDeviceIdentifier, BT: MulticastForwardingBindingsTypes> {
229 queue: ArrayVec<QueuedPacket<I, D>, PACKET_QUEUE_LEN>,
230 expires_at: BT::Instant,
232}
233
234impl<I: IpLayerIpExt, D: WeakDeviceIdentifier, BC: MulticastForwardingBindingsContext<I, D::Strong>>
235 PacketQueue<I, D, BC>
236{
237 fn new(bindings_ctx: &mut BC) -> Self {
238 Self {
239 queue: Default::default(),
240 expires_at: bindings_ctx.now().panicking_add(PENDING_ROUTE_EXPIRATION),
241 }
242 }
243
244 fn try_push(
251 &mut self,
252 packet_builder: impl FnOnce() -> QueuedPacket<I, D>,
253 ) -> Result<(), PacketQueueFullError> {
254 if self.queue.is_full() {
255 return Err(PacketQueueFullError);
256 }
257 self.queue.push(packet_builder());
258 Ok(())
259 }
260}
261
262#[derive(Debug)]
263struct PacketQueueFullError;
264
265impl<I: Ip, D: WeakDeviceIdentifier, BT: MulticastForwardingBindingsTypes> IntoIterator
266 for PacketQueue<I, D, BT>
267{
268 type Item = QueuedPacket<I, D>;
269 type IntoIter = <ArrayVec<QueuedPacket<I, D>, PACKET_QUEUE_LEN> as IntoIterator>::IntoIter;
270 fn into_iter(self) -> Self::IntoIter {
271 let Self { queue, expires_at: _ } = self;
272 queue.into_iter()
273 }
274}
275
276#[derive(Debug, PartialEq)]
278pub struct QueuedPacket<I: Ip, D: WeakDeviceIdentifier> {
279 pub(crate) device: D,
281 pub(crate) packet: ValidIpPacketBuf<I>,
283 pub(crate) frame_dst: Option<LocalFrameDestination>,
286 pub(crate) max_fragment_len: Option<usize>,
289 pub(crate) marks: Marks,
291}
292
293impl<I: IpLayerIpExt, D: WeakDeviceIdentifier> QueuedPacket<I, D> {
294 fn new<B: SplitByteSlice>(
295 device: &D::Strong,
296 packet: &I::Packet<B>,
297 frame_dst: Option<LocalFrameDestination>,
298 max_fragment_len: Option<usize>,
299 marks: Marks,
300 ) -> Self {
301 QueuedPacket {
302 device: device.downgrade(),
303 packet: ValidIpPacketBuf::new(packet),
304 frame_dst,
305 max_fragment_len,
306 marks,
307 }
308 }
309}
310
311#[derive(Clone, Debug, PartialEq)]
316pub(crate) struct ValidIpPacketBuf<I: Ip> {
317 buffer: Buf<Vec<u8>>,
318 _version_marker: IpVersionMarker<I>,
319}
320
321impl<I: IpLayerIpExt> ValidIpPacketBuf<I> {
322 fn new<B: SplitByteSlice>(packet: &I::Packet<B>) -> Self {
323 Self { buffer: Buf::new(packet.to_vec(), ..), _version_marker: Default::default() }
324 }
325
326 pub(crate) fn parse_ip_packet_mut(&mut self) -> I::Packet<&mut [u8]> {
334 self.buffer.parse_mut().unwrap()
336 }
337
338 pub(crate) fn into_inner(self) -> Buf<Vec<u8>> {
339 let Self { buffer, _version_marker } = self;
340 buffer
341 }
342}
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347
348 use assert_matches::assert_matches;
349 use ip_test_macro::ip_test;
350 use netstack3_base::testutil::{
351 FakeInstant, FakeTimerCtxExt, FakeWeakDeviceId, MultipleDevicesId,
352 };
353 use netstack3_base::{
354 CounterContext, InstantContext, MarkDomain, StrongDeviceIdentifier, TimerContext,
355 };
356 use packet::ParseBuffer;
357 use static_assertions::const_assert;
358 use test_case::test_case;
359
360 use crate::internal::multicast_forwarding;
361 use crate::internal::multicast_forwarding::counters::MulticastForwardingCounters;
362 use crate::internal::multicast_forwarding::testutil::{
363 FakeBindingsCtx, FakeCoreCtx, TestIpExt,
364 };
365
366 #[ip_test(I)]
367 #[test_case(None, None; "no_metadata")]
368 #[test_case(Some(LocalFrameDestination::Multicast), None; "some_frame_dst")]
369 #[test_case(None, Some(1400); "some_max_fragment_len")]
370 #[test_case(Some(LocalFrameDestination::Multicast), Some(1400); "some_all")]
371 fn queue_packet<I: TestIpExt>(
372 frame_dst: Option<LocalFrameDestination>,
373 max_fragment_len: Option<usize>,
374 ) {
375 const DEV: MultipleDevicesId = MultipleDevicesId::A;
376 let key1 = MulticastRouteKey::new(I::SRC1, I::DST1).unwrap();
377 let key2 = MulticastRouteKey::new(I::SRC2, I::DST2).unwrap();
378 let key3 = MulticastRouteKey::new(I::SRC1, I::DST2).unwrap();
379
380 let buf = multicast_forwarding::testutil::new_ip_packet_buf::<I>(I::SRC1, I::DST1);
383 let mut buf_ref = buf.as_ref();
384 let packet = buf_ref.parse::<I::Packet<_>>().expect("parse should succeed");
385
386 let mut bindings_ctx = FakeBindingsCtx::<I, MultipleDevicesId>::default();
387
388 let mut pending_table =
389 MulticastForwardingPendingPackets::<
390 I,
391 <MultipleDevicesId as StrongDeviceIdentifier>::Weak,
392 _,
393 >::new::<FakeCoreCtx<I, MultipleDevicesId>>(&mut bindings_ctx);
394
395 let marks = Marks::new([(MarkDomain::Mark1, 100)]);
398
399 assert_eq!(
401 pending_table.try_queue_packet(
402 &mut bindings_ctx,
403 key1.clone(),
404 &packet,
405 &DEV,
406 frame_dst,
407 max_fragment_len,
408 marks,
409 ),
410 QueuePacketOutcome::QueuedInNewQueue
411 );
412 for _ in 1..PACKET_QUEUE_LEN {
414 assert_eq!(
415 pending_table.try_queue_packet(
416 &mut bindings_ctx,
417 key1.clone(),
418 &packet,
419 &DEV,
420 frame_dst,
421 max_fragment_len,
422 marks,
423 ),
424 QueuePacketOutcome::QueuedInExistingQueue
425 );
426 }
427 assert_eq!(
429 pending_table.try_queue_packet(
430 &mut bindings_ctx,
431 key1.clone(),
432 &packet,
433 &DEV,
434 frame_dst,
435 max_fragment_len,
436 marks,
437 ),
438 QueuePacketOutcome::ExistingQueueFull
439 );
440
441 assert_eq!(
443 pending_table.try_queue_packet(
444 &mut bindings_ctx,
445 key2.clone(),
446 &packet,
447 &DEV,
448 frame_dst,
449 max_fragment_len,
450 marks,
451 ),
452 QueuePacketOutcome::QueuedInNewQueue
453 );
454
455 let expected_packet = QueuedPacket::new(&DEV, &packet, frame_dst, max_fragment_len, marks);
459 let queue =
460 pending_table.remove(&key1, &mut bindings_ctx).expect("key1 should have a queue");
461 assert_eq!(queue.queue.len(), PACKET_QUEUE_LEN);
462 for packet in queue.queue.as_slice() {
463 assert_eq!(packet, &expected_packet);
464 }
465
466 let queue =
467 pending_table.remove(&key2, &mut bindings_ctx).expect("key2 should have a queue");
468 let packet = assert_matches!(&queue.queue[..], [p] => p);
469 assert_eq!(packet, &expected_packet);
470
471 assert_matches!(pending_table.remove(&key3, &mut bindings_ctx), None);
472 }
473
474 fn next_gc_time<I: TestIpExt>(
476 core_ctx: &mut FakeCoreCtx<I, MultipleDevicesId>,
477 bindings_ctx: &mut FakeBindingsCtx<I, MultipleDevicesId>,
478 ) -> Option<FakeInstant> {
479 multicast_forwarding::testutil::with_pending_table(core_ctx, |pending_table| {
480 bindings_ctx.scheduled_instant(&mut pending_table.gc_timer)
481 })
482 }
483
484 fn try_queue_packet<I: TestIpExt>(
486 core_ctx: &mut FakeCoreCtx<I, MultipleDevicesId>,
487 bindings_ctx: &mut FakeBindingsCtx<I, MultipleDevicesId>,
488 key: MulticastRouteKey<I>,
489 dev: &MultipleDevicesId,
490 frame_dst: Option<LocalFrameDestination>,
491 max_fragment_len: Option<usize>,
492 ) -> QueuePacketOutcome {
493 let buf =
494 multicast_forwarding::testutil::new_ip_packet_buf::<I>(key.src_addr(), key.dst_addr());
495 let mut buf_ref = buf.as_ref();
496 let packet = buf_ref.parse::<I::Packet<_>>().expect("parse should succeed");
497 multicast_forwarding::testutil::with_pending_table(core_ctx, |pending_table| {
498 pending_table.try_queue_packet(
499 bindings_ctx,
500 key,
501 &packet,
502 dev,
503 frame_dst,
504 max_fragment_len,
505 Marks::default(),
506 )
507 })
508 }
509
510 fn remove_packet_queue<I: TestIpExt>(
512 core_ctx: &mut FakeCoreCtx<I, MultipleDevicesId>,
513 bindings_ctx: &mut FakeBindingsCtx<I, MultipleDevicesId>,
514 key: &MulticastRouteKey<I>,
515 ) -> Option<
516 PacketQueue<I, FakeWeakDeviceId<MultipleDevicesId>, FakeBindingsCtx<I, MultipleDevicesId>>,
517 > {
518 multicast_forwarding::testutil::with_pending_table(core_ctx, |pending_table| {
519 pending_table.remove(key, bindings_ctx)
520 })
521 }
522
523 fn run_gc<I: TestIpExt>(
525 core_ctx: &mut FakeCoreCtx<I, MultipleDevicesId>,
526 bindings_ctx: &mut FakeBindingsCtx<I, MultipleDevicesId>,
527 ) {
528 assert_matches!(
529 &bindings_ctx.trigger_timers_until_instant(bindings_ctx.now(), core_ctx)[..],
530 [MulticastForwardingTimerId::PendingPacketsGc(_)]
531 );
532 }
533
534 #[ip_test(I)]
535 fn garbage_collection<I: TestIpExt>() {
536 const DEV: MultipleDevicesId = MultipleDevicesId::A;
537 const FRAME_DST: Option<LocalFrameDestination> = None;
538 const MAX_FRAGMENT_LEN: Option<usize> = None;
539 let key1 = MulticastRouteKey::<I>::new(I::SRC1, I::DST1).unwrap();
540 let key2 = MulticastRouteKey::<I>::new(I::SRC2, I::DST2).unwrap();
541
542 let mut api = multicast_forwarding::testutil::new_api();
543 assert!(api.enable());
544 let (core_ctx, bindings_ctx) = api.contexts();
545
546 const_assert!(PENDING_ROUTE_GC_PERIOD.checked_sub(PENDING_ROUTE_EXPIRATION).is_some());
552 const_assert!(!PENDING_ROUTE_EXPIRATION.is_zero());
553
554 assert!(next_gc_time(core_ctx, bindings_ctx).is_none());
556 let counters: &MulticastForwardingCounters<I> = core_ctx.counters();
557 assert_eq!(counters.pending_table_gc.get(), 0);
558 assert_eq!(counters.pending_packet_drops_gc.get(), 0);
559
560 let expected_first_gc = bindings_ctx.now() + PENDING_ROUTE_GC_PERIOD;
562 assert_eq!(
563 try_queue_packet(
564 core_ctx,
565 bindings_ctx,
566 key1.clone(),
567 &DEV,
568 FRAME_DST,
569 MAX_FRAGMENT_LEN
570 ),
571 QueuePacketOutcome::QueuedInNewQueue
572 );
573 assert_eq!(next_gc_time(core_ctx, bindings_ctx), Some(expected_first_gc));
574
575 bindings_ctx.timers.instant.sleep(PENDING_ROUTE_GC_PERIOD);
579 assert_eq!(
580 try_queue_packet(
581 core_ctx,
582 bindings_ctx,
583 key2.clone(),
584 &DEV,
585 FRAME_DST,
586 MAX_FRAGMENT_LEN
587 ),
588 QueuePacketOutcome::QueuedInNewQueue
589 );
590 assert_eq!(next_gc_time(core_ctx, bindings_ctx), Some(expected_first_gc));
591
592 run_gc(core_ctx, bindings_ctx);
595 let expected_second_gc = bindings_ctx.timers.instant.now() + PENDING_ROUTE_GC_PERIOD;
596 assert_eq!(next_gc_time(core_ctx, bindings_ctx), Some(expected_second_gc));
597
598 let counters: &MulticastForwardingCounters<I> = core_ctx.counters();
600 assert_eq!(counters.pending_table_gc.get(), 1);
601 assert_eq!(counters.pending_packet_drops_gc.get(), 1);
602 assert_matches!(remove_packet_queue(core_ctx, bindings_ctx, &key1), None);
603 assert_matches!(remove_packet_queue(core_ctx, bindings_ctx, &key2), Some(_));
604
605 assert!(next_gc_time(core_ctx, bindings_ctx).is_none());
608
609 assert_eq!(
612 try_queue_packet(
613 core_ctx,
614 bindings_ctx,
615 key1.clone(),
616 &DEV,
617 FRAME_DST,
618 MAX_FRAGMENT_LEN
619 ),
620 QueuePacketOutcome::QueuedInNewQueue
621 );
622 assert_eq!(next_gc_time(core_ctx, bindings_ctx), Some(expected_second_gc));
623 bindings_ctx.timers.instant.sleep(PENDING_ROUTE_GC_PERIOD);
624 run_gc(core_ctx, bindings_ctx);
625 let counters: &MulticastForwardingCounters<I> = core_ctx.counters();
626 assert_eq!(counters.pending_table_gc.get(), 2);
627 assert_eq!(counters.pending_packet_drops_gc.get(), 2);
628 assert_matches!(remove_packet_queue(core_ctx, bindings_ctx, &key1), None);
629 assert!(next_gc_time(core_ctx, bindings_ctx).is_none());
630 }
631}