Skip to main content

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