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