1use alloc::vec::Vec;
12use core::marker::PhantomData;
13use core::num::NonZeroU16;
14use core::ops::ControlFlow;
15use core::time::Duration;
16
17use assert_matches::assert_matches;
18use log::{debug, error, trace, warn};
19use net_types::ip::{AddrSubnet, Ip as _, IpAddress, Ipv6, Ipv6Addr, Subnet};
20use net_types::Witness as _;
21use netstack3_base::{
22 AnyDevice, CoreTimerContext, Counter, CounterContext, DeviceIdContext, DeviceIdentifier,
23 EventContext, ExistsError, HandleableTimer, Instant, InstantBindingsTypes, InstantContext,
24 LocalTimerHeap, NotFoundError, RngContext, TimerBindingsTypes, TimerContext,
25 WeakDeviceIdentifier,
26};
27use packet_formats::icmp::ndp::NonZeroNdpLifetime;
28use packet_formats::utils::NonZeroDuration;
29use rand::distributions::Uniform;
30use rand::Rng;
31
32use crate::device::Ipv6AddrSlaacConfig;
33use crate::internal::device::opaque_iid::{IidSecret, OpaqueIid, OpaqueIidNonce};
34use crate::internal::device::state::{
35 Lifetime, PreferredLifetime, SlaacConfig, TemporarySlaacConfig,
36};
37use crate::internal::device::{
38 AddressRemovedReason, IpDeviceEvent, Ipv6DeviceAddr, Ipv6LinkLayerAddr,
39};
40
41const MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE: NonZeroDuration =
45 NonZeroDuration::new(Duration::from_secs(7200)).unwrap();
46
47const REQUIRED_PREFIX_BITS: u8 = 64;
52
53const MAX_LOCAL_REGEN_ATTEMPTS: u8 = 10;
58
59#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
61#[allow(missing_docs)]
62pub enum InnerSlaacTimerId {
63 DeprecateSlaacAddress { addr: Ipv6DeviceAddr },
65 InvalidateSlaacAddress { addr: Ipv6DeviceAddr },
67 RegenerateTemporaryAddress { addr_subnet: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> },
70}
71
72pub struct SlaacState<BT: SlaacBindingsTypes> {
74 timers: LocalTimerHeap<InnerSlaacTimerId, (), BT>,
75}
76
77impl<BC: SlaacBindingsTypes + TimerContext> SlaacState<BC> {
78 pub fn new<D: WeakDeviceIdentifier, CC: CoreTimerContext<SlaacTimerId<D>, BC>>(
80 bindings_ctx: &mut BC,
81 device_id: D,
82 ) -> Self {
83 Self {
84 timers: LocalTimerHeap::new_with_context::<_, CC>(
85 bindings_ctx,
86 SlaacTimerId { device_id },
87 ),
88 }
89 }
90
91 #[cfg(any(test, feature = "testutils"))]
93 pub fn timers(&self) -> &LocalTimerHeap<InnerSlaacTimerId, (), BC> {
94 &self.timers
95 }
96}
97
98#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
100pub struct SlaacTimerId<D: WeakDeviceIdentifier> {
101 device_id: D,
102}
103
104impl<D: WeakDeviceIdentifier> SlaacTimerId<D> {
105 pub(super) fn device_id(&self) -> &D {
106 let Self { device_id } = self;
107 device_id
108 }
109
110 #[cfg(any(test, feature = "testutils"))]
112 pub fn new(device_id: D) -> Self {
113 Self { device_id }
114 }
115}
116
117#[derive(Copy, Clone, Debug, Eq, PartialEq)]
119pub struct SlaacAddressEntry<Instant> {
120 pub addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
122 pub config: Ipv6AddrSlaacConfig<Instant>,
124}
125
126pub struct SlaacAddressEntryMut<'a, Instant> {
128 pub addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
130 pub config: &'a mut Ipv6AddrSlaacConfig<Instant>,
132}
133
134pub trait SlaacAddresses<BT: SlaacBindingsTypes> {
136 fn for_each_addr_mut<F: FnMut(SlaacAddressEntryMut<'_, BT::Instant>)>(&mut self, cb: F);
139
140 type AddrsIter<'x>: Iterator<Item = SlaacAddressEntry<BT::Instant>>;
142
143 fn with_addrs<O, F: FnOnce(Self::AddrsIter<'_>) -> O>(&mut self, cb: F) -> O;
145
146 fn add_addr_sub_and_then<O, F: FnOnce(SlaacAddressEntryMut<'_, BT::Instant>, &mut BT) -> O>(
149 &mut self,
150 bindings_ctx: &mut BT,
151 addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
152 config: Ipv6AddrSlaacConfig<BT::Instant>,
153 and_then: F,
154 ) -> Result<O, ExistsError>;
155
156 fn remove_addr(
162 &mut self,
163 bindings_ctx: &mut BT,
164 addr: &Ipv6DeviceAddr,
165 ) -> Result<
166 (AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>, Ipv6AddrSlaacConfig<BT::Instant>),
167 NotFoundError,
168 >;
169}
170
171pub struct SlaacConfigAndState<A: Ipv6LinkLayerAddr, BT: SlaacBindingsTypes> {
175 pub config: SlaacConfiguration,
177 pub dad_transmits: Option<NonZeroU16>,
179 pub retrans_timer: Duration,
181 pub link_layer_addr: Option<A>,
185 pub temp_secret_key: IidSecret,
187 pub stable_secret_key: IidSecret,
189 #[allow(missing_docs)]
190 pub _marker: PhantomData<BT>,
191}
192
193pub trait SlaacContext<BC: SlaacBindingsContext<Self::DeviceId>>:
195 DeviceIdContext<AnyDevice>
196{
197 type LinkLayerAddr: Ipv6LinkLayerAddr;
199
200 type SlaacAddrs<'a>: SlaacAddresses<BC> + CounterContext<SlaacCounters> + 'a;
202
203 fn with_slaac_addrs_mut_and_configs<
206 O,
207 F: FnOnce(
208 &mut Self::SlaacAddrs<'_>,
209 SlaacConfigAndState<Self::LinkLayerAddr, BC>,
210 &mut SlaacState<BC>,
211 ) -> O,
212 >(
213 &mut self,
214 device_id: &Self::DeviceId,
215 cb: F,
216 ) -> O;
217
218 fn with_slaac_addrs_mut<O, F: FnOnce(&mut Self::SlaacAddrs<'_>, &mut SlaacState<BC>) -> O>(
220 &mut self,
221 device_id: &Self::DeviceId,
222 cb: F,
223 ) -> O {
224 self.with_slaac_addrs_mut_and_configs(device_id, |addrs, _config, state| cb(addrs, state))
225 }
226}
227
228#[derive(Default)]
230pub struct SlaacCounters {
231 pub generated_slaac_addr_exists: Counter,
233}
234
235fn update_slaac_addr_valid_until<I: Instant>(
246 slaac_config: &mut SlaacConfig<I>,
247 valid_until: Lifetime<I>,
248) {
249 match slaac_config {
250 SlaacConfig::Stable { valid_until: v, .. } => *v = valid_until,
251 SlaacConfig::Temporary(TemporarySlaacConfig { valid_until: v, .. }) => {
252 *v = match valid_until {
253 Lifetime::Finite(v) => v,
254 Lifetime::Infinite => panic!("temporary addresses may not be valid forever"),
255 }
256 }
257 };
258}
259
260pub trait SlaacBindingsTypes: InstantBindingsTypes + TimerBindingsTypes {}
262impl<BT> SlaacBindingsTypes for BT where BT: InstantBindingsTypes + TimerBindingsTypes {}
263
264pub trait SlaacBindingsContext<D>:
266 RngContext + TimerContext + EventContext<IpDeviceEvent<D, Ipv6, Self::Instant>> + SlaacBindingsTypes
267{
268}
269impl<D, BC> SlaacBindingsContext<D> for BC where
270 BC: RngContext
271 + TimerContext
272 + EventContext<IpDeviceEvent<D, Ipv6, Self::Instant>>
273 + SlaacBindingsTypes
274{
275}
276
277pub trait SlaacHandler<BC: InstantContext>: DeviceIdContext<AnyDevice> {
279 fn apply_slaac_update(
289 &mut self,
290 bindings_ctx: &mut BC,
291 device_id: &Self::DeviceId,
292 prefix: Subnet<Ipv6Addr>,
293 preferred_lifetime: Option<NonZeroNdpLifetime>,
294 valid_lifetime: Option<NonZeroNdpLifetime>,
295 );
296
297 fn generate_link_local_address(&mut self, bindings_ctx: &mut BC, device_id: &Self::DeviceId);
299
300 fn on_address_removed(
304 &mut self,
305 bindings_ctx: &mut BC,
306 device_id: &Self::DeviceId,
307 addr: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
308 addr_config: Ipv6AddrSlaacConfig<BC::Instant>,
309 reason: AddressRemovedReason,
310 );
311
312 fn remove_all_slaac_addresses(&mut self, bindings_ctx: &mut BC, device_id: &Self::DeviceId);
314}
315
316impl<BC: SlaacBindingsContext<CC::DeviceId>, CC: SlaacContext<BC>> SlaacHandler<BC> for CC {
317 fn apply_slaac_update(
318 &mut self,
319 bindings_ctx: &mut BC,
320 device_id: &Self::DeviceId,
321 subnet: Subnet<Ipv6Addr>,
322 preferred_lifetime: Option<NonZeroNdpLifetime>,
323 valid_lifetime: Option<NonZeroNdpLifetime>,
324 ) {
325 if preferred_lifetime > valid_lifetime {
326 trace!("receive_ndp_packet: autonomous prefix's preferred lifetime is greater than valid lifetime, ignoring");
330 return;
331 }
332
333 let mut seen_stable = false;
334 let mut seen_temporary = false;
335
336 let now = bindings_ctx.now();
337 self.with_slaac_addrs_mut_and_configs(device_id, |slaac_addrs, config, slaac_state| {
338 let SlaacConfigAndState { config: device_config, dad_transmits, retrans_timer, .. } =
339 config;
340 slaac_addrs.for_each_addr_mut(|address_entry| {
343 let slaac_type = match apply_slaac_update_to_addr(
344 address_entry,
345 slaac_state,
346 subnet,
347 device_id,
348 valid_lifetime,
349 preferred_lifetime,
350 &device_config,
351 now,
352 retrans_timer,
353 dad_transmits,
354 bindings_ctx,
355 ) {
356 ControlFlow::Break(()) => return,
357 ControlFlow::Continue(slaac_type) => slaac_type,
358 };
359 match slaac_type {
366 SlaacType::Stable => seen_stable = true,
367 SlaacType::Temporary => seen_temporary = true,
368 }
369 });
370
371 let valid_lifetime = match valid_lifetime {
383 Some(valid_lifetime) => valid_lifetime,
384 None => {
385 trace!(
386 "receive_ndp_packet: autonomous prefix has valid \
387 lifetime = 0, ignoring"
388 );
389 return;
390 }
391 };
392
393 let address_types_to_add = (!seen_stable)
394 .then_some({
395 SlaacType::Stable
405 })
406 .into_iter()
407 .chain((!seen_temporary).then_some({
408 SlaacType::Temporary
415 }));
416
417 for slaac_type in address_types_to_add {
418 add_slaac_addr_sub::<_, CC>(
419 bindings_ctx,
420 device_id,
421 slaac_addrs,
422 &config,
423 slaac_state,
424 now,
425 SlaacInitConfig::new(slaac_type),
426 valid_lifetime,
427 preferred_lifetime,
428 &subnet,
429 );
430 }
431 });
432 }
433
434 fn generate_link_local_address(&mut self, bindings_ctx: &mut BC, device_id: &Self::DeviceId) {
435 let now = bindings_ctx.now();
436 self.with_slaac_addrs_mut_and_configs(device_id, |addrs, config, slaac_state| {
437 let link_local_subnet =
444 Subnet::new(Ipv6::LINK_LOCAL_UNICAST_SUBNET.network(), REQUIRED_PREFIX_BITS)
445 .expect("link local subnet should be valid");
446 add_slaac_addr_sub::<_, CC>(
447 bindings_ctx,
448 device_id,
449 addrs,
450 &config,
451 slaac_state,
452 now,
453 SlaacInitConfig::new(SlaacType::Stable),
454 NonZeroNdpLifetime::Infinite, Some(NonZeroNdpLifetime::Infinite), &link_local_subnet,
457 );
458 });
459 }
460
461 fn on_address_removed(
462 &mut self,
463 bindings_ctx: &mut BC,
464 device_id: &Self::DeviceId,
465 addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
466 addr_config: Ipv6AddrSlaacConfig<BC::Instant>,
467 reason: AddressRemovedReason,
468 ) {
469 self.with_slaac_addrs_mut_and_configs(device_id, |addrs, config, slaac_state| {
470 on_address_removed_inner::<_, CC>(
471 bindings_ctx,
472 addr_sub,
473 device_id,
474 addrs,
475 config,
476 slaac_state,
477 addr_config,
478 reason,
479 )
480 });
481 }
482
483 fn remove_all_slaac_addresses(&mut self, bindings_ctx: &mut BC, device_id: &Self::DeviceId) {
484 self.with_slaac_addrs_mut(device_id, |slaac_addrs, _| {
485 slaac_addrs
486 .with_addrs(|addrs| addrs.map(|a| a.addr_sub.addr()).collect::<Vec<_>>())
487 .into_iter()
488 .filter_map(|addr| {
489 slaac_addrs.remove_addr(bindings_ctx, &addr).map(Some).unwrap_or_else(
490 |NotFoundError| {
491 None
496 },
497 )
498 })
499 .collect::<Vec<_>>()
500 })
501 .into_iter()
502 .for_each(|(addr, config)| {
503 self.on_address_removed(
504 bindings_ctx,
505 device_id,
506 addr,
507 config,
508 AddressRemovedReason::Manual,
509 )
510 })
511 }
512}
513
514fn on_address_removed_inner<BC: SlaacBindingsContext<CC::DeviceId>, CC: SlaacContext<BC>>(
515 bindings_ctx: &mut BC,
516 addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
517 device_id: &CC::DeviceId,
518 slaac_addrs: &mut CC::SlaacAddrs<'_>,
519 config: SlaacConfigAndState<CC::LinkLayerAddr, BC>,
520 slaac_state: &mut SlaacState<BC>,
521 addr_config: Ipv6AddrSlaacConfig<BC::Instant>,
522 reason: AddressRemovedReason,
523) {
524 let SlaacState { timers } = slaac_state;
525 let preferred_until = timers
526 .cancel(bindings_ctx, &InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_sub.addr() })
527 .map(|(t, ())| t);
528 let _valid_until: Option<(BC::Instant, ())> = timers
529 .cancel(bindings_ctx, &InnerSlaacTimerId::InvalidateSlaacAddress { addr: addr_sub.addr() });
530
531 let now = bindings_ctx.now();
532
533 let Ipv6AddrSlaacConfig { inner, preferred_lifetime } = addr_config;
534
535 match inner {
536 SlaacConfig::Temporary(TemporarySlaacConfig {
537 valid_until,
538 creation_time,
539 desync_factor,
540 dad_counter,
541 }) => {
542 let _regen_at: Option<(BC::Instant, ())> = timers.cancel(
543 bindings_ctx,
544 &InnerSlaacTimerId::RegenerateTemporaryAddress { addr_subnet: addr_sub },
545 );
546
547 match reason {
548 AddressRemovedReason::Manual => return,
549 AddressRemovedReason::DadFailed => {
550 }
552 }
553
554 let temp_valid_lifetime = match config.config.temporary_address_configuration {
555 TemporarySlaacAddressConfiguration::Enabled {
556 temp_idgen_retries,
557 temp_valid_lifetime,
558 temp_preferred_lifetime: _,
559 } => {
560 if dad_counter >= temp_idgen_retries {
561 return;
562 }
563 temp_valid_lifetime
564 }
565 TemporarySlaacAddressConfiguration::Disabled => return,
566 };
567
568 let preferred_for = match preferred_until.map(|preferred_until| {
576 preferred_until.saturating_duration_since(creation_time) + desync_factor
577 }) {
578 Some(preferred_for) => preferred_for,
579 None => return,
582 };
583
584 let valid_for =
589 NonZeroDuration::new(valid_until.saturating_duration_since(creation_time))
590 .unwrap_or(temp_valid_lifetime);
591
592 add_slaac_addr_sub::<_, CC>(
593 bindings_ctx,
594 device_id,
595 slaac_addrs,
596 &config,
597 slaac_state,
598 now,
599 SlaacInitConfig::Temporary { dad_count: dad_counter + 1 },
600 NonZeroNdpLifetime::Finite(valid_for),
601 NonZeroDuration::new(preferred_for).map(NonZeroNdpLifetime::Finite),
602 &addr_sub.subnet(),
603 );
604 }
605 SlaacConfig::Stable { valid_until, creation_time, regen_counter, dad_counter } => {
606 match reason {
607 AddressRemovedReason::Manual => return,
608 AddressRemovedReason::DadFailed => {
609 }
611 }
612
613 match config.config.stable_address_configuration {
614 StableSlaacAddressConfiguration::Disabled => return,
617 StableSlaacAddressConfiguration::Enabled { iid_generation } => match iid_generation
618 {
619 IidGenerationConfiguration::Eui64 => return,
622 IidGenerationConfiguration::Opaque { idgen_retries } => {
623 if dad_counter >= idgen_retries {
624 return;
625 }
626 }
627 },
628 }
629
630 let valid_for = match valid_until {
634 Lifetime::Infinite => NonZeroNdpLifetime::Infinite,
635 Lifetime::Finite(valid_until) => {
636 let Some(valid_for) =
637 NonZeroDuration::new(valid_until.saturating_duration_since(creation_time))
638 else {
639 return;
641 };
642 NonZeroNdpLifetime::Finite(valid_for)
643 }
644 };
645
646 let preferred_for = match preferred_lifetime {
652 PreferredLifetime::Deprecated => None,
653 PreferredLifetime::Preferred(lifetime) => match lifetime {
654 Lifetime::Infinite => Some(NonZeroNdpLifetime::Infinite),
655 Lifetime::Finite(preferred_until) => NonZeroDuration::new(
656 preferred_until.saturating_duration_since(creation_time),
657 )
658 .map(NonZeroNdpLifetime::Finite),
659 },
660 };
661
662 add_slaac_addr_sub::<_, CC>(
663 bindings_ctx,
664 device_id,
665 slaac_addrs,
666 &config,
667 slaac_state,
668 now,
669 SlaacInitConfig::Stable { regen_count: regen_counter, dad_count: dad_counter + 1 },
670 valid_for,
671 preferred_for,
672 &addr_sub.subnet(),
673 );
674 }
675 }
676}
677
678fn apply_slaac_update_to_addr<D: DeviceIdentifier, BC: SlaacBindingsContext<D>>(
679 address_entry: SlaacAddressEntryMut<'_, BC::Instant>,
680 state: &mut SlaacState<BC>,
681 subnet: Subnet<Ipv6Addr>,
682 device_id: &D,
683 valid_lifetime: Option<NonZeroNdpLifetime>,
684 addr_preferred_lifetime: Option<NonZeroNdpLifetime>,
685 config: &SlaacConfiguration,
686 now: <BC as InstantBindingsTypes>::Instant,
687 retrans_timer: Duration,
688 dad_transmits: Option<NonZeroU16>,
689 bindings_ctx: &mut BC,
690) -> ControlFlow<(), SlaacType> {
691 let SlaacAddressEntryMut {
692 addr_sub,
693 config: Ipv6AddrSlaacConfig { inner: slaac_config, preferred_lifetime },
694 } = address_entry;
695 let SlaacState { timers } = state;
696 if addr_sub.subnet() != subnet {
697 return ControlFlow::Break(());
698 }
699 let addr = addr_sub.addr();
700 let slaac_type = SlaacType::from(&*slaac_config);
701 trace!(
702 "receive_ndp_packet: already have a {:?} SLAAC address {:?} configured on device {:?}",
703 slaac_type,
704 addr_sub,
705 device_id
706 );
707
708 #[derive(Copy, Clone)]
710 enum ValidLifetimeBound {
711 FromPrefix(Option<NonZeroNdpLifetime>),
712 FromMaxBound(Duration),
713 }
714 impl ValidLifetimeBound {
715 fn get(self) -> Option<NonZeroNdpLifetime> {
717 match self {
718 Self::FromPrefix(d) => d,
719 Self::FromMaxBound(d) => NonZeroDuration::new(d).map(NonZeroNdpLifetime::Finite),
720 }
721 }
722 }
723 let (valid_for, entry_valid_until, preferred_for_and_regen_at) = match slaac_config {
724 SlaacConfig::Stable {
725 valid_until: entry_valid_until,
726 creation_time: _,
727 regen_counter: _,
728 dad_counter: _,
729 } => (
730 ValidLifetimeBound::FromPrefix(valid_lifetime),
731 *entry_valid_until,
732 addr_preferred_lifetime.map(|p| (p, None)),
733 ),
734 SlaacConfig::Temporary(TemporarySlaacConfig {
737 valid_until: entry_valid_until,
738 creation_time,
739 desync_factor,
740 dad_counter: _,
741 }) => {
742 let SlaacConfiguration {
743 stable_address_configuration: _,
744 temporary_address_configuration,
745 } = config;
746 let (valid_for, preferred_for, entry_valid_until) =
747 match temporary_address_configuration {
748 TemporarySlaacAddressConfiguration::Disabled => {
754 (ValidLifetimeBound::FromMaxBound(Duration::ZERO), None, *entry_valid_until)
755 }
756 TemporarySlaacAddressConfiguration::Enabled {
757 temp_preferred_lifetime,
758 temp_valid_lifetime,
759 temp_idgen_retries: _,
760 } => {
761 let preferred_for =
769 addr_preferred_lifetime.and_then(|preferred_lifetime| {
770 temp_preferred_lifetime
771 .get()
772 .checked_sub(now.saturating_duration_since(*creation_time))
773 .and_then(|p| p.checked_sub(*desync_factor))
774 .and_then(NonZeroDuration::new)
775 .map(|d| preferred_lifetime.min_finite_duration(d))
776 });
777 let since_creation = now.saturating_duration_since(*creation_time);
785 let configured_max_lifetime = temp_valid_lifetime.get();
786 let max_valid_lifetime = if since_creation > configured_max_lifetime {
787 Duration::ZERO
788 } else {
789 configured_max_lifetime - since_creation
790 };
791
792 let valid_for = valid_lifetime.map_or(
793 ValidLifetimeBound::FromPrefix(None),
794 |d| match d {
795 NonZeroNdpLifetime::Infinite => {
796 ValidLifetimeBound::FromMaxBound(max_valid_lifetime)
797 }
798 NonZeroNdpLifetime::Finite(d) => {
799 if max_valid_lifetime <= d.get() {
800 ValidLifetimeBound::FromMaxBound(max_valid_lifetime)
801 } else {
802 ValidLifetimeBound::FromPrefix(valid_lifetime)
803 }
804 }
805 },
806 );
807
808 (valid_for, preferred_for, *entry_valid_until)
809 }
810 };
811
812 let preferred_for_and_regen_at = preferred_for.map(|preferred_for| {
813 let SlaacConfiguration {
814 stable_address_configuration: _,
815 temporary_address_configuration,
816 } = config;
817
818 let regen_at = match temporary_address_configuration {
819 TemporarySlaacAddressConfiguration::Disabled => None,
820 TemporarySlaacAddressConfiguration::Enabled {
821 temp_idgen_retries,
822 temp_preferred_lifetime: _,
823 temp_valid_lifetime: _,
824 } => {
825 let regen_advance = regen_advance(
826 *temp_idgen_retries,
827 retrans_timer,
828 dad_transmits.map_or(0, NonZeroU16::get),
829 )
830 .get();
831 preferred_for
844 .get()
845 .checked_sub(regen_advance)
846 .map_or(Some(now), |d| now.checked_add(d))
847 }
848 };
849
850 (NonZeroNdpLifetime::Finite(preferred_for), regen_at)
851 });
852
853 (valid_for, Lifetime::Finite(entry_valid_until), preferred_for_and_regen_at)
854 }
855 };
856
857 let remaining_lifetime = match entry_valid_until {
859 Lifetime::Infinite => Some(Lifetime::Infinite),
860 Lifetime::Finite(entry_valid_until) => entry_valid_until
861 .checked_duration_since(now)
862 .and_then(NonZeroDuration::new)
863 .map(|d| Lifetime::Finite(d)),
864 };
865
866 let preferred_lifetime_updated = match preferred_for_and_regen_at {
873 None => {
874 if preferred_lifetime.is_deprecated() {
875 false
876 } else {
877 *preferred_lifetime = PreferredLifetime::Deprecated;
878 let _: Option<(BC::Instant, ())> =
879 timers.cancel(bindings_ctx, &InnerSlaacTimerId::DeprecateSlaacAddress { addr });
880 let _: Option<(BC::Instant, ())> = timers.cancel(
881 bindings_ctx,
882 &InnerSlaacTimerId::RegenerateTemporaryAddress { addr_subnet: addr_sub },
883 );
884 true
885 }
886 }
887 Some((preferred_for, regen_at)) => {
888 let timer_id = InnerSlaacTimerId::DeprecateSlaacAddress { addr };
889 let preferred_instant = Lifetime::from_ndp(now, preferred_for);
890 match preferred_instant {
891 Lifetime::Finite(instant) => {
892 let _previously_scheduled_instant: Option<(BC::Instant, ())> =
893 timers.schedule_instant(bindings_ctx, timer_id, (), instant);
894 }
895 Lifetime::Infinite => {
896 let _previously_scheduled_instant: Option<(BC::Instant, ())> =
897 timers.cancel(bindings_ctx, &timer_id);
898 }
899 };
900 let new_lifetime = PreferredLifetime::Preferred(preferred_instant);
901 let updated = core::mem::replace(preferred_lifetime, new_lifetime) != new_lifetime;
902 let timer_id = InnerSlaacTimerId::RegenerateTemporaryAddress { addr_subnet: addr_sub };
903 let _prev_regen_at: Option<(BC::Instant, ())> = match regen_at {
904 Some(regen_at) => timers.schedule_instant(bindings_ctx, timer_id, (), regen_at),
905 None => timers.cancel(bindings_ctx, &timer_id),
906 };
907 updated
908 }
909 };
910
911 let valid_for_to_update = match valid_for {
916 ValidLifetimeBound::FromMaxBound(valid_for) => {
917 NonZeroDuration::new(valid_for).map(NonZeroNdpLifetime::Finite)
920 }
921 ValidLifetimeBound::FromPrefix(valid_for) => {
922 match valid_for {
927 Some(NonZeroNdpLifetime::Infinite) => Some(NonZeroNdpLifetime::Infinite),
928 Some(NonZeroNdpLifetime::Finite(v))
929 if v > MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE
930 || remaining_lifetime.map_or(true, |r| r < Lifetime::Finite(v)) =>
931 {
932 Some(NonZeroNdpLifetime::Finite(v))
933 }
934 None | Some(NonZeroNdpLifetime::Finite(_)) => {
935 if remaining_lifetime.map_or(true, |r| {
936 r <= Lifetime::Finite(MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE)
937 }) {
938 None
953 } else {
954 Some(NonZeroNdpLifetime::Finite(MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE))
957 }
958 }
959 }
960 }
961 };
962
963 match valid_for_to_update {
964 Some(valid_for) => match Lifetime::from_ndp(now, valid_for) {
965 Lifetime::Finite(valid_until) => {
966 trace!(
967 "receive_ndp_packet: updating valid lifetime to {:?} for SLAAC address {:?} on device {:?}",
968 valid_until,
969 addr,
970 device_id
971 );
972
973 update_slaac_addr_valid_until(slaac_config, Lifetime::Finite(valid_until));
975
976 let _: Option<(BC::Instant, ())> = timers.schedule_instant(
977 bindings_ctx,
978 InnerSlaacTimerId::InvalidateSlaacAddress { addr },
979 (),
980 valid_until,
981 );
982 }
983 Lifetime::Infinite => {
984 update_slaac_addr_valid_until(slaac_config, Lifetime::Infinite);
986
987 let _: Option<(BC::Instant, ())> = timers
988 .cancel(bindings_ctx, &InnerSlaacTimerId::InvalidateSlaacAddress { addr });
989 }
990 },
991 None => {
992 trace!(
993 "receive_ndp_packet: not updating valid lifetime for SLAAC address {:?} on device {:?} as remaining lifetime is less than 2 hours and new valid lifetime ({:?}) is less than remaining lifetime",
994 addr,
995 device_id,
996 valid_for.get()
997 );
998 }
999 }
1000
1001 if preferred_lifetime_updated || valid_for_to_update.is_some() {
1002 bindings_ctx.on_event(IpDeviceEvent::AddressPropertiesChanged {
1003 device: device_id.clone(),
1004 addr: addr_sub.addr().into(),
1005 valid_until: slaac_config.valid_until(),
1006 preferred_lifetime: *preferred_lifetime,
1007 });
1008 }
1009 ControlFlow::Continue(slaac_type)
1010}
1011
1012impl<BC: SlaacBindingsContext<CC::DeviceId>, CC: SlaacContext<BC>> HandleableTimer<CC, BC>
1013 for SlaacTimerId<CC::WeakDeviceId>
1014{
1015 fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, _: BC::UniqueTimerId) {
1016 let Self { device_id } = self;
1017 let Some(device_id) = device_id.upgrade() else {
1018 return;
1019 };
1020 core_ctx.with_slaac_addrs_mut_and_configs(&device_id, |addrs, config, slaac_state| {
1021 let Some((timer_id, ())) = slaac_state.timers.pop(bindings_ctx) else {
1022 return;
1023 };
1024 match timer_id {
1025 InnerSlaacTimerId::DeprecateSlaacAddress { addr } => {
1026 addrs.for_each_addr_mut(|SlaacAddressEntryMut { addr_sub, config }| {
1027 if addr_sub.addr() == addr {
1028 config.preferred_lifetime = PreferredLifetime::Deprecated;
1029
1030 bindings_ctx.on_event(IpDeviceEvent::AddressPropertiesChanged {
1031 device: device_id.clone(),
1032 addr: addr_sub.addr().into(),
1033 valid_until: config.inner.valid_until(),
1034 preferred_lifetime: config.preferred_lifetime,
1035 });
1036 }
1037 })
1038 }
1039 InnerSlaacTimerId::InvalidateSlaacAddress { addr } => {
1040 let (addr, slaac_config) = match addrs.remove_addr(bindings_ctx, &addr) {
1041 Ok(addr_config) => addr_config,
1042 Err(NotFoundError) => {
1043 #[cfg(test)]
1050 panic!("Failed to remove address {addr} on invalidation");
1051 #[cfg(not(test))]
1052 {
1053 log::warn!(
1054 "failed to remove SLAAC address {addr}, assuming raced \
1055 with user removal"
1056 );
1057 return;
1058 }
1059 }
1060 };
1061
1062 on_address_removed_inner::<_, CC>(
1063 bindings_ctx,
1064 addr,
1065 &device_id,
1066 addrs,
1067 config,
1068 slaac_state,
1069 slaac_config,
1070 AddressRemovedReason::Manual,
1071 );
1072 }
1073 InnerSlaacTimerId::RegenerateTemporaryAddress { addr_subnet } => {
1074 regenerate_temporary_slaac_addr::<_, CC>(
1075 bindings_ctx,
1076 addrs,
1077 config,
1078 slaac_state,
1079 &device_id,
1080 &addr_subnet,
1081 );
1082 }
1083 }
1084 });
1085 }
1086}
1087
1088#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1091pub enum IidGenerationConfiguration {
1092 Eui64,
1097 Opaque {
1101 idgen_retries: u8,
1105 },
1106}
1107
1108#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
1110pub enum StableSlaacAddressConfiguration {
1111 Enabled {
1113 iid_generation: IidGenerationConfiguration,
1116 },
1117 #[default]
1119 Disabled,
1120}
1121
1122impl StableSlaacAddressConfiguration {
1123 pub const DEFAULT_IDGEN_RETRIES: u8 = 3;
1127
1128 #[cfg(any(test, feature = "testutils"))]
1130 pub const ENABLED_WITH_EUI64: Self =
1131 Self::Enabled { iid_generation: IidGenerationConfiguration::Eui64 };
1132
1133 #[cfg(any(test, feature = "testutils"))]
1135 pub const ENABLED_WITH_OPAQUE_IIDS: Self = Self::Enabled {
1136 iid_generation: IidGenerationConfiguration::Opaque {
1137 idgen_retries: Self::DEFAULT_IDGEN_RETRIES,
1138 },
1139 };
1140}
1141
1142#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
1157pub enum TemporarySlaacAddressConfiguration {
1158 Enabled {
1160 temp_valid_lifetime: NonZeroDuration,
1163
1164 temp_preferred_lifetime: NonZeroDuration,
1167
1168 temp_idgen_retries: u8,
1172 },
1173 #[default]
1175 Disabled,
1176}
1177
1178impl TemporarySlaacAddressConfiguration {
1179 pub const DEFAULT_TEMP_VALID_LIFETIME: NonZeroDuration = NonZeroDuration::from_secs(2 * 24 * 60 * 60u64).unwrap();
1184
1185 pub const DEFAULT_TEMP_PREFERRED_LIFETIME: NonZeroDuration = NonZeroDuration::from_secs(1 * 24 * 60 * 60u64).unwrap();
1190
1191 pub const DEFAULT_TEMP_IDGEN_RETRIES: u8 = 3;
1195
1196 pub fn enabled_with_rfc_defaults() -> Self {
1198 Self::Enabled {
1199 temp_valid_lifetime: Self::DEFAULT_TEMP_VALID_LIFETIME,
1200 temp_preferred_lifetime: Self::DEFAULT_TEMP_PREFERRED_LIFETIME,
1201 temp_idgen_retries: Self::DEFAULT_TEMP_IDGEN_RETRIES,
1202 }
1203 }
1204
1205 pub fn is_enabled(&self) -> bool {
1207 match self {
1208 Self::Enabled { .. } => true,
1209 Self::Disabled => false,
1210 }
1211 }
1212}
1213
1214#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
1216pub struct SlaacConfiguration {
1217 pub stable_address_configuration: StableSlaacAddressConfiguration,
1219
1220 pub temporary_address_configuration: TemporarySlaacAddressConfiguration,
1222}
1223
1224impl SlaacConfiguration {
1225 pub fn update(
1227 &mut self,
1228 SlaacConfigurationUpdate {
1229 stable_address_configuration,
1230 temporary_address_configuration,
1231 }: SlaacConfigurationUpdate,
1232 ) -> SlaacConfigurationUpdate {
1233 fn get_prev_and_update<T>(old: &mut T, update: Option<T>) -> Option<T> {
1234 update.map(|new| core::mem::replace(old, new))
1235 }
1236 SlaacConfigurationUpdate {
1237 stable_address_configuration: get_prev_and_update(
1238 &mut self.stable_address_configuration,
1239 stable_address_configuration,
1240 ),
1241 temporary_address_configuration: get_prev_and_update(
1242 &mut self.temporary_address_configuration,
1243 temporary_address_configuration,
1244 ),
1245 }
1246 }
1247}
1248
1249#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
1253pub struct SlaacConfigurationUpdate {
1254 pub stable_address_configuration: Option<StableSlaacAddressConfiguration>,
1256
1257 pub temporary_address_configuration: Option<TemporarySlaacAddressConfiguration>,
1259}
1260
1261#[derive(PartialEq, Eq)]
1262enum SlaacType {
1263 Stable,
1264 Temporary,
1265}
1266
1267impl core::fmt::Debug for SlaacType {
1268 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1269 match self {
1270 SlaacType::Stable => f.write_str("stable"),
1271 SlaacType::Temporary => f.write_str("temporary"),
1272 }
1273 }
1274}
1275
1276impl<'a, Instant> From<&'a SlaacConfig<Instant>> for SlaacType {
1277 fn from(slaac_config: &'a SlaacConfig<Instant>) -> Self {
1278 match slaac_config {
1279 SlaacConfig::Stable { .. } => SlaacType::Stable,
1280 SlaacConfig::Temporary { .. } => SlaacType::Temporary,
1281 }
1282 }
1283}
1284
1285pub const SLAAC_MIN_REGEN_ADVANCE: NonZeroDuration = NonZeroDuration::from_secs(2).unwrap();
1296
1297fn regen_advance(
1301 temp_idgen_retries: u8,
1302 retrans_timer: Duration,
1303 dad_transmits: u16,
1304) -> NonZeroDuration {
1305 SLAAC_MIN_REGEN_ADVANCE
1311 + retrans_timer
1312 .checked_mul(u32::from(temp_idgen_retries) * u32::from(dad_transmits))
1313 .unwrap_or(Duration::ZERO)
1314}
1315
1316fn desync_factor<R: Rng>(
1332 rng: &mut R,
1333 temp_preferred_lifetime: NonZeroDuration,
1334 regen_advance: NonZeroDuration,
1335) -> Option<Duration> {
1336 let temp_preferred_lifetime = temp_preferred_lifetime.get();
1337
1338 temp_preferred_lifetime.checked_sub(regen_advance.get()).map(|max_desync_factor| {
1353 let max_desync_factor =
1354 core::cmp::min(max_desync_factor, (temp_preferred_lifetime * 2) / 5);
1355 rng.sample(Uniform::new(Duration::ZERO, max_desync_factor))
1356 })
1357}
1358
1359fn regenerate_temporary_slaac_addr<BC: SlaacBindingsContext<CC::DeviceId>, CC: SlaacContext<BC>>(
1360 bindings_ctx: &mut BC,
1361 slaac_addrs: &mut CC::SlaacAddrs<'_>,
1362 config_and_state: SlaacConfigAndState<CC::LinkLayerAddr, BC>,
1363 slaac_state: &mut SlaacState<BC>,
1364 device_id: &CC::DeviceId,
1365 addr_subnet: &AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
1366) {
1367 let SlaacConfigAndState { config, .. } = config_and_state;
1368 let SlaacState { timers } = slaac_state;
1369 let now = bindings_ctx.now();
1370
1371 enum Action {
1372 SkipRegen,
1373 Regen { valid_for: NonZeroDuration, preferred_for: Duration },
1374 }
1375
1376 let action = slaac_addrs.with_addrs(|addrs| {
1377 let entry = {
1378 let mut found_entry = None;
1379
1380 for entry in addrs {
1381 if entry.addr_sub.subnet() != addr_subnet.subnet() {
1382 continue;
1383 }
1384
1385 if !entry.config.preferred_lifetime.is_deprecated() {
1398 if let Some((entry, regen_at)) = timers
1399 .get(&InnerSlaacTimerId::RegenerateTemporaryAddress {
1400 addr_subnet: entry.addr_sub,
1401 })
1402 .map(|(instant, ())| (entry, instant))
1403 {
1404 debug!(
1405 "ignoring regen event at {:?} for {:?} since {:?} \
1406 will regenerate after at {:?}",
1407 bindings_ctx.now(),
1408 addr_subnet,
1409 entry.addr_sub.addr(),
1410 regen_at
1411 );
1412 return Action::SkipRegen;
1413 }
1414 }
1415
1416 if &entry.addr_sub == addr_subnet {
1417 assert_matches!(found_entry, None);
1418 found_entry = Some(entry);
1419 }
1420 }
1421
1422 found_entry.unwrap_or_else(|| panic!("couldn't find {:?} to regenerate", addr_subnet))
1423 };
1424
1425 assert!(
1426 !entry.config.preferred_lifetime.is_deprecated(),
1427 "can't regenerate deprecated address {:?}",
1428 addr_subnet
1429 );
1430
1431 let TemporarySlaacConfig { creation_time, desync_factor, valid_until, dad_counter: _ } =
1432 match entry.config.inner {
1433 SlaacConfig::Temporary(temporary_config) => temporary_config,
1434 SlaacConfig::Stable { .. } => unreachable!(
1435 "can't regenerate a temporary address for {:?}, which is stable",
1436 addr_subnet
1437 ),
1438 };
1439
1440 let temp_valid_lifetime = match config.temporary_address_configuration {
1441 TemporarySlaacAddressConfiguration::Enabled {
1442 temp_valid_lifetime,
1443 temp_preferred_lifetime: _,
1444 temp_idgen_retries: _,
1445 } => temp_valid_lifetime,
1446 TemporarySlaacAddressConfiguration::Disabled => return Action::SkipRegen,
1447 };
1448
1449 let (deprecate_at, ()) = timers
1450 .get(&InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_subnet.addr() })
1451 .unwrap_or_else(|| {
1452 unreachable!(
1453 "temporary SLAAC address {:?} had a regen timer fire but \
1454 does not have a deprecation timer",
1455 addr_subnet.addr()
1456 )
1457 });
1458 let preferred_for = deprecate_at.saturating_duration_since(creation_time) + desync_factor;
1459
1460 let valid_for = valid_until
1465 .checked_duration_since(creation_time)
1466 .and_then(NonZeroDuration::new)
1467 .unwrap_or(temp_valid_lifetime);
1468
1469 Action::Regen { valid_for, preferred_for }
1470 });
1471
1472 match action {
1473 Action::SkipRegen => {}
1474 Action::Regen { valid_for, preferred_for } => add_slaac_addr_sub::<_, CC>(
1475 bindings_ctx,
1476 device_id,
1477 slaac_addrs,
1478 &config_and_state,
1479 slaac_state,
1480 now,
1481 SlaacInitConfig::Temporary { dad_count: 0 },
1482 NonZeroNdpLifetime::Finite(valid_for),
1483 NonZeroDuration::new(preferred_for).map(NonZeroNdpLifetime::Finite),
1484 &addr_subnet.subnet(),
1485 ),
1486 }
1487}
1488
1489#[derive(Copy, Clone, Debug)]
1490enum SlaacInitConfig {
1491 Stable {
1492 regen_count: u8,
1495 dad_count: u8,
1497 },
1498 Temporary {
1499 dad_count: u8,
1500 },
1501}
1502
1503impl SlaacInitConfig {
1504 fn new(slaac_type: SlaacType) -> Self {
1505 match slaac_type {
1506 SlaacType::Stable => Self::Stable { regen_count: 0, dad_count: 0 },
1507 SlaacType::Temporary => Self::Temporary { dad_count: 0 },
1508 }
1509 }
1510}
1511
1512fn has_iana_allowed_iid(address: Ipv6Addr) -> bool {
1518 let mut iid = [0u8; 8];
1519 const U64_SUFFIX_LEN: usize = Ipv6Addr::BYTES as usize - u64::BITS as usize / 8;
1520 iid.copy_from_slice(&address.bytes()[U64_SUFFIX_LEN..]);
1521 let iid = u64::from_be_bytes(iid);
1522 match iid {
1523 0x0000_0000_0000_0000 => false,
1525 0x0200_5EFF_FE00_0000..=0x0200_5EFF_FEFF_FFFF => false,
1530 0xFDFF_FFFF_FFFF_FF80..=0xFDFF_FFFF_FFFF_FFFF => false,
1532
1533 _iid => true,
1535 }
1536}
1537
1538fn generate_stable_address(
1554 prefix: &Subnet<Ipv6Addr>,
1555 iid: &[u8],
1556) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
1557 if prefix.prefix() % 8 != 0 {
1558 unimplemented!(
1559 "generate_stable_address: not implemented for when prefix length is not a multiple of \
1560 8 bits"
1561 );
1562 }
1563
1564 let mut address = prefix.network().ipv6_bytes();
1565 let prefix_len = usize::from(prefix.prefix() / 8);
1566 assert_eq!(address.len() - prefix_len, iid.len());
1567 address[prefix_len..].copy_from_slice(&iid);
1568
1569 let address = AddrSubnet::new(Ipv6Addr::from(address), prefix.prefix()).unwrap();
1570 assert_eq!(address.subnet(), *prefix);
1571
1572 address
1573}
1574
1575fn generate_stable_address_with_opaque_iid(
1578 prefix: &Subnet<Ipv6Addr>,
1579 network_interface: &[u8],
1580 dad_counter: u8,
1581 secret_key: &IidSecret,
1582) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
1583 let iid = OpaqueIid::new(
1584 *prefix,
1585 network_interface,
1586 None::<&[_]>,
1587 OpaqueIidNonce::DadCounter(dad_counter),
1588 secret_key,
1589 );
1590 let prefix_len = prefix.prefix() / 8;
1591 let iid = &iid.to_be_bytes()[..usize::from(Ipv6Addr::BYTES - prefix_len)];
1592
1593 generate_stable_address(prefix, iid)
1594}
1595
1596fn generate_global_temporary_address(
1610 prefix: &Subnet<Ipv6Addr>,
1611 network_interface: &[u8],
1612 seed: u64,
1613 secret_key: &IidSecret,
1614) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
1615 let prefix_len = usize::from(prefix.prefix() / 8);
1616 let mut address = prefix.network().ipv6_bytes();
1617
1618 let interface_identifier = OpaqueIid::new(
1621 *prefix,
1622 network_interface,
1623 None::<[_; 0]>,
1624 OpaqueIidNonce::Random(seed),
1625 secret_key,
1626 );
1627 let suffix_bytes = &interface_identifier.to_be_bytes()[..(address.len() - prefix_len)];
1628 address[prefix_len..].copy_from_slice(suffix_bytes);
1629
1630 let address = AddrSubnet::new(Ipv6Addr::from(address), prefix.prefix()).unwrap();
1631 assert_eq!(address.subnet(), *prefix);
1632
1633 address
1634}
1635
1636fn add_slaac_addr_sub<BC: SlaacBindingsContext<CC::DeviceId>, CC: SlaacContext<BC>>(
1637 bindings_ctx: &mut BC,
1638 device_id: &CC::DeviceId,
1639 slaac_addrs: &mut CC::SlaacAddrs<'_>,
1640 config: &SlaacConfigAndState<CC::LinkLayerAddr, BC>,
1641 slaac_state: &mut SlaacState<BC>,
1642 now: BC::Instant,
1643 slaac_config: SlaacInitConfig,
1644 prefix_valid_for: NonZeroNdpLifetime,
1645 prefix_preferred_for: Option<NonZeroNdpLifetime>,
1646 subnet: &Subnet<Ipv6Addr>,
1647) {
1648 if subnet.prefix() != REQUIRED_PREFIX_BITS {
1649 error!(
1653 "receive_ndp_packet: autonomous prefix length {:?} and interface identifier length {:?} cannot form valid IPv6 address, ignoring",
1654 subnet.prefix(),
1655 REQUIRED_PREFIX_BITS
1656 );
1657 return;
1658 }
1659
1660 struct PreferredForAndRegenAt<Instant>(NonZeroNdpLifetime, Option<Instant>);
1661
1662 let SlaacConfigAndState {
1663 config,
1664 dad_transmits,
1665 retrans_timer,
1666 link_layer_addr,
1667 temp_secret_key,
1668 stable_secret_key,
1669 _marker,
1670 } = config;
1671
1672 let Some(link_layer_addr) = link_layer_addr else {
1673 warn!(
1674 "add_slaac_addr_sub: cannot derive IIDs for device {device_id:?} that does not support \
1675 link-layer addressing"
1676 );
1677 return;
1678 };
1679
1680 let SlaacConfiguration { stable_address_configuration, temporary_address_configuration } =
1681 config;
1682 let SlaacState { timers } = slaac_state;
1683
1684 let (valid_until, preferred_and_regen, mut addresses) = match slaac_config {
1685 SlaacInitConfig::Stable { mut regen_count, dad_count } => {
1686 let iid_generation = match stable_address_configuration {
1687 StableSlaacAddressConfiguration::Disabled => {
1688 trace!("stable SLAAC addresses are disabled on device {:?}", device_id);
1689 return;
1690 }
1691 StableSlaacAddressConfiguration::Enabled { iid_generation } => iid_generation,
1692 };
1693
1694 let valid_until = Lifetime::from_ndp(now, prefix_valid_for);
1695
1696 let addresses = either::Either::Left(match iid_generation {
1704 IidGenerationConfiguration::Eui64 => {
1705 assert_eq!(regen_count, 0);
1709
1710 if dad_count != 0 {
1714 return;
1715 }
1716
1717 let address = generate_stable_address(&subnet, &link_layer_addr.eui64_iid());
1718 let config = SlaacConfig::Stable {
1719 valid_until,
1720 creation_time: now,
1721 regen_counter: regen_count,
1722 dad_counter: dad_count,
1723 };
1724 either::Either::Left(core::iter::once((address, config)))
1725 }
1726 IidGenerationConfiguration::Opaque { idgen_retries: _ } => {
1727 either::Either::Right(core::iter::from_fn(move || {
1728 let mut attempts = 0;
1738 loop {
1739 let address = generate_stable_address_with_opaque_iid(
1740 &subnet,
1741 link_layer_addr.as_bytes(),
1742 regen_count + dad_count,
1750 &stable_secret_key,
1751 );
1752 let config = SlaacConfig::Stable {
1753 valid_until,
1754 creation_time: now,
1755 regen_counter: regen_count,
1756 dad_counter: dad_count,
1757 };
1758
1759 regen_count = regen_count.wrapping_add(1);
1760
1761 if has_iana_allowed_iid(address.addr().get()) {
1762 break Some((address, config));
1763 }
1764
1765 attempts += 1;
1766 if attempts > MAX_LOCAL_REGEN_ATTEMPTS {
1767 return None;
1768 }
1769 }
1770 }))
1771 }
1772 });
1773
1774 (valid_until, prefix_preferred_for.map(|p| PreferredForAndRegenAt(p, None)), addresses)
1775 }
1776 SlaacInitConfig::Temporary { dad_count } => {
1777 match temporary_address_configuration {
1778 TemporarySlaacAddressConfiguration::Disabled => {
1779 trace!(
1780 "receive_ndp_packet: temporary addresses are disabled on device {:?}",
1781 device_id
1782 );
1783 return;
1784 }
1785 TemporarySlaacAddressConfiguration::Enabled {
1786 temp_valid_lifetime,
1787 temp_preferred_lifetime,
1788 temp_idgen_retries,
1789 } => {
1790 let per_attempt_random_seed: u64 = bindings_ctx.rng().gen();
1791
1792 let valid_for = match prefix_valid_for {
1804 NonZeroNdpLifetime::Finite(prefix_valid_for) => {
1805 core::cmp::min(prefix_valid_for, *temp_valid_lifetime)
1806 }
1807 NonZeroNdpLifetime::Infinite => *temp_valid_lifetime,
1808 };
1809
1810 let regen_advance = regen_advance(
1811 *temp_idgen_retries,
1812 *retrans_timer,
1813 dad_transmits.map_or(0, NonZeroU16::get),
1814 );
1815
1816 let valid_until = now.saturating_add(valid_for.get());
1817
1818 let desync_factor = if let Some(d) = desync_factor(
1819 &mut bindings_ctx.rng(),
1820 *temp_preferred_lifetime,
1821 regen_advance,
1822 ) {
1823 d
1824 } else {
1825 trace!(
1833 "failed to calculate DESYNC_FACTOR; \
1834 temp_preferred_lifetime={:?}, regen_advance={:?}",
1835 temp_preferred_lifetime,
1836 regen_advance,
1837 );
1838 return;
1839 };
1840
1841 let preferred_for = prefix_preferred_for.and_then(|prefix_preferred_for| {
1842 temp_preferred_lifetime
1843 .get()
1844 .checked_sub(desync_factor)
1845 .and_then(NonZeroDuration::new)
1846 .map(|d| prefix_preferred_for.min_finite_duration(d))
1847 });
1848
1849 let preferred_for_and_regen_at = match preferred_for {
1855 None => return,
1856 Some(preferred_for) => {
1857 match preferred_for.get().checked_sub(regen_advance.get()) {
1858 Some(before_regen) => PreferredForAndRegenAt(
1859 NonZeroNdpLifetime::Finite(preferred_for),
1860 now.checked_add(before_regen),
1863 ),
1864 None => {
1865 trace!(
1866 "receive_ndp_packet: preferred lifetime of {:?} \
1867 for subnet {:?} is too short to allow regen",
1868 preferred_for,
1869 subnet
1870 );
1871 return;
1872 }
1873 }
1874 }
1875 };
1876
1877 let config = SlaacConfig::Temporary(TemporarySlaacConfig {
1878 desync_factor,
1879 valid_until,
1880 creation_time: now,
1881 dad_counter: dad_count,
1882 });
1883
1884 let mut seed = per_attempt_random_seed;
1885 let addresses = either::Either::Right(core::iter::from_fn(move || {
1886 let mut attempts = 0;
1895 loop {
1896 let address = generate_global_temporary_address(
1897 &subnet,
1898 link_layer_addr.as_bytes(),
1899 seed,
1900 &temp_secret_key,
1901 );
1902 seed = seed.wrapping_add(1);
1903
1904 if has_iana_allowed_iid(address.addr().get()) {
1905 break Some((address, config));
1906 }
1907
1908 attempts += 1;
1909 if attempts > MAX_LOCAL_REGEN_ATTEMPTS {
1910 return None;
1911 }
1912 }
1913 }));
1914
1915 (Lifetime::Finite(valid_until), Some(preferred_for_and_regen_at), addresses)
1916 }
1917 }
1918 }
1919 };
1920
1921 let mut local_regen_attempts = 0;
1923 loop {
1924 let Some((address, slaac_config)) = addresses.next() else {
1925 debug!("exhausted possible SLAAC addresses without assigning on device {device_id:?}");
1927 return;
1928 };
1929
1930 let (preferred_lifetime, regen_at) = match preferred_and_regen {
1935 Some(PreferredForAndRegenAt(preferred_for, regen_at)) => {
1936 (PreferredLifetime::preferred_for(now, preferred_for), regen_at)
1937 }
1938 None => (PreferredLifetime::Deprecated, None),
1939 };
1940 let config = Ipv6AddrSlaacConfig { inner: slaac_config, preferred_lifetime };
1941
1942 let res = slaac_addrs.add_addr_sub_and_then(
1945 bindings_ctx,
1946 address,
1947 config,
1948 |SlaacAddressEntryMut { addr_sub, config: _ }, ctx| {
1949 match valid_until {
1954 Lifetime::Finite(valid_until) => {
1955 assert_eq!(
1956 timers.schedule_instant(
1957 ctx,
1958 InnerSlaacTimerId::InvalidateSlaacAddress { addr: addr_sub.addr() },
1959 (),
1960 valid_until,
1961 ),
1962 None
1963 );
1964 }
1965 Lifetime::Infinite => {}
1966 }
1967
1968 let deprecate_timer_id =
1969 InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_sub.addr() };
1970
1971 match preferred_lifetime {
1972 PreferredLifetime::Preferred(Lifetime::Finite(instant)) => {
1973 assert_eq!(
1974 timers.schedule_instant(ctx, deprecate_timer_id, (), instant,),
1975 None
1976 );
1977 }
1978 PreferredLifetime::Preferred(Lifetime::Infinite) => {}
1979 PreferredLifetime::Deprecated => {
1980 assert_eq!(timers.cancel(ctx, &deprecate_timer_id), None);
1981 }
1982 }
1983
1984 match regen_at {
1985 Some(regen_at) => assert_eq!(
1986 timers.schedule_instant(
1987 ctx,
1988 InnerSlaacTimerId::RegenerateTemporaryAddress { addr_subnet: addr_sub },
1989 (),
1990 regen_at,
1991 ),
1992 None
1993 ),
1994 None => (),
1995 }
1996 addr_sub
1997 },
1998 );
1999
2000 match res {
2001 Err(ExistsError) => {
2002 trace!("IPv6 SLAAC address {:?} already exists on device {:?}", address, device_id);
2003
2004 slaac_addrs.counters().generated_slaac_addr_exists.increment();
2007 local_regen_attempts += 1;
2008 if local_regen_attempts > MAX_LOCAL_REGEN_ATTEMPTS {
2009 debug!(
2010 "exceeded max local SLAAC addr generation attempts on device {device_id:?}"
2011 );
2012 return;
2013 }
2014 }
2015 Ok(addr_sub) => {
2016 trace!("receive_ndp_packet: Successfully configured new IPv6 address {:?} on device {:?} via SLAAC", addr_sub, device_id);
2017 break;
2018 }
2019 }
2020 }
2021}
2022
2023#[cfg(any(test, feature = "testutils"))]
2024pub(crate) mod testutil {
2025 use super::*;
2026
2027 use alloc::collections::HashMap;
2028
2029 use net_types::ip::Ipv6;
2030
2031 use crate::internal::device::{IpDeviceBindingsContext, Ipv6DeviceConfigurationContext};
2032
2033 pub fn collect_slaac_timers_integration<CC, BC>(
2036 core_ctx: &mut CC,
2037 device_id: &CC::DeviceId,
2038 ) -> HashMap<InnerSlaacTimerId, BC::Instant>
2039 where
2040 CC: Ipv6DeviceConfigurationContext<BC>,
2041 for<'a> CC::Ipv6DeviceStateCtx<'a>: SlaacContext<BC>,
2042 BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId> + SlaacBindingsContext<CC::DeviceId>,
2043 {
2044 core_ctx.with_ipv6_device_configuration(device_id, |_, mut core_ctx| {
2045 core_ctx.with_slaac_addrs_mut(device_id, |_, state| {
2046 state.timers().iter().map(|(k, (), t)| (*k, *t)).collect::<HashMap<_, _>>()
2047 })
2048 })
2049 }
2050
2051 pub fn calculate_slaac_addr_sub(
2058 subnet: Subnet<Ipv6Addr>,
2059 iid: [u8; 8],
2060 ) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
2061 assert_eq!(subnet.prefix(), 64);
2062 let mut bytes = subnet.network().ipv6_bytes();
2063 bytes[8..].copy_from_slice(&iid);
2064 AddrSubnet::new(Ipv6Addr::from_bytes(bytes), subnet.prefix()).unwrap()
2065 }
2066}
2067
2068#[cfg(test)]
2069mod tests {
2070 use alloc::collections::HashSet;
2071 use alloc::vec;
2072 use core::convert::TryFrom as _;
2073
2074 use net_declare::net::ip_v6;
2075 use netstack3_base::testutil::{
2076 assert_empty, FakeBindingsCtx, FakeCoreCtx, FakeCryptoRng, FakeDeviceId, FakeInstant,
2077 FakeTimerCtxExt as _, FakeWeakDeviceId,
2078 };
2079 use netstack3_base::{CtxPair, IntoCoreTimerCtx};
2080 use test_case::test_case;
2081
2082 use super::*;
2083
2084 fn calculate_stable_slaac_addr_sub_with_opaque_iid(
2091 subnet: Subnet<Ipv6Addr>,
2092 network_interface: impl AsRef<[u8]>,
2093 dad_counter: u8,
2094 ) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
2095 let iid = OpaqueIid::new(
2096 subnet,
2097 network_interface.as_ref(),
2098 None::<&[_]>,
2099 OpaqueIidNonce::DadCounter(dad_counter),
2100 &STABLE_SECRET_KEY,
2101 );
2102 let iid = &iid.to_be_bytes()[..8];
2103 testutil::calculate_slaac_addr_sub(subnet, iid.try_into().unwrap())
2104 }
2105
2106 struct FakeSlaacContext {
2107 config: SlaacConfiguration,
2108 dad_transmits: Option<NonZeroU16>,
2109 retrans_timer: Duration,
2110 slaac_addrs: FakeSlaacAddrs,
2111 slaac_state: SlaacState<FakeBindingsCtxImpl>,
2112 }
2113
2114 type FakeCoreCtxImpl = FakeCoreCtx<FakeSlaacContext, (), FakeDeviceId>;
2115 type FakeBindingsCtxImpl = FakeBindingsCtx<
2116 SlaacTimerId<FakeWeakDeviceId<FakeDeviceId>>,
2117 IpDeviceEvent<FakeDeviceId, Ipv6, FakeInstant>,
2118 (),
2119 (),
2120 >;
2121
2122 struct FakeLinkLayerAddr;
2123
2124 const IID: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
2125
2126 impl Ipv6LinkLayerAddr for FakeLinkLayerAddr {
2127 fn as_bytes(&self) -> &[u8] {
2128 &IID
2129 }
2130
2131 fn eui64_iid(&self) -> [u8; 8] {
2132 IID
2133 }
2134 }
2135
2136 #[derive(Default)]
2137 struct FakeSlaacAddrs {
2138 slaac_addrs: Vec<SlaacAddressEntry<FakeInstant>>,
2139 non_slaac_addrs: Vec<Ipv6DeviceAddr>,
2140 counters: SlaacCounters,
2141 }
2142
2143 impl<'a> CounterContext<SlaacCounters> for &'a mut FakeSlaacAddrs {
2144 fn counters(&self) -> &SlaacCounters {
2145 &self.counters
2146 }
2147 }
2148
2149 impl<'a> SlaacAddresses<FakeBindingsCtxImpl> for &'a mut FakeSlaacAddrs {
2150 fn for_each_addr_mut<F: FnMut(SlaacAddressEntryMut<'_, FakeInstant>)>(
2151 &mut self,
2152 mut cb: F,
2153 ) {
2154 let FakeSlaacAddrs { slaac_addrs, non_slaac_addrs: _, counters: _ } = self;
2155 slaac_addrs.iter_mut().for_each(|SlaacAddressEntry { addr_sub, config }| {
2156 cb(SlaacAddressEntryMut { addr_sub: *addr_sub, config })
2157 })
2158 }
2159
2160 type AddrsIter<'b> =
2161 core::iter::Cloned<core::slice::Iter<'b, SlaacAddressEntry<FakeInstant>>>;
2162 fn with_addrs<O, F: FnOnce(Self::AddrsIter<'_>) -> O>(&mut self, cb: F) -> O {
2163 let FakeSlaacAddrs { slaac_addrs, non_slaac_addrs: _, counters: _ } = self;
2164 cb(slaac_addrs.iter().cloned())
2165 }
2166
2167 fn add_addr_sub_and_then<
2168 O,
2169 F: FnOnce(SlaacAddressEntryMut<'_, FakeInstant>, &mut FakeBindingsCtxImpl) -> O,
2170 >(
2171 &mut self,
2172 bindings_ctx: &mut FakeBindingsCtxImpl,
2173 add_addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
2174 config: Ipv6AddrSlaacConfig<FakeInstant>,
2175 and_then: F,
2176 ) -> Result<O, ExistsError> {
2177 let FakeSlaacAddrs { slaac_addrs, non_slaac_addrs, counters: _ } = self;
2178
2179 if non_slaac_addrs.iter().any(|a| *a == add_addr_sub.addr()) {
2180 return Err(ExistsError);
2181 }
2182
2183 if slaac_addrs.iter_mut().any(|e| e.addr_sub.addr() == add_addr_sub.addr()) {
2184 return Err(ExistsError);
2185 }
2186
2187 slaac_addrs.push(SlaacAddressEntry { addr_sub: add_addr_sub, config });
2188
2189 let SlaacAddressEntry { addr_sub, config } = slaac_addrs.iter_mut().last().unwrap();
2190
2191 Ok(and_then(SlaacAddressEntryMut { addr_sub: *addr_sub, config }, bindings_ctx))
2192 }
2193
2194 fn remove_addr(
2195 &mut self,
2196 _bindings_ctx: &mut FakeBindingsCtxImpl,
2197 addr: &Ipv6DeviceAddr,
2198 ) -> Result<
2199 (AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>, Ipv6AddrSlaacConfig<FakeInstant>),
2200 NotFoundError,
2201 > {
2202 let FakeSlaacAddrs { slaac_addrs, non_slaac_addrs: _, counters: _ } = self;
2203
2204 slaac_addrs
2205 .iter()
2206 .enumerate()
2207 .find_map(|(i, a)| (&a.addr_sub.addr() == addr).then(|| i))
2208 .ok_or(NotFoundError)
2209 .map(|i| {
2210 let SlaacAddressEntry { addr_sub, config } = slaac_addrs.remove(i);
2211 (addr_sub, config)
2212 })
2213 }
2214 }
2215
2216 impl SlaacContext<FakeBindingsCtxImpl> for FakeCoreCtxImpl {
2217 type LinkLayerAddr = FakeLinkLayerAddr;
2218
2219 type SlaacAddrs<'a>
2220 = &'a mut FakeSlaacAddrs
2221 where
2222 FakeCoreCtxImpl: 'a;
2223
2224 fn with_slaac_addrs_mut_and_configs<
2225 O,
2226 F: FnOnce(
2227 &mut Self::SlaacAddrs<'_>,
2228 SlaacConfigAndState<FakeLinkLayerAddr, FakeBindingsCtxImpl>,
2229 &mut SlaacState<FakeBindingsCtxImpl>,
2230 ) -> O,
2231 >(
2232 &mut self,
2233 &FakeDeviceId: &FakeDeviceId,
2234 cb: F,
2235 ) -> O {
2236 let FakeSlaacContext {
2237 config,
2238 dad_transmits,
2239 retrans_timer,
2240 slaac_addrs,
2241 slaac_state,
2242 ..
2243 } = &mut self.state;
2244 let mut slaac_addrs = slaac_addrs;
2245 cb(
2246 &mut slaac_addrs,
2247 SlaacConfigAndState {
2248 config: *config,
2249 dad_transmits: *dad_transmits,
2250 retrans_timer: *retrans_timer,
2251 link_layer_addr: Some(FakeLinkLayerAddr),
2252 temp_secret_key: TEMP_SECRET_KEY,
2253 stable_secret_key: STABLE_SECRET_KEY,
2254 _marker: PhantomData,
2255 },
2256 slaac_state,
2257 )
2258 }
2259 }
2260
2261 impl FakeSlaacContext {
2262 fn iter_slaac_addrs(&self) -> impl Iterator<Item = SlaacAddressEntry<FakeInstant>> + '_ {
2263 self.slaac_addrs.slaac_addrs.iter().cloned()
2264 }
2265 }
2266
2267 fn new_timer_id() -> SlaacTimerId<FakeWeakDeviceId<FakeDeviceId>> {
2268 SlaacTimerId { device_id: FakeWeakDeviceId(FakeDeviceId) }
2269 }
2270
2271 fn new_context(
2272 config: SlaacConfiguration,
2273 slaac_addrs: FakeSlaacAddrs,
2274 dad_transmits: Option<NonZeroU16>,
2275 retrans_timer: Duration,
2276 ) -> CtxPair<FakeCoreCtxImpl, FakeBindingsCtxImpl> {
2277 CtxPair::with_default_bindings_ctx(|bindings_ctx| {
2278 FakeCoreCtxImpl::with_state(FakeSlaacContext {
2279 config,
2280 dad_transmits,
2281 retrans_timer,
2282 slaac_addrs,
2283 slaac_state: SlaacState::new::<_, IntoCoreTimerCtx>(
2284 bindings_ctx,
2285 FakeWeakDeviceId(FakeDeviceId),
2286 ),
2287 })
2288 })
2289 }
2290
2291 impl<Instant> SlaacAddressEntry<Instant> {
2292 fn to_deprecated(self) -> Self {
2293 let Self { addr_sub, config: Ipv6AddrSlaacConfig { inner, preferred_lifetime: _ } } =
2294 self;
2295 Self {
2296 addr_sub,
2297 config: Ipv6AddrSlaacConfig {
2298 inner,
2299 preferred_lifetime: PreferredLifetime::Deprecated,
2300 },
2301 }
2302 }
2303 }
2304
2305 #[test_case(ip_v6!("1:2:3:4::"), false; "subnet-router anycast")]
2306 #[test_case(ip_v6!("::1"), true; "allowed 1")]
2307 #[test_case(ip_v6!("1:2:3:4::1"), true; "allowed 2")]
2308 #[test_case(ip_v6!("4:4:4:4:0200:5eff:fe00:1"), false; "first ethernet block")]
2309 #[test_case(ip_v6!("1:1:1:1:0200:5eff:fe00:5213"), false; "proxy mobile")]
2310 #[test_case(ip_v6!("8:8:8:8:0200:5eff:fe00:8000"), false; "second ethernet block")]
2311 #[test_case(ip_v6!("a:a:a:a:fdff:ffff:ffff:ffaa"), false; "subnet anycast")]
2312 #[test_case(ip_v6!("c:c:c:c:fe00::"), true; "allowed 3")]
2313 fn test_has_iana_allowed_iid(addr: Ipv6Addr, expect_allowed: bool) {
2314 assert_eq!(has_iana_allowed_iid(addr), expect_allowed);
2315 }
2316
2317 const DEFAULT_RETRANS_TIMER: Duration = Duration::from_secs(1);
2318 const SUBNET: Subnet<Ipv6Addr> = net_declare::net_subnet_v6!("200a::/64");
2319
2320 #[test_case(0, 0, true; "zero lifetimes")]
2321 #[test_case(2, 1, true; "preferred larger than valid")]
2322 #[test_case(1, 2, false; "disabled")]
2323 fn dont_generate_address(
2324 preferred_lifetime_secs: u32,
2325 valid_lifetime_secs: u32,
2326 enable_stable_addresses: bool,
2327 ) {
2328 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2329 SlaacConfiguration {
2330 stable_address_configuration: if enable_stable_addresses {
2331 StableSlaacAddressConfiguration::ENABLED_WITH_EUI64
2332 } else {
2333 StableSlaacAddressConfiguration::Disabled
2334 },
2335 ..Default::default()
2336 },
2337 Default::default(),
2338 None,
2339 DEFAULT_RETRANS_TIMER,
2340 );
2341
2342 SlaacHandler::apply_slaac_update(
2343 &mut core_ctx,
2344 &mut bindings_ctx,
2345 &FakeDeviceId,
2346 SUBNET,
2347 NonZeroNdpLifetime::from_u32_with_infinite(preferred_lifetime_secs),
2348 NonZeroNdpLifetime::from_u32_with_infinite(valid_lifetime_secs),
2349 );
2350 assert_empty(core_ctx.state.iter_slaac_addrs());
2351 bindings_ctx.timers.assert_no_timers_installed();
2352 }
2353
2354 #[test_case(0, false; "deprecated EUI64")]
2355 #[test_case(1, false; "preferred EUI64")]
2356 #[test_case(0, true; "deprecated opaque")]
2357 #[test_case(1, true; "preferred opaque")]
2358 fn generate_stable_address(preferred_lifetime_secs: u32, opaque_iids: bool) {
2359 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2360 SlaacConfiguration {
2361 stable_address_configuration: if opaque_iids {
2362 StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS
2363 } else {
2364 StableSlaacAddressConfiguration::ENABLED_WITH_EUI64
2365 },
2366 ..Default::default()
2367 },
2368 Default::default(),
2369 None,
2370 DEFAULT_RETRANS_TIMER,
2371 );
2372
2373 let valid_lifetime_secs = preferred_lifetime_secs + 1;
2374 let addr_sub = if opaque_iids {
2375 calculate_stable_slaac_addr_sub_with_opaque_iid(SUBNET, IID, 0)
2376 } else {
2377 testutil::calculate_slaac_addr_sub(SUBNET, IID)
2378 };
2379
2380 SlaacHandler::apply_slaac_update(
2382 &mut core_ctx,
2383 &mut bindings_ctx,
2384 &FakeDeviceId,
2385 SUBNET,
2386 NonZeroNdpLifetime::from_u32_with_infinite(preferred_lifetime_secs),
2387 NonZeroNdpLifetime::from_u32_with_infinite(valid_lifetime_secs),
2388 );
2389 let address_created_deprecated = preferred_lifetime_secs == 0;
2390 let now = bindings_ctx.now();
2391 let valid_until = now + Duration::from_secs(valid_lifetime_secs.into());
2392 let preferred_lifetime = match preferred_lifetime_secs {
2393 0 => PreferredLifetime::Deprecated,
2394 secs => PreferredLifetime::preferred_until(now + Duration::from_secs(secs.into())),
2395 };
2396 let inner = SlaacConfig::Stable {
2397 valid_until: Lifetime::Finite(valid_until),
2398 creation_time: bindings_ctx.now(),
2399 regen_counter: 0,
2400 dad_counter: 0,
2401 };
2402 let entry = SlaacAddressEntry {
2403 addr_sub,
2404 config: Ipv6AddrSlaacConfig { inner, preferred_lifetime },
2405 };
2406 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry],);
2407 let deprecate_timer_id = InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_sub.addr() };
2408 let invalidate_timer_id =
2409 InnerSlaacTimerId::InvalidateSlaacAddress { addr: addr_sub.addr() };
2410 if !address_created_deprecated {
2411 core_ctx.state.slaac_state.timers.assert_timers([
2412 (deprecate_timer_id, (), now + Duration::from_secs(preferred_lifetime_secs.into())),
2413 (invalidate_timer_id, (), valid_until),
2414 ]);
2415
2416 assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()));
2418 let entry = SlaacAddressEntry {
2419 addr_sub,
2420 config: Ipv6AddrSlaacConfig {
2421 inner,
2422 preferred_lifetime: PreferredLifetime::Deprecated,
2423 },
2424 };
2425 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2426 }
2427 core_ctx.state.slaac_state.timers.assert_timers([(invalidate_timer_id, (), valid_until)]);
2428
2429 assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()));
2431 assert_empty(core_ctx.state.iter_slaac_addrs());
2432 bindings_ctx.timers.assert_no_timers_installed();
2433 }
2434
2435 enum StableAddress {
2436 Global,
2437 LinkLocal,
2438 }
2439
2440 #[test_case(StableAddress::Global, true; "opaque global")]
2441 #[test_case(StableAddress::Global, false; "EUI64-based global")]
2442 #[test_case(StableAddress::LinkLocal, true; "opaque link-local")]
2443 #[test_case(StableAddress::LinkLocal, false; "EUI64-based link-local")]
2444 fn stable_address_conflict(address_type: StableAddress, opaque_iids: bool) {
2445 let subnet = match address_type {
2446 StableAddress::Global => SUBNET,
2447 StableAddress::LinkLocal => {
2448 Subnet::new(Ipv6::LINK_LOCAL_UNICAST_SUBNET.network(), REQUIRED_PREFIX_BITS)
2449 .unwrap()
2450 }
2451 };
2452 let addr_sub = if opaque_iids {
2453 let dad_counter = 0;
2454 calculate_stable_slaac_addr_sub_with_opaque_iid(subnet, IID, dad_counter)
2455 } else {
2456 testutil::calculate_slaac_addr_sub(subnet, IID)
2457 };
2458
2459 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2460 SlaacConfiguration {
2461 stable_address_configuration: if opaque_iids {
2462 StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS
2463 } else {
2464 StableSlaacAddressConfiguration::ENABLED_WITH_EUI64
2465 },
2466 ..Default::default()
2467 },
2468 FakeSlaacAddrs {
2469 slaac_addrs: Default::default(),
2470 non_slaac_addrs: vec![addr_sub.addr()],
2473 counters: Default::default(),
2474 },
2475 None,
2476 DEFAULT_RETRANS_TIMER,
2477 );
2478
2479 const LIFETIME_SECS: u32 = 1;
2480
2481 match address_type {
2483 StableAddress::Global => {
2484 SlaacHandler::apply_slaac_update(
2485 &mut core_ctx,
2486 &mut bindings_ctx,
2487 &FakeDeviceId,
2488 SUBNET,
2489 NonZeroNdpLifetime::from_u32_with_infinite(LIFETIME_SECS),
2490 NonZeroNdpLifetime::from_u32_with_infinite(LIFETIME_SECS),
2491 );
2492 }
2493 StableAddress::LinkLocal => {
2494 SlaacHandler::generate_link_local_address(
2495 &mut core_ctx,
2496 &mut bindings_ctx,
2497 &FakeDeviceId,
2498 );
2499 }
2500 }
2501
2502 if !opaque_iids {
2506 assert_empty(core_ctx.state.iter_slaac_addrs());
2507 bindings_ctx.timers.assert_no_timers_installed();
2508 return;
2509 }
2510
2511 let dad_counter = 1;
2515 let addr_sub = calculate_stable_slaac_addr_sub_with_opaque_iid(subnet, &IID, dad_counter);
2516 match address_type {
2517 StableAddress::Global => {
2518 let now = bindings_ctx.now();
2519 let valid_until = now + Duration::from_secs(LIFETIME_SECS.into());
2520 let entry = SlaacAddressEntry {
2521 addr_sub,
2522 config: Ipv6AddrSlaacConfig {
2523 inner: SlaacConfig::Stable {
2524 valid_until: Lifetime::Finite(valid_until),
2525 creation_time: bindings_ctx.now(),
2526 regen_counter: 1,
2527 dad_counter: 0,
2528 },
2529 preferred_lifetime: PreferredLifetime::preferred_until(valid_until),
2530 },
2531 };
2532 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2533 let deprecate_timer_id =
2534 InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_sub.addr() };
2535 let invalidate_timer_id =
2536 InnerSlaacTimerId::InvalidateSlaacAddress { addr: addr_sub.addr() };
2537 core_ctx.state.slaac_state.timers.assert_timers([
2538 (deprecate_timer_id, (), valid_until),
2539 (invalidate_timer_id, (), valid_until),
2540 ]);
2541 }
2542 StableAddress::LinkLocal => {
2543 let entry = SlaacAddressEntry {
2544 addr_sub,
2545 config: Ipv6AddrSlaacConfig {
2546 inner: SlaacConfig::Stable {
2547 valid_until: Lifetime::Infinite,
2548 creation_time: bindings_ctx.now(),
2549 regen_counter: 1,
2550 dad_counter: 0,
2551 },
2552 preferred_lifetime: PreferredLifetime::preferred_forever(),
2553 },
2554 };
2555 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2556 bindings_ctx.timers.assert_no_timers_installed();
2557 }
2558 };
2559 }
2560
2561 #[test]
2562 fn temporary_address_conflict() {
2563 const TEMP_IDGEN_RETRIES: u8 = 0;
2564
2565 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2566 SlaacConfiguration {
2567 temporary_address_configuration: TemporarySlaacAddressConfiguration::Enabled {
2568 temp_valid_lifetime: ONE_HOUR,
2569 temp_preferred_lifetime: ONE_HOUR,
2570 temp_idgen_retries: TEMP_IDGEN_RETRIES,
2571 },
2572 ..Default::default()
2573 },
2574 FakeSlaacAddrs::default(),
2575 None,
2576 DEFAULT_RETRANS_TIMER,
2577 );
2578
2579 let mut dup_rng = bindings_ctx.rng().deep_clone();
2582 let seed = dup_rng.gen();
2583 let first_attempt =
2584 generate_global_temporary_address(&SUBNET, &IID, seed, &TEMP_SECRET_KEY);
2585 core_ctx.state.slaac_addrs.non_slaac_addrs = vec![first_attempt.addr()];
2586
2587 SlaacHandler::apply_slaac_update(
2589 &mut core_ctx,
2590 &mut bindings_ctx,
2591 &FakeDeviceId,
2592 SUBNET,
2593 Some(NonZeroNdpLifetime::Finite(ONE_HOUR)),
2594 Some(NonZeroNdpLifetime::Finite(ONE_HOUR)),
2595 );
2596
2597 let seed = seed.wrapping_add(1);
2600 let addr_sub = generate_global_temporary_address(&SUBNET, &IID, seed, &TEMP_SECRET_KEY);
2601 assert_ne!(addr_sub, first_attempt);
2602 let regen_advance =
2603 regen_advance(TEMP_IDGEN_RETRIES, DEFAULT_RETRANS_TIMER, 0);
2604 let desync_factor = desync_factor(&mut dup_rng, ONE_HOUR, regen_advance).unwrap();
2605 let preferred_until = {
2606 let d = bindings_ctx.now() + ONE_HOUR.into();
2607 d - desync_factor
2608 };
2609 let entry = SlaacAddressEntry {
2610 addr_sub,
2611 config: Ipv6AddrSlaacConfig {
2612 inner: SlaacConfig::Temporary(TemporarySlaacConfig {
2613 valid_until: bindings_ctx.now() + ONE_HOUR.into(),
2614 desync_factor,
2615 creation_time: bindings_ctx.now(),
2616 dad_counter: 0,
2617 }),
2618 preferred_lifetime: PreferredLifetime::preferred_until(preferred_until),
2619 },
2620 };
2621 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2622 }
2623
2624 #[test]
2625 fn local_regen_limit() {
2626 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2627 SlaacConfiguration {
2628 stable_address_configuration:
2629 StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS,
2630 temporary_address_configuration: TemporarySlaacAddressConfiguration::Enabled {
2631 temp_valid_lifetime: ONE_HOUR,
2632 temp_preferred_lifetime: ONE_HOUR,
2633 temp_idgen_retries: 0,
2634 },
2635 ..Default::default()
2636 },
2637 FakeSlaacAddrs::default(),
2638 None,
2639 DEFAULT_RETRANS_TIMER,
2640 );
2641
2642 let mut dup_rng = bindings_ctx.rng().deep_clone();
2643 let mut seed = dup_rng.gen();
2644
2645 let link_local_subnet =
2646 Subnet::new(Ipv6::LINK_LOCAL_UNICAST_SUBNET.network(), REQUIRED_PREFIX_BITS).unwrap();
2647
2648 for attempt in 0..=MAX_LOCAL_REGEN_ATTEMPTS {
2651 let link_local =
2652 calculate_stable_slaac_addr_sub_with_opaque_iid(link_local_subnet, IID, attempt);
2653
2654 let stable = calculate_stable_slaac_addr_sub_with_opaque_iid(SUBNET, IID, attempt);
2655
2656 let temporary =
2657 generate_global_temporary_address(&SUBNET, &IID, seed, &TEMP_SECRET_KEY);
2658 seed = seed.wrapping_add(1);
2659
2660 core_ctx.state.slaac_addrs.non_slaac_addrs.extend(&[
2661 link_local.addr(),
2662 stable.addr(),
2663 temporary.addr(),
2664 ]);
2665 }
2666
2667 SlaacHandler::apply_slaac_update(
2670 &mut core_ctx,
2671 &mut bindings_ctx,
2672 &FakeDeviceId,
2673 SUBNET,
2674 Some(NonZeroNdpLifetime::Finite(ONE_HOUR)),
2675 Some(NonZeroNdpLifetime::Finite(ONE_HOUR)),
2676 );
2677 SlaacHandler::generate_link_local_address(&mut core_ctx, &mut bindings_ctx, &FakeDeviceId);
2678
2679 assert_empty(core_ctx.state.iter_slaac_addrs());
2682 bindings_ctx.timers.assert_no_timers_installed();
2683 }
2684
2685 const LIFETIME: NonZeroNdpLifetime =
2686 NonZeroNdpLifetime::Finite(NonZeroDuration::new(Duration::from_secs(1)).unwrap());
2687
2688 #[test_case(AddressRemovedReason::Manual, LIFETIME; "manual")]
2689 #[test_case(AddressRemovedReason::DadFailed, LIFETIME; "dad failed")]
2690 #[test_case(
2691 AddressRemovedReason::DadFailed,
2692 NonZeroNdpLifetime::Infinite;
2693 "dad failed infinite lifetime"
2694 )]
2695 fn remove_stable_address(reason: AddressRemovedReason, lifetime: NonZeroNdpLifetime) {
2696 let addr_sub =
2697 calculate_stable_slaac_addr_sub_with_opaque_iid(SUBNET, IID, 0);
2698
2699 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2700 SlaacConfiguration {
2701 stable_address_configuration:
2702 StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS,
2703 ..Default::default()
2704 },
2705 Default::default(),
2706 None,
2707 DEFAULT_RETRANS_TIMER,
2708 );
2709
2710 SlaacHandler::apply_slaac_update(
2712 &mut core_ctx,
2713 &mut bindings_ctx,
2714 &FakeDeviceId,
2715 SUBNET,
2716 Some(lifetime),
2717 Some(lifetime),
2718 );
2719 let now = bindings_ctx.now();
2720 let valid_until = Lifetime::from_ndp(now, lifetime);
2721 let preferred_lifetime = PreferredLifetime::preferred_for(now, lifetime);
2722 let entry = SlaacAddressEntry {
2723 addr_sub,
2724 config: Ipv6AddrSlaacConfig {
2725 inner: SlaacConfig::Stable {
2726 valid_until,
2727 creation_time: bindings_ctx.now(),
2728 regen_counter: 0,
2729 dad_counter: 0,
2730 },
2731 preferred_lifetime,
2732 },
2733 };
2734 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2735
2736 let assert_expected_timers = |slaac_state: &SlaacState<_>, addr| {
2737 let expected_timers = match lifetime {
2738 NonZeroNdpLifetime::Infinite => vec![],
2739 NonZeroNdpLifetime::Finite(duration) => {
2740 let deprecate_timer_id = InnerSlaacTimerId::DeprecateSlaacAddress { addr };
2741 let invalidate_timer_id = InnerSlaacTimerId::InvalidateSlaacAddress { addr };
2742 let instant = now + duration.get();
2743 vec![(deprecate_timer_id, (), instant), (invalidate_timer_id, (), instant)]
2744 }
2745 };
2746 slaac_state.timers.assert_timers(expected_timers);
2747 };
2748 assert_expected_timers(&core_ctx.state.slaac_state, addr_sub.addr());
2749
2750 let config = {
2752 let SlaacAddressEntry { addr_sub: got_addr_sub, config } =
2753 core_ctx.state.slaac_addrs.slaac_addrs.remove(0);
2754 assert_eq!(addr_sub, got_addr_sub);
2755 assert_eq!(config.preferred_lifetime, preferred_lifetime);
2756 config
2757 };
2758 SlaacHandler::on_address_removed(
2759 &mut core_ctx,
2760 &mut bindings_ctx,
2761 &FakeDeviceId,
2762 addr_sub,
2763 config,
2764 reason,
2765 );
2766 match reason {
2767 AddressRemovedReason::Manual => {
2768 bindings_ctx.timers.assert_no_timers_installed();
2770 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), []);
2771 return;
2772 }
2773 AddressRemovedReason::DadFailed => {}
2774 }
2775
2776 let addr_sub =
2779 calculate_stable_slaac_addr_sub_with_opaque_iid(SUBNET, IID, 1);
2780 let entry = SlaacAddressEntry {
2781 addr_sub,
2782 config: Ipv6AddrSlaacConfig {
2783 inner: SlaacConfig::Stable {
2784 valid_until,
2785 creation_time: now,
2786 regen_counter: 0,
2787 dad_counter: 1,
2788 },
2789 preferred_lifetime,
2790 },
2791 };
2792 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2793 assert_expected_timers(&core_ctx.state.slaac_state, addr_sub.addr());
2794 }
2795
2796 #[test]
2797 fn stable_addr_regen_counters() {
2798 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2802 SlaacConfiguration {
2803 stable_address_configuration:
2804 StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS,
2805 ..Default::default()
2806 },
2807 Default::default(),
2808 None,
2809 DEFAULT_RETRANS_TIMER,
2810 );
2811
2812 const LOCAL_REGEN_ATTEMPTS: u8 = 3;
2813 const DAD_FAILURE_REGEN_ATTEMPTS: u8 = 3;
2814
2815 let now = bindings_ctx.now();
2816 core_ctx.with_slaac_addrs_mut_and_configs(&FakeDeviceId, |addrs, config, slaac_state| {
2817 for regen_count in 0..LOCAL_REGEN_ATTEMPTS {
2818 for dad_count in 0..DAD_FAILURE_REGEN_ATTEMPTS {
2819 add_slaac_addr_sub::<_, FakeCoreCtx<_, _, _>>(
2820 &mut bindings_ctx,
2821 &FakeDeviceId,
2822 addrs,
2823 &config,
2824 slaac_state,
2825 now,
2826 SlaacInitConfig::Stable { regen_count, dad_count },
2827 NonZeroNdpLifetime::Infinite,
2828 Some(NonZeroNdpLifetime::Infinite),
2829 &SUBNET,
2830 );
2831 }
2832 }
2833 });
2834 let unique_addrs = core_ctx
2835 .state
2836 .iter_slaac_addrs()
2837 .map(|entry| entry.addr_sub.addr())
2838 .collect::<HashSet<_>>();
2839 assert_eq!(
2840 unique_addrs.len(),
2841 usize::from(LOCAL_REGEN_ATTEMPTS * DAD_FAILURE_REGEN_ATTEMPTS)
2842 );
2843 }
2844
2845 struct RefreshStableAddressTimersTest {
2846 orig_pl_secs: u32,
2847 orig_vl_secs: u32,
2848 new_pl_secs: u32,
2849 new_vl_secs: u32,
2850 effective_new_vl_secs: u32,
2851 }
2852
2853 const ONE_HOUR_AS_SECS: u32 = 60 * 60;
2854 const TWO_HOURS_AS_SECS: u32 = ONE_HOUR_AS_SECS * 2;
2855 const THREE_HOURS_AS_SECS: u32 = ONE_HOUR_AS_SECS * 3;
2856 const FOUR_HOURS_AS_SECS: u32 = ONE_HOUR_AS_SECS * 4;
2857 const INFINITE_LIFETIME: u32 = u32::MAX;
2858 const MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS: u32 =
2859 MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE.get().as_secs() as u32;
2860 #[test_case(RefreshStableAddressTimersTest {
2861 orig_pl_secs: 1,
2862 orig_vl_secs: 1,
2863 new_pl_secs: 1,
2864 new_vl_secs: 1,
2865 effective_new_vl_secs: 1,
2866 }; "do nothing")]
2867 #[test_case(RefreshStableAddressTimersTest {
2868 orig_pl_secs: 1,
2869 orig_vl_secs: 1,
2870 new_pl_secs: 2,
2871 new_vl_secs: 2,
2872 effective_new_vl_secs: 2,
2873 }; "increase lifetimes")]
2874 #[test_case(RefreshStableAddressTimersTest {
2875 orig_pl_secs: 1,
2876 orig_vl_secs: 1,
2877 new_pl_secs: 0,
2878 new_vl_secs: 1,
2879 effective_new_vl_secs: 1,
2880 }; "deprecate address only")]
2881 #[test_case(RefreshStableAddressTimersTest {
2882 orig_pl_secs: 0,
2883 orig_vl_secs: 1,
2884 new_pl_secs: 1,
2885 new_vl_secs: 1,
2886 effective_new_vl_secs: 1,
2887 }; "undeprecate address")]
2888 #[test_case(RefreshStableAddressTimersTest {
2889 orig_pl_secs: 1,
2890 orig_vl_secs: 1,
2891 new_pl_secs: 0,
2892 new_vl_secs: 0,
2893 effective_new_vl_secs: 1,
2894 }; "deprecate address only with new valid lifetime of zero")]
2895 #[test_case(RefreshStableAddressTimersTest {
2896 orig_pl_secs: ONE_HOUR_AS_SECS,
2897 orig_vl_secs: ONE_HOUR_AS_SECS,
2898 new_pl_secs: ONE_HOUR_AS_SECS - 1,
2899 new_vl_secs: ONE_HOUR_AS_SECS - 1,
2900 effective_new_vl_secs: ONE_HOUR_AS_SECS,
2901 }; "decrease preferred lifetime and ignore new valid lifetime if less than 2 hours and remaining lifetime")]
2902 #[test_case(RefreshStableAddressTimersTest {
2903 orig_pl_secs: THREE_HOURS_AS_SECS,
2904 orig_vl_secs: THREE_HOURS_AS_SECS,
2905 new_pl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS - 1,
2906 new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS - 1,
2907 effective_new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS,
2908 }; "deprecate address only and bring valid lifetime down to 2 hours at max")]
2909 #[test_case(RefreshStableAddressTimersTest {
2910 orig_pl_secs: ONE_HOUR_AS_SECS - 1,
2911 orig_vl_secs: ONE_HOUR_AS_SECS - 1,
2912 new_pl_secs: ONE_HOUR_AS_SECS - 1,
2913 new_vl_secs: ONE_HOUR_AS_SECS,
2914 effective_new_vl_secs: ONE_HOUR_AS_SECS,
2915 }; "increase valid lifetime if more than remaining valid lifetime")]
2916 #[test_case(RefreshStableAddressTimersTest {
2917 orig_pl_secs: INFINITE_LIFETIME,
2918 orig_vl_secs: INFINITE_LIFETIME,
2919 new_pl_secs: INFINITE_LIFETIME,
2920 new_vl_secs: INFINITE_LIFETIME,
2921 effective_new_vl_secs: INFINITE_LIFETIME,
2922 }; "infinite lifetimes")]
2923 #[test_case(RefreshStableAddressTimersTest {
2924 orig_pl_secs: ONE_HOUR_AS_SECS,
2925 orig_vl_secs: TWO_HOURS_AS_SECS,
2926 new_pl_secs: TWO_HOURS_AS_SECS,
2927 new_vl_secs: INFINITE_LIFETIME,
2928 effective_new_vl_secs: INFINITE_LIFETIME,
2929 }; "update valid lifetime from finite to infinite")]
2930 #[test_case(RefreshStableAddressTimersTest {
2931 orig_pl_secs: ONE_HOUR_AS_SECS,
2932 orig_vl_secs: TWO_HOURS_AS_SECS,
2933 new_pl_secs: INFINITE_LIFETIME,
2934 new_vl_secs: INFINITE_LIFETIME,
2935 effective_new_vl_secs: INFINITE_LIFETIME,
2936 }; "update both lifetimes from finite to infinite")]
2937 #[test_case(RefreshStableAddressTimersTest {
2938 orig_pl_secs: TWO_HOURS_AS_SECS,
2939 orig_vl_secs: INFINITE_LIFETIME,
2940 new_pl_secs: ONE_HOUR_AS_SECS,
2941 new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS - 1,
2942 effective_new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS,
2943 }; "update valid lifetime from infinite to finite")]
2944 #[test_case(RefreshStableAddressTimersTest {
2945 orig_pl_secs: INFINITE_LIFETIME,
2946 orig_vl_secs: INFINITE_LIFETIME,
2947 new_pl_secs: ONE_HOUR_AS_SECS,
2948 new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS - 1,
2949 effective_new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS,
2950 }; "update both lifetimes from infinite to finite")]
2951 fn stable_address_timers(
2952 RefreshStableAddressTimersTest {
2953 orig_pl_secs,
2954 orig_vl_secs,
2955 new_pl_secs,
2956 new_vl_secs,
2957 effective_new_vl_secs,
2958 }: RefreshStableAddressTimersTest,
2959 ) {
2960 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2961 SlaacConfiguration {
2962 stable_address_configuration: StableSlaacAddressConfiguration::ENABLED_WITH_EUI64,
2963 ..Default::default()
2964 },
2965 Default::default(),
2966 None,
2967 DEFAULT_RETRANS_TIMER,
2968 );
2969
2970 let addr_sub = testutil::calculate_slaac_addr_sub(SUBNET, IID);
2971
2972 let deprecate_timer_id = InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_sub.addr() };
2973 let invalidate_timer_id =
2974 InnerSlaacTimerId::InvalidateSlaacAddress { addr: addr_sub.addr() };
2975
2976 let ndp_pl = NonZeroNdpLifetime::from_u32_with_infinite(orig_pl_secs);
2978 let ndp_vl = NonZeroNdpLifetime::from_u32_with_infinite(orig_vl_secs);
2979 SlaacHandler::apply_slaac_update(
2980 &mut core_ctx,
2981 &mut bindings_ctx,
2982 &FakeDeviceId,
2983 SUBNET,
2984 ndp_pl,
2985 ndp_vl,
2986 );
2987 let now = bindings_ctx.now();
2988 let mut expected_timers = Vec::new();
2989 let valid_until = match ndp_vl.expect("this test expects to create an address") {
2990 NonZeroNdpLifetime::Finite(d) => {
2991 let valid_until = now + d.get();
2992 expected_timers.push((invalidate_timer_id, (), valid_until));
2993 Lifetime::Finite(valid_until)
2994 }
2995 NonZeroNdpLifetime::Infinite => Lifetime::Infinite,
2996 };
2997 match ndp_pl {
2998 None | Some(NonZeroNdpLifetime::Infinite) => {}
2999 Some(NonZeroNdpLifetime::Finite(d)) => {
3000 expected_timers.push((deprecate_timer_id, (), now + d.get()))
3001 }
3002 }
3003 let entry = SlaacAddressEntry {
3004 addr_sub,
3005 config: Ipv6AddrSlaacConfig {
3006 inner: SlaacConfig::Stable {
3007 valid_until,
3008 creation_time: bindings_ctx.now(),
3009 regen_counter: 0,
3010 dad_counter: 0,
3011 },
3012 preferred_lifetime: PreferredLifetime::maybe_preferred_for(now, ndp_pl),
3013 },
3014 };
3015 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
3016 core_ctx.state.slaac_state.timers.assert_timers(expected_timers);
3017
3018 let ndp_pl = NonZeroNdpLifetime::from_u32_with_infinite(new_pl_secs);
3020 SlaacHandler::apply_slaac_update(
3021 &mut core_ctx,
3022 &mut bindings_ctx,
3023 &FakeDeviceId,
3024 SUBNET,
3025 ndp_pl,
3026 NonZeroNdpLifetime::from_u32_with_infinite(new_vl_secs),
3027 );
3028 let mut expected_timers = Vec::new();
3029 let valid_until = match NonZeroNdpLifetime::from_u32_with_infinite(effective_new_vl_secs)
3030 .expect("this test expects to keep the address")
3031 {
3032 NonZeroNdpLifetime::Finite(d) => {
3033 let valid_until = now + d.get();
3034 expected_timers.push((invalidate_timer_id, (), valid_until));
3035 Lifetime::Finite(valid_until)
3036 }
3037 NonZeroNdpLifetime::Infinite => Lifetime::Infinite,
3038 };
3039 match ndp_pl {
3040 None | Some(NonZeroNdpLifetime::Infinite) => {}
3041 Some(NonZeroNdpLifetime::Finite(d)) => {
3042 expected_timers.push((deprecate_timer_id, (), now + d.get()))
3043 }
3044 }
3045 let entry = SlaacAddressEntry {
3046 config: Ipv6AddrSlaacConfig {
3047 inner: SlaacConfig::Stable {
3048 valid_until,
3049 creation_time: bindings_ctx.now(),
3050 regen_counter: 0,
3051 dad_counter: 0,
3052 },
3053 preferred_lifetime: PreferredLifetime::maybe_preferred_for(now, ndp_pl),
3054 },
3055 ..entry
3056 };
3057 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
3058 core_ctx.state.slaac_state.timers.assert_timers(expected_timers);
3059 }
3060
3061 const TEMP_SECRET_KEY: IidSecret = IidSecret::ALL_ONES;
3062 const STABLE_SECRET_KEY: IidSecret = IidSecret::ALL_TWOS;
3063
3064 const ONE_HOUR: NonZeroDuration = NonZeroDuration::from_secs(ONE_HOUR_AS_SECS as u64).unwrap();
3065
3066 struct DontGenerateTemporaryAddressTest {
3067 preferred_lifetime_config: NonZeroDuration,
3068 preferred_lifetime_secs: u32,
3069 valid_lifetime_secs: u32,
3070 temp_idgen_retries: u8,
3071 dad_transmits: u16,
3072 retrans_timer: Duration,
3073 enable: bool,
3074 }
3075
3076 impl DontGenerateTemporaryAddressTest {
3077 fn with_pl_less_than_regen_advance(
3078 dad_transmits: u16,
3079 retrans_timer: Duration,
3080 temp_idgen_retries: u8,
3081 ) -> Self {
3082 DontGenerateTemporaryAddressTest {
3083 preferred_lifetime_config: ONE_HOUR,
3084 preferred_lifetime_secs: u32::try_from(
3085 (SLAAC_MIN_REGEN_ADVANCE.get()
3086 + (u32::from(temp_idgen_retries)
3087 * u32::from(dad_transmits)
3088 * retrans_timer))
3089 .as_secs(),
3090 )
3091 .unwrap()
3092 - 1,
3093 valid_lifetime_secs: TWO_HOURS_AS_SECS,
3094 temp_idgen_retries,
3095 dad_transmits,
3096 retrans_timer,
3097 enable: true,
3098 }
3099 }
3100 }
3101
3102 #[test_case(DontGenerateTemporaryAddressTest {
3103 preferred_lifetime_config: ONE_HOUR,
3104 preferred_lifetime_secs: ONE_HOUR_AS_SECS,
3105 valid_lifetime_secs: TWO_HOURS_AS_SECS,
3106 temp_idgen_retries: 0,
3107 dad_transmits: 0,
3108 retrans_timer: DEFAULT_RETRANS_TIMER,
3109 enable: false,
3110 }; "disabled")]
3111 #[test_case(DontGenerateTemporaryAddressTest{
3112 preferred_lifetime_config: ONE_HOUR,
3113 preferred_lifetime_secs: 0,
3114 valid_lifetime_secs: 0,
3115 temp_idgen_retries: 0,
3116 dad_transmits: 0,
3117 retrans_timer: DEFAULT_RETRANS_TIMER,
3118 enable: true,
3119 }; "zero lifetimes")]
3120 #[test_case(DontGenerateTemporaryAddressTest {
3121 preferred_lifetime_config: ONE_HOUR,
3122 preferred_lifetime_secs: TWO_HOURS_AS_SECS,
3123 valid_lifetime_secs: ONE_HOUR_AS_SECS,
3124 temp_idgen_retries: 0,
3125 dad_transmits: 0,
3126 retrans_timer: DEFAULT_RETRANS_TIMER,
3127 enable: true,
3128 }; "preferred larger than valid")]
3129 #[test_case(DontGenerateTemporaryAddressTest {
3130 preferred_lifetime_config: ONE_HOUR,
3131 preferred_lifetime_secs: 0,
3132 valid_lifetime_secs: TWO_HOURS_AS_SECS,
3133 temp_idgen_retries: 0,
3134 dad_transmits: 0,
3135 retrans_timer: DEFAULT_RETRANS_TIMER,
3136 enable: true,
3137 }; "not preferred")]
3138 #[test_case(DontGenerateTemporaryAddressTest::with_pl_less_than_regen_advance(
3139 0 ,
3140 DEFAULT_RETRANS_TIMER ,
3141 0 ,
3142 ); "preferred lifetime less than regen advance with no DAD transmits")]
3143 #[test_case(DontGenerateTemporaryAddressTest::with_pl_less_than_regen_advance(
3144 1 ,
3145 DEFAULT_RETRANS_TIMER ,
3146 0 ,
3147 ); "preferred lifetime less than regen advance with DAD transmits")]
3148 #[test_case(DontGenerateTemporaryAddressTest::with_pl_less_than_regen_advance(
3149 1 ,
3150 DEFAULT_RETRANS_TIMER ,
3151 1 ,
3152 ); "preferred lifetime less than regen advance with DAD transmits and retries")]
3153 #[test_case(DontGenerateTemporaryAddressTest::with_pl_less_than_regen_advance(
3154 2 ,
3155 DEFAULT_RETRANS_TIMER + Duration::from_secs(1) ,
3156 3 ,
3157 ); "preferred lifetime less than regen advance with multiple DAD transmits and multiple retries")]
3158 #[test_case(DontGenerateTemporaryAddressTest {
3159 preferred_lifetime_config: SLAAC_MIN_REGEN_ADVANCE,
3160 preferred_lifetime_secs: ONE_HOUR_AS_SECS,
3161 valid_lifetime_secs: TWO_HOURS_AS_SECS,
3162 temp_idgen_retries: 1,
3163 dad_transmits: 1,
3164 retrans_timer: DEFAULT_RETRANS_TIMER,
3165 enable: true,
3166 }; "configured preferred lifetime less than regen advance")]
3167 fn dont_generate_temporary_address(
3168 DontGenerateTemporaryAddressTest {
3169 preferred_lifetime_config,
3170 preferred_lifetime_secs,
3171 valid_lifetime_secs,
3172 temp_idgen_retries,
3173 dad_transmits,
3174 retrans_timer,
3175 enable,
3176 }: DontGenerateTemporaryAddressTest,
3177 ) {
3178 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
3179 SlaacConfiguration {
3180 temporary_address_configuration: if enable {
3181 TemporarySlaacAddressConfiguration::Enabled {
3182 temp_valid_lifetime: ONE_HOUR,
3183 temp_preferred_lifetime: preferred_lifetime_config,
3184 temp_idgen_retries,
3185 }
3186 } else {
3187 TemporarySlaacAddressConfiguration::Disabled
3188 },
3189 ..Default::default()
3190 },
3191 Default::default(),
3192 NonZeroU16::new(dad_transmits),
3193 retrans_timer,
3194 );
3195
3196 SlaacHandler::apply_slaac_update(
3197 &mut core_ctx,
3198 &mut bindings_ctx,
3199 &FakeDeviceId,
3200 SUBNET,
3201 NonZeroNdpLifetime::from_u32_with_infinite(preferred_lifetime_secs),
3202 NonZeroNdpLifetime::from_u32_with_infinite(valid_lifetime_secs),
3203 );
3204 assert_empty(core_ctx.state.iter_slaac_addrs());
3205 bindings_ctx.timers.assert_no_timers_installed();
3206 }
3207
3208 struct GenerateTemporaryAddressTest {
3209 pl_config: u32,
3210 vl_config: u32,
3211 dad_transmits: u16,
3212 retrans_timer: Duration,
3213 temp_idgen_retries: u8,
3214 pl_ra: u32,
3215 vl_ra: u32,
3216 expected_pl_addr: u32,
3217 expected_vl_addr: u32,
3218 }
3219 #[test_case(GenerateTemporaryAddressTest{
3220 pl_config: ONE_HOUR_AS_SECS,
3221 vl_config: ONE_HOUR_AS_SECS,
3222 dad_transmits: 0,
3223 retrans_timer: DEFAULT_RETRANS_TIMER,
3224 temp_idgen_retries: 0,
3225 pl_ra: ONE_HOUR_AS_SECS,
3226 vl_ra: ONE_HOUR_AS_SECS,
3227 expected_pl_addr: ONE_HOUR_AS_SECS,
3228 expected_vl_addr: ONE_HOUR_AS_SECS,
3229 }; "config and prefix same lifetimes")]
3230 #[test_case(GenerateTemporaryAddressTest{
3231 pl_config: ONE_HOUR_AS_SECS,
3232 vl_config: TWO_HOURS_AS_SECS,
3233 dad_transmits: 0,
3234 retrans_timer: DEFAULT_RETRANS_TIMER,
3235 temp_idgen_retries: 0,
3236 pl_ra: THREE_HOURS_AS_SECS,
3237 vl_ra: THREE_HOURS_AS_SECS,
3238 expected_pl_addr: ONE_HOUR_AS_SECS,
3239 expected_vl_addr: TWO_HOURS_AS_SECS,
3240 }; "config smaller than prefix lifetimes")]
3241 #[test_case(GenerateTemporaryAddressTest{
3242 pl_config: TWO_HOURS_AS_SECS,
3243 vl_config: THREE_HOURS_AS_SECS,
3244 dad_transmits: 0,
3245 retrans_timer: DEFAULT_RETRANS_TIMER,
3246 temp_idgen_retries: 0,
3247 pl_ra: ONE_HOUR_AS_SECS,
3248 vl_ra: TWO_HOURS_AS_SECS,
3249 expected_pl_addr: ONE_HOUR_AS_SECS,
3250 expected_vl_addr: TWO_HOURS_AS_SECS,
3251 }; "config larger than prefix lifetimes")]
3252 #[test_case(GenerateTemporaryAddressTest{
3253 pl_config: TWO_HOURS_AS_SECS,
3254 vl_config: THREE_HOURS_AS_SECS,
3255 dad_transmits: 0,
3256 retrans_timer: DEFAULT_RETRANS_TIMER,
3257 temp_idgen_retries: 0,
3258 pl_ra: INFINITE_LIFETIME,
3259 vl_ra: INFINITE_LIFETIME,
3260 expected_pl_addr: TWO_HOURS_AS_SECS,
3261 expected_vl_addr: THREE_HOURS_AS_SECS,
3262 }; "prefix with infinite lifetimes")]
3263 #[test_case(GenerateTemporaryAddressTest{
3264 pl_config: TWO_HOURS_AS_SECS,
3265 vl_config: THREE_HOURS_AS_SECS,
3266 dad_transmits: 1,
3267 retrans_timer: DEFAULT_RETRANS_TIMER,
3268 temp_idgen_retries: 0,
3269 pl_ra: INFINITE_LIFETIME,
3270 vl_ra: INFINITE_LIFETIME,
3271 expected_pl_addr: TWO_HOURS_AS_SECS,
3272 expected_vl_addr: THREE_HOURS_AS_SECS,
3273 }; "generate_with_dad_enabled")]
3274 #[test_case(GenerateTemporaryAddressTest{
3275 pl_config: TWO_HOURS_AS_SECS,
3276 vl_config: THREE_HOURS_AS_SECS,
3277 dad_transmits: 2,
3278 retrans_timer: Duration::from_secs(5),
3279 temp_idgen_retries: 3,
3280 pl_ra: INFINITE_LIFETIME,
3281 vl_ra: INFINITE_LIFETIME,
3282 expected_pl_addr: TWO_HOURS_AS_SECS,
3283 expected_vl_addr: THREE_HOURS_AS_SECS,
3284 }; "generate_with_dad_enabled_and_retries")]
3285 #[test_case(GenerateTemporaryAddressTest{
3286 pl_config: TWO_HOURS_AS_SECS,
3287 vl_config: THREE_HOURS_AS_SECS,
3288 dad_transmits: 1,
3289 retrans_timer: Duration::from_secs(10),
3290 temp_idgen_retries: 0,
3291 pl_ra: INFINITE_LIFETIME,
3292 vl_ra: INFINITE_LIFETIME,
3293 expected_pl_addr: TWO_HOURS_AS_SECS,
3294 expected_vl_addr: THREE_HOURS_AS_SECS,
3295 }; "generate_with_dad_enabled_but_no_retries")]
3296 fn generate_temporary_address(
3297 GenerateTemporaryAddressTest {
3298 pl_config,
3299 vl_config,
3300 dad_transmits,
3301 retrans_timer,
3302 temp_idgen_retries,
3303 pl_ra,
3304 vl_ra,
3305 expected_pl_addr,
3306 expected_vl_addr,
3307 }: GenerateTemporaryAddressTest,
3308 ) {
3309 let pl_config = Duration::from_secs(pl_config.into());
3310 let regen_advance = regen_advance(temp_idgen_retries, retrans_timer, dad_transmits);
3311
3312 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
3313 SlaacConfiguration {
3314 temporary_address_configuration: TemporarySlaacAddressConfiguration::Enabled {
3315 temp_valid_lifetime: NonZeroDuration::new(Duration::from_secs(
3316 vl_config.into(),
3317 ))
3318 .unwrap(),
3319 temp_preferred_lifetime: NonZeroDuration::new(pl_config).unwrap(),
3320 temp_idgen_retries,
3321 },
3322 ..Default::default()
3323 },
3324 Default::default(),
3325 NonZeroU16::new(dad_transmits),
3326 retrans_timer,
3327 );
3328
3329 let mut dup_rng = bindings_ctx.rng().deep_clone();
3330
3331 struct AddrProps {
3332 desync_factor: Duration,
3333 valid_until: FakeInstant,
3334 preferred_until: FakeInstant,
3335 entry: SlaacAddressEntry<FakeInstant>,
3336 deprecate_timer_id: InnerSlaacTimerId,
3337 invalidate_timer_id: InnerSlaacTimerId,
3338 regenerate_timer_id: InnerSlaacTimerId,
3339 }
3340
3341 let addr_props = |rng: &mut FakeCryptoRng<_>,
3342 creation_time,
3343 config_greater_than_ra_desync_factor_offset| {
3344 let valid_until = creation_time + Duration::from_secs(expected_vl_addr.into());
3345 let addr_sub =
3346 generate_global_temporary_address(&SUBNET, &IID, rng.gen(), &TEMP_SECRET_KEY);
3347 let desync_factor =
3348 desync_factor(rng, NonZeroDuration::new(pl_config).unwrap(), regen_advance)
3349 .unwrap();
3350 let preferred_until = {
3351 let d = creation_time + Duration::from_secs(expected_pl_addr.into());
3352 if pl_config.as_secs() > pl_ra.into() {
3353 d + config_greater_than_ra_desync_factor_offset
3354 } else {
3355 d - desync_factor
3356 }
3357 };
3358
3359 AddrProps {
3360 desync_factor,
3361 valid_until,
3362 preferred_until,
3363 entry: SlaacAddressEntry {
3364 addr_sub,
3365 config: Ipv6AddrSlaacConfig {
3366 inner: SlaacConfig::Temporary(TemporarySlaacConfig {
3367 valid_until,
3368 desync_factor,
3369 creation_time,
3370 dad_counter: 0,
3371 }),
3372 preferred_lifetime: PreferredLifetime::preferred_until(preferred_until),
3373 },
3374 },
3375 deprecate_timer_id: InnerSlaacTimerId::DeprecateSlaacAddress {
3376 addr: addr_sub.addr(),
3377 },
3378 invalidate_timer_id: InnerSlaacTimerId::InvalidateSlaacAddress {
3379 addr: addr_sub.addr(),
3380 },
3381 regenerate_timer_id: InnerSlaacTimerId::RegenerateTemporaryAddress {
3382 addr_subnet: addr_sub,
3383 },
3384 }
3385 };
3386
3387 SlaacHandler::apply_slaac_update(
3389 &mut core_ctx,
3390 &mut bindings_ctx,
3391 &FakeDeviceId,
3392 SUBNET,
3393 NonZeroNdpLifetime::from_u32_with_infinite(pl_ra),
3394 NonZeroNdpLifetime::from_u32_with_infinite(vl_ra),
3395 );
3396 let AddrProps {
3397 desync_factor: first_desync_factor,
3398 valid_until: first_valid_until,
3399 preferred_until: first_preferred_until,
3400 entry: first_entry,
3401 deprecate_timer_id: first_deprecate_timer_id,
3402 invalidate_timer_id: first_invalidate_timer_id,
3403 regenerate_timer_id: first_regenerate_timer_id,
3404 } = addr_props(&mut dup_rng, bindings_ctx.now(), Duration::ZERO);
3405 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [first_entry]);
3406 core_ctx.state.slaac_state.timers.assert_timers([
3407 (first_deprecate_timer_id, (), first_preferred_until),
3408 (first_invalidate_timer_id, (), first_valid_until),
3409 (first_regenerate_timer_id, (), first_preferred_until - regen_advance.get()),
3410 ]);
3411
3412 assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()),);
3415 let AddrProps {
3416 desync_factor: second_desync_factor,
3417 valid_until: second_valid_until,
3418 preferred_until: second_preferred_until,
3419 entry: second_entry,
3420 deprecate_timer_id: second_deprecate_timer_id,
3421 invalidate_timer_id: second_invalidate_timer_id,
3422 regenerate_timer_id: second_regenerate_timer_id,
3423 } = addr_props(&mut dup_rng, bindings_ctx.now(), first_desync_factor);
3424 assert_eq!(
3425 core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(),
3426 [first_entry, second_entry]
3427 );
3428 let second_regen_at = second_preferred_until - regen_advance.get();
3429 core_ctx.state.slaac_state.timers.assert_timers([
3430 (first_deprecate_timer_id, (), first_preferred_until),
3431 (first_invalidate_timer_id, (), first_valid_until),
3432 (second_deprecate_timer_id, (), second_preferred_until),
3433 (second_invalidate_timer_id, (), second_valid_until),
3434 (second_regenerate_timer_id, (), second_regen_at),
3435 ]);
3436
3437 assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()),);
3439 let first_entry = first_entry.to_deprecated();
3440 assert_eq!(
3441 core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(),
3442 [first_entry, second_entry]
3443 );
3444 core_ctx.state.slaac_state.timers.assert_timers([
3445 (first_invalidate_timer_id, (), first_valid_until),
3446 (second_deprecate_timer_id, (), second_preferred_until),
3447 (second_invalidate_timer_id, (), second_valid_until),
3448 (second_regenerate_timer_id, (), second_regen_at),
3449 ]);
3450
3451 let third_created_at = {
3452 let expected_timer_order = if first_valid_until > second_regen_at {
3453 [second_regenerate_timer_id, second_deprecate_timer_id, first_invalidate_timer_id]
3454 } else {
3455 [first_invalidate_timer_id, second_regenerate_timer_id, second_deprecate_timer_id]
3456 };
3457
3458 let mut third_created_at = None;
3459 for timer_id in expected_timer_order.iter() {
3460 let timer_id = *timer_id;
3461
3462 core_ctx.state.slaac_state.timers.assert_top(&timer_id, &());
3463 assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()));
3464
3465 if timer_id == second_regenerate_timer_id {
3466 assert_eq!(third_created_at, None);
3467 third_created_at = Some(bindings_ctx.now());
3468 }
3469 }
3470
3471 third_created_at.unwrap()
3472 };
3473
3474 let AddrProps {
3477 desync_factor: _,
3478 valid_until: third_valid_until,
3479 preferred_until: third_preferred_until,
3480 entry: third_entry,
3481 deprecate_timer_id: third_deprecate_timer_id,
3482 invalidate_timer_id: third_invalidate_timer_id,
3483 regenerate_timer_id: third_regenerate_timer_id,
3484 } = addr_props(&mut dup_rng, third_created_at, first_desync_factor + second_desync_factor);
3485 let second_entry = second_entry.to_deprecated();
3486 assert_eq!(
3487 core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(),
3488 [second_entry, third_entry]
3489 );
3490 core_ctx.state.slaac_state.timers.assert_timers([
3491 (second_invalidate_timer_id, (), second_valid_until),
3492 (third_deprecate_timer_id, (), third_preferred_until),
3493 (third_invalidate_timer_id, (), third_valid_until),
3494 (third_regenerate_timer_id, (), third_preferred_until - regen_advance.get()),
3495 ]);
3496 }
3497
3498 #[test]
3499 fn temporary_address_not_updated_while_disabled() {
3500 let want_valid_until =
3501 FakeInstant::default() + Duration::from_secs(THREE_HOURS_AS_SECS.into());
3502 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
3503 SlaacConfiguration {
3504 stable_address_configuration: StableSlaacAddressConfiguration::Disabled,
3505 temporary_address_configuration: TemporarySlaacAddressConfiguration::Disabled,
3506 },
3507 FakeSlaacAddrs {
3508 slaac_addrs: vec![SlaacAddressEntry {
3509 addr_sub: testutil::calculate_slaac_addr_sub(SUBNET, IID),
3510 config: Ipv6AddrSlaacConfig {
3511 inner: SlaacConfig::Temporary(TemporarySlaacConfig {
3512 valid_until: want_valid_until,
3513 desync_factor: Duration::default(),
3514 creation_time: FakeInstant::default(),
3515 dad_counter: 0,
3516 }),
3517 preferred_lifetime: PreferredLifetime::preferred_forever(),
3518 },
3519 }],
3520 ..Default::default()
3521 },
3522 None, DEFAULT_RETRANS_TIMER,
3524 );
3525
3526 SlaacHandler::apply_slaac_update(
3527 &mut core_ctx,
3528 &mut bindings_ctx,
3529 &FakeDeviceId,
3530 SUBNET,
3531 NonZeroNdpLifetime::from_u32_with_infinite(FOUR_HOURS_AS_SECS),
3532 NonZeroNdpLifetime::from_u32_with_infinite(FOUR_HOURS_AS_SECS),
3533 );
3534 let addrs = core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>();
3535 assert_eq!(addrs.len(), 1);
3536 let SlaacAddressEntry { config: Ipv6AddrSlaacConfig { inner, preferred_lifetime }, .. } =
3537 addrs[0];
3538 assert_matches!(inner,SlaacConfig::Temporary(TemporarySlaacConfig {
3539 valid_until,
3540 ..
3541 }) => {
3542 assert_eq!(valid_until, want_valid_until);
3543 });
3544 assert_eq!(preferred_lifetime, PreferredLifetime::Deprecated);
3549 }
3550}