Skip to main content

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