Skip to main content

netstack3_ip/device/
slaac.rs

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