Skip to main content

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