1use alloc::collections::HashMap;
9use core::time::Duration;
10
11use log::trace;
12use net_types::ip::{GenericOverIp, Ip, IpAddress, IpVersionMarker, Mtu};
13use netstack3_base::{
14 CoreTimerContext, HandleableTimer, Instant, InstantBindingsTypes, TimerBindingsTypes,
15 TimerContext,
16};
17
18const MAINTENANCE_PERIOD: Duration = Duration::from_secs(3600);
26
27const PMTU_STALE_TIMEOUT: Duration = Duration::from_secs(10800);
32
33#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash, GenericOverIp)]
35#[generic_over_ip(I, Ip)]
36pub struct PmtuTimerId<I: Ip>(IpVersionMarker<I>);
37
38pub trait PmtuContext<I: Ip, BT: PmtuBindingsTypes> {
40 fn with_state_mut<O, F: FnOnce(&mut PmtuCache<I, BT>) -> O>(&mut self, cb: F) -> O;
42}
43
44pub trait PmtuBindingsTypes: TimerBindingsTypes + InstantBindingsTypes {}
46impl<BT> PmtuBindingsTypes for BT where BT: TimerBindingsTypes + InstantBindingsTypes {}
47
48trait PmtuBindingsContext: PmtuBindingsTypes + TimerContext {}
50impl<BC> PmtuBindingsContext for BC where BC: PmtuBindingsTypes + TimerContext {}
51
52pub(crate) trait PmtuHandler<I: Ip, BC> {
59 fn update_pmtu_if_less(
63 &mut self,
64 bindings_ctx: &mut BC,
65 src_ip: I::Addr,
66 dst_ip: I::Addr,
67 new_mtu: Mtu,
68 );
69
70 fn update_pmtu_next_lower(
73 &mut self,
74 bindings_ctx: &mut BC,
75 src_ip: I::Addr,
76 dst_ip: I::Addr,
77 from: Mtu,
78 );
79}
80
81fn maybe_schedule_timer<BC: PmtuBindingsContext>(
82 bindings_ctx: &mut BC,
83 timer: &mut BC::Timer,
84 cache_is_empty: bool,
85) {
86 if cache_is_empty {
91 return;
92 }
93
94 match bindings_ctx.scheduled_instant(timer) {
95 Some(scheduled_at) => {
96 let _: BC::Instant = scheduled_at;
97 }
99 None => {
100 assert_eq!(bindings_ctx.schedule_timer(MAINTENANCE_PERIOD, timer), None)
102 }
103 }
104}
105
106fn handle_update_result<BC: PmtuBindingsContext>(
107 bindings_ctx: &mut BC,
108 timer: &mut BC::Timer,
109 result: Result<Option<Mtu>, Option<Mtu>>,
110 cache_is_empty: bool,
111) {
112 let _: Result<_, _> = result.map(|ret| {
114 maybe_schedule_timer(bindings_ctx, timer, cache_is_empty);
115 ret
116 });
117}
118
119impl<I: Ip, BC: PmtuBindingsContext, CC: PmtuContext<I, BC>> PmtuHandler<I, BC> for CC {
120 fn update_pmtu_if_less(
121 &mut self,
122 bindings_ctx: &mut BC,
123 src_ip: I::Addr,
124 dst_ip: I::Addr,
125 new_mtu: Mtu,
126 ) {
127 self.with_state_mut(|cache| {
128 let now = bindings_ctx.now();
129 let res = cache.update_pmtu_if_less(src_ip, dst_ip, new_mtu, now);
130 let is_empty = cache.is_empty();
131 handle_update_result(bindings_ctx, &mut cache.timer, res, is_empty);
132 })
133 }
134
135 fn update_pmtu_next_lower(
136 &mut self,
137 bindings_ctx: &mut BC,
138 src_ip: I::Addr,
139 dst_ip: I::Addr,
140 from: Mtu,
141 ) {
142 self.with_state_mut(|cache| {
143 let now = bindings_ctx.now();
144 let res = cache.update_pmtu_next_lower(src_ip, dst_ip, from, now);
145 let is_empty = cache.is_empty();
146 handle_update_result(bindings_ctx, &mut cache.timer, res, is_empty);
147 })
148 }
149}
150
151impl<I: Ip, BC: PmtuBindingsContext, CC: PmtuContext<I, BC>> HandleableTimer<CC, BC>
152 for PmtuTimerId<I>
153{
154 fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, _: BC::UniqueTimerId) {
155 let Self(IpVersionMarker { .. }) = self;
156 core_ctx.with_state_mut(|cache| {
157 let now = bindings_ctx.now();
158 cache.handle_timer(now);
159 let is_empty = cache.is_empty();
160 maybe_schedule_timer(bindings_ctx, &mut cache.timer, is_empty);
161 })
162 }
163}
164
165#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
171pub(crate) struct PmtuCacheKey<A: IpAddress>(A, A);
172
173impl<A: IpAddress> PmtuCacheKey<A> {
174 fn new(src_ip: A, dst_ip: A) -> Self {
175 Self(src_ip, dst_ip)
176 }
177}
178
179#[derive(Debug, PartialEq)]
181pub(crate) struct PmtuCacheData<I> {
182 pmtu: Mtu,
183 last_updated: I,
184}
185
186impl<I: Instant> PmtuCacheData<I> {
187 fn new(pmtu: Mtu, now: I) -> Self {
191 Self { pmtu, last_updated: now }
192 }
193}
194
195pub struct PmtuCache<I: Ip, BT: PmtuBindingsTypes> {
197 cache: HashMap<PmtuCacheKey<I::Addr>, PmtuCacheData<BT::Instant>>,
198 timer: BT::Timer,
199}
200
201impl<I: Ip, BC: PmtuBindingsTypes + TimerContext> PmtuCache<I, BC> {
202 pub(crate) fn new<CC: CoreTimerContext<PmtuTimerId<I>, BC>>(bindings_ctx: &mut BC) -> Self {
203 Self {
204 cache: Default::default(),
205 timer: CC::new_timer(bindings_ctx, PmtuTimerId::default()),
206 }
207 }
208}
209
210impl<I: Ip, BT: PmtuBindingsTypes> PmtuCache<I, BT> {
211 pub fn get_pmtu(&self, src_ip: I::Addr, dst_ip: I::Addr) -> Option<Mtu> {
213 self.cache.get(&PmtuCacheKey::new(src_ip, dst_ip)).map(|x| x.pmtu)
214 }
215
216 fn update_pmtu_if_less(
220 &mut self,
221 src_ip: I::Addr,
222 dst_ip: I::Addr,
223 new_mtu: Mtu,
224 now: BT::Instant,
225 ) -> Result<Option<Mtu>, Option<Mtu>> {
226 match self.get_pmtu(src_ip, dst_ip) {
227 None => self.update_pmtu(src_ip, dst_ip, new_mtu, now),
229 Some(prev_mtu) if new_mtu < prev_mtu => self.update_pmtu(src_ip, dst_ip, new_mtu, now),
231 Some(prev_mtu) => {
234 trace!("update_pmtu_if_less: Not updating the PMTU between src {} and dest {} to {:?}; is {:?}", src_ip, dst_ip, new_mtu, prev_mtu);
235 Ok(Some(prev_mtu))
236 }
237 }
238 }
239
240 fn update_pmtu_next_lower(
248 &mut self,
249 src_ip: I::Addr,
250 dst_ip: I::Addr,
251 from: Mtu,
252 now: BT::Instant,
253 ) -> Result<Option<Mtu>, Option<Mtu>> {
254 if let Some(next_pmtu) = next_lower_pmtu_plateau(from) {
255 trace!(
256 "update_pmtu_next_lower: Attempting to update PMTU between src {} and dest {} to {:?}",
257 src_ip,
258 dst_ip,
259 next_pmtu
260 );
261
262 self.update_pmtu_if_less(src_ip, dst_ip, next_pmtu, now)
263 } else {
264 trace!("update_pmtu_next_lower: Not updating PMTU between src {} and dest {} as there is no lower PMTU value from {:?}", src_ip, dst_ip, from);
267 Err(self.get_pmtu(src_ip, dst_ip))
268 }
269 }
270
271 fn update_pmtu(
280 &mut self,
281 src_ip: I::Addr,
282 dst_ip: I::Addr,
283 new_mtu: Mtu,
284 now: BT::Instant,
285 ) -> Result<Option<Mtu>, Option<Mtu>> {
286 if new_mtu < I::MINIMUM_LINK_MTU {
288 return Err(self.get_pmtu(src_ip, dst_ip));
289 }
290
291 Ok(self
292 .cache
293 .insert(PmtuCacheKey::new(src_ip, dst_ip), PmtuCacheData::new(new_mtu, now))
294 .map(|PmtuCacheData { pmtu, last_updated: _ }| pmtu))
295 }
296
297 fn handle_timer(&mut self, now: BT::Instant) {
298 assert!(!self.cache.is_empty());
300
301 self.cache.retain(|_k, v| {
307 now.saturating_duration_since(v.last_updated) < PMTU_STALE_TIMEOUT
318 });
319 }
320
321 fn is_empty(&self) -> bool {
322 self.cache.is_empty()
323 }
324}
325
326fn next_lower_pmtu_plateau(start_mtu: Mtu) -> Option<Mtu> {
328 const PMTU_PLATEAUS: [Mtu; 12] = [
346 Mtu::new(65535),
347 Mtu::new(32000),
348 Mtu::new(17914),
349 Mtu::new(8166),
350 Mtu::new(4352),
351 Mtu::new(2002),
352 Mtu::new(1492),
353 Mtu::new(1280),
354 Mtu::new(1006),
355 Mtu::new(508),
356 Mtu::new(296),
357 Mtu::new(68),
358 ];
359
360 for i in 0..PMTU_PLATEAUS.len() {
361 let pmtu = PMTU_PLATEAUS[i];
362
363 if pmtu < start_mtu {
364 return Some(pmtu);
367 }
368 }
369
370 None
371}
372
373#[cfg(test)]
374#[macro_use]
375pub(crate) mod testutil {
376 macro_rules! impl_pmtu_handler {
378 ($ty:ty, $ctx:ty, $ip_version:ident) => {
379 impl PmtuHandler<net_types::ip::$ip_version, $ctx> for $ty {
380 fn update_pmtu_if_less(
381 &mut self,
382 _ctx: &mut $ctx,
383 _src_ip: <net_types::ip::$ip_version as net_types::ip::Ip>::Addr,
384 _dst_ip: <net_types::ip::$ip_version as net_types::ip::Ip>::Addr,
385 _new_mtu: Mtu,
386 ) {
387 unimplemented!()
388 }
389
390 fn update_pmtu_next_lower(
391 &mut self,
392 _ctx: &mut $ctx,
393 _src_ip: <net_types::ip::$ip_version as net_types::ip::Ip>::Addr,
394 _dst_ip: <net_types::ip::$ip_version as net_types::ip::Ip>::Addr,
395 _from: Mtu,
396 ) {
397 unimplemented!()
398 }
399 }
400 };
401 }
402}
403
404#[cfg(test)]
405mod tests {
406 use super::*;
407
408 use ip_test_macro::ip_test;
409 use net_types::{SpecifiedAddr, Witness};
410 use netstack3_base::testutil::{
411 assert_empty, FakeBindingsCtx, FakeCoreCtx, FakeInstant, FakeTimerCtxExt, TestIpExt,
412 };
413 use netstack3_base::{CtxPair, InstantContext, IntoCoreTimerCtx};
414 use test_case::test_case;
415
416 struct FakePmtuContext<I: Ip> {
417 cache: PmtuCache<I, FakeBindingsCtxImpl<I>>,
418 }
419
420 type FakeCtxImpl<I> = CtxPair<FakeCoreCtxImpl<I>, FakeBindingsCtxImpl<I>>;
421 type FakeCoreCtxImpl<I> = FakeCoreCtx<FakePmtuContext<I>, (), ()>;
422 type FakeBindingsCtxImpl<I> = FakeBindingsCtx<PmtuTimerId<I>, (), (), ()>;
423
424 impl<I: Ip> PmtuContext<I, FakeBindingsCtxImpl<I>> for FakeCoreCtxImpl<I> {
425 fn with_state_mut<O, F: FnOnce(&mut PmtuCache<I, FakeBindingsCtxImpl<I>>) -> O>(
426 &mut self,
427 cb: F,
428 ) -> O {
429 cb(&mut self.state.cache)
430 }
431 }
432
433 fn new_context<I: Ip>() -> FakeCtxImpl<I> {
434 FakeCtxImpl::with_default_bindings_ctx(|bindings_ctx| {
435 FakeCoreCtxImpl::with_state(FakePmtuContext {
436 cache: PmtuCache::new::<IntoCoreTimerCtx>(bindings_ctx),
437 })
438 })
439 }
440
441 fn get_other_ip_address<I: TestIpExt>() -> SpecifiedAddr<I::Addr> {
444 I::get_other_ip_address(3)
445 }
446
447 impl<I: Ip, BT: PmtuBindingsTypes> PmtuCache<I, BT> {
448 fn get_last_updated(&self, src_ip: I::Addr, dst_ip: I::Addr) -> Option<BT::Instant> {
453 self.cache.get(&PmtuCacheKey::new(src_ip, dst_ip)).map(|x| x.last_updated.clone())
454 }
455 }
456
457 #[test_case(Mtu::new(65536) => Some(Mtu::new(65535)))]
458 #[test_case(Mtu::new(65535) => Some(Mtu::new(32000)))]
459 #[test_case(Mtu::new(65534) => Some(Mtu::new(32000)))]
460 #[test_case(Mtu::new(32001) => Some(Mtu::new(32000)))]
461 #[test_case(Mtu::new(32000) => Some(Mtu::new(17914)))]
462 #[test_case(Mtu::new(31999) => Some(Mtu::new(17914)))]
463 #[test_case(Mtu::new(1281) => Some(Mtu::new(1280)))]
464 #[test_case(Mtu::new(1280) => Some(Mtu::new(1006)))]
465 #[test_case(Mtu::new(69) => Some(Mtu::new(68)))]
466 #[test_case(Mtu::new(68) => None)]
467 #[test_case(Mtu::new(67) => None)]
468 #[test_case(Mtu::new(0) => None)]
469 fn test_next_lower_pmtu_plateau(start: Mtu) -> Option<Mtu> {
470 next_lower_pmtu_plateau(start)
471 }
472
473 fn get_pmtu<I: Ip>(
474 core_ctx: &FakeCoreCtxImpl<I>,
475 src_ip: I::Addr,
476 dst_ip: I::Addr,
477 ) -> Option<Mtu> {
478 core_ctx.state.cache.get_pmtu(src_ip, dst_ip)
479 }
480
481 fn get_last_updated<I: Ip>(
482 core_ctx: &FakeCoreCtxImpl<I>,
483 src_ip: I::Addr,
484 dst_ip: I::Addr,
485 ) -> Option<FakeInstant> {
486 core_ctx.state.cache.get_last_updated(src_ip, dst_ip)
487 }
488
489 #[ip_test(I)]
490 fn test_ip_path_mtu_cache_ctx<I: TestIpExt>() {
491 let fake_config = I::TEST_ADDRS;
492 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
493
494 assert_eq!(
496 get_pmtu(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()),
497 None
498 );
499 assert_eq!(
500 get_last_updated(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()),
501 None
502 );
503
504 let new_mtu1 = Mtu::new(u32::from(I::MINIMUM_LINK_MTU) + 50);
505 let start_time = bindings_ctx.now();
506 let duration = Duration::from_secs(1);
507
508 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
510
511 PmtuHandler::update_pmtu_if_less(
515 &mut core_ctx,
516 &mut bindings_ctx,
517 fake_config.local_ip.get(),
518 fake_config.remote_ip.get(),
519 new_mtu1,
520 );
521
522 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
524
525 assert_eq!(
529 get_pmtu(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()).unwrap(),
530 new_mtu1
531 );
532 assert_eq!(
533 get_last_updated(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
534 .unwrap(),
535 start_time + duration
536 );
537
538 let new_mtu2 = Mtu::new(u32::from(new_mtu1) - 1);
539
540 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
542
543 PmtuHandler::update_pmtu_if_less(
547 &mut core_ctx,
548 &mut bindings_ctx,
549 fake_config.local_ip.get(),
550 fake_config.remote_ip.get(),
551 new_mtu2,
552 );
553
554 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
556
557 assert_eq!(
561 get_pmtu(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()).unwrap(),
562 new_mtu2
563 );
564 assert_eq!(
565 get_last_updated(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
566 .unwrap(),
567 start_time + (duration * 3)
568 );
569
570 let new_mtu3 = Mtu::new(u32::from(new_mtu2) - 1);
571
572 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
574
575 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_mtu3,
584 );
585
586 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
588
589 assert_eq!(
593 get_pmtu(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()).unwrap(),
594 new_mtu3
595 );
596 let last_updated = start_time + (duration * 5);
597 assert_eq!(
598 get_last_updated(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
599 .unwrap(),
600 last_updated
601 );
602
603 let new_mtu4 = Mtu::new(u32::from(new_mtu3) + 50);
604
605 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
607
608 PmtuHandler::update_pmtu_if_less(
610 &mut core_ctx,
611 &mut bindings_ctx,
612 fake_config.local_ip.get(),
613 fake_config.remote_ip.get(),
614 new_mtu4,
615 );
616
617 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
619
620 assert_eq!(
623 get_pmtu(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()).unwrap(),
624 new_mtu3
625 );
626 assert_eq!(
627 get_last_updated(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
628 .unwrap(),
629 last_updated
630 );
631
632 let low_mtu = Mtu::new(u32::from(I::MINIMUM_LINK_MTU) - 1);
633
634 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
636
637 PmtuHandler::update_pmtu_if_less(
639 &mut core_ctx,
640 &mut bindings_ctx,
641 fake_config.local_ip.get(),
642 fake_config.remote_ip.get(),
643 low_mtu,
644 );
645
646 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
648
649 assert_eq!(
652 get_pmtu(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()).unwrap(),
653 new_mtu3
654 );
655 assert_eq!(
656 get_last_updated(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
657 .unwrap(),
658 last_updated
659 );
660 }
661
662 #[ip_test(I)]
663 fn test_ip_pmtu_task<I: TestIpExt>() {
664 let fake_config = I::TEST_ADDRS;
665 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
666
667 bindings_ctx.timers.assert_no_timers_installed();
669
670 let new_mtu1 = Mtu::new(u32::from(I::MINIMUM_LINK_MTU) + 50);
671 let start_time = bindings_ctx.now();
672 let duration = Duration::from_secs(1);
673
674 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
676
677 PmtuHandler::update_pmtu_if_less(
681 &mut core_ctx,
682 &mut bindings_ctx,
683 fake_config.local_ip.get(),
684 fake_config.remote_ip.get(),
685 new_mtu1,
686 );
687
688 bindings_ctx.timers.assert_timers_installed([(
690 PmtuTimerId::default(),
691 FakeInstant::from(MAINTENANCE_PERIOD + Duration::from_secs(1)),
692 )]);
693
694 assert_empty(bindings_ctx.trigger_timers_for(duration, &mut core_ctx));
696
697 assert_eq!(
701 get_pmtu(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()).unwrap(),
702 new_mtu1
703 );
704 assert_eq!(
705 get_last_updated(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
706 .unwrap(),
707 start_time + duration
708 );
709
710 assert_empty(bindings_ctx.trigger_timers_for(duration * 1798, &mut core_ctx));
712
713 let other_ip = get_other_ip_address::<I>();
717 let new_mtu2 = Mtu::new(u32::from(I::MINIMUM_LINK_MTU) + 100);
718 PmtuHandler::update_pmtu_if_less(
719 &mut core_ctx,
720 &mut bindings_ctx,
721 fake_config.local_ip.get(),
722 other_ip.get(),
723 new_mtu2,
724 );
725
726 bindings_ctx.timers.assert_timers_installed([(
729 PmtuTimerId::default(),
730 FakeInstant::from(MAINTENANCE_PERIOD + Duration::from_secs(1)),
731 )]);
732
733 assert_eq!(
737 get_pmtu(&core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
738 new_mtu2
739 );
740 assert_eq!(
741 get_last_updated(&core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
742 start_time + (duration * 1800)
743 );
744 assert_eq!(
746 get_pmtu(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()).unwrap(),
747 new_mtu1
748 );
749 assert_eq!(
750 get_last_updated(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
751 .unwrap(),
752 start_time + duration
753 );
754
755 bindings_ctx.trigger_timers_for_and_expect(
757 duration * 1801,
758 [PmtuTimerId::default()],
759 &mut core_ctx,
760 );
761 assert_eq!(
764 get_pmtu(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()).unwrap(),
765 new_mtu1
766 );
767 assert_eq!(
768 get_last_updated(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get())
769 .unwrap(),
770 start_time + duration
771 );
772 assert_eq!(
773 get_pmtu(&core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
774 new_mtu2
775 );
776 assert_eq!(
777 get_last_updated(&core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
778 start_time + (duration * 1800)
779 );
780 bindings_ctx.timers.assert_timers_installed([(
782 PmtuTimerId::default(),
783 FakeInstant::from(MAINTENANCE_PERIOD * 2 + Duration::from_secs(1)),
784 )]);
785
786 bindings_ctx.trigger_timers_for_and_expect(
788 duration * 7200,
789 [PmtuTimerId::default(), PmtuTimerId::default()],
790 &mut core_ctx,
791 );
792 assert_eq!(
794 get_pmtu(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()),
795 None
796 );
797 assert_eq!(
798 get_last_updated(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()),
799 None
800 );
801 assert_eq!(
802 get_pmtu(&core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
803 new_mtu2
804 );
805 assert_eq!(
806 get_last_updated(&core_ctx, fake_config.local_ip.get(), other_ip.get()).unwrap(),
807 start_time + (duration * 1800)
808 );
809 bindings_ctx.timers.assert_timers_installed([(
811 PmtuTimerId::default(),
812 FakeInstant::from(MAINTENANCE_PERIOD * 4 + Duration::from_secs(1)),
813 )]);
814
815 bindings_ctx.trigger_timers_for_and_expect(
817 duration * 3600,
818 [PmtuTimerId::default()],
819 &mut core_ctx,
820 );
821 assert_eq!(
823 get_pmtu(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()),
824 None
825 );
826 assert_eq!(
827 get_last_updated(&core_ctx, fake_config.local_ip.get(), fake_config.remote_ip.get()),
828 None
829 );
830 assert_eq!(get_pmtu(&core_ctx, fake_config.local_ip.get(), other_ip.get()), None);
831 assert_eq!(get_last_updated(&core_ctx, fake_config.local_ip.get(), other_ip.get()), None);
832 bindings_ctx.timers.assert_no_timers_installed();
834 }
835}