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