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