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, 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::{
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>>,
272
273    /// Multicast groups and GMP handling state.
274    multicast_groups: RwLock<IpDeviceMulticastGroups<I, BT>>,
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>>,
279
280    /// The flags for this device.
281    flags: Mutex<IpMarked<I, IpDeviceFlags>>,
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>>;
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>>;
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>>;
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>>;
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>>;
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>> {
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>,
425    igmp_counters: IgmpCounters,
426}
427
428impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv4DeviceConfiguration>
429    for DualStackIpDeviceState<BT>
430{
431    type Lock = RwLock<Ipv4DeviceConfiguration>;
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>;
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 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>,
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 = dad_state.is_assigned();
1235        Self {
1236            addr_sub,
1237            dad_state: Mutex::new(dad_state),
1238            state: RwLock::new(IpAddressData {
1239                config: Some(config),
1240                flags: IpAddressFlags { assigned },
1241            }),
1242        }
1243    }
1244
1245    /// This entry's address and subnet.
1246    pub fn addr_sub(&self) -> &AddrSubnet<I::Addr, I::AssignedWitness> {
1247        &self.addr_sub
1248    }
1249}
1250
1251impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> OrderedLockAccess<DadState<I, BT>>
1252    for IpAddressEntry<I, BT>
1253{
1254    type Lock = Mutex<DadState<I, BT>>;
1255    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
1256        OrderedLockRef::new(&self.dad_state)
1257    }
1258}
1259
1260impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>
1261    OrderedLockAccess<IpAddressData<I, BT::Instant>> for IpAddressEntry<I, BT>
1262{
1263    type Lock = RwLock<IpAddressData<I, BT::Instant>>;
1264    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
1265        OrderedLockRef::new(&self.state)
1266    }
1267}
1268
1269#[cfg(test)]
1270mod tests {
1271    use super::*;
1272
1273    use net_types::ip::{Ipv4Addr, Ipv6Addr};
1274    use netstack3_base::InstantContext as _;
1275    use netstack3_base::testutil::{FakeBindingsCtx, FakeInstant};
1276    use test_case::test_case;
1277
1278    type FakeBindingsCtxImpl = FakeBindingsCtx<(), (), (), ()>;
1279
1280    #[test_case(Lifetime::Infinite ; "with infinite valid_until")]
1281    #[test_case(Lifetime::Finite(FakeInstant::from(Duration::from_secs(1))); "with finite valid_until")]
1282    fn test_add_addr_ipv4(valid_until: Lifetime<FakeInstant>) {
1283        const ADDRESS: Ipv4Addr = Ipv4Addr::new([1, 2, 3, 4]);
1284        const PREFIX_LEN: u8 = 8;
1285
1286        let mut ipv4 = IpDeviceAddresses::<Ipv4, FakeBindingsCtxImpl>::default();
1287        let config = Ipv4AddrConfig {
1288            properties: CommonAddressProperties { valid_until, ..Default::default() },
1289            ..Default::default()
1290        };
1291
1292        let _: AddressId<_, _> = ipv4
1293            .add(IpAddressEntry::new(
1294                AddrSubnet::new(ADDRESS, PREFIX_LEN).unwrap(),
1295                DadState::Uninitialized,
1296                config,
1297            ))
1298            .unwrap();
1299        // Adding the same address with different prefix should fail.
1300        assert_eq!(
1301            ipv4.add(IpAddressEntry::new(
1302                AddrSubnet::new(ADDRESS, PREFIX_LEN + 1).unwrap(),
1303                DadState::Uninitialized,
1304                config,
1305            ))
1306            .unwrap_err(),
1307            ExistsError
1308        );
1309    }
1310
1311    #[test_case(Lifetime::Infinite ; "with infinite valid_until")]
1312    #[test_case(Lifetime::Finite(FakeInstant::from(Duration::from_secs(1))); "with finite valid_until")]
1313    fn test_add_addr_ipv6(valid_until: Lifetime<FakeInstant>) {
1314        const ADDRESS: Ipv6Addr =
1315            Ipv6Addr::from_bytes([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]);
1316        const PREFIX_LEN: u8 = 8;
1317
1318        let mut ipv6 = IpDeviceAddresses::<Ipv6, FakeBindingsCtxImpl>::default();
1319
1320        let mut bindings_ctx = FakeBindingsCtxImpl::default();
1321
1322        let _: AddressId<_, _> = ipv6
1323            .add(IpAddressEntry::new(
1324                AddrSubnet::new(ADDRESS, PREFIX_LEN).unwrap(),
1325                DadState::Tentative {
1326                    dad_transmits_remaining: None,
1327                    timer: bindings_ctx.new_timer(()),
1328                    ip_specific_state: Default::default(),
1329                    probe_wait: None,
1330                },
1331                Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig {
1332                    inner: SlaacConfig::Stable {
1333                        valid_until,
1334                        creation_time: bindings_ctx.now(),
1335                        regen_counter: 0,
1336                        dad_counter: 0,
1337                    },
1338                    preferred_lifetime: PreferredLifetime::Preferred(Lifetime::Infinite),
1339                }),
1340            ))
1341            .unwrap();
1342        // Adding the same address with different prefix and configuration
1343        // should fail.
1344        assert_eq!(
1345            ipv6.add(IpAddressEntry::new(
1346                AddrSubnet::new(ADDRESS, PREFIX_LEN + 1).unwrap(),
1347                DadState::Assigned { ran_dad: false },
1348                Ipv6AddrConfig::Manual(Ipv6AddrManualConfig {
1349                    properties: CommonAddressProperties { valid_until, ..Default::default() },
1350                    ..Default::default()
1351                }),
1352            ))
1353            .unwrap_err(),
1354            ExistsError,
1355        );
1356    }
1357
1358    #[test_case(None => false; "default")]
1359    #[test_case(Some(false) => false; "disabled")]
1360    #[test_case(Some(true) => true; "enabled")]
1361    fn should_perform_dad_ipv4(setting: Option<bool>) -> bool {
1362        let state = IpAddressData::<Ipv4, FakeInstant> {
1363            flags: IpAddressFlags { assigned: false },
1364            config: Some(Ipv4AddrConfig::<FakeInstant> {
1365                config: CommonAddressConfig { should_perform_dad: setting },
1366                ..Default::default()
1367            }),
1368        };
1369        state.should_perform_dad()
1370    }
1371
1372    #[test_case(None => true; "default")]
1373    #[test_case(Some(false) => false; "disabled")]
1374    #[test_case(Some(true) => true; "enabled")]
1375    fn should_perform_dad_ipv6(setting: Option<bool>) -> bool {
1376        let state = IpAddressData::<Ipv6, FakeInstant> {
1377            flags: IpAddressFlags { assigned: false },
1378            config: Some(Ipv6AddrConfig::Manual(Ipv6AddrManualConfig::<FakeInstant> {
1379                config: CommonAddressConfig { should_perform_dad: setting },
1380                ..Default::default()
1381            })),
1382        };
1383        state.should_perform_dad()
1384    }
1385}