netstack3_ip/device/
dad.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//! Duplicate Address Detection.
6
7use core::convert::Infallible as Never;
8use core::fmt::Debug;
9use core::mem;
10use core::num::{NonZero, NonZeroU16};
11use core::ops::RangeInclusive;
12use core::time::Duration;
13
14use arrayvec::ArrayVec;
15use assert_matches::assert_matches;
16use derivative::Derivative;
17use log::debug;
18use net_types::MulticastAddr;
19use net_types::ip::{
20    GenericOverIp, Ip, IpVersion, IpVersionMarker, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr,
21};
22use netstack3_base::{
23    AnyDevice, CoreTimerContext, DeviceIdContext, EventContext, HandleableTimer,
24    InstantBindingsTypes, IpAddressId as _, IpDeviceAddressIdContext, RngContext,
25    StrongDeviceIdentifier as _, TimerBindingsTypes, TimerContext, WeakDeviceIdentifier,
26};
27use packet_formats::icmp::ndp::NeighborSolicitation;
28use packet_formats::icmp::ndp::options::{MIN_NONCE_LENGTH, NdpNonce};
29use packet_formats::utils::NonZeroDuration;
30use rand::Rng;
31
32use crate::internal::device::nud::DEFAULT_MAX_MULTICAST_SOLICIT;
33use crate::internal::device::{IpAddressState, IpDeviceEvent, IpDeviceIpExt, WeakIpAddressId};
34
35/// An IP Extension trait supporting Duplicate Address Detection.
36pub trait DadIpExt: Ip {
37    /// Whether or not DAD should be performed by default of a newly installed
38    /// address.
39    const DEFAULT_DAD_ENABLED: bool;
40
41    /// Data that accompanies a sent DAD probe/announcement.
42    type SendData: Debug;
43    /// Data that accompanies a received DAD packet.
44    type ReceivedPacketData<'a>;
45    /// State held for tentative addresses.
46    type TentativeState: Debug + Default + Send + Sync;
47    /// State held for announcing addresses.
48    type AnnouncingState: Debug + Send + Sync;
49    /// Metadata associated with the result of handling and incoming DAD packet.
50    type IncomingPacketResultMeta;
51    /// Data used to determine the interval to wait between DAD probes.
52    type RetransmitTimerData;
53
54    /// Generate data to accompany a sent probe from the tentative state.
55    fn generate_sent_probe_data<BC: RngContext>(
56        state: &mut Self::TentativeState,
57        addr: Self::Addr,
58        bindings_ctx: &mut BC,
59    ) -> Self::SendData;
60
61    /// Generate the delay to wait before the next DAD probe.
62    fn get_retransmission_interval<BC: RngContext>(
63        data: &Self::RetransmitTimerData,
64        bindings_ctx: &mut BC,
65        probes_remaining: &Option<NonZeroU16>,
66    ) -> NonZeroDuration;
67
68    /// Handles an incoming DAD packet while in the tentative state.
69    fn handle_incoming_packet_while_tentative<'a>(
70        data: Self::ReceivedPacketData<'a>,
71        state: &mut Self::TentativeState,
72        dad_transmits_remaining: &mut Option<NonZeroU16>,
73    ) -> Self::IncomingPacketResultMeta;
74
75    /// Handles an incoming DAD packet while in the assigned or announcing
76    /// state.
77    ///
78    /// `ran_dad`: indicates whether this address is assigned because DAD
79    /// completed successfully, or because DAD was disabled and skipped.
80    ///
81    /// Returns whether the address should be removed as a result of this
82    /// packet.
83    fn handle_incoming_packet_while_assigned<'a>(
84        data: Self::ReceivedPacketData<'a>,
85        ran_dad: bool,
86    ) -> bool;
87}
88
89impl DadIpExt for Ipv4 {
90    /// In the context of IPv4 addresses, DAD refers to Address Conflict Detection
91    /// (ACD) as specified in RFC 5227.
92    ///
93    /// This value is set to false, which is out of compliance with RFC 5227. As per
94    /// section 2.1:
95    ///   Before beginning to use an IPv4 address (whether received from manual
96    ///   configuration, DHCP, or some other means), a host implementing this
97    ///   specification MUST test to see if the address is already in use.
98    ///
99    /// However, we believe that disabling DAD for IPv4 addresses by default is more
100    /// inline with industry expectations. For example, Linux does not implement
101    /// DAD for IPv4 addresses at all: applications that want to prevent duplicate
102    /// IPv4 addresses must implement the ACD specification themselves (e.g.
103    /// dhclient, a common DHCP client on Linux).
104    const DEFAULT_DAD_ENABLED: bool = false;
105
106    type SendData = Ipv4DadSendData;
107    type ReceivedPacketData<'a> = Ipv4DadAddressInfo;
108    type TentativeState = ();
109    type AnnouncingState = Ipv4AnnouncingDadState;
110    type IncomingPacketResultMeta = ();
111    type RetransmitTimerData = ();
112
113    fn generate_sent_probe_data<BC: RngContext>(
114        _state: &mut (),
115        addr: Ipv4Addr,
116        _bindings_ctx: &mut BC,
117    ) -> Self::SendData {
118        Ipv4DadSendData { target_ip: addr, probe_type: Ipv4SentProbeType::Probe }
119    }
120
121    fn get_retransmission_interval<BC: RngContext>(
122        _data: &(),
123        bindings_ctx: &mut BC,
124        probes_remaining: &Option<NonZeroU16>,
125    ) -> NonZeroDuration {
126        match probes_remaining {
127            // If we have more probes to send, use the delay *between* probes.
128            Some(_) => ipv4_dad_probe_interval(bindings_ctx),
129            // Otherwise, use the delay before the announcing period.
130            None => IPV4_ANNOUNCE_WAIT,
131        }
132    }
133
134    fn handle_incoming_packet_while_tentative<'a>(
135        _data: Ipv4DadAddressInfo,
136        _state: &mut (),
137        _dad_transmits_remaining: &mut Option<NonZeroU16>,
138    ) -> () {
139        // TODO(https://fxbug.dev/416523141): Once we support ratelimiting, this
140        // would be an appropriate place to acknowledge that a conflict has
141        // occurred.
142    }
143
144    fn handle_incoming_packet_while_assigned<'a>(data: Ipv4DadAddressInfo, ran_dad: bool) -> bool {
145        match IPV4_ADDRESS_DEFENSE_STRATEGY {
146            // Forfeit the address, only if 1) DAD was enabled on the address
147            // and 2) the conflict was for the source address of the ARP packet
148            AddressDefenseStrategy::ForfeitAddress => {
149                ran_dad && data == Ipv4DadAddressInfo::SourceAddr
150            }
151        }
152    }
153}
154
155impl DadIpExt for Ipv6 {
156    /// True, as per RFC 4862, Section 5.4:
157    ///   Duplicate Address Detection MUST be performed on all unicast
158    ///   addresses prior to assigning them to an interface, regardless of
159    ///   whether they are obtained through stateless autoconfiguration,
160    ///   DHCPv6, or manual configuration.
161    const DEFAULT_DAD_ENABLED: bool = true;
162
163    type SendData = Ipv6DadSendData;
164    type ReceivedPacketData<'a> = Option<NdpNonce<&'a [u8]>>;
165    type TentativeState = Ipv6TentativeDadState;
166    // Dad for IPv6 addresses does not have an announcing period.
167    type AnnouncingState = Never;
168    type IncomingPacketResultMeta = Ipv6PacketResultMetadata;
169    type RetransmitTimerData = NonZeroDuration;
170
171    fn generate_sent_probe_data<BC: RngContext>(
172        state: &mut Ipv6TentativeDadState,
173        addr: Ipv6Addr,
174        bindings_ctx: &mut BC,
175    ) -> Self::SendData {
176        let Ipv6TentativeDadState {
177            nonces,
178            added_extra_transmits_after_detecting_looped_back_ns: _,
179        } = state;
180        Ipv6DadSendData {
181            dst_ip: addr.to_solicited_node_address(),
182            message: NeighborSolicitation::new(addr),
183            nonce: nonces.evicting_create_and_store_nonce(bindings_ctx.rng()),
184        }
185    }
186
187    fn get_retransmission_interval<BC: RngContext>(
188        data: &NonZeroDuration,
189        _bindings_ctx: &mut BC,
190        _probes_remaining: &Option<NonZeroU16>,
191    ) -> NonZeroDuration {
192        *data
193    }
194
195    fn handle_incoming_packet_while_tentative<'a>(
196        data: Option<NdpNonce<&'a [u8]>>,
197        state: &mut Ipv6TentativeDadState,
198        dad_transmits_remaining: &mut Option<NonZeroU16>,
199    ) -> Ipv6PacketResultMetadata {
200        // Check if the incoming nonce matches stored nonces.
201        let Ipv6TentativeDadState { nonces, added_extra_transmits_after_detecting_looped_back_ns } =
202            state;
203        let matched_nonce = data.is_some_and(|nonce| nonces.contains(nonce.bytes()));
204        if matched_nonce
205            && !core::mem::replace(added_extra_transmits_after_detecting_looped_back_ns, true)
206        {
207            // Detected a looped-back DAD neighbor solicitation.
208            // Per RFC 7527, we should send MAX_MULTICAST_SOLICIT more DAD probes.
209            *dad_transmits_remaining = Some(
210                DEFAULT_MAX_MULTICAST_SOLICIT
211                    .saturating_add(dad_transmits_remaining.map(NonZero::get).unwrap_or(0)),
212            );
213        }
214        Ipv6PacketResultMetadata { matched_nonce }
215    }
216
217    fn handle_incoming_packet_while_assigned<'a>(
218        _data: Option<NdpNonce<&'a [u8]>>,
219        _ran_dad: bool,
220    ) -> bool {
221        // IPv6 doesn't have a notion of ongoing address conflict detection.
222        // Once we've assigned an address, we refuse to forfeit it.
223        false
224    }
225}
226
227/// Additional data about a received IPv4 DAD packet.
228#[derive(Debug, PartialEq)]
229pub enum Ipv4DadAddressInfo {
230    /// The address in question was the source address of the ARP packet.
231    SourceAddr,
232    /// The address in question was the target address of the ARP packet.
233    TargetAddr,
234}
235
236/// The type of IPv4 DAD probe to send.
237#[derive(Debug, PartialEq)]
238pub enum Ipv4SentProbeType {
239    /// An "ARP Probe" as specified in RFC 5227 Section 1.1.
240    Probe,
241    /// An "ARP Announcement" as specified in RFC 5227 Section 1.1.
242    Announcement,
243}
244
245/// The data needed to send an IPv4 DAD probe/announcement.
246#[derive(Debug)]
247pub struct Ipv4DadSendData {
248    /// The Ipv4Addr to probe.
249    target_ip: Ipv4Addr,
250    /// The type of DAD probe to send
251    probe_type: Ipv4SentProbeType,
252}
253
254impl Ipv4DadSendData {
255    /// Returns the sender and target IP addresses to send in the ARP request.
256    pub fn into_sender_and_target_addr(self) -> (Ipv4Addr, Ipv4Addr) {
257        let Self { target_ip, probe_type } = self;
258        let sender_ip = match probe_type {
259            // As per RFC 5227 section 2.1.1 for "ARP probes":
260            //   The 'sender IP address' field MUST be set to all zeroes.
261            Ipv4SentProbeType::Probe => Ipv4::UNSPECIFIED_ADDRESS,
262            // As per RFC 5227 section 2.3 for "ARP announcements":
263            //   The sender and target IP addresses are both set to the
264            //   host's newly selected IPv4 address
265            Ipv4SentProbeType::Announcement => target_ip.clone(),
266        };
267        // As per RFC 5227 section 2.1.1:
268        //   The 'target IP address' field MUST be set to the address
269        //   being probed.
270        (sender_ip, target_ip)
271    }
272}
273
274/// The data needed to send an IPv6 DAD probe.
275#[derive(Debug)]
276pub struct Ipv6DadSendData {
277    /// The destination address of the probe.
278    pub dst_ip: MulticastAddr<Ipv6Addr>,
279    /// The Neighbor Solicication to send.
280    pub message: NeighborSolicitation,
281    /// The nonce to accompany the neighbor solicitation.
282    pub nonce: OwnedNdpNonce,
283}
284
285/// A timer ID for duplicate address detection.
286#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
287pub struct DadTimerId<I: Ip, D: WeakDeviceIdentifier, A: WeakIpAddressId<I::Addr>> {
288    pub(crate) device_id: D,
289    pub(crate) addr: A,
290    _marker: IpVersionMarker<I>,
291}
292
293impl<I: Ip, D: WeakDeviceIdentifier, A: WeakIpAddressId<I::Addr>> DadTimerId<I, D, A> {
294    pub(super) fn device_id(&self) -> &D {
295        let Self { device_id, addr: _, _marker } = self;
296        device_id
297    }
298
299    /// Creates a new [`DadTimerId`]  for `device_id` and `addr`.
300    #[cfg(any(test, feature = "testutils"))]
301    pub fn new(device_id: D, addr: A) -> Self {
302        Self { device_id, addr, _marker: IpVersionMarker::new() }
303    }
304}
305
306/// A reference to the DAD address state.
307pub struct DadAddressStateRef<'a, I: DadIpExt, CC, BT: DadBindingsTypes> {
308    /// A mutable reference to an address' state.
309    pub dad_state: &'a mut DadState<I, BT>,
310    /// The execution context available with the address's DAD state.
311    pub core_ctx: &'a mut CC,
312}
313
314/// Holds references to state associated with duplicate address detection.
315pub struct DadStateRef<'a, I: DadIpExt, CC, BT: DadBindingsTypes> {
316    /// A reference to the DAD address state.
317    pub state: DadAddressStateRef<'a, I, CC, BT>,
318    /// Data used to calculate the time between DAD probe retransmissions.
319    pub retrans_timer_data: &'a I::RetransmitTimerData,
320    /// The maximum number of DAD messages to send.
321    pub max_dad_transmits: &'a Option<NonZeroU16>,
322}
323
324/// The execution context while performing DAD.
325pub trait DadAddressContext<I: Ip, BC>: IpDeviceAddressIdContext<I> {
326    /// Calls the function with a mutable reference to the address's assigned
327    /// flag.
328    fn with_address_assigned<O, F: FnOnce(&mut bool) -> O>(
329        &mut self,
330        device_id: &Self::DeviceId,
331        addr: &Self::AddressId,
332        cb: F,
333    ) -> O;
334
335    /// Returns whether or not DAD should be performed for the given device and
336    /// address.
337    fn should_perform_dad(&mut self, device_id: &Self::DeviceId, addr: &Self::AddressId) -> bool;
338}
339
340/// Like [`DadAddressContext`], with additional IPv6 specific functionality.
341pub trait Ipv6DadAddressContext<BC>: DadAddressContext<Ipv6, BC> {
342    /// Joins the multicast group on the device.
343    fn join_multicast_group(
344        &mut self,
345        bindings_ctx: &mut BC,
346        device_id: &Self::DeviceId,
347        multicast_addr: MulticastAddr<Ipv6Addr>,
348    );
349
350    /// Leaves the multicast group on the device.
351    fn leave_multicast_group(
352        &mut self,
353        bindings_ctx: &mut BC,
354        device_id: &Self::DeviceId,
355        multicast_addr: MulticastAddr<Ipv6Addr>,
356    );
357}
358
359/// The execution context for DAD.
360pub trait DadContext<I: IpDeviceIpExt, BC: DadBindingsTypes>:
361    IpDeviceAddressIdContext<I>
362    + DeviceIdContext<AnyDevice>
363    + CoreTimerContext<DadTimerId<I, Self::WeakDeviceId, Self::WeakAddressId>, BC>
364{
365    /// The inner address context.
366    type DadAddressCtx<'a>: DadAddressContext<I, BC, DeviceId = Self::DeviceId, AddressId = Self::AddressId>;
367
368    /// Calls the function with the DAD state associated with the address.
369    fn with_dad_state<O, F: FnOnce(DadStateRef<'_, I, Self::DadAddressCtx<'_>, BC>) -> O>(
370        &mut self,
371        device_id: &Self::DeviceId,
372        addr: &Self::AddressId,
373        cb: F,
374    ) -> O;
375
376    /// Sends a DAD probe.
377    fn send_dad_probe(
378        &mut self,
379        bindings_ctx: &mut BC,
380        device_id: &Self::DeviceId,
381        data: I::SendData,
382    );
383}
384
385/// The various states DAD may be in for an address.
386#[derive(Derivative)]
387#[derivative(Debug(bound = ""))]
388pub enum DadState<I: DadIpExt, BT: DadBindingsTypes> {
389    /// The address is assigned to an interface and can be considered bound to
390    /// it (all packets destined to the address will be accepted).
391    Assigned {
392        /// Indicates whether the address is assigned because DAD completed
393        /// successfully, or because DAD was disabled and skipped.
394        ran_dad: bool,
395    },
396
397    /// Like [`Self::Assigned`], but the DAD engine is announcing to the network
398    /// that we are using this address.
399    ///
400    /// This state corresponds to the announcing described in RFC 5227 section
401    /// 2.3 and as such only applies to IPv4 addresses. The IPv6 DAD
402    /// specification (RFC 4862 section 5.4) does not define an announcement
403    /// period.
404    Announcing {
405        /// The timer used to schedule DAD operations in the future.
406        timer: BT::Timer,
407        /// Announcing state specific to the given IP version.
408        ip_specific_state: I::AnnouncingState,
409    },
410
411    /// The address is considered unassigned to an interface for normal
412    /// operations, but has the intention of being assigned in the future (e.g.
413    /// once Duplicate Address Detection is completed).
414    Tentative {
415        /// The number of probes that still need to be sent. When `None`, there
416        /// are no more probes to send and DAD may resolve.
417        dad_transmits_remaining: Option<NonZeroU16>,
418        /// The timer used to schedule DAD operations in the future.
419        timer: BT::Timer,
420        /// Probing state specific to the given IP version.
421        ip_specific_state: I::TentativeState,
422        /// The (optional) delay to wait before starting to send DAD probes.
423        probe_wait: Option<Duration>,
424    },
425
426    /// The address has not yet been initialized.
427    Uninitialized,
428}
429
430impl<I: DadIpExt, BT: DadBindingsTypes> DadState<I, BT> {
431    pub(crate) fn is_assigned(&self) -> bool {
432        match self {
433            DadState::Assigned { .. } => true,
434            // As per RFC 5227 section 2.3:
435            //   The host may begin legitimately using the IP address
436            //   immediately after sending the first of the two ARP
437            //   Announcements.
438            // As such, consider an address in Announcing to be assigned.
439            DadState::Announcing { .. } => true,
440            DadState::Tentative { .. } | DadState::Uninitialized => false,
441        }
442    }
443}
444
445/// Dad state specific to announcing IPv4 addresses.
446#[derive(Debug)]
447pub struct Ipv4AnnouncingDadState {
448    /// The number of announcements that still need to be sent.
449    pub announcements_remaining: NonZeroU16,
450}
451
452/// DAD state specific to tentative IPv6 addresses.
453#[derive(Debug, Default)]
454pub struct Ipv6TentativeDadState {
455    /// The collection of observed nonces.
456    ///
457    /// Used to detect looped back Neighbor Solicitations.
458    pub nonces: NonceCollection,
459    /// Initialized to false, and exists as a sentinel so that extra
460    /// transmits are added only after the first looped-back probe is
461    /// detected.
462    pub added_extra_transmits_after_detecting_looped_back_ns: bool,
463}
464
465// Chosen somewhat arbitrarily. It's unlikely we need to store many
466// previously-used nonces given that we'll probably only ever see the most
467// recently used nonce looped back at us.
468const MAX_DAD_PROBE_NONCES_STORED: usize = 4;
469
470/// Like [`NdpNonce`], but owns the underlying data.
471pub type OwnedNdpNonce = [u8; MIN_NONCE_LENGTH];
472
473/// A data structure storing a limited number of `NdpNonce`s.
474#[derive(Default, Debug)]
475pub struct NonceCollection {
476    nonces: ArrayVec<OwnedNdpNonce, MAX_DAD_PROBE_NONCES_STORED>,
477}
478
479impl NonceCollection {
480    /// Given an `rng` source, generates a new unique nonce and stores it,
481    /// deleting the oldest nonce if there is no space remaining.
482    pub fn evicting_create_and_store_nonce(&mut self, mut rng: impl rand::Rng) -> OwnedNdpNonce {
483        let Self { nonces } = self;
484        loop {
485            let nonce: OwnedNdpNonce = rng.random();
486            if nonces.iter().any(|stored_nonce| stored_nonce == &nonce) {
487                continue;
488            }
489
490            if nonces.remaining_capacity() == 0 {
491                let _: OwnedNdpNonce = nonces.remove(0);
492            }
493            nonces.push(nonce.clone());
494            break nonce;
495        }
496    }
497
498    /// Checks if `nonce` is in the collection.
499    pub fn contains(&self, nonce: &[u8]) -> bool {
500        if nonce.len() != MIN_NONCE_LENGTH {
501            return false;
502        }
503
504        let Self { nonces } = self;
505        nonces.iter().any(|stored_nonce| stored_nonce == &nonce)
506    }
507}
508
509/// The bindings types for DAD.
510pub trait DadBindingsTypes: TimerBindingsTypes + InstantBindingsTypes {}
511impl<BT> DadBindingsTypes for BT where BT: TimerBindingsTypes + InstantBindingsTypes {}
512
513/// The bindings execution context for DAD.
514pub trait DadBindingsContext<I: Ip, D>:
515    DadBindingsTypes + TimerContext + EventContext<IpDeviceEvent<D, I, Self::Instant>> + RngContext
516{
517}
518impl<I: Ip, D, BC> DadBindingsContext<I, D> for BC where
519    BC: DadBindingsTypes
520        + TimerContext
521        + EventContext<IpDeviceEvent<D, I, Self::Instant>>
522        + RngContext
523{
524}
525
526/// The result of handling an incoming DAD probe.
527#[derive(Debug, Clone, Copy, PartialEq, Eq)]
528pub enum DadIncomingPacketResult<I: DadIpExt> {
529    /// The probe's address is not assigned to ourself.
530    Uninitialized,
531    /// The probe's address is tentatively assigned to ourself.
532    ///
533    /// Includes IP specific `meta` related to handling this probe.
534    Tentative { meta: I::IncomingPacketResultMeta },
535    /// The probe's address is assigned to ourself.
536    ///
537    /// Includes `should_remove`, dictating whether the assigned address
538    /// should be removed as a result of this probe.
539    Assigned { should_remove: bool },
540}
541
542/// IPv6 specific metadata held by [`DadIncomingPacketResult`].
543#[derive(Debug, PartialEq)]
544pub struct Ipv6PacketResultMetadata {
545    /// True if the incoming Neighbor Solicitation contained a nonce that
546    /// matched a nonce previously sent by ourself. This indicates that the
547    /// network looped back a Neighbor Solicitation from ourself.
548    pub(crate) matched_nonce: bool,
549}
550
551/// An implementation for Duplicate Address Detection.
552pub trait DadHandler<I: IpDeviceIpExt, BC: DadBindingsTypes>:
553    DeviceIdContext<AnyDevice> + IpDeviceAddressIdContext<I>
554{
555    /// Initializes the DAD state for the given device and address.
556    ///
557    /// If DAD is required, the return value holds a [`StartDad`] token that
558    /// can be used to start the DAD algorithm.
559    fn initialize_duplicate_address_detection<
560        'a,
561        F: FnOnce(IpAddressState) -> IpDeviceEvent<Self::DeviceId, I, BC::Instant>,
562    >(
563        &mut self,
564        bindings_ctx: &mut BC,
565        device_id: &'a Self::DeviceId,
566        addr: &'a Self::AddressId,
567        into_bindings_event: F,
568    ) -> NeedsDad<'a, Self::AddressId, Self::DeviceId>;
569
570    /// Starts duplicate address detection.
571    ///
572    /// The provided [`StartDad`] token is proof that DAD is required for the
573    /// address & device.
574    fn start_duplicate_address_detection<'a>(
575        &mut self,
576        bindings_ctx: &mut BC,
577        start_dad: StartDad<'_, Self::AddressId, Self::DeviceId>,
578    );
579
580    /// Stops duplicate address detection.
581    ///
582    /// Does nothing if DAD is not being performed on the address.
583    fn stop_duplicate_address_detection(
584        &mut self,
585        bindings_ctx: &mut BC,
586        device_id: &Self::DeviceId,
587        addr: &Self::AddressId,
588    );
589
590    /// Handles an incoming duplicate address detection packet.
591    ///
592    /// This packet is indicative of the sender (possibly ourselves, if the
593    /// packet was looped back) performing duplicate address detection.
594    ///
595    /// The returned state indicates the address' state on ourself.
596    fn handle_incoming_packet(
597        &mut self,
598        bindings_ctx: &mut BC,
599        device_id: &Self::DeviceId,
600        addr: &Self::AddressId,
601        data: I::ReceivedPacketData<'_>,
602    ) -> DadIncomingPacketResult<I>;
603}
604
605/// Indicates whether DAD is needed for a given address on a given device.
606#[derive(Debug)]
607pub enum NeedsDad<'a, A, D> {
608    No,
609    Yes(StartDad<'a, A, D>),
610}
611
612/// Signals that DAD is allowed to run for the given address & device.
613///
614/// Inner members are private to ensure the type can only be constructed in the
615/// current module, which ensures that duplicate address detection can only be
616/// started after having checked that it's necessary.
617#[derive(Debug)]
618pub struct StartDad<'a, A, D> {
619    address_id: &'a A,
620    device_id: &'a D,
621}
622
623/// Initializes the DAD state for the given device and address.
624fn initialize_duplicate_address_detection<
625    'a,
626    I: IpDeviceIpExt,
627    BC: DadBindingsContext<I, CC::DeviceId>,
628    CC: DadContext<I, BC>,
629    F1: FnOnce(IpAddressState) -> IpDeviceEvent<CC::DeviceId, I, BC::Instant>,
630    F2: FnOnce(&mut CC::DadAddressCtx<'_>, &mut BC, &CC::DeviceId, &CC::AddressId),
631>(
632    core_ctx: &mut CC,
633    bindings_ctx: &mut BC,
634    device_id: &'a CC::DeviceId,
635    addr: &'a CC::AddressId,
636    into_bindings_event: F1,
637    on_initialized_cb: F2,
638) -> NeedsDad<'a, CC::AddressId, CC::DeviceId> {
639    core_ctx.with_dad_state(
640        device_id,
641        addr,
642        |DadStateRef { state, retrans_timer_data: _, max_dad_transmits }| {
643            let DadAddressStateRef { dad_state, core_ctx } = state;
644            let needs_dad = match (core_ctx.should_perform_dad(device_id, addr), max_dad_transmits)
645            {
646                // There are two mechanisms by which DAD may be disabled:
647                //   1) The address has opted out of DAD, or
648                //   2) the interface's `max_dad_transmits` is `None`.
649                // In either case, the address immediately enters `Assigned`.
650                (false, _) | (true, None) => {
651                    *dad_state = DadState::Assigned { ran_dad: false };
652                    core_ctx.with_address_assigned(device_id, addr, |assigned| *assigned = true);
653                    NeedsDad::No
654                }
655                (true, Some(max_dad_transmits)) => {
656                    // Note: IPv4 requires waiting before starting to send DAD
657                    // probes, while IPv6 starts sending DAD probes immediately.
658                    let probe_wait = match I::VERSION {
659                        IpVersion::V4 => Some(ipv4_dad_probe_wait(bindings_ctx)),
660                        IpVersion::V6 => None,
661                    };
662                    *dad_state = DadState::Tentative {
663                        dad_transmits_remaining: Some(*max_dad_transmits),
664                        timer: CC::new_timer(
665                            bindings_ctx,
666                            DadTimerId {
667                                device_id: device_id.downgrade(),
668                                addr: addr.downgrade(),
669                                _marker: IpVersionMarker::new(),
670                            },
671                        ),
672                        ip_specific_state: Default::default(),
673                        probe_wait,
674                    };
675                    core_ctx.with_address_assigned(device_id, addr, |assigned| *assigned = false);
676                    NeedsDad::Yes(StartDad { device_id, address_id: addr })
677                }
678            };
679
680            // Run IP specific "on initialized" actions while holding the dad
681            // state lock.
682            on_initialized_cb(core_ctx, bindings_ctx, device_id, addr);
683
684            // Dispatch the appropriate event to bindings while holding the dad
685            // state lock.
686            let address_state = match &needs_dad {
687                NeedsDad::No => IpAddressState::Assigned,
688                NeedsDad::Yes(_) => IpAddressState::Tentative,
689            };
690            bindings_ctx.on_event(into_bindings_event(address_state));
691
692            needs_dad
693        },
694    )
695}
696
697fn do_duplicate_address_detection<
698    I: IpDeviceIpExt,
699    BC: DadBindingsContext<I, CC::DeviceId>,
700    CC: DadContext<I, BC>,
701>(
702    core_ctx: &mut CC,
703    bindings_ctx: &mut BC,
704    device_id: &CC::DeviceId,
705    addr: &CC::AddressId,
706) {
707    let should_send_probe = core_ctx.with_dad_state(
708        device_id,
709        addr,
710        |DadStateRef { state, retrans_timer_data, max_dad_transmits: _ }| {
711            let DadAddressStateRef { dad_state, core_ctx } = state;
712            match dad_state {
713                DadState::Uninitialized => {
714                    // Note: Starting DAD must have raced with stopping DAD.
715                    // Short circuit DAD execution by returning `None`.
716                    return None;
717                }
718                DadState::Assigned { .. } => {
719                    panic!("cannot do DAD for an already assigned address; addr={addr:?}")
720                }
721                DadState::Tentative {
722                    dad_transmits_remaining,
723                    timer,
724                    ip_specific_state,
725                    probe_wait,
726                } => {
727                    let (should_send_probe, state_change) = run_tentative_step::<I, CC, BC>(
728                        core_ctx,
729                        bindings_ctx,
730                        device_id,
731                        addr,
732                        retrans_timer_data,
733                        dad_transmits_remaining,
734                        timer,
735                        ip_specific_state,
736                        probe_wait,
737                    );
738                    match state_change {
739                        DadStateChangeFromTentative::None => {}
740                        DadStateChangeFromTentative::ToAssigned => {
741                            *dad_state = DadState::Assigned { ran_dad: true }
742                        }
743                        DadStateChangeFromTentative::ToAnnouncing { ip_specific_state } => {
744                            // Note: Because of Rust semantics, we cannot
745                            // convert directly from tentative to announcing.
746                            // Instead we use uninitialized as an intermediary
747                            // step to gain access to an owned `timer`.
748                            let orig = mem::replace(dad_state, DadState::Uninitialized);
749                            let timer = assert_matches!(
750                                orig,
751                                DadState::Tentative{timer, ..} => timer,
752                                "state must be tentative"
753                            );
754                            *dad_state = DadState::Announcing { ip_specific_state, timer }
755                        }
756                    }
757                    should_send_probe
758                }
759                DadState::Announcing { ip_specific_state, timer } => {
760                    #[derive(GenericOverIp)]
761                    #[generic_over_ip(I, Ip)]
762                    struct WrapIn<'a, I: DadIpExt>(&'a mut I::AnnouncingState, I::Addr);
763                    #[derive(GenericOverIp)]
764                    #[generic_over_ip(I, Ip)]
765                    struct WrapOut<I: DadIpExt>(I::SendData, DadStateChangeFromAnnouncing);
766                    let WrapOut(send_data, state_change) = I::map_ip(
767                        WrapIn(ip_specific_state, addr.addr().addr()),
768                        |WrapIn(ipv4_state, ipv4_addr)| {
769                            let (send_data, state_change) = run_ipv4_announcing_step(
770                                bindings_ctx,
771                                device_id,
772                                ipv4_addr,
773                                ipv4_state,
774                                timer,
775                            );
776                            WrapOut(send_data, state_change)
777                        },
778                        |WrapIn(ipv6_state, _ipv6_addr)| match *ipv6_state {},
779                    );
780                    match state_change {
781                        DadStateChangeFromAnnouncing::None => {}
782                        DadStateChangeFromAnnouncing::ToAssigned => {
783                            *dad_state = DadState::Assigned { ran_dad: true }
784                        }
785                    }
786                    Some(send_data)
787                }
788            }
789        },
790    );
791
792    if let Some(probe_send_data) = should_send_probe {
793        core_ctx.send_dad_probe(bindings_ctx, device_id, probe_send_data);
794    }
795}
796
797/// A change in [`DadState`] variant that occurs from [`run_tentative_step()`].
798enum DadStateChangeFromTentative<I: DadIpExt> {
799    None,
800    ToAssigned,
801    ToAnnouncing { ip_specific_state: I::AnnouncingState },
802}
803
804/// Runs a single step of the DAD algorithm for an address in [`DadState::Tentative`].
805///
806/// Returns `Some(I::SendData)` if a DAD probe should be sent, and
807/// [`DadStateChangeFromTentative`] if the state machine needs to transition
808/// into a new state.
809fn run_tentative_step<
810    I: IpDeviceIpExt,
811    CC: DadContext<I, BC>,
812    BC: DadBindingsContext<I, CC::DeviceId>,
813>(
814    core_ctx: &mut CC::DadAddressCtx<'_>,
815    bindings_ctx: &mut BC,
816    device_id: &CC::DeviceId,
817    addr: &CC::AddressId,
818    retrans_timer_data: &I::RetransmitTimerData,
819    dad_transmits_remaining: &mut Option<NonZeroU16>,
820    timer: &mut BC::Timer,
821    ip_specific_state: &mut I::TentativeState,
822    probe_wait: &mut Option<Duration>,
823) -> (Option<I::SendData>, DadStateChangeFromTentative<I>) {
824    if let Some(probe_wait) = probe_wait.take() {
825        // Schedule a timer and short circuit, deferring the first probe
826        // until after the timer fires.
827        schedule_dad_timer(bindings_ctx, timer, probe_wait, addr.addr(), device_id);
828        return (None, DadStateChangeFromTentative::None);
829    }
830
831    match dad_transmits_remaining {
832        None => {
833            #[derive(GenericOverIp)]
834            #[generic_over_ip(I, Ip)]
835            struct WrapOut<I: DadIpExt>(Option<I::SendData>, DadStateChangeFromTentative<I>);
836            let WrapOut(send_data, state_change) = I::map_ip(
837                addr.addr().addr(),
838                // When there are no more probes to be sent, IPv4 addresses
839                // should enter the announcing state, see RFC 5227, section 2.3.
840                //   Having probed to determine that a desired address may be
841                //   used safely, a host implementing this specification MUST
842                //   then announce that it is commencing to use this address by
843                //   broadcasting ANNOUNCE_NUM ARP Announcements, spaced
844                //   ANNOUNCE_INTERVAL seconds apart.
845                |ipv4_addr| {
846                    let mut state =
847                        Ipv4AnnouncingDadState { announcements_remaining: IPV4_DAD_ANNOUNCE_NUM };
848                    let (send_data, state_change) = run_ipv4_announcing_step(
849                        bindings_ctx,
850                        device_id,
851                        ipv4_addr,
852                        &mut state,
853                        timer,
854                    );
855                    // Generate the associated "FromTentative" state change,
856                    // based on the "FromAnnouncing" state change.
857                    let state_change = match state_change {
858                        DadStateChangeFromAnnouncing::None => {
859                            // Announcing did not finish. Record that the
860                            // state is now Announcing.
861                            DadStateChangeFromTentative::ToAnnouncing { ip_specific_state: state }
862                        }
863                        DadStateChangeFromAnnouncing::ToAssigned => {
864                            // Announcing finished on the first step. Skip it
865                            // and go directly to Assigned. In practice we
866                            // should never hit this case as it only happens
867                            // when `IPV4_DAD_ANNOUNCE_NUM` <= 1.
868                            DadStateChangeFromTentative::ToAssigned
869                        }
870                    };
871                    WrapOut(Some(send_data), state_change)
872                },
873                // IPv6 does not have any announcing period. Transition
874                // directly to `DadState::Assigned` without sending any probes.
875                |_ipv6_addr| WrapOut(None, DadStateChangeFromTentative::ToAssigned),
876            );
877
878            // Regardless of which state we're transitioning to (Announcing or
879            // Assigned), the address should be considered assigned.
880            // See RFC 5227's commentary on Announcing from section 2.3:
881            //   The host may begin legitimately using the IP address
882            //   immediately after sending the first of the two ARP
883            //   Announcements.
884            core_ctx.with_address_assigned(device_id, addr, |assigned| *assigned = true);
885            bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
886                device: device_id.clone(),
887                addr: addr.addr_sub().addr().into(),
888                state: IpAddressState::Assigned,
889            });
890            return (send_data, state_change);
891        }
892        Some(non_zero_remaining) => {
893            *dad_transmits_remaining = NonZeroU16::new(non_zero_remaining.get() - 1);
894
895            // Delay sending subsequent DAD probes.
896            //
897            // For IPv4, per RFC 5227 Section 2.1.1
898            //   each of these probe packets spaced randomly and
899            //   uniformly, PROBE_MIN to PROBE_MAX seconds apart.
900            //
901            // And for IPv6, per RFC 4862 section 5.1,
902            //   DupAddrDetectTransmits ...
903            //      Autoconfiguration also assumes the presence of the variable
904            //      RetransTimer as defined in [RFC4861]. For autoconfiguration
905            //      purposes, RetransTimer specifies the delay between
906            //      consecutive Neighbor Solicitation transmissions performed
907            //      during Duplicate Address Detection (if
908            //      DupAddrDetectTransmits is greater than 1), as well as the
909            //      time a node waits after sending the last Neighbor
910            //      Solicitation before ending the Duplicate Address Detection
911            //      process.
912            let retrans_interval = I::get_retransmission_interval(
913                retrans_timer_data,
914                bindings_ctx,
915                dad_transmits_remaining,
916            );
917            schedule_dad_timer(bindings_ctx, timer, retrans_interval.get(), addr.addr(), device_id);
918            debug!(
919                "sending DAD probe for {}; {} remaining",
920                addr.addr(),
921                dad_transmits_remaining.map_or(0, NonZeroU16::get)
922            );
923            let send_data =
924                I::generate_sent_probe_data(ip_specific_state, addr.addr().addr(), bindings_ctx);
925            return (Some(send_data), DadStateChangeFromTentative::None);
926        }
927    }
928}
929
930/// A change in [`DadState`] variant that occurs from [`run_ipv4_announcing_step()`].
931enum DadStateChangeFromAnnouncing {
932    None,
933    ToAssigned,
934}
935
936/// Runs a single step of the DAD algorithm for an address in [`DadState::Announcing`].
937///
938/// Implemented concretely for `Ipv4` rather than any `I` because IPv6 does not
939/// support announcing.
940///
941/// Returns the `Ipv4DadSendData` for the announcement that should be sent, and
942/// [`DadStateChangeFromAnnouncing`] if the state machine needs to transition
943/// into a new new state.
944fn run_ipv4_announcing_step<BC: TimerContext>(
945    bindings_ctx: &mut BC,
946    device_id: impl Debug,
947    addr: Ipv4Addr,
948    ip_specific_state: &mut Ipv4AnnouncingDadState,
949    timer: &mut BC::Timer,
950) -> (Ipv4DadSendData, DadStateChangeFromAnnouncing) {
951    let Ipv4AnnouncingDadState { announcements_remaining } = ip_specific_state;
952
953    let new_announcements_remaining = NonZeroU16::new(announcements_remaining.get() - 1);
954    debug!(
955        "sending DAD announcement for {}; {} remaining",
956        addr,
957        new_announcements_remaining.map_or(0, NonZeroU16::get)
958    );
959
960    let send_data =
961        Ipv4DadSendData { target_ip: addr, probe_type: Ipv4SentProbeType::Announcement };
962
963    let state_change = match new_announcements_remaining {
964        // This is the final announcement, change to assigned
965        None => DadStateChangeFromAnnouncing::ToAssigned,
966        Some(non_zero_announcements_remaining) => {
967            // There are more announcements to send after this one. Schedule
968            // another timer.
969            *announcements_remaining = non_zero_announcements_remaining;
970            schedule_dad_timer(bindings_ctx, timer, IPV4_ANNOUNCE_INTERVAL.get(), addr, device_id);
971            DadStateChangeFromAnnouncing::None
972        }
973    };
974    (send_data, state_change)
975}
976
977/// Schedules a [`DadTimer`], panicking if it was already scheduled.
978fn schedule_dad_timer<BC: TimerContext>(
979    bindings_ctx: &mut BC,
980    timer: &mut BC::Timer,
981    duration: Duration,
982    addr: impl Debug,
983    device_id: impl Debug,
984) {
985    assert_eq!(
986        bindings_ctx.schedule_timer(duration, timer),
987        None,
988        "Unexpected DAD timer; addr={:?}, device_id={:?}",
989        addr,
990        device_id
991    );
992}
993
994/// Stop DAD for the given device and address.
995fn stop_duplicate_address_detection<
996    'a,
997    I: IpDeviceIpExt,
998    BC: DadBindingsContext<I, CC::DeviceId>,
999    CC: DadContext<I, BC>,
1000    F: FnOnce(&mut CC::DadAddressCtx<'_>, &mut BC, &CC::DeviceId, &CC::AddressId),
1001>(
1002    core_ctx: &mut CC,
1003    bindings_ctx: &mut BC,
1004    device_id: &'a CC::DeviceId,
1005    addr: &'a CC::AddressId,
1006    on_stopped_cb: F,
1007) {
1008    core_ctx.with_dad_state(
1009        device_id,
1010        addr,
1011        |DadStateRef { state, retrans_timer_data: _, max_dad_transmits: _ }| {
1012            let DadAddressStateRef { dad_state, core_ctx } = state;
1013
1014            match dad_state {
1015                DadState::Assigned { .. } => {}
1016                DadState::Announcing { timer, .. } | DadState::Tentative { timer, .. } => {
1017                    // Generally we should have a timer installed in the
1018                    // tentative/announcing state, but we could be racing with
1019                    // the timer firing in bindings so we can't assert that it's
1020                    // installed here.
1021                    let _: Option<_> = bindings_ctx.cancel_timer(timer);
1022                }
1023                // No actions are needed to stop DAD from `Uninitialized`.
1024                DadState::Uninitialized => return,
1025            };
1026
1027            // Undo the work we did when starting/performing DAD by putting
1028            // the address back into unassigned state.
1029
1030            *dad_state = DadState::Uninitialized;
1031            core_ctx.with_address_assigned(device_id, addr, |assigned| *assigned = false);
1032
1033            // Run IP specific "on stopped" actions while holding the dad state
1034            // lock.
1035            on_stopped_cb(core_ctx, bindings_ctx, device_id, addr)
1036        },
1037    )
1038}
1039
1040// Handles an incoming packet for the given addr received on the given device.
1041fn handle_incoming_packet<
1042    'a,
1043    I: IpDeviceIpExt,
1044    BC: DadBindingsContext<I, CC::DeviceId>,
1045    CC: DadContext<I, BC>,
1046>(
1047    core_ctx: &mut CC,
1048    _bindings_ctx: &mut BC,
1049    device_id: &'a CC::DeviceId,
1050    addr: &'a CC::AddressId,
1051    data: I::ReceivedPacketData<'a>,
1052) -> DadIncomingPacketResult<I> {
1053    core_ctx.with_dad_state(
1054        device_id,
1055        addr,
1056        |DadStateRef { state, retrans_timer_data: _, max_dad_transmits: _ }| {
1057            let DadAddressStateRef { dad_state, core_ctx: _ } = state;
1058            match dad_state {
1059                DadState::Uninitialized => DadIncomingPacketResult::Uninitialized,
1060                DadState::Assigned { ran_dad } => DadIncomingPacketResult::Assigned {
1061                    should_remove: I::handle_incoming_packet_while_assigned(data, *ran_dad),
1062                },
1063                DadState::Announcing { .. } => {
1064                    // NB: If we're in the Announcing state, DAD must be enabled
1065                    // for the address.
1066                    let ran_dad = true;
1067                    DadIncomingPacketResult::Assigned {
1068                        should_remove: I::handle_incoming_packet_while_assigned(data, ran_dad),
1069                    }
1070                }
1071                DadState::Tentative {
1072                    dad_transmits_remaining,
1073                    timer: _,
1074                    ip_specific_state,
1075                    probe_wait: _,
1076                } => DadIncomingPacketResult::Tentative {
1077                    meta: I::handle_incoming_packet_while_tentative(
1078                        data,
1079                        ip_specific_state,
1080                        dad_transmits_remaining,
1081                    ),
1082                },
1083            }
1084        },
1085    )
1086}
1087
1088impl<BC: DadBindingsContext<Ipv4, Self::DeviceId>, CC: DadContext<Ipv4, BC>> DadHandler<Ipv4, BC>
1089    for CC
1090{
1091    fn initialize_duplicate_address_detection<
1092        'a,
1093        F: FnOnce(IpAddressState) -> IpDeviceEvent<Self::DeviceId, Ipv4, BC::Instant>,
1094    >(
1095        &mut self,
1096        bindings_ctx: &mut BC,
1097        device_id: &'a Self::DeviceId,
1098        addr: &'a Self::AddressId,
1099        into_bindings_event: F,
1100    ) -> NeedsDad<'a, Self::AddressId, Self::DeviceId> {
1101        initialize_duplicate_address_detection(
1102            self,
1103            bindings_ctx,
1104            device_id,
1105            addr,
1106            into_bindings_event,
1107            // on_initialized_cb
1108            |_core_ctx, _bindings_ctx, _device_id, _addr| {},
1109        )
1110    }
1111
1112    fn start_duplicate_address_detection<'a>(
1113        &mut self,
1114        bindings_ctx: &mut BC,
1115        start_dad: StartDad<'_, Self::AddressId, Self::DeviceId>,
1116    ) {
1117        let StartDad { device_id, address_id } = start_dad;
1118        do_duplicate_address_detection(self, bindings_ctx, device_id, address_id)
1119    }
1120
1121    fn stop_duplicate_address_detection(
1122        &mut self,
1123        bindings_ctx: &mut BC,
1124        device_id: &Self::DeviceId,
1125        addr: &Self::AddressId,
1126    ) {
1127        stop_duplicate_address_detection(
1128            self,
1129            bindings_ctx,
1130            device_id,
1131            addr,
1132            // on_stopped_cb
1133            |_core_ctx, _bindings_ctx, _device_id, _addr| {},
1134        )
1135    }
1136
1137    /// Handles an incoming ARP packet.
1138    fn handle_incoming_packet(
1139        &mut self,
1140        bindings_ctx: &mut BC,
1141        device_id: &Self::DeviceId,
1142        addr: &Self::AddressId,
1143        data: Ipv4DadAddressInfo,
1144    ) -> DadIncomingPacketResult<Ipv4> {
1145        handle_incoming_packet(self, bindings_ctx, device_id, addr, data)
1146    }
1147}
1148
1149impl<BC: DadBindingsContext<Ipv6, Self::DeviceId>, CC: DadContext<Ipv6, BC>> DadHandler<Ipv6, BC>
1150    for CC
1151where
1152    for<'a> CC::DadAddressCtx<'a>: Ipv6DadAddressContext<BC>,
1153{
1154    fn initialize_duplicate_address_detection<
1155        'a,
1156        F: FnOnce(IpAddressState) -> IpDeviceEvent<Self::DeviceId, Ipv6, BC::Instant>,
1157    >(
1158        &mut self,
1159        bindings_ctx: &mut BC,
1160        device_id: &'a Self::DeviceId,
1161        addr: &'a Self::AddressId,
1162        into_bindings_event: F,
1163    ) -> NeedsDad<'a, Self::AddressId, Self::DeviceId> {
1164        initialize_duplicate_address_detection(
1165            self,
1166            bindings_ctx,
1167            device_id,
1168            addr,
1169            into_bindings_event,
1170            // on_initialized_cb
1171            |core_ctx, bindings_ctx, device_id, addr| {
1172                // As per RFC 4862 section 5.4.2,
1173                //
1174                //   Before sending a Neighbor Solicitation, an interface MUST
1175                //   join the all-nodes multicast address and the solicited-node
1176                //   multicast address of the tentative address.
1177                //
1178                // Note that:
1179                // * We join the all-nodes multicast address on interface
1180                //   enable.
1181                // * We join the solicited-node multicast address, even if the
1182                //   address is skipping DAD (and therefore, the tentative
1183                //   state).
1184                // * We join the solicited-node multicast address *after*
1185                //   initializing the address. If the address is tentative, it
1186                //   won't be used as the source for any outgoing MLD message.
1187                core_ctx.join_multicast_group(
1188                    bindings_ctx,
1189                    device_id,
1190                    addr.addr().addr().to_solicited_node_address(),
1191                );
1192            },
1193        )
1194    }
1195
1196    fn start_duplicate_address_detection<'a>(
1197        &mut self,
1198        bindings_ctx: &mut BC,
1199        start_dad: StartDad<'_, Self::AddressId, Self::DeviceId>,
1200    ) {
1201        let StartDad { device_id, address_id } = start_dad;
1202        do_duplicate_address_detection(self, bindings_ctx, device_id, address_id)
1203    }
1204
1205    fn stop_duplicate_address_detection(
1206        &mut self,
1207        bindings_ctx: &mut BC,
1208        device_id: &Self::DeviceId,
1209        addr: &Self::AddressId,
1210    ) {
1211        stop_duplicate_address_detection(
1212            self,
1213            bindings_ctx,
1214            device_id,
1215            addr,
1216            // on_stopped_cb
1217            |core_ctx, bindings_ctx, device_id, addr| {
1218                // Undo the steps taken when DAD was initialized and leave the
1219                // solicited node multicast group. Note that we leave the
1220                // solicited-node multicast address *after* stopping dad. The
1221                // address will no longer be assigned and won't be used as the
1222                // source for any outgoing MLD message.
1223                core_ctx.leave_multicast_group(
1224                    bindings_ctx,
1225                    device_id,
1226                    addr.addr().addr().to_solicited_node_address(),
1227                );
1228            },
1229        )
1230    }
1231
1232    /// Handles an incoming Neighbor Solicitation / Neighbor Advertisement.
1233    fn handle_incoming_packet(
1234        &mut self,
1235        bindings_ctx: &mut BC,
1236        device_id: &Self::DeviceId,
1237        addr: &Self::AddressId,
1238        data: Option<NdpNonce<&[u8]>>,
1239    ) -> DadIncomingPacketResult<Ipv6> {
1240        handle_incoming_packet(self, bindings_ctx, device_id, addr, data)
1241    }
1242}
1243
1244impl<I: IpDeviceIpExt, BC: DadBindingsContext<I, CC::DeviceId>, CC: DadContext<I, BC>>
1245    HandleableTimer<CC, BC> for DadTimerId<I, CC::WeakDeviceId, CC::WeakAddressId>
1246{
1247    fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, _: BC::UniqueTimerId) {
1248        let Self { device_id, addr, _marker } = self;
1249        let Some(device_id) = device_id.upgrade() else {
1250            return;
1251        };
1252        let Some(addr_id) = addr.upgrade() else {
1253            return;
1254        };
1255        do_duplicate_address_detection(core_ctx, bindings_ctx, &device_id, &addr_id)
1256    }
1257}
1258
1259/// As defined in RFC 5227, section 1.1:
1260///  PROBE_MIN            1 second   (minimum delay until repeated probe)
1261///  PROBE_MAX            2 seconds  (maximum delay until repeated probe)
1262const IPV4_PROBE_RANGE: RangeInclusive<Duration> = Duration::from_secs(1)..=Duration::from_secs(2);
1263
1264/// Generate the interval between repeated IPv4 DAD probes.
1265pub fn ipv4_dad_probe_interval<BC: RngContext>(bindings_ctx: &mut BC) -> NonZeroDuration {
1266    // As per RFC 5227, section 2.2.1:
1267    //   ... and should then send PROBE_NUM probe packets, each of these
1268    // probe packets spaced randomly and uniformly, PROBE_MIN to PROBE_MAX
1269    // seconds apart.
1270    let retrans_interval = bindings_ctx.rng().random_range(IPV4_PROBE_RANGE);
1271    // NB: Safe to unwrap because `IPV4_PROBE_RANGE` contains only non-zero
1272    // values.
1273    NonZeroDuration::new(retrans_interval).unwrap()
1274}
1275
1276/// As defined in RFC 5227, section 1.1:
1277///   PROBE_WAIT           1 second   (initial random delay)
1278const IPV4_PROBE_WAIT_RANGE: RangeInclusive<Duration> = Duration::ZERO..=Duration::from_secs(1);
1279
1280/// Generate the duration to wait before starting to send IPv4 DAD probes.
1281pub fn ipv4_dad_probe_wait<BC: RngContext>(bindings_ctx: &mut BC) -> Duration {
1282    // As per RFC 5227, section 2.2.1:
1283    //   When ready to begin probing, the host should then wait for a random
1284    //   time interval selected uniformly in the range zero to PROBE_WAIT
1285    //   seconds
1286    bindings_ctx.rng().random_range(IPV4_PROBE_WAIT_RANGE)
1287}
1288
1289/// As defined in RFC 5227, section 1.1:
1290///   ANNOUNCE_WAIT        2 seconds  (delay before announcing)
1291const IPV4_ANNOUNCE_WAIT: NonZeroDuration = NonZeroDuration::new(Duration::from_secs(2)).unwrap();
1292
1293/// As defined in RFC 5227, section 1.1:
1294///   ANNOUNCE_INTERVAL    2 seconds  (time between Announcement packets)
1295const IPV4_ANNOUNCE_INTERVAL: NonZeroDuration =
1296    NonZeroDuration::new(Duration::from_secs(2)).unwrap();
1297
1298/// As per RFC 5227, section 1.1:
1299///   ANNOUNCE_NUM         2          (number of Announcement packets)
1300pub const IPV4_DAD_ANNOUNCE_NUM: NonZeroU16 = NonZeroU16::new(2).unwrap();
1301
1302/// As per RFC 5227, section 2.4
1303///   To resolve the address conflict, a host MUST respond to a
1304///   conflicting ARP packet as described in either (a), (b), or
1305///  (c) below:
1306///
1307///   (a) [...] a host MAY elect to immediately cease using the address [...]
1308///   (b) [...] a host MAY elect to attempt to defend its address by recording
1309///       the time that the conflicting ARP packet was received, and then
1310///       broadcasting one single ARP Announcement. [...] Having done this, the
1311///       host can then continue to use the address normally without any further
1312///       special action.  However, if this is not the first conflicting ARP
1313///       packet the host has seen, and the time recorded for the previous
1314///       conflicting ARP packet is recent, within DEFEND_INTERVAL seconds, then
1315///       the host MUST immediately cease using this address [...].
1316///    (c) [...] a host may elect to defend its address indefinitely.  [...] If
1317///       the host has not seen any other conflicting ARP packets recently,
1318///       within the last DEFEND_INTERVAL seconds, then it MUST record the time
1319///       that the conflicting ARP packet was received, and then broadcast one
1320///       single ARP Announcement, giving its own IP and hardware addresses.
1321///       Having done this, the host can then continue to use the address
1322///       normally without any further special action.
1323enum AddressDefenseStrategy {
1324    /// Corresponds to Option A from RFC 5227, section 2.4.
1325    ForfeitAddress,
1326}
1327
1328/// As per RFC 5227, section 2.4:
1329///   For most client machines that do not need a fixed IP address, immediately
1330///   requesting the configuring agent (human user, DHCP client, etc.) to
1331///   configure a new address as soon as the conflict is detected is the best
1332///   way to restore useful communication as quickly as possible.
1333///
1334/// Here, we follow the RFC's guidance for "client machines" and
1335/// implement option (a) by removing the address. One may be
1336/// concerned about getting bullied by another node on the
1337/// network into forfeiting our address, however, the RFC points
1338/// out later in section 5 that:
1339///
1340///   A malicious host may send fraudulent ARP packets on the network,
1341///   interfering with the correct operation of other hosts. For example, it is
1342///   easy for a host to answer all ARP Requests with Replies giving its own
1343///   hardware address, thereby claiming ownership of every address on the
1344///   network.  This specification makes this existing ARP vulnerability no
1345///   worse, and in some ways makes it better: instead of failing silently with
1346///   no indication why, hosts implementing this specification either attempt to
1347///   reconfigure automatically, or at least inform the human user of what is
1348///   happening.
1349// TODO(https://fxbug.dev/418220970): In the future "client machine" may not
1350// accurately describe our use case. Allow configuring the address defense
1351// strategy.
1352const IPV4_ADDRESS_DEFENSE_STRATEGY: AddressDefenseStrategy =
1353    AddressDefenseStrategy::ForfeitAddress;
1354
1355#[cfg(test)]
1356mod tests {
1357    use core::ops::RangeBounds;
1358    use core::time::Duration;
1359
1360    use assert_matches::assert_matches;
1361    use ip_test_macro::ip_test;
1362    use net_types::ip::{AddrSubnet, GenericOverIp, IpAddress as _, IpVersion, Ipv4Addr};
1363    use net_types::{NonMappedAddr, NonMulticastAddr, SpecifiedAddr, UnicastAddr, Witness as _};
1364    use netstack3_base::testutil::{
1365        FakeBindingsCtx, FakeCoreCtx, FakeCryptoRng, FakeDeviceId, FakeInstant,
1366        FakeTimerCtxExt as _, FakeWeakAddressId, FakeWeakDeviceId,
1367    };
1368    use netstack3_base::{
1369        AssignedAddrIpExt, CtxPair, InstantContext as _, Ipv4DeviceAddr, Ipv6DeviceAddr,
1370        SendFrameContext as _, TimerHandler,
1371    };
1372    use netstack3_hashmap::hash_map::{Entry, HashMap};
1373    use packet::EmptyBuf;
1374    use test_case::test_case;
1375
1376    use super::*;
1377
1378    struct FakeDadAddressContext<I: IpDeviceIpExt> {
1379        addr: I::AssignedWitness,
1380        assigned: bool,
1381        // NB: DAD only joins multicast groups for IPv6.
1382        groups: HashMap<MulticastAddr<Ipv6Addr>, usize>,
1383        should_perform_dad: bool,
1384    }
1385
1386    trait TestDadIpExt: IpDeviceIpExt {
1387        const DAD_ADDRESS: Self::AssignedWitness;
1388
1389        const DEFAULT_RETRANS_TIMER_DATA: Self::RetransmitTimerData;
1390
1391        const EXPECTED_NUM_ANNOUNCEMENTS: u16;
1392
1393        // Returns the range of `FakeInstant` that a timer is expected
1394        // to be installed within.
1395        fn expected_timer_range(now: FakeInstant) -> impl RangeBounds<FakeInstant> + Debug;
1396
1397        type DadHandlerCtx: DadHandler<
1398                Self,
1399                FakeBindingsCtxImpl<Self>,
1400                DeviceId = FakeDeviceId,
1401                AddressId = AddrSubnet<Self::Addr, Self::AssignedWitness>,
1402            > + AsRef<FakeDadContext<Self>>;
1403
1404        // A trampoline method to get access to `DadHandler<I, _>`.
1405        //
1406        // Because the base implementation for DadHandler exists only for IPv4
1407        // and IPv6, we need this helper to access a DadHandler for any `I`.
1408        fn with_dad_handler<O, F: FnOnce(&mut Self::DadHandlerCtx) -> O>(
1409            core_ctx: &mut FakeCoreCtxImpl<Self>,
1410            cb: F,
1411        ) -> O;
1412    }
1413
1414    impl TestDadIpExt for Ipv4 {
1415        const DAD_ADDRESS: Ipv4DeviceAddr = unsafe {
1416            NonMulticastAddr::new_unchecked(NonMappedAddr::new_unchecked(
1417                SpecifiedAddr::new_unchecked(Ipv4Addr::new([192, 168, 0, 1])),
1418            ))
1419        };
1420
1421        const DEFAULT_RETRANS_TIMER_DATA: () = ();
1422
1423        const EXPECTED_NUM_ANNOUNCEMENTS: u16 = IPV4_DAD_ANNOUNCE_NUM.get();
1424
1425        fn expected_timer_range(now: FakeInstant) -> impl RangeBounds<FakeInstant> + Debug {
1426            let FakeInstant { offset } = now;
1427            FakeInstant::from(offset + *IPV4_PROBE_RANGE.start())
1428                ..=FakeInstant::from(offset + *IPV4_PROBE_RANGE.end())
1429        }
1430
1431        type DadHandlerCtx = FakeCoreCtxImpl<Ipv4>;
1432
1433        fn with_dad_handler<O, F: FnOnce(&mut FakeCoreCtxImpl<Ipv4>) -> O>(
1434            core_ctx: &mut FakeCoreCtxImpl<Ipv4>,
1435            cb: F,
1436        ) -> O {
1437            cb(core_ctx)
1438        }
1439    }
1440
1441    impl TestDadIpExt for Ipv6 {
1442        const DAD_ADDRESS: Ipv6DeviceAddr = unsafe {
1443            NonMappedAddr::new_unchecked(UnicastAddr::new_unchecked(Ipv6Addr::new([
1444                0xa, 0, 0, 0, 0, 0, 0, 1,
1445            ])))
1446        };
1447
1448        const DEFAULT_RETRANS_TIMER_DATA: NonZeroDuration =
1449            NonZeroDuration::new(Duration::from_secs(1)).unwrap();
1450
1451        // NB: Announcing isn't supported for IPv6.
1452        const EXPECTED_NUM_ANNOUNCEMENTS: u16 = 0;
1453
1454        fn expected_timer_range(now: FakeInstant) -> impl RangeBounds<FakeInstant> + Debug {
1455            let FakeInstant { offset } = now;
1456            let expected_timer = FakeInstant::from(offset + Self::DEFAULT_RETRANS_TIMER_DATA.get());
1457            expected_timer..=expected_timer
1458        }
1459
1460        type DadHandlerCtx = FakeCoreCtxImpl<Ipv6>;
1461
1462        fn with_dad_handler<O, F: FnOnce(&mut FakeCoreCtxImpl<Ipv6>) -> O>(
1463            core_ctx: &mut FakeCoreCtxImpl<Ipv6>,
1464            cb: F,
1465        ) -> O {
1466            cb(core_ctx)
1467        }
1468    }
1469
1470    impl<I: TestDadIpExt> Default for FakeDadAddressContext<I> {
1471        fn default() -> Self {
1472            Self {
1473                addr: I::DAD_ADDRESS,
1474                assigned: false,
1475                groups: Default::default(),
1476                should_perform_dad: true,
1477            }
1478        }
1479    }
1480
1481    type FakeAddressCtxImpl<I> = FakeCoreCtx<FakeDadAddressContext<I>, (), FakeDeviceId>;
1482
1483    impl<I: IpDeviceIpExt> DadAddressContext<I, FakeBindingsCtxImpl<I>> for FakeAddressCtxImpl<I> {
1484        fn with_address_assigned<O, F: FnOnce(&mut bool) -> O>(
1485            &mut self,
1486            &FakeDeviceId: &Self::DeviceId,
1487            request_addr: &Self::AddressId,
1488            cb: F,
1489        ) -> O {
1490            let FakeDadAddressContext { addr, assigned, .. } = &mut self.state;
1491            assert_eq!(request_addr.addr(), *addr);
1492            cb(assigned)
1493        }
1494
1495        fn should_perform_dad(
1496            &mut self,
1497            &FakeDeviceId: &Self::DeviceId,
1498            request_addr: &Self::AddressId,
1499        ) -> bool {
1500            let FakeDadAddressContext { addr, should_perform_dad, .. } = &mut self.state;
1501            assert_eq!(request_addr.addr(), *addr);
1502            *should_perform_dad
1503        }
1504    }
1505
1506    impl Ipv6DadAddressContext<FakeBindingsCtxImpl<Ipv6>> for FakeAddressCtxImpl<Ipv6> {
1507        fn join_multicast_group(
1508            &mut self,
1509            _bindings_ctx: &mut FakeBindingsCtxImpl<Ipv6>,
1510            &FakeDeviceId: &Self::DeviceId,
1511            multicast_addr: MulticastAddr<Ipv6Addr>,
1512        ) {
1513            *self.state.groups.entry(multicast_addr).or_default() += 1;
1514        }
1515
1516        fn leave_multicast_group(
1517            &mut self,
1518            _bindings_ctx: &mut FakeBindingsCtxImpl<Ipv6>,
1519            &FakeDeviceId: &Self::DeviceId,
1520            multicast_addr: MulticastAddr<Ipv6Addr>,
1521        ) {
1522            match self.state.groups.entry(multicast_addr) {
1523                Entry::Vacant(_) => {}
1524                Entry::Occupied(mut e) => {
1525                    let v = e.get_mut();
1526                    const COUNT_BEFORE_REMOVE: usize = 1;
1527                    if *v == COUNT_BEFORE_REMOVE {
1528                        assert_eq!(e.remove(), COUNT_BEFORE_REMOVE);
1529                    } else {
1530                        *v -= 1
1531                    }
1532                }
1533            }
1534        }
1535    }
1536
1537    struct FakeDadContext<I: IpDeviceIpExt> {
1538        state: DadState<I, FakeBindingsCtxImpl<I>>,
1539        max_dad_transmits: Option<NonZeroU16>,
1540        address_ctx: FakeAddressCtxImpl<I>,
1541    }
1542
1543    type TestDadTimerId<I> = DadTimerId<
1544        I,
1545        FakeWeakDeviceId<FakeDeviceId>,
1546        FakeWeakAddressId<AddrSubnet<<I as Ip>::Addr, <I as AssignedAddrIpExt>::AssignedWitness>>,
1547    >;
1548
1549    type FakeBindingsCtxImpl<I> =
1550        FakeBindingsCtx<TestDadTimerId<I>, IpDeviceEvent<FakeDeviceId, I, FakeInstant>, (), ()>;
1551
1552    type FakeCoreCtxImpl<I> =
1553        FakeCoreCtx<FakeDadContext<I>, <I as DadIpExt>::SendData, FakeDeviceId>;
1554
1555    fn get_address_id<I: IpDeviceIpExt>(
1556        addr: I::AssignedWitness,
1557    ) -> AddrSubnet<I::Addr, I::AssignedWitness> {
1558        AddrSubnet::from_witness(addr, I::Addr::BYTES * 8).unwrap()
1559    }
1560
1561    impl<I: IpDeviceIpExt> CoreTimerContext<TestDadTimerId<I>, FakeBindingsCtxImpl<I>>
1562        for FakeCoreCtxImpl<I>
1563    {
1564        fn convert_timer(dispatch_id: TestDadTimerId<I>) -> TestDadTimerId<I> {
1565            dispatch_id
1566        }
1567    }
1568
1569    impl<I: TestDadIpExt> DadContext<I, FakeBindingsCtxImpl<I>> for FakeCoreCtxImpl<I> {
1570        type DadAddressCtx<'a> = FakeAddressCtxImpl<I>;
1571
1572        fn with_dad_state<
1573            O,
1574            F: FnOnce(DadStateRef<'_, I, Self::DadAddressCtx<'_>, FakeBindingsCtxImpl<I>>) -> O,
1575        >(
1576            &mut self,
1577            &FakeDeviceId: &FakeDeviceId,
1578            request_addr: &Self::AddressId,
1579            cb: F,
1580        ) -> O {
1581            let FakeDadContext { state, max_dad_transmits, address_ctx } = &mut self.state;
1582            let ctx_addr = address_ctx.state.addr;
1583            let requested_addr = request_addr.addr();
1584            assert!(
1585                ctx_addr == requested_addr,
1586                "invalid address {requested_addr} expected {ctx_addr}"
1587            );
1588            cb(DadStateRef {
1589                state: DadAddressStateRef { dad_state: state, core_ctx: address_ctx },
1590                retrans_timer_data: &I::DEFAULT_RETRANS_TIMER_DATA,
1591                max_dad_transmits,
1592            })
1593        }
1594
1595        fn send_dad_probe(
1596            &mut self,
1597            bindings_ctx: &mut FakeBindingsCtxImpl<I>,
1598            &FakeDeviceId: &FakeDeviceId,
1599            data: I::SendData,
1600        ) {
1601            self.send_frame(bindings_ctx, data, EmptyBuf).unwrap()
1602        }
1603    }
1604
1605    type FakeCtx<I> = CtxPair<FakeCoreCtxImpl<I>, FakeBindingsCtxImpl<I>>;
1606
1607    // An example `into_bindings_event` callback to be provided to
1608    // `initialize_duplicate_address_detection()`.
1609    fn into_state_change_event<I: TestDadIpExt>(
1610        state: IpAddressState,
1611    ) -> IpDeviceEvent<FakeDeviceId, I, FakeInstant> {
1612        IpDeviceEvent::AddressStateChanged {
1613            state,
1614            addr: I::DAD_ADDRESS.into(),
1615            device: FakeDeviceId,
1616        }
1617    }
1618
1619    #[ip_test(I)]
1620    #[should_panic(expected = "cannot do DAD for an already assigned address")]
1621    fn panic_non_tentative_address_handle_timer<I: TestDadIpExt>() {
1622        let FakeCtx::<I> { mut core_ctx, mut bindings_ctx } =
1623            FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1624                state: DadState::Assigned { ran_dad: true },
1625                max_dad_transmits: None,
1626                address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1627            }));
1628        TimerHandler::handle_timer(
1629            &mut core_ctx,
1630            &mut bindings_ctx,
1631            dad_timer_id(),
1632            Default::default(),
1633        );
1634    }
1635
1636    #[ip_test(I)]
1637    fn dad_disabled<I: TestDadIpExt>() {
1638        let FakeCtx::<I> { mut core_ctx, mut bindings_ctx } =
1639            FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1640                state: DadState::Uninitialized,
1641                max_dad_transmits: None,
1642                address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1643            }));
1644        let address_id = get_address_id::<I>(I::DAD_ADDRESS);
1645        let start_dad = I::with_dad_handler(&mut core_ctx, |core_ctx| {
1646            core_ctx.initialize_duplicate_address_detection(
1647                &mut bindings_ctx,
1648                &FakeDeviceId,
1649                &address_id,
1650                into_state_change_event::<I>,
1651            )
1652        });
1653        assert_matches!(start_dad, NeedsDad::No);
1654        let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1655        assert_matches!(*state, DadState::Assigned { ran_dad: false });
1656        let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1657        assert!(*assigned);
1658        check_multicast_groups(I::VERSION, groups);
1659        assert_eq!(
1660            bindings_ctx.take_events(),
1661            &[IpDeviceEvent::AddressStateChanged {
1662                state: IpAddressState::Assigned,
1663                device: FakeDeviceId,
1664                addr: I::DAD_ADDRESS.into(),
1665            }][..]
1666        );
1667    }
1668
1669    fn dad_timer_id<I: TestDadIpExt>() -> TestDadTimerId<I> {
1670        DadTimerId {
1671            addr: FakeWeakAddressId(get_address_id::<I>(I::DAD_ADDRESS)),
1672            device_id: FakeWeakDeviceId(FakeDeviceId),
1673            _marker: IpVersionMarker::new(),
1674        }
1675    }
1676
1677    #[track_caller]
1678    fn check_multicast_groups(
1679        ip_version: IpVersion,
1680        groups: &HashMap<MulticastAddr<Ipv6Addr>, usize>,
1681    ) {
1682        match ip_version {
1683            // IPv4 should not join any multicast groups.
1684            IpVersion::V4 => assert_eq!(groups, &HashMap::new()),
1685            // IPv6 should join the solicited node multicast group.
1686            IpVersion::V6 => {
1687                assert_eq!(
1688                    groups,
1689                    &HashMap::from([(Ipv6::DAD_ADDRESS.to_solicited_node_address(), 1)])
1690                )
1691            }
1692        }
1693    }
1694
1695    /// Verify the expected probe was sent while in [`DadState::Tentative`].
1696    fn check_probe_while_tentative<I: TestDadIpExt>(
1697        core_ctx: &FakeCoreCtxImpl<I>,
1698        bindings_ctx: &FakeBindingsCtxImpl<I>,
1699        frames_len: usize,
1700        dad_transmits_remaining: Option<NonZeroU16>,
1701    ) {
1702        let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1703        let (got_transmits_remaining, ip_specific_state, probe_wait) = assert_matches!(
1704            state,
1705            DadState::Tentative {
1706                timer: _,
1707                dad_transmits_remaining,
1708                ip_specific_state,
1709                probe_wait,
1710            } => (dad_transmits_remaining, ip_specific_state, probe_wait)
1711        );
1712        assert_eq!(
1713            *got_transmits_remaining, dad_transmits_remaining,
1714            "got dad_transmits_remaining = {got_transmits_remaining:?},
1715                want dad_transmits_remaining = {dad_transmits_remaining:?}"
1716        );
1717        assert_eq!(probe_wait, &None);
1718        let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1719        assert!(!*assigned);
1720        check_multicast_groups(I::VERSION, groups);
1721
1722        let frames = core_ctx.frames();
1723        assert_eq!(frames.len(), frames_len, "frames = {:?}", frames);
1724
1725        #[derive(GenericOverIp)]
1726        #[generic_over_ip(I, Ip)]
1727        struct Wrap<'a, I: TestDadIpExt> {
1728            meta: &'a I::SendData,
1729            ip_specific_state: &'a I::TentativeState,
1730        }
1731
1732        let (meta, frame) = frames.last().expect("should have transmitted a frame");
1733        assert_eq!(&frame[..], EmptyBuf.as_ref());
1734
1735        // Perform IP specific validation of the sent probe.
1736        I::map_ip::<_, ()>(
1737            Wrap { meta, ip_specific_state },
1738            |Wrap { meta: Ipv4DadSendData { target_ip, probe_type }, ip_specific_state: () }| {
1739                assert_eq!(*target_ip, Ipv4::DAD_ADDRESS.get());
1740                assert_eq!(*probe_type, Ipv4SentProbeType::Probe);
1741            },
1742            |Wrap {
1743                 meta: Ipv6DadSendData { dst_ip, message, nonce },
1744                 ip_specific_state:
1745                     Ipv6TentativeDadState {
1746                         nonces,
1747                         added_extra_transmits_after_detecting_looped_back_ns: _,
1748                     },
1749             }| {
1750                assert_eq!(*dst_ip, Ipv6::DAD_ADDRESS.to_solicited_node_address());
1751                assert_eq!(*message, NeighborSolicitation::new(Ipv6::DAD_ADDRESS.get()));
1752                assert!(nonces.contains(nonce), "should have stored nonce");
1753            },
1754        );
1755
1756        bindings_ctx.timers.assert_timers_installed_range([(
1757            dad_timer_id(),
1758            I::expected_timer_range(bindings_ctx.now()),
1759        )]);
1760    }
1761
1762    // Verify that the probe wait field is properly set after DAD initialization.
1763    fn check_probe_wait<I: TestDadIpExt>(core_ctx: &FakeDadContext<I>) {
1764        let FakeDadContext { state, .. } = core_ctx;
1765        let probe_wait = assert_matches!(
1766            state,
1767            DadState::Tentative { probe_wait, .. } => probe_wait
1768        );
1769        // IPv4 is expected to have a probe wait, while IPv6 should not.
1770        match I::VERSION {
1771            IpVersion::V4 => assert_matches!(probe_wait, Some(_)),
1772            IpVersion::V6 => assert_matches!(probe_wait, None),
1773        }
1774    }
1775
1776    // Skip the probe wait period by triggering the next timer.
1777    fn skip_probe_wait<I: TestDadIpExt>(
1778        core_ctx: &mut FakeCoreCtxImpl<I>,
1779        bindings_ctx: &mut FakeBindingsCtxImpl<I>,
1780    ) {
1781        // IPv4 is expected to have scheduled a probe wait timer, while IPv6
1782        // should not.
1783        match I::VERSION {
1784            IpVersion::V4 => {
1785                assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()))
1786            }
1787            IpVersion::V6 => {}
1788        }
1789    }
1790
1791    // Verify the expected announcement was sent while in [`DadState::Announcing`].
1792    fn check_announcement<I: TestDadIpExt>(
1793        core_ctx: &FakeCoreCtxImpl<I>,
1794        frames_len: usize,
1795        announcements_remaining: Option<NonZeroU16>,
1796    ) {
1797        let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1798        match announcements_remaining {
1799            // If we have no more outstanding announcements, we should have
1800            // entered `DadState::Assigned`.
1801            None => assert_matches!(state, DadState::Assigned { ran_dad: true }),
1802            // Otherwise, we should still be in `DadState::Announcing`.
1803            Some(announcements_remaining) => {
1804                let state = assert_matches!(
1805                    state,
1806                    DadState::Announcing {
1807                        timer: _,
1808                        ip_specific_state,
1809                    } => ip_specific_state
1810                );
1811                #[derive(GenericOverIp)]
1812                #[generic_over_ip(I, Ip)]
1813                struct Wrap<'a, I: DadIpExt>(&'a I::AnnouncingState);
1814                let got_announcements_remaining = I::map_ip_in(
1815                    Wrap(state),
1816                    |Wrap(Ipv4AnnouncingDadState { announcements_remaining })| {
1817                        *announcements_remaining
1818                    },
1819                    |Wrap(never)| match *never {},
1820                );
1821                assert_eq!(
1822                    got_announcements_remaining, announcements_remaining,
1823                    "got announcements_remaining = {got_announcements_remaining:?},
1824                        want announcements_remaining = {announcements_remaining:?}"
1825                );
1826            }
1827        }
1828
1829        let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
1830        assert!(*assigned);
1831
1832        let frames = core_ctx.frames();
1833        assert_eq!(frames.len(), frames_len, "frames = {:?}", frames);
1834        let (meta, frame) = frames.last().expect("should have transmitted a frame");
1835        assert_eq!(&frame[..], EmptyBuf.as_ref());
1836
1837        #[derive(GenericOverIp)]
1838        #[generic_over_ip(I, Ip)]
1839        struct Wrap<'a, I: TestDadIpExt> {
1840            meta: &'a I::SendData,
1841        }
1842
1843        // Perform IP specific validation of the sent probe.
1844        I::map_ip::<_, ()>(
1845            Wrap { meta },
1846            |Wrap { meta: Ipv4DadSendData { target_ip, probe_type } }| {
1847                assert_eq!(*target_ip, Ipv4::DAD_ADDRESS.get());
1848                assert_eq!(*probe_type, Ipv4SentProbeType::Announcement);
1849            },
1850            |_| unreachable!("DAD shouldn't send announcements for IPv6 addresses."),
1851        );
1852    }
1853
1854    #[ip_test(I)]
1855    fn perform_dad<I: TestDadIpExt>() {
1856        const DAD_TRANSMITS_REQUIRED: u16 = 5;
1857
1858        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1859            state: DadState::Uninitialized,
1860            max_dad_transmits: NonZeroU16::new(DAD_TRANSMITS_REQUIRED),
1861            address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1862        }));
1863        let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
1864        let address_id = get_address_id::<I>(I::DAD_ADDRESS);
1865        I::with_dad_handler(core_ctx, |core_ctx| {
1866            let start_dad = core_ctx.initialize_duplicate_address_detection(
1867                bindings_ctx,
1868                &FakeDeviceId,
1869                &address_id,
1870                into_state_change_event::<I>,
1871            );
1872            assert_eq!(
1873                bindings_ctx.take_events(),
1874                &[IpDeviceEvent::AddressStateChanged {
1875                    device: FakeDeviceId,
1876                    addr: I::DAD_ADDRESS.into(),
1877                    state: IpAddressState::Tentative,
1878                }][..]
1879            );
1880            let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
1881            check_probe_wait::<I>(core_ctx.as_ref());
1882            core_ctx.start_duplicate_address_detection(bindings_ctx, token);
1883        });
1884
1885        skip_probe_wait::<I>(core_ctx, bindings_ctx);
1886
1887        for count in 0..=(DAD_TRANSMITS_REQUIRED - 1) {
1888            check_probe_while_tentative(
1889                core_ctx,
1890                bindings_ctx,
1891                usize::from(count + 1),
1892                NonZeroU16::new(DAD_TRANSMITS_REQUIRED - count - 1),
1893            );
1894            assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
1895        }
1896
1897        // The address should be considered assigned once tentative phase
1898        // finishes.
1899        let FakeDadContext { address_ctx, .. } = &core_ctx.state;
1900        let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1901        assert!(*assigned);
1902        check_multicast_groups(I::VERSION, groups);
1903        assert_eq!(
1904            bindings_ctx.take_events(),
1905            &[IpDeviceEvent::AddressStateChanged {
1906                device: FakeDeviceId,
1907                addr: I::DAD_ADDRESS.into(),
1908                state: IpAddressState::Assigned
1909            }][..]
1910        );
1911
1912        for count in 1..=I::EXPECTED_NUM_ANNOUNCEMENTS {
1913            check_announcement(
1914                core_ctx,
1915                usize::from(DAD_TRANSMITS_REQUIRED + count),
1916                NonZeroU16::new(I::EXPECTED_NUM_ANNOUNCEMENTS - count),
1917            );
1918            // There should be a timer installed after all but the final
1919            // announcement.
1920            if count == I::EXPECTED_NUM_ANNOUNCEMENTS {
1921                bindings_ctx.timers.assert_no_timers_installed();
1922            } else {
1923                assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
1924            }
1925        }
1926
1927        let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1928        let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
1929        assert_matches!(*state, DadState::Assigned { ran_dad: true });
1930        assert!(*assigned);
1931    }
1932
1933    #[ip_test(I)]
1934    fn stop_dad_while_tentative<I: TestDadIpExt>() {
1935        const DAD_TRANSMITS_REQUIRED: u16 = 2;
1936
1937        let FakeCtx { mut core_ctx, mut bindings_ctx } =
1938            FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1939                state: DadState::Uninitialized,
1940                max_dad_transmits: NonZeroU16::new(DAD_TRANSMITS_REQUIRED),
1941                address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1942            }));
1943        let address_id = get_address_id::<I>(I::DAD_ADDRESS);
1944        I::with_dad_handler(&mut core_ctx, |core_ctx| {
1945            let start_dad = core_ctx.initialize_duplicate_address_detection(
1946                &mut bindings_ctx,
1947                &FakeDeviceId,
1948                &address_id,
1949                into_state_change_event::<I>,
1950            );
1951            let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
1952            core_ctx.start_duplicate_address_detection(&mut bindings_ctx, token);
1953        });
1954
1955        skip_probe_wait::<I>(&mut core_ctx, &mut bindings_ctx);
1956
1957        check_probe_while_tentative(
1958            &core_ctx,
1959            &bindings_ctx,
1960            1,
1961            NonZeroU16::new(DAD_TRANSMITS_REQUIRED - 1),
1962        );
1963
1964        I::with_dad_handler(&mut core_ctx, |core_ctx| {
1965            core_ctx.stop_duplicate_address_detection(
1966                &mut bindings_ctx,
1967                &FakeDeviceId,
1968                &address_id,
1969            );
1970        });
1971
1972        bindings_ctx.timers.assert_no_timers_installed();
1973        let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1974        assert_matches!(*state, DadState::Uninitialized);
1975        let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1976        assert!(!*assigned);
1977        assert_eq!(groups, &HashMap::new());
1978    }
1979
1980    #[test]
1981    fn stop_dad_while_announcing() {
1982        // Verify that DAD can be properly canceled from the Announcing state.
1983        // Announcing is only supported for IPv4.
1984        let FakeCtx { mut core_ctx, mut bindings_ctx } =
1985            FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1986                state: DadState::Uninitialized,
1987                max_dad_transmits: NonZeroU16::new(1),
1988                address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1989            }));
1990        let address_id = get_address_id::<Ipv4>(Ipv4::DAD_ADDRESS);
1991
1992        // Initialize DAD and fully run through the Tentative state.
1993        let start_dad = core_ctx.initialize_duplicate_address_detection(
1994            &mut bindings_ctx,
1995            &FakeDeviceId,
1996            &address_id,
1997            into_state_change_event::<Ipv4>,
1998        );
1999        let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
2000        core_ctx.start_duplicate_address_detection(&mut bindings_ctx, token);
2001        skip_probe_wait::<Ipv4>(&mut core_ctx, &mut bindings_ctx);
2002        check_probe_while_tentative(
2003            &core_ctx,
2004            &bindings_ctx,
2005            1,
2006            None, /* dad_transmits_remaining */
2007        );
2008        assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(dad_timer_id()));
2009
2010        // Verify we're in the Announcing state.
2011        let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
2012        let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
2013        assert_matches!(*state, DadState::Announcing { .. });
2014        bindings_ctx.timers.assert_timers_installed_range([(dad_timer_id(), ..)]);
2015        assert!(*assigned);
2016
2017        // Stop DAD and verify everything was cleaned up properly.
2018        core_ctx.stop_duplicate_address_detection(&mut bindings_ctx, &FakeDeviceId, &address_id);
2019        bindings_ctx.timers.assert_no_timers_installed();
2020        let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
2021        let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
2022        assert_matches!(*state, DadState::Uninitialized);
2023        assert!(!*assigned);
2024    }
2025
2026    enum IncomingArpPacketCase {
2027        Unavailable,
2028        Assigned { ran_dad: bool, receive_data: Ipv4DadAddressInfo },
2029        Tentative,
2030    }
2031
2032    #[test_case(IncomingArpPacketCase::Unavailable; "uninitialized")]
2033    #[test_case(IncomingArpPacketCase::Tentative; "tentative")]
2034    #[test_case(IncomingArpPacketCase::Assigned{
2035            ran_dad: true, receive_data: Ipv4DadAddressInfo::SourceAddr
2036        }; "assigned_ran_dad_source_addr")]
2037    #[test_case(IncomingArpPacketCase::Assigned {
2038            ran_dad: true, receive_data: Ipv4DadAddressInfo::TargetAddr
2039    }; "assigned_ran_dad_target_addr")]
2040    #[test_case(IncomingArpPacketCase::Assigned {
2041            ran_dad: false, receive_data: Ipv4DadAddressInfo::SourceAddr
2042    }; "assigned_skipped_dad_source_addr")]
2043    #[test_case(IncomingArpPacketCase::Assigned {
2044            ran_dad: false, receive_data: Ipv4DadAddressInfo::TargetAddr
2045    }; "assigned_skipped_dad_target_addr")]
2046    fn handle_incoming_arp_packet(case: IncomingArpPacketCase) {
2047        let mut ctx = FakeCtx::with_default_bindings_ctx(|bindings_ctx| {
2048            let dad_state = match &case {
2049                IncomingArpPacketCase::Unavailable => DadState::Uninitialized,
2050                IncomingArpPacketCase::Assigned { ran_dad, receive_data: _ } => {
2051                    DadState::Assigned { ran_dad: *ran_dad }
2052                }
2053                IncomingArpPacketCase::Tentative => DadState::Tentative {
2054                    dad_transmits_remaining: NonZeroU16::new(1),
2055                    timer: bindings_ctx.new_timer(dad_timer_id()),
2056                    ip_specific_state: Default::default(),
2057                    probe_wait: None,
2058                },
2059            };
2060
2061            FakeCoreCtxImpl::with_state(FakeDadContext {
2062                state: dad_state,
2063                max_dad_transmits: NonZeroU16::new(1),
2064                address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2065            })
2066        });
2067
2068        let (want_lookup_result, optional_receive_data) = match case {
2069            IncomingArpPacketCase::Unavailable => (DadIncomingPacketResult::Uninitialized, None),
2070            IncomingArpPacketCase::Tentative => {
2071                (DadIncomingPacketResult::Tentative { meta: () }, None)
2072            }
2073            IncomingArpPacketCase::Assigned { ran_dad, receive_data } => {
2074                let should_remove = match (ran_dad, &receive_data) {
2075                    (true, Ipv4DadAddressInfo::SourceAddr) => true,
2076                    _ => false,
2077                };
2078                (DadIncomingPacketResult::Assigned { should_remove }, Some(receive_data))
2079            }
2080        };
2081
2082        let addr = get_address_id::<Ipv4>(Ipv4::DAD_ADDRESS);
2083        let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
2084        const ARBITRARY_RECEIVE_DATA: Ipv4DadAddressInfo = Ipv4DadAddressInfo::SourceAddr;
2085        assert_eq!(
2086            DadHandler::<Ipv4, _>::handle_incoming_packet(
2087                core_ctx,
2088                bindings_ctx,
2089                &FakeDeviceId,
2090                &addr,
2091                optional_receive_data.unwrap_or(ARBITRARY_RECEIVE_DATA),
2092            ),
2093            want_lookup_result
2094        );
2095    }
2096
2097    #[test_case(true, None ; "assigned with no incoming nonce")]
2098    #[test_case(true, Some([1u8; MIN_NONCE_LENGTH]) ; "assigned with incoming nonce")]
2099    #[test_case(false, None ; "uninitialized with no incoming nonce")]
2100    #[test_case(false, Some([1u8; MIN_NONCE_LENGTH]) ; "uninitialized with incoming nonce")]
2101    fn handle_incoming_dad_neighbor_solicitation_while_not_tentative(
2102        assigned: bool,
2103        nonce: Option<OwnedNdpNonce>,
2104    ) {
2105        const MAX_DAD_TRANSMITS: u16 = 1;
2106
2107        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
2108            state: if assigned {
2109                DadState::Assigned { ran_dad: true }
2110            } else {
2111                DadState::Uninitialized
2112            },
2113            max_dad_transmits: NonZeroU16::new(MAX_DAD_TRANSMITS),
2114            address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2115        }));
2116        let addr = get_address_id::<Ipv6>(Ipv6::DAD_ADDRESS);
2117
2118        let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
2119
2120        let want_lookup_result = if assigned {
2121            DadIncomingPacketResult::Assigned { should_remove: false }
2122        } else {
2123            DadIncomingPacketResult::Uninitialized
2124        };
2125
2126        assert_eq!(
2127            DadHandler::<Ipv6, _>::handle_incoming_packet(
2128                core_ctx,
2129                bindings_ctx,
2130                &FakeDeviceId,
2131                &addr,
2132                nonce.as_ref().map(NdpNonce::from),
2133            ),
2134            want_lookup_result
2135        );
2136    }
2137
2138    #[test_case(true ; "discards looped back NS")]
2139    #[test_case(false ; "acts on non-looped-back NS")]
2140    fn handle_incoming_dad_neighbor_solicitation_during_tentative(looped_back: bool) {
2141        const DAD_TRANSMITS_REQUIRED: u16 = 1;
2142
2143        let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
2144            state: DadState::Uninitialized,
2145            max_dad_transmits: NonZeroU16::new(DAD_TRANSMITS_REQUIRED),
2146            address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2147        }));
2148        let addr = get_address_id::<Ipv6>(Ipv6::DAD_ADDRESS);
2149
2150        let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
2151        let address_id = get_address_id::<Ipv6>(Ipv6::DAD_ADDRESS);
2152        let start_dad = DadHandler::<Ipv6, _>::initialize_duplicate_address_detection(
2153            core_ctx,
2154            bindings_ctx,
2155            &FakeDeviceId,
2156            &address_id,
2157            into_state_change_event::<Ipv6>,
2158        );
2159        assert_eq!(
2160            bindings_ctx.take_events(),
2161            &[IpDeviceEvent::AddressStateChanged {
2162                device: FakeDeviceId,
2163                addr: Ipv6::DAD_ADDRESS.into(),
2164                state: IpAddressState::Tentative,
2165            }][..]
2166        );
2167        let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
2168        DadHandler::<Ipv6, _>::start_duplicate_address_detection(core_ctx, bindings_ctx, token);
2169
2170        check_probe_while_tentative(core_ctx, bindings_ctx, 1, None);
2171
2172        let sent_nonce: OwnedNdpNonce = {
2173            let (Ipv6DadSendData { dst_ip: _, message: _, nonce }, _frame) =
2174                core_ctx.frames().last().expect("should have transmitted a frame");
2175            *nonce
2176        };
2177
2178        let alternative_nonce = {
2179            let mut nonce = sent_nonce.clone();
2180            nonce[0] = nonce[0].wrapping_add(1);
2181            nonce
2182        };
2183
2184        let incoming_nonce =
2185            NdpNonce::from(if looped_back { &sent_nonce } else { &alternative_nonce });
2186
2187        let matched_nonce = assert_matches!(
2188            DadHandler::<Ipv6, _>::handle_incoming_packet(
2189                core_ctx,
2190                bindings_ctx,
2191                &FakeDeviceId,
2192                &addr,
2193                Some(incoming_nonce),
2194            ),
2195            DadIncomingPacketResult::Tentative {
2196                meta: Ipv6PacketResultMetadata {matched_nonce}
2197            } => matched_nonce
2198        );
2199
2200        assert_eq!(matched_nonce, looped_back);
2201
2202        let frames_len_before_extra_transmits = core_ctx.frames().len();
2203        assert_eq!(frames_len_before_extra_transmits, 1);
2204
2205        let extra_dad_transmits_required =
2206            NonZero::new(if looped_back { DEFAULT_MAX_MULTICAST_SOLICIT.get() } else { 0 });
2207
2208        let (dad_transmits_remaining, added_extra_transmits_after_detecting_looped_back_ns) = assert_matches!(
2209            &core_ctx.state.state,
2210            DadState::Tentative {
2211                dad_transmits_remaining,
2212                timer: _,
2213                ip_specific_state: Ipv6TentativeDadState {
2214                    nonces: _,
2215                    added_extra_transmits_after_detecting_looped_back_ns
2216                },
2217                probe_wait: None,
2218            } => (dad_transmits_remaining, added_extra_transmits_after_detecting_looped_back_ns),
2219            "DAD state should be Tentative"
2220        );
2221
2222        assert_eq!(dad_transmits_remaining, &extra_dad_transmits_required);
2223        assert_eq!(added_extra_transmits_after_detecting_looped_back_ns, &matched_nonce);
2224
2225        let extra_dad_transmits_required =
2226            extra_dad_transmits_required.map(|n| n.get()).unwrap_or(0);
2227
2228        // The retransmit timer should have been kicked when we observed the matching nonce.
2229        assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
2230
2231        // Even though we originally required only 1 DAD transmit, MAX_MULTICAST_SOLICIT more
2232        // should be required as a result of the looped back solicitation.
2233        for count in 0..extra_dad_transmits_required {
2234            check_probe_while_tentative(
2235                core_ctx,
2236                bindings_ctx,
2237                usize::from(count) + frames_len_before_extra_transmits + 1,
2238                NonZeroU16::new(extra_dad_transmits_required - count - 1),
2239            );
2240            assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
2241        }
2242        let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
2243        assert_matches!(*state, DadState::Assigned { ran_dad: true });
2244        let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
2245        assert!(*assigned);
2246        assert_eq!(groups, &HashMap::from([(Ipv6::DAD_ADDRESS.to_solicited_node_address(), 1)]));
2247        assert_eq!(
2248            bindings_ctx.take_events(),
2249            &[IpDeviceEvent::AddressStateChanged {
2250                device: FakeDeviceId,
2251                addr: Ipv6::DAD_ADDRESS.into(),
2252                state: IpAddressState::Assigned
2253            }][..]
2254        );
2255    }
2256
2257    #[test]
2258    fn ipv4_dad_probe_interval_is_valid() {
2259        // Verify that the IPv4 Dad Probe delay is always valid with a handful
2260        // of different RNG seeds.
2261        FakeCryptoRng::with_fake_rngs(100, |mut bindings_ctx| {
2262            let duration = ipv4_dad_probe_interval(&mut bindings_ctx).get();
2263            assert!(IPV4_PROBE_RANGE.contains(&duration), "actual={duration:?}");
2264        })
2265    }
2266
2267    #[test]
2268    fn ipv4_dad_probe_wait_is_valid() {
2269        // Verify that the IPv4 Dad Probe wait is always valid with a handful
2270        // of different RNG seeds.
2271        FakeCryptoRng::with_fake_rngs(100, |mut bindings_ctx| {
2272            let duration = ipv4_dad_probe_wait(&mut bindings_ctx);
2273            assert!(IPV4_PROBE_WAIT_RANGE.contains(&duration), "actual={duration:?}");
2274        })
2275    }
2276}