netstack3_ip/device/
state.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//! State for an IP device.
6
7use alloc::vec::Vec;
8use core::fmt::Debug;
9use core::hash::Hash;
10use core::num::{NonZeroU16, NonZeroU8};
11use core::ops::{Deref, DerefMut};
12use core::time::Duration;
13
14use derivative::Derivative;
15use lock_order::lock::{OrderedLockAccess, OrderedLockRef};
16use net_types::ip::{AddrSubnet, GenericOverIp, Ip, IpMarked, IpVersionMarker, Ipv4, Ipv6};
17use net_types::Witness as _;
18use netstack3_base::sync::{Mutex, PrimaryRc, RwLock, StrongRc, WeakRc};
19use netstack3_base::{
20    AssignedAddrIpExt, BroadcastIpExt, CoreTimerContext, ExistsError, Inspectable,
21    InspectableValue, Inspector, Instant, InstantBindingsTypes, IpAddressId,
22    NestedIntoCoreTimerCtx, NotFoundError, ReferenceNotifiers, TimerBindingsTypes, TimerContext,
23    WeakDeviceIdentifier,
24};
25use packet_formats::icmp::ndp::NonZeroNdpLifetime;
26use packet_formats::utils::NonZeroDuration;
27
28use crate::internal::counters::{IpCounters, IpCountersIpExt};
29use crate::internal::device::dad::{DadIpExt, DadState};
30use crate::internal::device::route_discovery::Ipv6RouteDiscoveryState;
31use crate::internal::device::router_solicitation::RsState;
32use crate::internal::device::slaac::{SlaacConfiguration, SlaacState};
33use crate::internal::device::{
34    IpDeviceAddr, IpDeviceTimerId, Ipv4DeviceTimerId, Ipv6DeviceTimerId, WeakIpAddressId,
35};
36use crate::internal::gmp::igmp::{IgmpConfig, IgmpCounters, IgmpTimerId, IgmpTypeLayout};
37use crate::internal::gmp::mld::{MldConfig, MldCounters, MldTimerId, MldTypeLayout};
38use crate::internal::gmp::{GmpGroupState, GmpState, GmpTimerId, GmpTypeLayout, MulticastGroupSet};
39use crate::internal::types::RawMetric;
40
41/// The default value for *RetransTimer* as defined in [RFC 4861 section 10].
42///
43/// [RFC 4861 section 10]: https://tools.ietf.org/html/rfc4861#section-10
44pub const RETRANS_TIMER_DEFAULT: NonZeroDuration = NonZeroDuration::from_secs(1).unwrap();
45
46/// The default value for the default hop limit to be used when sending IP
47/// packets.
48const DEFAULT_HOP_LIMIT: NonZeroU8 = NonZeroU8::new(64).unwrap();
49
50/// An `Ip` extension trait adding IP device state properties.
51pub trait IpDeviceStateIpExt:
52    DadIpExt + BroadcastIpExt + IpCountersIpExt + AssignedAddrIpExt
53{
54    /// Configuration held for an IP address assigned to an interface.
55    type AddressConfig<I: Instant>: Default + Debug + Inspectable + PartialEq + Send + Sync;
56    /// The GMP protocol-specific configuration.
57    type GmpProtoConfig: Default;
58    /// The GMP type layout used by IP-version specific state.
59    type GmpTypeLayout<BT: IpDeviceStateBindingsTypes>: GmpTypeLayout<Self, BT>;
60    /// The timer id for GMP timers.
61    type GmpTimerId<D: WeakDeviceIdentifier>: From<GmpTimerId<Self, D>>;
62}
63
64impl IpDeviceStateIpExt for Ipv4 {
65    type AddressConfig<I: Instant> = Ipv4AddrConfig<I>;
66    type GmpTimerId<D: WeakDeviceIdentifier> = IgmpTimerId<D>;
67    type GmpProtoConfig = IgmpConfig;
68    type GmpTypeLayout<BT: IpDeviceStateBindingsTypes> = IgmpTypeLayout;
69}
70
71impl IpDeviceStateIpExt for Ipv6 {
72    type AddressConfig<I: Instant> = Ipv6AddrConfig<I>;
73    type GmpTimerId<D: WeakDeviceIdentifier> = MldTimerId<D>;
74    type GmpProtoConfig = MldConfig;
75    type GmpTypeLayout<BT: IpDeviceStateBindingsTypes> = MldTypeLayout;
76}
77
78/// The primary reference to the state associated with an IP address assigned
79/// to an IP device.
80pub struct PrimaryAddressId<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>(
81    PrimaryRc<IpAddressEntry<I, BT>>,
82);
83
84impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> Debug for PrimaryAddressId<I, BT> {
85    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
86        let Self(rc) = self;
87        write!(f, "PrimaryAddressId({:?} => {})", rc.debug_id(), rc.addr_sub)
88    }
89}
90
91impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> Deref for PrimaryAddressId<I, BT> {
92    type Target = IpAddressEntry<I, BT>;
93
94    fn deref(&self) -> &Self::Target {
95        let Self(inner) = self;
96        inner.deref()
97    }
98}
99
100impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> PrimaryAddressId<I, BT> {
101    /// Creates a new primary reference to the provided state.
102    fn new_with_strong_clone(addr: IpAddressEntry<I, BT>) -> (Self, AddressId<I, BT>) {
103        let primary = PrimaryRc::new(addr);
104        let strong = PrimaryRc::clone_strong(&primary);
105        (Self(primary), AddressId(strong))
106    }
107
108    /// Clones a strongly-held reference.
109    pub fn clone_strong(&self) -> AddressId<I, BT> {
110        let Self(inner) = self;
111        AddressId(PrimaryRc::clone_strong(inner))
112    }
113
114    /// Checks for equality with the provided strongly-held reference.
115    pub fn ptr_eq(&self, other: &AddressId<I, BT>) -> bool {
116        let Self(inner) = self;
117        let AddressId(other) = other;
118        PrimaryRc::ptr_eq(inner, other)
119    }
120
121    /// Consumes `self` and returns the inner [`PrimaryRc`].
122    pub fn into_inner(self) -> PrimaryRc<IpAddressEntry<I, BT>> {
123        self.0
124    }
125
126    /// The underlying address for this ID.
127    pub fn addr(&self) -> I::AssignedWitness {
128        let Self(inner) = self;
129        inner.addr_sub.addr()
130    }
131}
132
133/// A strongly-held reference to an IP address assigned to a device.
134#[derive(Derivative)]
135#[derivative(Clone(bound = ""), Eq(bound = ""), Hash(bound = ""), PartialEq(bound = ""))]
136pub struct AddressId<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>(
137    StrongRc<IpAddressEntry<I, BT>>,
138);
139
140impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> Debug for AddressId<I, BT> {
141    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
142        let Self(rc) = self;
143        write!(f, "AddressId({:?} => {})", rc.debug_id(), self.addr_sub)
144    }
145}
146
147impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> Deref for AddressId<I, BT> {
148    type Target = IpAddressEntry<I, BT>;
149
150    fn deref(&self) -> &Self::Target {
151        let Self(inner) = self;
152        inner.deref()
153    }
154}
155
156impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> IpAddressId<I::Addr>
157    for AddressId<I, BT>
158{
159    type Weak = WeakAddressId<I, BT>;
160
161    fn downgrade(&self) -> Self::Weak {
162        let Self(inner) = self;
163        WeakAddressId(StrongRc::downgrade(inner))
164    }
165
166    fn addr(&self) -> IpDeviceAddr<I::Addr> {
167        let Self(inner) = self;
168        inner.addr_sub().addr().into()
169    }
170
171    fn addr_sub(&self) -> AddrSubnet<I::Addr, I::AssignedWitness> {
172        let Self(inner) = self;
173        *inner.addr_sub()
174    }
175}
176
177/// A weakly-held reference to an IP address assigned to a device.
178#[derive(Derivative)]
179#[derivative(Clone(bound = ""), Eq(bound = ""), Hash(bound = ""), PartialEq(bound = ""))]
180pub struct WeakAddressId<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>(
181    WeakRc<IpAddressEntry<I, BT>>,
182);
183
184impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> Debug for WeakAddressId<I, BT> {
185    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
186        let Self(rc) = self;
187        if let Some(id) = self.upgrade() {
188            write!(f, "WeakAddressId({:?} => {})", rc.debug_id(), id.addr_sub)
189        } else {
190            write!(f, "WeakAddressId({:?} => {})", rc.debug_id(), I::NAME)
191        }
192    }
193}
194
195impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> InspectableValue
196    for WeakAddressId<I, BT>
197{
198    fn record<II: Inspector>(&self, name: &str, inspector: &mut II) {
199        inspector.record_debug(name, self);
200    }
201}
202
203impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> WeakIpAddressId<I::Addr>
204    for WeakAddressId<I, BT>
205{
206    type Strong = AddressId<I, BT>;
207
208    fn upgrade(&self) -> Option<Self::Strong> {
209        let Self(inner) = self;
210        inner.upgrade().map(AddressId)
211    }
212
213    fn is_assigned(&self) -> bool {
214        let Self(inner) = self;
215        inner.strong_count() != 0
216    }
217}
218
219/// The flags for an IP device.
220#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
221pub struct IpDeviceFlags {
222    /// Is the device enabled?
223    pub ip_enabled: bool,
224}
225
226/// The state kept for each device to handle multicast group membership.
227pub struct IpDeviceMulticastGroups<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> {
228    /// Multicast groups this device has joined.
229    pub groups: MulticastGroupSet<I::Addr, GmpGroupState<I, BT>>,
230    /// GMP state.
231    pub gmp: GmpState<I, I::GmpTypeLayout<BT>, BT>,
232    /// GMP protocol-specific configuration.
233    pub gmp_config: I::GmpProtoConfig,
234}
235
236/// A container for the default hop limit kept by [`IpDeviceState`].
237///
238/// This type makes the [`OrderedLockAccess`] implementation clearer by
239/// newtyping the `NonZeroU8` value and adding a version marker.
240#[derive(Copy, Clone, Debug)]
241pub struct DefaultHopLimit<I: Ip>(NonZeroU8, IpVersionMarker<I>);
242
243impl<I: Ip> Deref for DefaultHopLimit<I> {
244    type Target = NonZeroU8;
245    fn deref(&self) -> &NonZeroU8 {
246        let Self(value, IpVersionMarker { .. }) = self;
247        value
248    }
249}
250
251impl<I: Ip> DerefMut for DefaultHopLimit<I> {
252    fn deref_mut(&mut self) -> &mut NonZeroU8 {
253        let Self(value, IpVersionMarker { .. }) = self;
254        value
255    }
256}
257
258impl<I: Ip> Default for DefaultHopLimit<I> {
259    fn default() -> Self {
260        Self(DEFAULT_HOP_LIMIT, IpVersionMarker::new())
261    }
262}
263
264/// The state common to all IP devices.
265#[derive(GenericOverIp)]
266#[generic_over_ip(I, Ip)]
267pub struct IpDeviceState<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> {
268    /// IP addresses assigned to this device.
269    ///
270    /// IPv6 addresses may be tentative (performing NDP's Duplicate Address
271    /// Detection).
272    ///
273    /// Does not contain any duplicates.
274    addrs: RwLock<IpDeviceAddresses<I, BT>>,
275
276    /// Multicast groups and GMP handling state.
277    multicast_groups: RwLock<IpDeviceMulticastGroups<I, BT>>,
278
279    /// The default TTL (IPv4) or hop limit (IPv6) for outbound packets sent
280    /// over this device.
281    default_hop_limit: RwLock<DefaultHopLimit<I>>,
282
283    /// The flags for this device.
284    flags: Mutex<IpMarked<I, IpDeviceFlags>>,
285
286    /// The IP counters for this device.
287    ip_counters: IpCounters<I>,
288}
289
290impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>
291    OrderedLockAccess<IpDeviceAddresses<I, BT>> for DualStackIpDeviceState<BT>
292{
293    type Lock = RwLock<IpDeviceAddresses<I, BT>>;
294    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
295        OrderedLockRef::new(&self.ip_state::<I>().addrs)
296    }
297}
298
299impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>
300    OrderedLockAccess<IpDeviceMulticastGroups<I, BT>> for DualStackIpDeviceState<BT>
301{
302    type Lock = RwLock<IpDeviceMulticastGroups<I, BT>>;
303    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
304        OrderedLockRef::new(&self.ip_state::<I>().multicast_groups)
305    }
306}
307
308impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> OrderedLockAccess<DefaultHopLimit<I>>
309    for DualStackIpDeviceState<BT>
310{
311    type Lock = RwLock<DefaultHopLimit<I>>;
312    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
313        OrderedLockRef::new(&self.ip_state::<I>().default_hop_limit)
314    }
315}
316
317impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>
318    OrderedLockAccess<IpMarked<I, IpDeviceFlags>> for DualStackIpDeviceState<BT>
319{
320    type Lock = Mutex<IpMarked<I, IpDeviceFlags>>;
321    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
322        OrderedLockRef::new(&self.ip_state::<I>().flags)
323    }
324}
325
326impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<SlaacState<BT>>
327    for DualStackIpDeviceState<BT>
328{
329    type Lock = Mutex<SlaacState<BT>>;
330    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
331        OrderedLockRef::new(&self.ipv6.slaac_state)
332    }
333}
334
335impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> IpDeviceState<I, BT> {
336    /// A direct accessor to `IpDeviceAddresses` available in tests.
337    #[cfg(any(test, feature = "testutils"))]
338    pub fn addrs(&self) -> &RwLock<IpDeviceAddresses<I, BT>> {
339        &self.addrs
340    }
341}
342
343impl<I: IpDeviceStateIpExt, BC: IpDeviceStateBindingsTypes + TimerContext> IpDeviceState<I, BC> {
344    fn new<D: WeakDeviceIdentifier, CC: CoreTimerContext<I::GmpTimerId<D>, BC>>(
345        bindings_ctx: &mut BC,
346        device_id: D,
347    ) -> IpDeviceState<I, BC> {
348        IpDeviceState {
349            addrs: Default::default(),
350            multicast_groups: RwLock::new(IpDeviceMulticastGroups {
351                groups: Default::default(),
352                gmp: GmpState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(bindings_ctx, device_id),
353                gmp_config: Default::default(),
354            }),
355            default_hop_limit: Default::default(),
356            flags: Default::default(),
357            ip_counters: Default::default(),
358        }
359    }
360}
361
362/// A device's IP addresses for IP version `I`.
363#[derive(Derivative)]
364#[derivative(Default(bound = ""))]
365#[cfg_attr(test, derive(Debug))]
366pub struct IpDeviceAddresses<I: Ip + IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> {
367    addrs: Vec<PrimaryAddressId<I, BT>>,
368}
369
370// TODO(https://fxbug.dev/42165707): Once we figure out what invariants we want to
371// hold regarding the set of IP addresses assigned to a device, ensure that all
372// of the methods on `IpDeviceAddresses` uphold those invariants.
373impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> IpDeviceAddresses<I, BT> {
374    /// Iterates over the addresses assigned to this device.
375    pub fn iter(
376        &self,
377    ) -> impl ExactSizeIterator<Item = &PrimaryAddressId<I, BT>> + ExactSizeIterator + Clone {
378        self.addrs.iter()
379    }
380
381    /// Iterates over strong clones of addresses assigned to this device.
382    pub fn strong_iter(&self) -> AddressIdIter<'_, I, BT> {
383        AddressIdIter(self.addrs.iter())
384    }
385
386    /// Adds an IP address to this interface.
387    pub fn add(&mut self, addr: IpAddressEntry<I, BT>) -> Result<AddressId<I, BT>, ExistsError> {
388        if self.iter().any(|a| a.addr() == addr.addr_sub.addr()) {
389            return Err(ExistsError);
390        }
391        let (primary, strong) = PrimaryAddressId::new_with_strong_clone(addr);
392        self.addrs.push(primary);
393        Ok(strong)
394    }
395
396    /// Removes the address.
397    pub fn remove(&mut self, addr: &I::Addr) -> Result<PrimaryAddressId<I, BT>, NotFoundError> {
398        let (index, _entry): (_, &PrimaryAddressId<I, BT>) = self
399            .addrs
400            .iter()
401            .enumerate()
402            .find(|(_, entry)| entry.addr().get() == *addr)
403            .ok_or(NotFoundError)?;
404        Ok(self.addrs.remove(index))
405    }
406}
407
408/// An iterator over address StrongIds. Created from `IpDeviceAddresses`.
409pub struct AddressIdIter<'a, I: Ip + IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>(
410    core::slice::Iter<'a, PrimaryAddressId<I, BT>>,
411);
412
413impl<'a, I: Ip + IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> Iterator
414    for AddressIdIter<'a, I, BT>
415{
416    type Item = AddressId<I, BT>;
417
418    fn next(&mut self) -> Option<Self::Item> {
419        let Self(inner) = self;
420        inner.next().map(|addr| addr.clone_strong())
421    }
422}
423
424/// The state common to all IPv4 devices.
425pub struct Ipv4DeviceState<BT: IpDeviceStateBindingsTypes> {
426    ip_state: IpDeviceState<Ipv4, BT>,
427    config: RwLock<Ipv4DeviceConfiguration>,
428    igmp_counters: IgmpCounters,
429}
430
431impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv4DeviceConfiguration>
432    for DualStackIpDeviceState<BT>
433{
434    type Lock = RwLock<Ipv4DeviceConfiguration>;
435    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
436        OrderedLockRef::new(&self.ipv4.config)
437    }
438}
439
440impl<BC: IpDeviceStateBindingsTypes + TimerContext> Ipv4DeviceState<BC> {
441    fn new<D: WeakDeviceIdentifier, CC: CoreTimerContext<Ipv4DeviceTimerId<D, BC>, BC>>(
442        bindings_ctx: &mut BC,
443        device_id: D,
444    ) -> Ipv4DeviceState<BC> {
445        Ipv4DeviceState {
446            ip_state: IpDeviceState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(
447                bindings_ctx,
448                device_id,
449            ),
450            config: Default::default(),
451            igmp_counters: Default::default(),
452        }
453    }
454}
455
456impl<BT: IpDeviceStateBindingsTypes> Ipv4DeviceState<BT> {
457    fn igmp_counters(&self) -> &IgmpCounters {
458        &self.igmp_counters
459    }
460}
461
462impl<BT: IpDeviceStateBindingsTypes> AsRef<IpDeviceState<Ipv4, BT>> for Ipv4DeviceState<BT> {
463    fn as_ref(&self) -> &IpDeviceState<Ipv4, BT> {
464        &self.ip_state
465    }
466}
467
468impl<BT: IpDeviceStateBindingsTypes> AsMut<IpDeviceState<Ipv4, BT>> for Ipv4DeviceState<BT> {
469    fn as_mut(&mut self) -> &mut IpDeviceState<Ipv4, BT> {
470        &mut self.ip_state
471    }
472}
473
474/// Configurations common to all IP devices.
475#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
476pub struct IpDeviceConfiguration {
477    /// Is a Group Messaging Protocol (GMP) enabled for this device?
478    ///
479    /// If `gmp_enabled` is false, multicast groups will still be added to
480    /// `multicast_groups`, but we will not inform the network of our membership
481    /// in those groups using a GMP.
482    ///
483    /// Default: `false`.
484    pub gmp_enabled: bool,
485
486    /// A flag indicating whether forwarding of unicast IP packets not destined
487    /// for this device is enabled.
488    ///
489    /// This flag controls whether or not packets can be forwarded from this
490    /// device. That is, when a packet arrives at a device it is not destined
491    /// for, the packet can only be forwarded if the device it arrived at has
492    /// forwarding enabled and there exists another device that has a path to
493    /// the packet's destination, regardless of the other device's forwarding
494    /// ability.
495    ///
496    /// Default: `false`.
497    pub unicast_forwarding_enabled: bool,
498
499    /// A flag indicating whether forwarding of multicast IP packets received on
500    /// this device is enabled.
501    ///
502    /// This flag controls whether or not packets can be forwarded from this
503    /// device. That is, when a multicast packet arrives at this device, the
504    /// multicast routing table will be consulted and the packet will be
505    /// forwarded out of the matching route's corresponding outbound devices
506    /// (regardless of the outbound device's forwarding ability). Enabling
507    /// multicast forwarding does not disrupt local delivery: the packet will
508    /// both be forwarded and delivered locally.
509    ///
510    /// Default: `false`.
511    pub multicast_forwarding_enabled: bool,
512    /// The number of probes to send as part of Duplicate Address Detection.
513    ///
514    /// For IPv6, this corresponds to NDP's `DupAddrDetectTransmits` parameter
515    /// as defined by RFC 4862 section 5.1.
516    ///
517    /// For IPv4, this corresponds to ACD's `PROBE_NUM` parameter, as defined by
518    /// RFC 5227, section 1.1.
519    ///
520    /// A value of `None` means DAD will not be performed on the interface.
521    pub dad_transmits: Option<NonZeroU16>,
522}
523
524/// Configuration common to all IPv4 devices.
525#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
526pub struct Ipv4DeviceConfiguration {
527    /// The configuration common to all IP devices.
528    pub ip_config: IpDeviceConfiguration,
529}
530
531impl Ipv4DeviceConfiguration {
532    /// The default `PROBE_NUM` value from RFC 5227 Section 1.1.
533    pub const DEFAULT_DUPLICATE_ADDRESS_DETECTION_TRANSMITS: NonZeroU16 =
534        NonZeroU16::new(3).unwrap();
535}
536
537impl AsRef<IpDeviceConfiguration> for Ipv4DeviceConfiguration {
538    fn as_ref(&self) -> &IpDeviceConfiguration {
539        &self.ip_config
540    }
541}
542
543impl AsMut<IpDeviceConfiguration> for Ipv4DeviceConfiguration {
544    fn as_mut(&mut self) -> &mut IpDeviceConfiguration {
545        &mut self.ip_config
546    }
547}
548
549/// Configuration common to all IPv6 devices.
550#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
551pub struct Ipv6DeviceConfiguration {
552    /// Value for NDP's `MAX_RTR_SOLICITATIONS` parameter to configure how many
553    /// router solicitation messages to send when solicing routers.
554    ///
555    /// A value of `None` means router solicitation will not be performed.
556    ///
557    /// See [RFC 4861 section 6.3.7] for details.
558    ///
559    /// [RFC 4861 section 6.3.7]: https://datatracker.ietf.org/doc/html/rfc4861#section-6.3.7
560    pub max_router_solicitations: Option<NonZeroU8>,
561
562    /// The configuration for SLAAC.
563    pub slaac_config: SlaacConfiguration,
564
565    /// The configuration common to all IP devices.
566    pub ip_config: IpDeviceConfiguration,
567}
568
569impl Ipv6DeviceConfiguration {
570    /// The default `MAX_RTR_SOLICITATIONS` value from [RFC 4861 section 10].
571    ///
572    /// [RFC 4861 section 10]: https://datatracker.ietf.org/doc/html/rfc4861#section-10
573    pub const DEFAULT_MAX_RTR_SOLICITATIONS: NonZeroU8 = NonZeroU8::new(3).unwrap();
574
575    /// The default `DupAddrDetectTransmits` value from [RFC 4862 Section 5.1]
576    ///
577    /// [RFC 4862 Section 5.1]: https://www.rfc-editor.org/rfc/rfc4862#section-5.1
578    pub const DEFAULT_DUPLICATE_ADDRESS_DETECTION_TRANSMITS: NonZeroU16 =
579        NonZeroU16::new(1).unwrap();
580}
581
582impl AsRef<IpDeviceConfiguration> for Ipv6DeviceConfiguration {
583    fn as_ref(&self) -> &IpDeviceConfiguration {
584        &self.ip_config
585    }
586}
587
588impl AsMut<IpDeviceConfiguration> for Ipv6DeviceConfiguration {
589    fn as_mut(&mut self) -> &mut IpDeviceConfiguration {
590        &mut self.ip_config
591    }
592}
593
594impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv6NetworkLearnedParameters>
595    for DualStackIpDeviceState<BT>
596{
597    type Lock = RwLock<Ipv6NetworkLearnedParameters>;
598    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
599        OrderedLockRef::new(&self.ipv6.learned_params)
600    }
601}
602
603impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv6RouteDiscoveryState<BT>>
604    for DualStackIpDeviceState<BT>
605{
606    type Lock = Mutex<Ipv6RouteDiscoveryState<BT>>;
607    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
608        OrderedLockRef::new(&self.ipv6.route_discovery)
609    }
610}
611
612impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<RsState<BT>> for DualStackIpDeviceState<BT> {
613    type Lock = Mutex<RsState<BT>>;
614    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
615        OrderedLockRef::new(&self.ipv6.router_solicitations)
616    }
617}
618
619impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv6DeviceConfiguration>
620    for DualStackIpDeviceState<BT>
621{
622    type Lock = RwLock<Ipv6DeviceConfiguration>;
623    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
624        OrderedLockRef::new(&self.ipv6.config)
625    }
626}
627
628/// IPv6 device parameters that can be learned from router advertisements.
629#[derive(Default)]
630pub struct Ipv6NetworkLearnedParameters {
631    /// The time between retransmissions of Neighbor Solicitation messages to a
632    /// neighbor when resolving the address or when probing the reachability of
633    /// a neighbor.
634    ///
635    ///
636    /// See RetransTimer in [RFC 4861 section 6.3.2] for more details.
637    ///
638    /// [RFC 4861 section 6.3.2]: https://tools.ietf.org/html/rfc4861#section-6.3.2
639    pub retrans_timer: Option<NonZeroDuration>,
640}
641
642impl Ipv6NetworkLearnedParameters {
643    /// Returns the learned retransmission timer or the default stack value if a
644    /// network-learned one isn't available.
645    pub fn retrans_timer_or_default(&self) -> NonZeroDuration {
646        self.retrans_timer.clone().unwrap_or(RETRANS_TIMER_DEFAULT)
647    }
648
649    /// Forgets any learned network parameters, resetting to the default values.
650    pub fn reset(&mut self) {
651        *self = Default::default()
652    }
653}
654
655/// The state common to all IPv6 devices.
656pub struct Ipv6DeviceState<BT: IpDeviceStateBindingsTypes> {
657    learned_params: RwLock<Ipv6NetworkLearnedParameters>,
658    route_discovery: Mutex<Ipv6RouteDiscoveryState<BT>>,
659    router_solicitations: Mutex<RsState<BT>>,
660    ip_state: IpDeviceState<Ipv6, BT>,
661    config: RwLock<Ipv6DeviceConfiguration>,
662    slaac_state: Mutex<SlaacState<BT>>,
663    mld_counters: MldCounters,
664}
665
666impl<BC: IpDeviceStateBindingsTypes + TimerContext> Ipv6DeviceState<BC> {
667    pub fn new<D: WeakDeviceIdentifier, CC: CoreTimerContext<Ipv6DeviceTimerId<D, BC>, BC>>(
668        bindings_ctx: &mut BC,
669        device_id: D,
670    ) -> Self {
671        Ipv6DeviceState {
672            learned_params: Default::default(),
673            route_discovery: Mutex::new(Ipv6RouteDiscoveryState::new::<
674                _,
675                NestedIntoCoreTimerCtx<CC, _>,
676            >(bindings_ctx, device_id.clone())),
677            router_solicitations: Default::default(),
678            ip_state: IpDeviceState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(
679                bindings_ctx,
680                device_id.clone(),
681            ),
682            config: Default::default(),
683            slaac_state: Mutex::new(SlaacState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(
684                bindings_ctx,
685                device_id,
686            )),
687            mld_counters: Default::default(),
688        }
689    }
690}
691
692impl<BT: IpDeviceStateBindingsTypes> Ipv6DeviceState<BT> {
693    fn mld_counters(&self) -> &MldCounters {
694        &self.mld_counters
695    }
696}
697
698impl<BT: IpDeviceStateBindingsTypes> AsRef<IpDeviceState<Ipv6, BT>> for Ipv6DeviceState<BT> {
699    fn as_ref(&self) -> &IpDeviceState<Ipv6, BT> {
700        &self.ip_state
701    }
702}
703
704impl<BT: IpDeviceStateBindingsTypes> AsMut<IpDeviceState<Ipv6, BT>> for Ipv6DeviceState<BT> {
705    fn as_mut(&mut self) -> &mut IpDeviceState<Ipv6, BT> {
706        &mut self.ip_state
707    }
708}
709
710/// Bindings types required for IP device state.
711pub trait IpDeviceStateBindingsTypes:
712    InstantBindingsTypes + TimerBindingsTypes + ReferenceNotifiers + 'static
713{
714}
715impl<BT> IpDeviceStateBindingsTypes for BT where
716    BT: InstantBindingsTypes + TimerBindingsTypes + ReferenceNotifiers + 'static
717{
718}
719
720/// IPv4 and IPv6 state combined.
721pub struct DualStackIpDeviceState<BT: IpDeviceStateBindingsTypes> {
722    /// IPv4 state.
723    ipv4: Ipv4DeviceState<BT>,
724
725    /// IPv6 state.
726    ipv6: Ipv6DeviceState<BT>,
727
728    /// The device's routing metric.
729    metric: RawMetric,
730}
731
732impl<BC: IpDeviceStateBindingsTypes + TimerContext> DualStackIpDeviceState<BC> {
733    /// Constructs a new `DualStackIpDeviceState` for `device_id`.
734    ///
735    /// `metric` is the default metric used by routes through this device.
736    pub fn new<
737        D: WeakDeviceIdentifier,
738        CC: CoreTimerContext<IpDeviceTimerId<Ipv6, D, BC>, BC>
739            + CoreTimerContext<IpDeviceTimerId<Ipv4, D, BC>, BC>,
740    >(
741        bindings_ctx: &mut BC,
742        device_id: D,
743        metric: RawMetric,
744    ) -> Self {
745        let ipv4 = Ipv4DeviceState::new::<
746            D,
747            NestedIntoCoreTimerCtx<CC, IpDeviceTimerId<Ipv4, D, BC>>,
748        >(bindings_ctx, device_id.clone());
749        let ipv6 = Ipv6DeviceState::new::<
750            D,
751            NestedIntoCoreTimerCtx<CC, IpDeviceTimerId<Ipv6, D, BC>>,
752        >(bindings_ctx, device_id);
753        Self { ipv4, ipv6, metric }
754    }
755}
756
757impl<BT: IpDeviceStateBindingsTypes> DualStackIpDeviceState<BT> {
758    /// Returns the [`RawMetric`] for this device.
759    pub fn metric(&self) -> &RawMetric {
760        &self.metric
761    }
762
763    /// Returns the IP device state for version `I`.
764    pub fn ip_state<I: IpDeviceStateIpExt>(&self) -> &IpDeviceState<I, BT> {
765        I::map_ip_out(
766            self,
767            |dual_stack| &dual_stack.ipv4.ip_state,
768            |dual_stack| &dual_stack.ipv6.ip_state,
769        )
770    }
771
772    /// Access the IGMP counters associated with this specific device state.
773    pub fn igmp_counters(&self) -> &IgmpCounters {
774        self.ipv4.igmp_counters()
775    }
776
777    /// Access the MLD counters associated with this specific device state.
778    pub fn mld_counters(&self) -> &MldCounters {
779        self.ipv6.mld_counters()
780    }
781
782    /// Access the IP counters associated with this specific device state.
783    pub fn ip_counters<I: IpDeviceStateIpExt>(&self) -> &IpCounters<I> {
784        &self.ip_state::<I>().ip_counters
785    }
786}
787
788/// Configuration for a temporary IPv6 address assigned via SLAAC.
789#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
790pub struct TemporarySlaacConfig<Instant> {
791    /// The time at which the address is no longer valid.
792    pub valid_until: Instant,
793    /// The per-address DESYNC_FACTOR specified in RFC 8981 Section 3.4.
794    pub desync_factor: Duration,
795    /// The time at which the address was created.
796    pub creation_time: Instant,
797    /// The DAD_Counter parameter specified by RFC 8981 Section 3.3.2.1. This is
798    /// used to track the number of retries that occurred prior to picking this
799    /// address.
800    pub dad_counter: u8,
801}
802
803/// A lifetime that may be forever/infinite.
804#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
805pub enum Lifetime<I> {
806    /// A finite lifetime.
807    Finite(I),
808    /// An infinite lifetime.
809    Infinite,
810}
811
812impl<I: Instant> InspectableValue for Lifetime<I> {
813    fn record<N: Inspector>(&self, name: &str, inspector: &mut N) {
814        match self {
815            Self::Finite(instant) => inspector.record_inspectable_value(name, instant),
816            Self::Infinite => inspector.record_str(name, "infinite"),
817        }
818    }
819}
820
821impl<I: Instant> Lifetime<I> {
822    /// Creates a new `Lifetime` `duration` from `now`, saturating to
823    /// `Infinite`.
824    pub fn from_ndp(now: I, duration: NonZeroNdpLifetime) -> Self {
825        match duration {
826            NonZeroNdpLifetime::Finite(d) => Self::after(now, d.get()),
827            NonZeroNdpLifetime::Infinite => Self::Infinite,
828        }
829    }
830
831    /// Creates a new `Lifetime` `duration` from `now`, saturating to
832    /// `Infinite`.
833    pub fn after(now: I, duration: Duration) -> Self {
834        match now.checked_add(duration) {
835            Some(i) => Self::Finite(i),
836            None => Self::Infinite,
837        }
838    }
839}
840
841impl<Instant> Lifetime<Instant> {
842    /// Maps the instant value in this `Lifetime` with `f`.
843    pub fn map_instant<N, F: FnOnce(Instant) -> N>(self, f: F) -> Lifetime<N> {
844        match self {
845            Self::Infinite => Lifetime::Infinite,
846            Self::Finite(i) => Lifetime::Finite(f(i)),
847        }
848    }
849}
850
851/// An address' preferred lifetime information.
852#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
853pub enum PreferredLifetime<Instant> {
854    /// Address is preferred. It can be used for new connections.
855    ///
856    /// `Lifetime` indicates until when the address is expected to remain in
857    /// preferred state.
858    Preferred(Lifetime<Instant>),
859    /// Address is deprecated, it should not be used for new connections.
860    Deprecated,
861}
862
863impl<Instant> PreferredLifetime<Instant> {
864    /// Creates a new `PreferredLifetime` that is preferred until `instant`.
865    pub const fn preferred_until(instant: Instant) -> Self {
866        Self::Preferred(Lifetime::Finite(instant))
867    }
868
869    /// Creates a new `PreferredLifetime` that is preferred with an infinite
870    /// lifetime.
871    pub const fn preferred_forever() -> Self {
872        Self::Preferred(Lifetime::Infinite)
873    }
874
875    /// Returns true if the address is deprecated.
876    pub const fn is_deprecated(&self) -> bool {
877        match self {
878            Self::Preferred(_) => false,
879            Self::Deprecated => true,
880        }
881    }
882
883    /// Maps the instant value in this `PreferredLifetime` with `f`.
884    pub fn map_instant<N, F: FnOnce(Instant) -> N>(self, f: F) -> PreferredLifetime<N> {
885        match self {
886            Self::Deprecated => PreferredLifetime::Deprecated,
887            Self::Preferred(l) => PreferredLifetime::Preferred(l.map_instant(f)),
888        }
889    }
890}
891
892impl<I: Instant> PreferredLifetime<I> {
893    /// Creates a new `PreferredLifetime` that is preferred for `duration` after
894    /// `now`.
895    pub fn preferred_for(now: I, duration: NonZeroNdpLifetime) -> Self {
896        Self::Preferred(Lifetime::from_ndp(now, duration))
897    }
898
899    /// Creates a new `PreferredLifetime` that is preferred for `duration` after
900    /// `now` if it is `Some`, `deprecated` otherwise.
901    pub fn maybe_preferred_for(now: I, duration: Option<NonZeroNdpLifetime>) -> Self {
902        match duration {
903            Some(d) => Self::preferred_for(now, d),
904            None => Self::Deprecated,
905        }
906    }
907}
908
909impl<Instant> Default for PreferredLifetime<Instant> {
910    fn default() -> Self {
911        Self::Preferred(Lifetime::Infinite)
912    }
913}
914
915impl<I: Instant> InspectableValue for PreferredLifetime<I> {
916    fn record<N: Inspector>(&self, name: &str, inspector: &mut N) {
917        match self {
918            Self::Deprecated => inspector.record_str(name, "deprecated"),
919            Self::Preferred(lifetime) => inspector.record_inspectable_value(name, lifetime),
920        }
921    }
922}
923
924/// Address properties common to IPv4 and IPv6.
925///
926/// These properties may change throughout the lifetime of the address.
927#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
928pub struct CommonAddressProperties<Instant> {
929    /// The lifetime for which the address is valid.
930    pub valid_until: Lifetime<Instant>,
931    /// The address' preferred lifetime.
932    ///
933    /// Address preferred status is used in IPv6 source address selection. IPv4
934    /// addresses simply keep this information on behalf of bindings.
935    ///
936    /// Note that core does not deprecate manual addresses automatically. Manual
937    /// addresses are fully handled outside of core.
938    pub preferred_lifetime: PreferredLifetime<Instant>,
939}
940
941impl<I> Default for CommonAddressProperties<I> {
942    fn default() -> Self {
943        Self {
944            valid_until: Lifetime::Infinite,
945            preferred_lifetime: PreferredLifetime::preferred_forever(),
946        }
947    }
948}
949
950impl<Inst: Instant> Inspectable for CommonAddressProperties<Inst> {
951    fn record<I: Inspector>(&self, inspector: &mut I) {
952        let Self { valid_until, preferred_lifetime } = self;
953        inspector.record_inspectable_value("ValidUntil", valid_until);
954        inspector.record_inspectable_value("PreferredLifetime", preferred_lifetime);
955    }
956}
957
958/// Address configuration common to IPv4 and IPv6.
959///
960/// Unlike [`CommonAddressProperties`], the fields here are not expected to
961/// change throughout the lifetime of the address.
962#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
963pub struct CommonAddressConfig {
964    /// Whether the address was installed with a specific preference towards
965    /// performing Duplicate Address Detection (DAD).
966    pub should_perform_dad: Option<bool>,
967}
968
969impl Inspectable for CommonAddressConfig {
970    fn record<I: Inspector>(&self, inspector: &mut I) {
971        let Self { should_perform_dad } = self;
972        if let Some(should_perform_dad) = should_perform_dad {
973            inspector.record_bool("DadOverride", *should_perform_dad);
974        }
975    }
976}
977
978/// The configuration for an IPv4 address.
979#[derive(Clone, Copy, Debug, Derivative, PartialEq, Eq, Hash)]
980#[derivative(Default(bound = ""))]
981pub struct Ipv4AddrConfig<Instant> {
982    /// IP version agnostic config.
983    pub config: CommonAddressConfig,
984    /// IP version agnostic properties.
985    pub properties: CommonAddressProperties<Instant>,
986}
987
988impl<Inst: Instant> Inspectable for Ipv4AddrConfig<Inst> {
989    fn record<I: Inspector>(&self, inspector: &mut I) {
990        let Self { config, properties } = self;
991        inspector.delegate_inspectable(config);
992        inspector.delegate_inspectable(properties);
993    }
994}
995
996/// Configuration for an IPv6 address assigned via SLAAC that varies based on
997/// whether the address is stable or temporary.
998#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
999pub enum SlaacConfig<Instant> {
1000    /// The address is a stable address.
1001    Stable {
1002        /// The lifetime of the address.
1003        valid_until: Lifetime<Instant>,
1004        /// The time at which the address was created.
1005        creation_time: Instant,
1006        /// The number of times the address has been regenerated to avoid either an
1007        /// IANA-reserved IID or an address already assigned to the same interface.
1008        regen_counter: u8,
1009        /// The number of times the address has been regenerated due to DAD failure.
1010        dad_counter: u8,
1011    },
1012    /// The address is a temporary address, as specified by [RFC 8981].
1013    ///
1014    /// [RFC 8981]: https://tools.ietf.org/html/rfc8981
1015    Temporary(TemporarySlaacConfig<Instant>),
1016}
1017
1018impl<Instant: Copy> SlaacConfig<Instant> {
1019    /// The lifetime for which the address is valid.
1020    pub fn valid_until(&self) -> Lifetime<Instant> {
1021        match self {
1022            SlaacConfig::Stable { valid_until, .. } => *valid_until,
1023            SlaacConfig::Temporary(TemporarySlaacConfig { valid_until, .. }) => {
1024                Lifetime::Finite(*valid_until)
1025            }
1026        }
1027    }
1028}
1029
1030/// The configuration for an IPv6 address.
1031#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1032pub enum Ipv6AddrConfig<Instant> {
1033    /// Configured by stateless address autoconfiguration.
1034    Slaac(Ipv6AddrSlaacConfig<Instant>),
1035
1036    /// Manually configured.
1037    Manual(Ipv6AddrManualConfig<Instant>),
1038}
1039
1040impl<Instant> Default for Ipv6AddrConfig<Instant> {
1041    fn default() -> Self {
1042        Self::Manual(Default::default())
1043    }
1044}
1045
1046impl<Inst: Instant> Inspectable for Ipv6AddrConfig<Inst> {
1047    fn record<I: Inspector>(&self, inspector: &mut I) {
1048        match self {
1049            // NB: Ignored fields are recorded after the match.
1050            Ipv6AddrConfig::Manual(Ipv6AddrManualConfig {
1051                config,
1052                properties: _,
1053                temporary: _,
1054            }) => {
1055                inspector.delegate_inspectable(config);
1056            }
1057            Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig { inner, preferred_lifetime: _ }) => {
1058                match inner {
1059                    SlaacConfig::Stable {
1060                        valid_until: _,
1061                        creation_time,
1062                        regen_counter,
1063                        dad_counter,
1064                    } => {
1065                        inspector.record_inspectable_value("CreationTime", creation_time);
1066                        inspector.record_uint("RegenCounter", *regen_counter);
1067                        inspector.record_uint("DadCounter", *dad_counter);
1068                    }
1069                    SlaacConfig::Temporary(TemporarySlaacConfig {
1070                        valid_until: _,
1071                        desync_factor,
1072                        creation_time,
1073                        dad_counter,
1074                    }) => {
1075                        inspector.record_double("DesyncFactorSecs", desync_factor.as_secs_f64());
1076                        inspector.record_uint("DadCounter", *dad_counter);
1077                        inspector.record_inspectable_value("CreationTime", creation_time);
1078                    }
1079                }
1080            }
1081        };
1082        inspector.record_bool("IsSlaac", self.is_slaac());
1083        inspector.record_inspectable_value("ValidUntil", &self.valid_until());
1084        inspector.record_inspectable_value("PreferredLifetime", &self.preferred_lifetime());
1085        inspector.record_bool("Temporary", self.is_temporary());
1086    }
1087}
1088
1089/// The common configuration for a SLAAC-assigned IPv6 address.
1090#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1091pub struct Ipv6AddrSlaacConfig<Instant> {
1092    /// The inner slaac config, tracking stable and temporary behavior.
1093    pub inner: SlaacConfig<Instant>,
1094    /// The address' preferred lifetime.
1095    pub preferred_lifetime: PreferredLifetime<Instant>,
1096}
1097
1098/// The configuration for a manually-assigned IPv6 address.
1099#[derive(Clone, Copy, Debug, Derivative, PartialEq, Eq, Hash)]
1100#[derivative(Default(bound = ""))]
1101pub struct Ipv6AddrManualConfig<Instant> {
1102    /// IP version agnostic config.
1103    pub config: CommonAddressConfig,
1104    /// IP version agnostic properties.
1105    pub properties: CommonAddressProperties<Instant>,
1106    /// True if the address is temporary.
1107    ///
1108    /// Used in source address selection.
1109    pub temporary: bool,
1110}
1111
1112impl<Instant> From<Ipv6AddrManualConfig<Instant>> for Ipv6AddrConfig<Instant> {
1113    fn from(value: Ipv6AddrManualConfig<Instant>) -> Self {
1114        Self::Manual(value)
1115    }
1116}
1117
1118impl<Instant: Copy + PartialEq> Ipv6AddrConfig<Instant> {
1119    /// The lifetime for which the address is valid.
1120    pub fn valid_until(&self) -> Lifetime<Instant> {
1121        match self {
1122            Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig { inner, .. }) => inner.valid_until(),
1123            Ipv6AddrConfig::Manual(Ipv6AddrManualConfig { properties, .. }) => {
1124                properties.valid_until
1125            }
1126        }
1127    }
1128
1129    /// Returns the preferred lifetime for this address.
1130    pub fn preferred_lifetime(&self) -> PreferredLifetime<Instant> {
1131        match self {
1132            Ipv6AddrConfig::Manual(Ipv6AddrManualConfig { properties, .. }) => {
1133                properties.preferred_lifetime
1134            }
1135            Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig { preferred_lifetime, .. }) => {
1136                *preferred_lifetime
1137            }
1138        }
1139    }
1140
1141    /// Returns true if the address is deprecated.
1142    pub fn is_deprecated(&self) -> bool {
1143        self.preferred_lifetime().is_deprecated()
1144    }
1145
1146    /// Returns true if the address is temporary.
1147    pub fn is_temporary(&self) -> bool {
1148        match self {
1149            Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig { inner, .. }) => match inner {
1150                SlaacConfig::Stable { .. } => false,
1151                SlaacConfig::Temporary(_) => true,
1152            },
1153            Ipv6AddrConfig::Manual(Ipv6AddrManualConfig { temporary, .. }) => *temporary,
1154        }
1155    }
1156
1157    /// Returns true if the address was configured via SLAAC.
1158    pub fn is_slaac(&self) -> bool {
1159        match self {
1160            Ipv6AddrConfig::Slaac(_) => true,
1161            Ipv6AddrConfig::Manual(_) => false,
1162        }
1163    }
1164}
1165
1166/// Flags associated with an IP device address.
1167#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1168pub struct IpAddressFlags {
1169    /// True if the address is assigned.
1170    pub assigned: bool,
1171}
1172
1173/// The state associated with an IP device address.
1174#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1175pub struct IpAddressData<I: IpDeviceStateIpExt, Inst: Instant> {
1176    /// The address' flags.
1177    pub flags: IpAddressFlags,
1178    /// The address' configuration. `None` when the address is being removed.
1179    pub config: Option<I::AddressConfig<Inst>>,
1180}
1181
1182impl<I: IpDeviceStateIpExt, Inst: Instant> IpAddressData<I, Inst> {
1183    /// Return whether DAD should be performed for this address.
1184    pub fn should_perform_dad(&self) -> bool {
1185        let Some(config) = self.config.as_ref() else {
1186            // When config is `None` the address is being removed. Insertion
1187            // must be racing with removal; don't perform DAD.
1188            return false;
1189        };
1190
1191        #[derive(GenericOverIp)]
1192        #[generic_over_ip(I, Ip)]
1193        struct Wrap<'a, I: IpDeviceStateIpExt, Inst: Instant>(&'a I::AddressConfig<Inst>);
1194        let should_perform_dad = I::map_ip_in(
1195            Wrap(config),
1196            |Wrap(Ipv4AddrConfig { config, properties: _ })| config.should_perform_dad,
1197            |Wrap(config)| match config {
1198                Ipv6AddrConfig::Slaac(_) => None,
1199                Ipv6AddrConfig::Manual(c) => c.config.should_perform_dad,
1200            },
1201        );
1202
1203        should_perform_dad.unwrap_or(I::DEFAULT_DAD_ENABLED)
1204    }
1205}
1206
1207impl<I: IpDeviceStateIpExt, Inst: Instant> Inspectable for IpAddressData<I, Inst> {
1208    fn record<II: Inspector>(&self, inspector: &mut II) {
1209        let Self { flags: IpAddressFlags { assigned }, config } = self;
1210        inspector.record_bool("Assigned", *assigned);
1211        if let Some(config) = config {
1212            inspector.delegate_inspectable(config);
1213        }
1214    }
1215}
1216
1217/// Data associated with an IPv6 address on an interface.
1218// TODO(https://fxbug.dev/42173351): Should this be generalized for loopback?
1219#[derive(Derivative)]
1220#[derivative(Debug(bound = ""))]
1221pub struct IpAddressEntry<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> {
1222    addr_sub: AddrSubnet<I::Addr, I::AssignedWitness>,
1223    dad_state: Mutex<DadState<I, BT>>,
1224    state: RwLock<IpAddressData<I, BT::Instant>>,
1225}
1226
1227impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> IpAddressEntry<I, BT> {
1228    /// Constructs a new `IpAddressEntry`.
1229    pub fn new(
1230        addr_sub: AddrSubnet<I::Addr, I::AssignedWitness>,
1231        dad_state: DadState<I, BT>,
1232        config: I::AddressConfig<BT::Instant>,
1233    ) -> Self {
1234        let assigned = match dad_state {
1235            DadState::Assigned => true,
1236            DadState::Tentative { .. } | DadState::Uninitialized => false,
1237        };
1238
1239        Self {
1240            addr_sub,
1241            dad_state: Mutex::new(dad_state),
1242            state: RwLock::new(IpAddressData {
1243                config: Some(config),
1244                flags: IpAddressFlags { assigned },
1245            }),
1246        }
1247    }
1248
1249    /// This entry's address and subnet.
1250    pub fn addr_sub(&self) -> &AddrSubnet<I::Addr, I::AssignedWitness> {
1251        &self.addr_sub
1252    }
1253}
1254
1255impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> OrderedLockAccess<DadState<I, BT>>
1256    for IpAddressEntry<I, BT>
1257{
1258    type Lock = Mutex<DadState<I, BT>>;
1259    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
1260        OrderedLockRef::new(&self.dad_state)
1261    }
1262}
1263
1264impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>
1265    OrderedLockAccess<IpAddressData<I, BT::Instant>> for IpAddressEntry<I, BT>
1266{
1267    type Lock = RwLock<IpAddressData<I, BT::Instant>>;
1268    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
1269        OrderedLockRef::new(&self.state)
1270    }
1271}
1272
1273#[cfg(test)]
1274mod tests {
1275    use super::*;
1276
1277    use net_types::ip::{Ipv4Addr, Ipv6Addr};
1278    use netstack3_base::testutil::{FakeBindingsCtx, FakeInstant};
1279    use netstack3_base::InstantContext as _;
1280    use test_case::test_case;
1281
1282    type FakeBindingsCtxImpl = FakeBindingsCtx<(), (), (), ()>;
1283
1284    #[test_case(Lifetime::Infinite ; "with infinite valid_until")]
1285    #[test_case(Lifetime::Finite(FakeInstant::from(Duration::from_secs(1))); "with finite valid_until")]
1286    fn test_add_addr_ipv4(valid_until: Lifetime<FakeInstant>) {
1287        const ADDRESS: Ipv4Addr = Ipv4Addr::new([1, 2, 3, 4]);
1288        const PREFIX_LEN: u8 = 8;
1289
1290        let mut ipv4 = IpDeviceAddresses::<Ipv4, FakeBindingsCtxImpl>::default();
1291        let config = Ipv4AddrConfig {
1292            properties: CommonAddressProperties { valid_until, ..Default::default() },
1293            ..Default::default()
1294        };
1295
1296        let _: AddressId<_, _> = ipv4
1297            .add(IpAddressEntry::new(
1298                AddrSubnet::new(ADDRESS, PREFIX_LEN).unwrap(),
1299                DadState::Uninitialized,
1300                config,
1301            ))
1302            .unwrap();
1303        // Adding the same address with different prefix should fail.
1304        assert_eq!(
1305            ipv4.add(IpAddressEntry::new(
1306                AddrSubnet::new(ADDRESS, PREFIX_LEN + 1).unwrap(),
1307                DadState::Uninitialized,
1308                config,
1309            ))
1310            .unwrap_err(),
1311            ExistsError
1312        );
1313    }
1314
1315    #[test_case(Lifetime::Infinite ; "with infinite valid_until")]
1316    #[test_case(Lifetime::Finite(FakeInstant::from(Duration::from_secs(1))); "with finite valid_until")]
1317    fn test_add_addr_ipv6(valid_until: Lifetime<FakeInstant>) {
1318        const ADDRESS: Ipv6Addr =
1319            Ipv6Addr::from_bytes([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]);
1320        const PREFIX_LEN: u8 = 8;
1321
1322        let mut ipv6 = IpDeviceAddresses::<Ipv6, FakeBindingsCtxImpl>::default();
1323
1324        let mut bindings_ctx = FakeBindingsCtxImpl::default();
1325
1326        let _: AddressId<_, _> = ipv6
1327            .add(IpAddressEntry::new(
1328                AddrSubnet::new(ADDRESS, PREFIX_LEN).unwrap(),
1329                DadState::Tentative {
1330                    dad_transmits_remaining: None,
1331                    timer: bindings_ctx.new_timer(()),
1332                    ip_specific_state: Default::default(),
1333                },
1334                Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig {
1335                    inner: SlaacConfig::Stable {
1336                        valid_until,
1337                        creation_time: bindings_ctx.now(),
1338                        regen_counter: 0,
1339                        dad_counter: 0,
1340                    },
1341                    preferred_lifetime: PreferredLifetime::Preferred(Lifetime::Infinite),
1342                }),
1343            ))
1344            .unwrap();
1345        // Adding the same address with different prefix and configuration
1346        // should fail.
1347        assert_eq!(
1348            ipv6.add(IpAddressEntry::new(
1349                AddrSubnet::new(ADDRESS, PREFIX_LEN + 1).unwrap(),
1350                DadState::Assigned,
1351                Ipv6AddrConfig::Manual(Ipv6AddrManualConfig {
1352                    properties: CommonAddressProperties { valid_until, ..Default::default() },
1353                    ..Default::default()
1354                }),
1355            ))
1356            .unwrap_err(),
1357            ExistsError,
1358        );
1359    }
1360
1361    #[test_case(None => false; "default")]
1362    #[test_case(Some(false) => false; "disabled")]
1363    #[test_case(Some(true) => true; "enabled")]
1364    fn should_perform_dad_ipv4(setting: Option<bool>) -> bool {
1365        let state = IpAddressData::<Ipv4, FakeInstant> {
1366            flags: IpAddressFlags { assigned: false },
1367            config: Some(Ipv4AddrConfig::<FakeInstant> {
1368                config: CommonAddressConfig { should_perform_dad: setting },
1369                ..Default::default()
1370            }),
1371        };
1372        state.should_perform_dad()
1373    }
1374
1375    #[test_case(None => true; "default")]
1376    #[test_case(Some(false) => false; "disabled")]
1377    #[test_case(Some(true) => true; "enabled")]
1378    fn should_perform_dad_ipv6(setting: Option<bool>) -> bool {
1379        let state = IpAddressData::<Ipv6, FakeInstant> {
1380            flags: IpAddressFlags { assigned: false },
1381            config: Some(Ipv6AddrConfig::Manual(Ipv6AddrManualConfig::<FakeInstant> {
1382                config: CommonAddressConfig { should_perform_dad: setting },
1383                ..Default::default()
1384            })),
1385        };
1386        state.should_perform_dad()
1387    }
1388}