1use core::fmt::Debug;
8use core::mem;
9use core::num::{NonZero, NonZeroU16};
10use core::ops::RangeInclusive;
11use core::time::Duration;
12
13use arrayvec::ArrayVec;
14use assert_matches::assert_matches;
15use derivative::Derivative;
16use log::debug;
17use net_types::MulticastAddr;
18use net_types::ip::{
19 GenericOverIp, Ip, IpVersion, IpVersionMarker, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr,
20};
21use netstack3_base::{
22 AnyDevice, CoreTimerContext, DeviceIdContext, EventContext, HandleableTimer,
23 InstantBindingsTypes, IpAddressId as _, IpDeviceAddressIdContext, RngContext,
24 StrongDeviceIdentifier as _, TimerBindingsTypes, TimerContext, WeakDeviceIdentifier,
25};
26use packet_formats::icmp::ndp::NeighborSolicitation;
27use packet_formats::icmp::ndp::options::{MIN_NONCE_LENGTH, NdpNonce};
28use packet_formats::utils::NonZeroDuration;
29use rand::RngExt as _;
30
31use crate::internal::device::nud::DEFAULT_MAX_MULTICAST_SOLICIT;
32use crate::internal::device::{IpAddressState, IpDeviceEvent, IpDeviceIpExt, WeakIpAddressId};
33
34pub trait DadIpExt: Ip {
36 const DEFAULT_DAD_ENABLED: bool;
39
40 type SendData: Debug;
42 type ReceivedPacketData<'a>;
44 type TentativeState: Debug + Default + Send + Sync;
46 type AnnouncingState: Debug + Send + Sync;
48 type IncomingPacketResultMeta;
50 type RetransmitTimerData;
52
53 fn generate_sent_probe_data<BC: RngContext>(
55 state: &mut Self::TentativeState,
56 addr: Self::Addr,
57 bindings_ctx: &mut BC,
58 ) -> Self::SendData;
59
60 fn get_retransmission_interval<BC: RngContext>(
62 data: &Self::RetransmitTimerData,
63 bindings_ctx: &mut BC,
64 probes_remaining: &Option<NonZeroU16>,
65 ) -> NonZeroDuration;
66
67 fn handle_incoming_packet_while_tentative<'a>(
69 data: Self::ReceivedPacketData<'a>,
70 state: &mut Self::TentativeState,
71 dad_transmits_remaining: &mut Option<NonZeroU16>,
72 ) -> Self::IncomingPacketResultMeta;
73
74 fn handle_incoming_packet_while_assigned<'a>(
83 data: Self::ReceivedPacketData<'a>,
84 ran_dad: bool,
85 ) -> bool;
86}
87
88impl DadIpExt for Ipv4 {
89 const DEFAULT_DAD_ENABLED: bool = false;
104
105 type SendData = Ipv4DadSendData;
106 type ReceivedPacketData<'a> = Ipv4DadAddressInfo;
107 type TentativeState = ();
108 type AnnouncingState = Ipv4AnnouncingDadState;
109 type IncomingPacketResultMeta = ();
110 type RetransmitTimerData = ();
111
112 fn generate_sent_probe_data<BC: RngContext>(
113 _state: &mut (),
114 addr: Ipv4Addr,
115 _bindings_ctx: &mut BC,
116 ) -> Self::SendData {
117 Ipv4DadSendData { target_ip: addr, probe_type: Ipv4SentProbeType::Probe }
118 }
119
120 fn get_retransmission_interval<BC: RngContext>(
121 _data: &(),
122 bindings_ctx: &mut BC,
123 probes_remaining: &Option<NonZeroU16>,
124 ) -> NonZeroDuration {
125 match probes_remaining {
126 Some(_) => ipv4_dad_probe_interval(bindings_ctx),
128 None => IPV4_ANNOUNCE_WAIT,
130 }
131 }
132
133 fn handle_incoming_packet_while_tentative<'a>(
134 _data: Ipv4DadAddressInfo,
135 _state: &mut (),
136 _dad_transmits_remaining: &mut Option<NonZeroU16>,
137 ) -> () {
138 }
142
143 fn handle_incoming_packet_while_assigned<'a>(data: Ipv4DadAddressInfo, ran_dad: bool) -> bool {
144 match IPV4_ADDRESS_DEFENSE_STRATEGY {
145 AddressDefenseStrategy::ForfeitAddress => {
148 ran_dad && data == Ipv4DadAddressInfo::SourceAddr
149 }
150 }
151 }
152}
153
154impl DadIpExt for Ipv6 {
155 const DEFAULT_DAD_ENABLED: bool = true;
161
162 type SendData = Ipv6DadSendData;
163 type ReceivedPacketData<'a> = Option<NdpNonce<&'a [u8]>>;
164 type TentativeState = Ipv6TentativeDadState;
165 type AnnouncingState = !;
167 type IncomingPacketResultMeta = Ipv6PacketResultMetadata;
168 type RetransmitTimerData = NonZeroDuration;
169
170 fn generate_sent_probe_data<BC: RngContext>(
171 state: &mut Ipv6TentativeDadState,
172 addr: Ipv6Addr,
173 bindings_ctx: &mut BC,
174 ) -> Self::SendData {
175 let Ipv6TentativeDadState {
176 nonces,
177 added_extra_transmits_after_detecting_looped_back_ns: _,
178 } = state;
179 Ipv6DadSendData {
180 dst_ip: addr.to_solicited_node_address(),
181 message: NeighborSolicitation::new(addr),
182 nonce: nonces.evicting_create_and_store_nonce(bindings_ctx.rng()),
183 }
184 }
185
186 fn get_retransmission_interval<BC: RngContext>(
187 data: &NonZeroDuration,
188 _bindings_ctx: &mut BC,
189 _probes_remaining: &Option<NonZeroU16>,
190 ) -> NonZeroDuration {
191 *data
192 }
193
194 fn handle_incoming_packet_while_tentative<'a>(
195 data: Option<NdpNonce<&'a [u8]>>,
196 state: &mut Ipv6TentativeDadState,
197 dad_transmits_remaining: &mut Option<NonZeroU16>,
198 ) -> Ipv6PacketResultMetadata {
199 let Ipv6TentativeDadState { nonces, added_extra_transmits_after_detecting_looped_back_ns } =
201 state;
202 let matched_nonce = data.is_some_and(|nonce| nonces.contains(nonce.bytes()));
203 if matched_nonce
204 && !core::mem::replace(added_extra_transmits_after_detecting_looped_back_ns, true)
205 {
206 *dad_transmits_remaining = Some(
209 DEFAULT_MAX_MULTICAST_SOLICIT
210 .saturating_add(dad_transmits_remaining.map(NonZero::get).unwrap_or(0)),
211 );
212 }
213 Ipv6PacketResultMetadata { matched_nonce }
214 }
215
216 fn handle_incoming_packet_while_assigned<'a>(
217 _data: Option<NdpNonce<&'a [u8]>>,
218 _ran_dad: bool,
219 ) -> bool {
220 false
223 }
224}
225
226#[derive(Debug, PartialEq)]
228pub enum Ipv4DadAddressInfo {
229 SourceAddr,
231 TargetAddr,
233}
234
235#[derive(Debug, PartialEq)]
237pub enum Ipv4SentProbeType {
238 Probe,
240 Announcement,
242}
243
244#[derive(Debug)]
246pub struct Ipv4DadSendData {
247 target_ip: Ipv4Addr,
249 probe_type: Ipv4SentProbeType,
251}
252
253impl Ipv4DadSendData {
254 pub fn into_sender_and_target_addr(self) -> (Ipv4Addr, Ipv4Addr) {
256 let Self { target_ip, probe_type } = self;
257 let sender_ip = match probe_type {
258 Ipv4SentProbeType::Probe => Ipv4::UNSPECIFIED_ADDRESS,
261 Ipv4SentProbeType::Announcement => target_ip.clone(),
265 };
266 (sender_ip, target_ip)
270 }
271}
272
273#[derive(Debug)]
275pub struct Ipv6DadSendData {
276 pub dst_ip: MulticastAddr<Ipv6Addr>,
278 pub message: NeighborSolicitation,
280 pub nonce: OwnedNdpNonce,
282}
283
284#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
286pub struct DadTimerId<I: Ip, D: WeakDeviceIdentifier, A: WeakIpAddressId<I::Addr>> {
287 pub(crate) device_id: D,
288 pub(crate) addr: A,
289 _marker: IpVersionMarker<I>,
290}
291
292impl<I: Ip, D: WeakDeviceIdentifier, A: WeakIpAddressId<I::Addr>> DadTimerId<I, D, A> {
293 pub(super) fn device_id(&self) -> &D {
294 let Self { device_id, addr: _, _marker } = self;
295 device_id
296 }
297
298 #[cfg(any(test, feature = "testutils"))]
300 pub fn new(device_id: D, addr: A) -> Self {
301 Self { device_id, addr, _marker: IpVersionMarker::new() }
302 }
303}
304
305pub struct DadAddressStateRef<'a, I: DadIpExt, CC, BT: DadBindingsTypes> {
307 pub dad_state: &'a mut DadState<I, BT>,
309 pub core_ctx: &'a mut CC,
311}
312
313pub struct DadStateRef<'a, I: DadIpExt, CC, BT: DadBindingsTypes> {
315 pub state: DadAddressStateRef<'a, I, CC, BT>,
317 pub retrans_timer_data: &'a I::RetransmitTimerData,
319 pub max_dad_transmits: &'a Option<NonZeroU16>,
321}
322
323pub trait DadAddressContext<I: Ip, BC>: IpDeviceAddressIdContext<I> {
325 fn with_address_assigned<O, F: FnOnce(&mut bool) -> O>(
328 &mut self,
329 device_id: &Self::DeviceId,
330 addr: &Self::AddressId,
331 cb: F,
332 ) -> O;
333
334 fn should_perform_dad(&mut self, device_id: &Self::DeviceId, addr: &Self::AddressId) -> bool;
337}
338
339pub trait Ipv6DadAddressContext<BC>: DadAddressContext<Ipv6, BC> {
341 fn join_multicast_group(
343 &mut self,
344 bindings_ctx: &mut BC,
345 device_id: &Self::DeviceId,
346 multicast_addr: MulticastAddr<Ipv6Addr>,
347 );
348
349 fn leave_multicast_group(
351 &mut self,
352 bindings_ctx: &mut BC,
353 device_id: &Self::DeviceId,
354 multicast_addr: MulticastAddr<Ipv6Addr>,
355 );
356}
357
358pub trait DadContext<I: IpDeviceIpExt, BC: DadBindingsTypes>:
360 IpDeviceAddressIdContext<I>
361 + DeviceIdContext<AnyDevice>
362 + CoreTimerContext<DadTimerId<I, Self::WeakDeviceId, Self::WeakAddressId>, BC>
363{
364 type DadAddressCtx<'a>: DadAddressContext<I, BC, DeviceId = Self::DeviceId, AddressId = Self::AddressId>;
366
367 fn with_dad_state<O, F: FnOnce(DadStateRef<'_, I, Self::DadAddressCtx<'_>, BC>) -> O>(
369 &mut self,
370 device_id: &Self::DeviceId,
371 addr: &Self::AddressId,
372 cb: F,
373 ) -> O;
374
375 fn send_dad_probe(
377 &mut self,
378 bindings_ctx: &mut BC,
379 device_id: &Self::DeviceId,
380 data: I::SendData,
381 );
382}
383
384#[derive(Derivative)]
386#[derivative(Debug(bound = ""))]
387pub enum DadState<I: DadIpExt, BT: DadBindingsTypes> {
388 Assigned {
391 ran_dad: bool,
394 },
395
396 Announcing {
404 timer: BT::Timer,
406 ip_specific_state: I::AnnouncingState,
408 },
409
410 Tentative {
414 dad_transmits_remaining: Option<NonZeroU16>,
417 timer: BT::Timer,
419 ip_specific_state: I::TentativeState,
421 probe_wait: Option<Duration>,
423 },
424
425 Uninitialized,
427}
428
429impl<I: DadIpExt, BT: DadBindingsTypes> DadState<I, BT> {
430 pub(crate) fn is_assigned(&self) -> bool {
431 match self {
432 DadState::Assigned { .. } => true,
433 DadState::Announcing { .. } => true,
439 DadState::Tentative { .. } | DadState::Uninitialized => false,
440 }
441 }
442}
443
444#[derive(Debug)]
446pub struct Ipv4AnnouncingDadState {
447 pub announcements_remaining: NonZeroU16,
449}
450
451#[derive(Debug, Default)]
453pub struct Ipv6TentativeDadState {
454 pub nonces: NonceCollection,
458 pub added_extra_transmits_after_detecting_looped_back_ns: bool,
462}
463
464const MAX_DAD_PROBE_NONCES_STORED: usize = 4;
468
469pub type OwnedNdpNonce = [u8; MIN_NONCE_LENGTH];
471
472#[derive(Default, Debug)]
474pub struct NonceCollection {
475 nonces: ArrayVec<OwnedNdpNonce, MAX_DAD_PROBE_NONCES_STORED>,
476}
477
478impl NonceCollection {
479 pub fn evicting_create_and_store_nonce(&mut self, mut rng: impl rand::Rng) -> OwnedNdpNonce {
482 let Self { nonces } = self;
483 loop {
484 let nonce: OwnedNdpNonce = rng.random();
485 if nonces.iter().any(|stored_nonce| stored_nonce == &nonce) {
486 continue;
487 }
488
489 if nonces.remaining_capacity() == 0 {
490 let _: OwnedNdpNonce = nonces.remove(0);
491 }
492 nonces.push(nonce.clone());
493 break nonce;
494 }
495 }
496
497 pub fn contains(&self, nonce: &[u8]) -> bool {
499 if nonce.len() != MIN_NONCE_LENGTH {
500 return false;
501 }
502
503 let Self { nonces } = self;
504 nonces.iter().any(|stored_nonce| stored_nonce == &nonce)
505 }
506}
507
508pub trait DadBindingsTypes: TimerBindingsTypes + InstantBindingsTypes {}
510impl<BT> DadBindingsTypes for BT where BT: TimerBindingsTypes + InstantBindingsTypes {}
511
512pub trait DadBindingsContext<I: Ip, D>:
514 DadBindingsTypes + TimerContext + EventContext<IpDeviceEvent<D, I, Self::Instant>> + RngContext
515{
516}
517impl<I: Ip, D, BC> DadBindingsContext<I, D> for BC where
518 BC: DadBindingsTypes
519 + TimerContext
520 + EventContext<IpDeviceEvent<D, I, Self::Instant>>
521 + RngContext
522{
523}
524
525#[derive(Debug, Clone, Copy, PartialEq, Eq)]
527pub enum DadIncomingPacketResult<I: DadIpExt> {
528 Uninitialized,
530 Tentative { meta: I::IncomingPacketResultMeta },
534 Assigned { should_remove: bool },
539}
540
541#[derive(Debug, PartialEq)]
543pub struct Ipv6PacketResultMetadata {
544 pub(crate) matched_nonce: bool,
548}
549
550pub trait DadHandler<I: IpDeviceIpExt, BC: DadBindingsTypes>:
552 DeviceIdContext<AnyDevice> + IpDeviceAddressIdContext<I>
553{
554 fn initialize_duplicate_address_detection<
559 'a,
560 F: FnOnce(IpAddressState) -> IpDeviceEvent<Self::DeviceId, I, BC::Instant>,
561 >(
562 &mut self,
563 bindings_ctx: &mut BC,
564 device_id: &'a Self::DeviceId,
565 addr: &'a Self::AddressId,
566 into_bindings_event: F,
567 ) -> NeedsDad<'a, Self::AddressId, Self::DeviceId>;
568
569 fn start_duplicate_address_detection<'a>(
574 &mut self,
575 bindings_ctx: &mut BC,
576 start_dad: StartDad<'_, Self::AddressId, Self::DeviceId>,
577 );
578
579 fn stop_duplicate_address_detection(
583 &mut self,
584 bindings_ctx: &mut BC,
585 device_id: &Self::DeviceId,
586 addr: &Self::AddressId,
587 );
588
589 fn handle_incoming_packet(
596 &mut self,
597 bindings_ctx: &mut BC,
598 device_id: &Self::DeviceId,
599 addr: &Self::AddressId,
600 data: I::ReceivedPacketData<'_>,
601 ) -> DadIncomingPacketResult<I>;
602}
603
604#[derive(Debug)]
606pub enum NeedsDad<'a, A, D> {
607 No,
608 Yes(StartDad<'a, A, D>),
609}
610
611#[derive(Debug)]
617pub struct StartDad<'a, A, D> {
618 address_id: &'a A,
619 device_id: &'a D,
620}
621
622fn initialize_duplicate_address_detection<
624 'a,
625 I: IpDeviceIpExt,
626 BC: DadBindingsContext<I, CC::DeviceId>,
627 CC: DadContext<I, BC>,
628 F1: FnOnce(IpAddressState) -> IpDeviceEvent<CC::DeviceId, I, BC::Instant>,
629 F2: FnOnce(&mut CC::DadAddressCtx<'_>, &mut BC, &CC::DeviceId, &CC::AddressId),
630>(
631 core_ctx: &mut CC,
632 bindings_ctx: &mut BC,
633 device_id: &'a CC::DeviceId,
634 addr: &'a CC::AddressId,
635 into_bindings_event: F1,
636 on_initialized_cb: F2,
637) -> NeedsDad<'a, CC::AddressId, CC::DeviceId> {
638 core_ctx.with_dad_state(
639 device_id,
640 addr,
641 |DadStateRef { state, retrans_timer_data: _, max_dad_transmits }| {
642 let DadAddressStateRef { dad_state, core_ctx } = state;
643 let needs_dad = match (core_ctx.should_perform_dad(device_id, addr), max_dad_transmits)
644 {
645 (false, _) | (true, None) => {
650 *dad_state = DadState::Assigned { ran_dad: false };
651 core_ctx.with_address_assigned(device_id, addr, |assigned| *assigned = true);
652 NeedsDad::No
653 }
654 (true, Some(max_dad_transmits)) => {
655 let probe_wait = match I::VERSION {
658 IpVersion::V4 => Some(ipv4_dad_probe_wait(bindings_ctx)),
659 IpVersion::V6 => None,
660 };
661 *dad_state = DadState::Tentative {
662 dad_transmits_remaining: Some(*max_dad_transmits),
663 timer: CC::new_timer(
664 bindings_ctx,
665 DadTimerId {
666 device_id: device_id.downgrade(),
667 addr: addr.downgrade(),
668 _marker: IpVersionMarker::new(),
669 },
670 ),
671 ip_specific_state: Default::default(),
672 probe_wait,
673 };
674 core_ctx.with_address_assigned(device_id, addr, |assigned| *assigned = false);
675 NeedsDad::Yes(StartDad { device_id, address_id: addr })
676 }
677 };
678
679 on_initialized_cb(core_ctx, bindings_ctx, device_id, addr);
682
683 let address_state = match &needs_dad {
686 NeedsDad::No => IpAddressState::Assigned,
687 NeedsDad::Yes(_) => IpAddressState::Tentative,
688 };
689 bindings_ctx.on_event(into_bindings_event(address_state));
690
691 needs_dad
692 },
693 )
694}
695
696fn do_duplicate_address_detection<
697 I: IpDeviceIpExt,
698 BC: DadBindingsContext<I, CC::DeviceId>,
699 CC: DadContext<I, BC>,
700>(
701 core_ctx: &mut CC,
702 bindings_ctx: &mut BC,
703 device_id: &CC::DeviceId,
704 addr: &CC::AddressId,
705 timer_id: Option<BC::UniqueTimerId>,
706) {
707 let should_send_probe = core_ctx.with_dad_state(
708 device_id,
709 addr,
710 |DadStateRef { state, retrans_timer_data, max_dad_transmits: _ }| {
711 let DadAddressStateRef { dad_state, core_ctx } = state;
712 match dad_state {
713 DadState::Uninitialized => {
714 return None;
717 }
718 DadState::Assigned { .. } => {
719 panic!("cannot do DAD for an already assigned address; addr={addr:?}")
720 }
721 DadState::Tentative {
722 dad_transmits_remaining,
723 timer,
724 ip_specific_state,
725 probe_wait,
726 } => {
727 if let Some(timer_id) = timer_id
728 && timer_id != bindings_ctx.unique_timer_id(timer)
729 {
730 return None;
733 }
734
735 let (should_send_probe, state_change) = run_tentative_step::<I, CC, BC>(
736 core_ctx,
737 bindings_ctx,
738 device_id,
739 addr,
740 retrans_timer_data,
741 dad_transmits_remaining,
742 timer,
743 ip_specific_state,
744 probe_wait,
745 );
746 match state_change {
747 DadStateChangeFromTentative::None => {}
748 DadStateChangeFromTentative::ToAssigned => {
749 *dad_state = DadState::Assigned { ran_dad: true }
750 }
751 DadStateChangeFromTentative::ToAnnouncing { ip_specific_state } => {
752 let orig = mem::replace(dad_state, DadState::Uninitialized);
757 let timer = assert_matches!(
758 orig,
759 DadState::Tentative{timer, ..} => timer,
760 "state must be tentative"
761 );
762 *dad_state = DadState::Announcing { ip_specific_state, timer }
763 }
764 }
765 should_send_probe
766 }
767 DadState::Announcing { ip_specific_state, timer } => {
768 if let Some(timer_id) = timer_id
769 && timer_id != bindings_ctx.unique_timer_id(timer)
770 {
771 return None;
774 }
775
776 #[derive(GenericOverIp)]
777 #[generic_over_ip(I, Ip)]
778 struct WrapIn<'a, I: DadIpExt>(&'a mut I::AnnouncingState, I::Addr);
779 #[derive(GenericOverIp)]
780 #[generic_over_ip(I, Ip)]
781 struct WrapOut<I: DadIpExt>(I::SendData, DadStateChangeFromAnnouncing);
782 let WrapOut(send_data, state_change) = I::map_ip(
783 WrapIn(ip_specific_state, addr.addr().addr()),
784 |WrapIn(ipv4_state, ipv4_addr)| {
785 let (send_data, state_change) = run_ipv4_announcing_step(
786 bindings_ctx,
787 device_id,
788 ipv4_addr,
789 ipv4_state,
790 timer,
791 );
792 WrapOut(send_data, state_change)
793 },
794 |WrapIn(ipv6_state, _ipv6_addr)| match *ipv6_state {},
795 );
796 match state_change {
797 DadStateChangeFromAnnouncing::None => {}
798 DadStateChangeFromAnnouncing::ToAssigned => {
799 *dad_state = DadState::Assigned { ran_dad: true }
800 }
801 }
802 Some(send_data)
803 }
804 }
805 },
806 );
807
808 if let Some(probe_send_data) = should_send_probe {
809 core_ctx.send_dad_probe(bindings_ctx, device_id, probe_send_data);
810 }
811}
812
813enum DadStateChangeFromTentative<I: DadIpExt> {
815 None,
816 ToAssigned,
817 ToAnnouncing { ip_specific_state: I::AnnouncingState },
818}
819
820fn run_tentative_step<
826 I: IpDeviceIpExt,
827 CC: DadContext<I, BC>,
828 BC: DadBindingsContext<I, CC::DeviceId>,
829>(
830 core_ctx: &mut CC::DadAddressCtx<'_>,
831 bindings_ctx: &mut BC,
832 device_id: &CC::DeviceId,
833 addr: &CC::AddressId,
834 retrans_timer_data: &I::RetransmitTimerData,
835 dad_transmits_remaining: &mut Option<NonZeroU16>,
836 timer: &mut BC::Timer,
837 ip_specific_state: &mut I::TentativeState,
838 probe_wait: &mut Option<Duration>,
839) -> (Option<I::SendData>, DadStateChangeFromTentative<I>) {
840 if let Some(probe_wait) = probe_wait.take() {
841 schedule_dad_timer(bindings_ctx, timer, probe_wait, addr.addr(), device_id);
844 return (None, DadStateChangeFromTentative::None);
845 }
846
847 match dad_transmits_remaining {
848 None => {
849 #[derive(GenericOverIp)]
850 #[generic_over_ip(I, Ip)]
851 struct WrapOut<I: DadIpExt>(Option<I::SendData>, DadStateChangeFromTentative<I>);
852 let WrapOut(send_data, state_change) = I::map_ip(
853 addr.addr().addr(),
854 |ipv4_addr| {
862 let mut state =
863 Ipv4AnnouncingDadState { announcements_remaining: IPV4_DAD_ANNOUNCE_NUM };
864 let (send_data, state_change) = run_ipv4_announcing_step(
865 bindings_ctx,
866 device_id,
867 ipv4_addr,
868 &mut state,
869 timer,
870 );
871 let state_change = match state_change {
874 DadStateChangeFromAnnouncing::None => {
875 DadStateChangeFromTentative::ToAnnouncing { ip_specific_state: state }
878 }
879 DadStateChangeFromAnnouncing::ToAssigned => {
880 DadStateChangeFromTentative::ToAssigned
885 }
886 };
887 WrapOut(Some(send_data), state_change)
888 },
889 |_ipv6_addr| WrapOut(None, DadStateChangeFromTentative::ToAssigned),
892 );
893
894 core_ctx.with_address_assigned(device_id, addr, |assigned| *assigned = true);
901 bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
902 device: device_id.clone(),
903 addr: addr.addr_sub().addr().into(),
904 state: IpAddressState::Assigned,
905 });
906 return (send_data, state_change);
907 }
908 Some(non_zero_remaining) => {
909 *dad_transmits_remaining = NonZeroU16::new(non_zero_remaining.get() - 1);
910
911 let retrans_interval = I::get_retransmission_interval(
929 retrans_timer_data,
930 bindings_ctx,
931 dad_transmits_remaining,
932 );
933 schedule_dad_timer(bindings_ctx, timer, retrans_interval.get(), addr.addr(), device_id);
934 debug!(
935 "sending DAD probe for {}; {} remaining",
936 addr.addr(),
937 dad_transmits_remaining.map_or(0, NonZeroU16::get)
938 );
939 let send_data =
940 I::generate_sent_probe_data(ip_specific_state, addr.addr().addr(), bindings_ctx);
941 return (Some(send_data), DadStateChangeFromTentative::None);
942 }
943 }
944}
945
946enum DadStateChangeFromAnnouncing {
948 None,
949 ToAssigned,
950}
951
952fn run_ipv4_announcing_step<BC: TimerContext>(
961 bindings_ctx: &mut BC,
962 device_id: impl Debug,
963 addr: Ipv4Addr,
964 ip_specific_state: &mut Ipv4AnnouncingDadState,
965 timer: &mut BC::Timer,
966) -> (Ipv4DadSendData, DadStateChangeFromAnnouncing) {
967 let Ipv4AnnouncingDadState { announcements_remaining } = ip_specific_state;
968
969 let new_announcements_remaining = NonZeroU16::new(announcements_remaining.get() - 1);
970 debug!(
971 "sending DAD announcement for {}; {} remaining",
972 addr,
973 new_announcements_remaining.map_or(0, NonZeroU16::get)
974 );
975
976 let send_data =
977 Ipv4DadSendData { target_ip: addr, probe_type: Ipv4SentProbeType::Announcement };
978
979 let state_change = match new_announcements_remaining {
980 None => DadStateChangeFromAnnouncing::ToAssigned,
982 Some(non_zero_announcements_remaining) => {
983 *announcements_remaining = non_zero_announcements_remaining;
986 schedule_dad_timer(bindings_ctx, timer, IPV4_ANNOUNCE_INTERVAL.get(), addr, device_id);
987 DadStateChangeFromAnnouncing::None
988 }
989 };
990 (send_data, state_change)
991}
992
993fn schedule_dad_timer<BC: TimerContext>(
995 bindings_ctx: &mut BC,
996 timer: &mut BC::Timer,
997 duration: Duration,
998 addr: impl Debug,
999 device_id: impl Debug,
1000) {
1001 assert_eq!(
1002 bindings_ctx.schedule_timer(duration, timer),
1003 None,
1004 "Unexpected DAD timer; addr={:?}, device_id={:?}",
1005 addr,
1006 device_id
1007 );
1008}
1009
1010fn stop_duplicate_address_detection<
1012 'a,
1013 I: IpDeviceIpExt,
1014 BC: DadBindingsContext<I, CC::DeviceId>,
1015 CC: DadContext<I, BC>,
1016 F: FnOnce(&mut CC::DadAddressCtx<'_>, &mut BC, &CC::DeviceId, &CC::AddressId),
1017>(
1018 core_ctx: &mut CC,
1019 bindings_ctx: &mut BC,
1020 device_id: &'a CC::DeviceId,
1021 addr: &'a CC::AddressId,
1022 on_stopped_cb: F,
1023) {
1024 core_ctx.with_dad_state(
1025 device_id,
1026 addr,
1027 |DadStateRef { state, retrans_timer_data: _, max_dad_transmits: _ }| {
1028 let DadAddressStateRef { dad_state, core_ctx } = state;
1029
1030 match dad_state {
1031 DadState::Assigned { .. } => {}
1032 DadState::Announcing { timer, .. } | DadState::Tentative { timer, .. } => {
1033 let _: Option<_> = bindings_ctx.cancel_timer(timer);
1038 }
1039 DadState::Uninitialized => return,
1041 };
1042
1043 *dad_state = DadState::Uninitialized;
1047 core_ctx.with_address_assigned(device_id, addr, |assigned| *assigned = false);
1048
1049 on_stopped_cb(core_ctx, bindings_ctx, device_id, addr)
1052 },
1053 )
1054}
1055
1056fn handle_incoming_packet<
1058 'a,
1059 I: IpDeviceIpExt,
1060 BC: DadBindingsContext<I, CC::DeviceId>,
1061 CC: DadContext<I, BC>,
1062>(
1063 core_ctx: &mut CC,
1064 _bindings_ctx: &mut BC,
1065 device_id: &'a CC::DeviceId,
1066 addr: &'a CC::AddressId,
1067 data: I::ReceivedPacketData<'a>,
1068) -> DadIncomingPacketResult<I> {
1069 core_ctx.with_dad_state(
1070 device_id,
1071 addr,
1072 |DadStateRef { state, retrans_timer_data: _, max_dad_transmits: _ }| {
1073 let DadAddressStateRef { dad_state, core_ctx: _ } = state;
1074 match dad_state {
1075 DadState::Uninitialized => DadIncomingPacketResult::Uninitialized,
1076 DadState::Assigned { ran_dad } => DadIncomingPacketResult::Assigned {
1077 should_remove: I::handle_incoming_packet_while_assigned(data, *ran_dad),
1078 },
1079 DadState::Announcing { .. } => {
1080 let ran_dad = true;
1083 DadIncomingPacketResult::Assigned {
1084 should_remove: I::handle_incoming_packet_while_assigned(data, ran_dad),
1085 }
1086 }
1087 DadState::Tentative {
1088 dad_transmits_remaining,
1089 timer: _,
1090 ip_specific_state,
1091 probe_wait: _,
1092 } => DadIncomingPacketResult::Tentative {
1093 meta: I::handle_incoming_packet_while_tentative(
1094 data,
1095 ip_specific_state,
1096 dad_transmits_remaining,
1097 ),
1098 },
1099 }
1100 },
1101 )
1102}
1103
1104impl<BC: DadBindingsContext<Ipv4, Self::DeviceId>, CC: DadContext<Ipv4, BC>> DadHandler<Ipv4, BC>
1105 for CC
1106{
1107 fn initialize_duplicate_address_detection<
1108 'a,
1109 F: FnOnce(IpAddressState) -> IpDeviceEvent<Self::DeviceId, Ipv4, BC::Instant>,
1110 >(
1111 &mut self,
1112 bindings_ctx: &mut BC,
1113 device_id: &'a Self::DeviceId,
1114 addr: &'a Self::AddressId,
1115 into_bindings_event: F,
1116 ) -> NeedsDad<'a, Self::AddressId, Self::DeviceId> {
1117 initialize_duplicate_address_detection(
1118 self,
1119 bindings_ctx,
1120 device_id,
1121 addr,
1122 into_bindings_event,
1123 |_core_ctx, _bindings_ctx, _device_id, _addr| {},
1125 )
1126 }
1127
1128 fn start_duplicate_address_detection<'a>(
1129 &mut self,
1130 bindings_ctx: &mut BC,
1131 start_dad: StartDad<'_, Self::AddressId, Self::DeviceId>,
1132 ) {
1133 let StartDad { device_id, address_id } = start_dad;
1134 do_duplicate_address_detection(self, bindings_ctx, device_id, address_id, None)
1135 }
1136
1137 fn stop_duplicate_address_detection(
1138 &mut self,
1139 bindings_ctx: &mut BC,
1140 device_id: &Self::DeviceId,
1141 addr: &Self::AddressId,
1142 ) {
1143 stop_duplicate_address_detection(
1144 self,
1145 bindings_ctx,
1146 device_id,
1147 addr,
1148 |_core_ctx, _bindings_ctx, _device_id, _addr| {},
1150 )
1151 }
1152
1153 fn handle_incoming_packet(
1155 &mut self,
1156 bindings_ctx: &mut BC,
1157 device_id: &Self::DeviceId,
1158 addr: &Self::AddressId,
1159 data: Ipv4DadAddressInfo,
1160 ) -> DadIncomingPacketResult<Ipv4> {
1161 handle_incoming_packet(self, bindings_ctx, device_id, addr, data)
1162 }
1163}
1164
1165impl<BC: DadBindingsContext<Ipv6, Self::DeviceId>, CC: DadContext<Ipv6, BC>> DadHandler<Ipv6, BC>
1166 for CC
1167where
1168 for<'a> CC::DadAddressCtx<'a>: Ipv6DadAddressContext<BC>,
1169{
1170 fn initialize_duplicate_address_detection<
1171 'a,
1172 F: FnOnce(IpAddressState) -> IpDeviceEvent<Self::DeviceId, Ipv6, BC::Instant>,
1173 >(
1174 &mut self,
1175 bindings_ctx: &mut BC,
1176 device_id: &'a Self::DeviceId,
1177 addr: &'a Self::AddressId,
1178 into_bindings_event: F,
1179 ) -> NeedsDad<'a, Self::AddressId, Self::DeviceId> {
1180 initialize_duplicate_address_detection(
1181 self,
1182 bindings_ctx,
1183 device_id,
1184 addr,
1185 into_bindings_event,
1186 |core_ctx, bindings_ctx, device_id, addr| {
1188 core_ctx.join_multicast_group(
1204 bindings_ctx,
1205 device_id,
1206 addr.addr().addr().to_solicited_node_address(),
1207 );
1208 },
1209 )
1210 }
1211
1212 fn start_duplicate_address_detection<'a>(
1213 &mut self,
1214 bindings_ctx: &mut BC,
1215 start_dad: StartDad<'_, Self::AddressId, Self::DeviceId>,
1216 ) {
1217 let StartDad { device_id, address_id } = start_dad;
1218 do_duplicate_address_detection(self, bindings_ctx, device_id, address_id, None)
1219 }
1220
1221 fn stop_duplicate_address_detection(
1222 &mut self,
1223 bindings_ctx: &mut BC,
1224 device_id: &Self::DeviceId,
1225 addr: &Self::AddressId,
1226 ) {
1227 stop_duplicate_address_detection(
1228 self,
1229 bindings_ctx,
1230 device_id,
1231 addr,
1232 |core_ctx, bindings_ctx, device_id, addr| {
1234 core_ctx.leave_multicast_group(
1240 bindings_ctx,
1241 device_id,
1242 addr.addr().addr().to_solicited_node_address(),
1243 );
1244 },
1245 )
1246 }
1247
1248 fn handle_incoming_packet(
1250 &mut self,
1251 bindings_ctx: &mut BC,
1252 device_id: &Self::DeviceId,
1253 addr: &Self::AddressId,
1254 data: Option<NdpNonce<&[u8]>>,
1255 ) -> DadIncomingPacketResult<Ipv6> {
1256 handle_incoming_packet(self, bindings_ctx, device_id, addr, data)
1257 }
1258}
1259
1260impl<I: IpDeviceIpExt, BC: DadBindingsContext<I, CC::DeviceId>, CC: DadContext<I, BC>>
1261 HandleableTimer<CC, BC> for DadTimerId<I, CC::WeakDeviceId, CC::WeakAddressId>
1262{
1263 fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, timer_id: BC::UniqueTimerId) {
1264 let Self { device_id, addr, _marker } = self;
1265 let Some(device_id) = device_id.upgrade() else {
1266 return;
1267 };
1268 let Some(addr_id) = addr.upgrade() else {
1269 return;
1270 };
1271 do_duplicate_address_detection(core_ctx, bindings_ctx, &device_id, &addr_id, Some(timer_id))
1272 }
1273}
1274
1275const IPV4_PROBE_RANGE: RangeInclusive<Duration> = Duration::from_secs(1)..=Duration::from_secs(2);
1279
1280pub fn ipv4_dad_probe_interval<BC: RngContext>(bindings_ctx: &mut BC) -> NonZeroDuration {
1282 let retrans_interval = bindings_ctx.rng().random_range(IPV4_PROBE_RANGE);
1287 NonZeroDuration::new(retrans_interval).unwrap()
1290}
1291
1292const IPV4_PROBE_WAIT_RANGE: RangeInclusive<Duration> = Duration::ZERO..=Duration::from_secs(1);
1295
1296pub fn ipv4_dad_probe_wait<BC: RngContext>(bindings_ctx: &mut BC) -> Duration {
1298 bindings_ctx.rng().random_range(IPV4_PROBE_WAIT_RANGE)
1303}
1304
1305const IPV4_ANNOUNCE_WAIT: NonZeroDuration = NonZeroDuration::new(Duration::from_secs(2)).unwrap();
1308
1309const IPV4_ANNOUNCE_INTERVAL: NonZeroDuration =
1312 NonZeroDuration::new(Duration::from_secs(2)).unwrap();
1313
1314pub const IPV4_DAD_ANNOUNCE_NUM: NonZeroU16 = NonZeroU16::new(2).unwrap();
1317
1318enum AddressDefenseStrategy {
1340 ForfeitAddress,
1342}
1343
1344const IPV4_ADDRESS_DEFENSE_STRATEGY: AddressDefenseStrategy =
1369 AddressDefenseStrategy::ForfeitAddress;
1370
1371#[cfg(test)]
1372mod tests {
1373 use core::ops::RangeBounds;
1374 use core::time::Duration;
1375
1376 use assert_matches::assert_matches;
1377 use ip_test_macro::ip_test;
1378 use net_types::ip::{AddrSubnet, GenericOverIp, IpAddress as _, IpVersion, Ipv4Addr};
1379 use net_types::{NonMappedAddr, NonMulticastAddr, SpecifiedAddr, UnicastAddr, Witness as _};
1380 use netstack3_base::testutil::{
1381 FakeBindingsCtx, FakeCoreCtx, FakeCryptoRng, FakeDeviceId, FakeInstant,
1382 FakeTimerCtxExt as _, FakeWeakAddressId, FakeWeakDeviceId,
1383 };
1384 use netstack3_base::{
1385 AssignedAddrIpExt, CtxPair, InstantContext as _, Ipv4DeviceAddr, Ipv6DeviceAddr,
1386 SendFrameContext as _, TimerHandler,
1387 };
1388 use netstack3_hashmap::hash_map::{Entry, HashMap};
1389 use packet::EmptyBuf;
1390 use test_case::test_case;
1391
1392 use super::*;
1393
1394 struct FakeDadAddressContext<I: IpDeviceIpExt> {
1395 addr: I::AssignedWitness,
1396 assigned: bool,
1397 groups: HashMap<MulticastAddr<Ipv6Addr>, usize>,
1399 should_perform_dad: bool,
1400 }
1401
1402 trait TestDadIpExt: IpDeviceIpExt {
1403 const DAD_ADDRESS: Self::AssignedWitness;
1404
1405 const DEFAULT_RETRANS_TIMER_DATA: Self::RetransmitTimerData;
1406
1407 const EXPECTED_NUM_ANNOUNCEMENTS: u16;
1408
1409 fn expected_timer_range(now: FakeInstant) -> impl RangeBounds<FakeInstant> + Debug;
1412
1413 type DadHandlerCtx: DadHandler<
1414 Self,
1415 FakeBindingsCtxImpl<Self>,
1416 DeviceId = FakeDeviceId,
1417 AddressId = AddrSubnet<Self::Addr, Self::AssignedWitness>,
1418 > + AsRef<FakeDadContext<Self>>;
1419
1420 fn with_dad_handler<O, F: FnOnce(&mut Self::DadHandlerCtx) -> O>(
1425 core_ctx: &mut FakeCoreCtxImpl<Self>,
1426 cb: F,
1427 ) -> O;
1428 }
1429
1430 impl TestDadIpExt for Ipv4 {
1431 const DAD_ADDRESS: Ipv4DeviceAddr = unsafe {
1432 NonMulticastAddr::new_unchecked(NonMappedAddr::new_unchecked(
1433 SpecifiedAddr::new_unchecked(Ipv4Addr::new([192, 168, 0, 1])),
1434 ))
1435 };
1436
1437 const DEFAULT_RETRANS_TIMER_DATA: () = ();
1438
1439 const EXPECTED_NUM_ANNOUNCEMENTS: u16 = IPV4_DAD_ANNOUNCE_NUM.get();
1440
1441 fn expected_timer_range(now: FakeInstant) -> impl RangeBounds<FakeInstant> + Debug {
1442 let FakeInstant { offset } = now;
1443 FakeInstant::from(offset + *IPV4_PROBE_RANGE.start())
1444 ..=FakeInstant::from(offset + *IPV4_PROBE_RANGE.end())
1445 }
1446
1447 type DadHandlerCtx = FakeCoreCtxImpl<Ipv4>;
1448
1449 fn with_dad_handler<O, F: FnOnce(&mut FakeCoreCtxImpl<Ipv4>) -> O>(
1450 core_ctx: &mut FakeCoreCtxImpl<Ipv4>,
1451 cb: F,
1452 ) -> O {
1453 cb(core_ctx)
1454 }
1455 }
1456
1457 impl TestDadIpExt for Ipv6 {
1458 const DAD_ADDRESS: Ipv6DeviceAddr = unsafe {
1459 NonMappedAddr::new_unchecked(UnicastAddr::new_unchecked(Ipv6Addr::new([
1460 0xa, 0, 0, 0, 0, 0, 0, 1,
1461 ])))
1462 };
1463
1464 const DEFAULT_RETRANS_TIMER_DATA: NonZeroDuration =
1465 NonZeroDuration::new(Duration::from_secs(1)).unwrap();
1466
1467 const EXPECTED_NUM_ANNOUNCEMENTS: u16 = 0;
1469
1470 fn expected_timer_range(now: FakeInstant) -> impl RangeBounds<FakeInstant> + Debug {
1471 let FakeInstant { offset } = now;
1472 let expected_timer = FakeInstant::from(offset + Self::DEFAULT_RETRANS_TIMER_DATA.get());
1473 expected_timer..=expected_timer
1474 }
1475
1476 type DadHandlerCtx = FakeCoreCtxImpl<Ipv6>;
1477
1478 fn with_dad_handler<O, F: FnOnce(&mut FakeCoreCtxImpl<Ipv6>) -> O>(
1479 core_ctx: &mut FakeCoreCtxImpl<Ipv6>,
1480 cb: F,
1481 ) -> O {
1482 cb(core_ctx)
1483 }
1484 }
1485
1486 impl<I: TestDadIpExt> Default for FakeDadAddressContext<I> {
1487 fn default() -> Self {
1488 Self {
1489 addr: I::DAD_ADDRESS,
1490 assigned: false,
1491 groups: Default::default(),
1492 should_perform_dad: true,
1493 }
1494 }
1495 }
1496
1497 type FakeAddressCtxImpl<I> = FakeCoreCtx<FakeDadAddressContext<I>, (), FakeDeviceId>;
1498
1499 impl<I: IpDeviceIpExt> DadAddressContext<I, FakeBindingsCtxImpl<I>> for FakeAddressCtxImpl<I> {
1500 fn with_address_assigned<O, F: FnOnce(&mut bool) -> O>(
1501 &mut self,
1502 &FakeDeviceId: &Self::DeviceId,
1503 request_addr: &Self::AddressId,
1504 cb: F,
1505 ) -> O {
1506 let FakeDadAddressContext { addr, assigned, .. } = &mut self.state;
1507 assert_eq!(request_addr.addr(), *addr);
1508 cb(assigned)
1509 }
1510
1511 fn should_perform_dad(
1512 &mut self,
1513 &FakeDeviceId: &Self::DeviceId,
1514 request_addr: &Self::AddressId,
1515 ) -> bool {
1516 let FakeDadAddressContext { addr, should_perform_dad, .. } = &mut self.state;
1517 assert_eq!(request_addr.addr(), *addr);
1518 *should_perform_dad
1519 }
1520 }
1521
1522 impl Ipv6DadAddressContext<FakeBindingsCtxImpl<Ipv6>> for FakeAddressCtxImpl<Ipv6> {
1523 fn join_multicast_group(
1524 &mut self,
1525 _bindings_ctx: &mut FakeBindingsCtxImpl<Ipv6>,
1526 &FakeDeviceId: &Self::DeviceId,
1527 multicast_addr: MulticastAddr<Ipv6Addr>,
1528 ) {
1529 *self.state.groups.entry(multicast_addr).or_default() += 1;
1530 }
1531
1532 fn leave_multicast_group(
1533 &mut self,
1534 _bindings_ctx: &mut FakeBindingsCtxImpl<Ipv6>,
1535 &FakeDeviceId: &Self::DeviceId,
1536 multicast_addr: MulticastAddr<Ipv6Addr>,
1537 ) {
1538 match self.state.groups.entry(multicast_addr) {
1539 Entry::Vacant(_) => {}
1540 Entry::Occupied(mut e) => {
1541 let v = e.get_mut();
1542 const COUNT_BEFORE_REMOVE: usize = 1;
1543 if *v == COUNT_BEFORE_REMOVE {
1544 assert_eq!(e.remove(), COUNT_BEFORE_REMOVE);
1545 } else {
1546 *v -= 1
1547 }
1548 }
1549 }
1550 }
1551 }
1552
1553 struct FakeDadContext<I: IpDeviceIpExt> {
1554 state: DadState<I, FakeBindingsCtxImpl<I>>,
1555 max_dad_transmits: Option<NonZeroU16>,
1556 address_ctx: FakeAddressCtxImpl<I>,
1557 }
1558
1559 type TestDadTimerId<I> = DadTimerId<
1560 I,
1561 FakeWeakDeviceId<FakeDeviceId>,
1562 FakeWeakAddressId<AddrSubnet<<I as Ip>::Addr, <I as AssignedAddrIpExt>::AssignedWitness>>,
1563 >;
1564
1565 type FakeBindingsCtxImpl<I> =
1566 FakeBindingsCtx<TestDadTimerId<I>, IpDeviceEvent<FakeDeviceId, I, FakeInstant>, (), ()>;
1567
1568 type FakeCoreCtxImpl<I> =
1569 FakeCoreCtx<FakeDadContext<I>, <I as DadIpExt>::SendData, FakeDeviceId>;
1570
1571 fn get_address_id<I: IpDeviceIpExt>(
1572 addr: I::AssignedWitness,
1573 ) -> AddrSubnet<I::Addr, I::AssignedWitness> {
1574 AddrSubnet::from_witness(addr, I::Addr::BYTES * 8).unwrap()
1575 }
1576
1577 impl<I: IpDeviceIpExt> CoreTimerContext<TestDadTimerId<I>, FakeBindingsCtxImpl<I>>
1578 for FakeCoreCtxImpl<I>
1579 {
1580 fn convert_timer(dispatch_id: TestDadTimerId<I>) -> TestDadTimerId<I> {
1581 dispatch_id
1582 }
1583 }
1584
1585 impl<I: TestDadIpExt> DadContext<I, FakeBindingsCtxImpl<I>> for FakeCoreCtxImpl<I> {
1586 type DadAddressCtx<'a> = FakeAddressCtxImpl<I>;
1587
1588 fn with_dad_state<
1589 O,
1590 F: FnOnce(DadStateRef<'_, I, Self::DadAddressCtx<'_>, FakeBindingsCtxImpl<I>>) -> O,
1591 >(
1592 &mut self,
1593 &FakeDeviceId: &FakeDeviceId,
1594 request_addr: &Self::AddressId,
1595 cb: F,
1596 ) -> O {
1597 let FakeDadContext { state, max_dad_transmits, address_ctx } = &mut self.state;
1598 let ctx_addr = address_ctx.state.addr;
1599 let requested_addr = request_addr.addr();
1600 assert!(
1601 ctx_addr == requested_addr,
1602 "invalid address {requested_addr} expected {ctx_addr}"
1603 );
1604 cb(DadStateRef {
1605 state: DadAddressStateRef { dad_state: state, core_ctx: address_ctx },
1606 retrans_timer_data: &I::DEFAULT_RETRANS_TIMER_DATA,
1607 max_dad_transmits,
1608 })
1609 }
1610
1611 fn send_dad_probe(
1612 &mut self,
1613 bindings_ctx: &mut FakeBindingsCtxImpl<I>,
1614 &FakeDeviceId: &FakeDeviceId,
1615 data: I::SendData,
1616 ) {
1617 self.send_frame(bindings_ctx, data, EmptyBuf).unwrap()
1618 }
1619 }
1620
1621 type FakeCtx<I> = CtxPair<FakeCoreCtxImpl<I>, FakeBindingsCtxImpl<I>>;
1622
1623 fn into_state_change_event<I: TestDadIpExt>(
1626 state: IpAddressState,
1627 ) -> IpDeviceEvent<FakeDeviceId, I, FakeInstant> {
1628 IpDeviceEvent::AddressStateChanged {
1629 state,
1630 addr: I::DAD_ADDRESS.into(),
1631 device: FakeDeviceId,
1632 }
1633 }
1634
1635 #[ip_test(I)]
1636 #[should_panic(expected = "cannot do DAD for an already assigned address")]
1637 fn panic_non_tentative_address_handle_timer<I: TestDadIpExt>() {
1638 let FakeCtx::<I> { mut core_ctx, mut bindings_ctx } =
1639 FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1640 state: DadState::Assigned { ran_dad: true },
1641 max_dad_transmits: None,
1642 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1643 }));
1644 TimerHandler::handle_timer(
1645 &mut core_ctx,
1646 &mut bindings_ctx,
1647 dad_timer_id(),
1648 Default::default(),
1649 );
1650 }
1651
1652 #[ip_test(I)]
1653 fn dad_disabled<I: TestDadIpExt>() {
1654 let FakeCtx::<I> { mut core_ctx, mut bindings_ctx } =
1655 FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1656 state: DadState::Uninitialized,
1657 max_dad_transmits: None,
1658 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1659 }));
1660 let address_id = get_address_id::<I>(I::DAD_ADDRESS);
1661 let start_dad = I::with_dad_handler(&mut core_ctx, |core_ctx| {
1662 core_ctx.initialize_duplicate_address_detection(
1663 &mut bindings_ctx,
1664 &FakeDeviceId,
1665 &address_id,
1666 into_state_change_event::<I>,
1667 )
1668 });
1669 assert_matches!(start_dad, NeedsDad::No);
1670 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1671 assert_matches!(*state, DadState::Assigned { ran_dad: false });
1672 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1673 assert!(*assigned);
1674 check_multicast_groups(I::VERSION, groups);
1675 assert_eq!(
1676 bindings_ctx.take_events(),
1677 &[IpDeviceEvent::AddressStateChanged {
1678 state: IpAddressState::Assigned,
1679 device: FakeDeviceId,
1680 addr: I::DAD_ADDRESS.into(),
1681 }][..]
1682 );
1683 }
1684
1685 fn dad_timer_id<I: TestDadIpExt>() -> TestDadTimerId<I> {
1686 DadTimerId {
1687 addr: FakeWeakAddressId(get_address_id::<I>(I::DAD_ADDRESS)),
1688 device_id: FakeWeakDeviceId(FakeDeviceId),
1689 _marker: IpVersionMarker::new(),
1690 }
1691 }
1692
1693 #[track_caller]
1694 fn check_multicast_groups(
1695 ip_version: IpVersion,
1696 groups: &HashMap<MulticastAddr<Ipv6Addr>, usize>,
1697 ) {
1698 match ip_version {
1699 IpVersion::V4 => assert_eq!(groups, &HashMap::new()),
1701 IpVersion::V6 => {
1703 assert_eq!(
1704 groups,
1705 &HashMap::from([(Ipv6::DAD_ADDRESS.to_solicited_node_address(), 1)])
1706 )
1707 }
1708 }
1709 }
1710
1711 fn check_probe_while_tentative<I: TestDadIpExt>(
1713 core_ctx: &FakeCoreCtxImpl<I>,
1714 bindings_ctx: &FakeBindingsCtxImpl<I>,
1715 frames_len: usize,
1716 dad_transmits_remaining: Option<NonZeroU16>,
1717 ) {
1718 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1719 let (got_transmits_remaining, ip_specific_state, probe_wait) = assert_matches!(
1720 state,
1721 DadState::Tentative {
1722 timer: _,
1723 dad_transmits_remaining,
1724 ip_specific_state,
1725 probe_wait,
1726 } => (dad_transmits_remaining, ip_specific_state, probe_wait)
1727 );
1728 assert_eq!(
1729 *got_transmits_remaining, dad_transmits_remaining,
1730 "got dad_transmits_remaining = {got_transmits_remaining:?},
1731 want dad_transmits_remaining = {dad_transmits_remaining:?}"
1732 );
1733 assert_eq!(probe_wait, &None);
1734 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1735 assert!(!*assigned);
1736 check_multicast_groups(I::VERSION, groups);
1737
1738 let frames = core_ctx.frames();
1739 assert_eq!(frames.len(), frames_len, "frames = {:?}", frames);
1740
1741 #[derive(GenericOverIp)]
1742 #[generic_over_ip(I, Ip)]
1743 struct Wrap<'a, I: TestDadIpExt> {
1744 meta: &'a I::SendData,
1745 ip_specific_state: &'a I::TentativeState,
1746 }
1747
1748 let (meta, frame) = frames.last().expect("should have transmitted a frame");
1749 assert_eq!(&frame[..], EmptyBuf.as_ref());
1750
1751 I::map_ip::<_, ()>(
1753 Wrap { meta, ip_specific_state },
1754 |Wrap { meta: Ipv4DadSendData { target_ip, probe_type }, ip_specific_state: () }| {
1755 assert_eq!(*target_ip, Ipv4::DAD_ADDRESS.get());
1756 assert_eq!(*probe_type, Ipv4SentProbeType::Probe);
1757 },
1758 |Wrap {
1759 meta: Ipv6DadSendData { dst_ip, message, nonce },
1760 ip_specific_state:
1761 Ipv6TentativeDadState {
1762 nonces,
1763 added_extra_transmits_after_detecting_looped_back_ns: _,
1764 },
1765 }| {
1766 assert_eq!(*dst_ip, Ipv6::DAD_ADDRESS.to_solicited_node_address());
1767 assert_eq!(*message, NeighborSolicitation::new(Ipv6::DAD_ADDRESS.get()));
1768 assert!(nonces.contains(nonce), "should have stored nonce");
1769 },
1770 );
1771
1772 bindings_ctx.timers.assert_timers_installed_range([(
1773 dad_timer_id(),
1774 I::expected_timer_range(bindings_ctx.now()),
1775 )]);
1776 }
1777
1778 fn check_probe_wait<I: TestDadIpExt>(core_ctx: &FakeDadContext<I>) {
1780 let FakeDadContext { state, .. } = core_ctx;
1781 let probe_wait = assert_matches!(
1782 state,
1783 DadState::Tentative { probe_wait, .. } => probe_wait
1784 );
1785 match I::VERSION {
1787 IpVersion::V4 => assert_matches!(probe_wait, Some(_)),
1788 IpVersion::V6 => assert_matches!(probe_wait, None),
1789 }
1790 }
1791
1792 fn skip_probe_wait<I: TestDadIpExt>(
1794 core_ctx: &mut FakeCoreCtxImpl<I>,
1795 bindings_ctx: &mut FakeBindingsCtxImpl<I>,
1796 ) {
1797 match I::VERSION {
1800 IpVersion::V4 => {
1801 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()))
1802 }
1803 IpVersion::V6 => {}
1804 }
1805 }
1806
1807 fn check_announcement<I: TestDadIpExt>(
1809 core_ctx: &FakeCoreCtxImpl<I>,
1810 frames_len: usize,
1811 announcements_remaining: Option<NonZeroU16>,
1812 ) {
1813 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1814 match announcements_remaining {
1815 None => assert_matches!(state, DadState::Assigned { ran_dad: true }),
1818 Some(announcements_remaining) => {
1820 let state = assert_matches!(
1821 state,
1822 DadState::Announcing {
1823 timer: _,
1824 ip_specific_state,
1825 } => ip_specific_state
1826 );
1827 #[derive(GenericOverIp)]
1828 #[generic_over_ip(I, Ip)]
1829 struct Wrap<'a, I: DadIpExt>(&'a I::AnnouncingState);
1830 let got_announcements_remaining = I::map_ip_in(
1831 Wrap(state),
1832 |Wrap(Ipv4AnnouncingDadState { announcements_remaining })| {
1833 *announcements_remaining
1834 },
1835 |Wrap(never)| match *never {},
1836 );
1837 assert_eq!(
1838 got_announcements_remaining, announcements_remaining,
1839 "got announcements_remaining = {got_announcements_remaining:?},
1840 want announcements_remaining = {announcements_remaining:?}"
1841 );
1842 }
1843 }
1844
1845 let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
1846 assert!(*assigned);
1847
1848 let frames = core_ctx.frames();
1849 assert_eq!(frames.len(), frames_len, "frames = {:?}", frames);
1850 let (meta, frame) = frames.last().expect("should have transmitted a frame");
1851 assert_eq!(&frame[..], EmptyBuf.as_ref());
1852
1853 #[derive(GenericOverIp)]
1854 #[generic_over_ip(I, Ip)]
1855 struct Wrap<'a, I: TestDadIpExt> {
1856 meta: &'a I::SendData,
1857 }
1858
1859 I::map_ip::<_, ()>(
1861 Wrap { meta },
1862 |Wrap { meta: Ipv4DadSendData { target_ip, probe_type } }| {
1863 assert_eq!(*target_ip, Ipv4::DAD_ADDRESS.get());
1864 assert_eq!(*probe_type, Ipv4SentProbeType::Announcement);
1865 },
1866 |_| unreachable!("DAD shouldn't send announcements for IPv6 addresses."),
1867 );
1868 }
1869
1870 #[ip_test(I)]
1871 fn perform_dad<I: TestDadIpExt>() {
1872 const DAD_TRANSMITS_REQUIRED: u16 = 5;
1873
1874 let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1875 state: DadState::Uninitialized,
1876 max_dad_transmits: NonZeroU16::new(DAD_TRANSMITS_REQUIRED),
1877 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1878 }));
1879 let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
1880 let address_id = get_address_id::<I>(I::DAD_ADDRESS);
1881 I::with_dad_handler(core_ctx, |core_ctx| {
1882 let start_dad = core_ctx.initialize_duplicate_address_detection(
1883 bindings_ctx,
1884 &FakeDeviceId,
1885 &address_id,
1886 into_state_change_event::<I>,
1887 );
1888 assert_eq!(
1889 bindings_ctx.take_events(),
1890 &[IpDeviceEvent::AddressStateChanged {
1891 device: FakeDeviceId,
1892 addr: I::DAD_ADDRESS.into(),
1893 state: IpAddressState::Tentative,
1894 }][..]
1895 );
1896 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
1897 check_probe_wait::<I>(core_ctx.as_ref());
1898 core_ctx.start_duplicate_address_detection(bindings_ctx, token);
1899 });
1900
1901 skip_probe_wait::<I>(core_ctx, bindings_ctx);
1902
1903 for count in 0..=(DAD_TRANSMITS_REQUIRED - 1) {
1904 check_probe_while_tentative(
1905 core_ctx,
1906 bindings_ctx,
1907 usize::from(count + 1),
1908 NonZeroU16::new(DAD_TRANSMITS_REQUIRED - count - 1),
1909 );
1910 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
1911 }
1912
1913 let FakeDadContext { address_ctx, .. } = &core_ctx.state;
1916 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1917 assert!(*assigned);
1918 check_multicast_groups(I::VERSION, groups);
1919 assert_eq!(
1920 bindings_ctx.take_events(),
1921 &[IpDeviceEvent::AddressStateChanged {
1922 device: FakeDeviceId,
1923 addr: I::DAD_ADDRESS.into(),
1924 state: IpAddressState::Assigned
1925 }][..]
1926 );
1927
1928 for count in 1..=I::EXPECTED_NUM_ANNOUNCEMENTS {
1929 check_announcement(
1930 core_ctx,
1931 usize::from(DAD_TRANSMITS_REQUIRED + count),
1932 NonZeroU16::new(I::EXPECTED_NUM_ANNOUNCEMENTS - count),
1933 );
1934 if count == I::EXPECTED_NUM_ANNOUNCEMENTS {
1937 bindings_ctx.timers.assert_no_timers_installed();
1938 } else {
1939 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
1940 }
1941 }
1942
1943 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1944 let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
1945 assert_matches!(*state, DadState::Assigned { ran_dad: true });
1946 assert!(*assigned);
1947 }
1948
1949 #[ip_test(I)]
1950 fn stop_dad_while_tentative<I: TestDadIpExt>() {
1951 const DAD_TRANSMITS_REQUIRED: u16 = 2;
1952
1953 let FakeCtx { mut core_ctx, mut bindings_ctx } =
1954 FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1955 state: DadState::Uninitialized,
1956 max_dad_transmits: NonZeroU16::new(DAD_TRANSMITS_REQUIRED),
1957 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1958 }));
1959 let address_id = get_address_id::<I>(I::DAD_ADDRESS);
1960 I::with_dad_handler(&mut core_ctx, |core_ctx| {
1961 let start_dad = core_ctx.initialize_duplicate_address_detection(
1962 &mut bindings_ctx,
1963 &FakeDeviceId,
1964 &address_id,
1965 into_state_change_event::<I>,
1966 );
1967 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
1968 core_ctx.start_duplicate_address_detection(&mut bindings_ctx, token);
1969 });
1970
1971 skip_probe_wait::<I>(&mut core_ctx, &mut bindings_ctx);
1972
1973 check_probe_while_tentative(
1974 &core_ctx,
1975 &bindings_ctx,
1976 1,
1977 NonZeroU16::new(DAD_TRANSMITS_REQUIRED - 1),
1978 );
1979
1980 I::with_dad_handler(&mut core_ctx, |core_ctx| {
1981 core_ctx.stop_duplicate_address_detection(
1982 &mut bindings_ctx,
1983 &FakeDeviceId,
1984 &address_id,
1985 );
1986 });
1987
1988 bindings_ctx.timers.assert_no_timers_installed();
1989 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1990 assert_matches!(*state, DadState::Uninitialized);
1991 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1992 assert!(!*assigned);
1993 assert_eq!(groups, &HashMap::new());
1994 }
1995
1996 #[test]
1997 fn stop_dad_while_announcing() {
1998 let FakeCtx { mut core_ctx, mut bindings_ctx } =
2001 FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
2002 state: DadState::Uninitialized,
2003 max_dad_transmits: NonZeroU16::new(1),
2004 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2005 }));
2006 let address_id = get_address_id::<Ipv4>(Ipv4::DAD_ADDRESS);
2007
2008 let start_dad = core_ctx.initialize_duplicate_address_detection(
2010 &mut bindings_ctx,
2011 &FakeDeviceId,
2012 &address_id,
2013 into_state_change_event::<Ipv4>,
2014 );
2015 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
2016 core_ctx.start_duplicate_address_detection(&mut bindings_ctx, token);
2017 skip_probe_wait::<Ipv4>(&mut core_ctx, &mut bindings_ctx);
2018 check_probe_while_tentative(
2019 &core_ctx,
2020 &bindings_ctx,
2021 1,
2022 None, );
2024 assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(dad_timer_id()));
2025
2026 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
2028 let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
2029 assert_matches!(*state, DadState::Announcing { .. });
2030 bindings_ctx.timers.assert_timers_installed_range([(dad_timer_id(), ..)]);
2031 assert!(*assigned);
2032
2033 core_ctx.stop_duplicate_address_detection(&mut bindings_ctx, &FakeDeviceId, &address_id);
2035 bindings_ctx.timers.assert_no_timers_installed();
2036 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
2037 let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
2038 assert_matches!(*state, DadState::Uninitialized);
2039 assert!(!*assigned);
2040 }
2041
2042 enum IncomingArpPacketCase {
2043 Unavailable,
2044 Assigned { ran_dad: bool, receive_data: Ipv4DadAddressInfo },
2045 Tentative,
2046 }
2047
2048 #[test_case(IncomingArpPacketCase::Unavailable; "uninitialized")]
2049 #[test_case(IncomingArpPacketCase::Tentative; "tentative")]
2050 #[test_case(IncomingArpPacketCase::Assigned{
2051 ran_dad: true, receive_data: Ipv4DadAddressInfo::SourceAddr
2052 }; "assigned_ran_dad_source_addr")]
2053 #[test_case(IncomingArpPacketCase::Assigned {
2054 ran_dad: true, receive_data: Ipv4DadAddressInfo::TargetAddr
2055 }; "assigned_ran_dad_target_addr")]
2056 #[test_case(IncomingArpPacketCase::Assigned {
2057 ran_dad: false, receive_data: Ipv4DadAddressInfo::SourceAddr
2058 }; "assigned_skipped_dad_source_addr")]
2059 #[test_case(IncomingArpPacketCase::Assigned {
2060 ran_dad: false, receive_data: Ipv4DadAddressInfo::TargetAddr
2061 }; "assigned_skipped_dad_target_addr")]
2062 fn handle_incoming_arp_packet(case: IncomingArpPacketCase) {
2063 let mut ctx = FakeCtx::with_default_bindings_ctx(|bindings_ctx| {
2064 let dad_state = match &case {
2065 IncomingArpPacketCase::Unavailable => DadState::Uninitialized,
2066 IncomingArpPacketCase::Assigned { ran_dad, receive_data: _ } => {
2067 DadState::Assigned { ran_dad: *ran_dad }
2068 }
2069 IncomingArpPacketCase::Tentative => DadState::Tentative {
2070 dad_transmits_remaining: NonZeroU16::new(1),
2071 timer: bindings_ctx.new_timer(dad_timer_id()),
2072 ip_specific_state: Default::default(),
2073 probe_wait: None,
2074 },
2075 };
2076
2077 FakeCoreCtxImpl::with_state(FakeDadContext {
2078 state: dad_state,
2079 max_dad_transmits: NonZeroU16::new(1),
2080 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2081 })
2082 });
2083
2084 let (want_lookup_result, optional_receive_data) = match case {
2085 IncomingArpPacketCase::Unavailable => (DadIncomingPacketResult::Uninitialized, None),
2086 IncomingArpPacketCase::Tentative => {
2087 (DadIncomingPacketResult::Tentative { meta: () }, None)
2088 }
2089 IncomingArpPacketCase::Assigned { ran_dad, receive_data } => {
2090 let should_remove = match (ran_dad, &receive_data) {
2091 (true, Ipv4DadAddressInfo::SourceAddr) => true,
2092 _ => false,
2093 };
2094 (DadIncomingPacketResult::Assigned { should_remove }, Some(receive_data))
2095 }
2096 };
2097
2098 let addr = get_address_id::<Ipv4>(Ipv4::DAD_ADDRESS);
2099 let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
2100 const ARBITRARY_RECEIVE_DATA: Ipv4DadAddressInfo = Ipv4DadAddressInfo::SourceAddr;
2101 assert_eq!(
2102 DadHandler::<Ipv4, _>::handle_incoming_packet(
2103 core_ctx,
2104 bindings_ctx,
2105 &FakeDeviceId,
2106 &addr,
2107 optional_receive_data.unwrap_or(ARBITRARY_RECEIVE_DATA),
2108 ),
2109 want_lookup_result
2110 );
2111 }
2112
2113 #[test_case(true, None ; "assigned with no incoming nonce")]
2114 #[test_case(true, Some([1u8; MIN_NONCE_LENGTH]) ; "assigned with incoming nonce")]
2115 #[test_case(false, None ; "uninitialized with no incoming nonce")]
2116 #[test_case(false, Some([1u8; MIN_NONCE_LENGTH]) ; "uninitialized with incoming nonce")]
2117 fn handle_incoming_dad_neighbor_solicitation_while_not_tentative(
2118 assigned: bool,
2119 nonce: Option<OwnedNdpNonce>,
2120 ) {
2121 const MAX_DAD_TRANSMITS: u16 = 1;
2122
2123 let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
2124 state: if assigned {
2125 DadState::Assigned { ran_dad: true }
2126 } else {
2127 DadState::Uninitialized
2128 },
2129 max_dad_transmits: NonZeroU16::new(MAX_DAD_TRANSMITS),
2130 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2131 }));
2132 let addr = get_address_id::<Ipv6>(Ipv6::DAD_ADDRESS);
2133
2134 let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
2135
2136 let want_lookup_result = if assigned {
2137 DadIncomingPacketResult::Assigned { should_remove: false }
2138 } else {
2139 DadIncomingPacketResult::Uninitialized
2140 };
2141
2142 assert_eq!(
2143 DadHandler::<Ipv6, _>::handle_incoming_packet(
2144 core_ctx,
2145 bindings_ctx,
2146 &FakeDeviceId,
2147 &addr,
2148 nonce.as_ref().map(NdpNonce::from),
2149 ),
2150 want_lookup_result
2151 );
2152 }
2153
2154 #[test_case(true ; "discards looped back NS")]
2155 #[test_case(false ; "acts on non-looped-back NS")]
2156 fn handle_incoming_dad_neighbor_solicitation_during_tentative(looped_back: bool) {
2157 const DAD_TRANSMITS_REQUIRED: u16 = 1;
2158
2159 let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
2160 state: DadState::Uninitialized,
2161 max_dad_transmits: NonZeroU16::new(DAD_TRANSMITS_REQUIRED),
2162 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2163 }));
2164 let addr = get_address_id::<Ipv6>(Ipv6::DAD_ADDRESS);
2165
2166 let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
2167 let address_id = get_address_id::<Ipv6>(Ipv6::DAD_ADDRESS);
2168 let start_dad = DadHandler::<Ipv6, _>::initialize_duplicate_address_detection(
2169 core_ctx,
2170 bindings_ctx,
2171 &FakeDeviceId,
2172 &address_id,
2173 into_state_change_event::<Ipv6>,
2174 );
2175 assert_eq!(
2176 bindings_ctx.take_events(),
2177 &[IpDeviceEvent::AddressStateChanged {
2178 device: FakeDeviceId,
2179 addr: Ipv6::DAD_ADDRESS.into(),
2180 state: IpAddressState::Tentative,
2181 }][..]
2182 );
2183 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
2184 DadHandler::<Ipv6, _>::start_duplicate_address_detection(core_ctx, bindings_ctx, token);
2185
2186 check_probe_while_tentative(core_ctx, bindings_ctx, 1, None);
2187
2188 let sent_nonce: OwnedNdpNonce = {
2189 let (Ipv6DadSendData { dst_ip: _, message: _, nonce }, _frame) =
2190 core_ctx.frames().last().expect("should have transmitted a frame");
2191 *nonce
2192 };
2193
2194 let alternative_nonce = {
2195 let mut nonce = sent_nonce.clone();
2196 nonce[0] = nonce[0].wrapping_add(1);
2197 nonce
2198 };
2199
2200 let incoming_nonce =
2201 NdpNonce::from(if looped_back { &sent_nonce } else { &alternative_nonce });
2202
2203 let matched_nonce = assert_matches!(
2204 DadHandler::<Ipv6, _>::handle_incoming_packet(
2205 core_ctx,
2206 bindings_ctx,
2207 &FakeDeviceId,
2208 &addr,
2209 Some(incoming_nonce),
2210 ),
2211 DadIncomingPacketResult::Tentative {
2212 meta: Ipv6PacketResultMetadata {matched_nonce}
2213 } => matched_nonce
2214 );
2215
2216 assert_eq!(matched_nonce, looped_back);
2217
2218 let frames_len_before_extra_transmits = core_ctx.frames().len();
2219 assert_eq!(frames_len_before_extra_transmits, 1);
2220
2221 let extra_dad_transmits_required =
2222 NonZero::new(if looped_back { DEFAULT_MAX_MULTICAST_SOLICIT.get() } else { 0 });
2223
2224 let (dad_transmits_remaining, added_extra_transmits_after_detecting_looped_back_ns) = assert_matches!(
2225 &core_ctx.state.state,
2226 DadState::Tentative {
2227 dad_transmits_remaining,
2228 timer: _,
2229 ip_specific_state: Ipv6TentativeDadState {
2230 nonces: _,
2231 added_extra_transmits_after_detecting_looped_back_ns
2232 },
2233 probe_wait: None,
2234 } => (dad_transmits_remaining, added_extra_transmits_after_detecting_looped_back_ns),
2235 "DAD state should be Tentative"
2236 );
2237
2238 assert_eq!(dad_transmits_remaining, &extra_dad_transmits_required);
2239 assert_eq!(added_extra_transmits_after_detecting_looped_back_ns, &matched_nonce);
2240
2241 let extra_dad_transmits_required =
2242 extra_dad_transmits_required.map(|n| n.get()).unwrap_or(0);
2243
2244 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
2246
2247 for count in 0..extra_dad_transmits_required {
2250 check_probe_while_tentative(
2251 core_ctx,
2252 bindings_ctx,
2253 usize::from(count) + frames_len_before_extra_transmits + 1,
2254 NonZeroU16::new(extra_dad_transmits_required - count - 1),
2255 );
2256 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
2257 }
2258 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
2259 assert_matches!(*state, DadState::Assigned { ran_dad: true });
2260 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
2261 assert!(*assigned);
2262 assert_eq!(groups, &HashMap::from([(Ipv6::DAD_ADDRESS.to_solicited_node_address(), 1)]));
2263 assert_eq!(
2264 bindings_ctx.take_events(),
2265 &[IpDeviceEvent::AddressStateChanged {
2266 device: FakeDeviceId,
2267 addr: Ipv6::DAD_ADDRESS.into(),
2268 state: IpAddressState::Assigned
2269 }][..]
2270 );
2271 }
2272
2273 #[test]
2274 fn ipv4_dad_probe_interval_is_valid() {
2275 FakeCryptoRng::with_fake_rngs(100, |mut bindings_ctx| {
2278 let duration = ipv4_dad_probe_interval(&mut bindings_ctx).get();
2279 assert!(IPV4_PROBE_RANGE.contains(&duration), "actual={duration:?}");
2280 })
2281 }
2282
2283 #[test]
2284 fn ipv4_dad_probe_wait_is_valid() {
2285 FakeCryptoRng::with_fake_rngs(100, |mut bindings_ctx| {
2288 let duration = ipv4_dad_probe_wait(&mut bindings_ctx);
2289 assert!(IPV4_PROBE_WAIT_RANGE.contains(&duration), "actual={duration:?}");
2290 })
2291 }
2292
2293 #[ip_test(I)]
2294 fn dad_ignores_stale_timers<I: TestDadIpExt>() {
2295 const DAD_TRANSMITS_REQUIRED: u16 = 1;
2296
2297 let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
2298 state: DadState::Uninitialized,
2299 max_dad_transmits: NonZeroU16::new(DAD_TRANSMITS_REQUIRED),
2300 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2301 }));
2302 let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
2303 let address_id = get_address_id::<I>(I::DAD_ADDRESS);
2304 I::with_dad_handler(core_ctx, |core_ctx| {
2305 let start_dad = core_ctx.initialize_duplicate_address_detection(
2306 bindings_ctx,
2307 &FakeDeviceId,
2308 &address_id,
2309 into_state_change_event::<I>,
2310 );
2311 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
2312 core_ctx.start_duplicate_address_detection(bindings_ctx, token);
2313 });
2314
2315 let old_timer = assert_matches!(
2316 &core_ctx.state.state,
2317 DadState::Tentative { timer, .. } => timer.clone()
2318 );
2319
2320 I::with_dad_handler(core_ctx, |core_ctx| {
2322 core_ctx.stop_duplicate_address_detection(bindings_ctx, &FakeDeviceId, &address_id);
2323 let start_dad = core_ctx.initialize_duplicate_address_detection(
2324 bindings_ctx,
2325 &FakeDeviceId,
2326 &address_id,
2327 into_state_change_event::<I>,
2328 );
2329 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
2330 core_ctx.start_duplicate_address_detection(bindings_ctx, token);
2331 });
2332
2333 skip_probe_wait::<I>(core_ctx, bindings_ctx);
2334
2335 assert_matches!(core_ctx.state.state, DadState::Tentative { .. });
2336 let unique_timer_id = bindings_ctx.unique_timer_id(&old_timer);
2337 old_timer.dispatch_id.handle(core_ctx, bindings_ctx, unique_timer_id);
2338 assert_matches!(core_ctx.state.state, DadState::Tentative { .. });
2339
2340 let mut new_timer = assert_matches!(
2343 &core_ctx.state.state,
2344 DadState::Tentative { timer, .. } => timer.clone()
2345 );
2346 assert_matches!(bindings_ctx.cancel_timer(&mut new_timer), Some(_));
2348 let unique_timer_id = bindings_ctx.unique_timer_id(&new_timer);
2349 new_timer.dispatch_id.handle(core_ctx, bindings_ctx, unique_timer_id);
2350 match I::VERSION {
2351 IpVersion::V4 => assert_matches!(core_ctx.state.state, DadState::Announcing { .. }),
2352 IpVersion::V6 => assert_matches!(core_ctx.state.state, DadState::Assigned { .. }),
2353 };
2354 }
2355}