1use core::num::NonZeroUsize;
9use core::time::Duration;
10
11use log::trace;
12use lru::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: NonZeroUsize = NonZeroUsize::new(256).unwrap();
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.put(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 self.cache
366 .retain(|_k, v| now.saturating_duration_since(v.last_updated) < PMTU_STALE_TIMEOUT);
367 }
368
369 fn is_empty(&self) -> bool {
370 self.cache.is_empty()
371 }
372}
373
374fn next_lower_pmtu_plateau(start_mtu: Mtu) -> Option<Mtu> {
376 const PMTU_PLATEAUS: [Mtu; 12] = [
394 Mtu::new(65535),
395 Mtu::new(32000),
396 Mtu::new(17914),
397 Mtu::new(8166),
398 Mtu::new(4352),
399 Mtu::new(2002),
400 Mtu::new(1492),
401 Mtu::new(1280),
402 Mtu::new(1006),
403 Mtu::new(508),
404 Mtu::new(296),
405 Mtu::new(68),
406 ];
407
408 for i in 0..PMTU_PLATEAUS.len() {
409 let pmtu = PMTU_PLATEAUS[i];
410
411 if pmtu < start_mtu {
412 return Some(pmtu);
415 }
416 }
417
418 None
419}
420
421#[cfg(test)]
422#[macro_use]
423pub(crate) mod testutil {
424 macro_rules! impl_pmtu_handler {
426 ($ty:ty, $ctx:ty, $ip_version:ident) => {
427 impl PmtuHandler<net_types::ip::$ip_version, $ctx> for $ty {
428 fn update_pmtu_if_less(
429 &mut self,
430 _ctx: &mut $ctx,
431 _src_ip: <net_types::ip::$ip_version as net_types::ip::Ip>::Addr,
432 _dst_ip: <net_types::ip::$ip_version as net_types::ip::Ip>::Addr,
433 _new_mtu: Mtu,
434 ) -> Option<Mtu> {
435 unimplemented!()
436 }
437
438 fn update_pmtu_next_lower(
439 &mut self,
440 _ctx: &mut $ctx,
441 _src_ip: <net_types::ip::$ip_version as net_types::ip::Ip>::Addr,
442 _dst_ip: <net_types::ip::$ip_version as net_types::ip::Ip>::Addr,
443 _from: Mtu,
444 ) -> Option<Mtu> {
445 unimplemented!()
446 }
447 }
448 };
449 }
450}
451
452#[cfg(test)]
453mod tests {
454 use super::*;
455
456 use ip_test_macro::ip_test;
457 use net_types::{SpecifiedAddr, Witness};
458 use netstack3_base::testutil::{
459 FakeBindingsCtx, FakeCoreCtx, FakeInstant, FakeTimerCtxExt, TestIpExt, assert_empty,
460 };
461 use netstack3_base::{CtxPair, InstantContext, IntoCoreTimerCtx};
462 use test_case::test_case;
463
464 struct FakePmtuContext<I: Ip> {
465 cache: PmtuCache<I, FakeBindingsCtxImpl<I>>,
466 }
467
468 type FakeCtxImpl<I> = CtxPair<FakeCoreCtxImpl<I>, FakeBindingsCtxImpl<I>>;
469 type FakeCoreCtxImpl<I> = FakeCoreCtx<FakePmtuContext<I>, (), ()>;
470 type FakeBindingsCtxImpl<I> = FakeBindingsCtx<PmtuTimerId<I>, (), (), ()>;
471
472 impl<I: Ip> PmtuContext<I, FakeBindingsCtxImpl<I>> for FakeCoreCtxImpl<I> {
473 fn with_state_mut<O, F: FnOnce(&mut PmtuCache<I, FakeBindingsCtxImpl<I>>) -> O>(
474 &mut self,
475 cb: F,
476 ) -> O {
477 cb(&mut self.state.cache)
478 }
479 }
480
481 fn new_context<I: Ip>() -> FakeCtxImpl<I> {
482 FakeCtxImpl::with_default_bindings_ctx(|bindings_ctx| {
483 FakeCoreCtxImpl::with_state(FakePmtuContext {
484 cache: PmtuCache::new::<IntoCoreTimerCtx>(bindings_ctx),
485 })
486 })
487 }
488
489 fn get_other_ip_address<I: TestIpExt>() -> SpecifiedAddr<I::Addr> {
492 I::get_other_ip_address(3)
493 }
494
495 impl<I: Ip, BT: PmtuBindingsTypes> PmtuCache<I, BT> {
496 fn get_last_updated(&mut self, src_ip: I::Addr, dst_ip: I::Addr) -> Option<BT::Instant> {
501 self.cache.get_mut(&PmtuCacheKey::new(src_ip, dst_ip)).map(|x| x.last_updated.clone())
502 }
503 }
504
505 #[test_case(Mtu::new(65536) => Some(Mtu::new(65535)))]
506 #[test_case(Mtu::new(65535) => Some(Mtu::new(32000)))]
507 #[test_case(Mtu::new(65534) => Some(Mtu::new(32000)))]
508 #[test_case(Mtu::new(32001) => Some(Mtu::new(32000)))]
509 #[test_case(Mtu::new(32000) => Some(Mtu::new(17914)))]
510 #[test_case(Mtu::new(31999) => Some(Mtu::new(17914)))]
511 #[test_case(Mtu::new(1281) => Some(Mtu::new(1280)))]
512 #[test_case(Mtu::new(1280) => Some(Mtu::new(1006)))]
513 #[test_case(Mtu::new(69) => Some(Mtu::new(68)))]
514 #[test_case(Mtu::new(68) => None)]
515 #[test_case(Mtu::new(67) => None)]
516 #[test_case(Mtu::new(0) => None)]
517 fn test_next_lower_pmtu_plateau(start: Mtu) -> Option<Mtu> {
518 next_lower_pmtu_plateau(start)
519 }
520
521 fn get_pmtu<I: Ip>(
522 core_ctx: &mut FakeCoreCtxImpl<I>,
523 src_ip: I::Addr,
524 dst_ip: I::Addr,
525 ) -> Option<Mtu> {
526 core_ctx.state.cache.get_pmtu(src_ip, dst_ip)
527 }
528
529 fn get_last_updated<I: Ip>(
530 core_ctx: &mut FakeCoreCtxImpl<I>,
531 src_ip: I::Addr,
532 dst_ip: I::Addr,
533 ) -> Option<FakeInstant> {
534 core_ctx.state.cache.get_last_updated(src_ip, dst_ip)
535 }
536
537 #[ip_test(I)]
538 fn test_ip_path_mtu_cache_ctx<I: TestIpExt>() {
539 let fake_config = I::TEST_ADDRS;
540 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
541
542 assert_eq!(
544 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()),
545 None
546 );
547 assert_eq!(
548 get_last_updated(
549 &mut core_ctx,
550 fake_config.local_ip.get(),
551 fake_config.remote_ip.get()
552 ),
553 None
554 );
555
556 let new_mtu1 = Mtu::new(u32::from(I::MINIMUM_LINK_MTU) + 50);
557 let start_time = bindings_ctx.now();
558 let duration = Duration::from_secs(1);
559
560 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
562
563 assert_eq!(
567 PmtuHandler::update_pmtu_if_less(
568 &mut core_ctx,
569 &mut bindings_ctx,
570 fake_config.local_ip.get(),
571 fake_config.remote_ip.get(),
572 new_mtu1,
573 ),
574 Some(new_mtu1)
575 );
576
577 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
579
580 assert_eq!(
584 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
585 .unwrap(),
586 new_mtu1
587 );
588 assert_eq!(
589 get_last_updated(
590 &mut core_ctx,
591 fake_config.local_ip.get(),
592 fake_config.remote_ip.get()
593 )
594 .unwrap(),
595 start_time + duration
596 );
597
598 let new_mtu2 = Mtu::new(u32::from(new_mtu1) - 1);
599
600 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
602
603 assert_eq!(
607 PmtuHandler::update_pmtu_if_less(
608 &mut core_ctx,
609 &mut bindings_ctx,
610 fake_config.local_ip.get(),
611 fake_config.remote_ip.get(),
612 new_mtu2,
613 ),
614 Some(new_mtu2)
615 );
616
617 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
619
620 assert_eq!(
624 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
625 .unwrap(),
626 new_mtu2
627 );
628 assert_eq!(
629 get_last_updated(
630 &mut core_ctx,
631 fake_config.local_ip.get(),
632 fake_config.remote_ip.get()
633 )
634 .unwrap(),
635 start_time + (duration * 3)
636 );
637
638 let new_mtu3 = Mtu::new(u32::from(new_mtu2) - 1);
639
640 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
642
643 assert_eq!(
647 PmtuHandler::update_pmtu_if_less(
648 &mut core_ctx,
649 &mut bindings_ctx,
650 fake_config.local_ip.get(),
651 fake_config.remote_ip.get(),
652 new_mtu3,
653 ),
654 Some(new_mtu3)
655 );
656
657 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
659
660 assert_eq!(
664 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
665 .unwrap(),
666 new_mtu3
667 );
668 let last_updated = start_time + (duration * 5);
669 assert_eq!(
670 get_last_updated(
671 &mut core_ctx,
672 fake_config.local_ip.get(),
673 fake_config.remote_ip.get()
674 )
675 .unwrap(),
676 last_updated
677 );
678
679 let new_mtu4 = Mtu::new(u32::from(new_mtu3) + 50);
680
681 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
683
684 assert_eq!(
686 PmtuHandler::update_pmtu_if_less(
687 &mut core_ctx,
688 &mut bindings_ctx,
689 fake_config.local_ip.get(),
690 fake_config.remote_ip.get(),
691 new_mtu4,
692 ),
693 Some(new_mtu3)
694 );
695
696 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
698
699 assert_eq!(
702 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
703 .unwrap(),
704 new_mtu3
705 );
706 assert_eq!(
707 get_last_updated(
708 &mut core_ctx,
709 fake_config.local_ip.get(),
710 fake_config.remote_ip.get()
711 )
712 .unwrap(),
713 last_updated
714 );
715
716 let low_mtu = Mtu::new(u32::from(I::MINIMUM_LINK_MTU) - 1);
717
718 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
720
721 assert_eq!(
723 PmtuHandler::update_pmtu_if_less(
724 &mut core_ctx,
725 &mut bindings_ctx,
726 fake_config.local_ip.get(),
727 fake_config.remote_ip.get(),
728 low_mtu,
729 ),
730 Some(new_mtu3)
731 );
732
733 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
735
736 assert_eq!(
739 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
740 .unwrap(),
741 new_mtu3
742 );
743 assert_eq!(
744 get_last_updated(
745 &mut core_ctx,
746 fake_config.local_ip.get(),
747 fake_config.remote_ip.get()
748 )
749 .unwrap(),
750 last_updated
751 );
752 }
753
754 #[ip_test(I)]
755 fn test_ip_pmtu_task<I: TestIpExt>() {
756 let fake_config = I::TEST_ADDRS;
757 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
758
759 bindings_ctx.timers.assert_no_timers_installed();
761
762 let new_mtu1 = Mtu::new(u32::from(I::MINIMUM_LINK_MTU) + 50);
763 let start_time = bindings_ctx.now();
764 let duration = Duration::from_secs(1);
765
766 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
768
769 assert_eq!(
773 PmtuHandler::update_pmtu_if_less(
774 &mut core_ctx,
775 &mut bindings_ctx,
776 fake_config.local_ip.get(),
777 fake_config.remote_ip.get(),
778 new_mtu1,
779 ),
780 Some(new_mtu1)
781 );
782
783 bindings_ctx.timers.assert_timers_installed([(
785 PmtuTimerId::default(),
786 FakeInstant::from(MAINTENANCE_PERIOD + Duration::from_secs(1)),
787 )]);
788
789 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
791
792 assert_eq!(
796 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
797 .unwrap(),
798 new_mtu1
799 );
800 assert_eq!(
801 get_last_updated(
802 &mut core_ctx,
803 fake_config.local_ip.get(),
804 fake_config.remote_ip.get()
805 )
806 .unwrap(),
807 start_time + duration
808 );
809
810 assert_empty(bindings_ctx.trigger_timers_for(duration * 1798, &mut core_ctx));
812
813 let other_ip = get_other_ip_address::<I>();
817 let new_mtu2 = Mtu::new(u32::from(I::MINIMUM_LINK_MTU) + 100);
818 assert_eq!(
819 PmtuHandler::update_pmtu_if_less(
820 &mut core_ctx,
821 &mut bindings_ctx,
822 fake_config.local_ip.get(),
823 other_ip.get(),
824 new_mtu2,
825 ),
826 Some(new_mtu2)
827 );
828
829 bindings_ctx.timers.assert_timers_installed([(
832 PmtuTimerId::default(),
833 FakeInstant::from(MAINTENANCE_PERIOD + Duration::from_secs(1)),
834 )]);
835
836 assert_eq!(
840 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
841 new_mtu2
842 );
843 assert_eq!(
844 get_last_updated(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
845 start_time + (duration * 1800)
846 );
847 assert_eq!(
849 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
850 .unwrap(),
851 new_mtu1
852 );
853 assert_eq!(
854 get_last_updated(
855 &mut core_ctx,
856 fake_config.local_ip.get(),
857 fake_config.remote_ip.get()
858 )
859 .unwrap(),
860 start_time + duration
861 );
862
863 bindings_ctx.trigger_timers_for_and_expect(
865 duration * 1801,
866 [PmtuTimerId::default()],
867 &mut core_ctx,
868 );
869 assert_eq!(
872 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
873 .unwrap(),
874 new_mtu1
875 );
876 assert_eq!(
877 get_last_updated(
878 &mut core_ctx,
879 fake_config.local_ip.get(),
880 fake_config.remote_ip.get()
881 )
882 .unwrap(),
883 start_time + duration
884 );
885 assert_eq!(
886 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
887 new_mtu2
888 );
889 assert_eq!(
890 get_last_updated(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
891 start_time + (duration * 1800)
892 );
893 bindings_ctx.timers.assert_timers_installed([(
895 PmtuTimerId::default(),
896 FakeInstant::from(MAINTENANCE_PERIOD * 2 + Duration::from_secs(1)),
897 )]);
898
899 bindings_ctx.trigger_timers_for_and_expect(
901 duration * 7200,
902 [PmtuTimerId::default(), PmtuTimerId::default()],
903 &mut core_ctx,
904 );
905 assert_eq!(
907 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()),
908 None
909 );
910 assert_eq!(
911 get_last_updated(
912 &mut core_ctx,
913 fake_config.local_ip.get(),
914 fake_config.remote_ip.get()
915 ),
916 None
917 );
918 assert_eq!(
919 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
920 new_mtu2
921 );
922 assert_eq!(
923 get_last_updated(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
924 start_time + (duration * 1800)
925 );
926 bindings_ctx.timers.assert_timers_installed([(
928 PmtuTimerId::default(),
929 FakeInstant::from(MAINTENANCE_PERIOD * 4 + Duration::from_secs(1)),
930 )]);
931
932 bindings_ctx.trigger_timers_for_and_expect(
934 duration * 3600,
935 [PmtuTimerId::default()],
936 &mut core_ctx,
937 );
938 assert_eq!(
940 get_pmtu(&mut core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()),
941 None
942 );
943 assert_eq!(
944 get_last_updated(
945 &mut core_ctx,
946 fake_config.local_ip.get(),
947 fake_config.remote_ip.get()
948 ),
949 None
950 );
951 assert_eq!(get_pmtu(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()), None);
952 assert_eq!(
953 get_last_updated(&mut core_ctx, fake_config.local_ip.get(), other_ip.get()),
954 None
955 );
956 bindings_ctx.timers.assert_no_timers_installed();
958 }
959
960 #[ip_test(I)]
961 fn discard_lru<I: TestIpExt>() {
962 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
963
964 assert!(MAX_ENTRIES.get() <= usize::from(u8::MAX) + 1);
970 for i in 0..MAX_ENTRIES.get() {
971 let i = u8::try_from(i).unwrap();
972 assert_eq!(
973 PmtuHandler::update_pmtu_if_less(
974 &mut core_ctx,
975 &mut bindings_ctx,
976 *I::TEST_ADDRS.local_ip,
977 *I::get_other_ip_address(i),
978 Mtu::max(),
979 ),
980 Some(Mtu::max())
981 );
982 }
983 assert_eq!(core_ctx.state.cache.cache.len(), MAX_ENTRIES.get());
984
985 assert_eq!(
987 PmtuHandler::update_pmtu_if_less(
988 &mut core_ctx,
989 &mut bindings_ctx,
990 *I::TEST_ADDRS.remote_ip,
991 *I::TEST_ADDRS.local_ip,
992 Mtu::max(),
993 ),
994 Some(Mtu::max())
995 );
996 assert_eq!(core_ctx.state.cache.cache.len(), MAX_ENTRIES.get());
997 assert_eq!(
998 core_ctx.state.cache.get_pmtu(*I::TEST_ADDRS.local_ip, *I::get_other_ip_address(0)),
999 None
1000 );
1001 assert_eq!(
1002 core_ctx.state.cache.get_pmtu(*I::TEST_ADDRS.remote_ip, *I::TEST_ADDRS.local_ip),
1003 Some(Mtu::max())
1004 );
1005 }
1006}