1use alloc::vec::Vec;
9use core::time::Duration;
10
11use log::trace;
12use lru_cache::LruCache;
13use net_types::ip::{GenericOverIp, Ip, IpAddress, IpVersionMarker, Mtu};
14use netstack3_base::{
15 CoreTimerContext, HandleableTimer, Instant, InstantBindingsTypes, TimerBindingsTypes,
16 TimerContext,
17};
18
19const MAINTENANCE_PERIOD: Duration = Duration::from_secs(3600);
27
28const PMTU_STALE_TIMEOUT: Duration = Duration::from_secs(10800);
33
34const MAX_ENTRIES: usize = 256;
35
36#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash, GenericOverIp)]
38#[generic_over_ip(I, Ip)]
39pub struct PmtuTimerId<I: Ip>(IpVersionMarker<I>);
40
41pub trait PmtuContext<I: Ip, BT: PmtuBindingsTypes> {
43 fn with_state_mut<O, F: FnOnce(&mut PmtuCache<I, BT>) -> O>(&mut self, cb: F) -> O;
45}
46
47pub trait PmtuBindingsTypes: TimerBindingsTypes + InstantBindingsTypes {}
49impl<BT> PmtuBindingsTypes for BT where BT: TimerBindingsTypes + InstantBindingsTypes {}
50
51trait PmtuBindingsContext: PmtuBindingsTypes + TimerContext {}
53impl<BC> PmtuBindingsContext for BC where BC: PmtuBindingsTypes + TimerContext {}
54
55pub(crate) trait PmtuHandler<I: Ip, BC> {
62 fn update_pmtu_if_less(
73 &mut self,
74 bindings_ctx: &mut BC,
75 src_ip: I::Addr,
76 dst_ip: I::Addr,
77 new_mtu: Mtu,
78 ) -> Option<Mtu>;
79
80 fn update_pmtu_next_lower(
90 &mut self,
91 bindings_ctx: &mut BC,
92 src_ip: I::Addr,
93 dst_ip: I::Addr,
94 from: Mtu,
95 ) -> Option<Mtu>;
96}
97
98fn maybe_schedule_timer<BC: PmtuBindingsContext>(
99 bindings_ctx: &mut BC,
100 timer: &mut BC::Timer,
101 cache_is_empty: bool,
102) {
103 if cache_is_empty {
108 return;
109 }
110
111 match bindings_ctx.scheduled_instant(timer) {
112 Some(scheduled_at) => {
113 let _: BC::Instant = scheduled_at;
114 }
116 None => {
117 assert_eq!(bindings_ctx.schedule_timer(MAINTENANCE_PERIOD, timer), None)
119 }
120 }
121}
122
123fn handle_update_result<BC: PmtuBindingsContext>(
125 bindings_ctx: &mut BC,
126 timer: &mut BC::Timer,
127 result: UpdateResult,
128 cache_is_empty: bool,
129) -> Option<Mtu> {
130 match result {
131 UpdateResult::Updated(new_mtu) => {
132 maybe_schedule_timer(bindings_ctx, timer, cache_is_empty);
133 Some(new_mtu)
134 }
135 UpdateResult::NotUpdated(mtu) => mtu,
138 }
139}
140
141impl<I: Ip, BC: PmtuBindingsContext, CC: PmtuContext<I, BC>> PmtuHandler<I, BC> for CC {
142 fn update_pmtu_if_less(
143 &mut self,
144 bindings_ctx: &mut BC,
145 src_ip: I::Addr,
146 dst_ip: I::Addr,
147 new_mtu: Mtu,
148 ) -> Option<Mtu> {
149 self.with_state_mut(|cache| {
150 let now = bindings_ctx.now();
151 let res = cache.update_pmtu_if_less(src_ip, dst_ip, new_mtu, now);
152 let is_empty = cache.is_empty();
153 handle_update_result(bindings_ctx, &mut cache.timer, res, is_empty)
154 })
155 }
156
157 fn update_pmtu_next_lower(
158 &mut self,
159 bindings_ctx: &mut BC,
160 src_ip: I::Addr,
161 dst_ip: I::Addr,
162 from: Mtu,
163 ) -> Option<Mtu> {
164 self.with_state_mut(|cache| {
165 let now = bindings_ctx.now();
166 let res = cache.update_pmtu_next_lower(src_ip, dst_ip, from, now);
167 let is_empty = cache.is_empty();
168 handle_update_result(bindings_ctx, &mut cache.timer, res, is_empty)
169 })
170 }
171}
172
173impl<I: Ip, BC: PmtuBindingsContext, CC: PmtuContext<I, BC>> HandleableTimer<CC, BC>
174 for PmtuTimerId<I>
175{
176 fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, _: BC::UniqueTimerId) {
177 let Self(IpVersionMarker { .. }) = self;
178 core_ctx.with_state_mut(|cache| {
179 let now = bindings_ctx.now();
180 cache.handle_timer(now);
181 let is_empty = cache.is_empty();
182 maybe_schedule_timer(bindings_ctx, &mut cache.timer, is_empty);
183 })
184 }
185}
186
187#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
193pub(crate) struct PmtuCacheKey<A: IpAddress>(A, A);
194
195impl<A: IpAddress> PmtuCacheKey<A> {
196 fn new(src_ip: A, dst_ip: A) -> Self {
197 Self(src_ip, dst_ip)
198 }
199}
200
201#[derive(Debug, PartialEq)]
203pub(crate) struct PmtuCacheData<I> {
204 pmtu: Mtu,
205 last_updated: I,
206}
207
208impl<I: Instant> PmtuCacheData<I> {
209 fn new(pmtu: Mtu, now: I) -> Self {
213 Self { pmtu, last_updated: now }
214 }
215}
216
217pub struct PmtuCache<I: Ip, BT: PmtuBindingsTypes> {
219 cache: LruCache<PmtuCacheKey<I::Addr>, PmtuCacheData<BT::Instant>>,
220 timer: BT::Timer,
221}
222
223impl<I: Ip, BC: PmtuBindingsTypes + TimerContext> PmtuCache<I, BC> {
224 pub(crate) fn new<CC: CoreTimerContext<PmtuTimerId<I>, BC>>(bindings_ctx: &mut BC) -> Self {
225 Self {
226 cache: LruCache::new(MAX_ENTRIES),
227 timer: CC::new_timer(bindings_ctx, PmtuTimerId::default()),
228 }
229 }
230}
231
232enum UpdateResult {
233 Updated(Mtu),
234 NotUpdated(Option<Mtu>),
235}
236
237impl<I: Ip, BT: PmtuBindingsTypes> PmtuCache<I, BT> {
238 pub fn get_pmtu(&mut self, src_ip: I::Addr, dst_ip: I::Addr) -> Option<Mtu> {
240 self.cache.get_mut(&PmtuCacheKey::new(src_ip, dst_ip)).map(|x| x.pmtu)
241 }
242
243 fn update_pmtu_if_less(
251 &mut self,
252 src_ip: I::Addr,
253 dst_ip: I::Addr,
254 new_mtu: Mtu,
255 now: BT::Instant,
256 ) -> UpdateResult {
257 match self.get_pmtu(src_ip, dst_ip) {
258 None => self.update_pmtu(src_ip, dst_ip, new_mtu, now),
260 Some(prev_mtu) if new_mtu < prev_mtu => self.update_pmtu(src_ip, dst_ip, new_mtu, now),
262 Some(prev_mtu) => {
265 trace!(
266 "update_pmtu_if_less: Not updating the PMTU between src {src_ip} and dst
267 {dst_ip} to {new_mtu:?}; is {prev_mtu:?}"
268 );
269 UpdateResult::NotUpdated(Some(prev_mtu))
270 }
271 }
272 }
273
274 fn update_pmtu_next_lower(
286 &mut self,
287 src_ip: I::Addr,
288 dst_ip: I::Addr,
289 from: Mtu,
290 now: BT::Instant,
291 ) -> UpdateResult {
292 if let Some(next_pmtu) = next_lower_pmtu_plateau(from) {
293 trace!(
294 "update_pmtu_next_lower: Attempting to update PMTU between src {src_ip} and dst \
295 {dst_ip} to {next_pmtu:?}"
296 );
297
298 self.update_pmtu_if_less(src_ip, dst_ip, next_pmtu, now)
299 } else {
300 trace!(
303 "update_pmtu_next_lower: Not updating PMTU between src {src_ip} and dst {dst_ip} \
304 as there is no lower PMTU value from {from:?}"
305 );
306 UpdateResult::NotUpdated(self.get_pmtu(src_ip, dst_ip))
307 }
308 }
309
310 fn update_pmtu(
317 &mut self,
318 src_ip: I::Addr,
319 dst_ip: I::Addr,
320 new_mtu: Mtu,
321 now: BT::Instant,
322 ) -> UpdateResult {
323 if new_mtu < I::MINIMUM_LINK_MTU {
329 return UpdateResult::NotUpdated(self.get_pmtu(src_ip, dst_ip));
330 }
331 let _previous =
332 self.cache.insert(PmtuCacheKey::new(src_ip, dst_ip), PmtuCacheData::new(new_mtu, now));
333
334 log::debug!("updated PMTU for path {src_ip} -> {dst_ip} to {new_mtu:?}");
335
336 UpdateResult::Updated(new_mtu)
337 }
338
339 fn handle_timer(&mut self, now: BT::Instant) {
340 assert!(!self.cache.is_empty());
342
343 let to_remove: Vec<_> = self
369 .cache
370 .iter()
371 .filter_map(|(k, v)| {
372 (now.saturating_duration_since(v.last_updated) >= PMTU_STALE_TIMEOUT).then_some(*k)
373 })
374 .collect();
375 for key in to_remove {
376 let _: Option<_> = self.cache.remove(&key);
377 }
378 }
379
380 fn is_empty(&self) -> bool {
381 self.cache.is_empty()
382 }
383}
384
385fn next_lower_pmtu_plateau(start_mtu: Mtu) -> Option<Mtu> {
387 const PMTU_PLATEAUS: [Mtu; 12] = [
405 Mtu::new(65535),
406 Mtu::new(32000),
407 Mtu::new(17914),
408 Mtu::new(8166),
409 Mtu::new(4352),
410 Mtu::new(2002),
411 Mtu::new(1492),
412 Mtu::new(1280),
413 Mtu::new(1006),
414 Mtu::new(508),
415 Mtu::new(296),
416 Mtu::new(68),
417 ];
418
419 for i in 0..PMTU_PLATEAUS.len() {
420 let pmtu = PMTU_PLATEAUS[i];
421
422 if pmtu < start_mtu {
423 return Some(pmtu);
426 }
427 }
428
429 None
430}
431
432#[cfg(test)]
433#[macro_use]
434pub(crate) mod testutil {
435 macro_rules! impl_pmtu_handler {
437 ($ty:ty, $ctx:ty, $ip_version:ident) => {
438 impl PmtuHandler<net_types::ip::$ip_version, $ctx> for $ty {
439 fn update_pmtu_if_less(
440 &mut self,
441 _ctx: &mut $ctx,
442 _src_ip: <net_types::ip::$ip_version as net_types::ip::Ip>::Addr,
443 _dst_ip: <net_types::ip::$ip_version as net_types::ip::Ip>::Addr,
444 _new_mtu: Mtu,
445 ) -> Option<Mtu> {
446 unimplemented!()
447 }
448
449 fn update_pmtu_next_lower(
450 &mut self,
451 _ctx: &mut $ctx,
452 _src_ip: <net_types::ip::$ip_version as net_types::ip::Ip>::Addr,
453 _dst_ip: <net_types::ip::$ip_version as net_types::ip::Ip>::Addr,
454 _from: Mtu,
455 ) -> Option<Mtu> {
456 unimplemented!()
457 }
458 }
459 };
460 }
461}
462
463#[cfg(test)]
464mod tests {
465 use super::*;
466
467 use ip_test_macro::ip_test;
468 use net_types::{SpecifiedAddr, Witness};
469 use netstack3_base::testutil::{
470 FakeBindingsCtx, FakeCoreCtx, FakeInstant, FakeTimerCtxExt, TestIpExt, assert_empty,
471 };
472 use netstack3_base::{CtxPair, InstantContext, IntoCoreTimerCtx};
473 use test_case::test_case;
474
475 struct FakePmtuContext<I: Ip> {
476 cache: PmtuCache<I, FakeBindingsCtxImpl<I>>,
477 }
478
479 type FakeCtxImpl<I> = CtxPair<FakeCoreCtxImpl<I>, FakeBindingsCtxImpl<I>>;
480 type FakeCoreCtxImpl<I> = FakeCoreCtx<FakePmtuContext<I>, (), ()>;
481 type FakeBindingsCtxImpl<I> = FakeBindingsCtx<PmtuTimerId<I>, (), (), ()>;
482
483 impl<I: Ip> PmtuContext<I, FakeBindingsCtxImpl<I>> for FakeCoreCtxImpl<I> {
484 fn with_state_mut<O, F: FnOnce(&mut PmtuCache<I, FakeBindingsCtxImpl<I>>) -> O>(
485 &mut self,
486 cb: F,
487 ) -> O {
488 cb(&mut self.state.cache)
489 }
490 }
491
492 fn new_context<I: Ip>() -> FakeCtxImpl<I> {
493 FakeCtxImpl::with_default_bindings_ctx(|bindings_ctx| {
494 FakeCoreCtxImpl::with_state(FakePmtuContext {
495 cache: PmtuCache::new::<IntoCoreTimerCtx>(bindings_ctx),
496 })
497 })
498 }
499
500 fn get_other_ip_address<I: TestIpExt>() -> SpecifiedAddr<I::Addr> {
503 I::get_other_ip_address(3)
504 }
505
506 impl<I: Ip, BT: PmtuBindingsTypes> PmtuCache<I, BT> {
507 fn get_last_updated(&mut self, src_ip: I::Addr, dst_ip: I::Addr) -> Option<BT::Instant> {
512 self.cache.get_mut(&PmtuCacheKey::new(src_ip, dst_ip)).map(|x| x.last_updated.clone())
513 }
514 }
515
516 #[test_case(Mtu::new(65536) => Some(Mtu::new(65535)))]
517 #[test_case(Mtu::new(65535) => Some(Mtu::new(32000)))]
518 #[test_case(Mtu::new(65534) => Some(Mtu::new(32000)))]
519 #[test_case(Mtu::new(32001) => Some(Mtu::new(32000)))]
520 #[test_case(Mtu::new(32000) => Some(Mtu::new(17914)))]
521 #[test_case(Mtu::new(31999) => Some(Mtu::new(17914)))]
522 #[test_case(Mtu::new(1281) => Some(Mtu::new(1280)))]
523 #[test_case(Mtu::new(1280) => Some(Mtu::new(1006)))]
524 #[test_case(Mtu::new(69) => Some(Mtu::new(68)))]
525 #[test_case(Mtu::new(68) => None)]
526 #[test_case(Mtu::new(67) => None)]
527 #[test_case(Mtu::new(0) => None)]
528 fn test_next_lower_pmtu_plateau(start: Mtu) -> Option<Mtu> {
529 next_lower_pmtu_plateau(start)
530 }
531
532 fn get_pmtu<I: Ip>(
533 core_ctx: &mut FakeCoreCtxImpl<I>,
534 src_ip: I::Addr,
535 dst_ip: I::Addr,
536 ) -> Option<Mtu> {
537 core_ctx.state.cache.get_pmtu(src_ip, dst_ip)
538 }
539
540 fn get_last_updated<I: Ip>(
541 core_ctx: &mut FakeCoreCtxImpl<I>,
542 src_ip: I::Addr,
543 dst_ip: I::Addr,
544 ) -> Option<FakeInstant> {
545 core_ctx.state.cache.get_last_updated(src_ip, dst_ip)
546 }
547
548 #[ip_test(I)]
549 fn test_ip_path_mtu_cache_ctx<I: TestIpExt>() {
550 let fake_config = I::TEST_ADDRS;
551 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
552
553 assert_eq!(
555 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()),
556 None
557 );
558 assert_eq!(
559 get_last_updated(
560 &mut core_ctx,
561 fake_config.local_ip.get(),
562 fake_config.remote_ip.get()
563 ),
564 None
565 );
566
567 let new_mtu1 = Mtu::new(u32::from(I::MINIMUM_LINK_MTU) + 50);
568 let start_time = bindings_ctx.now();
569 let duration = Duration::from_secs(1);
570
571 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
573
574 assert_eq!(
578 PmtuHandler::update_pmtu_if_less(
579 &mut core_ctx,
580 &mut bindings_ctx,
581 fake_config.local_ip.get(),
582 fake_config.remote_ip.get(),
583 new_mtu1,
584 ),
585 Some(new_mtu1)
586 );
587
588 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
590
591 assert_eq!(
595 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
596 .unwrap(),
597 new_mtu1
598 );
599 assert_eq!(
600 get_last_updated(
601 &mut core_ctx,
602 fake_config.local_ip.get(),
603 fake_config.remote_ip.get()
604 )
605 .unwrap(),
606 start_time + duration
607 );
608
609 let new_mtu2 = Mtu::new(u32::from(new_mtu1) - 1);
610
611 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
613
614 assert_eq!(
618 PmtuHandler::update_pmtu_if_less(
619 &mut core_ctx,
620 &mut bindings_ctx,
621 fake_config.local_ip.get(),
622 fake_config.remote_ip.get(),
623 new_mtu2,
624 ),
625 Some(new_mtu2)
626 );
627
628 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
630
631 assert_eq!(
635 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
636 .unwrap(),
637 new_mtu2
638 );
639 assert_eq!(
640 get_last_updated(
641 &mut core_ctx,
642 fake_config.local_ip.get(),
643 fake_config.remote_ip.get()
644 )
645 .unwrap(),
646 start_time + (duration * 3)
647 );
648
649 let new_mtu3 = Mtu::new(u32::from(new_mtu2) - 1);
650
651 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
653
654 assert_eq!(
658 PmtuHandler::update_pmtu_if_less(
659 &mut core_ctx,
660 &mut bindings_ctx,
661 fake_config.local_ip.get(),
662 fake_config.remote_ip.get(),
663 new_mtu3,
664 ),
665 Some(new_mtu3)
666 );
667
668 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
670
671 assert_eq!(
675 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
676 .unwrap(),
677 new_mtu3
678 );
679 let last_updated = start_time + (duration * 5);
680 assert_eq!(
681 get_last_updated(
682 &mut core_ctx,
683 fake_config.local_ip.get(),
684 fake_config.remote_ip.get()
685 )
686 .unwrap(),
687 last_updated
688 );
689
690 let new_mtu4 = Mtu::new(u32::from(new_mtu3) + 50);
691
692 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
694
695 assert_eq!(
697 PmtuHandler::update_pmtu_if_less(
698 &mut core_ctx,
699 &mut bindings_ctx,
700 fake_config.local_ip.get(),
701 fake_config.remote_ip.get(),
702 new_mtu4,
703 ),
704 Some(new_mtu3)
705 );
706
707 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
709
710 assert_eq!(
713 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
714 .unwrap(),
715 new_mtu3
716 );
717 assert_eq!(
718 get_last_updated(
719 &mut core_ctx,
720 fake_config.local_ip.get(),
721 fake_config.remote_ip.get()
722 )
723 .unwrap(),
724 last_updated
725 );
726
727 let low_mtu = Mtu::new(u32::from(I::MINIMUM_LINK_MTU) - 1);
728
729 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
731
732 assert_eq!(
734 PmtuHandler::update_pmtu_if_less(
735 &mut core_ctx,
736 &mut bindings_ctx,
737 fake_config.local_ip.get(),
738 fake_config.remote_ip.get(),
739 low_mtu,
740 ),
741 Some(new_mtu3)
742 );
743
744 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
746
747 assert_eq!(
750 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
751 .unwrap(),
752 new_mtu3
753 );
754 assert_eq!(
755 get_last_updated(
756 &mut core_ctx,
757 fake_config.local_ip.get(),
758 fake_config.remote_ip.get()
759 )
760 .unwrap(),
761 last_updated
762 );
763 }
764
765 #[ip_test(I)]
766 fn test_ip_pmtu_task<I: TestIpExt>() {
767 let fake_config = I::TEST_ADDRS;
768 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
769
770 bindings_ctx.timers.assert_no_timers_installed();
772
773 let new_mtu1 = Mtu::new(u32::from(I::MINIMUM_LINK_MTU) + 50);
774 let start_time = bindings_ctx.now();
775 let duration = Duration::from_secs(1);
776
777 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
779
780 assert_eq!(
784 PmtuHandler::update_pmtu_if_less(
785 &mut core_ctx,
786 &mut bindings_ctx,
787 fake_config.local_ip.get(),
788 fake_config.remote_ip.get(),
789 new_mtu1,
790 ),
791 Some(new_mtu1)
792 );
793
794 bindings_ctx.timers.assert_timers_installed([(
796 PmtuTimerId::default(),
797 FakeInstant::from(MAINTENANCE_PERIOD + Duration::from_secs(1)),
798 )]);
799
800 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
802
803 assert_eq!(
807 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
808 .unwrap(),
809 new_mtu1
810 );
811 assert_eq!(
812 get_last_updated(
813 &mut core_ctx,
814 fake_config.local_ip.get(),
815 fake_config.remote_ip.get()
816 )
817 .unwrap(),
818 start_time + duration
819 );
820
821 assert_empty(bindings_ctx.trigger_timers_for(duration * 1798, &mut core_ctx));
823
824 let other_ip = get_other_ip_address::<I>();
828 let new_mtu2 = Mtu::new(u32::from(I::MINIMUM_LINK_MTU) + 100);
829 assert_eq!(
830 PmtuHandler::update_pmtu_if_less(
831 &mut core_ctx,
832 &mut bindings_ctx,
833 fake_config.local_ip.get(),
834 other_ip.get(),
835 new_mtu2,
836 ),
837 Some(new_mtu2)
838 );
839
840 bindings_ctx.timers.assert_timers_installed([(
843 PmtuTimerId::default(),
844 FakeInstant::from(MAINTENANCE_PERIOD + Duration::from_secs(1)),
845 )]);
846
847 assert_eq!(
851 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
852 new_mtu2
853 );
854 assert_eq!(
855 get_last_updated(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
856 start_time + (duration * 1800)
857 );
858 assert_eq!(
860 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
861 .unwrap(),
862 new_mtu1
863 );
864 assert_eq!(
865 get_last_updated(
866 &mut core_ctx,
867 fake_config.local_ip.get(),
868 fake_config.remote_ip.get()
869 )
870 .unwrap(),
871 start_time + duration
872 );
873
874 bindings_ctx.trigger_timers_for_and_expect(
876 duration * 1801,
877 [PmtuTimerId::default()],
878 &mut core_ctx,
879 );
880 assert_eq!(
883 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
884 .unwrap(),
885 new_mtu1
886 );
887 assert_eq!(
888 get_last_updated(
889 &mut core_ctx,
890 fake_config.local_ip.get(),
891 fake_config.remote_ip.get()
892 )
893 .unwrap(),
894 start_time + duration
895 );
896 assert_eq!(
897 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
898 new_mtu2
899 );
900 assert_eq!(
901 get_last_updated(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
902 start_time + (duration * 1800)
903 );
904 bindings_ctx.timers.assert_timers_installed([(
906 PmtuTimerId::default(),
907 FakeInstant::from(MAINTENANCE_PERIOD * 2 + Duration::from_secs(1)),
908 )]);
909
910 bindings_ctx.trigger_timers_for_and_expect(
912 duration * 7200,
913 [PmtuTimerId::default(), PmtuTimerId::default()],
914 &mut core_ctx,
915 );
916 assert_eq!(
918 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()),
919 None
920 );
921 assert_eq!(
922 get_last_updated(
923 &mut core_ctx,
924 fake_config.local_ip.get(),
925 fake_config.remote_ip.get()
926 ),
927 None
928 );
929 assert_eq!(
930 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
931 new_mtu2
932 );
933 assert_eq!(
934 get_last_updated(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
935 start_time + (duration * 1800)
936 );
937 bindings_ctx.timers.assert_timers_installed([(
939 PmtuTimerId::default(),
940 FakeInstant::from(MAINTENANCE_PERIOD * 4 + Duration::from_secs(1)),
941 )]);
942
943 bindings_ctx.trigger_timers_for_and_expect(
945 duration * 3600,
946 [PmtuTimerId::default()],
947 &mut core_ctx,
948 );
949 assert_eq!(
951 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()),
952 None
953 );
954 assert_eq!(
955 get_last_updated(
956 &mut core_ctx,
957 fake_config.local_ip.get(),
958 fake_config.remote_ip.get()
959 ),
960 None
961 );
962 assert_eq!(get_pmtu(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()), None);
963 assert_eq!(
964 get_last_updated(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()),
965 None
966 );
967 bindings_ctx.timers.assert_no_timers_installed();
969 }
970
971 #[ip_test(I)]
972 fn discard_lru<I: TestIpExt>() {
973 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
974
975 assert!(MAX_ENTRIES <= usize::from(u8::MAX) + 1);
981 for i in 0..MAX_ENTRIES {
982 let i = u8::try_from(i).unwrap();
983 assert_eq!(
984 PmtuHandler::update_pmtu_if_less(
985 &mut core_ctx,
986 &mut bindings_ctx,
987 *I::TEST_ADDRS.local_ip,
988 *I::get_other_ip_address(i),
989 Mtu::max(),
990 ),
991 Some(Mtu::max())
992 );
993 }
994 assert_eq!(core_ctx.state.cache.cache.len(), MAX_ENTRIES);
995
996 assert_eq!(
998 PmtuHandler::update_pmtu_if_less(
999 &mut core_ctx,
1000 &mut bindings_ctx,
1001 *I::TEST_ADDRS.remote_ip,
1002 *I::TEST_ADDRS.local_ip,
1003 Mtu::max(),
1004 ),
1005 Some(Mtu::max())
1006 );
1007 assert_eq!(core_ctx.state.cache.cache.len(), MAX_ENTRIES);
1008 assert_eq!(
1009 core_ctx.state.cache.get_pmtu(*I::TEST_ADDRS.local_ip, *I::get_other_ip_address(0)),
1010 None
1011 );
1012 assert_eq!(
1013 core_ctx.state.cache.get_pmtu(*I::TEST_ADDRS.remote_ip, *I::TEST_ADDRS.local_ip),
1014 Some(Mtu::max())
1015 );
1016 }
1017}