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 if let Some(entry) = found_entry {
1432 entry
1433 } else {
1434 log::warn!(
1437 "Could not find temporary SLAAC address {addr_subnet}. \
1438 Assuming timer raced with removal"
1439 );
1440 return Action::SkipRegen;
1441 }
1442 };
1443
1444 assert!(
1445 !entry.config.preferred_lifetime.is_deprecated(),
1446 "can't regenerate deprecated address {:?}",
1447 addr_subnet
1448 );
1449
1450 let TemporarySlaacConfig { creation_time, desync_factor, valid_until, dad_counter: _ } =
1451 match entry.config.inner {
1452 SlaacConfig::Temporary(temporary_config) => temporary_config,
1453 SlaacConfig::Stable { .. } => unreachable!(
1454 "can't regenerate a temporary address for {:?}, which is stable",
1455 addr_subnet
1456 ),
1457 };
1458
1459 let temp_valid_lifetime = match config.temporary_address_configuration {
1460 TemporarySlaacAddressConfiguration::Enabled {
1461 temp_valid_lifetime,
1462 temp_preferred_lifetime: _,
1463 temp_idgen_retries: _,
1464 } => temp_valid_lifetime,
1465 TemporarySlaacAddressConfiguration::Disabled => return Action::SkipRegen,
1466 };
1467
1468 let (deprecate_at, ()) = timers
1469 .get(&InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_subnet.addr() })
1470 .unwrap_or_else(|| {
1471 unreachable!(
1472 "temporary SLAAC address {:?} had a regen timer fire but \
1473 does not have a deprecation timer",
1474 addr_subnet.addr()
1475 )
1476 });
1477 let preferred_for = deprecate_at.saturating_duration_since(creation_time) + desync_factor;
1478
1479 let valid_for = valid_until
1484 .checked_duration_since(creation_time)
1485 .and_then(NonZeroDuration::new)
1486 .unwrap_or(temp_valid_lifetime);
1487
1488 Action::Regen { valid_for, preferred_for }
1489 });
1490
1491 match action {
1492 Action::SkipRegen => {}
1493 Action::Regen { valid_for, preferred_for } => add_slaac_addr_sub::<_, CC>(
1494 bindings_ctx,
1495 device_id,
1496 slaac_addrs,
1497 &config_and_state,
1498 slaac_state,
1499 now,
1500 SlaacInitConfig::Temporary { dad_count: 0 },
1501 NonZeroNdpLifetime::Finite(valid_for),
1502 NonZeroDuration::new(preferred_for).map(NonZeroNdpLifetime::Finite),
1503 &addr_subnet.subnet(),
1504 ),
1505 }
1506}
1507
1508#[derive(Copy, Clone, Debug)]
1509enum SlaacInitConfig {
1510 Stable {
1511 regen_count: u8,
1514 dad_count: u8,
1516 },
1517 Temporary {
1518 dad_count: u8,
1519 },
1520}
1521
1522impl SlaacInitConfig {
1523 fn new(slaac_type: SlaacType) -> Self {
1524 match slaac_type {
1525 SlaacType::Stable => Self::Stable { regen_count: 0, dad_count: 0 },
1526 SlaacType::Temporary => Self::Temporary { dad_count: 0 },
1527 }
1528 }
1529}
1530
1531fn has_iana_allowed_iid(address: Ipv6Addr) -> bool {
1537 let mut iid = [0u8; 8];
1538 const U64_SUFFIX_LEN: usize = Ipv6Addr::BYTES as usize - u64::BITS as usize / 8;
1539 iid.copy_from_slice(&address.bytes()[U64_SUFFIX_LEN..]);
1540 let iid = u64::from_be_bytes(iid);
1541 match iid {
1542 0x0000_0000_0000_0000 => false,
1544 0x0200_5EFF_FE00_0000..=0x0200_5EFF_FEFF_FFFF => false,
1549 0xFDFF_FFFF_FFFF_FF80..=0xFDFF_FFFF_FFFF_FFFF => false,
1551
1552 _iid => true,
1554 }
1555}
1556
1557fn generate_stable_address(
1573 prefix: &Subnet<Ipv6Addr>,
1574 iid: &[u8],
1575) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
1576 if prefix.prefix() % 8 != 0 {
1577 unimplemented!(
1578 "generate_stable_address: not implemented for when prefix length is not a multiple of \
1579 8 bits"
1580 );
1581 }
1582
1583 let mut address = prefix.network().ipv6_bytes();
1584 let prefix_len = usize::from(prefix.prefix() / 8);
1585 assert_eq!(address.len() - prefix_len, iid.len());
1586 address[prefix_len..].copy_from_slice(&iid);
1587
1588 let address = AddrSubnet::new(Ipv6Addr::from(address), prefix.prefix()).unwrap();
1589 assert_eq!(address.subnet(), *prefix);
1590
1591 address
1592}
1593
1594fn generate_stable_address_with_opaque_iid(
1597 prefix: &Subnet<Ipv6Addr>,
1598 network_interface: &[u8],
1599 dad_counter: u8,
1600 secret_key: &IidSecret,
1601) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
1602 let iid = OpaqueIid::new(
1603 *prefix,
1604 network_interface,
1605 None::<&[_]>,
1606 OpaqueIidNonce::DadCounter(dad_counter),
1607 secret_key,
1608 );
1609 let prefix_len = prefix.prefix() / 8;
1610 let iid = &iid.to_be_bytes()[..usize::from(Ipv6Addr::BYTES - prefix_len)];
1611
1612 generate_stable_address(prefix, iid)
1613}
1614
1615fn generate_global_temporary_address(
1629 prefix: &Subnet<Ipv6Addr>,
1630 network_interface: &[u8],
1631 seed: u64,
1632 secret_key: &IidSecret,
1633) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
1634 let prefix_len = usize::from(prefix.prefix() / 8);
1635 let mut address = prefix.network().ipv6_bytes();
1636
1637 let interface_identifier = OpaqueIid::new(
1640 *prefix,
1641 network_interface,
1642 None::<[_; 0]>,
1643 OpaqueIidNonce::Random(seed),
1644 secret_key,
1645 );
1646 let suffix_bytes = &interface_identifier.to_be_bytes()[..(address.len() - prefix_len)];
1647 address[prefix_len..].copy_from_slice(suffix_bytes);
1648
1649 let address = AddrSubnet::new(Ipv6Addr::from(address), prefix.prefix()).unwrap();
1650 assert_eq!(address.subnet(), *prefix);
1651
1652 address
1653}
1654
1655fn add_slaac_addr_sub<BC: SlaacBindingsContext<CC::DeviceId>, CC: SlaacContext<BC>>(
1656 bindings_ctx: &mut BC,
1657 device_id: &CC::DeviceId,
1658 slaac_addrs: &mut CC::SlaacAddrs<'_>,
1659 config: &SlaacConfigAndState<CC::LinkLayerAddr, BC>,
1660 slaac_state: &mut SlaacState<BC>,
1661 now: BC::Instant,
1662 slaac_config: SlaacInitConfig,
1663 prefix_valid_for: NonZeroNdpLifetime,
1664 prefix_preferred_for: Option<NonZeroNdpLifetime>,
1665 subnet: &Subnet<Ipv6Addr>,
1666) {
1667 if subnet.prefix() != REQUIRED_PREFIX_BITS {
1668 error!(
1672 "receive_ndp_packet: autonomous prefix length {:?} and interface identifier length {:?} cannot form valid IPv6 address, ignoring",
1673 subnet.prefix(),
1674 REQUIRED_PREFIX_BITS
1675 );
1676 return;
1677 }
1678
1679 struct PreferredForAndRegenAt<Instant>(NonZeroNdpLifetime, Option<Instant>);
1680
1681 let SlaacConfigAndState {
1682 config,
1683 dad_transmits,
1684 retrans_timer,
1685 link_layer_addr,
1686 temp_secret_key,
1687 stable_secret_key,
1688 _marker,
1689 } = config;
1690
1691 let Some(link_layer_addr) = link_layer_addr else {
1692 warn!(
1693 "add_slaac_addr_sub: cannot derive IIDs for device {device_id:?} that does not support \
1694 link-layer addressing"
1695 );
1696 return;
1697 };
1698
1699 let SlaacConfiguration { stable_address_configuration, temporary_address_configuration } =
1700 config;
1701 let SlaacState { timers } = slaac_state;
1702
1703 let (valid_until, preferred_and_regen, mut addresses) = match slaac_config {
1704 SlaacInitConfig::Stable { mut regen_count, dad_count } => {
1705 let iid_generation = match stable_address_configuration {
1706 StableSlaacAddressConfiguration::Disabled => {
1707 trace!("stable SLAAC addresses are disabled on device {:?}", device_id);
1708 return;
1709 }
1710 StableSlaacAddressConfiguration::Enabled { iid_generation } => iid_generation,
1711 };
1712
1713 let valid_until = Lifetime::from_ndp(now, prefix_valid_for);
1714
1715 let addresses = either::Either::Left(match iid_generation {
1723 IidGenerationConfiguration::Eui64 => {
1724 assert_eq!(regen_count, 0);
1728
1729 if dad_count != 0 {
1733 return;
1734 }
1735
1736 let address = generate_stable_address(&subnet, &link_layer_addr.eui64_iid());
1737 let config = SlaacConfig::Stable {
1738 valid_until,
1739 creation_time: now,
1740 regen_counter: regen_count,
1741 dad_counter: dad_count,
1742 };
1743 either::Either::Left(core::iter::once((address, config)))
1744 }
1745 IidGenerationConfiguration::Opaque { idgen_retries: _ } => {
1746 either::Either::Right(core::iter::from_fn(move || {
1747 let mut attempts = 0;
1757 loop {
1758 let address = generate_stable_address_with_opaque_iid(
1759 &subnet,
1760 link_layer_addr.as_bytes(),
1761 regen_count + dad_count,
1769 &stable_secret_key,
1770 );
1771 let config = SlaacConfig::Stable {
1772 valid_until,
1773 creation_time: now,
1774 regen_counter: regen_count,
1775 dad_counter: dad_count,
1776 };
1777
1778 regen_count = regen_count.wrapping_add(1);
1779
1780 if has_iana_allowed_iid(address.addr().get()) {
1781 break Some((address, config));
1782 }
1783
1784 attempts += 1;
1785 if attempts > MAX_LOCAL_REGEN_ATTEMPTS {
1786 return None;
1787 }
1788 }
1789 }))
1790 }
1791 });
1792
1793 (valid_until, prefix_preferred_for.map(|p| PreferredForAndRegenAt(p, None)), addresses)
1794 }
1795 SlaacInitConfig::Temporary { dad_count } => {
1796 match temporary_address_configuration {
1797 TemporarySlaacAddressConfiguration::Disabled => {
1798 trace!(
1799 "receive_ndp_packet: temporary addresses are disabled on device {:?}",
1800 device_id
1801 );
1802 return;
1803 }
1804 TemporarySlaacAddressConfiguration::Enabled {
1805 temp_valid_lifetime,
1806 temp_preferred_lifetime,
1807 temp_idgen_retries,
1808 } => {
1809 let per_attempt_random_seed: u64 = bindings_ctx.rng().random();
1810
1811 let valid_for = match prefix_valid_for {
1823 NonZeroNdpLifetime::Finite(prefix_valid_for) => {
1824 core::cmp::min(prefix_valid_for, *temp_valid_lifetime)
1825 }
1826 NonZeroNdpLifetime::Infinite => *temp_valid_lifetime,
1827 };
1828
1829 let regen_advance = regen_advance(
1830 *temp_idgen_retries,
1831 *retrans_timer,
1832 dad_transmits.map_or(0, NonZeroU16::get),
1833 );
1834
1835 let valid_until = now.saturating_add(valid_for.get());
1836
1837 let desync_factor = if let Some(d) = desync_factor(
1838 &mut bindings_ctx.rng(),
1839 *temp_preferred_lifetime,
1840 regen_advance,
1841 ) {
1842 d
1843 } else {
1844 trace!(
1852 "failed to calculate DESYNC_FACTOR; \
1853 temp_preferred_lifetime={:?}, regen_advance={:?}",
1854 temp_preferred_lifetime, regen_advance,
1855 );
1856 return;
1857 };
1858
1859 let preferred_for = prefix_preferred_for.and_then(|prefix_preferred_for| {
1860 temp_preferred_lifetime
1861 .get()
1862 .checked_sub(desync_factor)
1863 .and_then(NonZeroDuration::new)
1864 .map(|d| prefix_preferred_for.min_finite_duration(d))
1865 });
1866
1867 let preferred_for_and_regen_at = match preferred_for {
1873 None => return,
1874 Some(preferred_for) => {
1875 match preferred_for.get().checked_sub(regen_advance.get()) {
1876 Some(before_regen) => PreferredForAndRegenAt(
1877 NonZeroNdpLifetime::Finite(preferred_for),
1878 now.checked_add(before_regen),
1881 ),
1882 None => {
1883 trace!(
1884 "receive_ndp_packet: preferred lifetime of {:?} \
1885 for subnet {:?} is too short to allow regen",
1886 preferred_for, subnet
1887 );
1888 return;
1889 }
1890 }
1891 }
1892 };
1893
1894 let config = SlaacConfig::Temporary(TemporarySlaacConfig {
1895 desync_factor,
1896 valid_until,
1897 creation_time: now,
1898 dad_counter: dad_count,
1899 });
1900
1901 let mut seed = per_attempt_random_seed;
1902 let addresses = either::Either::Right(core::iter::from_fn(move || {
1903 let mut attempts = 0;
1912 loop {
1913 let address = generate_global_temporary_address(
1914 &subnet,
1915 link_layer_addr.as_bytes(),
1916 seed,
1917 &temp_secret_key,
1918 );
1919 seed = seed.wrapping_add(1);
1920
1921 if has_iana_allowed_iid(address.addr().get()) {
1922 break Some((address, config));
1923 }
1924
1925 attempts += 1;
1926 if attempts > MAX_LOCAL_REGEN_ATTEMPTS {
1927 return None;
1928 }
1929 }
1930 }));
1931
1932 (Lifetime::Finite(valid_until), Some(preferred_for_and_regen_at), addresses)
1933 }
1934 }
1935 }
1936 };
1937
1938 let mut local_regen_attempts = 0;
1940 loop {
1941 let Some((address, slaac_config)) = addresses.next() else {
1942 debug!("exhausted possible SLAAC addresses without assigning on device {device_id:?}");
1944 return;
1945 };
1946
1947 let (preferred_lifetime, regen_at) = match preferred_and_regen {
1952 Some(PreferredForAndRegenAt(preferred_for, regen_at)) => {
1953 (PreferredLifetime::preferred_for(now, preferred_for), regen_at)
1954 }
1955 None => (PreferredLifetime::Deprecated, None),
1956 };
1957 let config = Ipv6AddrSlaacConfig { inner: slaac_config, preferred_lifetime };
1958
1959 let res = slaac_addrs.add_addr_sub_and_then(
1962 bindings_ctx,
1963 address,
1964 config,
1965 |SlaacAddressEntryMut { addr_sub, config: _ }, ctx| {
1966 match valid_until {
1971 Lifetime::Finite(valid_until) => {
1972 assert_eq!(
1973 timers.schedule_instant(
1974 ctx,
1975 InnerSlaacTimerId::InvalidateSlaacAddress { addr: addr_sub.addr() },
1976 (),
1977 valid_until,
1978 ),
1979 None
1980 );
1981 }
1982 Lifetime::Infinite => {}
1983 }
1984
1985 let deprecate_timer_id =
1986 InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_sub.addr() };
1987
1988 match preferred_lifetime {
1989 PreferredLifetime::Preferred(Lifetime::Finite(instant)) => {
1990 assert_eq!(
1991 timers.schedule_instant(ctx, deprecate_timer_id, (), instant,),
1992 None
1993 );
1994 }
1995 PreferredLifetime::Preferred(Lifetime::Infinite) => {}
1996 PreferredLifetime::Deprecated => {
1997 assert_eq!(timers.cancel(ctx, &deprecate_timer_id), None);
1998 }
1999 }
2000
2001 match regen_at {
2002 Some(regen_at) => assert_eq!(
2003 timers.schedule_instant(
2004 ctx,
2005 InnerSlaacTimerId::RegenerateTemporaryAddress { addr_subnet: addr_sub },
2006 (),
2007 regen_at,
2008 ),
2009 None
2010 ),
2011 None => (),
2012 }
2013 addr_sub
2014 },
2015 );
2016
2017 match res {
2018 Err(ExistsError) => {
2019 trace!("IPv6 SLAAC address {:?} already exists on device {:?}", address, device_id);
2020
2021 slaac_addrs.counters().generated_slaac_addr_exists.increment();
2024 local_regen_attempts += 1;
2025 if local_regen_attempts > MAX_LOCAL_REGEN_ATTEMPTS {
2026 debug!(
2027 "exceeded max local SLAAC addr generation attempts on device {device_id:?}"
2028 );
2029 return;
2030 }
2031 }
2032 Ok(addr_sub) => {
2033 trace!(
2034 "receive_ndp_packet: Successfully configured new IPv6 address {:?} on device {:?} via SLAAC",
2035 addr_sub, device_id
2036 );
2037 break;
2038 }
2039 }
2040 }
2041}
2042
2043#[cfg(any(test, feature = "testutils"))]
2044pub(crate) mod testutil {
2045 use super::*;
2046
2047 use netstack3_hashmap::HashMap;
2048
2049 use net_types::ip::Ipv6;
2050
2051 use crate::internal::device::{IpDeviceBindingsContext, Ipv6DeviceConfigurationContext};
2052
2053 pub fn collect_slaac_timers_integration<CC, BC>(
2056 core_ctx: &mut CC,
2057 device_id: &CC::DeviceId,
2058 ) -> HashMap<InnerSlaacTimerId, BC::Instant>
2059 where
2060 CC: Ipv6DeviceConfigurationContext<BC>,
2061 for<'a> CC::Ipv6DeviceStateCtx<'a>: SlaacContext<BC>,
2062 BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId> + SlaacBindingsContext<CC::DeviceId>,
2063 {
2064 core_ctx.with_ipv6_device_configuration(device_id, |_, mut core_ctx| {
2065 core_ctx.with_slaac_addrs_mut(device_id, |_, state| {
2066 state.timers().iter().map(|(k, (), t)| (*k, *t)).collect::<HashMap<_, _>>()
2067 })
2068 })
2069 }
2070
2071 pub fn calculate_slaac_addr_sub(
2078 subnet: Subnet<Ipv6Addr>,
2079 iid: [u8; 8],
2080 ) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
2081 assert_eq!(subnet.prefix(), 64);
2082 let mut bytes = subnet.network().ipv6_bytes();
2083 bytes[8..].copy_from_slice(&iid);
2084 AddrSubnet::new(Ipv6Addr::from_bytes(bytes), subnet.prefix()).unwrap()
2085 }
2086}
2087
2088#[cfg(test)]
2089mod tests {
2090 use alloc::vec;
2091 use core::convert::TryFrom as _;
2092
2093 use net_declare::net::ip_v6;
2094 use netstack3_base::testutil::{
2095 FakeBindingsCtx, FakeCoreCtx, FakeCryptoRng, FakeDeviceId, FakeInstant,
2096 FakeTimerCtxExt as _, FakeWeakDeviceId, assert_empty,
2097 };
2098 use netstack3_base::{CtxPair, IntoCoreTimerCtx};
2099 use netstack3_hashmap::HashSet;
2100 use test_case::test_case;
2101
2102 use super::*;
2103
2104 fn calculate_stable_slaac_addr_sub_with_opaque_iid(
2111 subnet: Subnet<Ipv6Addr>,
2112 network_interface: impl AsRef<[u8]>,
2113 dad_counter: u8,
2114 ) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
2115 let iid = OpaqueIid::new(
2116 subnet,
2117 network_interface.as_ref(),
2118 None::<&[_]>,
2119 OpaqueIidNonce::DadCounter(dad_counter),
2120 &STABLE_SECRET_KEY,
2121 );
2122 let iid = &iid.to_be_bytes()[..8];
2123 testutil::calculate_slaac_addr_sub(subnet, iid.try_into().unwrap())
2124 }
2125
2126 struct FakeSlaacContext {
2127 config: SlaacConfiguration,
2128 dad_transmits: Option<NonZeroU16>,
2129 retrans_timer: Duration,
2130 slaac_addrs: FakeSlaacAddrs,
2131 slaac_state: SlaacState<FakeBindingsCtxImpl>,
2132 }
2133
2134 type FakeCoreCtxImpl = FakeCoreCtx<FakeSlaacContext, (), FakeDeviceId>;
2135 type FakeBindingsCtxImpl = FakeBindingsCtx<
2136 SlaacTimerId<FakeWeakDeviceId<FakeDeviceId>>,
2137 IpDeviceEvent<FakeDeviceId, Ipv6, FakeInstant>,
2138 (),
2139 (),
2140 >;
2141
2142 struct FakeLinkLayerAddr;
2143
2144 const IID: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
2145
2146 impl Ipv6LinkLayerAddr for FakeLinkLayerAddr {
2147 fn as_bytes(&self) -> &[u8] {
2148 &IID
2149 }
2150
2151 fn eui64_iid(&self) -> [u8; 8] {
2152 IID
2153 }
2154 }
2155
2156 #[derive(Default)]
2157 struct FakeSlaacAddrs {
2158 slaac_addrs: Vec<SlaacAddressEntry<FakeInstant>>,
2159 non_slaac_addrs: Vec<Ipv6DeviceAddr>,
2160 counters: SlaacCounters,
2161 }
2162
2163 impl<'a> CounterContext<SlaacCounters> for &'a mut FakeSlaacAddrs {
2164 fn counters(&self) -> &SlaacCounters {
2165 &self.counters
2166 }
2167 }
2168
2169 impl<'a> SlaacAddresses<FakeBindingsCtxImpl> for &'a mut FakeSlaacAddrs {
2170 fn for_each_addr_mut<F: FnMut(SlaacAddressEntryMut<'_, FakeInstant>)>(
2171 &mut self,
2172 mut cb: F,
2173 ) {
2174 let FakeSlaacAddrs { slaac_addrs, non_slaac_addrs: _, counters: _ } = self;
2175 slaac_addrs.iter_mut().for_each(|SlaacAddressEntry { addr_sub, config }| {
2176 cb(SlaacAddressEntryMut { addr_sub: *addr_sub, config })
2177 })
2178 }
2179
2180 type AddrsIter<'b> =
2181 core::iter::Cloned<core::slice::Iter<'b, SlaacAddressEntry<FakeInstant>>>;
2182 fn with_addrs<O, F: FnOnce(Self::AddrsIter<'_>) -> O>(&mut self, cb: F) -> O {
2183 let FakeSlaacAddrs { slaac_addrs, non_slaac_addrs: _, counters: _ } = self;
2184 cb(slaac_addrs.iter().cloned())
2185 }
2186
2187 fn add_addr_sub_and_then<
2188 O,
2189 F: FnOnce(SlaacAddressEntryMut<'_, FakeInstant>, &mut FakeBindingsCtxImpl) -> O,
2190 >(
2191 &mut self,
2192 bindings_ctx: &mut FakeBindingsCtxImpl,
2193 add_addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
2194 config: Ipv6AddrSlaacConfig<FakeInstant>,
2195 and_then: F,
2196 ) -> Result<O, ExistsError> {
2197 let FakeSlaacAddrs { slaac_addrs, non_slaac_addrs, counters: _ } = self;
2198
2199 if non_slaac_addrs.iter().any(|a| *a == add_addr_sub.addr()) {
2200 return Err(ExistsError);
2201 }
2202
2203 if slaac_addrs.iter_mut().any(|e| e.addr_sub.addr() == add_addr_sub.addr()) {
2204 return Err(ExistsError);
2205 }
2206
2207 slaac_addrs.push(SlaacAddressEntry { addr_sub: add_addr_sub, config });
2208
2209 let SlaacAddressEntry { addr_sub, config } = slaac_addrs.iter_mut().last().unwrap();
2210
2211 Ok(and_then(SlaacAddressEntryMut { addr_sub: *addr_sub, config }, bindings_ctx))
2212 }
2213
2214 fn remove_addr(
2215 &mut self,
2216 _bindings_ctx: &mut FakeBindingsCtxImpl,
2217 addr: &Ipv6DeviceAddr,
2218 ) -> Result<
2219 (AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>, Ipv6AddrSlaacConfig<FakeInstant>),
2220 NotFoundError,
2221 > {
2222 let FakeSlaacAddrs { slaac_addrs, non_slaac_addrs: _, counters: _ } = self;
2223
2224 slaac_addrs
2225 .iter()
2226 .enumerate()
2227 .find_map(|(i, a)| (&a.addr_sub.addr() == addr).then(|| i))
2228 .ok_or(NotFoundError)
2229 .map(|i| {
2230 let SlaacAddressEntry { addr_sub, config } = slaac_addrs.remove(i);
2231 (addr_sub, config)
2232 })
2233 }
2234 }
2235
2236 impl SlaacContext<FakeBindingsCtxImpl> for FakeCoreCtxImpl {
2237 type LinkLayerAddr = FakeLinkLayerAddr;
2238
2239 type SlaacAddrs<'a>
2240 = &'a mut FakeSlaacAddrs
2241 where
2242 FakeCoreCtxImpl: 'a;
2243
2244 fn with_slaac_addrs_mut_and_configs<
2245 O,
2246 F: FnOnce(
2247 &mut Self::SlaacAddrs<'_>,
2248 SlaacConfigAndState<FakeLinkLayerAddr, FakeBindingsCtxImpl>,
2249 &mut SlaacState<FakeBindingsCtxImpl>,
2250 ) -> O,
2251 >(
2252 &mut self,
2253 &FakeDeviceId: &FakeDeviceId,
2254 cb: F,
2255 ) -> O {
2256 let FakeSlaacContext {
2257 config,
2258 dad_transmits,
2259 retrans_timer,
2260 slaac_addrs,
2261 slaac_state,
2262 ..
2263 } = &mut self.state;
2264 let mut slaac_addrs = slaac_addrs;
2265 cb(
2266 &mut slaac_addrs,
2267 SlaacConfigAndState {
2268 config: *config,
2269 dad_transmits: *dad_transmits,
2270 retrans_timer: *retrans_timer,
2271 link_layer_addr: Some(FakeLinkLayerAddr),
2272 temp_secret_key: TEMP_SECRET_KEY,
2273 stable_secret_key: STABLE_SECRET_KEY,
2274 _marker: PhantomData,
2275 },
2276 slaac_state,
2277 )
2278 }
2279 }
2280
2281 impl FakeSlaacContext {
2282 fn iter_slaac_addrs(&self) -> impl Iterator<Item = SlaacAddressEntry<FakeInstant>> + '_ {
2283 self.slaac_addrs.slaac_addrs.iter().cloned()
2284 }
2285 }
2286
2287 fn new_timer_id() -> SlaacTimerId<FakeWeakDeviceId<FakeDeviceId>> {
2288 SlaacTimerId { device_id: FakeWeakDeviceId(FakeDeviceId) }
2289 }
2290
2291 fn new_context(
2292 config: SlaacConfiguration,
2293 slaac_addrs: FakeSlaacAddrs,
2294 dad_transmits: Option<NonZeroU16>,
2295 retrans_timer: Duration,
2296 ) -> CtxPair<FakeCoreCtxImpl, FakeBindingsCtxImpl> {
2297 CtxPair::with_default_bindings_ctx(|bindings_ctx| {
2298 FakeCoreCtxImpl::with_state(FakeSlaacContext {
2299 config,
2300 dad_transmits,
2301 retrans_timer,
2302 slaac_addrs,
2303 slaac_state: SlaacState::new::<_, IntoCoreTimerCtx>(
2304 bindings_ctx,
2305 FakeWeakDeviceId(FakeDeviceId),
2306 ),
2307 })
2308 })
2309 }
2310
2311 impl<Instant> SlaacAddressEntry<Instant> {
2312 fn to_deprecated(self) -> Self {
2313 let Self { addr_sub, config: Ipv6AddrSlaacConfig { inner, preferred_lifetime: _ } } =
2314 self;
2315 Self {
2316 addr_sub,
2317 config: Ipv6AddrSlaacConfig {
2318 inner,
2319 preferred_lifetime: PreferredLifetime::Deprecated,
2320 },
2321 }
2322 }
2323 }
2324
2325 #[test_case(ip_v6!("1:2:3:4::"), false; "subnet-router anycast")]
2326 #[test_case(ip_v6!("::1"), true; "allowed 1")]
2327 #[test_case(ip_v6!("1:2:3:4::1"), true; "allowed 2")]
2328 #[test_case(ip_v6!("4:4:4:4:0200:5eff:fe00:1"), false; "first ethernet block")]
2329 #[test_case(ip_v6!("1:1:1:1:0200:5eff:fe00:5213"), false; "proxy mobile")]
2330 #[test_case(ip_v6!("8:8:8:8:0200:5eff:fe00:8000"), false; "second ethernet block")]
2331 #[test_case(ip_v6!("a:a:a:a:fdff:ffff:ffff:ffaa"), false; "subnet anycast")]
2332 #[test_case(ip_v6!("c:c:c:c:fe00::"), true; "allowed 3")]
2333 fn test_has_iana_allowed_iid(addr: Ipv6Addr, expect_allowed: bool) {
2334 assert_eq!(has_iana_allowed_iid(addr), expect_allowed);
2335 }
2336
2337 const DEFAULT_RETRANS_TIMER: Duration = Duration::from_secs(1);
2338 const SUBNET: Subnet<Ipv6Addr> = net_declare::net_subnet_v6!("200a::/64");
2339
2340 #[test_case(0, 0, true; "zero lifetimes")]
2341 #[test_case(2, 1, true; "preferred larger than valid")]
2342 #[test_case(1, 2, false; "disabled")]
2343 fn dont_generate_address(
2344 preferred_lifetime_secs: u32,
2345 valid_lifetime_secs: u32,
2346 enable_stable_addresses: bool,
2347 ) {
2348 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2349 SlaacConfiguration {
2350 stable_address_configuration: if enable_stable_addresses {
2351 StableSlaacAddressConfiguration::ENABLED_WITH_EUI64
2352 } else {
2353 StableSlaacAddressConfiguration::Disabled
2354 },
2355 ..Default::default()
2356 },
2357 Default::default(),
2358 None,
2359 DEFAULT_RETRANS_TIMER,
2360 );
2361
2362 SlaacHandler::apply_slaac_update(
2363 &mut core_ctx,
2364 &mut bindings_ctx,
2365 &FakeDeviceId,
2366 SUBNET,
2367 NonZeroNdpLifetime::from_u32_with_infinite(preferred_lifetime_secs),
2368 NonZeroNdpLifetime::from_u32_with_infinite(valid_lifetime_secs),
2369 );
2370 assert_empty(core_ctx.state.iter_slaac_addrs());
2371 bindings_ctx.timers.assert_no_timers_installed();
2372 }
2373
2374 #[test_case(0, false; "deprecated EUI64")]
2375 #[test_case(1, false; "preferred EUI64")]
2376 #[test_case(0, true; "deprecated opaque")]
2377 #[test_case(1, true; "preferred opaque")]
2378 fn generate_stable_address(preferred_lifetime_secs: u32, opaque_iids: bool) {
2379 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2380 SlaacConfiguration {
2381 stable_address_configuration: if opaque_iids {
2382 StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS
2383 } else {
2384 StableSlaacAddressConfiguration::ENABLED_WITH_EUI64
2385 },
2386 ..Default::default()
2387 },
2388 Default::default(),
2389 None,
2390 DEFAULT_RETRANS_TIMER,
2391 );
2392
2393 let valid_lifetime_secs = preferred_lifetime_secs + 1;
2394 let addr_sub = if opaque_iids {
2395 calculate_stable_slaac_addr_sub_with_opaque_iid(SUBNET, IID, 0)
2396 } else {
2397 testutil::calculate_slaac_addr_sub(SUBNET, IID)
2398 };
2399
2400 SlaacHandler::apply_slaac_update(
2402 &mut core_ctx,
2403 &mut bindings_ctx,
2404 &FakeDeviceId,
2405 SUBNET,
2406 NonZeroNdpLifetime::from_u32_with_infinite(preferred_lifetime_secs),
2407 NonZeroNdpLifetime::from_u32_with_infinite(valid_lifetime_secs),
2408 );
2409 let address_created_deprecated = preferred_lifetime_secs == 0;
2410 let now = bindings_ctx.now();
2411 let valid_until = now + Duration::from_secs(valid_lifetime_secs.into());
2412 let preferred_lifetime = match preferred_lifetime_secs {
2413 0 => PreferredLifetime::Deprecated,
2414 secs => PreferredLifetime::preferred_until(now + Duration::from_secs(secs.into())),
2415 };
2416 let inner = SlaacConfig::Stable {
2417 valid_until: Lifetime::Finite(valid_until),
2418 creation_time: bindings_ctx.now(),
2419 regen_counter: 0,
2420 dad_counter: 0,
2421 };
2422 let entry = SlaacAddressEntry {
2423 addr_sub,
2424 config: Ipv6AddrSlaacConfig { inner, preferred_lifetime },
2425 };
2426 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry],);
2427 let deprecate_timer_id = InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_sub.addr() };
2428 let invalidate_timer_id =
2429 InnerSlaacTimerId::InvalidateSlaacAddress { addr: addr_sub.addr() };
2430 if !address_created_deprecated {
2431 core_ctx.state.slaac_state.timers.assert_timers([
2432 (deprecate_timer_id, (), now + Duration::from_secs(preferred_lifetime_secs.into())),
2433 (invalidate_timer_id, (), valid_until),
2434 ]);
2435
2436 assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()));
2438 let entry = SlaacAddressEntry {
2439 addr_sub,
2440 config: Ipv6AddrSlaacConfig {
2441 inner,
2442 preferred_lifetime: PreferredLifetime::Deprecated,
2443 },
2444 };
2445 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2446 }
2447 core_ctx.state.slaac_state.timers.assert_timers([(invalidate_timer_id, (), valid_until)]);
2448
2449 assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()));
2451 assert_empty(core_ctx.state.iter_slaac_addrs());
2452 bindings_ctx.timers.assert_no_timers_installed();
2453 }
2454
2455 enum StableAddress {
2456 Global,
2457 LinkLocal,
2458 }
2459
2460 #[test_case(StableAddress::Global, true; "opaque global")]
2461 #[test_case(StableAddress::Global, false; "EUI64-based global")]
2462 #[test_case(StableAddress::LinkLocal, true; "opaque link-local")]
2463 #[test_case(StableAddress::LinkLocal, false; "EUI64-based link-local")]
2464 fn stable_address_conflict(address_type: StableAddress, opaque_iids: bool) {
2465 let subnet = match address_type {
2466 StableAddress::Global => SUBNET,
2467 StableAddress::LinkLocal => {
2468 Subnet::new(Ipv6::LINK_LOCAL_UNICAST_SUBNET.network(), REQUIRED_PREFIX_BITS)
2469 .unwrap()
2470 }
2471 };
2472 let addr_sub = if opaque_iids {
2473 let dad_counter = 0;
2474 calculate_stable_slaac_addr_sub_with_opaque_iid(subnet, IID, dad_counter)
2475 } else {
2476 testutil::calculate_slaac_addr_sub(subnet, IID)
2477 };
2478
2479 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2480 SlaacConfiguration {
2481 stable_address_configuration: if opaque_iids {
2482 StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS
2483 } else {
2484 StableSlaacAddressConfiguration::ENABLED_WITH_EUI64
2485 },
2486 ..Default::default()
2487 },
2488 FakeSlaacAddrs {
2489 slaac_addrs: Default::default(),
2490 non_slaac_addrs: vec![addr_sub.addr()],
2493 counters: Default::default(),
2494 },
2495 None,
2496 DEFAULT_RETRANS_TIMER,
2497 );
2498
2499 const LIFETIME_SECS: u32 = 1;
2500
2501 match address_type {
2503 StableAddress::Global => {
2504 SlaacHandler::apply_slaac_update(
2505 &mut core_ctx,
2506 &mut bindings_ctx,
2507 &FakeDeviceId,
2508 SUBNET,
2509 NonZeroNdpLifetime::from_u32_with_infinite(LIFETIME_SECS),
2510 NonZeroNdpLifetime::from_u32_with_infinite(LIFETIME_SECS),
2511 );
2512 }
2513 StableAddress::LinkLocal => {
2514 SlaacHandler::generate_link_local_address(
2515 &mut core_ctx,
2516 &mut bindings_ctx,
2517 &FakeDeviceId,
2518 );
2519 }
2520 }
2521
2522 if !opaque_iids {
2526 assert_empty(core_ctx.state.iter_slaac_addrs());
2527 bindings_ctx.timers.assert_no_timers_installed();
2528 return;
2529 }
2530
2531 let dad_counter = 1;
2535 let addr_sub = calculate_stable_slaac_addr_sub_with_opaque_iid(subnet, &IID, dad_counter);
2536 match address_type {
2537 StableAddress::Global => {
2538 let now = bindings_ctx.now();
2539 let valid_until = now + Duration::from_secs(LIFETIME_SECS.into());
2540 let entry = SlaacAddressEntry {
2541 addr_sub,
2542 config: Ipv6AddrSlaacConfig {
2543 inner: SlaacConfig::Stable {
2544 valid_until: Lifetime::Finite(valid_until),
2545 creation_time: bindings_ctx.now(),
2546 regen_counter: 1,
2547 dad_counter: 0,
2548 },
2549 preferred_lifetime: PreferredLifetime::preferred_until(valid_until),
2550 },
2551 };
2552 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2553 let deprecate_timer_id =
2554 InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_sub.addr() };
2555 let invalidate_timer_id =
2556 InnerSlaacTimerId::InvalidateSlaacAddress { addr: addr_sub.addr() };
2557 core_ctx.state.slaac_state.timers.assert_timers([
2558 (deprecate_timer_id, (), valid_until),
2559 (invalidate_timer_id, (), valid_until),
2560 ]);
2561 }
2562 StableAddress::LinkLocal => {
2563 let entry = SlaacAddressEntry {
2564 addr_sub,
2565 config: Ipv6AddrSlaacConfig {
2566 inner: SlaacConfig::Stable {
2567 valid_until: Lifetime::Infinite,
2568 creation_time: bindings_ctx.now(),
2569 regen_counter: 1,
2570 dad_counter: 0,
2571 },
2572 preferred_lifetime: PreferredLifetime::preferred_forever(),
2573 },
2574 };
2575 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2576 bindings_ctx.timers.assert_no_timers_installed();
2577 }
2578 };
2579 }
2580
2581 #[test]
2582 fn temporary_address_conflict() {
2583 const TEMP_IDGEN_RETRIES: u8 = 0;
2584
2585 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2586 SlaacConfiguration {
2587 temporary_address_configuration: TemporarySlaacAddressConfiguration::Enabled {
2588 temp_valid_lifetime: ONE_HOUR,
2589 temp_preferred_lifetime: ONE_HOUR,
2590 temp_idgen_retries: TEMP_IDGEN_RETRIES,
2591 },
2592 ..Default::default()
2593 },
2594 FakeSlaacAddrs::default(),
2595 None,
2596 DEFAULT_RETRANS_TIMER,
2597 );
2598
2599 let mut dup_rng = bindings_ctx.rng().deep_clone();
2602 let seed = dup_rng.random();
2603 let first_attempt =
2604 generate_global_temporary_address(&SUBNET, &IID, seed, &TEMP_SECRET_KEY);
2605 core_ctx.state.slaac_addrs.non_slaac_addrs = vec![first_attempt.addr()];
2606
2607 SlaacHandler::apply_slaac_update(
2609 &mut core_ctx,
2610 &mut bindings_ctx,
2611 &FakeDeviceId,
2612 SUBNET,
2613 Some(NonZeroNdpLifetime::Finite(ONE_HOUR)),
2614 Some(NonZeroNdpLifetime::Finite(ONE_HOUR)),
2615 );
2616
2617 let seed = seed.wrapping_add(1);
2620 let addr_sub = generate_global_temporary_address(&SUBNET, &IID, seed, &TEMP_SECRET_KEY);
2621 assert_ne!(addr_sub, first_attempt);
2622 let regen_advance =
2623 regen_advance(TEMP_IDGEN_RETRIES, DEFAULT_RETRANS_TIMER, 0);
2624 let desync_factor = desync_factor(&mut dup_rng, ONE_HOUR, regen_advance).unwrap();
2625 let preferred_until = {
2626 let d = bindings_ctx.now() + ONE_HOUR.into();
2627 d - desync_factor
2628 };
2629 let entry = SlaacAddressEntry {
2630 addr_sub,
2631 config: Ipv6AddrSlaacConfig {
2632 inner: SlaacConfig::Temporary(TemporarySlaacConfig {
2633 valid_until: bindings_ctx.now() + ONE_HOUR.into(),
2634 desync_factor,
2635 creation_time: bindings_ctx.now(),
2636 dad_counter: 0,
2637 }),
2638 preferred_lifetime: PreferredLifetime::preferred_until(preferred_until),
2639 },
2640 };
2641 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2642 }
2643
2644 #[test]
2645 fn local_regen_limit() {
2646 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2647 SlaacConfiguration {
2648 stable_address_configuration:
2649 StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS,
2650 temporary_address_configuration: TemporarySlaacAddressConfiguration::Enabled {
2651 temp_valid_lifetime: ONE_HOUR,
2652 temp_preferred_lifetime: ONE_HOUR,
2653 temp_idgen_retries: 0,
2654 },
2655 ..Default::default()
2656 },
2657 FakeSlaacAddrs::default(),
2658 None,
2659 DEFAULT_RETRANS_TIMER,
2660 );
2661
2662 let mut dup_rng = bindings_ctx.rng().deep_clone();
2663 let mut seed = dup_rng.random();
2664
2665 let link_local_subnet =
2666 Subnet::new(Ipv6::LINK_LOCAL_UNICAST_SUBNET.network(), REQUIRED_PREFIX_BITS).unwrap();
2667
2668 for attempt in 0..=MAX_LOCAL_REGEN_ATTEMPTS {
2671 let link_local =
2672 calculate_stable_slaac_addr_sub_with_opaque_iid(link_local_subnet, IID, attempt);
2673
2674 let stable = calculate_stable_slaac_addr_sub_with_opaque_iid(SUBNET, IID, attempt);
2675
2676 let temporary =
2677 generate_global_temporary_address(&SUBNET, &IID, seed, &TEMP_SECRET_KEY);
2678 seed = seed.wrapping_add(1);
2679
2680 core_ctx.state.slaac_addrs.non_slaac_addrs.extend(&[
2681 link_local.addr(),
2682 stable.addr(),
2683 temporary.addr(),
2684 ]);
2685 }
2686
2687 SlaacHandler::apply_slaac_update(
2690 &mut core_ctx,
2691 &mut bindings_ctx,
2692 &FakeDeviceId,
2693 SUBNET,
2694 Some(NonZeroNdpLifetime::Finite(ONE_HOUR)),
2695 Some(NonZeroNdpLifetime::Finite(ONE_HOUR)),
2696 );
2697 SlaacHandler::generate_link_local_address(&mut core_ctx, &mut bindings_ctx, &FakeDeviceId);
2698
2699 assert_empty(core_ctx.state.iter_slaac_addrs());
2702 bindings_ctx.timers.assert_no_timers_installed();
2703 }
2704
2705 const LIFETIME: NonZeroNdpLifetime =
2706 NonZeroNdpLifetime::Finite(NonZeroDuration::new(Duration::from_secs(1)).unwrap());
2707
2708 #[test_case(AddressRemovedReason::Manual, LIFETIME; "manual")]
2709 #[test_case(AddressRemovedReason::DadFailed, LIFETIME; "dad failed")]
2710 #[test_case(
2711 AddressRemovedReason::DadFailed,
2712 NonZeroNdpLifetime::Infinite;
2713 "dad failed infinite lifetime"
2714 )]
2715 fn remove_stable_address(reason: AddressRemovedReason, lifetime: NonZeroNdpLifetime) {
2716 let addr_sub =
2717 calculate_stable_slaac_addr_sub_with_opaque_iid(SUBNET, IID, 0);
2718
2719 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2720 SlaacConfiguration {
2721 stable_address_configuration:
2722 StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS,
2723 ..Default::default()
2724 },
2725 Default::default(),
2726 None,
2727 DEFAULT_RETRANS_TIMER,
2728 );
2729
2730 SlaacHandler::apply_slaac_update(
2732 &mut core_ctx,
2733 &mut bindings_ctx,
2734 &FakeDeviceId,
2735 SUBNET,
2736 Some(lifetime),
2737 Some(lifetime),
2738 );
2739 let now = bindings_ctx.now();
2740 let valid_until = Lifetime::from_ndp(now, lifetime);
2741 let preferred_lifetime = PreferredLifetime::preferred_for(now, lifetime);
2742 let entry = SlaacAddressEntry {
2743 addr_sub,
2744 config: Ipv6AddrSlaacConfig {
2745 inner: SlaacConfig::Stable {
2746 valid_until,
2747 creation_time: bindings_ctx.now(),
2748 regen_counter: 0,
2749 dad_counter: 0,
2750 },
2751 preferred_lifetime,
2752 },
2753 };
2754 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2755
2756 let assert_expected_timers = |slaac_state: &SlaacState<_>, addr| {
2757 let expected_timers = match lifetime {
2758 NonZeroNdpLifetime::Infinite => vec![],
2759 NonZeroNdpLifetime::Finite(duration) => {
2760 let deprecate_timer_id = InnerSlaacTimerId::DeprecateSlaacAddress { addr };
2761 let invalidate_timer_id = InnerSlaacTimerId::InvalidateSlaacAddress { addr };
2762 let instant = now + duration.get();
2763 vec![(deprecate_timer_id, (), instant), (invalidate_timer_id, (), instant)]
2764 }
2765 };
2766 slaac_state.timers.assert_timers(expected_timers);
2767 };
2768 assert_expected_timers(&core_ctx.state.slaac_state, addr_sub.addr());
2769
2770 let config = {
2772 let SlaacAddressEntry { addr_sub: got_addr_sub, config } =
2773 core_ctx.state.slaac_addrs.slaac_addrs.remove(0);
2774 assert_eq!(addr_sub, got_addr_sub);
2775 assert_eq!(config.preferred_lifetime, preferred_lifetime);
2776 config
2777 };
2778 SlaacHandler::on_address_removed(
2779 &mut core_ctx,
2780 &mut bindings_ctx,
2781 &FakeDeviceId,
2782 addr_sub,
2783 config,
2784 reason,
2785 );
2786 match reason {
2787 AddressRemovedReason::Manual => {
2788 bindings_ctx.timers.assert_no_timers_installed();
2790 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), []);
2791 return;
2792 }
2793 AddressRemovedReason::DadFailed => {}
2794 AddressRemovedReason::Forfeited => {
2795 unreachable!("forfeited IPv6 addresses are not tested");
2796 }
2797 }
2798
2799 let addr_sub =
2802 calculate_stable_slaac_addr_sub_with_opaque_iid(SUBNET, IID, 1);
2803 let entry = SlaacAddressEntry {
2804 addr_sub,
2805 config: Ipv6AddrSlaacConfig {
2806 inner: SlaacConfig::Stable {
2807 valid_until,
2808 creation_time: now,
2809 regen_counter: 0,
2810 dad_counter: 1,
2811 },
2812 preferred_lifetime,
2813 },
2814 };
2815 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2816 assert_expected_timers(&core_ctx.state.slaac_state, addr_sub.addr());
2817 }
2818
2819 #[test]
2820 fn stable_addr_regen_counters() {
2821 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2825 SlaacConfiguration {
2826 stable_address_configuration:
2827 StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS,
2828 ..Default::default()
2829 },
2830 Default::default(),
2831 None,
2832 DEFAULT_RETRANS_TIMER,
2833 );
2834
2835 const LOCAL_REGEN_ATTEMPTS: u8 = 3;
2836 const DAD_FAILURE_REGEN_ATTEMPTS: u8 = 3;
2837
2838 let now = bindings_ctx.now();
2839 core_ctx.with_slaac_addrs_mut_and_configs(&FakeDeviceId, |addrs, config, slaac_state| {
2840 for regen_count in 0..LOCAL_REGEN_ATTEMPTS {
2841 for dad_count in 0..DAD_FAILURE_REGEN_ATTEMPTS {
2842 add_slaac_addr_sub::<_, FakeCoreCtx<_, _, _>>(
2843 &mut bindings_ctx,
2844 &FakeDeviceId,
2845 addrs,
2846 &config,
2847 slaac_state,
2848 now,
2849 SlaacInitConfig::Stable { regen_count, dad_count },
2850 NonZeroNdpLifetime::Infinite,
2851 Some(NonZeroNdpLifetime::Infinite),
2852 &SUBNET,
2853 );
2854 }
2855 }
2856 });
2857 let unique_addrs = core_ctx
2858 .state
2859 .iter_slaac_addrs()
2860 .map(|entry| entry.addr_sub.addr())
2861 .collect::<HashSet<_>>();
2862 assert_eq!(
2863 unique_addrs.len(),
2864 usize::from(LOCAL_REGEN_ATTEMPTS * DAD_FAILURE_REGEN_ATTEMPTS)
2865 );
2866 }
2867
2868 struct RefreshStableAddressTimersTest {
2869 orig_pl_secs: u32,
2870 orig_vl_secs: u32,
2871 new_pl_secs: u32,
2872 new_vl_secs: u32,
2873 effective_new_vl_secs: u32,
2874 }
2875
2876 const ONE_HOUR_AS_SECS: u32 = 60 * 60;
2877 const TWO_HOURS_AS_SECS: u32 = ONE_HOUR_AS_SECS * 2;
2878 const THREE_HOURS_AS_SECS: u32 = ONE_HOUR_AS_SECS * 3;
2879 const FOUR_HOURS_AS_SECS: u32 = ONE_HOUR_AS_SECS * 4;
2880 const INFINITE_LIFETIME: u32 = u32::MAX;
2881 const MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS: u32 =
2882 MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE.get().as_secs() as u32;
2883 #[test_case(RefreshStableAddressTimersTest {
2884 orig_pl_secs: 1,
2885 orig_vl_secs: 1,
2886 new_pl_secs: 1,
2887 new_vl_secs: 1,
2888 effective_new_vl_secs: 1,
2889 }; "do nothing")]
2890 #[test_case(RefreshStableAddressTimersTest {
2891 orig_pl_secs: 1,
2892 orig_vl_secs: 1,
2893 new_pl_secs: 2,
2894 new_vl_secs: 2,
2895 effective_new_vl_secs: 2,
2896 }; "increase lifetimes")]
2897 #[test_case(RefreshStableAddressTimersTest {
2898 orig_pl_secs: 1,
2899 orig_vl_secs: 1,
2900 new_pl_secs: 0,
2901 new_vl_secs: 1,
2902 effective_new_vl_secs: 1,
2903 }; "deprecate address only")]
2904 #[test_case(RefreshStableAddressTimersTest {
2905 orig_pl_secs: 0,
2906 orig_vl_secs: 1,
2907 new_pl_secs: 1,
2908 new_vl_secs: 1,
2909 effective_new_vl_secs: 1,
2910 }; "undeprecate address")]
2911 #[test_case(RefreshStableAddressTimersTest {
2912 orig_pl_secs: 1,
2913 orig_vl_secs: 1,
2914 new_pl_secs: 0,
2915 new_vl_secs: 0,
2916 effective_new_vl_secs: 1,
2917 }; "deprecate address only with new valid lifetime of zero")]
2918 #[test_case(RefreshStableAddressTimersTest {
2919 orig_pl_secs: ONE_HOUR_AS_SECS,
2920 orig_vl_secs: ONE_HOUR_AS_SECS,
2921 new_pl_secs: ONE_HOUR_AS_SECS - 1,
2922 new_vl_secs: ONE_HOUR_AS_SECS - 1,
2923 effective_new_vl_secs: ONE_HOUR_AS_SECS,
2924 }; "decrease preferred lifetime and ignore new valid lifetime if less than 2 hours and remaining lifetime")]
2925 #[test_case(RefreshStableAddressTimersTest {
2926 orig_pl_secs: THREE_HOURS_AS_SECS,
2927 orig_vl_secs: THREE_HOURS_AS_SECS,
2928 new_pl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS - 1,
2929 new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS - 1,
2930 effective_new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS,
2931 }; "deprecate address only and bring valid lifetime down to 2 hours at max")]
2932 #[test_case(RefreshStableAddressTimersTest {
2933 orig_pl_secs: ONE_HOUR_AS_SECS - 1,
2934 orig_vl_secs: ONE_HOUR_AS_SECS - 1,
2935 new_pl_secs: ONE_HOUR_AS_SECS - 1,
2936 new_vl_secs: ONE_HOUR_AS_SECS,
2937 effective_new_vl_secs: ONE_HOUR_AS_SECS,
2938 }; "increase valid lifetime if more than remaining valid lifetime")]
2939 #[test_case(RefreshStableAddressTimersTest {
2940 orig_pl_secs: INFINITE_LIFETIME,
2941 orig_vl_secs: INFINITE_LIFETIME,
2942 new_pl_secs: INFINITE_LIFETIME,
2943 new_vl_secs: INFINITE_LIFETIME,
2944 effective_new_vl_secs: INFINITE_LIFETIME,
2945 }; "infinite lifetimes")]
2946 #[test_case(RefreshStableAddressTimersTest {
2947 orig_pl_secs: ONE_HOUR_AS_SECS,
2948 orig_vl_secs: TWO_HOURS_AS_SECS,
2949 new_pl_secs: TWO_HOURS_AS_SECS,
2950 new_vl_secs: INFINITE_LIFETIME,
2951 effective_new_vl_secs: INFINITE_LIFETIME,
2952 }; "update valid lifetime from finite to infinite")]
2953 #[test_case(RefreshStableAddressTimersTest {
2954 orig_pl_secs: ONE_HOUR_AS_SECS,
2955 orig_vl_secs: TWO_HOURS_AS_SECS,
2956 new_pl_secs: INFINITE_LIFETIME,
2957 new_vl_secs: INFINITE_LIFETIME,
2958 effective_new_vl_secs: INFINITE_LIFETIME,
2959 }; "update both lifetimes from finite to infinite")]
2960 #[test_case(RefreshStableAddressTimersTest {
2961 orig_pl_secs: TWO_HOURS_AS_SECS,
2962 orig_vl_secs: INFINITE_LIFETIME,
2963 new_pl_secs: ONE_HOUR_AS_SECS,
2964 new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS - 1,
2965 effective_new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS,
2966 }; "update valid lifetime from infinite to finite")]
2967 #[test_case(RefreshStableAddressTimersTest {
2968 orig_pl_secs: INFINITE_LIFETIME,
2969 orig_vl_secs: INFINITE_LIFETIME,
2970 new_pl_secs: ONE_HOUR_AS_SECS,
2971 new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS - 1,
2972 effective_new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS,
2973 }; "update both lifetimes from infinite to finite")]
2974 fn stable_address_timers(
2975 RefreshStableAddressTimersTest {
2976 orig_pl_secs,
2977 orig_vl_secs,
2978 new_pl_secs,
2979 new_vl_secs,
2980 effective_new_vl_secs,
2981 }: RefreshStableAddressTimersTest,
2982 ) {
2983 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2984 SlaacConfiguration {
2985 stable_address_configuration: StableSlaacAddressConfiguration::ENABLED_WITH_EUI64,
2986 ..Default::default()
2987 },
2988 Default::default(),
2989 None,
2990 DEFAULT_RETRANS_TIMER,
2991 );
2992
2993 let addr_sub = testutil::calculate_slaac_addr_sub(SUBNET, IID);
2994
2995 let deprecate_timer_id = InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_sub.addr() };
2996 let invalidate_timer_id =
2997 InnerSlaacTimerId::InvalidateSlaacAddress { addr: addr_sub.addr() };
2998
2999 let ndp_pl = NonZeroNdpLifetime::from_u32_with_infinite(orig_pl_secs);
3001 let ndp_vl = NonZeroNdpLifetime::from_u32_with_infinite(orig_vl_secs);
3002 SlaacHandler::apply_slaac_update(
3003 &mut core_ctx,
3004 &mut bindings_ctx,
3005 &FakeDeviceId,
3006 SUBNET,
3007 ndp_pl,
3008 ndp_vl,
3009 );
3010 let now = bindings_ctx.now();
3011 let mut expected_timers = Vec::new();
3012 let valid_until = match ndp_vl.expect("this test expects to create an address") {
3013 NonZeroNdpLifetime::Finite(d) => {
3014 let valid_until = now + d.get();
3015 expected_timers.push((invalidate_timer_id, (), valid_until));
3016 Lifetime::Finite(valid_until)
3017 }
3018 NonZeroNdpLifetime::Infinite => Lifetime::Infinite,
3019 };
3020 match ndp_pl {
3021 None | Some(NonZeroNdpLifetime::Infinite) => {}
3022 Some(NonZeroNdpLifetime::Finite(d)) => {
3023 expected_timers.push((deprecate_timer_id, (), now + d.get()))
3024 }
3025 }
3026 let entry = SlaacAddressEntry {
3027 addr_sub,
3028 config: Ipv6AddrSlaacConfig {
3029 inner: SlaacConfig::Stable {
3030 valid_until,
3031 creation_time: bindings_ctx.now(),
3032 regen_counter: 0,
3033 dad_counter: 0,
3034 },
3035 preferred_lifetime: PreferredLifetime::maybe_preferred_for(now, ndp_pl),
3036 },
3037 };
3038 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
3039 core_ctx.state.slaac_state.timers.assert_timers(expected_timers);
3040
3041 let ndp_pl = NonZeroNdpLifetime::from_u32_with_infinite(new_pl_secs);
3043 SlaacHandler::apply_slaac_update(
3044 &mut core_ctx,
3045 &mut bindings_ctx,
3046 &FakeDeviceId,
3047 SUBNET,
3048 ndp_pl,
3049 NonZeroNdpLifetime::from_u32_with_infinite(new_vl_secs),
3050 );
3051 let mut expected_timers = Vec::new();
3052 let valid_until = match NonZeroNdpLifetime::from_u32_with_infinite(effective_new_vl_secs)
3053 .expect("this test expects to keep the address")
3054 {
3055 NonZeroNdpLifetime::Finite(d) => {
3056 let valid_until = now + d.get();
3057 expected_timers.push((invalidate_timer_id, (), valid_until));
3058 Lifetime::Finite(valid_until)
3059 }
3060 NonZeroNdpLifetime::Infinite => Lifetime::Infinite,
3061 };
3062 match ndp_pl {
3063 None | Some(NonZeroNdpLifetime::Infinite) => {}
3064 Some(NonZeroNdpLifetime::Finite(d)) => {
3065 expected_timers.push((deprecate_timer_id, (), now + d.get()))
3066 }
3067 }
3068 let entry = SlaacAddressEntry {
3069 config: Ipv6AddrSlaacConfig {
3070 inner: SlaacConfig::Stable {
3071 valid_until,
3072 creation_time: bindings_ctx.now(),
3073 regen_counter: 0,
3074 dad_counter: 0,
3075 },
3076 preferred_lifetime: PreferredLifetime::maybe_preferred_for(now, ndp_pl),
3077 },
3078 ..entry
3079 };
3080 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
3081 core_ctx.state.slaac_state.timers.assert_timers(expected_timers);
3082 }
3083
3084 const TEMP_SECRET_KEY: IidSecret = IidSecret::ALL_ONES;
3085 const STABLE_SECRET_KEY: IidSecret = IidSecret::ALL_TWOS;
3086
3087 const ONE_HOUR: NonZeroDuration = NonZeroDuration::from_secs(ONE_HOUR_AS_SECS as u64).unwrap();
3088
3089 struct DontGenerateTemporaryAddressTest {
3090 preferred_lifetime_config: NonZeroDuration,
3091 preferred_lifetime_secs: u32,
3092 valid_lifetime_secs: u32,
3093 temp_idgen_retries: u8,
3094 dad_transmits: u16,
3095 retrans_timer: Duration,
3096 enable: bool,
3097 }
3098
3099 impl DontGenerateTemporaryAddressTest {
3100 fn with_pl_less_than_regen_advance(
3101 dad_transmits: u16,
3102 retrans_timer: Duration,
3103 temp_idgen_retries: u8,
3104 ) -> Self {
3105 DontGenerateTemporaryAddressTest {
3106 preferred_lifetime_config: ONE_HOUR,
3107 preferred_lifetime_secs: u32::try_from(
3108 (SLAAC_MIN_REGEN_ADVANCE.get()
3109 + (u32::from(temp_idgen_retries)
3110 * u32::from(dad_transmits)
3111 * retrans_timer))
3112 .as_secs(),
3113 )
3114 .unwrap()
3115 - 1,
3116 valid_lifetime_secs: TWO_HOURS_AS_SECS,
3117 temp_idgen_retries,
3118 dad_transmits,
3119 retrans_timer,
3120 enable: true,
3121 }
3122 }
3123 }
3124
3125 #[test_case(DontGenerateTemporaryAddressTest {
3126 preferred_lifetime_config: ONE_HOUR,
3127 preferred_lifetime_secs: ONE_HOUR_AS_SECS,
3128 valid_lifetime_secs: TWO_HOURS_AS_SECS,
3129 temp_idgen_retries: 0,
3130 dad_transmits: 0,
3131 retrans_timer: DEFAULT_RETRANS_TIMER,
3132 enable: false,
3133 }; "disabled")]
3134 #[test_case(DontGenerateTemporaryAddressTest{
3135 preferred_lifetime_config: ONE_HOUR,
3136 preferred_lifetime_secs: 0,
3137 valid_lifetime_secs: 0,
3138 temp_idgen_retries: 0,
3139 dad_transmits: 0,
3140 retrans_timer: DEFAULT_RETRANS_TIMER,
3141 enable: true,
3142 }; "zero lifetimes")]
3143 #[test_case(DontGenerateTemporaryAddressTest {
3144 preferred_lifetime_config: ONE_HOUR,
3145 preferred_lifetime_secs: TWO_HOURS_AS_SECS,
3146 valid_lifetime_secs: ONE_HOUR_AS_SECS,
3147 temp_idgen_retries: 0,
3148 dad_transmits: 0,
3149 retrans_timer: DEFAULT_RETRANS_TIMER,
3150 enable: true,
3151 }; "preferred larger than valid")]
3152 #[test_case(DontGenerateTemporaryAddressTest {
3153 preferred_lifetime_config: ONE_HOUR,
3154 preferred_lifetime_secs: 0,
3155 valid_lifetime_secs: TWO_HOURS_AS_SECS,
3156 temp_idgen_retries: 0,
3157 dad_transmits: 0,
3158 retrans_timer: DEFAULT_RETRANS_TIMER,
3159 enable: true,
3160 }; "not preferred")]
3161 #[test_case(DontGenerateTemporaryAddressTest::with_pl_less_than_regen_advance(
3162 0 ,
3163 DEFAULT_RETRANS_TIMER ,
3164 0 ,
3165 ); "preferred lifetime less than regen advance with no DAD transmits")]
3166 #[test_case(DontGenerateTemporaryAddressTest::with_pl_less_than_regen_advance(
3167 1 ,
3168 DEFAULT_RETRANS_TIMER ,
3169 0 ,
3170 ); "preferred lifetime less than regen advance with DAD transmits")]
3171 #[test_case(DontGenerateTemporaryAddressTest::with_pl_less_than_regen_advance(
3172 1 ,
3173 DEFAULT_RETRANS_TIMER ,
3174 1 ,
3175 ); "preferred lifetime less than regen advance with DAD transmits and retries")]
3176 #[test_case(DontGenerateTemporaryAddressTest::with_pl_less_than_regen_advance(
3177 2 ,
3178 DEFAULT_RETRANS_TIMER + Duration::from_secs(1) ,
3179 3 ,
3180 ); "preferred lifetime less than regen advance with multiple DAD transmits and multiple retries")]
3181 #[test_case(DontGenerateTemporaryAddressTest {
3182 preferred_lifetime_config: SLAAC_MIN_REGEN_ADVANCE,
3183 preferred_lifetime_secs: ONE_HOUR_AS_SECS,
3184 valid_lifetime_secs: TWO_HOURS_AS_SECS,
3185 temp_idgen_retries: 1,
3186 dad_transmits: 1,
3187 retrans_timer: DEFAULT_RETRANS_TIMER,
3188 enable: true,
3189 }; "configured preferred lifetime less than regen advance")]
3190 fn dont_generate_temporary_address(
3191 DontGenerateTemporaryAddressTest {
3192 preferred_lifetime_config,
3193 preferred_lifetime_secs,
3194 valid_lifetime_secs,
3195 temp_idgen_retries,
3196 dad_transmits,
3197 retrans_timer,
3198 enable,
3199 }: DontGenerateTemporaryAddressTest,
3200 ) {
3201 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
3202 SlaacConfiguration {
3203 temporary_address_configuration: if enable {
3204 TemporarySlaacAddressConfiguration::Enabled {
3205 temp_valid_lifetime: ONE_HOUR,
3206 temp_preferred_lifetime: preferred_lifetime_config,
3207 temp_idgen_retries,
3208 }
3209 } else {
3210 TemporarySlaacAddressConfiguration::Disabled
3211 },
3212 ..Default::default()
3213 },
3214 Default::default(),
3215 NonZeroU16::new(dad_transmits),
3216 retrans_timer,
3217 );
3218
3219 SlaacHandler::apply_slaac_update(
3220 &mut core_ctx,
3221 &mut bindings_ctx,
3222 &FakeDeviceId,
3223 SUBNET,
3224 NonZeroNdpLifetime::from_u32_with_infinite(preferred_lifetime_secs),
3225 NonZeroNdpLifetime::from_u32_with_infinite(valid_lifetime_secs),
3226 );
3227 assert_empty(core_ctx.state.iter_slaac_addrs());
3228 bindings_ctx.timers.assert_no_timers_installed();
3229 }
3230
3231 struct GenerateTemporaryAddressTest {
3232 pl_config: u32,
3233 vl_config: u32,
3234 dad_transmits: u16,
3235 retrans_timer: Duration,
3236 temp_idgen_retries: u8,
3237 pl_ra: u32,
3238 vl_ra: u32,
3239 expected_pl_addr: u32,
3240 expected_vl_addr: u32,
3241 }
3242 #[test_case(GenerateTemporaryAddressTest{
3243 pl_config: ONE_HOUR_AS_SECS,
3244 vl_config: ONE_HOUR_AS_SECS,
3245 dad_transmits: 0,
3246 retrans_timer: DEFAULT_RETRANS_TIMER,
3247 temp_idgen_retries: 0,
3248 pl_ra: ONE_HOUR_AS_SECS,
3249 vl_ra: ONE_HOUR_AS_SECS,
3250 expected_pl_addr: ONE_HOUR_AS_SECS,
3251 expected_vl_addr: ONE_HOUR_AS_SECS,
3252 }; "config and prefix same lifetimes")]
3253 #[test_case(GenerateTemporaryAddressTest{
3254 pl_config: ONE_HOUR_AS_SECS,
3255 vl_config: TWO_HOURS_AS_SECS,
3256 dad_transmits: 0,
3257 retrans_timer: DEFAULT_RETRANS_TIMER,
3258 temp_idgen_retries: 0,
3259 pl_ra: THREE_HOURS_AS_SECS,
3260 vl_ra: THREE_HOURS_AS_SECS,
3261 expected_pl_addr: ONE_HOUR_AS_SECS,
3262 expected_vl_addr: TWO_HOURS_AS_SECS,
3263 }; "config smaller than prefix lifetimes")]
3264 #[test_case(GenerateTemporaryAddressTest{
3265 pl_config: TWO_HOURS_AS_SECS,
3266 vl_config: THREE_HOURS_AS_SECS,
3267 dad_transmits: 0,
3268 retrans_timer: DEFAULT_RETRANS_TIMER,
3269 temp_idgen_retries: 0,
3270 pl_ra: ONE_HOUR_AS_SECS,
3271 vl_ra: TWO_HOURS_AS_SECS,
3272 expected_pl_addr: ONE_HOUR_AS_SECS,
3273 expected_vl_addr: TWO_HOURS_AS_SECS,
3274 }; "config larger than prefix lifetimes")]
3275 #[test_case(GenerateTemporaryAddressTest{
3276 pl_config: TWO_HOURS_AS_SECS,
3277 vl_config: THREE_HOURS_AS_SECS,
3278 dad_transmits: 0,
3279 retrans_timer: DEFAULT_RETRANS_TIMER,
3280 temp_idgen_retries: 0,
3281 pl_ra: INFINITE_LIFETIME,
3282 vl_ra: INFINITE_LIFETIME,
3283 expected_pl_addr: TWO_HOURS_AS_SECS,
3284 expected_vl_addr: THREE_HOURS_AS_SECS,
3285 }; "prefix with infinite lifetimes")]
3286 #[test_case(GenerateTemporaryAddressTest{
3287 pl_config: TWO_HOURS_AS_SECS,
3288 vl_config: THREE_HOURS_AS_SECS,
3289 dad_transmits: 1,
3290 retrans_timer: DEFAULT_RETRANS_TIMER,
3291 temp_idgen_retries: 0,
3292 pl_ra: INFINITE_LIFETIME,
3293 vl_ra: INFINITE_LIFETIME,
3294 expected_pl_addr: TWO_HOURS_AS_SECS,
3295 expected_vl_addr: THREE_HOURS_AS_SECS,
3296 }; "generate_with_dad_enabled")]
3297 #[test_case(GenerateTemporaryAddressTest{
3298 pl_config: TWO_HOURS_AS_SECS,
3299 vl_config: THREE_HOURS_AS_SECS,
3300 dad_transmits: 2,
3301 retrans_timer: Duration::from_secs(5),
3302 temp_idgen_retries: 3,
3303 pl_ra: INFINITE_LIFETIME,
3304 vl_ra: INFINITE_LIFETIME,
3305 expected_pl_addr: TWO_HOURS_AS_SECS,
3306 expected_vl_addr: THREE_HOURS_AS_SECS,
3307 }; "generate_with_dad_enabled_and_retries")]
3308 #[test_case(GenerateTemporaryAddressTest{
3309 pl_config: TWO_HOURS_AS_SECS,
3310 vl_config: THREE_HOURS_AS_SECS,
3311 dad_transmits: 1,
3312 retrans_timer: Duration::from_secs(10),
3313 temp_idgen_retries: 0,
3314 pl_ra: INFINITE_LIFETIME,
3315 vl_ra: INFINITE_LIFETIME,
3316 expected_pl_addr: TWO_HOURS_AS_SECS,
3317 expected_vl_addr: THREE_HOURS_AS_SECS,
3318 }; "generate_with_dad_enabled_but_no_retries")]
3319 fn generate_temporary_address(
3320 GenerateTemporaryAddressTest {
3321 pl_config,
3322 vl_config,
3323 dad_transmits,
3324 retrans_timer,
3325 temp_idgen_retries,
3326 pl_ra,
3327 vl_ra,
3328 expected_pl_addr,
3329 expected_vl_addr,
3330 }: GenerateTemporaryAddressTest,
3331 ) {
3332 let pl_config = Duration::from_secs(pl_config.into());
3333 let regen_advance = regen_advance(temp_idgen_retries, retrans_timer, dad_transmits);
3334
3335 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
3336 SlaacConfiguration {
3337 temporary_address_configuration: TemporarySlaacAddressConfiguration::Enabled {
3338 temp_valid_lifetime: NonZeroDuration::new(Duration::from_secs(
3339 vl_config.into(),
3340 ))
3341 .unwrap(),
3342 temp_preferred_lifetime: NonZeroDuration::new(pl_config).unwrap(),
3343 temp_idgen_retries,
3344 },
3345 ..Default::default()
3346 },
3347 Default::default(),
3348 NonZeroU16::new(dad_transmits),
3349 retrans_timer,
3350 );
3351
3352 let mut dup_rng = bindings_ctx.rng().deep_clone();
3353
3354 struct AddrProps {
3355 desync_factor: Duration,
3356 valid_until: FakeInstant,
3357 preferred_until: FakeInstant,
3358 entry: SlaacAddressEntry<FakeInstant>,
3359 deprecate_timer_id: InnerSlaacTimerId,
3360 invalidate_timer_id: InnerSlaacTimerId,
3361 regenerate_timer_id: InnerSlaacTimerId,
3362 }
3363
3364 let addr_props = |rng: &mut FakeCryptoRng<_>,
3365 creation_time,
3366 config_greater_than_ra_desync_factor_offset| {
3367 let valid_until = creation_time + Duration::from_secs(expected_vl_addr.into());
3368 let addr_sub =
3369 generate_global_temporary_address(&SUBNET, &IID, rng.random(), &TEMP_SECRET_KEY);
3370 let desync_factor =
3371 desync_factor(rng, NonZeroDuration::new(pl_config).unwrap(), regen_advance)
3372 .unwrap();
3373 let preferred_until = {
3374 let d = creation_time + Duration::from_secs(expected_pl_addr.into());
3375 if pl_config.as_secs() > pl_ra.into() {
3376 d + config_greater_than_ra_desync_factor_offset
3377 } else {
3378 d - desync_factor
3379 }
3380 };
3381
3382 AddrProps {
3383 desync_factor,
3384 valid_until,
3385 preferred_until,
3386 entry: SlaacAddressEntry {
3387 addr_sub,
3388 config: Ipv6AddrSlaacConfig {
3389 inner: SlaacConfig::Temporary(TemporarySlaacConfig {
3390 valid_until,
3391 desync_factor,
3392 creation_time,
3393 dad_counter: 0,
3394 }),
3395 preferred_lifetime: PreferredLifetime::preferred_until(preferred_until),
3396 },
3397 },
3398 deprecate_timer_id: InnerSlaacTimerId::DeprecateSlaacAddress {
3399 addr: addr_sub.addr(),
3400 },
3401 invalidate_timer_id: InnerSlaacTimerId::InvalidateSlaacAddress {
3402 addr: addr_sub.addr(),
3403 },
3404 regenerate_timer_id: InnerSlaacTimerId::RegenerateTemporaryAddress {
3405 addr_subnet: addr_sub,
3406 },
3407 }
3408 };
3409
3410 SlaacHandler::apply_slaac_update(
3412 &mut core_ctx,
3413 &mut bindings_ctx,
3414 &FakeDeviceId,
3415 SUBNET,
3416 NonZeroNdpLifetime::from_u32_with_infinite(pl_ra),
3417 NonZeroNdpLifetime::from_u32_with_infinite(vl_ra),
3418 );
3419 let AddrProps {
3420 desync_factor: first_desync_factor,
3421 valid_until: first_valid_until,
3422 preferred_until: first_preferred_until,
3423 entry: first_entry,
3424 deprecate_timer_id: first_deprecate_timer_id,
3425 invalidate_timer_id: first_invalidate_timer_id,
3426 regenerate_timer_id: first_regenerate_timer_id,
3427 } = addr_props(&mut dup_rng, bindings_ctx.now(), Duration::ZERO);
3428 assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [first_entry]);
3429 core_ctx.state.slaac_state.timers.assert_timers([
3430 (first_deprecate_timer_id, (), first_preferred_until),
3431 (first_invalidate_timer_id, (), first_valid_until),
3432 (first_regenerate_timer_id, (), first_preferred_until - regen_advance.get()),
3433 ]);
3434
3435 assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()),);
3438 let AddrProps {
3439 desync_factor: second_desync_factor,
3440 valid_until: second_valid_until,
3441 preferred_until: second_preferred_until,
3442 entry: second_entry,
3443 deprecate_timer_id: second_deprecate_timer_id,
3444 invalidate_timer_id: second_invalidate_timer_id,
3445 regenerate_timer_id: second_regenerate_timer_id,
3446 } = addr_props(&mut dup_rng, bindings_ctx.now(), first_desync_factor);
3447 assert_eq!(
3448 core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(),
3449 [first_entry, second_entry]
3450 );
3451 let second_regen_at = second_preferred_until - regen_advance.get();
3452 core_ctx.state.slaac_state.timers.assert_timers([
3453 (first_deprecate_timer_id, (), first_preferred_until),
3454 (first_invalidate_timer_id, (), first_valid_until),
3455 (second_deprecate_timer_id, (), second_preferred_until),
3456 (second_invalidate_timer_id, (), second_valid_until),
3457 (second_regenerate_timer_id, (), second_regen_at),
3458 ]);
3459
3460 assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()),);
3462 let first_entry = first_entry.to_deprecated();
3463 assert_eq!(
3464 core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(),
3465 [first_entry, second_entry]
3466 );
3467 core_ctx.state.slaac_state.timers.assert_timers([
3468 (first_invalidate_timer_id, (), first_valid_until),
3469 (second_deprecate_timer_id, (), second_preferred_until),
3470 (second_invalidate_timer_id, (), second_valid_until),
3471 (second_regenerate_timer_id, (), second_regen_at),
3472 ]);
3473
3474 let third_created_at = {
3475 let expected_timer_order = if first_valid_until > second_regen_at {
3476 [second_regenerate_timer_id, second_deprecate_timer_id, first_invalidate_timer_id]
3477 } else {
3478 [first_invalidate_timer_id, second_regenerate_timer_id, second_deprecate_timer_id]
3479 };
3480
3481 let mut third_created_at = None;
3482 for timer_id in expected_timer_order.iter() {
3483 let timer_id = *timer_id;
3484
3485 core_ctx.state.slaac_state.timers.assert_top(&timer_id, &());
3486 assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()));
3487
3488 if timer_id == second_regenerate_timer_id {
3489 assert_eq!(third_created_at, None);
3490 third_created_at = Some(bindings_ctx.now());
3491 }
3492 }
3493
3494 third_created_at.unwrap()
3495 };
3496
3497 let AddrProps {
3500 desync_factor: _,
3501 valid_until: third_valid_until,
3502 preferred_until: third_preferred_until,
3503 entry: third_entry,
3504 deprecate_timer_id: third_deprecate_timer_id,
3505 invalidate_timer_id: third_invalidate_timer_id,
3506 regenerate_timer_id: third_regenerate_timer_id,
3507 } = addr_props(&mut dup_rng, third_created_at, first_desync_factor + second_desync_factor);
3508 let second_entry = second_entry.to_deprecated();
3509 assert_eq!(
3510 core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(),
3511 [second_entry, third_entry]
3512 );
3513 core_ctx.state.slaac_state.timers.assert_timers([
3514 (second_invalidate_timer_id, (), second_valid_until),
3515 (third_deprecate_timer_id, (), third_preferred_until),
3516 (third_invalidate_timer_id, (), third_valid_until),
3517 (third_regenerate_timer_id, (), third_preferred_until - regen_advance.get()),
3518 ]);
3519 }
3520
3521 #[test]
3522 fn temporary_address_not_updated_while_disabled() {
3523 let want_valid_until =
3524 FakeInstant::default() + Duration::from_secs(THREE_HOURS_AS_SECS.into());
3525 let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
3526 SlaacConfiguration {
3527 stable_address_configuration: StableSlaacAddressConfiguration::Disabled,
3528 temporary_address_configuration: TemporarySlaacAddressConfiguration::Disabled,
3529 },
3530 FakeSlaacAddrs {
3531 slaac_addrs: vec![SlaacAddressEntry {
3532 addr_sub: testutil::calculate_slaac_addr_sub(SUBNET, IID),
3533 config: Ipv6AddrSlaacConfig {
3534 inner: SlaacConfig::Temporary(TemporarySlaacConfig {
3535 valid_until: want_valid_until,
3536 desync_factor: Duration::default(),
3537 creation_time: FakeInstant::default(),
3538 dad_counter: 0,
3539 }),
3540 preferred_lifetime: PreferredLifetime::preferred_forever(),
3541 },
3542 }],
3543 ..Default::default()
3544 },
3545 None, DEFAULT_RETRANS_TIMER,
3547 );
3548
3549 SlaacHandler::apply_slaac_update(
3550 &mut core_ctx,
3551 &mut bindings_ctx,
3552 &FakeDeviceId,
3553 SUBNET,
3554 NonZeroNdpLifetime::from_u32_with_infinite(FOUR_HOURS_AS_SECS),
3555 NonZeroNdpLifetime::from_u32_with_infinite(FOUR_HOURS_AS_SECS),
3556 );
3557 let addrs = core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>();
3558 assert_eq!(addrs.len(), 1);
3559 let SlaacAddressEntry { config: Ipv6AddrSlaacConfig { inner, preferred_lifetime }, .. } =
3560 addrs[0];
3561 assert_matches!(inner,SlaacConfig::Temporary(TemporarySlaacConfig {
3562 valid_until,
3563 ..
3564 }) => {
3565 assert_eq!(valid_until, want_valid_until);
3566 });
3567 assert_eq!(preferred_lifetime, PreferredLifetime::Deprecated);
3572 }
3573}