1use 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
35pub trait DadIpExt: Ip {
37 const DEFAULT_DAD_ENABLED: bool;
40
41 type SendData: Debug;
43 type ReceivedPacketData<'a>;
45 type TentativeState: Debug + Default + Send + Sync;
47 type AnnouncingState: Debug + Send + Sync;
49 type IncomingPacketResultMeta;
51 type RetransmitTimerData;
53
54 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 fn get_retransmission_interval<BC: RngContext>(
63 data: &Self::RetransmitTimerData,
64 bindings_ctx: &mut BC,
65 probes_remaining: &Option<NonZeroU16>,
66 ) -> NonZeroDuration;
67
68 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 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 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 Some(_) => ipv4_dad_probe_interval(bindings_ctx),
129 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 }
143
144 fn handle_incoming_packet_while_assigned<'a>(data: Ipv4DadAddressInfo, ran_dad: bool) -> bool {
145 match IPV4_ADDRESS_DEFENSE_STRATEGY {
146 AddressDefenseStrategy::ForfeitAddress => {
149 ran_dad && data == Ipv4DadAddressInfo::SourceAddr
150 }
151 }
152 }
153}
154
155impl DadIpExt for Ipv6 {
156 const DEFAULT_DAD_ENABLED: bool = true;
162
163 type SendData = Ipv6DadSendData;
164 type ReceivedPacketData<'a> = Option<NdpNonce<&'a [u8]>>;
165 type TentativeState = Ipv6TentativeDadState;
166 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 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 *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 false
224 }
225}
226
227#[derive(Debug, PartialEq)]
229pub enum Ipv4DadAddressInfo {
230 SourceAddr,
232 TargetAddr,
234}
235
236#[derive(Debug, PartialEq)]
238pub enum Ipv4SentProbeType {
239 Probe,
241 Announcement,
243}
244
245#[derive(Debug)]
247pub struct Ipv4DadSendData {
248 target_ip: Ipv4Addr,
250 probe_type: Ipv4SentProbeType,
252}
253
254impl Ipv4DadSendData {
255 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 Ipv4SentProbeType::Probe => Ipv4::UNSPECIFIED_ADDRESS,
262 Ipv4SentProbeType::Announcement => target_ip.clone(),
266 };
267 (sender_ip, target_ip)
271 }
272}
273
274#[derive(Debug)]
276pub struct Ipv6DadSendData {
277 pub dst_ip: MulticastAddr<Ipv6Addr>,
279 pub message: NeighborSolicitation,
281 pub nonce: OwnedNdpNonce,
283}
284
285#[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 #[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
306pub struct DadAddressStateRef<'a, I: DadIpExt, CC, BT: DadBindingsTypes> {
308 pub dad_state: &'a mut DadState<I, BT>,
310 pub core_ctx: &'a mut CC,
312}
313
314pub struct DadStateRef<'a, I: DadIpExt, CC, BT: DadBindingsTypes> {
316 pub state: DadAddressStateRef<'a, I, CC, BT>,
318 pub retrans_timer_data: &'a I::RetransmitTimerData,
320 pub max_dad_transmits: &'a Option<NonZeroU16>,
322}
323
324pub trait DadAddressContext<I: Ip, BC>: IpDeviceAddressIdContext<I> {
326 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 fn should_perform_dad(&mut self, device_id: &Self::DeviceId, addr: &Self::AddressId) -> bool;
338}
339
340pub trait Ipv6DadAddressContext<BC>: DadAddressContext<Ipv6, BC> {
342 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 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
359pub trait DadContext<I: IpDeviceIpExt, BC: DadBindingsTypes>:
361 IpDeviceAddressIdContext<I>
362 + DeviceIdContext<AnyDevice>
363 + CoreTimerContext<DadTimerId<I, Self::WeakDeviceId, Self::WeakAddressId>, BC>
364{
365 type DadAddressCtx<'a>: DadAddressContext<I, BC, DeviceId = Self::DeviceId, AddressId = Self::AddressId>;
367
368 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 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#[derive(Derivative)]
387#[derivative(Debug(bound = ""))]
388pub enum DadState<I: DadIpExt, BT: DadBindingsTypes> {
389 Assigned {
392 ran_dad: bool,
395 },
396
397 Announcing {
405 timer: BT::Timer,
407 ip_specific_state: I::AnnouncingState,
409 },
410
411 Tentative {
415 dad_transmits_remaining: Option<NonZeroU16>,
418 timer: BT::Timer,
420 ip_specific_state: I::TentativeState,
422 probe_wait: Option<Duration>,
424 },
425
426 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 DadState::Announcing { .. } => true,
440 DadState::Tentative { .. } | DadState::Uninitialized => false,
441 }
442 }
443}
444
445#[derive(Debug)]
447pub struct Ipv4AnnouncingDadState {
448 pub announcements_remaining: NonZeroU16,
450}
451
452#[derive(Debug, Default)]
454pub struct Ipv6TentativeDadState {
455 pub nonces: NonceCollection,
459 pub added_extra_transmits_after_detecting_looped_back_ns: bool,
463}
464
465const MAX_DAD_PROBE_NONCES_STORED: usize = 4;
469
470pub type OwnedNdpNonce = [u8; MIN_NONCE_LENGTH];
472
473#[derive(Default, Debug)]
475pub struct NonceCollection {
476 nonces: ArrayVec<OwnedNdpNonce, MAX_DAD_PROBE_NONCES_STORED>,
477}
478
479impl NonceCollection {
480 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 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
509pub trait DadBindingsTypes: TimerBindingsTypes + InstantBindingsTypes {}
511impl<BT> DadBindingsTypes for BT where BT: TimerBindingsTypes + InstantBindingsTypes {}
512
513pub 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
528pub enum DadIncomingPacketResult<I: DadIpExt> {
529 Uninitialized,
531 Tentative { meta: I::IncomingPacketResultMeta },
535 Assigned { should_remove: bool },
540}
541
542#[derive(Debug, PartialEq)]
544pub struct Ipv6PacketResultMetadata {
545 pub(crate) matched_nonce: bool,
549}
550
551pub trait DadHandler<I: IpDeviceIpExt, BC: DadBindingsTypes>:
553 DeviceIdContext<AnyDevice> + IpDeviceAddressIdContext<I>
554{
555 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 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 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 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#[derive(Debug)]
607pub enum NeedsDad<'a, A, D> {
608 No,
609 Yes(StartDad<'a, A, D>),
610}
611
612#[derive(Debug)]
618pub struct StartDad<'a, A, D> {
619 address_id: &'a A,
620 device_id: &'a D,
621}
622
623fn 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 (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 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 on_initialized_cb(core_ctx, bindings_ctx, device_id, addr);
683
684 let address_state = match &needs_dad {
687 NeedsDad::No => IpAddressState::Assigned,
688 NeedsDad::Yes(_) => IpAddressState::Tentative,
689 };
690 bindings_ctx.on_event(into_bindings_event(address_state));
691
692 needs_dad
693 },
694 )
695}
696
697fn do_duplicate_address_detection<
698 I: IpDeviceIpExt,
699 BC: DadBindingsContext<I, CC::DeviceId>,
700 CC: DadContext<I, BC>,
701>(
702 core_ctx: &mut CC,
703 bindings_ctx: &mut BC,
704 device_id: &CC::DeviceId,
705 addr: &CC::AddressId,
706) {
707 let should_send_probe = core_ctx.with_dad_state(
708 device_id,
709 addr,
710 |DadStateRef { state, retrans_timer_data, max_dad_transmits: _ }| {
711 let DadAddressStateRef { dad_state, core_ctx } = state;
712 match dad_state {
713 DadState::Uninitialized => {
714 return None;
717 }
718 DadState::Assigned { .. } => {
719 panic!("cannot do DAD for an already assigned address; addr={addr:?}")
720 }
721 DadState::Tentative {
722 dad_transmits_remaining,
723 timer,
724 ip_specific_state,
725 probe_wait,
726 } => {
727 let (should_send_probe, state_change) = run_tentative_step::<I, CC, BC>(
728 core_ctx,
729 bindings_ctx,
730 device_id,
731 addr,
732 retrans_timer_data,
733 dad_transmits_remaining,
734 timer,
735 ip_specific_state,
736 probe_wait,
737 );
738 match state_change {
739 DadStateChangeFromTentative::None => {}
740 DadStateChangeFromTentative::ToAssigned => {
741 *dad_state = DadState::Assigned { ran_dad: true }
742 }
743 DadStateChangeFromTentative::ToAnnouncing { ip_specific_state } => {
744 let orig = mem::replace(dad_state, DadState::Uninitialized);
749 let timer = assert_matches!(
750 orig,
751 DadState::Tentative{timer, ..} => timer,
752 "state must be tentative"
753 );
754 *dad_state = DadState::Announcing { ip_specific_state, timer }
755 }
756 }
757 should_send_probe
758 }
759 DadState::Announcing { ip_specific_state, timer } => {
760 #[derive(GenericOverIp)]
761 #[generic_over_ip(I, Ip)]
762 struct WrapIn<'a, I: DadIpExt>(&'a mut I::AnnouncingState, I::Addr);
763 #[derive(GenericOverIp)]
764 #[generic_over_ip(I, Ip)]
765 struct WrapOut<I: DadIpExt>(I::SendData, DadStateChangeFromAnnouncing);
766 let WrapOut(send_data, state_change) = I::map_ip(
767 WrapIn(ip_specific_state, addr.addr().addr()),
768 |WrapIn(ipv4_state, ipv4_addr)| {
769 let (send_data, state_change) = run_ipv4_announcing_step(
770 bindings_ctx,
771 device_id,
772 ipv4_addr,
773 ipv4_state,
774 timer,
775 );
776 WrapOut(send_data, state_change)
777 },
778 |WrapIn(ipv6_state, _ipv6_addr)| match *ipv6_state {},
779 );
780 match state_change {
781 DadStateChangeFromAnnouncing::None => {}
782 DadStateChangeFromAnnouncing::ToAssigned => {
783 *dad_state = DadState::Assigned { ran_dad: true }
784 }
785 }
786 Some(send_data)
787 }
788 }
789 },
790 );
791
792 if let Some(probe_send_data) = should_send_probe {
793 core_ctx.send_dad_probe(bindings_ctx, device_id, probe_send_data);
794 }
795}
796
797enum DadStateChangeFromTentative<I: DadIpExt> {
799 None,
800 ToAssigned,
801 ToAnnouncing { ip_specific_state: I::AnnouncingState },
802}
803
804fn run_tentative_step<
810 I: IpDeviceIpExt,
811 CC: DadContext<I, BC>,
812 BC: DadBindingsContext<I, CC::DeviceId>,
813>(
814 core_ctx: &mut CC::DadAddressCtx<'_>,
815 bindings_ctx: &mut BC,
816 device_id: &CC::DeviceId,
817 addr: &CC::AddressId,
818 retrans_timer_data: &I::RetransmitTimerData,
819 dad_transmits_remaining: &mut Option<NonZeroU16>,
820 timer: &mut BC::Timer,
821 ip_specific_state: &mut I::TentativeState,
822 probe_wait: &mut Option<Duration>,
823) -> (Option<I::SendData>, DadStateChangeFromTentative<I>) {
824 if let Some(probe_wait) = probe_wait.take() {
825 schedule_dad_timer(bindings_ctx, timer, probe_wait, addr.addr(), device_id);
828 return (None, DadStateChangeFromTentative::None);
829 }
830
831 match dad_transmits_remaining {
832 None => {
833 #[derive(GenericOverIp)]
834 #[generic_over_ip(I, Ip)]
835 struct WrapOut<I: DadIpExt>(Option<I::SendData>, DadStateChangeFromTentative<I>);
836 let WrapOut(send_data, state_change) = I::map_ip(
837 addr.addr().addr(),
838 |ipv4_addr| {
846 let mut state =
847 Ipv4AnnouncingDadState { announcements_remaining: IPV4_DAD_ANNOUNCE_NUM };
848 let (send_data, state_change) = run_ipv4_announcing_step(
849 bindings_ctx,
850 device_id,
851 ipv4_addr,
852 &mut state,
853 timer,
854 );
855 let state_change = match state_change {
858 DadStateChangeFromAnnouncing::None => {
859 DadStateChangeFromTentative::ToAnnouncing { ip_specific_state: state }
862 }
863 DadStateChangeFromAnnouncing::ToAssigned => {
864 DadStateChangeFromTentative::ToAssigned
869 }
870 };
871 WrapOut(Some(send_data), state_change)
872 },
873 |_ipv6_addr| WrapOut(None, DadStateChangeFromTentative::ToAssigned),
876 );
877
878 core_ctx.with_address_assigned(device_id, addr, |assigned| *assigned = true);
885 bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
886 device: device_id.clone(),
887 addr: addr.addr_sub().addr().into(),
888 state: IpAddressState::Assigned,
889 });
890 return (send_data, state_change);
891 }
892 Some(non_zero_remaining) => {
893 *dad_transmits_remaining = NonZeroU16::new(non_zero_remaining.get() - 1);
894
895 let retrans_interval = I::get_retransmission_interval(
913 retrans_timer_data,
914 bindings_ctx,
915 dad_transmits_remaining,
916 );
917 schedule_dad_timer(bindings_ctx, timer, retrans_interval.get(), addr.addr(), device_id);
918 debug!(
919 "sending DAD probe for {}; {} remaining",
920 addr.addr(),
921 dad_transmits_remaining.map_or(0, NonZeroU16::get)
922 );
923 let send_data =
924 I::generate_sent_probe_data(ip_specific_state, addr.addr().addr(), bindings_ctx);
925 return (Some(send_data), DadStateChangeFromTentative::None);
926 }
927 }
928}
929
930enum DadStateChangeFromAnnouncing {
932 None,
933 ToAssigned,
934}
935
936fn run_ipv4_announcing_step<BC: TimerContext>(
945 bindings_ctx: &mut BC,
946 device_id: impl Debug,
947 addr: Ipv4Addr,
948 ip_specific_state: &mut Ipv4AnnouncingDadState,
949 timer: &mut BC::Timer,
950) -> (Ipv4DadSendData, DadStateChangeFromAnnouncing) {
951 let Ipv4AnnouncingDadState { announcements_remaining } = ip_specific_state;
952
953 let new_announcements_remaining = NonZeroU16::new(announcements_remaining.get() - 1);
954 debug!(
955 "sending DAD announcement for {}; {} remaining",
956 addr,
957 new_announcements_remaining.map_or(0, NonZeroU16::get)
958 );
959
960 let send_data =
961 Ipv4DadSendData { target_ip: addr, probe_type: Ipv4SentProbeType::Announcement };
962
963 let state_change = match new_announcements_remaining {
964 None => DadStateChangeFromAnnouncing::ToAssigned,
966 Some(non_zero_announcements_remaining) => {
967 *announcements_remaining = non_zero_announcements_remaining;
970 schedule_dad_timer(bindings_ctx, timer, IPV4_ANNOUNCE_INTERVAL.get(), addr, device_id);
971 DadStateChangeFromAnnouncing::None
972 }
973 };
974 (send_data, state_change)
975}
976
977fn schedule_dad_timer<BC: TimerContext>(
979 bindings_ctx: &mut BC,
980 timer: &mut BC::Timer,
981 duration: Duration,
982 addr: impl Debug,
983 device_id: impl Debug,
984) {
985 assert_eq!(
986 bindings_ctx.schedule_timer(duration, timer),
987 None,
988 "Unexpected DAD timer; addr={:?}, device_id={:?}",
989 addr,
990 device_id
991 );
992}
993
994fn stop_duplicate_address_detection<
996 'a,
997 I: IpDeviceIpExt,
998 BC: DadBindingsContext<I, CC::DeviceId>,
999 CC: DadContext<I, BC>,
1000 F: FnOnce(&mut CC::DadAddressCtx<'_>, &mut BC, &CC::DeviceId, &CC::AddressId),
1001>(
1002 core_ctx: &mut CC,
1003 bindings_ctx: &mut BC,
1004 device_id: &'a CC::DeviceId,
1005 addr: &'a CC::AddressId,
1006 on_stopped_cb: F,
1007) {
1008 core_ctx.with_dad_state(
1009 device_id,
1010 addr,
1011 |DadStateRef { state, retrans_timer_data: _, max_dad_transmits: _ }| {
1012 let DadAddressStateRef { dad_state, core_ctx } = state;
1013
1014 match dad_state {
1015 DadState::Assigned { .. } => {}
1016 DadState::Announcing { timer, .. } | DadState::Tentative { timer, .. } => {
1017 let _: Option<_> = bindings_ctx.cancel_timer(timer);
1022 }
1023 DadState::Uninitialized => return,
1025 };
1026
1027 *dad_state = DadState::Uninitialized;
1031 core_ctx.with_address_assigned(device_id, addr, |assigned| *assigned = false);
1032
1033 on_stopped_cb(core_ctx, bindings_ctx, device_id, addr)
1036 },
1037 )
1038}
1039
1040fn handle_incoming_packet<
1042 'a,
1043 I: IpDeviceIpExt,
1044 BC: DadBindingsContext<I, CC::DeviceId>,
1045 CC: DadContext<I, BC>,
1046>(
1047 core_ctx: &mut CC,
1048 _bindings_ctx: &mut BC,
1049 device_id: &'a CC::DeviceId,
1050 addr: &'a CC::AddressId,
1051 data: I::ReceivedPacketData<'a>,
1052) -> DadIncomingPacketResult<I> {
1053 core_ctx.with_dad_state(
1054 device_id,
1055 addr,
1056 |DadStateRef { state, retrans_timer_data: _, max_dad_transmits: _ }| {
1057 let DadAddressStateRef { dad_state, core_ctx: _ } = state;
1058 match dad_state {
1059 DadState::Uninitialized => DadIncomingPacketResult::Uninitialized,
1060 DadState::Assigned { ran_dad } => DadIncomingPacketResult::Assigned {
1061 should_remove: I::handle_incoming_packet_while_assigned(data, *ran_dad),
1062 },
1063 DadState::Announcing { .. } => {
1064 let ran_dad = true;
1067 DadIncomingPacketResult::Assigned {
1068 should_remove: I::handle_incoming_packet_while_assigned(data, ran_dad),
1069 }
1070 }
1071 DadState::Tentative {
1072 dad_transmits_remaining,
1073 timer: _,
1074 ip_specific_state,
1075 probe_wait: _,
1076 } => DadIncomingPacketResult::Tentative {
1077 meta: I::handle_incoming_packet_while_tentative(
1078 data,
1079 ip_specific_state,
1080 dad_transmits_remaining,
1081 ),
1082 },
1083 }
1084 },
1085 )
1086}
1087
1088impl<BC: DadBindingsContext<Ipv4, Self::DeviceId>, CC: DadContext<Ipv4, BC>> DadHandler<Ipv4, BC>
1089 for CC
1090{
1091 fn initialize_duplicate_address_detection<
1092 'a,
1093 F: FnOnce(IpAddressState) -> IpDeviceEvent<Self::DeviceId, Ipv4, BC::Instant>,
1094 >(
1095 &mut self,
1096 bindings_ctx: &mut BC,
1097 device_id: &'a Self::DeviceId,
1098 addr: &'a Self::AddressId,
1099 into_bindings_event: F,
1100 ) -> NeedsDad<'a, Self::AddressId, Self::DeviceId> {
1101 initialize_duplicate_address_detection(
1102 self,
1103 bindings_ctx,
1104 device_id,
1105 addr,
1106 into_bindings_event,
1107 |_core_ctx, _bindings_ctx, _device_id, _addr| {},
1109 )
1110 }
1111
1112 fn start_duplicate_address_detection<'a>(
1113 &mut self,
1114 bindings_ctx: &mut BC,
1115 start_dad: StartDad<'_, Self::AddressId, Self::DeviceId>,
1116 ) {
1117 let StartDad { device_id, address_id } = start_dad;
1118 do_duplicate_address_detection(self, bindings_ctx, device_id, address_id)
1119 }
1120
1121 fn stop_duplicate_address_detection(
1122 &mut self,
1123 bindings_ctx: &mut BC,
1124 device_id: &Self::DeviceId,
1125 addr: &Self::AddressId,
1126 ) {
1127 stop_duplicate_address_detection(
1128 self,
1129 bindings_ctx,
1130 device_id,
1131 addr,
1132 |_core_ctx, _bindings_ctx, _device_id, _addr| {},
1134 )
1135 }
1136
1137 fn handle_incoming_packet(
1139 &mut self,
1140 bindings_ctx: &mut BC,
1141 device_id: &Self::DeviceId,
1142 addr: &Self::AddressId,
1143 data: Ipv4DadAddressInfo,
1144 ) -> DadIncomingPacketResult<Ipv4> {
1145 handle_incoming_packet(self, bindings_ctx, device_id, addr, data)
1146 }
1147}
1148
1149impl<BC: DadBindingsContext<Ipv6, Self::DeviceId>, CC: DadContext<Ipv6, BC>> DadHandler<Ipv6, BC>
1150 for CC
1151where
1152 for<'a> CC::DadAddressCtx<'a>: Ipv6DadAddressContext<BC>,
1153{
1154 fn initialize_duplicate_address_detection<
1155 'a,
1156 F: FnOnce(IpAddressState) -> IpDeviceEvent<Self::DeviceId, Ipv6, BC::Instant>,
1157 >(
1158 &mut self,
1159 bindings_ctx: &mut BC,
1160 device_id: &'a Self::DeviceId,
1161 addr: &'a Self::AddressId,
1162 into_bindings_event: F,
1163 ) -> NeedsDad<'a, Self::AddressId, Self::DeviceId> {
1164 initialize_duplicate_address_detection(
1165 self,
1166 bindings_ctx,
1167 device_id,
1168 addr,
1169 into_bindings_event,
1170 |core_ctx, bindings_ctx, device_id, addr| {
1172 core_ctx.join_multicast_group(
1188 bindings_ctx,
1189 device_id,
1190 addr.addr().addr().to_solicited_node_address(),
1191 );
1192 },
1193 )
1194 }
1195
1196 fn start_duplicate_address_detection<'a>(
1197 &mut self,
1198 bindings_ctx: &mut BC,
1199 start_dad: StartDad<'_, Self::AddressId, Self::DeviceId>,
1200 ) {
1201 let StartDad { device_id, address_id } = start_dad;
1202 do_duplicate_address_detection(self, bindings_ctx, device_id, address_id)
1203 }
1204
1205 fn stop_duplicate_address_detection(
1206 &mut self,
1207 bindings_ctx: &mut BC,
1208 device_id: &Self::DeviceId,
1209 addr: &Self::AddressId,
1210 ) {
1211 stop_duplicate_address_detection(
1212 self,
1213 bindings_ctx,
1214 device_id,
1215 addr,
1216 |core_ctx, bindings_ctx, device_id, addr| {
1218 core_ctx.leave_multicast_group(
1224 bindings_ctx,
1225 device_id,
1226 addr.addr().addr().to_solicited_node_address(),
1227 );
1228 },
1229 )
1230 }
1231
1232 fn handle_incoming_packet(
1234 &mut self,
1235 bindings_ctx: &mut BC,
1236 device_id: &Self::DeviceId,
1237 addr: &Self::AddressId,
1238 data: Option<NdpNonce<&[u8]>>,
1239 ) -> DadIncomingPacketResult<Ipv6> {
1240 handle_incoming_packet(self, bindings_ctx, device_id, addr, data)
1241 }
1242}
1243
1244impl<I: IpDeviceIpExt, BC: DadBindingsContext<I, CC::DeviceId>, CC: DadContext<I, BC>>
1245 HandleableTimer<CC, BC> for DadTimerId<I, CC::WeakDeviceId, CC::WeakAddressId>
1246{
1247 fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, _: BC::UniqueTimerId) {
1248 let Self { device_id, addr, _marker } = self;
1249 let Some(device_id) = device_id.upgrade() else {
1250 return;
1251 };
1252 let Some(addr_id) = addr.upgrade() else {
1253 return;
1254 };
1255 do_duplicate_address_detection(core_ctx, bindings_ctx, &device_id, &addr_id)
1256 }
1257}
1258
1259const IPV4_PROBE_RANGE: RangeInclusive<Duration> = Duration::from_secs(1)..=Duration::from_secs(2);
1263
1264pub fn ipv4_dad_probe_interval<BC: RngContext>(bindings_ctx: &mut BC) -> NonZeroDuration {
1266 let retrans_interval = bindings_ctx.rng().random_range(IPV4_PROBE_RANGE);
1271 NonZeroDuration::new(retrans_interval).unwrap()
1274}
1275
1276const IPV4_PROBE_WAIT_RANGE: RangeInclusive<Duration> = Duration::ZERO..=Duration::from_secs(1);
1279
1280pub fn ipv4_dad_probe_wait<BC: RngContext>(bindings_ctx: &mut BC) -> Duration {
1282 bindings_ctx.rng().random_range(IPV4_PROBE_WAIT_RANGE)
1287}
1288
1289const IPV4_ANNOUNCE_WAIT: NonZeroDuration = NonZeroDuration::new(Duration::from_secs(2)).unwrap();
1292
1293const IPV4_ANNOUNCE_INTERVAL: NonZeroDuration =
1296 NonZeroDuration::new(Duration::from_secs(2)).unwrap();
1297
1298pub const IPV4_DAD_ANNOUNCE_NUM: NonZeroU16 = NonZeroU16::new(2).unwrap();
1301
1302enum AddressDefenseStrategy {
1324 ForfeitAddress,
1326}
1327
1328const IPV4_ADDRESS_DEFENSE_STRATEGY: AddressDefenseStrategy =
1353 AddressDefenseStrategy::ForfeitAddress;
1354
1355#[cfg(test)]
1356mod tests {
1357 use core::ops::RangeBounds;
1358 use core::time::Duration;
1359
1360 use assert_matches::assert_matches;
1361 use ip_test_macro::ip_test;
1362 use net_types::ip::{AddrSubnet, GenericOverIp, IpAddress as _, IpVersion, Ipv4Addr};
1363 use net_types::{NonMappedAddr, NonMulticastAddr, SpecifiedAddr, UnicastAddr, Witness as _};
1364 use netstack3_base::testutil::{
1365 FakeBindingsCtx, FakeCoreCtx, FakeCryptoRng, FakeDeviceId, FakeInstant,
1366 FakeTimerCtxExt as _, FakeWeakAddressId, FakeWeakDeviceId,
1367 };
1368 use netstack3_base::{
1369 AssignedAddrIpExt, CtxPair, InstantContext as _, Ipv4DeviceAddr, Ipv6DeviceAddr,
1370 SendFrameContext as _, TimerHandler,
1371 };
1372 use netstack3_hashmap::hash_map::{Entry, HashMap};
1373 use packet::EmptyBuf;
1374 use test_case::test_case;
1375
1376 use super::*;
1377
1378 struct FakeDadAddressContext<I: IpDeviceIpExt> {
1379 addr: I::AssignedWitness,
1380 assigned: bool,
1381 groups: HashMap<MulticastAddr<Ipv6Addr>, usize>,
1383 should_perform_dad: bool,
1384 }
1385
1386 trait TestDadIpExt: IpDeviceIpExt {
1387 const DAD_ADDRESS: Self::AssignedWitness;
1388
1389 const DEFAULT_RETRANS_TIMER_DATA: Self::RetransmitTimerData;
1390
1391 const EXPECTED_NUM_ANNOUNCEMENTS: u16;
1392
1393 fn expected_timer_range(now: FakeInstant) -> impl RangeBounds<FakeInstant> + Debug;
1396
1397 type DadHandlerCtx: DadHandler<
1398 Self,
1399 FakeBindingsCtxImpl<Self>,
1400 DeviceId = FakeDeviceId,
1401 AddressId = AddrSubnet<Self::Addr, Self::AssignedWitness>,
1402 > + AsRef<FakeDadContext<Self>>;
1403
1404 fn with_dad_handler<O, F: FnOnce(&mut Self::DadHandlerCtx) -> O>(
1409 core_ctx: &mut FakeCoreCtxImpl<Self>,
1410 cb: F,
1411 ) -> O;
1412 }
1413
1414 impl TestDadIpExt for Ipv4 {
1415 const DAD_ADDRESS: Ipv4DeviceAddr = unsafe {
1416 NonMulticastAddr::new_unchecked(NonMappedAddr::new_unchecked(
1417 SpecifiedAddr::new_unchecked(Ipv4Addr::new([192, 168, 0, 1])),
1418 ))
1419 };
1420
1421 const DEFAULT_RETRANS_TIMER_DATA: () = ();
1422
1423 const EXPECTED_NUM_ANNOUNCEMENTS: u16 = IPV4_DAD_ANNOUNCE_NUM.get();
1424
1425 fn expected_timer_range(now: FakeInstant) -> impl RangeBounds<FakeInstant> + Debug {
1426 let FakeInstant { offset } = now;
1427 FakeInstant::from(offset + *IPV4_PROBE_RANGE.start())
1428 ..=FakeInstant::from(offset + *IPV4_PROBE_RANGE.end())
1429 }
1430
1431 type DadHandlerCtx = FakeCoreCtxImpl<Ipv4>;
1432
1433 fn with_dad_handler<O, F: FnOnce(&mut FakeCoreCtxImpl<Ipv4>) -> O>(
1434 core_ctx: &mut FakeCoreCtxImpl<Ipv4>,
1435 cb: F,
1436 ) -> O {
1437 cb(core_ctx)
1438 }
1439 }
1440
1441 impl TestDadIpExt for Ipv6 {
1442 const DAD_ADDRESS: Ipv6DeviceAddr = unsafe {
1443 NonMappedAddr::new_unchecked(UnicastAddr::new_unchecked(Ipv6Addr::new([
1444 0xa, 0, 0, 0, 0, 0, 0, 1,
1445 ])))
1446 };
1447
1448 const DEFAULT_RETRANS_TIMER_DATA: NonZeroDuration =
1449 NonZeroDuration::new(Duration::from_secs(1)).unwrap();
1450
1451 const EXPECTED_NUM_ANNOUNCEMENTS: u16 = 0;
1453
1454 fn expected_timer_range(now: FakeInstant) -> impl RangeBounds<FakeInstant> + Debug {
1455 let FakeInstant { offset } = now;
1456 let expected_timer = FakeInstant::from(offset + Self::DEFAULT_RETRANS_TIMER_DATA.get());
1457 expected_timer..=expected_timer
1458 }
1459
1460 type DadHandlerCtx = FakeCoreCtxImpl<Ipv6>;
1461
1462 fn with_dad_handler<O, F: FnOnce(&mut FakeCoreCtxImpl<Ipv6>) -> O>(
1463 core_ctx: &mut FakeCoreCtxImpl<Ipv6>,
1464 cb: F,
1465 ) -> O {
1466 cb(core_ctx)
1467 }
1468 }
1469
1470 impl<I: TestDadIpExt> Default for FakeDadAddressContext<I> {
1471 fn default() -> Self {
1472 Self {
1473 addr: I::DAD_ADDRESS,
1474 assigned: false,
1475 groups: Default::default(),
1476 should_perform_dad: true,
1477 }
1478 }
1479 }
1480
1481 type FakeAddressCtxImpl<I> = FakeCoreCtx<FakeDadAddressContext<I>, (), FakeDeviceId>;
1482
1483 impl<I: IpDeviceIpExt> DadAddressContext<I, FakeBindingsCtxImpl<I>> for FakeAddressCtxImpl<I> {
1484 fn with_address_assigned<O, F: FnOnce(&mut bool) -> O>(
1485 &mut self,
1486 &FakeDeviceId: &Self::DeviceId,
1487 request_addr: &Self::AddressId,
1488 cb: F,
1489 ) -> O {
1490 let FakeDadAddressContext { addr, assigned, .. } = &mut self.state;
1491 assert_eq!(request_addr.addr(), *addr);
1492 cb(assigned)
1493 }
1494
1495 fn should_perform_dad(
1496 &mut self,
1497 &FakeDeviceId: &Self::DeviceId,
1498 request_addr: &Self::AddressId,
1499 ) -> bool {
1500 let FakeDadAddressContext { addr, should_perform_dad, .. } = &mut self.state;
1501 assert_eq!(request_addr.addr(), *addr);
1502 *should_perform_dad
1503 }
1504 }
1505
1506 impl Ipv6DadAddressContext<FakeBindingsCtxImpl<Ipv6>> for FakeAddressCtxImpl<Ipv6> {
1507 fn join_multicast_group(
1508 &mut self,
1509 _bindings_ctx: &mut FakeBindingsCtxImpl<Ipv6>,
1510 &FakeDeviceId: &Self::DeviceId,
1511 multicast_addr: MulticastAddr<Ipv6Addr>,
1512 ) {
1513 *self.state.groups.entry(multicast_addr).or_default() += 1;
1514 }
1515
1516 fn leave_multicast_group(
1517 &mut self,
1518 _bindings_ctx: &mut FakeBindingsCtxImpl<Ipv6>,
1519 &FakeDeviceId: &Self::DeviceId,
1520 multicast_addr: MulticastAddr<Ipv6Addr>,
1521 ) {
1522 match self.state.groups.entry(multicast_addr) {
1523 Entry::Vacant(_) => {}
1524 Entry::Occupied(mut e) => {
1525 let v = e.get_mut();
1526 const COUNT_BEFORE_REMOVE: usize = 1;
1527 if *v == COUNT_BEFORE_REMOVE {
1528 assert_eq!(e.remove(), COUNT_BEFORE_REMOVE);
1529 } else {
1530 *v -= 1
1531 }
1532 }
1533 }
1534 }
1535 }
1536
1537 struct FakeDadContext<I: IpDeviceIpExt> {
1538 state: DadState<I, FakeBindingsCtxImpl<I>>,
1539 max_dad_transmits: Option<NonZeroU16>,
1540 address_ctx: FakeAddressCtxImpl<I>,
1541 }
1542
1543 type TestDadTimerId<I> = DadTimerId<
1544 I,
1545 FakeWeakDeviceId<FakeDeviceId>,
1546 FakeWeakAddressId<AddrSubnet<<I as Ip>::Addr, <I as AssignedAddrIpExt>::AssignedWitness>>,
1547 >;
1548
1549 type FakeBindingsCtxImpl<I> =
1550 FakeBindingsCtx<TestDadTimerId<I>, IpDeviceEvent<FakeDeviceId, I, FakeInstant>, (), ()>;
1551
1552 type FakeCoreCtxImpl<I> =
1553 FakeCoreCtx<FakeDadContext<I>, <I as DadIpExt>::SendData, FakeDeviceId>;
1554
1555 fn get_address_id<I: IpDeviceIpExt>(
1556 addr: I::AssignedWitness,
1557 ) -> AddrSubnet<I::Addr, I::AssignedWitness> {
1558 AddrSubnet::from_witness(addr, I::Addr::BYTES * 8).unwrap()
1559 }
1560
1561 impl<I: IpDeviceIpExt> CoreTimerContext<TestDadTimerId<I>, FakeBindingsCtxImpl<I>>
1562 for FakeCoreCtxImpl<I>
1563 {
1564 fn convert_timer(dispatch_id: TestDadTimerId<I>) -> TestDadTimerId<I> {
1565 dispatch_id
1566 }
1567 }
1568
1569 impl<I: TestDadIpExt> DadContext<I, FakeBindingsCtxImpl<I>> for FakeCoreCtxImpl<I> {
1570 type DadAddressCtx<'a> = FakeAddressCtxImpl<I>;
1571
1572 fn with_dad_state<
1573 O,
1574 F: FnOnce(DadStateRef<'_, I, Self::DadAddressCtx<'_>, FakeBindingsCtxImpl<I>>) -> O,
1575 >(
1576 &mut self,
1577 &FakeDeviceId: &FakeDeviceId,
1578 request_addr: &Self::AddressId,
1579 cb: F,
1580 ) -> O {
1581 let FakeDadContext { state, max_dad_transmits, address_ctx } = &mut self.state;
1582 let ctx_addr = address_ctx.state.addr;
1583 let requested_addr = request_addr.addr();
1584 assert!(
1585 ctx_addr == requested_addr,
1586 "invalid address {requested_addr} expected {ctx_addr}"
1587 );
1588 cb(DadStateRef {
1589 state: DadAddressStateRef { dad_state: state, core_ctx: address_ctx },
1590 retrans_timer_data: &I::DEFAULT_RETRANS_TIMER_DATA,
1591 max_dad_transmits,
1592 })
1593 }
1594
1595 fn send_dad_probe(
1596 &mut self,
1597 bindings_ctx: &mut FakeBindingsCtxImpl<I>,
1598 &FakeDeviceId: &FakeDeviceId,
1599 data: I::SendData,
1600 ) {
1601 self.send_frame(bindings_ctx, data, EmptyBuf).unwrap()
1602 }
1603 }
1604
1605 type FakeCtx<I> = CtxPair<FakeCoreCtxImpl<I>, FakeBindingsCtxImpl<I>>;
1606
1607 fn into_state_change_event<I: TestDadIpExt>(
1610 state: IpAddressState,
1611 ) -> IpDeviceEvent<FakeDeviceId, I, FakeInstant> {
1612 IpDeviceEvent::AddressStateChanged {
1613 state,
1614 addr: I::DAD_ADDRESS.into(),
1615 device: FakeDeviceId,
1616 }
1617 }
1618
1619 #[ip_test(I)]
1620 #[should_panic(expected = "cannot do DAD for an already assigned address")]
1621 fn panic_non_tentative_address_handle_timer<I: TestDadIpExt>() {
1622 let FakeCtx::<I> { mut core_ctx, mut bindings_ctx } =
1623 FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1624 state: DadState::Assigned { ran_dad: true },
1625 max_dad_transmits: None,
1626 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1627 }));
1628 TimerHandler::handle_timer(
1629 &mut core_ctx,
1630 &mut bindings_ctx,
1631 dad_timer_id(),
1632 Default::default(),
1633 );
1634 }
1635
1636 #[ip_test(I)]
1637 fn dad_disabled<I: TestDadIpExt>() {
1638 let FakeCtx::<I> { mut core_ctx, mut bindings_ctx } =
1639 FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1640 state: DadState::Uninitialized,
1641 max_dad_transmits: None,
1642 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1643 }));
1644 let address_id = get_address_id::<I>(I::DAD_ADDRESS);
1645 let start_dad = I::with_dad_handler(&mut core_ctx, |core_ctx| {
1646 core_ctx.initialize_duplicate_address_detection(
1647 &mut bindings_ctx,
1648 &FakeDeviceId,
1649 &address_id,
1650 into_state_change_event::<I>,
1651 )
1652 });
1653 assert_matches!(start_dad, NeedsDad::No);
1654 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1655 assert_matches!(*state, DadState::Assigned { ran_dad: false });
1656 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1657 assert!(*assigned);
1658 check_multicast_groups(I::VERSION, groups);
1659 assert_eq!(
1660 bindings_ctx.take_events(),
1661 &[IpDeviceEvent::AddressStateChanged {
1662 state: IpAddressState::Assigned,
1663 device: FakeDeviceId,
1664 addr: I::DAD_ADDRESS.into(),
1665 }][..]
1666 );
1667 }
1668
1669 fn dad_timer_id<I: TestDadIpExt>() -> TestDadTimerId<I> {
1670 DadTimerId {
1671 addr: FakeWeakAddressId(get_address_id::<I>(I::DAD_ADDRESS)),
1672 device_id: FakeWeakDeviceId(FakeDeviceId),
1673 _marker: IpVersionMarker::new(),
1674 }
1675 }
1676
1677 #[track_caller]
1678 fn check_multicast_groups(
1679 ip_version: IpVersion,
1680 groups: &HashMap<MulticastAddr<Ipv6Addr>, usize>,
1681 ) {
1682 match ip_version {
1683 IpVersion::V4 => assert_eq!(groups, &HashMap::new()),
1685 IpVersion::V6 => {
1687 assert_eq!(
1688 groups,
1689 &HashMap::from([(Ipv6::DAD_ADDRESS.to_solicited_node_address(), 1)])
1690 )
1691 }
1692 }
1693 }
1694
1695 fn check_probe_while_tentative<I: TestDadIpExt>(
1697 core_ctx: &FakeCoreCtxImpl<I>,
1698 bindings_ctx: &FakeBindingsCtxImpl<I>,
1699 frames_len: usize,
1700 dad_transmits_remaining: Option<NonZeroU16>,
1701 ) {
1702 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1703 let (got_transmits_remaining, ip_specific_state, probe_wait) = assert_matches!(
1704 state,
1705 DadState::Tentative {
1706 timer: _,
1707 dad_transmits_remaining,
1708 ip_specific_state,
1709 probe_wait,
1710 } => (dad_transmits_remaining, ip_specific_state, probe_wait)
1711 );
1712 assert_eq!(
1713 *got_transmits_remaining, dad_transmits_remaining,
1714 "got dad_transmits_remaining = {got_transmits_remaining:?},
1715 want dad_transmits_remaining = {dad_transmits_remaining:?}"
1716 );
1717 assert_eq!(probe_wait, &None);
1718 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1719 assert!(!*assigned);
1720 check_multicast_groups(I::VERSION, groups);
1721
1722 let frames = core_ctx.frames();
1723 assert_eq!(frames.len(), frames_len, "frames = {:?}", frames);
1724
1725 #[derive(GenericOverIp)]
1726 #[generic_over_ip(I, Ip)]
1727 struct Wrap<'a, I: TestDadIpExt> {
1728 meta: &'a I::SendData,
1729 ip_specific_state: &'a I::TentativeState,
1730 }
1731
1732 let (meta, frame) = frames.last().expect("should have transmitted a frame");
1733 assert_eq!(&frame[..], EmptyBuf.as_ref());
1734
1735 I::map_ip::<_, ()>(
1737 Wrap { meta, ip_specific_state },
1738 |Wrap { meta: Ipv4DadSendData { target_ip, probe_type }, ip_specific_state: () }| {
1739 assert_eq!(*target_ip, Ipv4::DAD_ADDRESS.get());
1740 assert_eq!(*probe_type, Ipv4SentProbeType::Probe);
1741 },
1742 |Wrap {
1743 meta: Ipv6DadSendData { dst_ip, message, nonce },
1744 ip_specific_state:
1745 Ipv6TentativeDadState {
1746 nonces,
1747 added_extra_transmits_after_detecting_looped_back_ns: _,
1748 },
1749 }| {
1750 assert_eq!(*dst_ip, Ipv6::DAD_ADDRESS.to_solicited_node_address());
1751 assert_eq!(*message, NeighborSolicitation::new(Ipv6::DAD_ADDRESS.get()));
1752 assert!(nonces.contains(nonce), "should have stored nonce");
1753 },
1754 );
1755
1756 bindings_ctx.timers.assert_timers_installed_range([(
1757 dad_timer_id(),
1758 I::expected_timer_range(bindings_ctx.now()),
1759 )]);
1760 }
1761
1762 fn check_probe_wait<I: TestDadIpExt>(core_ctx: &FakeDadContext<I>) {
1764 let FakeDadContext { state, .. } = core_ctx;
1765 let probe_wait = assert_matches!(
1766 state,
1767 DadState::Tentative { probe_wait, .. } => probe_wait
1768 );
1769 match I::VERSION {
1771 IpVersion::V4 => assert_matches!(probe_wait, Some(_)),
1772 IpVersion::V6 => assert_matches!(probe_wait, None),
1773 }
1774 }
1775
1776 fn skip_probe_wait<I: TestDadIpExt>(
1778 core_ctx: &mut FakeCoreCtxImpl<I>,
1779 bindings_ctx: &mut FakeBindingsCtxImpl<I>,
1780 ) {
1781 match I::VERSION {
1784 IpVersion::V4 => {
1785 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()))
1786 }
1787 IpVersion::V6 => {}
1788 }
1789 }
1790
1791 fn check_announcement<I: TestDadIpExt>(
1793 core_ctx: &FakeCoreCtxImpl<I>,
1794 frames_len: usize,
1795 announcements_remaining: Option<NonZeroU16>,
1796 ) {
1797 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1798 match announcements_remaining {
1799 None => assert_matches!(state, DadState::Assigned { ran_dad: true }),
1802 Some(announcements_remaining) => {
1804 let state = assert_matches!(
1805 state,
1806 DadState::Announcing {
1807 timer: _,
1808 ip_specific_state,
1809 } => ip_specific_state
1810 );
1811 #[derive(GenericOverIp)]
1812 #[generic_over_ip(I, Ip)]
1813 struct Wrap<'a, I: DadIpExt>(&'a I::AnnouncingState);
1814 let got_announcements_remaining = I::map_ip_in(
1815 Wrap(state),
1816 |Wrap(Ipv4AnnouncingDadState { announcements_remaining })| {
1817 *announcements_remaining
1818 },
1819 |Wrap(never)| match *never {},
1820 );
1821 assert_eq!(
1822 got_announcements_remaining, announcements_remaining,
1823 "got announcements_remaining = {got_announcements_remaining:?},
1824 want announcements_remaining = {announcements_remaining:?}"
1825 );
1826 }
1827 }
1828
1829 let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
1830 assert!(*assigned);
1831
1832 let frames = core_ctx.frames();
1833 assert_eq!(frames.len(), frames_len, "frames = {:?}", frames);
1834 let (meta, frame) = frames.last().expect("should have transmitted a frame");
1835 assert_eq!(&frame[..], EmptyBuf.as_ref());
1836
1837 #[derive(GenericOverIp)]
1838 #[generic_over_ip(I, Ip)]
1839 struct Wrap<'a, I: TestDadIpExt> {
1840 meta: &'a I::SendData,
1841 }
1842
1843 I::map_ip::<_, ()>(
1845 Wrap { meta },
1846 |Wrap { meta: Ipv4DadSendData { target_ip, probe_type } }| {
1847 assert_eq!(*target_ip, Ipv4::DAD_ADDRESS.get());
1848 assert_eq!(*probe_type, Ipv4SentProbeType::Announcement);
1849 },
1850 |_| unreachable!("DAD shouldn't send announcements for IPv6 addresses."),
1851 );
1852 }
1853
1854 #[ip_test(I)]
1855 fn perform_dad<I: TestDadIpExt>() {
1856 const DAD_TRANSMITS_REQUIRED: u16 = 5;
1857
1858 let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1859 state: DadState::Uninitialized,
1860 max_dad_transmits: NonZeroU16::new(DAD_TRANSMITS_REQUIRED),
1861 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1862 }));
1863 let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
1864 let address_id = get_address_id::<I>(I::DAD_ADDRESS);
1865 I::with_dad_handler(core_ctx, |core_ctx| {
1866 let start_dad = core_ctx.initialize_duplicate_address_detection(
1867 bindings_ctx,
1868 &FakeDeviceId,
1869 &address_id,
1870 into_state_change_event::<I>,
1871 );
1872 assert_eq!(
1873 bindings_ctx.take_events(),
1874 &[IpDeviceEvent::AddressStateChanged {
1875 device: FakeDeviceId,
1876 addr: I::DAD_ADDRESS.into(),
1877 state: IpAddressState::Tentative,
1878 }][..]
1879 );
1880 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
1881 check_probe_wait::<I>(core_ctx.as_ref());
1882 core_ctx.start_duplicate_address_detection(bindings_ctx, token);
1883 });
1884
1885 skip_probe_wait::<I>(core_ctx, bindings_ctx);
1886
1887 for count in 0..=(DAD_TRANSMITS_REQUIRED - 1) {
1888 check_probe_while_tentative(
1889 core_ctx,
1890 bindings_ctx,
1891 usize::from(count + 1),
1892 NonZeroU16::new(DAD_TRANSMITS_REQUIRED - count - 1),
1893 );
1894 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
1895 }
1896
1897 let FakeDadContext { address_ctx, .. } = &core_ctx.state;
1900 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1901 assert!(*assigned);
1902 check_multicast_groups(I::VERSION, groups);
1903 assert_eq!(
1904 bindings_ctx.take_events(),
1905 &[IpDeviceEvent::AddressStateChanged {
1906 device: FakeDeviceId,
1907 addr: I::DAD_ADDRESS.into(),
1908 state: IpAddressState::Assigned
1909 }][..]
1910 );
1911
1912 for count in 1..=I::EXPECTED_NUM_ANNOUNCEMENTS {
1913 check_announcement(
1914 core_ctx,
1915 usize::from(DAD_TRANSMITS_REQUIRED + count),
1916 NonZeroU16::new(I::EXPECTED_NUM_ANNOUNCEMENTS - count),
1917 );
1918 if count == I::EXPECTED_NUM_ANNOUNCEMENTS {
1921 bindings_ctx.timers.assert_no_timers_installed();
1922 } else {
1923 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
1924 }
1925 }
1926
1927 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1928 let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
1929 assert_matches!(*state, DadState::Assigned { ran_dad: true });
1930 assert!(*assigned);
1931 }
1932
1933 #[ip_test(I)]
1934 fn stop_dad_while_tentative<I: TestDadIpExt>() {
1935 const DAD_TRANSMITS_REQUIRED: u16 = 2;
1936
1937 let FakeCtx { mut core_ctx, mut bindings_ctx } =
1938 FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1939 state: DadState::Uninitialized,
1940 max_dad_transmits: NonZeroU16::new(DAD_TRANSMITS_REQUIRED),
1941 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1942 }));
1943 let address_id = get_address_id::<I>(I::DAD_ADDRESS);
1944 I::with_dad_handler(&mut core_ctx, |core_ctx| {
1945 let start_dad = core_ctx.initialize_duplicate_address_detection(
1946 &mut bindings_ctx,
1947 &FakeDeviceId,
1948 &address_id,
1949 into_state_change_event::<I>,
1950 );
1951 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
1952 core_ctx.start_duplicate_address_detection(&mut bindings_ctx, token);
1953 });
1954
1955 skip_probe_wait::<I>(&mut core_ctx, &mut bindings_ctx);
1956
1957 check_probe_while_tentative(
1958 &core_ctx,
1959 &bindings_ctx,
1960 1,
1961 NonZeroU16::new(DAD_TRANSMITS_REQUIRED - 1),
1962 );
1963
1964 I::with_dad_handler(&mut core_ctx, |core_ctx| {
1965 core_ctx.stop_duplicate_address_detection(
1966 &mut bindings_ctx,
1967 &FakeDeviceId,
1968 &address_id,
1969 );
1970 });
1971
1972 bindings_ctx.timers.assert_no_timers_installed();
1973 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1974 assert_matches!(*state, DadState::Uninitialized);
1975 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1976 assert!(!*assigned);
1977 assert_eq!(groups, &HashMap::new());
1978 }
1979
1980 #[test]
1981 fn stop_dad_while_announcing() {
1982 let FakeCtx { mut core_ctx, mut bindings_ctx } =
1985 FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1986 state: DadState::Uninitialized,
1987 max_dad_transmits: NonZeroU16::new(1),
1988 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1989 }));
1990 let address_id = get_address_id::<Ipv4>(Ipv4::DAD_ADDRESS);
1991
1992 let start_dad = core_ctx.initialize_duplicate_address_detection(
1994 &mut bindings_ctx,
1995 &FakeDeviceId,
1996 &address_id,
1997 into_state_change_event::<Ipv4>,
1998 );
1999 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
2000 core_ctx.start_duplicate_address_detection(&mut bindings_ctx, token);
2001 skip_probe_wait::<Ipv4>(&mut core_ctx, &mut bindings_ctx);
2002 check_probe_while_tentative(
2003 &core_ctx,
2004 &bindings_ctx,
2005 1,
2006 None, );
2008 assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(dad_timer_id()));
2009
2010 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
2012 let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
2013 assert_matches!(*state, DadState::Announcing { .. });
2014 bindings_ctx.timers.assert_timers_installed_range([(dad_timer_id(), ..)]);
2015 assert!(*assigned);
2016
2017 core_ctx.stop_duplicate_address_detection(&mut bindings_ctx, &FakeDeviceId, &address_id);
2019 bindings_ctx.timers.assert_no_timers_installed();
2020 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
2021 let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
2022 assert_matches!(*state, DadState::Uninitialized);
2023 assert!(!*assigned);
2024 }
2025
2026 enum IncomingArpPacketCase {
2027 Unavailable,
2028 Assigned { ran_dad: bool, receive_data: Ipv4DadAddressInfo },
2029 Tentative,
2030 }
2031
2032 #[test_case(IncomingArpPacketCase::Unavailable; "uninitialized")]
2033 #[test_case(IncomingArpPacketCase::Tentative; "tentative")]
2034 #[test_case(IncomingArpPacketCase::Assigned{
2035 ran_dad: true, receive_data: Ipv4DadAddressInfo::SourceAddr
2036 }; "assigned_ran_dad_source_addr")]
2037 #[test_case(IncomingArpPacketCase::Assigned {
2038 ran_dad: true, receive_data: Ipv4DadAddressInfo::TargetAddr
2039 }; "assigned_ran_dad_target_addr")]
2040 #[test_case(IncomingArpPacketCase::Assigned {
2041 ran_dad: false, receive_data: Ipv4DadAddressInfo::SourceAddr
2042 }; "assigned_skipped_dad_source_addr")]
2043 #[test_case(IncomingArpPacketCase::Assigned {
2044 ran_dad: false, receive_data: Ipv4DadAddressInfo::TargetAddr
2045 }; "assigned_skipped_dad_target_addr")]
2046 fn handle_incoming_arp_packet(case: IncomingArpPacketCase) {
2047 let mut ctx = FakeCtx::with_default_bindings_ctx(|bindings_ctx| {
2048 let dad_state = match &case {
2049 IncomingArpPacketCase::Unavailable => DadState::Uninitialized,
2050 IncomingArpPacketCase::Assigned { ran_dad, receive_data: _ } => {
2051 DadState::Assigned { ran_dad: *ran_dad }
2052 }
2053 IncomingArpPacketCase::Tentative => DadState::Tentative {
2054 dad_transmits_remaining: NonZeroU16::new(1),
2055 timer: bindings_ctx.new_timer(dad_timer_id()),
2056 ip_specific_state: Default::default(),
2057 probe_wait: None,
2058 },
2059 };
2060
2061 FakeCoreCtxImpl::with_state(FakeDadContext {
2062 state: dad_state,
2063 max_dad_transmits: NonZeroU16::new(1),
2064 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2065 })
2066 });
2067
2068 let (want_lookup_result, optional_receive_data) = match case {
2069 IncomingArpPacketCase::Unavailable => (DadIncomingPacketResult::Uninitialized, None),
2070 IncomingArpPacketCase::Tentative => {
2071 (DadIncomingPacketResult::Tentative { meta: () }, None)
2072 }
2073 IncomingArpPacketCase::Assigned { ran_dad, receive_data } => {
2074 let should_remove = match (ran_dad, &receive_data) {
2075 (true, Ipv4DadAddressInfo::SourceAddr) => true,
2076 _ => false,
2077 };
2078 (DadIncomingPacketResult::Assigned { should_remove }, Some(receive_data))
2079 }
2080 };
2081
2082 let addr = get_address_id::<Ipv4>(Ipv4::DAD_ADDRESS);
2083 let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
2084 const ARBITRARY_RECEIVE_DATA: Ipv4DadAddressInfo = Ipv4DadAddressInfo::SourceAddr;
2085 assert_eq!(
2086 DadHandler::<Ipv4, _>::handle_incoming_packet(
2087 core_ctx,
2088 bindings_ctx,
2089 &FakeDeviceId,
2090 &addr,
2091 optional_receive_data.unwrap_or(ARBITRARY_RECEIVE_DATA),
2092 ),
2093 want_lookup_result
2094 );
2095 }
2096
2097 #[test_case(true, None ; "assigned with no incoming nonce")]
2098 #[test_case(true, Some([1u8; MIN_NONCE_LENGTH]) ; "assigned with incoming nonce")]
2099 #[test_case(false, None ; "uninitialized with no incoming nonce")]
2100 #[test_case(false, Some([1u8; MIN_NONCE_LENGTH]) ; "uninitialized with incoming nonce")]
2101 fn handle_incoming_dad_neighbor_solicitation_while_not_tentative(
2102 assigned: bool,
2103 nonce: Option<OwnedNdpNonce>,
2104 ) {
2105 const MAX_DAD_TRANSMITS: u16 = 1;
2106
2107 let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
2108 state: if assigned {
2109 DadState::Assigned { ran_dad: true }
2110 } else {
2111 DadState::Uninitialized
2112 },
2113 max_dad_transmits: NonZeroU16::new(MAX_DAD_TRANSMITS),
2114 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2115 }));
2116 let addr = get_address_id::<Ipv6>(Ipv6::DAD_ADDRESS);
2117
2118 let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
2119
2120 let want_lookup_result = if assigned {
2121 DadIncomingPacketResult::Assigned { should_remove: false }
2122 } else {
2123 DadIncomingPacketResult::Uninitialized
2124 };
2125
2126 assert_eq!(
2127 DadHandler::<Ipv6, _>::handle_incoming_packet(
2128 core_ctx,
2129 bindings_ctx,
2130 &FakeDeviceId,
2131 &addr,
2132 nonce.as_ref().map(NdpNonce::from),
2133 ),
2134 want_lookup_result
2135 );
2136 }
2137
2138 #[test_case(true ; "discards looped back NS")]
2139 #[test_case(false ; "acts on non-looped-back NS")]
2140 fn handle_incoming_dad_neighbor_solicitation_during_tentative(looped_back: bool) {
2141 const DAD_TRANSMITS_REQUIRED: u16 = 1;
2142
2143 let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
2144 state: DadState::Uninitialized,
2145 max_dad_transmits: NonZeroU16::new(DAD_TRANSMITS_REQUIRED),
2146 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2147 }));
2148 let addr = get_address_id::<Ipv6>(Ipv6::DAD_ADDRESS);
2149
2150 let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
2151 let address_id = get_address_id::<Ipv6>(Ipv6::DAD_ADDRESS);
2152 let start_dad = DadHandler::<Ipv6, _>::initialize_duplicate_address_detection(
2153 core_ctx,
2154 bindings_ctx,
2155 &FakeDeviceId,
2156 &address_id,
2157 into_state_change_event::<Ipv6>,
2158 );
2159 assert_eq!(
2160 bindings_ctx.take_events(),
2161 &[IpDeviceEvent::AddressStateChanged {
2162 device: FakeDeviceId,
2163 addr: Ipv6::DAD_ADDRESS.into(),
2164 state: IpAddressState::Tentative,
2165 }][..]
2166 );
2167 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
2168 DadHandler::<Ipv6, _>::start_duplicate_address_detection(core_ctx, bindings_ctx, token);
2169
2170 check_probe_while_tentative(core_ctx, bindings_ctx, 1, None);
2171
2172 let sent_nonce: OwnedNdpNonce = {
2173 let (Ipv6DadSendData { dst_ip: _, message: _, nonce }, _frame) =
2174 core_ctx.frames().last().expect("should have transmitted a frame");
2175 *nonce
2176 };
2177
2178 let alternative_nonce = {
2179 let mut nonce = sent_nonce.clone();
2180 nonce[0] = nonce[0].wrapping_add(1);
2181 nonce
2182 };
2183
2184 let incoming_nonce =
2185 NdpNonce::from(if looped_back { &sent_nonce } else { &alternative_nonce });
2186
2187 let matched_nonce = assert_matches!(
2188 DadHandler::<Ipv6, _>::handle_incoming_packet(
2189 core_ctx,
2190 bindings_ctx,
2191 &FakeDeviceId,
2192 &addr,
2193 Some(incoming_nonce),
2194 ),
2195 DadIncomingPacketResult::Tentative {
2196 meta: Ipv6PacketResultMetadata {matched_nonce}
2197 } => matched_nonce
2198 );
2199
2200 assert_eq!(matched_nonce, looped_back);
2201
2202 let frames_len_before_extra_transmits = core_ctx.frames().len();
2203 assert_eq!(frames_len_before_extra_transmits, 1);
2204
2205 let extra_dad_transmits_required =
2206 NonZero::new(if looped_back { DEFAULT_MAX_MULTICAST_SOLICIT.get() } else { 0 });
2207
2208 let (dad_transmits_remaining, added_extra_transmits_after_detecting_looped_back_ns) = assert_matches!(
2209 &core_ctx.state.state,
2210 DadState::Tentative {
2211 dad_transmits_remaining,
2212 timer: _,
2213 ip_specific_state: Ipv6TentativeDadState {
2214 nonces: _,
2215 added_extra_transmits_after_detecting_looped_back_ns
2216 },
2217 probe_wait: None,
2218 } => (dad_transmits_remaining, added_extra_transmits_after_detecting_looped_back_ns),
2219 "DAD state should be Tentative"
2220 );
2221
2222 assert_eq!(dad_transmits_remaining, &extra_dad_transmits_required);
2223 assert_eq!(added_extra_transmits_after_detecting_looped_back_ns, &matched_nonce);
2224
2225 let extra_dad_transmits_required =
2226 extra_dad_transmits_required.map(|n| n.get()).unwrap_or(0);
2227
2228 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
2230
2231 for count in 0..extra_dad_transmits_required {
2234 check_probe_while_tentative(
2235 core_ctx,
2236 bindings_ctx,
2237 usize::from(count) + frames_len_before_extra_transmits + 1,
2238 NonZeroU16::new(extra_dad_transmits_required - count - 1),
2239 );
2240 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
2241 }
2242 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
2243 assert_matches!(*state, DadState::Assigned { ran_dad: true });
2244 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
2245 assert!(*assigned);
2246 assert_eq!(groups, &HashMap::from([(Ipv6::DAD_ADDRESS.to_solicited_node_address(), 1)]));
2247 assert_eq!(
2248 bindings_ctx.take_events(),
2249 &[IpDeviceEvent::AddressStateChanged {
2250 device: FakeDeviceId,
2251 addr: Ipv6::DAD_ADDRESS.into(),
2252 state: IpAddressState::Assigned
2253 }][..]
2254 );
2255 }
2256
2257 #[test]
2258 fn ipv4_dad_probe_interval_is_valid() {
2259 FakeCryptoRng::with_fake_rngs(100, |mut bindings_ctx| {
2262 let duration = ipv4_dad_probe_interval(&mut bindings_ctx).get();
2263 assert!(IPV4_PROBE_RANGE.contains(&duration), "actual={duration:?}");
2264 })
2265 }
2266
2267 #[test]
2268 fn ipv4_dad_probe_wait_is_valid() {
2269 FakeCryptoRng::with_fake_rngs(100, |mut bindings_ctx| {
2272 let duration = ipv4_dad_probe_wait(&mut bindings_ctx);
2273 assert!(IPV4_PROBE_WAIT_RANGE.contains(&duration), "actual={duration:?}");
2274 })
2275 }
2276}