netstack3_ip/device/
slaac.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! IPv6 Stateless Address Autoconfiguration (SLAAC) as defined by [RFC 4862]
6//! and temporary address extensions for SLAAC as defined by [RFC 8981].
7//!
8//! [RFC 4862]: https://datatracker.ietf.org/doc/html/rfc4862
9//! [RFC 8981]: https://datatracker.ietf.org/doc/html/rfc8981
10
11use alloc::vec::Vec;
12use core::marker::PhantomData;
13use core::num::NonZeroU16;
14use core::ops::ControlFlow;
15use core::time::Duration;
16
17use assert_matches::assert_matches;
18use log::{debug, error, trace, warn};
19use net_types::ip::{AddrSubnet, Ip as _, IpAddress, Ipv6, Ipv6Addr, Subnet};
20use net_types::Witness as _;
21use netstack3_base::{
22    AnyDevice, CoreTimerContext, Counter, CounterContext, DeviceIdContext, DeviceIdentifier,
23    EventContext, ExistsError, HandleableTimer, Instant, InstantBindingsTypes, InstantContext,
24    LocalTimerHeap, NotFoundError, RngContext, TimerBindingsTypes, TimerContext,
25    WeakDeviceIdentifier,
26};
27use packet_formats::icmp::ndp::NonZeroNdpLifetime;
28use packet_formats::utils::NonZeroDuration;
29use rand::distributions::Uniform;
30use rand::Rng;
31
32use crate::device::Ipv6AddrSlaacConfig;
33use crate::internal::device::opaque_iid::{IidSecret, OpaqueIid, OpaqueIidNonce};
34use crate::internal::device::state::{
35    Lifetime, PreferredLifetime, SlaacConfig, TemporarySlaacConfig,
36};
37use crate::internal::device::{
38    AddressRemovedReason, IpDeviceEvent, Ipv6DeviceAddr, Ipv6LinkLayerAddr,
39};
40
41/// Minimum Valid Lifetime value to actually update an address's valid lifetime.
42///
43/// 2 hours.
44const MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE: NonZeroDuration =
45    NonZeroDuration::new(Duration::from_secs(7200)).unwrap();
46
47/// Required prefix length for SLAAC.
48///
49/// We need 64 bits in the prefix because the interface identifier is 64 bits,
50/// and IPv6 addresses are 128 bits.
51const REQUIRED_PREFIX_BITS: u8 = 64;
52
53/// The maximum number of times to attempt to regenerate a SLAAC address after
54/// a local conflict (as opposed to DAD failure), either with an address already
55/// assigned to the interface or with an IANA-reserved IID, before stopping and
56/// giving up on address generation for that prefix.
57const MAX_LOCAL_REGEN_ATTEMPTS: u8 = 10;
58
59/// Internal SLAAC timer ID key for [`SlaacState`]'s `LocalTimerHeap`.
60#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
61#[allow(missing_docs)]
62pub enum InnerSlaacTimerId {
63    /// Timer to deprecate an address configured via SLAAC.
64    DeprecateSlaacAddress { addr: Ipv6DeviceAddr },
65    /// Timer to invalidate an address configured via SLAAC.
66    InvalidateSlaacAddress { addr: Ipv6DeviceAddr },
67    /// Timer to generate a new temporary SLAAC address before an existing one
68    /// expires.
69    RegenerateTemporaryAddress { addr_subnet: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> },
70}
71
72/// Global Slaac state on a device.
73pub struct SlaacState<BT: SlaacBindingsTypes> {
74    timers: LocalTimerHeap<InnerSlaacTimerId, (), BT>,
75}
76
77impl<BC: SlaacBindingsTypes + TimerContext> SlaacState<BC> {
78    /// Constructs a new SLAAC state for `device_id`.
79    pub fn new<D: WeakDeviceIdentifier, CC: CoreTimerContext<SlaacTimerId<D>, BC>>(
80        bindings_ctx: &mut BC,
81        device_id: D,
82    ) -> Self {
83        Self {
84            timers: LocalTimerHeap::new_with_context::<_, CC>(
85                bindings_ctx,
86                SlaacTimerId { device_id },
87            ),
88        }
89    }
90
91    /// Provides direct access to the internal timer heap.
92    #[cfg(any(test, feature = "testutils"))]
93    pub fn timers(&self) -> &LocalTimerHeap<InnerSlaacTimerId, (), BC> {
94        &self.timers
95    }
96}
97
98/// A timer ID for SLAAC.
99#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
100pub struct SlaacTimerId<D: WeakDeviceIdentifier> {
101    device_id: D,
102}
103
104impl<D: WeakDeviceIdentifier> SlaacTimerId<D> {
105    pub(super) fn device_id(&self) -> &D {
106        let Self { device_id } = self;
107        device_id
108    }
109
110    /// Creates a new SLAAC timer id for `device_id`.
111    #[cfg(any(test, feature = "testutils"))]
112    pub fn new(device_id: D) -> Self {
113        Self { device_id }
114    }
115}
116
117/// The state associated with a SLAAC address.
118#[derive(Copy, Clone, Debug, Eq, PartialEq)]
119pub struct SlaacAddressEntry<Instant> {
120    /// The address and the subnet.
121    pub addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
122    /// The address' SLAAC configuration.
123    pub config: Ipv6AddrSlaacConfig<Instant>,
124}
125
126/// A mutable view into state associated with a SLAAC address's mutable state.
127pub struct SlaacAddressEntryMut<'a, Instant> {
128    /// The address and the subnet.
129    pub addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
130    /// Mutable access to the address' SLAAC configuration.
131    pub config: &'a mut Ipv6AddrSlaacConfig<Instant>,
132}
133
134/// Abstracts iteration over a device's SLAAC addresses.
135pub trait SlaacAddresses<BT: SlaacBindingsTypes> {
136    /// Returns an iterator providing a mutable view of mutable SLAAC address
137    /// state.
138    fn for_each_addr_mut<F: FnMut(SlaacAddressEntryMut<'_, BT::Instant>)>(&mut self, cb: F);
139
140    /// The iterator provided to `with_addrs`.
141    type AddrsIter<'x>: Iterator<Item = SlaacAddressEntry<BT::Instant>>;
142
143    /// Calls the callback with an iterator over the addresses.
144    fn with_addrs<O, F: FnOnce(Self::AddrsIter<'_>) -> O>(&mut self, cb: F) -> O;
145
146    /// Adds `addr_sub` with `config` to the device and calls `and_then` with
147    /// the newly added entry.
148    fn add_addr_sub_and_then<O, F: FnOnce(SlaacAddressEntryMut<'_, BT::Instant>, &mut BT) -> O>(
149        &mut self,
150        bindings_ctx: &mut BT,
151        addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
152        config: Ipv6AddrSlaacConfig<BT::Instant>,
153        and_then: F,
154    ) -> Result<O, ExistsError>;
155
156    /// Removes a SLAAC address.
157    ///
158    /// # Panics
159    ///
160    /// May panic if `addr` is not an address recognized.
161    fn remove_addr(
162        &mut self,
163        bindings_ctx: &mut BT,
164        addr: &Ipv6DeviceAddr,
165    ) -> Result<
166        (AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>, Ipv6AddrSlaacConfig<BT::Instant>),
167        NotFoundError,
168    >;
169}
170
171/// Supports [`SlaacContext::with_slaac_addrs_mut_and_configs`].
172///
173/// Contains the fields necessary for the SLAAC state machine.
174pub struct SlaacConfigAndState<A: Ipv6LinkLayerAddr, BT: SlaacBindingsTypes> {
175    /// The current config for the device.
176    pub config: SlaacConfiguration,
177    /// The configured number of DAD transmits.
178    pub dad_transmits: Option<NonZeroU16>,
179    /// The configured retransmission timer (can be learned from the network).
180    pub retrans_timer: Duration,
181    /// The link-layer address of the interface, if it has one.
182    ///
183    /// Used to generate IIDs for stable addresses.
184    pub link_layer_addr: Option<A>,
185    /// Secret key for generating temporary addresses.
186    pub temp_secret_key: IidSecret,
187    /// Secret key for generating stable addresses.
188    pub stable_secret_key: IidSecret,
189    #[allow(missing_docs)]
190    pub _marker: PhantomData<BT>,
191}
192
193/// The execution context for SLAAC.
194pub trait SlaacContext<BC: SlaacBindingsContext<Self::DeviceId>>:
195    DeviceIdContext<AnyDevice>
196{
197    /// A link-layer address.
198    type LinkLayerAddr: Ipv6LinkLayerAddr;
199
200    /// The inner [`SlaacAddresses`] impl.
201    type SlaacAddrs<'a>: SlaacAddresses<BC> + CounterContext<SlaacCounters> + 'a;
202
203    /// Calls `cb` with access to the SLAAC addresses and configuration for
204    /// `device_id`.
205    fn with_slaac_addrs_mut_and_configs<
206        O,
207        F: FnOnce(
208            &mut Self::SlaacAddrs<'_>,
209            SlaacConfigAndState<Self::LinkLayerAddr, BC>,
210            &mut SlaacState<BC>,
211        ) -> O,
212    >(
213        &mut self,
214        device_id: &Self::DeviceId,
215        cb: F,
216    ) -> O;
217
218    /// Calls `cb` with access to the SLAAC addresses for `device_id`.
219    fn with_slaac_addrs_mut<O, F: FnOnce(&mut Self::SlaacAddrs<'_>, &mut SlaacState<BC>) -> O>(
220        &mut self,
221        device_id: &Self::DeviceId,
222        cb: F,
223    ) -> O {
224        self.with_slaac_addrs_mut_and_configs(device_id, |addrs, _config, state| cb(addrs, state))
225    }
226}
227
228/// Counters for SLAAC.
229#[derive(Default)]
230pub struct SlaacCounters {
231    /// Count of already exists errors when adding a generated SLAAC address.
232    pub generated_slaac_addr_exists: Counter,
233}
234
235/// Update the instant at which an address configured via SLAAC is no longer
236/// valid.
237///
238/// A `None` value for `valid_until` indicates that the address is valid
239/// forever; `Some` indicates valid for some finite lifetime.
240///
241/// # Panics
242///
243/// May panic if `addr` is not an address configured via SLAAC on
244/// `device_id`.
245fn update_slaac_addr_valid_until<I: Instant>(
246    slaac_config: &mut SlaacConfig<I>,
247    valid_until: Lifetime<I>,
248) {
249    match slaac_config {
250        SlaacConfig::Stable { valid_until: v, .. } => *v = valid_until,
251        SlaacConfig::Temporary(TemporarySlaacConfig { valid_until: v, .. }) => {
252            *v = match valid_until {
253                Lifetime::Finite(v) => v,
254                Lifetime::Infinite => panic!("temporary addresses may not be valid forever"),
255            }
256        }
257    };
258}
259
260/// The bindings types for SLAAC.
261pub trait SlaacBindingsTypes: InstantBindingsTypes + TimerBindingsTypes {}
262impl<BT> SlaacBindingsTypes for BT where BT: InstantBindingsTypes + TimerBindingsTypes {}
263
264/// The bindings execution context for SLAAC.
265pub trait SlaacBindingsContext<D>:
266    RngContext + TimerContext + EventContext<IpDeviceEvent<D, Ipv6, Self::Instant>> + SlaacBindingsTypes
267{
268}
269impl<D, BC> SlaacBindingsContext<D> for BC where
270    BC: RngContext
271        + TimerContext
272        + EventContext<IpDeviceEvent<D, Ipv6, Self::Instant>>
273        + SlaacBindingsTypes
274{
275}
276
277/// An implementation of SLAAC.
278pub trait SlaacHandler<BC: InstantContext>: DeviceIdContext<AnyDevice> {
279    /// Executes the algorithm in [RFC 4862 Section 5.5.3], with the extensions
280    /// from [RFC 8981 Section 3.4] for temporary addresses, for a given prefix
281    /// advertised by a router.
282    ///
283    /// This function updates all stable and temporary SLAAC addresses for the
284    /// given prefix and adds new ones if necessary.
285    ///
286    /// [RFC 4862 Section 5.5.3]: http://tools.ietf.org/html/rfc4862#section-5.5.3
287    /// [RFC 8981 Section 3.4]: https://tools.ietf.org/html/rfc8981#section-3.4
288    fn apply_slaac_update(
289        &mut self,
290        bindings_ctx: &mut BC,
291        device_id: &Self::DeviceId,
292        prefix: Subnet<Ipv6Addr>,
293        preferred_lifetime: Option<NonZeroNdpLifetime>,
294        valid_lifetime: Option<NonZeroNdpLifetime>,
295    );
296
297    /// Generates a link-local SLAAC address for the given interface.
298    fn generate_link_local_address(&mut self, bindings_ctx: &mut BC, device_id: &Self::DeviceId);
299
300    /// Handles SLAAC specific aspects of address removal.
301    ///
302    /// Must only be called after the address is removed from the interface.
303    fn on_address_removed(
304        &mut self,
305        bindings_ctx: &mut BC,
306        device_id: &Self::DeviceId,
307        addr: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
308        addr_config: Ipv6AddrSlaacConfig<BC::Instant>,
309        reason: AddressRemovedReason,
310    );
311
312    /// Removes all SLAAC addresses assigned to the device.
313    fn remove_all_slaac_addresses(&mut self, bindings_ctx: &mut BC, device_id: &Self::DeviceId);
314}
315
316impl<BC: SlaacBindingsContext<CC::DeviceId>, CC: SlaacContext<BC>> SlaacHandler<BC> for CC {
317    fn apply_slaac_update(
318        &mut self,
319        bindings_ctx: &mut BC,
320        device_id: &Self::DeviceId,
321        subnet: Subnet<Ipv6Addr>,
322        preferred_lifetime: Option<NonZeroNdpLifetime>,
323        valid_lifetime: Option<NonZeroNdpLifetime>,
324    ) {
325        if preferred_lifetime > valid_lifetime {
326            // If the preferred lifetime is greater than the valid lifetime,
327            // silently ignore the Prefix Information option, as per RFC 4862
328            // section 5.5.3.
329            trace!("receive_ndp_packet: autonomous prefix's preferred lifetime is greater than valid lifetime, ignoring");
330            return;
331        }
332
333        let mut seen_stable = false;
334        let mut seen_temporary = false;
335
336        let now = bindings_ctx.now();
337        self.with_slaac_addrs_mut_and_configs(device_id, |slaac_addrs, config, slaac_state| {
338            let SlaacConfigAndState { config: device_config, dad_transmits, retrans_timer, .. } =
339                config;
340            // Apply the update to each existing address, stable or temporary, for the
341            // prefix.
342            slaac_addrs.for_each_addr_mut(|address_entry| {
343                let slaac_type = match apply_slaac_update_to_addr(
344                    address_entry,
345                    slaac_state,
346                    subnet,
347                    device_id,
348                    valid_lifetime,
349                    preferred_lifetime,
350                    &device_config,
351                    now,
352                    retrans_timer,
353                    dad_transmits,
354                    bindings_ctx,
355                ) {
356                    ControlFlow::Break(()) => return,
357                    ControlFlow::Continue(slaac_type) => slaac_type,
358                };
359                // Mark the SLAAC address type as existing so we know not to
360                // generate an address for the type later.
361                //
362                // Note that SLAAC addresses are never invalidated/removed
363                // in response to a prefix update and addresses types never
364                // change after the address is added.
365                match slaac_type {
366                    SlaacType::Stable => seen_stable = true,
367                    SlaacType::Temporary => seen_temporary = true,
368                }
369            });
370
371            // As per RFC 4862 section 5.5.3.e, if the prefix advertised is not equal to
372            // the prefix of an address configured by stateless autoconfiguration
373            // already in the list of addresses associated with the interface, and if
374            // the Valid Lifetime is not 0, form an address (and add it to the list) by
375            // combining the advertised prefix with an interface identifier of the link
376            // as follows:
377            //
378            // |    128 - N bits    |        N bits          |
379            // +--------------------+------------------------+
380            // |    link prefix     |  interface identifier  |
381            // +---------------------------------------------+
382            let valid_lifetime = match valid_lifetime {
383                Some(valid_lifetime) => valid_lifetime,
384                None => {
385                    trace!(
386                        "receive_ndp_packet: autonomous prefix has valid \
387                            lifetime = 0, ignoring"
388                    );
389                    return;
390                }
391            };
392
393            let address_types_to_add = (!seen_stable)
394                .then_some({
395                    // As per RFC 4862 Section 5.5.3.d,
396                    //
397                    //   If the prefix advertised is not equal to the prefix of an
398                    //   address configured by stateless autoconfiguration already
399                    //   in the list of addresses associated with the interface
400                    //   (where 'equal' means the two prefix lengths are the same
401                    //   and the first prefix- length bits of the prefixes are
402                    //   identical), and if the Valid Lifetime is not 0, form an
403                    //   address [...].
404                    SlaacType::Stable
405                })
406                .into_iter()
407                .chain((!seen_temporary).then_some({
408                    // As per RFC 8981 Section 3.4.3,
409                    //
410                    //   If the host has not configured any temporary
411                    //   address for the corresponding prefix, the host
412                    //   SHOULD create a new temporary address for such
413                    //   prefix.
414                    SlaacType::Temporary
415                }));
416
417            for slaac_type in address_types_to_add {
418                add_slaac_addr_sub::<_, CC>(
419                    bindings_ctx,
420                    device_id,
421                    slaac_addrs,
422                    &config,
423                    slaac_state,
424                    now,
425                    SlaacInitConfig::new(slaac_type),
426                    valid_lifetime,
427                    preferred_lifetime,
428                    &subnet,
429                );
430            }
431        });
432    }
433
434    fn generate_link_local_address(&mut self, bindings_ctx: &mut BC, device_id: &Self::DeviceId) {
435        let now = bindings_ctx.now();
436        self.with_slaac_addrs_mut_and_configs(device_id, |addrs, config, slaac_state| {
437            // Configure a link-local address via SLAAC.
438            //
439            // Per [RFC 4862 Section 5.3]: "A link-local address has an infinite preferred
440            // and valid lifetime; it is never timed out."
441            //
442            // [RFC 4862 Section 5.3]: https://tools.ietf.org/html/rfc4862#section-5.3
443            let link_local_subnet =
444                Subnet::new(Ipv6::LINK_LOCAL_UNICAST_SUBNET.network(), REQUIRED_PREFIX_BITS)
445                    .expect("link local subnet should be valid");
446            add_slaac_addr_sub::<_, CC>(
447                bindings_ctx,
448                device_id,
449                addrs,
450                &config,
451                slaac_state,
452                now,
453                SlaacInitConfig::new(SlaacType::Stable),
454                NonZeroNdpLifetime::Infinite,       /* valid_lifetime */
455                Some(NonZeroNdpLifetime::Infinite), /* preferred_lifetime */
456                &link_local_subnet,
457            );
458        });
459    }
460
461    fn on_address_removed(
462        &mut self,
463        bindings_ctx: &mut BC,
464        device_id: &Self::DeviceId,
465        addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
466        addr_config: Ipv6AddrSlaacConfig<BC::Instant>,
467        reason: AddressRemovedReason,
468    ) {
469        self.with_slaac_addrs_mut_and_configs(device_id, |addrs, config, slaac_state| {
470            on_address_removed_inner::<_, CC>(
471                bindings_ctx,
472                addr_sub,
473                device_id,
474                addrs,
475                config,
476                slaac_state,
477                addr_config,
478                reason,
479            )
480        });
481    }
482
483    fn remove_all_slaac_addresses(&mut self, bindings_ctx: &mut BC, device_id: &Self::DeviceId) {
484        self.with_slaac_addrs_mut(device_id, |slaac_addrs, _| {
485            slaac_addrs
486                .with_addrs(|addrs| addrs.map(|a| a.addr_sub.addr()).collect::<Vec<_>>())
487                .into_iter()
488                .filter_map(|addr| {
489                    slaac_addrs.remove_addr(bindings_ctx, &addr).map(Some).unwrap_or_else(
490                        |NotFoundError| {
491                            // We're not holding locks on the assigned addresses
492                            // here, so we can't assume a race is impossible with
493                            // something else removing the address. Just assume that
494                            // it is gone.
495                            None
496                        },
497                    )
498                })
499                .collect::<Vec<_>>()
500        })
501        .into_iter()
502        .for_each(|(addr, config)| {
503            self.on_address_removed(
504                bindings_ctx,
505                device_id,
506                addr,
507                config,
508                AddressRemovedReason::Manual,
509            )
510        })
511    }
512}
513
514fn on_address_removed_inner<BC: SlaacBindingsContext<CC::DeviceId>, CC: SlaacContext<BC>>(
515    bindings_ctx: &mut BC,
516    addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
517    device_id: &CC::DeviceId,
518    slaac_addrs: &mut CC::SlaacAddrs<'_>,
519    config: SlaacConfigAndState<CC::LinkLayerAddr, BC>,
520    slaac_state: &mut SlaacState<BC>,
521    addr_config: Ipv6AddrSlaacConfig<BC::Instant>,
522    reason: AddressRemovedReason,
523) {
524    let SlaacState { timers } = slaac_state;
525    let preferred_until = timers
526        .cancel(bindings_ctx, &InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_sub.addr() })
527        .map(|(t, ())| t);
528    let _valid_until: Option<(BC::Instant, ())> = timers
529        .cancel(bindings_ctx, &InnerSlaacTimerId::InvalidateSlaacAddress { addr: addr_sub.addr() });
530
531    let now = bindings_ctx.now();
532
533    let Ipv6AddrSlaacConfig { inner, preferred_lifetime } = addr_config;
534
535    match inner {
536        SlaacConfig::Temporary(TemporarySlaacConfig {
537            valid_until,
538            creation_time,
539            desync_factor,
540            dad_counter,
541        }) => {
542            let _regen_at: Option<(BC::Instant, ())> = timers.cancel(
543                bindings_ctx,
544                &InnerSlaacTimerId::RegenerateTemporaryAddress { addr_subnet: addr_sub },
545            );
546
547            match reason {
548                AddressRemovedReason::Manual => return,
549                AddressRemovedReason::DadFailed => {
550                    // Attempt to regenerate the address.
551                }
552            }
553
554            let temp_valid_lifetime = match config.config.temporary_address_configuration {
555                TemporarySlaacAddressConfiguration::Enabled {
556                    temp_idgen_retries,
557                    temp_valid_lifetime,
558                    temp_preferred_lifetime: _,
559                } => {
560                    if dad_counter >= temp_idgen_retries {
561                        return;
562                    }
563                    temp_valid_lifetime
564                }
565                TemporarySlaacAddressConfiguration::Disabled => return,
566            };
567
568            // Compute the original preferred lifetime for the removed address so that
569            // it can be used for the new address being generated. If, when the address
570            // was created, the prefix's preferred lifetime was less than
571            // `temporary_address_configuration.temp_preferred_lifetime`, then that's
572            // what will be calculated here. That's fine because it's a lower bound on
573            // the prefix's value, which means the prefix's value is still being
574            // respected.
575            let preferred_for = match preferred_until.map(|preferred_until| {
576                preferred_until.saturating_duration_since(creation_time) + desync_factor
577            }) {
578                Some(preferred_for) => preferred_for,
579                // If the address is already deprecated, a new address should already
580                // have been generated, so ignore this one.
581                None => return,
582            };
583
584            // It's possible this `valid_for` value is larger than `temp_valid_lifetime`
585            // (e.g. if the NDP configuration was changed since this address was
586            // generated). That's okay, because `add_slaac_addr_sub` will apply the
587            // current maximum valid lifetime when called below.
588            let valid_for =
589                NonZeroDuration::new(valid_until.saturating_duration_since(creation_time))
590                    .unwrap_or(temp_valid_lifetime);
591
592            add_slaac_addr_sub::<_, CC>(
593                bindings_ctx,
594                device_id,
595                slaac_addrs,
596                &config,
597                slaac_state,
598                now,
599                SlaacInitConfig::Temporary { dad_count: dad_counter + 1 },
600                NonZeroNdpLifetime::Finite(valid_for),
601                NonZeroDuration::new(preferred_for).map(NonZeroNdpLifetime::Finite),
602                &addr_sub.subnet(),
603            );
604        }
605        SlaacConfig::Stable { valid_until, creation_time, regen_counter, dad_counter } => {
606            match reason {
607                AddressRemovedReason::Manual => return,
608                AddressRemovedReason::DadFailed => {
609                    // Attempt to regenerate the address.
610                }
611            }
612
613            match config.config.stable_address_configuration {
614                // If DAD failure raced with stable SLAAC being disabled, don't attempt to
615                // regenerate the address.
616                StableSlaacAddressConfiguration::Disabled => return,
617                StableSlaacAddressConfiguration::Enabled { iid_generation } => match iid_generation
618                {
619                    // If DAD failure raced with the IID generation config changing to EUI-64, don't
620                    // attempt to regenerate the address.
621                    IidGenerationConfiguration::Eui64 => return,
622                    IidGenerationConfiguration::Opaque { idgen_retries } => {
623                        if dad_counter >= idgen_retries {
624                            return;
625                        }
626                    }
627                },
628            }
629
630            // TODO(https://fxbug.dev/394628149): regenerate address on a delay to avoid
631            // lockstep behavior of multiple hosts.
632
633            let valid_for = match valid_until {
634                Lifetime::Infinite => NonZeroNdpLifetime::Infinite,
635                Lifetime::Finite(valid_until) => {
636                    let Some(valid_for) =
637                        NonZeroDuration::new(valid_until.saturating_duration_since(creation_time))
638                    else {
639                        // The address is already invalid; do not regenerate it.
640                        return;
641                    };
642                    NonZeroNdpLifetime::Finite(valid_for)
643                }
644            };
645
646            // Rather than gleaning the preferred lifetime from the presence or absence of a
647            // deprecation timer as we do for temporary addresses, use the preferred
648            // lifetime that was originally configured for the address, as it's possible
649            // that it was configured with an infinite lifetime and therefore no deprecation
650            // timer was scheduled.
651            let preferred_for = match preferred_lifetime {
652                PreferredLifetime::Deprecated => None,
653                PreferredLifetime::Preferred(lifetime) => match lifetime {
654                    Lifetime::Infinite => Some(NonZeroNdpLifetime::Infinite),
655                    Lifetime::Finite(preferred_until) => NonZeroDuration::new(
656                        preferred_until.saturating_duration_since(creation_time),
657                    )
658                    .map(NonZeroNdpLifetime::Finite),
659                },
660            };
661
662            add_slaac_addr_sub::<_, CC>(
663                bindings_ctx,
664                device_id,
665                slaac_addrs,
666                &config,
667                slaac_state,
668                now,
669                SlaacInitConfig::Stable { regen_count: regen_counter, dad_count: dad_counter + 1 },
670                valid_for,
671                preferred_for,
672                &addr_sub.subnet(),
673            );
674        }
675    }
676}
677
678fn apply_slaac_update_to_addr<D: DeviceIdentifier, BC: SlaacBindingsContext<D>>(
679    address_entry: SlaacAddressEntryMut<'_, BC::Instant>,
680    state: &mut SlaacState<BC>,
681    subnet: Subnet<Ipv6Addr>,
682    device_id: &D,
683    valid_lifetime: Option<NonZeroNdpLifetime>,
684    addr_preferred_lifetime: Option<NonZeroNdpLifetime>,
685    config: &SlaacConfiguration,
686    now: <BC as InstantBindingsTypes>::Instant,
687    retrans_timer: Duration,
688    dad_transmits: Option<NonZeroU16>,
689    bindings_ctx: &mut BC,
690) -> ControlFlow<(), SlaacType> {
691    let SlaacAddressEntryMut {
692        addr_sub,
693        config: Ipv6AddrSlaacConfig { inner: slaac_config, preferred_lifetime },
694    } = address_entry;
695    let SlaacState { timers } = state;
696    if addr_sub.subnet() != subnet {
697        return ControlFlow::Break(());
698    }
699    let addr = addr_sub.addr();
700    let slaac_type = SlaacType::from(&*slaac_config);
701    trace!(
702        "receive_ndp_packet: already have a {:?} SLAAC address {:?} configured on device {:?}",
703        slaac_type,
704        addr_sub,
705        device_id
706    );
707
708    /// Encapsulates a lifetime bound and where it came from.
709    #[derive(Copy, Clone)]
710    enum ValidLifetimeBound {
711        FromPrefix(Option<NonZeroNdpLifetime>),
712        FromMaxBound(Duration),
713    }
714    impl ValidLifetimeBound {
715        /// Unwraps the object and returns the wrapped duration.
716        fn get(self) -> Option<NonZeroNdpLifetime> {
717            match self {
718                Self::FromPrefix(d) => d,
719                Self::FromMaxBound(d) => NonZeroDuration::new(d).map(NonZeroNdpLifetime::Finite),
720            }
721        }
722    }
723    let (valid_for, entry_valid_until, preferred_for_and_regen_at) = match slaac_config {
724        SlaacConfig::Stable {
725            valid_until: entry_valid_until,
726            creation_time: _,
727            regen_counter: _,
728            dad_counter: _,
729        } => (
730            ValidLifetimeBound::FromPrefix(valid_lifetime),
731            *entry_valid_until,
732            addr_preferred_lifetime.map(|p| (p, None)),
733        ),
734        // Select valid_for and preferred_for according to RFC 8981
735        // Section 3.4.
736        SlaacConfig::Temporary(TemporarySlaacConfig {
737            valid_until: entry_valid_until,
738            creation_time,
739            desync_factor,
740            dad_counter: _,
741        }) => {
742            let SlaacConfiguration {
743                stable_address_configuration: _,
744                temporary_address_configuration,
745            } = config;
746            let (valid_for, preferred_for, entry_valid_until) =
747                match temporary_address_configuration {
748                    // Since it's possible to change NDP configuration for a
749                    // device during runtime, we can end up here, with a
750                    // temporary address on an interface even though temporary
751                    // addressing is disabled. Don't update the valid or
752                    // preferred lifetimes in this case.
753                    TemporarySlaacAddressConfiguration::Disabled => {
754                        (ValidLifetimeBound::FromMaxBound(Duration::ZERO), None, *entry_valid_until)
755                    }
756                    TemporarySlaacAddressConfiguration::Enabled {
757                        temp_preferred_lifetime,
758                        temp_valid_lifetime,
759                        temp_idgen_retries: _,
760                    } => {
761                        // RFC 8981 Section 3.4.2:
762                        //   When updating the preferred lifetime of an existing
763                        //   temporary address, it would be set to expire at
764                        //   whichever time is earlier: the time indicated by
765                        //   the received lifetime or (CREATION_TIME +
766                        //   TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR). A similar
767                        //   approach can be used with the valid lifetime.
768                        let preferred_for =
769                            addr_preferred_lifetime.and_then(|preferred_lifetime| {
770                                temp_preferred_lifetime
771                                    .get()
772                                    .checked_sub(now.saturating_duration_since(*creation_time))
773                                    .and_then(|p| p.checked_sub(*desync_factor))
774                                    .and_then(NonZeroDuration::new)
775                                    .map(|d| preferred_lifetime.min_finite_duration(d))
776                            });
777                        // Per RFC 8981 Section 3.4.1, `desync_factor` is only
778                        // used for preferred lifetime:
779                        //   [...] with the overall constraint that no temporary
780                        //   addresses should ever remain "valid" or "preferred"
781                        //   for a time longer than (TEMP_VALID_LIFETIME) or
782                        //   (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR),
783                        //   respectively.
784                        let since_creation = now.saturating_duration_since(*creation_time);
785                        let configured_max_lifetime = temp_valid_lifetime.get();
786                        let max_valid_lifetime = if since_creation > configured_max_lifetime {
787                            Duration::ZERO
788                        } else {
789                            configured_max_lifetime - since_creation
790                        };
791
792                        let valid_for = valid_lifetime.map_or(
793                            ValidLifetimeBound::FromPrefix(None),
794                            |d| match d {
795                                NonZeroNdpLifetime::Infinite => {
796                                    ValidLifetimeBound::FromMaxBound(max_valid_lifetime)
797                                }
798                                NonZeroNdpLifetime::Finite(d) => {
799                                    if max_valid_lifetime <= d.get() {
800                                        ValidLifetimeBound::FromMaxBound(max_valid_lifetime)
801                                    } else {
802                                        ValidLifetimeBound::FromPrefix(valid_lifetime)
803                                    }
804                                }
805                            },
806                        );
807
808                        (valid_for, preferred_for, *entry_valid_until)
809                    }
810                };
811
812            let preferred_for_and_regen_at = preferred_for.map(|preferred_for| {
813                let SlaacConfiguration {
814                    stable_address_configuration: _,
815                    temporary_address_configuration,
816                } = config;
817
818                let regen_at = match temporary_address_configuration {
819                    TemporarySlaacAddressConfiguration::Disabled => None,
820                    TemporarySlaacAddressConfiguration::Enabled {
821                        temp_idgen_retries,
822                        temp_preferred_lifetime: _,
823                        temp_valid_lifetime: _,
824                    } => {
825                        let regen_advance = regen_advance(
826                            *temp_idgen_retries,
827                            retrans_timer,
828                            dad_transmits.map_or(0, NonZeroU16::get),
829                        )
830                        .get();
831                        // Per RFC 8981 Section 3.6:
832                        //
833                        //   Hosts following this specification SHOULD
834                        //   generate new temporary addresses over time.
835                        //   This can be achieved by generating a new
836                        //   temporary address REGEN_ADVANCE time units
837                        //   before a temporary address becomes deprecated.
838                        //
839                        // It's possible for regen_at to be before the
840                        // current time. In that case, set it to `now` so
841                        // that a new address is generated after the current
842                        // prefix information is handled.
843                        preferred_for
844                            .get()
845                            .checked_sub(regen_advance)
846                            .map_or(Some(now), |d| now.checked_add(d))
847                    }
848                };
849
850                (NonZeroNdpLifetime::Finite(preferred_for), regen_at)
851            });
852
853            (valid_for, Lifetime::Finite(entry_valid_until), preferred_for_and_regen_at)
854        }
855    };
856
857    // `Some` iff the remaining lifetime is a positive non-zero lifetime.
858    let remaining_lifetime = match entry_valid_until {
859        Lifetime::Infinite => Some(Lifetime::Infinite),
860        Lifetime::Finite(entry_valid_until) => entry_valid_until
861            .checked_duration_since(now)
862            .and_then(NonZeroDuration::new)
863            .map(|d| Lifetime::Finite(d)),
864    };
865
866    // As per RFC 4862 section 5.5.3.e, if the advertised prefix is equal to the
867    // prefix of an address configured by stateless autoconfiguration in the
868    // list, the preferred lifetime of the address is reset to the Preferred
869    // Lifetime in the received advertisement.
870
871    // Update the preferred lifetime for this address.
872    let preferred_lifetime_updated = match preferred_for_and_regen_at {
873        None => {
874            if preferred_lifetime.is_deprecated() {
875                false
876            } else {
877                *preferred_lifetime = PreferredLifetime::Deprecated;
878                let _: Option<(BC::Instant, ())> =
879                    timers.cancel(bindings_ctx, &InnerSlaacTimerId::DeprecateSlaacAddress { addr });
880                let _: Option<(BC::Instant, ())> = timers.cancel(
881                    bindings_ctx,
882                    &InnerSlaacTimerId::RegenerateTemporaryAddress { addr_subnet: addr_sub },
883                );
884                true
885            }
886        }
887        Some((preferred_for, regen_at)) => {
888            let timer_id = InnerSlaacTimerId::DeprecateSlaacAddress { addr };
889            let preferred_instant = Lifetime::from_ndp(now, preferred_for);
890            match preferred_instant {
891                Lifetime::Finite(instant) => {
892                    let _previously_scheduled_instant: Option<(BC::Instant, ())> =
893                        timers.schedule_instant(bindings_ctx, timer_id, (), instant);
894                }
895                Lifetime::Infinite => {
896                    let _previously_scheduled_instant: Option<(BC::Instant, ())> =
897                        timers.cancel(bindings_ctx, &timer_id);
898                }
899            };
900            let new_lifetime = PreferredLifetime::Preferred(preferred_instant);
901            let updated = core::mem::replace(preferred_lifetime, new_lifetime) != new_lifetime;
902            let timer_id = InnerSlaacTimerId::RegenerateTemporaryAddress { addr_subnet: addr_sub };
903            let _prev_regen_at: Option<(BC::Instant, ())> = match regen_at {
904                Some(regen_at) => timers.schedule_instant(bindings_ctx, timer_id, (), regen_at),
905                None => timers.cancel(bindings_ctx, &timer_id),
906            };
907            updated
908        }
909    };
910
911    // As per RFC 4862 section 5.5.3.e, the specific action to perform for the
912    // valid lifetime of the address depends on the Valid Lifetime in the
913    // received advertisement and the remaining time to the valid lifetime
914    // expiration of the previously autoconfigured address:
915    let valid_for_to_update = match valid_for {
916        ValidLifetimeBound::FromMaxBound(valid_for) => {
917            // If the maximum lifetime for the address is smaller than the
918            // lifetime specified for the prefix, then it must be applied.
919            NonZeroDuration::new(valid_for).map(NonZeroNdpLifetime::Finite)
920        }
921        ValidLifetimeBound::FromPrefix(valid_for) => {
922            // If the received Valid Lifetime is greater than 2 hours or
923            // greater than RemainingLifetime, set the valid lifetime of
924            // the corresponding address to the advertised Valid
925            // Lifetime.
926            match valid_for {
927                Some(NonZeroNdpLifetime::Infinite) => Some(NonZeroNdpLifetime::Infinite),
928                Some(NonZeroNdpLifetime::Finite(v))
929                    if v > MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE
930                        || remaining_lifetime.map_or(true, |r| r < Lifetime::Finite(v)) =>
931                {
932                    Some(NonZeroNdpLifetime::Finite(v))
933                }
934                None | Some(NonZeroNdpLifetime::Finite(_)) => {
935                    if remaining_lifetime.map_or(true, |r| {
936                        r <= Lifetime::Finite(MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE)
937                    }) {
938                        // If RemainingLifetime is less than or equal to 2 hours,
939                        // ignore the Prefix Information option with regards to the
940                        // valid lifetime, unless the Router Advertisement from
941                        // which this option was obtained has been authenticated
942                        // (e.g., via Secure Neighbor Discovery [RFC3971]).  If the
943                        // Router Advertisement was authenticated, the valid
944                        // lifetime of the corresponding address should be set to
945                        // the Valid Lifetime in the received option.
946                        //
947                        // TODO(ghanan): If the NDP packet this prefix option is in
948                        //               was authenticated, update the valid
949                        //               lifetime of the address to the valid
950                        //               lifetime in the received option, as per RFC
951                        //               4862 section 5.5.3.e.
952                        None
953                    } else {
954                        // Otherwise, reset the valid lifetime of the corresponding
955                        // address to 2 hours.
956                        Some(NonZeroNdpLifetime::Finite(MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE))
957                    }
958                }
959            }
960        }
961    };
962
963    match valid_for_to_update {
964        Some(valid_for) => match Lifetime::from_ndp(now, valid_for) {
965            Lifetime::Finite(valid_until) => {
966                trace!(
967                    "receive_ndp_packet: updating valid lifetime to {:?} for SLAAC address {:?} on device {:?}",
968                    valid_until,
969                    addr,
970                    device_id
971                );
972
973                // Set the valid lifetime for this address.
974                update_slaac_addr_valid_until(slaac_config, Lifetime::Finite(valid_until));
975
976                let _: Option<(BC::Instant, ())> = timers.schedule_instant(
977                    bindings_ctx,
978                    InnerSlaacTimerId::InvalidateSlaacAddress { addr },
979                    (),
980                    valid_until,
981                );
982            }
983            Lifetime::Infinite => {
984                // Set the valid lifetime for this address.
985                update_slaac_addr_valid_until(slaac_config, Lifetime::Infinite);
986
987                let _: Option<(BC::Instant, ())> = timers
988                    .cancel(bindings_ctx, &InnerSlaacTimerId::InvalidateSlaacAddress { addr });
989            }
990        },
991        None => {
992            trace!(
993                "receive_ndp_packet: not updating valid lifetime for SLAAC address {:?} on device {:?} as remaining lifetime is less than 2 hours and new valid lifetime ({:?}) is less than remaining lifetime",
994                addr,
995                device_id,
996                valid_for.get()
997            );
998        }
999    }
1000
1001    if preferred_lifetime_updated || valid_for_to_update.is_some() {
1002        bindings_ctx.on_event(IpDeviceEvent::AddressPropertiesChanged {
1003            device: device_id.clone(),
1004            addr: addr_sub.addr().into(),
1005            valid_until: slaac_config.valid_until(),
1006            preferred_lifetime: *preferred_lifetime,
1007        });
1008    }
1009    ControlFlow::Continue(slaac_type)
1010}
1011
1012impl<BC: SlaacBindingsContext<CC::DeviceId>, CC: SlaacContext<BC>> HandleableTimer<CC, BC>
1013    for SlaacTimerId<CC::WeakDeviceId>
1014{
1015    fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, _: BC::UniqueTimerId) {
1016        let Self { device_id } = self;
1017        let Some(device_id) = device_id.upgrade() else {
1018            return;
1019        };
1020        core_ctx.with_slaac_addrs_mut_and_configs(&device_id, |addrs, config, slaac_state| {
1021            let Some((timer_id, ())) = slaac_state.timers.pop(bindings_ctx) else {
1022                return;
1023            };
1024            match timer_id {
1025                InnerSlaacTimerId::DeprecateSlaacAddress { addr } => {
1026                    addrs.for_each_addr_mut(|SlaacAddressEntryMut { addr_sub, config }| {
1027                        if addr_sub.addr() == addr {
1028                            config.preferred_lifetime = PreferredLifetime::Deprecated;
1029
1030                            bindings_ctx.on_event(IpDeviceEvent::AddressPropertiesChanged {
1031                                device: device_id.clone(),
1032                                addr: addr_sub.addr().into(),
1033                                valid_until: config.inner.valid_until(),
1034                                preferred_lifetime: config.preferred_lifetime,
1035                            });
1036                        }
1037                    })
1038                }
1039                InnerSlaacTimerId::InvalidateSlaacAddress { addr } => {
1040                    let (addr, slaac_config) = match addrs.remove_addr(bindings_ctx, &addr) {
1041                        Ok(addr_config) => addr_config,
1042                        Err(NotFoundError) => {
1043                            // Even though when a user removes an address we
1044                            // get notified, we could still race with our
1045                            // own timer here. This is a tight enough race
1046                            // that we can log at warn to call out in case
1047                            // something else is wrong. It should certainly
1048                            // not happen in tests, however.
1049                            #[cfg(test)]
1050                            panic!("Failed to remove address {addr} on invalidation");
1051                            #[cfg(not(test))]
1052                            {
1053                                log::warn!(
1054                                    "failed to remove SLAAC address {addr}, assuming raced \
1055                                        with user removal"
1056                                );
1057                                return;
1058                            }
1059                        }
1060                    };
1061
1062                    on_address_removed_inner::<_, CC>(
1063                        bindings_ctx,
1064                        addr,
1065                        &device_id,
1066                        addrs,
1067                        config,
1068                        slaac_state,
1069                        slaac_config,
1070                        AddressRemovedReason::Manual,
1071                    );
1072                }
1073                InnerSlaacTimerId::RegenerateTemporaryAddress { addr_subnet } => {
1074                    regenerate_temporary_slaac_addr::<_, CC>(
1075                        bindings_ctx,
1076                        addrs,
1077                        config,
1078                        slaac_state,
1079                        &device_id,
1080                        &addr_subnet,
1081                    );
1082                }
1083            }
1084        });
1085    }
1086}
1087
1088/// The method to use for generating the Interface Identifier portion of stable
1089/// SLAAC addresses.
1090#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1091pub enum IidGenerationConfiguration {
1092    /// Use the EUI-64 method described in [RFC 4291] to derive the IID from the MAC
1093    /// address.
1094    ///
1095    /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
1096    Eui64,
1097    /// Use the algorithm in [RFC 7217 Section 5] to generate opaque IIDs.
1098    ///
1099    /// [RFC 7217 Section 5]: https://tools.ietf.org/html/rfc7217/#section-5
1100    Opaque {
1101        /// The number of times to attempt to pick a new stable address after DAD
1102        /// detects a duplicate before stopping and giving up on stable address
1103        /// generation for that prefix.
1104        idgen_retries: u8,
1105    },
1106}
1107
1108/// Configuration values for SLAAC stable addressing.
1109#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
1110pub enum StableSlaacAddressConfiguration {
1111    /// Stable SLAAC address generation is enabled.
1112    Enabled {
1113        /// The method to use for generating the Interface Identifier portion of stable
1114        /// SLAAC addresses.
1115        iid_generation: IidGenerationConfiguration,
1116    },
1117    /// Stable SLAAC address generation is disabled.
1118    #[default]
1119    Disabled,
1120}
1121
1122impl StableSlaacAddressConfiguration {
1123    /// Default IDGEN_RETRIES specified by [RFC 7217 Section 7].
1124    ///
1125    /// [RFC 7217 Section 7]: https://tools.ietf.org/html/rfc7217#section-7
1126    pub const DEFAULT_IDGEN_RETRIES: u8 = 3;
1127
1128    /// Enable stable addressing, using the EUI-64 method to derive the IID.
1129    #[cfg(any(test, feature = "testutils"))]
1130    pub const ENABLED_WITH_EUI64: Self =
1131        Self::Enabled { iid_generation: IidGenerationConfiguration::Eui64 };
1132
1133    /// Enable stable addressing, using opaque IIDs.
1134    #[cfg(any(test, feature = "testutils"))]
1135    pub const ENABLED_WITH_OPAQUE_IIDS: Self = Self::Enabled {
1136        iid_generation: IidGenerationConfiguration::Opaque {
1137            idgen_retries: Self::DEFAULT_IDGEN_RETRIES,
1138        },
1139    };
1140}
1141
1142/// Configuration values for SLAAC temporary addressing.
1143///
1144/// The algorithm specified in [RFC 8981 Section 3.4] references several
1145/// configuration parameters, which are defined in [Section 3.8] and
1146/// [Section 3.3.2] This struct contains the following values specified by the
1147/// RFC:
1148/// - TEMP_VALID_LIFETIME
1149/// - TEMP_PREFERRED_LIFETIME
1150/// - TEMP_IDGEN_RETRIES
1151/// - secret_key
1152///
1153/// [RFC 8981 Section 3.4]: http://tools.ietf.org/html/rfc8981#section-3.4
1154/// [Section 3.3.2]: http://tools.ietf.org/html/rfc8981#section-3.3.2
1155/// [Section 3.8]: http://tools.ietf.org/html/rfc8981#section-3.8
1156#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
1157pub enum TemporarySlaacAddressConfiguration {
1158    /// Temporary SLAAC address generation is enabled.
1159    Enabled {
1160        /// The maximum amount of time that a temporary address can be considered
1161        /// valid, from the time of its creation.
1162        temp_valid_lifetime: NonZeroDuration,
1163
1164        /// The maximum amount of time that a temporary address can be preferred,
1165        /// from the time of its creation.
1166        temp_preferred_lifetime: NonZeroDuration,
1167
1168        /// The number of times to attempt to pick a new temporary address after DAD
1169        /// detects a duplicate before stopping and giving up on temporary address
1170        /// generation for that prefix.
1171        temp_idgen_retries: u8,
1172    },
1173    /// Temporary SLAAC address generation is disabled.
1174    #[default]
1175    Disabled,
1176}
1177
1178impl TemporarySlaacAddressConfiguration {
1179    /// Default TEMP_VALID_LIFETIME specified by [RFC 8981 Section 3.8].
1180    ///
1181    /// [RFC 8981 Section 3.8]: https://www.rfc-editor.org/rfc/rfc8981#section-3.8
1182    pub const DEFAULT_TEMP_VALID_LIFETIME: NonZeroDuration = // 2 days
1183        NonZeroDuration::from_secs(2 * 24 * 60 * 60u64).unwrap();
1184
1185    /// Default TEMP_PREFERRED_LIFETIME specified by [RFC 8981 Section 3.8].
1186    ///
1187    /// [RFC 8981 Section 3.8]: https://www.rfc-editor.org/rfc/rfc8981#section-3.8
1188    pub const DEFAULT_TEMP_PREFERRED_LIFETIME: NonZeroDuration = // 1 day
1189        NonZeroDuration::from_secs(1 * 24 * 60 * 60u64).unwrap();
1190
1191    /// Default TEMP_IDGEN_RETRIES specified by [RFC 8981 Section 3.8].
1192    ///
1193    /// [RFC 8981 Section 3.8]: https://www.rfc-editor.org/rfc/rfc8981#section-3.8
1194    pub const DEFAULT_TEMP_IDGEN_RETRIES: u8 = 3;
1195
1196    /// Constructs a new instance with default values.
1197    pub fn enabled_with_rfc_defaults() -> Self {
1198        Self::Enabled {
1199            temp_valid_lifetime: Self::DEFAULT_TEMP_VALID_LIFETIME,
1200            temp_preferred_lifetime: Self::DEFAULT_TEMP_PREFERRED_LIFETIME,
1201            temp_idgen_retries: Self::DEFAULT_TEMP_IDGEN_RETRIES,
1202        }
1203    }
1204
1205    /// Returns if `self` is enabled.
1206    pub fn is_enabled(&self) -> bool {
1207        match self {
1208            Self::Enabled { .. } => true,
1209            Self::Disabled => false,
1210        }
1211    }
1212}
1213
1214/// The configuration for SLAAC.
1215#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
1216pub struct SlaacConfiguration {
1217    /// Configuration for stable address assignment.
1218    pub stable_address_configuration: StableSlaacAddressConfiguration,
1219
1220    /// Configuration for temporary address assignment.
1221    pub temporary_address_configuration: TemporarySlaacAddressConfiguration,
1222}
1223
1224impl SlaacConfiguration {
1225    /// Updates self and returns the previous values in a new update structure.
1226    pub fn update(
1227        &mut self,
1228        SlaacConfigurationUpdate {
1229            stable_address_configuration,
1230            temporary_address_configuration,
1231        }: SlaacConfigurationUpdate,
1232    ) -> SlaacConfigurationUpdate {
1233        fn get_prev_and_update<T>(old: &mut T, update: Option<T>) -> Option<T> {
1234            update.map(|new| core::mem::replace(old, new))
1235        }
1236        SlaacConfigurationUpdate {
1237            stable_address_configuration: get_prev_and_update(
1238                &mut self.stable_address_configuration,
1239                stable_address_configuration,
1240            ),
1241            temporary_address_configuration: get_prev_and_update(
1242                &mut self.temporary_address_configuration,
1243                temporary_address_configuration,
1244            ),
1245        }
1246    }
1247}
1248
1249/// An update structure for [`SlaacConfiguration`].
1250///
1251/// Only fields with variant `Some` are updated.
1252#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
1253pub struct SlaacConfigurationUpdate {
1254    /// Configuration to enable stable address assignment.
1255    pub stable_address_configuration: Option<StableSlaacAddressConfiguration>,
1256
1257    /// Update value for temporary address configuration.
1258    pub temporary_address_configuration: Option<TemporarySlaacAddressConfiguration>,
1259}
1260
1261#[derive(PartialEq, Eq)]
1262enum SlaacType {
1263    Stable,
1264    Temporary,
1265}
1266
1267impl core::fmt::Debug for SlaacType {
1268    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1269        match self {
1270            SlaacType::Stable => f.write_str("stable"),
1271            SlaacType::Temporary => f.write_str("temporary"),
1272        }
1273    }
1274}
1275
1276impl<'a, Instant> From<&'a SlaacConfig<Instant>> for SlaacType {
1277    fn from(slaac_config: &'a SlaacConfig<Instant>) -> Self {
1278        match slaac_config {
1279            SlaacConfig::Stable { .. } => SlaacType::Stable,
1280            SlaacConfig::Temporary { .. } => SlaacType::Temporary,
1281        }
1282    }
1283}
1284
1285/// The minimum REGEN_ADVANCE as specified in [RFC 8981 Section 3.8].
1286///
1287/// [RFC 8981 Section 3.8]: https://datatracker.ietf.org/doc/html/rfc8981#section-3.8
1288// As per [RFC 8981 Section 3.8],
1289//
1290//   REGEN_ADVANCE
1291//      2 + (TEMP_IDGEN_RETRIES * DupAddrDetectTransmits * RetransTimer /
1292//      1000)
1293//
1294//      ..., such that REGEN_ADVANCE is expressed in seconds.
1295pub const SLAAC_MIN_REGEN_ADVANCE: NonZeroDuration = NonZeroDuration::from_secs(2).unwrap();
1296
1297/// Computes REGEN_ADVANCE as specified in [RFC 8981 Section 3.8].
1298///
1299/// [RFC 8981 Section 3.8]: http://tools.ietf.org/html/rfc8981#section-3.8
1300fn regen_advance(
1301    temp_idgen_retries: u8,
1302    retrans_timer: Duration,
1303    dad_transmits: u16,
1304) -> NonZeroDuration {
1305    // Per the RFC, REGEN_ADVANCE in seconds =
1306    //   2 + (TEMP_IDGEN_RETRIES * DupAddrDetectTransmits * RetransTimer / 1000)
1307    //
1308    // where RetransTimer is in milliseconds. Since values here are kept as
1309    // Durations, there is no need to apply scale factors.
1310    SLAAC_MIN_REGEN_ADVANCE
1311        + retrans_timer
1312            .checked_mul(u32::from(temp_idgen_retries) * u32::from(dad_transmits))
1313            .unwrap_or(Duration::ZERO)
1314}
1315
1316/// Computes the DESYNC_FACTOR as specified in [RFC 8981 section 3.8].
1317///
1318/// Per the RFC,
1319///
1320///    DESYNC_FACTOR
1321///       A random value within the range 0 - MAX_DESYNC_FACTOR.  It
1322///       is computed each time a temporary address is generated, and
1323///       is associated with the corresponding address.  It MUST be
1324///       smaller than (TEMP_PREFERRED_LIFETIME - REGEN_ADVANCE).
1325///
1326/// Returns `None` if a DESYNC_FACTOR value cannot be calculated. This will
1327/// occur when REGEN_ADVANCE is larger than TEMP_PREFERRED_LIFETIME as no valid
1328/// DESYNC_FACTOR exists that is greater than or equal to 0.
1329///
1330/// [RFC 8981 Section 3.8]: http://tools.ietf.org/html/rfc8981#section-3.8
1331fn desync_factor<R: Rng>(
1332    rng: &mut R,
1333    temp_preferred_lifetime: NonZeroDuration,
1334    regen_advance: NonZeroDuration,
1335) -> Option<Duration> {
1336    let temp_preferred_lifetime = temp_preferred_lifetime.get();
1337
1338    // Per RFC 8981 Section 3.8:
1339    //    MAX_DESYNC_FACTOR
1340    //       0.4 * TEMP_PREFERRED_LIFETIME.  Upper bound on DESYNC_FACTOR.
1341    //
1342    //       |  Rationale: Setting MAX_DESYNC_FACTOR to 0.4
1343    //       |  TEMP_PREFERRED_LIFETIME results in addresses that have
1344    //       |  statistically different lifetimes, and a maximum of three
1345    //       |  concurrent temporary addresses when the default values
1346    //       |  specified in this section are employed.
1347    //    DESYNC_FACTOR
1348    //       A random value within the range 0 - MAX_DESYNC_FACTOR.  It
1349    //       is computed each time a temporary address is generated, and
1350    //       is associated with the corresponding address.  It MUST be
1351    //       smaller than (TEMP_PREFERRED_LIFETIME - REGEN_ADVANCE).
1352    temp_preferred_lifetime.checked_sub(regen_advance.get()).map(|max_desync_factor| {
1353        let max_desync_factor =
1354            core::cmp::min(max_desync_factor, (temp_preferred_lifetime * 2) / 5);
1355        rng.sample(Uniform::new(Duration::ZERO, max_desync_factor))
1356    })
1357}
1358
1359fn regenerate_temporary_slaac_addr<BC: SlaacBindingsContext<CC::DeviceId>, CC: SlaacContext<BC>>(
1360    bindings_ctx: &mut BC,
1361    slaac_addrs: &mut CC::SlaacAddrs<'_>,
1362    config_and_state: SlaacConfigAndState<CC::LinkLayerAddr, BC>,
1363    slaac_state: &mut SlaacState<BC>,
1364    device_id: &CC::DeviceId,
1365    addr_subnet: &AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
1366) {
1367    let SlaacConfigAndState { config, .. } = config_and_state;
1368    let SlaacState { timers } = slaac_state;
1369    let now = bindings_ctx.now();
1370
1371    enum Action {
1372        SkipRegen,
1373        Regen { valid_for: NonZeroDuration, preferred_for: Duration },
1374    }
1375
1376    let action = slaac_addrs.with_addrs(|addrs| {
1377        let entry = {
1378            let mut found_entry = None;
1379
1380            for entry in addrs {
1381                if entry.addr_sub.subnet() != addr_subnet.subnet() {
1382                    continue;
1383                }
1384
1385                // It's possible that there are multiple non-deprecated temporary
1386                // addresses in a subnet for this host (if prefix updates are received
1387                // after regen but before deprecation). Per RFC 8981 Section 3.5:
1388                //
1389                //   Note that, in normal operation, except for the transient period
1390                //   when a temporary address is being regenerated, at most one
1391                //   temporary address per prefix should be in a nondeprecated state at
1392                //   any given time on a given interface.
1393                //
1394                // In order to tend towards only one non-deprecated temporary address on
1395                // a subnet, we ignore all but the last regen timer for the
1396                // non-deprecated addresses in a subnet.
1397                if !entry.config.preferred_lifetime.is_deprecated() {
1398                    if let Some((entry, regen_at)) = timers
1399                        .get(&InnerSlaacTimerId::RegenerateTemporaryAddress {
1400                            addr_subnet: entry.addr_sub,
1401                        })
1402                        .map(|(instant, ())| (entry, instant))
1403                    {
1404                        debug!(
1405                            "ignoring regen event at {:?} for {:?} since {:?} \
1406                            will regenerate after at {:?}",
1407                            bindings_ctx.now(),
1408                            addr_subnet,
1409                            entry.addr_sub.addr(),
1410                            regen_at
1411                        );
1412                        return Action::SkipRegen;
1413                    }
1414                }
1415
1416                if &entry.addr_sub == addr_subnet {
1417                    assert_matches!(found_entry, None);
1418                    found_entry = Some(entry);
1419                }
1420            }
1421
1422            found_entry.unwrap_or_else(|| panic!("couldn't find {:?} to regenerate", addr_subnet))
1423        };
1424
1425        assert!(
1426            !entry.config.preferred_lifetime.is_deprecated(),
1427            "can't regenerate deprecated address {:?}",
1428            addr_subnet
1429        );
1430
1431        let TemporarySlaacConfig { creation_time, desync_factor, valid_until, dad_counter: _ } =
1432            match entry.config.inner {
1433                SlaacConfig::Temporary(temporary_config) => temporary_config,
1434                SlaacConfig::Stable { .. } => unreachable!(
1435                    "can't regenerate a temporary address for {:?}, which is stable",
1436                    addr_subnet
1437                ),
1438            };
1439
1440        let temp_valid_lifetime = match config.temporary_address_configuration {
1441            TemporarySlaacAddressConfiguration::Enabled {
1442                temp_valid_lifetime,
1443                temp_preferred_lifetime: _,
1444                temp_idgen_retries: _,
1445            } => temp_valid_lifetime,
1446            TemporarySlaacAddressConfiguration::Disabled => return Action::SkipRegen,
1447        };
1448
1449        let (deprecate_at, ()) = timers
1450            .get(&InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_subnet.addr() })
1451            .unwrap_or_else(|| {
1452                unreachable!(
1453                    "temporary SLAAC address {:?} had a regen timer fire but \
1454                    does not have a deprecation timer",
1455                    addr_subnet.addr()
1456                )
1457            });
1458        let preferred_for = deprecate_at.saturating_duration_since(creation_time) + desync_factor;
1459
1460        // It's possible this `valid_for` value is larger than `temp_valid_lifetime`
1461        // (e.g. if the NDP configuration was changed since this address was
1462        // generated). That's okay, because `add_slaac_addr_sub` will apply the
1463        // current maximum valid lifetime when called below.
1464        let valid_for = valid_until
1465            .checked_duration_since(creation_time)
1466            .and_then(NonZeroDuration::new)
1467            .unwrap_or(temp_valid_lifetime);
1468
1469        Action::Regen { valid_for, preferred_for }
1470    });
1471
1472    match action {
1473        Action::SkipRegen => {}
1474        Action::Regen { valid_for, preferred_for } => add_slaac_addr_sub::<_, CC>(
1475            bindings_ctx,
1476            device_id,
1477            slaac_addrs,
1478            &config_and_state,
1479            slaac_state,
1480            now,
1481            SlaacInitConfig::Temporary { dad_count: 0 },
1482            NonZeroNdpLifetime::Finite(valid_for),
1483            NonZeroDuration::new(preferred_for).map(NonZeroNdpLifetime::Finite),
1484            &addr_subnet.subnet(),
1485        ),
1486    }
1487}
1488
1489#[derive(Copy, Clone, Debug)]
1490enum SlaacInitConfig {
1491    Stable {
1492        // The number of times the address has been regenerated to avoid either an IANA-
1493        // reserved IID or an address already assigned to the same interface.
1494        regen_count: u8,
1495        // The number of times the address has been regenerated due to DAD failure.
1496        dad_count: u8,
1497    },
1498    Temporary {
1499        dad_count: u8,
1500    },
1501}
1502
1503impl SlaacInitConfig {
1504    fn new(slaac_type: SlaacType) -> Self {
1505        match slaac_type {
1506            SlaacType::Stable => Self::Stable { regen_count: 0, dad_count: 0 },
1507            SlaacType::Temporary => Self::Temporary { dad_count: 0 },
1508        }
1509    }
1510}
1511
1512/// Checks whether the address has an IID that doesn't conflict with existing
1513/// IANA reserved ranges.
1514///
1515/// Compares against the ranges defined by various RFCs and listed at
1516/// https://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xhtml
1517fn has_iana_allowed_iid(address: Ipv6Addr) -> bool {
1518    let mut iid = [0u8; 8];
1519    const U64_SUFFIX_LEN: usize = Ipv6Addr::BYTES as usize - u64::BITS as usize / 8;
1520    iid.copy_from_slice(&address.bytes()[U64_SUFFIX_LEN..]);
1521    let iid = u64::from_be_bytes(iid);
1522    match iid {
1523        // Subnet-Router Anycast
1524        0x0000_0000_0000_0000 => false,
1525        // Consolidated match for
1526        // - Ethernet Block: 0x200:5EFF:FE00:0000-0200:4EFF:FE00:5212
1527        // - Proxy Mobile: 0x200:5EFF:FE00:5213
1528        // - Ethernet Block: 0x200:5EFF:FE00:5214-0200:4EFF:FEFF:FFFF
1529        0x0200_5EFF_FE00_0000..=0x0200_5EFF_FEFF_FFFF => false,
1530        // Subnet Anycast Addresses
1531        0xFDFF_FFFF_FFFF_FF80..=0xFDFF_FFFF_FFFF_FFFF => false,
1532
1533        // All other IIDs not in the reserved ranges
1534        _iid => true,
1535    }
1536}
1537
1538/// Generate a stable IPv6 Address as defined by RFC 4862 section 5.5.3.d.
1539///
1540/// The generated address will be of the format:
1541///
1542/// |            128 - N bits               |       N bits           |
1543/// +---------------------------------------+------------------------+
1544/// |            link prefix                |  interface identifier  |
1545/// +----------------------------------------------------------------+
1546///
1547/// # Panics
1548///
1549/// Panics if a valid IPv6 unicast address cannot be formed with the provided
1550/// prefix and interface identifier: for example, if the prefix length of the
1551/// provided subnet and the length of `iid` do not sum to 128 bits), or if the
1552/// prefix length is not a multiple of 8 bits.
1553fn generate_stable_address(
1554    prefix: &Subnet<Ipv6Addr>,
1555    iid: &[u8],
1556) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
1557    if prefix.prefix() % 8 != 0 {
1558        unimplemented!(
1559            "generate_stable_address: not implemented for when prefix length is not a multiple of \
1560            8 bits"
1561        );
1562    }
1563
1564    let mut address = prefix.network().ipv6_bytes();
1565    let prefix_len = usize::from(prefix.prefix() / 8);
1566    assert_eq!(address.len() - prefix_len, iid.len());
1567    address[prefix_len..].copy_from_slice(&iid);
1568
1569    let address = AddrSubnet::new(Ipv6Addr::from(address), prefix.prefix()).unwrap();
1570    assert_eq!(address.subnet(), *prefix);
1571
1572    address
1573}
1574
1575/// Generate a stable IPv6 Address with an opaque IID generated from the
1576/// provided parameters, as defined by RFC 7217.
1577fn generate_stable_address_with_opaque_iid(
1578    prefix: &Subnet<Ipv6Addr>,
1579    network_interface: &[u8],
1580    dad_counter: u8,
1581    secret_key: &IidSecret,
1582) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
1583    let iid = OpaqueIid::new(
1584        /* prefix */ *prefix,
1585        /* net_iface */ network_interface,
1586        /* net_id */ None::<&[_]>,
1587        /* nonce */ OpaqueIidNonce::DadCounter(dad_counter),
1588        /* secret_key */ secret_key,
1589    );
1590    let prefix_len = prefix.prefix() / 8;
1591    let iid = &iid.to_be_bytes()[..usize::from(Ipv6Addr::BYTES - prefix_len)];
1592
1593    generate_stable_address(prefix, iid)
1594}
1595
1596/// Generate a temporary IPv6 Global Address.
1597///
1598/// The generated address will be of the format:
1599///
1600/// |            128 - N bits              |        N bits           |
1601/// +--------------------------------------+-------------------------+
1602/// |            link prefix               |  randomized identifier  |
1603/// +----------------------------------------------------------------+
1604///
1605/// # Panics
1606///
1607/// Panics if a valid IPv6 unicast address cannot be formed with the provided
1608/// prefix, or if the prefix length is not a multiple of 8 bits.
1609fn generate_global_temporary_address(
1610    prefix: &Subnet<Ipv6Addr>,
1611    network_interface: &[u8],
1612    seed: u64,
1613    secret_key: &IidSecret,
1614) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
1615    let prefix_len = usize::from(prefix.prefix() / 8);
1616    let mut address = prefix.network().ipv6_bytes();
1617
1618    // TODO(https://fxbug.dev/368449998): Use the algorithm in RFC 8981
1619    // instead of the one for stable SLAAC addresses as described in RFC 7217.
1620    let interface_identifier = OpaqueIid::new(
1621        /* prefix */ *prefix,
1622        /* net_iface */ network_interface,
1623        /* net_id */ None::<[_; 0]>,
1624        /* nonce */ OpaqueIidNonce::Random(seed),
1625        /* secret_key */ secret_key,
1626    );
1627    let suffix_bytes = &interface_identifier.to_be_bytes()[..(address.len() - prefix_len)];
1628    address[prefix_len..].copy_from_slice(suffix_bytes);
1629
1630    let address = AddrSubnet::new(Ipv6Addr::from(address), prefix.prefix()).unwrap();
1631    assert_eq!(address.subnet(), *prefix);
1632
1633    address
1634}
1635
1636fn add_slaac_addr_sub<BC: SlaacBindingsContext<CC::DeviceId>, CC: SlaacContext<BC>>(
1637    bindings_ctx: &mut BC,
1638    device_id: &CC::DeviceId,
1639    slaac_addrs: &mut CC::SlaacAddrs<'_>,
1640    config: &SlaacConfigAndState<CC::LinkLayerAddr, BC>,
1641    slaac_state: &mut SlaacState<BC>,
1642    now: BC::Instant,
1643    slaac_config: SlaacInitConfig,
1644    prefix_valid_for: NonZeroNdpLifetime,
1645    prefix_preferred_for: Option<NonZeroNdpLifetime>,
1646    subnet: &Subnet<Ipv6Addr>,
1647) {
1648    if subnet.prefix() != REQUIRED_PREFIX_BITS {
1649        // If the sum of the prefix length and interface identifier length does
1650        // not equal 128 bits, the Prefix Information option MUST be ignored, as
1651        // per RFC 4862 section 5.5.3.
1652        error!(
1653            "receive_ndp_packet: autonomous prefix length {:?} and interface identifier length {:?} cannot form valid IPv6 address, ignoring",
1654            subnet.prefix(),
1655            REQUIRED_PREFIX_BITS
1656        );
1657        return;
1658    }
1659
1660    struct PreferredForAndRegenAt<Instant>(NonZeroNdpLifetime, Option<Instant>);
1661
1662    let SlaacConfigAndState {
1663        config,
1664        dad_transmits,
1665        retrans_timer,
1666        link_layer_addr,
1667        temp_secret_key,
1668        stable_secret_key,
1669        _marker,
1670    } = config;
1671
1672    let Some(link_layer_addr) = link_layer_addr else {
1673        warn!(
1674            "add_slaac_addr_sub: cannot derive IIDs for device {device_id:?} that does not support \
1675            link-layer addressing"
1676        );
1677        return;
1678    };
1679
1680    let SlaacConfiguration { stable_address_configuration, temporary_address_configuration } =
1681        config;
1682    let SlaacState { timers } = slaac_state;
1683
1684    let (valid_until, preferred_and_regen, mut addresses) = match slaac_config {
1685        SlaacInitConfig::Stable { mut regen_count, dad_count } => {
1686            let iid_generation = match stable_address_configuration {
1687                StableSlaacAddressConfiguration::Disabled => {
1688                    trace!("stable SLAAC addresses are disabled on device {:?}", device_id);
1689                    return;
1690                }
1691                StableSlaacAddressConfiguration::Enabled { iid_generation } => iid_generation,
1692            };
1693
1694            let valid_until = Lifetime::from_ndp(now, prefix_valid_for);
1695
1696            // Generate the address as defined by RFC 4862 section 5.5.3.d.
1697            //
1698            // We only use an opaque IID to generate the stable address if opaque IIDs are
1699            // enabled at both the interface level (via the IidGenerationConfiguration), and
1700            // at the global level for the entire stack (indicated by `stable_secret_key`
1701            // being non-None). If they are disabled at either level, EUI64-based IIDs are
1702            // used.
1703            let addresses = either::Either::Left(match iid_generation {
1704                IidGenerationConfiguration::Eui64 => {
1705                    // `regen_count` is only ever updated if we are using opaque IIDs to generate
1706                    // the address. When using EUI-64 IIDs, address regeneration is impossible and
1707                    // will not be attempted.
1708                    assert_eq!(regen_count, 0);
1709
1710                    // If this is an attempt to regenerate a stable address to avoid a conflict, we
1711                    // have to bail; there is no way to regenerate an address that is derived using
1712                    // EUI-64 (as opposed to opaque IIDs).
1713                    if dad_count != 0 {
1714                        return;
1715                    }
1716
1717                    let address = generate_stable_address(&subnet, &link_layer_addr.eui64_iid());
1718                    let config = SlaacConfig::Stable {
1719                        valid_until,
1720                        creation_time: now,
1721                        regen_counter: regen_count,
1722                        dad_counter: dad_count,
1723                    };
1724                    either::Either::Left(core::iter::once((address, config)))
1725                }
1726                IidGenerationConfiguration::Opaque { idgen_retries: _ } => {
1727                    either::Either::Right(core::iter::from_fn(move || {
1728                        // RFC 7217 Section 5:
1729                        //
1730                        //   The resulting Interface Identifier SHOULD be compared against the
1731                        //   reserved IPv6 Interface Identifiers [RFC5453] [IANA-RESERVED-IID]
1732                        //   and against those Interface Identifiers already employed in an
1733                        //   address of the same network interface and the same network
1734                        //   prefix.  In the event that an unacceptable identifier has been
1735                        //   generated, this situation SHOULD be handled in the same way as
1736                        //   the case of duplicate addresses (see Section 6).
1737                        let mut attempts = 0;
1738                        loop {
1739                            let address = generate_stable_address_with_opaque_iid(
1740                                &subnet,
1741                                link_layer_addr.as_bytes(),
1742                                // Sum both regeneration counters to get the `DAD_Counter` parameter
1743                                // defined in RFC 7217 section 5.
1744                                //
1745                                // We store these two counters separately so that we don't count
1746                                // conflicts that are *not* due to DAD failure towards the maximum
1747                                // number of DAD retries, but add them together to ensure that we
1748                                // regenerate a new address each time we retry for either reason.
1749                                regen_count + dad_count,
1750                                &stable_secret_key,
1751                            );
1752                            let config = SlaacConfig::Stable {
1753                                valid_until,
1754                                creation_time: now,
1755                                regen_counter: regen_count,
1756                                dad_counter: dad_count,
1757                            };
1758
1759                            regen_count = regen_count.wrapping_add(1);
1760
1761                            if has_iana_allowed_iid(address.addr().get()) {
1762                                break Some((address, config));
1763                            }
1764
1765                            attempts += 1;
1766                            if attempts > MAX_LOCAL_REGEN_ATTEMPTS {
1767                                return None;
1768                            }
1769                        }
1770                    }))
1771                }
1772            });
1773
1774            (valid_until, prefix_preferred_for.map(|p| PreferredForAndRegenAt(p, None)), addresses)
1775        }
1776        SlaacInitConfig::Temporary { dad_count } => {
1777            match temporary_address_configuration {
1778                TemporarySlaacAddressConfiguration::Disabled => {
1779                    trace!(
1780                        "receive_ndp_packet: temporary addresses are disabled on device {:?}",
1781                        device_id
1782                    );
1783                    return;
1784                }
1785                TemporarySlaacAddressConfiguration::Enabled {
1786                    temp_valid_lifetime,
1787                    temp_preferred_lifetime,
1788                    temp_idgen_retries,
1789                } => {
1790                    let per_attempt_random_seed: u64 = bindings_ctx.rng().gen();
1791
1792                    // Per RFC 8981 Section 3.4.4:
1793                    //    When creating a temporary address, DESYNC_FACTOR MUST be computed
1794                    //    and associated with the newly created address, and the address
1795                    //    lifetime values MUST be derived from the corresponding prefix as
1796                    //    follows:
1797                    //
1798                    //    *  Its valid lifetime is the lower of the Valid Lifetime of the
1799                    //       prefix and TEMP_VALID_LIFETIME.
1800                    //
1801                    //    *  Its preferred lifetime is the lower of the Preferred Lifetime
1802                    //       of the prefix and TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR.
1803                    let valid_for = match prefix_valid_for {
1804                        NonZeroNdpLifetime::Finite(prefix_valid_for) => {
1805                            core::cmp::min(prefix_valid_for, *temp_valid_lifetime)
1806                        }
1807                        NonZeroNdpLifetime::Infinite => *temp_valid_lifetime,
1808                    };
1809
1810                    let regen_advance = regen_advance(
1811                        *temp_idgen_retries,
1812                        *retrans_timer,
1813                        dad_transmits.map_or(0, NonZeroU16::get),
1814                    );
1815
1816                    let valid_until = now.saturating_add(valid_for.get());
1817
1818                    let desync_factor = if let Some(d) = desync_factor(
1819                        &mut bindings_ctx.rng(),
1820                        *temp_preferred_lifetime,
1821                        regen_advance,
1822                    ) {
1823                        d
1824                    } else {
1825                        // We only fail to calculate a desync factor when the configured
1826                        // maximum temporary address preferred lifetime is less than
1827                        // REGEN_ADVANCE and per RFC 8981 Section 3.4.5,
1828                        //
1829                        //   A temporary address is created only if this calculated
1830                        //   preferred lifetime is greater than REGEN_ADVANCE time
1831                        //   units.
1832                        trace!(
1833                            "failed to calculate DESYNC_FACTOR; \
1834                                temp_preferred_lifetime={:?}, regen_advance={:?}",
1835                            temp_preferred_lifetime,
1836                            regen_advance,
1837                        );
1838                        return;
1839                    };
1840
1841                    let preferred_for = prefix_preferred_for.and_then(|prefix_preferred_for| {
1842                        temp_preferred_lifetime
1843                            .get()
1844                            .checked_sub(desync_factor)
1845                            .and_then(NonZeroDuration::new)
1846                            .map(|d| prefix_preferred_for.min_finite_duration(d))
1847                    });
1848
1849                    // RFC 8981 Section 3.4.5:
1850                    //
1851                    //   A temporary address is created only if this calculated
1852                    //   preferred lifetime is greater than REGEN_ADVANCE time
1853                    //   units.
1854                    let preferred_for_and_regen_at = match preferred_for {
1855                        None => return,
1856                        Some(preferred_for) => {
1857                            match preferred_for.get().checked_sub(regen_advance.get()) {
1858                                Some(before_regen) => PreferredForAndRegenAt(
1859                                    NonZeroNdpLifetime::Finite(preferred_for),
1860                                    // Checked add, if we overflow it's as good
1861                                    // as not ever having to regenerate.
1862                                    now.checked_add(before_regen),
1863                                ),
1864                                None => {
1865                                    trace!(
1866                                        "receive_ndp_packet: preferred lifetime of {:?} \
1867                                            for subnet {:?} is too short to allow regen",
1868                                        preferred_for,
1869                                        subnet
1870                                    );
1871                                    return;
1872                                }
1873                            }
1874                        }
1875                    };
1876
1877                    let config = SlaacConfig::Temporary(TemporarySlaacConfig {
1878                        desync_factor,
1879                        valid_until,
1880                        creation_time: now,
1881                        dad_counter: dad_count,
1882                    });
1883
1884                    let mut seed = per_attempt_random_seed;
1885                    let addresses = either::Either::Right(core::iter::from_fn(move || {
1886                        // RFC 8981 Section 3.3.3 specifies that
1887                        //
1888                        //   The resulting IID MUST be compared against the reserved
1889                        //   IPv6 IIDs and against those IIDs already employed in an
1890                        //   address of the same network interface and the same network
1891                        //   prefix.  In the event that an unacceptable identifier has
1892                        //   been generated, the DAD_Counter should be incremented by 1,
1893                        //   and the algorithm should be restarted from the first step.
1894                        let mut attempts = 0;
1895                        loop {
1896                            let address = generate_global_temporary_address(
1897                                &subnet,
1898                                link_layer_addr.as_bytes(),
1899                                seed,
1900                                &temp_secret_key,
1901                            );
1902                            seed = seed.wrapping_add(1);
1903
1904                            if has_iana_allowed_iid(address.addr().get()) {
1905                                break Some((address, config));
1906                            }
1907
1908                            attempts += 1;
1909                            if attempts > MAX_LOCAL_REGEN_ATTEMPTS {
1910                                return None;
1911                            }
1912                        }
1913                    }));
1914
1915                    (Lifetime::Finite(valid_until), Some(preferred_for_and_regen_at), addresses)
1916                }
1917            }
1918        }
1919    };
1920
1921    // Attempt to add the address to the device.
1922    let mut local_regen_attempts = 0;
1923    loop {
1924        let Some((address, slaac_config)) = addresses.next() else {
1925            // No more addresses to try - do nothing further.
1926            debug!("exhausted possible SLAAC addresses without assigning on device {device_id:?}");
1927            return;
1928        };
1929
1930        // Calculate the durations to instants relative to the previously
1931        // recorded `now` value. This helps prevent skew in cases where this
1932        // task gets preempted and isn't scheduled for some period of time
1933        // between recording `now` and here.
1934        let (preferred_lifetime, regen_at) = match preferred_and_regen {
1935            Some(PreferredForAndRegenAt(preferred_for, regen_at)) => {
1936                (PreferredLifetime::preferred_for(now, preferred_for), regen_at)
1937            }
1938            None => (PreferredLifetime::Deprecated, None),
1939        };
1940        let config = Ipv6AddrSlaacConfig { inner: slaac_config, preferred_lifetime };
1941
1942        // TODO(https://fxbug.dev/42172850): Should bindings be the one to actually
1943        // assign the address to maintain a "single source of truth"?
1944        let res = slaac_addrs.add_addr_sub_and_then(
1945            bindings_ctx,
1946            address,
1947            config,
1948            |SlaacAddressEntryMut { addr_sub, config: _ }, ctx| {
1949                // Set the valid lifetime for this address.
1950                //
1951                // Must not have reached this point if the address was already assigned
1952                // to a device.
1953                match valid_until {
1954                    Lifetime::Finite(valid_until) => {
1955                        assert_eq!(
1956                            timers.schedule_instant(
1957                                ctx,
1958                                InnerSlaacTimerId::InvalidateSlaacAddress { addr: addr_sub.addr() },
1959                                (),
1960                                valid_until,
1961                            ),
1962                            None
1963                        );
1964                    }
1965                    Lifetime::Infinite => {}
1966                }
1967
1968                let deprecate_timer_id =
1969                    InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_sub.addr() };
1970
1971                match preferred_lifetime {
1972                    PreferredLifetime::Preferred(Lifetime::Finite(instant)) => {
1973                        assert_eq!(
1974                            timers.schedule_instant(ctx, deprecate_timer_id, (), instant,),
1975                            None
1976                        );
1977                    }
1978                    PreferredLifetime::Preferred(Lifetime::Infinite) => {}
1979                    PreferredLifetime::Deprecated => {
1980                        assert_eq!(timers.cancel(ctx, &deprecate_timer_id), None);
1981                    }
1982                }
1983
1984                match regen_at {
1985                    Some(regen_at) => assert_eq!(
1986                        timers.schedule_instant(
1987                            ctx,
1988                            InnerSlaacTimerId::RegenerateTemporaryAddress { addr_subnet: addr_sub },
1989                            (),
1990                            regen_at,
1991                        ),
1992                        None
1993                    ),
1994                    None => (),
1995                }
1996                addr_sub
1997            },
1998        );
1999
2000        match res {
2001            Err(ExistsError) => {
2002                trace!("IPv6 SLAAC address {:?} already exists on device {:?}", address, device_id);
2003
2004                // Try the next address, as long as we have not reached the maximum number of
2005                // attempts.
2006                slaac_addrs.counters().generated_slaac_addr_exists.increment();
2007                local_regen_attempts += 1;
2008                if local_regen_attempts > MAX_LOCAL_REGEN_ATTEMPTS {
2009                    debug!(
2010                        "exceeded max local SLAAC addr generation attempts on device {device_id:?}"
2011                    );
2012                    return;
2013                }
2014            }
2015            Ok(addr_sub) => {
2016                trace!("receive_ndp_packet: Successfully configured new IPv6 address {:?} on device {:?} via SLAAC", addr_sub, device_id);
2017                break;
2018            }
2019        }
2020    }
2021}
2022
2023#[cfg(any(test, feature = "testutils"))]
2024pub(crate) mod testutil {
2025    use super::*;
2026
2027    use alloc::collections::HashMap;
2028
2029    use net_types::ip::Ipv6;
2030
2031    use crate::internal::device::{IpDeviceBindingsContext, Ipv6DeviceConfigurationContext};
2032
2033    /// Collects all the currently installed SLAAC timers for `device_id` in
2034    /// `core_ctx`.
2035    pub fn collect_slaac_timers_integration<CC, BC>(
2036        core_ctx: &mut CC,
2037        device_id: &CC::DeviceId,
2038    ) -> HashMap<InnerSlaacTimerId, BC::Instant>
2039    where
2040        CC: Ipv6DeviceConfigurationContext<BC>,
2041        for<'a> CC::Ipv6DeviceStateCtx<'a>: SlaacContext<BC>,
2042        BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId> + SlaacBindingsContext<CC::DeviceId>,
2043    {
2044        core_ctx.with_ipv6_device_configuration(device_id, |_, mut core_ctx| {
2045            core_ctx.with_slaac_addrs_mut(device_id, |_, state| {
2046                state.timers().iter().map(|(k, (), t)| (*k, *t)).collect::<HashMap<_, _>>()
2047            })
2048        })
2049    }
2050
2051    /// Returns the address and subnet used by SLAAC on `subnet` with interface
2052    /// identifier `iid`.
2053    ///
2054    /// # Panics
2055    ///
2056    /// Panics if the prefix length of the provided `subnet` is not 64.
2057    pub fn calculate_slaac_addr_sub(
2058        subnet: Subnet<Ipv6Addr>,
2059        iid: [u8; 8],
2060    ) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
2061        assert_eq!(subnet.prefix(), 64);
2062        let mut bytes = subnet.network().ipv6_bytes();
2063        bytes[8..].copy_from_slice(&iid);
2064        AddrSubnet::new(Ipv6Addr::from_bytes(bytes), subnet.prefix()).unwrap()
2065    }
2066}
2067
2068#[cfg(test)]
2069mod tests {
2070    use alloc::collections::HashSet;
2071    use alloc::vec;
2072    use core::convert::TryFrom as _;
2073
2074    use net_declare::net::ip_v6;
2075    use netstack3_base::testutil::{
2076        assert_empty, FakeBindingsCtx, FakeCoreCtx, FakeCryptoRng, FakeDeviceId, FakeInstant,
2077        FakeTimerCtxExt as _, FakeWeakDeviceId,
2078    };
2079    use netstack3_base::{CtxPair, IntoCoreTimerCtx};
2080    use test_case::test_case;
2081
2082    use super::*;
2083
2084    /// Returns the address and subnet generated by SLAAC for `subnet` with an
2085    /// opaque IID, using the provided `network_interface` and `dad_counter` as the
2086    /// values for the `Net_Iface` and `DAD_Counter` parameters, respectively, in
2087    /// [RFC 7217 section 5].
2088    ///
2089    /// [RFC 7217 section 5](https://tools.ietf.org/html/rfc7217/#section-5)
2090    fn calculate_stable_slaac_addr_sub_with_opaque_iid(
2091        subnet: Subnet<Ipv6Addr>,
2092        network_interface: impl AsRef<[u8]>,
2093        dad_counter: u8,
2094    ) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
2095        let iid = OpaqueIid::new(
2096            subnet,
2097            network_interface.as_ref(),
2098            None::<&[_]>,
2099            OpaqueIidNonce::DadCounter(dad_counter),
2100            &STABLE_SECRET_KEY,
2101        );
2102        let iid = &iid.to_be_bytes()[..8];
2103        testutil::calculate_slaac_addr_sub(subnet, iid.try_into().unwrap())
2104    }
2105
2106    struct FakeSlaacContext {
2107        config: SlaacConfiguration,
2108        dad_transmits: Option<NonZeroU16>,
2109        retrans_timer: Duration,
2110        slaac_addrs: FakeSlaacAddrs,
2111        slaac_state: SlaacState<FakeBindingsCtxImpl>,
2112    }
2113
2114    type FakeCoreCtxImpl = FakeCoreCtx<FakeSlaacContext, (), FakeDeviceId>;
2115    type FakeBindingsCtxImpl = FakeBindingsCtx<
2116        SlaacTimerId<FakeWeakDeviceId<FakeDeviceId>>,
2117        IpDeviceEvent<FakeDeviceId, Ipv6, FakeInstant>,
2118        (),
2119        (),
2120    >;
2121
2122    struct FakeLinkLayerAddr;
2123
2124    const IID: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
2125
2126    impl Ipv6LinkLayerAddr for FakeLinkLayerAddr {
2127        fn as_bytes(&self) -> &[u8] {
2128            &IID
2129        }
2130
2131        fn eui64_iid(&self) -> [u8; 8] {
2132            IID
2133        }
2134    }
2135
2136    #[derive(Default)]
2137    struct FakeSlaacAddrs {
2138        slaac_addrs: Vec<SlaacAddressEntry<FakeInstant>>,
2139        non_slaac_addrs: Vec<Ipv6DeviceAddr>,
2140        counters: SlaacCounters,
2141    }
2142
2143    impl<'a> CounterContext<SlaacCounters> for &'a mut FakeSlaacAddrs {
2144        fn counters(&self) -> &SlaacCounters {
2145            &self.counters
2146        }
2147    }
2148
2149    impl<'a> SlaacAddresses<FakeBindingsCtxImpl> for &'a mut FakeSlaacAddrs {
2150        fn for_each_addr_mut<F: FnMut(SlaacAddressEntryMut<'_, FakeInstant>)>(
2151            &mut self,
2152            mut cb: F,
2153        ) {
2154            let FakeSlaacAddrs { slaac_addrs, non_slaac_addrs: _, counters: _ } = self;
2155            slaac_addrs.iter_mut().for_each(|SlaacAddressEntry { addr_sub, config }| {
2156                cb(SlaacAddressEntryMut { addr_sub: *addr_sub, config })
2157            })
2158        }
2159
2160        type AddrsIter<'b> =
2161            core::iter::Cloned<core::slice::Iter<'b, SlaacAddressEntry<FakeInstant>>>;
2162        fn with_addrs<O, F: FnOnce(Self::AddrsIter<'_>) -> O>(&mut self, cb: F) -> O {
2163            let FakeSlaacAddrs { slaac_addrs, non_slaac_addrs: _, counters: _ } = self;
2164            cb(slaac_addrs.iter().cloned())
2165        }
2166
2167        fn add_addr_sub_and_then<
2168            O,
2169            F: FnOnce(SlaacAddressEntryMut<'_, FakeInstant>, &mut FakeBindingsCtxImpl) -> O,
2170        >(
2171            &mut self,
2172            bindings_ctx: &mut FakeBindingsCtxImpl,
2173            add_addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
2174            config: Ipv6AddrSlaacConfig<FakeInstant>,
2175            and_then: F,
2176        ) -> Result<O, ExistsError> {
2177            let FakeSlaacAddrs { slaac_addrs, non_slaac_addrs, counters: _ } = self;
2178
2179            if non_slaac_addrs.iter().any(|a| *a == add_addr_sub.addr()) {
2180                return Err(ExistsError);
2181            }
2182
2183            if slaac_addrs.iter_mut().any(|e| e.addr_sub.addr() == add_addr_sub.addr()) {
2184                return Err(ExistsError);
2185            }
2186
2187            slaac_addrs.push(SlaacAddressEntry { addr_sub: add_addr_sub, config });
2188
2189            let SlaacAddressEntry { addr_sub, config } = slaac_addrs.iter_mut().last().unwrap();
2190
2191            Ok(and_then(SlaacAddressEntryMut { addr_sub: *addr_sub, config }, bindings_ctx))
2192        }
2193
2194        fn remove_addr(
2195            &mut self,
2196            _bindings_ctx: &mut FakeBindingsCtxImpl,
2197            addr: &Ipv6DeviceAddr,
2198        ) -> Result<
2199            (AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>, Ipv6AddrSlaacConfig<FakeInstant>),
2200            NotFoundError,
2201        > {
2202            let FakeSlaacAddrs { slaac_addrs, non_slaac_addrs: _, counters: _ } = self;
2203
2204            slaac_addrs
2205                .iter()
2206                .enumerate()
2207                .find_map(|(i, a)| (&a.addr_sub.addr() == addr).then(|| i))
2208                .ok_or(NotFoundError)
2209                .map(|i| {
2210                    let SlaacAddressEntry { addr_sub, config } = slaac_addrs.remove(i);
2211                    (addr_sub, config)
2212                })
2213        }
2214    }
2215
2216    impl SlaacContext<FakeBindingsCtxImpl> for FakeCoreCtxImpl {
2217        type LinkLayerAddr = FakeLinkLayerAddr;
2218
2219        type SlaacAddrs<'a>
2220            = &'a mut FakeSlaacAddrs
2221        where
2222            FakeCoreCtxImpl: 'a;
2223
2224        fn with_slaac_addrs_mut_and_configs<
2225            O,
2226            F: FnOnce(
2227                &mut Self::SlaacAddrs<'_>,
2228                SlaacConfigAndState<FakeLinkLayerAddr, FakeBindingsCtxImpl>,
2229                &mut SlaacState<FakeBindingsCtxImpl>,
2230            ) -> O,
2231        >(
2232            &mut self,
2233            &FakeDeviceId: &FakeDeviceId,
2234            cb: F,
2235        ) -> O {
2236            let FakeSlaacContext {
2237                config,
2238                dad_transmits,
2239                retrans_timer,
2240                slaac_addrs,
2241                slaac_state,
2242                ..
2243            } = &mut self.state;
2244            let mut slaac_addrs = slaac_addrs;
2245            cb(
2246                &mut slaac_addrs,
2247                SlaacConfigAndState {
2248                    config: *config,
2249                    dad_transmits: *dad_transmits,
2250                    retrans_timer: *retrans_timer,
2251                    link_layer_addr: Some(FakeLinkLayerAddr),
2252                    temp_secret_key: TEMP_SECRET_KEY,
2253                    stable_secret_key: STABLE_SECRET_KEY,
2254                    _marker: PhantomData,
2255                },
2256                slaac_state,
2257            )
2258        }
2259    }
2260
2261    impl FakeSlaacContext {
2262        fn iter_slaac_addrs(&self) -> impl Iterator<Item = SlaacAddressEntry<FakeInstant>> + '_ {
2263            self.slaac_addrs.slaac_addrs.iter().cloned()
2264        }
2265    }
2266
2267    fn new_timer_id() -> SlaacTimerId<FakeWeakDeviceId<FakeDeviceId>> {
2268        SlaacTimerId { device_id: FakeWeakDeviceId(FakeDeviceId) }
2269    }
2270
2271    fn new_context(
2272        config: SlaacConfiguration,
2273        slaac_addrs: FakeSlaacAddrs,
2274        dad_transmits: Option<NonZeroU16>,
2275        retrans_timer: Duration,
2276    ) -> CtxPair<FakeCoreCtxImpl, FakeBindingsCtxImpl> {
2277        CtxPair::with_default_bindings_ctx(|bindings_ctx| {
2278            FakeCoreCtxImpl::with_state(FakeSlaacContext {
2279                config,
2280                dad_transmits,
2281                retrans_timer,
2282                slaac_addrs,
2283                slaac_state: SlaacState::new::<_, IntoCoreTimerCtx>(
2284                    bindings_ctx,
2285                    FakeWeakDeviceId(FakeDeviceId),
2286                ),
2287            })
2288        })
2289    }
2290
2291    impl<Instant> SlaacAddressEntry<Instant> {
2292        fn to_deprecated(self) -> Self {
2293            let Self { addr_sub, config: Ipv6AddrSlaacConfig { inner, preferred_lifetime: _ } } =
2294                self;
2295            Self {
2296                addr_sub,
2297                config: Ipv6AddrSlaacConfig {
2298                    inner,
2299                    preferred_lifetime: PreferredLifetime::Deprecated,
2300                },
2301            }
2302        }
2303    }
2304
2305    #[test_case(ip_v6!("1:2:3:4::"), false; "subnet-router anycast")]
2306    #[test_case(ip_v6!("::1"), true; "allowed 1")]
2307    #[test_case(ip_v6!("1:2:3:4::1"), true; "allowed 2")]
2308    #[test_case(ip_v6!("4:4:4:4:0200:5eff:fe00:1"), false; "first ethernet block")]
2309    #[test_case(ip_v6!("1:1:1:1:0200:5eff:fe00:5213"), false; "proxy mobile")]
2310    #[test_case(ip_v6!("8:8:8:8:0200:5eff:fe00:8000"), false; "second ethernet block")]
2311    #[test_case(ip_v6!("a:a:a:a:fdff:ffff:ffff:ffaa"), false; "subnet anycast")]
2312    #[test_case(ip_v6!("c:c:c:c:fe00::"), true; "allowed 3")]
2313    fn test_has_iana_allowed_iid(addr: Ipv6Addr, expect_allowed: bool) {
2314        assert_eq!(has_iana_allowed_iid(addr), expect_allowed);
2315    }
2316
2317    const DEFAULT_RETRANS_TIMER: Duration = Duration::from_secs(1);
2318    const SUBNET: Subnet<Ipv6Addr> = net_declare::net_subnet_v6!("200a::/64");
2319
2320    #[test_case(0, 0, true; "zero lifetimes")]
2321    #[test_case(2, 1, true; "preferred larger than valid")]
2322    #[test_case(1, 2, false; "disabled")]
2323    fn dont_generate_address(
2324        preferred_lifetime_secs: u32,
2325        valid_lifetime_secs: u32,
2326        enable_stable_addresses: bool,
2327    ) {
2328        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2329            SlaacConfiguration {
2330                stable_address_configuration: if enable_stable_addresses {
2331                    StableSlaacAddressConfiguration::ENABLED_WITH_EUI64
2332                } else {
2333                    StableSlaacAddressConfiguration::Disabled
2334                },
2335                ..Default::default()
2336            },
2337            Default::default(),
2338            None,
2339            DEFAULT_RETRANS_TIMER,
2340        );
2341
2342        SlaacHandler::apply_slaac_update(
2343            &mut core_ctx,
2344            &mut bindings_ctx,
2345            &FakeDeviceId,
2346            SUBNET,
2347            NonZeroNdpLifetime::from_u32_with_infinite(preferred_lifetime_secs),
2348            NonZeroNdpLifetime::from_u32_with_infinite(valid_lifetime_secs),
2349        );
2350        assert_empty(core_ctx.state.iter_slaac_addrs());
2351        bindings_ctx.timers.assert_no_timers_installed();
2352    }
2353
2354    #[test_case(0, false; "deprecated EUI64")]
2355    #[test_case(1, false; "preferred EUI64")]
2356    #[test_case(0, true; "deprecated opaque")]
2357    #[test_case(1, true; "preferred opaque")]
2358    fn generate_stable_address(preferred_lifetime_secs: u32, opaque_iids: bool) {
2359        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2360            SlaacConfiguration {
2361                stable_address_configuration: if opaque_iids {
2362                    StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS
2363                } else {
2364                    StableSlaacAddressConfiguration::ENABLED_WITH_EUI64
2365                },
2366                ..Default::default()
2367            },
2368            Default::default(),
2369            None,
2370            DEFAULT_RETRANS_TIMER,
2371        );
2372
2373        let valid_lifetime_secs = preferred_lifetime_secs + 1;
2374        let addr_sub = if opaque_iids {
2375            calculate_stable_slaac_addr_sub_with_opaque_iid(SUBNET, IID, 0)
2376        } else {
2377            testutil::calculate_slaac_addr_sub(SUBNET, IID)
2378        };
2379
2380        // Generate a new SLAAC address.
2381        SlaacHandler::apply_slaac_update(
2382            &mut core_ctx,
2383            &mut bindings_ctx,
2384            &FakeDeviceId,
2385            SUBNET,
2386            NonZeroNdpLifetime::from_u32_with_infinite(preferred_lifetime_secs),
2387            NonZeroNdpLifetime::from_u32_with_infinite(valid_lifetime_secs),
2388        );
2389        let address_created_deprecated = preferred_lifetime_secs == 0;
2390        let now = bindings_ctx.now();
2391        let valid_until = now + Duration::from_secs(valid_lifetime_secs.into());
2392        let preferred_lifetime = match preferred_lifetime_secs {
2393            0 => PreferredLifetime::Deprecated,
2394            secs => PreferredLifetime::preferred_until(now + Duration::from_secs(secs.into())),
2395        };
2396        let inner = SlaacConfig::Stable {
2397            valid_until: Lifetime::Finite(valid_until),
2398            creation_time: bindings_ctx.now(),
2399            regen_counter: 0,
2400            dad_counter: 0,
2401        };
2402        let entry = SlaacAddressEntry {
2403            addr_sub,
2404            config: Ipv6AddrSlaacConfig { inner, preferred_lifetime },
2405        };
2406        assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry],);
2407        let deprecate_timer_id = InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_sub.addr() };
2408        let invalidate_timer_id =
2409            InnerSlaacTimerId::InvalidateSlaacAddress { addr: addr_sub.addr() };
2410        if !address_created_deprecated {
2411            core_ctx.state.slaac_state.timers.assert_timers([
2412                (deprecate_timer_id, (), now + Duration::from_secs(preferred_lifetime_secs.into())),
2413                (invalidate_timer_id, (), valid_until),
2414            ]);
2415
2416            // Trigger the deprecation timer.
2417            assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()));
2418            let entry = SlaacAddressEntry {
2419                addr_sub,
2420                config: Ipv6AddrSlaacConfig {
2421                    inner,
2422                    preferred_lifetime: PreferredLifetime::Deprecated,
2423                },
2424            };
2425            assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2426        }
2427        core_ctx.state.slaac_state.timers.assert_timers([(invalidate_timer_id, (), valid_until)]);
2428
2429        // Trigger the invalidation timer.
2430        assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()));
2431        assert_empty(core_ctx.state.iter_slaac_addrs());
2432        bindings_ctx.timers.assert_no_timers_installed();
2433    }
2434
2435    enum StableAddress {
2436        Global,
2437        LinkLocal,
2438    }
2439
2440    #[test_case(StableAddress::Global, true; "opaque global")]
2441    #[test_case(StableAddress::Global, false; "EUI64-based global")]
2442    #[test_case(StableAddress::LinkLocal, true; "opaque link-local")]
2443    #[test_case(StableAddress::LinkLocal, false; "EUI64-based link-local")]
2444    fn stable_address_conflict(address_type: StableAddress, opaque_iids: bool) {
2445        let subnet = match address_type {
2446            StableAddress::Global => SUBNET,
2447            StableAddress::LinkLocal => {
2448                Subnet::new(Ipv6::LINK_LOCAL_UNICAST_SUBNET.network(), REQUIRED_PREFIX_BITS)
2449                    .unwrap()
2450            }
2451        };
2452        let addr_sub = if opaque_iids {
2453            let dad_counter = 0;
2454            calculate_stable_slaac_addr_sub_with_opaque_iid(subnet, IID, dad_counter)
2455        } else {
2456            testutil::calculate_slaac_addr_sub(subnet, IID)
2457        };
2458
2459        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2460            SlaacConfiguration {
2461                stable_address_configuration: if opaque_iids {
2462                    StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS
2463                } else {
2464                    StableSlaacAddressConfiguration::ENABLED_WITH_EUI64
2465                },
2466                ..Default::default()
2467            },
2468            FakeSlaacAddrs {
2469                slaac_addrs: Default::default(),
2470                // Consider the address we will generate as already assigned without
2471                // SLAAC.
2472                non_slaac_addrs: vec![addr_sub.addr()],
2473                counters: Default::default(),
2474            },
2475            None,
2476            DEFAULT_RETRANS_TIMER,
2477        );
2478
2479        const LIFETIME_SECS: u32 = 1;
2480
2481        // Generate a new SLAAC address.
2482        match address_type {
2483            StableAddress::Global => {
2484                SlaacHandler::apply_slaac_update(
2485                    &mut core_ctx,
2486                    &mut bindings_ctx,
2487                    &FakeDeviceId,
2488                    SUBNET,
2489                    NonZeroNdpLifetime::from_u32_with_infinite(LIFETIME_SECS),
2490                    NonZeroNdpLifetime::from_u32_with_infinite(LIFETIME_SECS),
2491                );
2492            }
2493            StableAddress::LinkLocal => {
2494                SlaacHandler::generate_link_local_address(
2495                    &mut core_ctx,
2496                    &mut bindings_ctx,
2497                    &FakeDeviceId,
2498                );
2499            }
2500        }
2501
2502        // If we are using only the link-layer address of the interface to generate
2503        // SLAAC addresses, there is nothing that can be done to regenerate the address
2504        // in case of a conflict.
2505        if !opaque_iids {
2506            assert_empty(core_ctx.state.iter_slaac_addrs());
2507            bindings_ctx.timers.assert_no_timers_installed();
2508            return;
2509        }
2510
2511        // If opaque IIDs are being used to generate SLAAC addresses, the new address
2512        // will be regenerated so that it has a unique IID by incrementing the
2513        // DAD_Counter.
2514        let dad_counter = 1;
2515        let addr_sub = calculate_stable_slaac_addr_sub_with_opaque_iid(subnet, &IID, dad_counter);
2516        match address_type {
2517            StableAddress::Global => {
2518                let now = bindings_ctx.now();
2519                let valid_until = now + Duration::from_secs(LIFETIME_SECS.into());
2520                let entry = SlaacAddressEntry {
2521                    addr_sub,
2522                    config: Ipv6AddrSlaacConfig {
2523                        inner: SlaacConfig::Stable {
2524                            valid_until: Lifetime::Finite(valid_until),
2525                            creation_time: bindings_ctx.now(),
2526                            regen_counter: 1,
2527                            dad_counter: 0,
2528                        },
2529                        preferred_lifetime: PreferredLifetime::preferred_until(valid_until),
2530                    },
2531                };
2532                assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2533                let deprecate_timer_id =
2534                    InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_sub.addr() };
2535                let invalidate_timer_id =
2536                    InnerSlaacTimerId::InvalidateSlaacAddress { addr: addr_sub.addr() };
2537                core_ctx.state.slaac_state.timers.assert_timers([
2538                    (deprecate_timer_id, (), valid_until),
2539                    (invalidate_timer_id, (), valid_until),
2540                ]);
2541            }
2542            StableAddress::LinkLocal => {
2543                let entry = SlaacAddressEntry {
2544                    addr_sub,
2545                    config: Ipv6AddrSlaacConfig {
2546                        inner: SlaacConfig::Stable {
2547                            valid_until: Lifetime::Infinite,
2548                            creation_time: bindings_ctx.now(),
2549                            regen_counter: 1,
2550                            dad_counter: 0,
2551                        },
2552                        preferred_lifetime: PreferredLifetime::preferred_forever(),
2553                    },
2554                };
2555                assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2556                bindings_ctx.timers.assert_no_timers_installed();
2557            }
2558        };
2559    }
2560
2561    #[test]
2562    fn temporary_address_conflict() {
2563        const TEMP_IDGEN_RETRIES: u8 = 0;
2564
2565        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2566            SlaacConfiguration {
2567                temporary_address_configuration: TemporarySlaacAddressConfiguration::Enabled {
2568                    temp_valid_lifetime: ONE_HOUR,
2569                    temp_preferred_lifetime: ONE_HOUR,
2570                    temp_idgen_retries: TEMP_IDGEN_RETRIES,
2571                },
2572                ..Default::default()
2573            },
2574            FakeSlaacAddrs::default(),
2575            None,
2576            DEFAULT_RETRANS_TIMER,
2577        );
2578
2579        // Consider the address we will generate as already assigned without
2580        // SLAAC.
2581        let mut dup_rng = bindings_ctx.rng().deep_clone();
2582        let seed = dup_rng.gen();
2583        let first_attempt =
2584            generate_global_temporary_address(&SUBNET, &IID, seed, &TEMP_SECRET_KEY);
2585        core_ctx.state.slaac_addrs.non_slaac_addrs = vec![first_attempt.addr()];
2586
2587        // Generate a new temporary SLAAC address.
2588        SlaacHandler::apply_slaac_update(
2589            &mut core_ctx,
2590            &mut bindings_ctx,
2591            &FakeDeviceId,
2592            SUBNET,
2593            Some(NonZeroNdpLifetime::Finite(ONE_HOUR)),
2594            Some(NonZeroNdpLifetime::Finite(ONE_HOUR)),
2595        );
2596
2597        // The new address will be regenerated so that it has a unique IID by
2598        // incrementing the RNG seed.
2599        let seed = seed.wrapping_add(1);
2600        let addr_sub = generate_global_temporary_address(&SUBNET, &IID, seed, &TEMP_SECRET_KEY);
2601        assert_ne!(addr_sub, first_attempt);
2602        let regen_advance =
2603            regen_advance(TEMP_IDGEN_RETRIES, DEFAULT_RETRANS_TIMER, /* dad_transmits */ 0);
2604        let desync_factor = desync_factor(&mut dup_rng, ONE_HOUR, regen_advance).unwrap();
2605        let preferred_until = {
2606            let d = bindings_ctx.now() + ONE_HOUR.into();
2607            d - desync_factor
2608        };
2609        let entry = SlaacAddressEntry {
2610            addr_sub,
2611            config: Ipv6AddrSlaacConfig {
2612                inner: SlaacConfig::Temporary(TemporarySlaacConfig {
2613                    valid_until: bindings_ctx.now() + ONE_HOUR.into(),
2614                    desync_factor,
2615                    creation_time: bindings_ctx.now(),
2616                    dad_counter: 0,
2617                }),
2618                preferred_lifetime: PreferredLifetime::preferred_until(preferred_until),
2619            },
2620        };
2621        assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2622    }
2623
2624    #[test]
2625    fn local_regen_limit() {
2626        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2627            SlaacConfiguration {
2628                stable_address_configuration:
2629                    StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS,
2630                temporary_address_configuration: TemporarySlaacAddressConfiguration::Enabled {
2631                    temp_valid_lifetime: ONE_HOUR,
2632                    temp_preferred_lifetime: ONE_HOUR,
2633                    temp_idgen_retries: 0,
2634                },
2635                ..Default::default()
2636            },
2637            FakeSlaacAddrs::default(),
2638            None,
2639            DEFAULT_RETRANS_TIMER,
2640        );
2641
2642        let mut dup_rng = bindings_ctx.rng().deep_clone();
2643        let mut seed = dup_rng.gen();
2644
2645        let link_local_subnet =
2646            Subnet::new(Ipv6::LINK_LOCAL_UNICAST_SUBNET.network(), REQUIRED_PREFIX_BITS).unwrap();
2647
2648        // Consider all the SLAAC addresses we will generate (link-local, stable, and
2649        // temporary) as already assigned manually without SLAAC.
2650        for attempt in 0..=MAX_LOCAL_REGEN_ATTEMPTS {
2651            let link_local =
2652                calculate_stable_slaac_addr_sub_with_opaque_iid(link_local_subnet, IID, attempt);
2653
2654            let stable = calculate_stable_slaac_addr_sub_with_opaque_iid(SUBNET, IID, attempt);
2655
2656            let temporary =
2657                generate_global_temporary_address(&SUBNET, &IID, seed, &TEMP_SECRET_KEY);
2658            seed = seed.wrapping_add(1);
2659
2660            core_ctx.state.slaac_addrs.non_slaac_addrs.extend(&[
2661                link_local.addr(),
2662                stable.addr(),
2663                temporary.addr(),
2664            ]);
2665        }
2666
2667        // Trigger SLAAC address generation (both link-local and global addresses for an
2668        // advertised prefix).
2669        SlaacHandler::apply_slaac_update(
2670            &mut core_ctx,
2671            &mut bindings_ctx,
2672            &FakeDeviceId,
2673            SUBNET,
2674            Some(NonZeroNdpLifetime::Finite(ONE_HOUR)),
2675            Some(NonZeroNdpLifetime::Finite(ONE_HOUR)),
2676        );
2677        SlaacHandler::generate_link_local_address(&mut core_ctx, &mut bindings_ctx, &FakeDeviceId);
2678
2679        // The maximum number of local retries should be exhausted due to the
2680        // conflicting addresses and no addresses of any kind should be generated.
2681        assert_empty(core_ctx.state.iter_slaac_addrs());
2682        bindings_ctx.timers.assert_no_timers_installed();
2683    }
2684
2685    const LIFETIME: NonZeroNdpLifetime =
2686        NonZeroNdpLifetime::Finite(NonZeroDuration::new(Duration::from_secs(1)).unwrap());
2687
2688    #[test_case(AddressRemovedReason::Manual, LIFETIME; "manual")]
2689    #[test_case(AddressRemovedReason::DadFailed, LIFETIME; "dad failed")]
2690    #[test_case(
2691        AddressRemovedReason::DadFailed,
2692        NonZeroNdpLifetime::Infinite;
2693        "dad failed infinite lifetime"
2694    )]
2695    fn remove_stable_address(reason: AddressRemovedReason, lifetime: NonZeroNdpLifetime) {
2696        let addr_sub =
2697            calculate_stable_slaac_addr_sub_with_opaque_iid(SUBNET, IID, /* dad_counter */ 0);
2698
2699        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2700            SlaacConfiguration {
2701                stable_address_configuration:
2702                    StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS,
2703                ..Default::default()
2704            },
2705            Default::default(),
2706            None,
2707            DEFAULT_RETRANS_TIMER,
2708        );
2709
2710        // Generate a new SLAAC address.
2711        SlaacHandler::apply_slaac_update(
2712            &mut core_ctx,
2713            &mut bindings_ctx,
2714            &FakeDeviceId,
2715            SUBNET,
2716            Some(lifetime),
2717            Some(lifetime),
2718        );
2719        let now = bindings_ctx.now();
2720        let valid_until = Lifetime::from_ndp(now, lifetime);
2721        let preferred_lifetime = PreferredLifetime::preferred_for(now, lifetime);
2722        let entry = SlaacAddressEntry {
2723            addr_sub,
2724            config: Ipv6AddrSlaacConfig {
2725                inner: SlaacConfig::Stable {
2726                    valid_until,
2727                    creation_time: bindings_ctx.now(),
2728                    regen_counter: 0,
2729                    dad_counter: 0,
2730                },
2731                preferred_lifetime,
2732            },
2733        };
2734        assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2735
2736        let assert_expected_timers = |slaac_state: &SlaacState<_>, addr| {
2737            let expected_timers = match lifetime {
2738                NonZeroNdpLifetime::Infinite => vec![],
2739                NonZeroNdpLifetime::Finite(duration) => {
2740                    let deprecate_timer_id = InnerSlaacTimerId::DeprecateSlaacAddress { addr };
2741                    let invalidate_timer_id = InnerSlaacTimerId::InvalidateSlaacAddress { addr };
2742                    let instant = now + duration.get();
2743                    vec![(deprecate_timer_id, (), instant), (invalidate_timer_id, (), instant)]
2744                }
2745            };
2746            slaac_state.timers.assert_timers(expected_timers);
2747        };
2748        assert_expected_timers(&core_ctx.state.slaac_state, addr_sub.addr());
2749
2750        // Remove the address and let SLAAC know the address was removed.
2751        let config = {
2752            let SlaacAddressEntry { addr_sub: got_addr_sub, config } =
2753                core_ctx.state.slaac_addrs.slaac_addrs.remove(0);
2754            assert_eq!(addr_sub, got_addr_sub);
2755            assert_eq!(config.preferred_lifetime, preferred_lifetime);
2756            config
2757        };
2758        SlaacHandler::on_address_removed(
2759            &mut core_ctx,
2760            &mut bindings_ctx,
2761            &FakeDeviceId,
2762            addr_sub,
2763            config,
2764            reason,
2765        );
2766        match reason {
2767            AddressRemovedReason::Manual => {
2768                // Addresses that are removed manually are not regenerated.
2769                bindings_ctx.timers.assert_no_timers_installed();
2770                assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), []);
2771                return;
2772            }
2773            AddressRemovedReason::DadFailed => {}
2774        }
2775
2776        // If the address was removed due to DAD failure, it should be regenerated with
2777        // an incremented DAD counter.
2778        let addr_sub =
2779            calculate_stable_slaac_addr_sub_with_opaque_iid(SUBNET, IID, /* dad_counter */ 1);
2780        let entry = SlaacAddressEntry {
2781            addr_sub,
2782            config: Ipv6AddrSlaacConfig {
2783                inner: SlaacConfig::Stable {
2784                    valid_until,
2785                    creation_time: now,
2786                    regen_counter: 0,
2787                    dad_counter: 1,
2788                },
2789                preferred_lifetime,
2790            },
2791        };
2792        assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
2793        assert_expected_timers(&core_ctx.state.slaac_state, addr_sub.addr());
2794    }
2795
2796    #[test]
2797    fn stable_addr_regen_counters() {
2798        // Ensure that all address regeneration attempts, whether due to local conflict
2799        // or DAD failure, result in a new unique address being generated.
2800
2801        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2802            SlaacConfiguration {
2803                stable_address_configuration:
2804                    StableSlaacAddressConfiguration::ENABLED_WITH_OPAQUE_IIDS,
2805                ..Default::default()
2806            },
2807            Default::default(),
2808            None,
2809            DEFAULT_RETRANS_TIMER,
2810        );
2811
2812        const LOCAL_REGEN_ATTEMPTS: u8 = 3;
2813        const DAD_FAILURE_REGEN_ATTEMPTS: u8 = 3;
2814
2815        let now = bindings_ctx.now();
2816        core_ctx.with_slaac_addrs_mut_and_configs(&FakeDeviceId, |addrs, config, slaac_state| {
2817            for regen_count in 0..LOCAL_REGEN_ATTEMPTS {
2818                for dad_count in 0..DAD_FAILURE_REGEN_ATTEMPTS {
2819                    add_slaac_addr_sub::<_, FakeCoreCtx<_, _, _>>(
2820                        &mut bindings_ctx,
2821                        &FakeDeviceId,
2822                        addrs,
2823                        &config,
2824                        slaac_state,
2825                        now,
2826                        SlaacInitConfig::Stable { regen_count, dad_count },
2827                        NonZeroNdpLifetime::Infinite,
2828                        Some(NonZeroNdpLifetime::Infinite),
2829                        &SUBNET,
2830                    );
2831                }
2832            }
2833        });
2834        let unique_addrs = core_ctx
2835            .state
2836            .iter_slaac_addrs()
2837            .map(|entry| entry.addr_sub.addr())
2838            .collect::<HashSet<_>>();
2839        assert_eq!(
2840            unique_addrs.len(),
2841            usize::from(LOCAL_REGEN_ATTEMPTS * DAD_FAILURE_REGEN_ATTEMPTS)
2842        );
2843    }
2844
2845    struct RefreshStableAddressTimersTest {
2846        orig_pl_secs: u32,
2847        orig_vl_secs: u32,
2848        new_pl_secs: u32,
2849        new_vl_secs: u32,
2850        effective_new_vl_secs: u32,
2851    }
2852
2853    const ONE_HOUR_AS_SECS: u32 = 60 * 60;
2854    const TWO_HOURS_AS_SECS: u32 = ONE_HOUR_AS_SECS * 2;
2855    const THREE_HOURS_AS_SECS: u32 = ONE_HOUR_AS_SECS * 3;
2856    const FOUR_HOURS_AS_SECS: u32 = ONE_HOUR_AS_SECS * 4;
2857    const INFINITE_LIFETIME: u32 = u32::MAX;
2858    const MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS: u32 =
2859        MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE.get().as_secs() as u32;
2860    #[test_case(RefreshStableAddressTimersTest {
2861        orig_pl_secs: 1,
2862        orig_vl_secs: 1,
2863        new_pl_secs: 1,
2864        new_vl_secs: 1,
2865        effective_new_vl_secs: 1,
2866    }; "do nothing")]
2867    #[test_case(RefreshStableAddressTimersTest {
2868        orig_pl_secs: 1,
2869        orig_vl_secs: 1,
2870        new_pl_secs: 2,
2871        new_vl_secs: 2,
2872        effective_new_vl_secs: 2,
2873    }; "increase lifetimes")]
2874    #[test_case(RefreshStableAddressTimersTest {
2875        orig_pl_secs: 1,
2876        orig_vl_secs: 1,
2877        new_pl_secs: 0,
2878        new_vl_secs: 1,
2879        effective_new_vl_secs: 1,
2880    }; "deprecate address only")]
2881    #[test_case(RefreshStableAddressTimersTest {
2882        orig_pl_secs: 0,
2883        orig_vl_secs: 1,
2884        new_pl_secs: 1,
2885        new_vl_secs: 1,
2886        effective_new_vl_secs: 1,
2887    }; "undeprecate address")]
2888    #[test_case(RefreshStableAddressTimersTest {
2889        orig_pl_secs: 1,
2890        orig_vl_secs: 1,
2891        new_pl_secs: 0,
2892        new_vl_secs: 0,
2893        effective_new_vl_secs: 1,
2894    }; "deprecate address only with new valid lifetime of zero")]
2895    #[test_case(RefreshStableAddressTimersTest {
2896        orig_pl_secs: ONE_HOUR_AS_SECS,
2897        orig_vl_secs: ONE_HOUR_AS_SECS,
2898        new_pl_secs: ONE_HOUR_AS_SECS - 1,
2899        new_vl_secs: ONE_HOUR_AS_SECS - 1,
2900        effective_new_vl_secs: ONE_HOUR_AS_SECS,
2901    }; "decrease preferred lifetime and ignore new valid lifetime if less than 2 hours and remaining lifetime")]
2902    #[test_case(RefreshStableAddressTimersTest {
2903        orig_pl_secs: THREE_HOURS_AS_SECS,
2904        orig_vl_secs: THREE_HOURS_AS_SECS,
2905        new_pl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS - 1,
2906        new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS - 1,
2907        effective_new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS,
2908    }; "deprecate address only and bring valid lifetime down to 2 hours at max")]
2909    #[test_case(RefreshStableAddressTimersTest {
2910        orig_pl_secs: ONE_HOUR_AS_SECS - 1,
2911        orig_vl_secs: ONE_HOUR_AS_SECS - 1,
2912        new_pl_secs: ONE_HOUR_AS_SECS - 1,
2913        new_vl_secs: ONE_HOUR_AS_SECS,
2914        effective_new_vl_secs: ONE_HOUR_AS_SECS,
2915    }; "increase valid lifetime if more than remaining valid lifetime")]
2916    #[test_case(RefreshStableAddressTimersTest {
2917        orig_pl_secs: INFINITE_LIFETIME,
2918        orig_vl_secs: INFINITE_LIFETIME,
2919        new_pl_secs: INFINITE_LIFETIME,
2920        new_vl_secs: INFINITE_LIFETIME,
2921        effective_new_vl_secs: INFINITE_LIFETIME,
2922    }; "infinite lifetimes")]
2923    #[test_case(RefreshStableAddressTimersTest {
2924        orig_pl_secs: ONE_HOUR_AS_SECS,
2925        orig_vl_secs: TWO_HOURS_AS_SECS,
2926        new_pl_secs: TWO_HOURS_AS_SECS,
2927        new_vl_secs: INFINITE_LIFETIME,
2928        effective_new_vl_secs: INFINITE_LIFETIME,
2929    }; "update valid lifetime from finite to infinite")]
2930    #[test_case(RefreshStableAddressTimersTest {
2931        orig_pl_secs: ONE_HOUR_AS_SECS,
2932        orig_vl_secs: TWO_HOURS_AS_SECS,
2933        new_pl_secs: INFINITE_LIFETIME,
2934        new_vl_secs: INFINITE_LIFETIME,
2935        effective_new_vl_secs: INFINITE_LIFETIME,
2936    }; "update both lifetimes from finite to infinite")]
2937    #[test_case(RefreshStableAddressTimersTest {
2938        orig_pl_secs: TWO_HOURS_AS_SECS,
2939        orig_vl_secs: INFINITE_LIFETIME,
2940        new_pl_secs: ONE_HOUR_AS_SECS,
2941        new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS - 1,
2942        effective_new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS,
2943    }; "update valid lifetime from infinite to finite")]
2944    #[test_case(RefreshStableAddressTimersTest {
2945        orig_pl_secs: INFINITE_LIFETIME,
2946        orig_vl_secs: INFINITE_LIFETIME,
2947        new_pl_secs: ONE_HOUR_AS_SECS,
2948        new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS - 1,
2949        effective_new_vl_secs: MIN_PREFIX_VALID_LIFETIME_FOR_UPDATE_AS_SECS,
2950    }; "update both lifetimes from infinite to finite")]
2951    fn stable_address_timers(
2952        RefreshStableAddressTimersTest {
2953            orig_pl_secs,
2954            orig_vl_secs,
2955            new_pl_secs,
2956            new_vl_secs,
2957            effective_new_vl_secs,
2958        }: RefreshStableAddressTimersTest,
2959    ) {
2960        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
2961            SlaacConfiguration {
2962                stable_address_configuration: StableSlaacAddressConfiguration::ENABLED_WITH_EUI64,
2963                ..Default::default()
2964            },
2965            Default::default(),
2966            None,
2967            DEFAULT_RETRANS_TIMER,
2968        );
2969
2970        let addr_sub = testutil::calculate_slaac_addr_sub(SUBNET, IID);
2971
2972        let deprecate_timer_id = InnerSlaacTimerId::DeprecateSlaacAddress { addr: addr_sub.addr() };
2973        let invalidate_timer_id =
2974            InnerSlaacTimerId::InvalidateSlaacAddress { addr: addr_sub.addr() };
2975
2976        // Generate a new SLAAC address.
2977        let ndp_pl = NonZeroNdpLifetime::from_u32_with_infinite(orig_pl_secs);
2978        let ndp_vl = NonZeroNdpLifetime::from_u32_with_infinite(orig_vl_secs);
2979        SlaacHandler::apply_slaac_update(
2980            &mut core_ctx,
2981            &mut bindings_ctx,
2982            &FakeDeviceId,
2983            SUBNET,
2984            ndp_pl,
2985            ndp_vl,
2986        );
2987        let now = bindings_ctx.now();
2988        let mut expected_timers = Vec::new();
2989        let valid_until = match ndp_vl.expect("this test expects to create an address") {
2990            NonZeroNdpLifetime::Finite(d) => {
2991                let valid_until = now + d.get();
2992                expected_timers.push((invalidate_timer_id, (), valid_until));
2993                Lifetime::Finite(valid_until)
2994            }
2995            NonZeroNdpLifetime::Infinite => Lifetime::Infinite,
2996        };
2997        match ndp_pl {
2998            None | Some(NonZeroNdpLifetime::Infinite) => {}
2999            Some(NonZeroNdpLifetime::Finite(d)) => {
3000                expected_timers.push((deprecate_timer_id, (), now + d.get()))
3001            }
3002        }
3003        let entry = SlaacAddressEntry {
3004            addr_sub,
3005            config: Ipv6AddrSlaacConfig {
3006                inner: SlaacConfig::Stable {
3007                    valid_until,
3008                    creation_time: bindings_ctx.now(),
3009                    regen_counter: 0,
3010                    dad_counter: 0,
3011                },
3012                preferred_lifetime: PreferredLifetime::maybe_preferred_for(now, ndp_pl),
3013            },
3014        };
3015        assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
3016        core_ctx.state.slaac_state.timers.assert_timers(expected_timers);
3017
3018        // Refresh timers.
3019        let ndp_pl = NonZeroNdpLifetime::from_u32_with_infinite(new_pl_secs);
3020        SlaacHandler::apply_slaac_update(
3021            &mut core_ctx,
3022            &mut bindings_ctx,
3023            &FakeDeviceId,
3024            SUBNET,
3025            ndp_pl,
3026            NonZeroNdpLifetime::from_u32_with_infinite(new_vl_secs),
3027        );
3028        let mut expected_timers = Vec::new();
3029        let valid_until = match NonZeroNdpLifetime::from_u32_with_infinite(effective_new_vl_secs)
3030            .expect("this test expects to keep the address")
3031        {
3032            NonZeroNdpLifetime::Finite(d) => {
3033                let valid_until = now + d.get();
3034                expected_timers.push((invalidate_timer_id, (), valid_until));
3035                Lifetime::Finite(valid_until)
3036            }
3037            NonZeroNdpLifetime::Infinite => Lifetime::Infinite,
3038        };
3039        match ndp_pl {
3040            None | Some(NonZeroNdpLifetime::Infinite) => {}
3041            Some(NonZeroNdpLifetime::Finite(d)) => {
3042                expected_timers.push((deprecate_timer_id, (), now + d.get()))
3043            }
3044        }
3045        let entry = SlaacAddressEntry {
3046            config: Ipv6AddrSlaacConfig {
3047                inner: SlaacConfig::Stable {
3048                    valid_until,
3049                    creation_time: bindings_ctx.now(),
3050                    regen_counter: 0,
3051                    dad_counter: 0,
3052                },
3053                preferred_lifetime: PreferredLifetime::maybe_preferred_for(now, ndp_pl),
3054            },
3055            ..entry
3056        };
3057        assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [entry]);
3058        core_ctx.state.slaac_state.timers.assert_timers(expected_timers);
3059    }
3060
3061    const TEMP_SECRET_KEY: IidSecret = IidSecret::ALL_ONES;
3062    const STABLE_SECRET_KEY: IidSecret = IidSecret::ALL_TWOS;
3063
3064    const ONE_HOUR: NonZeroDuration = NonZeroDuration::from_secs(ONE_HOUR_AS_SECS as u64).unwrap();
3065
3066    struct DontGenerateTemporaryAddressTest {
3067        preferred_lifetime_config: NonZeroDuration,
3068        preferred_lifetime_secs: u32,
3069        valid_lifetime_secs: u32,
3070        temp_idgen_retries: u8,
3071        dad_transmits: u16,
3072        retrans_timer: Duration,
3073        enable: bool,
3074    }
3075
3076    impl DontGenerateTemporaryAddressTest {
3077        fn with_pl_less_than_regen_advance(
3078            dad_transmits: u16,
3079            retrans_timer: Duration,
3080            temp_idgen_retries: u8,
3081        ) -> Self {
3082            DontGenerateTemporaryAddressTest {
3083                preferred_lifetime_config: ONE_HOUR,
3084                preferred_lifetime_secs: u32::try_from(
3085                    (SLAAC_MIN_REGEN_ADVANCE.get()
3086                        + (u32::from(temp_idgen_retries)
3087                            * u32::from(dad_transmits)
3088                            * retrans_timer))
3089                        .as_secs(),
3090                )
3091                .unwrap()
3092                    - 1,
3093                valid_lifetime_secs: TWO_HOURS_AS_SECS,
3094                temp_idgen_retries,
3095                dad_transmits,
3096                retrans_timer,
3097                enable: true,
3098            }
3099        }
3100    }
3101
3102    #[test_case(DontGenerateTemporaryAddressTest {
3103        preferred_lifetime_config: ONE_HOUR,
3104        preferred_lifetime_secs: ONE_HOUR_AS_SECS,
3105        valid_lifetime_secs: TWO_HOURS_AS_SECS,
3106        temp_idgen_retries: 0,
3107        dad_transmits: 0,
3108        retrans_timer: DEFAULT_RETRANS_TIMER,
3109        enable: false,
3110    }; "disabled")]
3111    #[test_case(DontGenerateTemporaryAddressTest{
3112        preferred_lifetime_config: ONE_HOUR,
3113        preferred_lifetime_secs: 0,
3114        valid_lifetime_secs: 0,
3115        temp_idgen_retries: 0,
3116        dad_transmits: 0,
3117        retrans_timer: DEFAULT_RETRANS_TIMER,
3118        enable: true,
3119    }; "zero lifetimes")]
3120    #[test_case(DontGenerateTemporaryAddressTest {
3121        preferred_lifetime_config: ONE_HOUR,
3122        preferred_lifetime_secs: TWO_HOURS_AS_SECS,
3123        valid_lifetime_secs: ONE_HOUR_AS_SECS,
3124        temp_idgen_retries: 0,
3125        dad_transmits: 0,
3126        retrans_timer: DEFAULT_RETRANS_TIMER,
3127        enable: true,
3128    }; "preferred larger than valid")]
3129    #[test_case(DontGenerateTemporaryAddressTest {
3130        preferred_lifetime_config: ONE_HOUR,
3131        preferred_lifetime_secs: 0,
3132        valid_lifetime_secs: TWO_HOURS_AS_SECS,
3133        temp_idgen_retries: 0,
3134        dad_transmits: 0,
3135        retrans_timer: DEFAULT_RETRANS_TIMER,
3136        enable: true,
3137    }; "not preferred")]
3138    #[test_case(DontGenerateTemporaryAddressTest::with_pl_less_than_regen_advance(
3139        0 /* dad_transmits */,
3140        DEFAULT_RETRANS_TIMER /* retrans_timer */,
3141        0 /* temp_idgen_retries */,
3142    ); "preferred lifetime less than regen advance with no DAD transmits")]
3143    #[test_case(DontGenerateTemporaryAddressTest::with_pl_less_than_regen_advance(
3144        1 /* dad_transmits */,
3145        DEFAULT_RETRANS_TIMER /* retrans_timer */,
3146        0 /* temp_idgen_retries */,
3147    ); "preferred lifetime less than regen advance with DAD transmits")]
3148    #[test_case(DontGenerateTemporaryAddressTest::with_pl_less_than_regen_advance(
3149        1 /* dad_transmits */,
3150        DEFAULT_RETRANS_TIMER /* retrans_timer */,
3151        1 /* temp_idgen_retries */,
3152    ); "preferred lifetime less than regen advance with DAD transmits and retries")]
3153    #[test_case(DontGenerateTemporaryAddressTest::with_pl_less_than_regen_advance(
3154        2 /* dad_transmits */,
3155        DEFAULT_RETRANS_TIMER + Duration::from_secs(1) /* retrans_timer */,
3156        3 /* temp_idgen_retries */,
3157    ); "preferred lifetime less than regen advance with multiple DAD transmits and multiple retries")]
3158    #[test_case(DontGenerateTemporaryAddressTest {
3159        preferred_lifetime_config: SLAAC_MIN_REGEN_ADVANCE,
3160        preferred_lifetime_secs: ONE_HOUR_AS_SECS,
3161        valid_lifetime_secs: TWO_HOURS_AS_SECS,
3162        temp_idgen_retries: 1,
3163        dad_transmits: 1,
3164        retrans_timer: DEFAULT_RETRANS_TIMER,
3165        enable: true,
3166    }; "configured preferred lifetime less than regen advance")]
3167    fn dont_generate_temporary_address(
3168        DontGenerateTemporaryAddressTest {
3169            preferred_lifetime_config,
3170            preferred_lifetime_secs,
3171            valid_lifetime_secs,
3172            temp_idgen_retries,
3173            dad_transmits,
3174            retrans_timer,
3175            enable,
3176        }: DontGenerateTemporaryAddressTest,
3177    ) {
3178        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
3179            SlaacConfiguration {
3180                temporary_address_configuration: if enable {
3181                    TemporarySlaacAddressConfiguration::Enabled {
3182                        temp_valid_lifetime: ONE_HOUR,
3183                        temp_preferred_lifetime: preferred_lifetime_config,
3184                        temp_idgen_retries,
3185                    }
3186                } else {
3187                    TemporarySlaacAddressConfiguration::Disabled
3188                },
3189                ..Default::default()
3190            },
3191            Default::default(),
3192            NonZeroU16::new(dad_transmits),
3193            retrans_timer,
3194        );
3195
3196        SlaacHandler::apply_slaac_update(
3197            &mut core_ctx,
3198            &mut bindings_ctx,
3199            &FakeDeviceId,
3200            SUBNET,
3201            NonZeroNdpLifetime::from_u32_with_infinite(preferred_lifetime_secs),
3202            NonZeroNdpLifetime::from_u32_with_infinite(valid_lifetime_secs),
3203        );
3204        assert_empty(core_ctx.state.iter_slaac_addrs());
3205        bindings_ctx.timers.assert_no_timers_installed();
3206    }
3207
3208    struct GenerateTemporaryAddressTest {
3209        pl_config: u32,
3210        vl_config: u32,
3211        dad_transmits: u16,
3212        retrans_timer: Duration,
3213        temp_idgen_retries: u8,
3214        pl_ra: u32,
3215        vl_ra: u32,
3216        expected_pl_addr: u32,
3217        expected_vl_addr: u32,
3218    }
3219    #[test_case(GenerateTemporaryAddressTest{
3220        pl_config: ONE_HOUR_AS_SECS,
3221        vl_config: ONE_HOUR_AS_SECS,
3222        dad_transmits: 0,
3223        retrans_timer: DEFAULT_RETRANS_TIMER,
3224        temp_idgen_retries: 0,
3225        pl_ra: ONE_HOUR_AS_SECS,
3226        vl_ra: ONE_HOUR_AS_SECS,
3227        expected_pl_addr: ONE_HOUR_AS_SECS,
3228        expected_vl_addr: ONE_HOUR_AS_SECS,
3229    }; "config and prefix same lifetimes")]
3230    #[test_case(GenerateTemporaryAddressTest{
3231        pl_config: ONE_HOUR_AS_SECS,
3232        vl_config: TWO_HOURS_AS_SECS,
3233        dad_transmits: 0,
3234        retrans_timer: DEFAULT_RETRANS_TIMER,
3235        temp_idgen_retries: 0,
3236        pl_ra: THREE_HOURS_AS_SECS,
3237        vl_ra: THREE_HOURS_AS_SECS,
3238        expected_pl_addr: ONE_HOUR_AS_SECS,
3239        expected_vl_addr: TWO_HOURS_AS_SECS,
3240    }; "config smaller than prefix lifetimes")]
3241    #[test_case(GenerateTemporaryAddressTest{
3242        pl_config: TWO_HOURS_AS_SECS,
3243        vl_config: THREE_HOURS_AS_SECS,
3244        dad_transmits: 0,
3245        retrans_timer: DEFAULT_RETRANS_TIMER,
3246        temp_idgen_retries: 0,
3247        pl_ra: ONE_HOUR_AS_SECS,
3248        vl_ra: TWO_HOURS_AS_SECS,
3249        expected_pl_addr: ONE_HOUR_AS_SECS,
3250        expected_vl_addr: TWO_HOURS_AS_SECS,
3251    }; "config larger than prefix lifetimes")]
3252    #[test_case(GenerateTemporaryAddressTest{
3253        pl_config: TWO_HOURS_AS_SECS,
3254        vl_config: THREE_HOURS_AS_SECS,
3255        dad_transmits: 0,
3256        retrans_timer: DEFAULT_RETRANS_TIMER,
3257        temp_idgen_retries: 0,
3258        pl_ra: INFINITE_LIFETIME,
3259        vl_ra: INFINITE_LIFETIME,
3260        expected_pl_addr: TWO_HOURS_AS_SECS,
3261        expected_vl_addr: THREE_HOURS_AS_SECS,
3262    }; "prefix with infinite lifetimes")]
3263    #[test_case(GenerateTemporaryAddressTest{
3264        pl_config: TWO_HOURS_AS_SECS,
3265        vl_config: THREE_HOURS_AS_SECS,
3266        dad_transmits: 1,
3267        retrans_timer: DEFAULT_RETRANS_TIMER,
3268        temp_idgen_retries: 0,
3269        pl_ra: INFINITE_LIFETIME,
3270        vl_ra: INFINITE_LIFETIME,
3271        expected_pl_addr: TWO_HOURS_AS_SECS,
3272        expected_vl_addr: THREE_HOURS_AS_SECS,
3273    }; "generate_with_dad_enabled")]
3274    #[test_case(GenerateTemporaryAddressTest{
3275        pl_config: TWO_HOURS_AS_SECS,
3276        vl_config: THREE_HOURS_AS_SECS,
3277        dad_transmits: 2,
3278        retrans_timer: Duration::from_secs(5),
3279        temp_idgen_retries: 3,
3280        pl_ra: INFINITE_LIFETIME,
3281        vl_ra: INFINITE_LIFETIME,
3282        expected_pl_addr: TWO_HOURS_AS_SECS,
3283        expected_vl_addr: THREE_HOURS_AS_SECS,
3284    }; "generate_with_dad_enabled_and_retries")]
3285    #[test_case(GenerateTemporaryAddressTest{
3286        pl_config: TWO_HOURS_AS_SECS,
3287        vl_config: THREE_HOURS_AS_SECS,
3288        dad_transmits: 1,
3289        retrans_timer: Duration::from_secs(10),
3290        temp_idgen_retries: 0,
3291        pl_ra: INFINITE_LIFETIME,
3292        vl_ra: INFINITE_LIFETIME,
3293        expected_pl_addr: TWO_HOURS_AS_SECS,
3294        expected_vl_addr: THREE_HOURS_AS_SECS,
3295    }; "generate_with_dad_enabled_but_no_retries")]
3296    fn generate_temporary_address(
3297        GenerateTemporaryAddressTest {
3298            pl_config,
3299            vl_config,
3300            dad_transmits,
3301            retrans_timer,
3302            temp_idgen_retries,
3303            pl_ra,
3304            vl_ra,
3305            expected_pl_addr,
3306            expected_vl_addr,
3307        }: GenerateTemporaryAddressTest,
3308    ) {
3309        let pl_config = Duration::from_secs(pl_config.into());
3310        let regen_advance = regen_advance(temp_idgen_retries, retrans_timer, dad_transmits);
3311
3312        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
3313            SlaacConfiguration {
3314                temporary_address_configuration: TemporarySlaacAddressConfiguration::Enabled {
3315                    temp_valid_lifetime: NonZeroDuration::new(Duration::from_secs(
3316                        vl_config.into(),
3317                    ))
3318                    .unwrap(),
3319                    temp_preferred_lifetime: NonZeroDuration::new(pl_config).unwrap(),
3320                    temp_idgen_retries,
3321                },
3322                ..Default::default()
3323            },
3324            Default::default(),
3325            NonZeroU16::new(dad_transmits),
3326            retrans_timer,
3327        );
3328
3329        let mut dup_rng = bindings_ctx.rng().deep_clone();
3330
3331        struct AddrProps {
3332            desync_factor: Duration,
3333            valid_until: FakeInstant,
3334            preferred_until: FakeInstant,
3335            entry: SlaacAddressEntry<FakeInstant>,
3336            deprecate_timer_id: InnerSlaacTimerId,
3337            invalidate_timer_id: InnerSlaacTimerId,
3338            regenerate_timer_id: InnerSlaacTimerId,
3339        }
3340
3341        let addr_props = |rng: &mut FakeCryptoRng<_>,
3342                          creation_time,
3343                          config_greater_than_ra_desync_factor_offset| {
3344            let valid_until = creation_time + Duration::from_secs(expected_vl_addr.into());
3345            let addr_sub =
3346                generate_global_temporary_address(&SUBNET, &IID, rng.gen(), &TEMP_SECRET_KEY);
3347            let desync_factor =
3348                desync_factor(rng, NonZeroDuration::new(pl_config).unwrap(), regen_advance)
3349                    .unwrap();
3350            let preferred_until = {
3351                let d = creation_time + Duration::from_secs(expected_pl_addr.into());
3352                if pl_config.as_secs() > pl_ra.into() {
3353                    d + config_greater_than_ra_desync_factor_offset
3354                } else {
3355                    d - desync_factor
3356                }
3357            };
3358
3359            AddrProps {
3360                desync_factor,
3361                valid_until,
3362                preferred_until,
3363                entry: SlaacAddressEntry {
3364                    addr_sub,
3365                    config: Ipv6AddrSlaacConfig {
3366                        inner: SlaacConfig::Temporary(TemporarySlaacConfig {
3367                            valid_until,
3368                            desync_factor,
3369                            creation_time,
3370                            dad_counter: 0,
3371                        }),
3372                        preferred_lifetime: PreferredLifetime::preferred_until(preferred_until),
3373                    },
3374                },
3375                deprecate_timer_id: InnerSlaacTimerId::DeprecateSlaacAddress {
3376                    addr: addr_sub.addr(),
3377                },
3378                invalidate_timer_id: InnerSlaacTimerId::InvalidateSlaacAddress {
3379                    addr: addr_sub.addr(),
3380                },
3381                regenerate_timer_id: InnerSlaacTimerId::RegenerateTemporaryAddress {
3382                    addr_subnet: addr_sub,
3383                },
3384            }
3385        };
3386
3387        // Generate the first temporary SLAAC address.
3388        SlaacHandler::apply_slaac_update(
3389            &mut core_ctx,
3390            &mut bindings_ctx,
3391            &FakeDeviceId,
3392            SUBNET,
3393            NonZeroNdpLifetime::from_u32_with_infinite(pl_ra),
3394            NonZeroNdpLifetime::from_u32_with_infinite(vl_ra),
3395        );
3396        let AddrProps {
3397            desync_factor: first_desync_factor,
3398            valid_until: first_valid_until,
3399            preferred_until: first_preferred_until,
3400            entry: first_entry,
3401            deprecate_timer_id: first_deprecate_timer_id,
3402            invalidate_timer_id: first_invalidate_timer_id,
3403            regenerate_timer_id: first_regenerate_timer_id,
3404        } = addr_props(&mut dup_rng, bindings_ctx.now(), Duration::ZERO);
3405        assert_eq!(core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(), [first_entry]);
3406        core_ctx.state.slaac_state.timers.assert_timers([
3407            (first_deprecate_timer_id, (), first_preferred_until),
3408            (first_invalidate_timer_id, (), first_valid_until),
3409            (first_regenerate_timer_id, (), first_preferred_until - regen_advance.get()),
3410        ]);
3411
3412        // Trigger the regenerate timer to generate the second temporary SLAAC
3413        // address.
3414        assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()),);
3415        let AddrProps {
3416            desync_factor: second_desync_factor,
3417            valid_until: second_valid_until,
3418            preferred_until: second_preferred_until,
3419            entry: second_entry,
3420            deprecate_timer_id: second_deprecate_timer_id,
3421            invalidate_timer_id: second_invalidate_timer_id,
3422            regenerate_timer_id: second_regenerate_timer_id,
3423        } = addr_props(&mut dup_rng, bindings_ctx.now(), first_desync_factor);
3424        assert_eq!(
3425            core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(),
3426            [first_entry, second_entry]
3427        );
3428        let second_regen_at = second_preferred_until - regen_advance.get();
3429        core_ctx.state.slaac_state.timers.assert_timers([
3430            (first_deprecate_timer_id, (), first_preferred_until),
3431            (first_invalidate_timer_id, (), first_valid_until),
3432            (second_deprecate_timer_id, (), second_preferred_until),
3433            (second_invalidate_timer_id, (), second_valid_until),
3434            (second_regenerate_timer_id, (), second_regen_at),
3435        ]);
3436
3437        // Deprecate first address.
3438        assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()),);
3439        let first_entry = first_entry.to_deprecated();
3440        assert_eq!(
3441            core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(),
3442            [first_entry, second_entry]
3443        );
3444        core_ctx.state.slaac_state.timers.assert_timers([
3445            (first_invalidate_timer_id, (), first_valid_until),
3446            (second_deprecate_timer_id, (), second_preferred_until),
3447            (second_invalidate_timer_id, (), second_valid_until),
3448            (second_regenerate_timer_id, (), second_regen_at),
3449        ]);
3450
3451        let third_created_at = {
3452            let expected_timer_order = if first_valid_until > second_regen_at {
3453                [second_regenerate_timer_id, second_deprecate_timer_id, first_invalidate_timer_id]
3454            } else {
3455                [first_invalidate_timer_id, second_regenerate_timer_id, second_deprecate_timer_id]
3456            };
3457
3458            let mut third_created_at = None;
3459            for timer_id in expected_timer_order.iter() {
3460                let timer_id = *timer_id;
3461
3462                core_ctx.state.slaac_state.timers.assert_top(&timer_id, &());
3463                assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(new_timer_id()));
3464
3465                if timer_id == second_regenerate_timer_id {
3466                    assert_eq!(third_created_at, None);
3467                    third_created_at = Some(bindings_ctx.now());
3468                }
3469            }
3470
3471            third_created_at.unwrap()
3472        };
3473
3474        // Make sure we regenerated the third address, deprecated the second and
3475        // invalidated the first.
3476        let AddrProps {
3477            desync_factor: _,
3478            valid_until: third_valid_until,
3479            preferred_until: third_preferred_until,
3480            entry: third_entry,
3481            deprecate_timer_id: third_deprecate_timer_id,
3482            invalidate_timer_id: third_invalidate_timer_id,
3483            regenerate_timer_id: third_regenerate_timer_id,
3484        } = addr_props(&mut dup_rng, third_created_at, first_desync_factor + second_desync_factor);
3485        let second_entry = second_entry.to_deprecated();
3486        assert_eq!(
3487            core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>(),
3488            [second_entry, third_entry]
3489        );
3490        core_ctx.state.slaac_state.timers.assert_timers([
3491            (second_invalidate_timer_id, (), second_valid_until),
3492            (third_deprecate_timer_id, (), third_preferred_until),
3493            (third_invalidate_timer_id, (), third_valid_until),
3494            (third_regenerate_timer_id, (), third_preferred_until - regen_advance.get()),
3495        ]);
3496    }
3497
3498    #[test]
3499    fn temporary_address_not_updated_while_disabled() {
3500        let want_valid_until =
3501            FakeInstant::default() + Duration::from_secs(THREE_HOURS_AS_SECS.into());
3502        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context(
3503            SlaacConfiguration {
3504                stable_address_configuration: StableSlaacAddressConfiguration::Disabled,
3505                temporary_address_configuration: TemporarySlaacAddressConfiguration::Disabled,
3506            },
3507            FakeSlaacAddrs {
3508                slaac_addrs: vec![SlaacAddressEntry {
3509                    addr_sub: testutil::calculate_slaac_addr_sub(SUBNET, IID),
3510                    config: Ipv6AddrSlaacConfig {
3511                        inner: SlaacConfig::Temporary(TemporarySlaacConfig {
3512                            valid_until: want_valid_until,
3513                            desync_factor: Duration::default(),
3514                            creation_time: FakeInstant::default(),
3515                            dad_counter: 0,
3516                        }),
3517                        preferred_lifetime: PreferredLifetime::preferred_forever(),
3518                    },
3519                }],
3520                ..Default::default()
3521            },
3522            None, /* dad_transmits */
3523            DEFAULT_RETRANS_TIMER,
3524        );
3525
3526        SlaacHandler::apply_slaac_update(
3527            &mut core_ctx,
3528            &mut bindings_ctx,
3529            &FakeDeviceId,
3530            SUBNET,
3531            NonZeroNdpLifetime::from_u32_with_infinite(FOUR_HOURS_AS_SECS),
3532            NonZeroNdpLifetime::from_u32_with_infinite(FOUR_HOURS_AS_SECS),
3533        );
3534        let addrs = core_ctx.state.iter_slaac_addrs().collect::<Vec<_>>();
3535        assert_eq!(addrs.len(), 1);
3536        let SlaacAddressEntry { config: Ipv6AddrSlaacConfig { inner, preferred_lifetime }, .. } =
3537            addrs[0];
3538        assert_matches!(inner,SlaacConfig::Temporary(TemporarySlaacConfig {
3539                valid_until,
3540                ..
3541            }) => {
3542            assert_eq!(valid_until, want_valid_until);
3543        });
3544        // Even though we don't remove the addresses immediately, they may
3545        // become deprecated. So the address is not completely removed as a
3546        // side-effect of disabling temporary addresses, but we'll steer it away
3547        // from being used more.
3548        assert_eq!(preferred_lifetime, PreferredLifetime::Deprecated);
3549    }
3550}