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 timer_id: Option<BC::UniqueTimerId>,
707) {
708 let should_send_probe = core_ctx.with_dad_state(
709 device_id,
710 addr,
711 |DadStateRef { state, retrans_timer_data, max_dad_transmits: _ }| {
712 let DadAddressStateRef { dad_state, core_ctx } = state;
713 match dad_state {
714 DadState::Uninitialized => {
715 return None;
718 }
719 DadState::Assigned { .. } => {
720 panic!("cannot do DAD for an already assigned address; addr={addr:?}")
721 }
722 DadState::Tentative {
723 dad_transmits_remaining,
724 timer,
725 ip_specific_state,
726 probe_wait,
727 } => {
728 if let Some(timer_id) = timer_id
729 && timer_id != bindings_ctx.unique_timer_id(timer)
730 {
731 return None;
734 }
735
736 let (should_send_probe, state_change) = run_tentative_step::<I, CC, BC>(
737 core_ctx,
738 bindings_ctx,
739 device_id,
740 addr,
741 retrans_timer_data,
742 dad_transmits_remaining,
743 timer,
744 ip_specific_state,
745 probe_wait,
746 );
747 match state_change {
748 DadStateChangeFromTentative::None => {}
749 DadStateChangeFromTentative::ToAssigned => {
750 *dad_state = DadState::Assigned { ran_dad: true }
751 }
752 DadStateChangeFromTentative::ToAnnouncing { ip_specific_state } => {
753 let orig = mem::replace(dad_state, DadState::Uninitialized);
758 let timer = assert_matches!(
759 orig,
760 DadState::Tentative{timer, ..} => timer,
761 "state must be tentative"
762 );
763 *dad_state = DadState::Announcing { ip_specific_state, timer }
764 }
765 }
766 should_send_probe
767 }
768 DadState::Announcing { ip_specific_state, timer } => {
769 if let Some(timer_id) = timer_id
770 && timer_id != bindings_ctx.unique_timer_id(timer)
771 {
772 return None;
775 }
776
777 #[derive(GenericOverIp)]
778 #[generic_over_ip(I, Ip)]
779 struct WrapIn<'a, I: DadIpExt>(&'a mut I::AnnouncingState, I::Addr);
780 #[derive(GenericOverIp)]
781 #[generic_over_ip(I, Ip)]
782 struct WrapOut<I: DadIpExt>(I::SendData, DadStateChangeFromAnnouncing);
783 let WrapOut(send_data, state_change) = I::map_ip(
784 WrapIn(ip_specific_state, addr.addr().addr()),
785 |WrapIn(ipv4_state, ipv4_addr)| {
786 let (send_data, state_change) = run_ipv4_announcing_step(
787 bindings_ctx,
788 device_id,
789 ipv4_addr,
790 ipv4_state,
791 timer,
792 );
793 WrapOut(send_data, state_change)
794 },
795 |WrapIn(ipv6_state, _ipv6_addr)| match *ipv6_state {},
796 );
797 match state_change {
798 DadStateChangeFromAnnouncing::None => {}
799 DadStateChangeFromAnnouncing::ToAssigned => {
800 *dad_state = DadState::Assigned { ran_dad: true }
801 }
802 }
803 Some(send_data)
804 }
805 }
806 },
807 );
808
809 if let Some(probe_send_data) = should_send_probe {
810 core_ctx.send_dad_probe(bindings_ctx, device_id, probe_send_data);
811 }
812}
813
814enum DadStateChangeFromTentative<I: DadIpExt> {
816 None,
817 ToAssigned,
818 ToAnnouncing { ip_specific_state: I::AnnouncingState },
819}
820
821fn run_tentative_step<
827 I: IpDeviceIpExt,
828 CC: DadContext<I, BC>,
829 BC: DadBindingsContext<I, CC::DeviceId>,
830>(
831 core_ctx: &mut CC::DadAddressCtx<'_>,
832 bindings_ctx: &mut BC,
833 device_id: &CC::DeviceId,
834 addr: &CC::AddressId,
835 retrans_timer_data: &I::RetransmitTimerData,
836 dad_transmits_remaining: &mut Option<NonZeroU16>,
837 timer: &mut BC::Timer,
838 ip_specific_state: &mut I::TentativeState,
839 probe_wait: &mut Option<Duration>,
840) -> (Option<I::SendData>, DadStateChangeFromTentative<I>) {
841 if let Some(probe_wait) = probe_wait.take() {
842 schedule_dad_timer(bindings_ctx, timer, probe_wait, addr.addr(), device_id);
845 return (None, DadStateChangeFromTentative::None);
846 }
847
848 match dad_transmits_remaining {
849 None => {
850 #[derive(GenericOverIp)]
851 #[generic_over_ip(I, Ip)]
852 struct WrapOut<I: DadIpExt>(Option<I::SendData>, DadStateChangeFromTentative<I>);
853 let WrapOut(send_data, state_change) = I::map_ip(
854 addr.addr().addr(),
855 |ipv4_addr| {
863 let mut state =
864 Ipv4AnnouncingDadState { announcements_remaining: IPV4_DAD_ANNOUNCE_NUM };
865 let (send_data, state_change) = run_ipv4_announcing_step(
866 bindings_ctx,
867 device_id,
868 ipv4_addr,
869 &mut state,
870 timer,
871 );
872 let state_change = match state_change {
875 DadStateChangeFromAnnouncing::None => {
876 DadStateChangeFromTentative::ToAnnouncing { ip_specific_state: state }
879 }
880 DadStateChangeFromAnnouncing::ToAssigned => {
881 DadStateChangeFromTentative::ToAssigned
886 }
887 };
888 WrapOut(Some(send_data), state_change)
889 },
890 |_ipv6_addr| WrapOut(None, DadStateChangeFromTentative::ToAssigned),
893 );
894
895 core_ctx.with_address_assigned(device_id, addr, |assigned| *assigned = true);
902 bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
903 device: device_id.clone(),
904 addr: addr.addr_sub().addr().into(),
905 state: IpAddressState::Assigned,
906 });
907 return (send_data, state_change);
908 }
909 Some(non_zero_remaining) => {
910 *dad_transmits_remaining = NonZeroU16::new(non_zero_remaining.get() - 1);
911
912 let retrans_interval = I::get_retransmission_interval(
930 retrans_timer_data,
931 bindings_ctx,
932 dad_transmits_remaining,
933 );
934 schedule_dad_timer(bindings_ctx, timer, retrans_interval.get(), addr.addr(), device_id);
935 debug!(
936 "sending DAD probe for {}; {} remaining",
937 addr.addr(),
938 dad_transmits_remaining.map_or(0, NonZeroU16::get)
939 );
940 let send_data =
941 I::generate_sent_probe_data(ip_specific_state, addr.addr().addr(), bindings_ctx);
942 return (Some(send_data), DadStateChangeFromTentative::None);
943 }
944 }
945}
946
947enum DadStateChangeFromAnnouncing {
949 None,
950 ToAssigned,
951}
952
953fn run_ipv4_announcing_step<BC: TimerContext>(
962 bindings_ctx: &mut BC,
963 device_id: impl Debug,
964 addr: Ipv4Addr,
965 ip_specific_state: &mut Ipv4AnnouncingDadState,
966 timer: &mut BC::Timer,
967) -> (Ipv4DadSendData, DadStateChangeFromAnnouncing) {
968 let Ipv4AnnouncingDadState { announcements_remaining } = ip_specific_state;
969
970 let new_announcements_remaining = NonZeroU16::new(announcements_remaining.get() - 1);
971 debug!(
972 "sending DAD announcement for {}; {} remaining",
973 addr,
974 new_announcements_remaining.map_or(0, NonZeroU16::get)
975 );
976
977 let send_data =
978 Ipv4DadSendData { target_ip: addr, probe_type: Ipv4SentProbeType::Announcement };
979
980 let state_change = match new_announcements_remaining {
981 None => DadStateChangeFromAnnouncing::ToAssigned,
983 Some(non_zero_announcements_remaining) => {
984 *announcements_remaining = non_zero_announcements_remaining;
987 schedule_dad_timer(bindings_ctx, timer, IPV4_ANNOUNCE_INTERVAL.get(), addr, device_id);
988 DadStateChangeFromAnnouncing::None
989 }
990 };
991 (send_data, state_change)
992}
993
994fn schedule_dad_timer<BC: TimerContext>(
996 bindings_ctx: &mut BC,
997 timer: &mut BC::Timer,
998 duration: Duration,
999 addr: impl Debug,
1000 device_id: impl Debug,
1001) {
1002 assert_eq!(
1003 bindings_ctx.schedule_timer(duration, timer),
1004 None,
1005 "Unexpected DAD timer; addr={:?}, device_id={:?}",
1006 addr,
1007 device_id
1008 );
1009}
1010
1011fn stop_duplicate_address_detection<
1013 'a,
1014 I: IpDeviceIpExt,
1015 BC: DadBindingsContext<I, CC::DeviceId>,
1016 CC: DadContext<I, BC>,
1017 F: FnOnce(&mut CC::DadAddressCtx<'_>, &mut BC, &CC::DeviceId, &CC::AddressId),
1018>(
1019 core_ctx: &mut CC,
1020 bindings_ctx: &mut BC,
1021 device_id: &'a CC::DeviceId,
1022 addr: &'a CC::AddressId,
1023 on_stopped_cb: F,
1024) {
1025 core_ctx.with_dad_state(
1026 device_id,
1027 addr,
1028 |DadStateRef { state, retrans_timer_data: _, max_dad_transmits: _ }| {
1029 let DadAddressStateRef { dad_state, core_ctx } = state;
1030
1031 match dad_state {
1032 DadState::Assigned { .. } => {}
1033 DadState::Announcing { timer, .. } | DadState::Tentative { timer, .. } => {
1034 let _: Option<_> = bindings_ctx.cancel_timer(timer);
1039 }
1040 DadState::Uninitialized => return,
1042 };
1043
1044 *dad_state = DadState::Uninitialized;
1048 core_ctx.with_address_assigned(device_id, addr, |assigned| *assigned = false);
1049
1050 on_stopped_cb(core_ctx, bindings_ctx, device_id, addr)
1053 },
1054 )
1055}
1056
1057fn handle_incoming_packet<
1059 'a,
1060 I: IpDeviceIpExt,
1061 BC: DadBindingsContext<I, CC::DeviceId>,
1062 CC: DadContext<I, BC>,
1063>(
1064 core_ctx: &mut CC,
1065 _bindings_ctx: &mut BC,
1066 device_id: &'a CC::DeviceId,
1067 addr: &'a CC::AddressId,
1068 data: I::ReceivedPacketData<'a>,
1069) -> DadIncomingPacketResult<I> {
1070 core_ctx.with_dad_state(
1071 device_id,
1072 addr,
1073 |DadStateRef { state, retrans_timer_data: _, max_dad_transmits: _ }| {
1074 let DadAddressStateRef { dad_state, core_ctx: _ } = state;
1075 match dad_state {
1076 DadState::Uninitialized => DadIncomingPacketResult::Uninitialized,
1077 DadState::Assigned { ran_dad } => DadIncomingPacketResult::Assigned {
1078 should_remove: I::handle_incoming_packet_while_assigned(data, *ran_dad),
1079 },
1080 DadState::Announcing { .. } => {
1081 let ran_dad = true;
1084 DadIncomingPacketResult::Assigned {
1085 should_remove: I::handle_incoming_packet_while_assigned(data, ran_dad),
1086 }
1087 }
1088 DadState::Tentative {
1089 dad_transmits_remaining,
1090 timer: _,
1091 ip_specific_state,
1092 probe_wait: _,
1093 } => DadIncomingPacketResult::Tentative {
1094 meta: I::handle_incoming_packet_while_tentative(
1095 data,
1096 ip_specific_state,
1097 dad_transmits_remaining,
1098 ),
1099 },
1100 }
1101 },
1102 )
1103}
1104
1105impl<BC: DadBindingsContext<Ipv4, Self::DeviceId>, CC: DadContext<Ipv4, BC>> DadHandler<Ipv4, BC>
1106 for CC
1107{
1108 fn initialize_duplicate_address_detection<
1109 'a,
1110 F: FnOnce(IpAddressState) -> IpDeviceEvent<Self::DeviceId, Ipv4, BC::Instant>,
1111 >(
1112 &mut self,
1113 bindings_ctx: &mut BC,
1114 device_id: &'a Self::DeviceId,
1115 addr: &'a Self::AddressId,
1116 into_bindings_event: F,
1117 ) -> NeedsDad<'a, Self::AddressId, Self::DeviceId> {
1118 initialize_duplicate_address_detection(
1119 self,
1120 bindings_ctx,
1121 device_id,
1122 addr,
1123 into_bindings_event,
1124 |_core_ctx, _bindings_ctx, _device_id, _addr| {},
1126 )
1127 }
1128
1129 fn start_duplicate_address_detection<'a>(
1130 &mut self,
1131 bindings_ctx: &mut BC,
1132 start_dad: StartDad<'_, Self::AddressId, Self::DeviceId>,
1133 ) {
1134 let StartDad { device_id, address_id } = start_dad;
1135 do_duplicate_address_detection(self, bindings_ctx, device_id, address_id, None)
1136 }
1137
1138 fn stop_duplicate_address_detection(
1139 &mut self,
1140 bindings_ctx: &mut BC,
1141 device_id: &Self::DeviceId,
1142 addr: &Self::AddressId,
1143 ) {
1144 stop_duplicate_address_detection(
1145 self,
1146 bindings_ctx,
1147 device_id,
1148 addr,
1149 |_core_ctx, _bindings_ctx, _device_id, _addr| {},
1151 )
1152 }
1153
1154 fn handle_incoming_packet(
1156 &mut self,
1157 bindings_ctx: &mut BC,
1158 device_id: &Self::DeviceId,
1159 addr: &Self::AddressId,
1160 data: Ipv4DadAddressInfo,
1161 ) -> DadIncomingPacketResult<Ipv4> {
1162 handle_incoming_packet(self, bindings_ctx, device_id, addr, data)
1163 }
1164}
1165
1166impl<BC: DadBindingsContext<Ipv6, Self::DeviceId>, CC: DadContext<Ipv6, BC>> DadHandler<Ipv6, BC>
1167 for CC
1168where
1169 for<'a> CC::DadAddressCtx<'a>: Ipv6DadAddressContext<BC>,
1170{
1171 fn initialize_duplicate_address_detection<
1172 'a,
1173 F: FnOnce(IpAddressState) -> IpDeviceEvent<Self::DeviceId, Ipv6, BC::Instant>,
1174 >(
1175 &mut self,
1176 bindings_ctx: &mut BC,
1177 device_id: &'a Self::DeviceId,
1178 addr: &'a Self::AddressId,
1179 into_bindings_event: F,
1180 ) -> NeedsDad<'a, Self::AddressId, Self::DeviceId> {
1181 initialize_duplicate_address_detection(
1182 self,
1183 bindings_ctx,
1184 device_id,
1185 addr,
1186 into_bindings_event,
1187 |core_ctx, bindings_ctx, device_id, addr| {
1189 core_ctx.join_multicast_group(
1205 bindings_ctx,
1206 device_id,
1207 addr.addr().addr().to_solicited_node_address(),
1208 );
1209 },
1210 )
1211 }
1212
1213 fn start_duplicate_address_detection<'a>(
1214 &mut self,
1215 bindings_ctx: &mut BC,
1216 start_dad: StartDad<'_, Self::AddressId, Self::DeviceId>,
1217 ) {
1218 let StartDad { device_id, address_id } = start_dad;
1219 do_duplicate_address_detection(self, bindings_ctx, device_id, address_id, None)
1220 }
1221
1222 fn stop_duplicate_address_detection(
1223 &mut self,
1224 bindings_ctx: &mut BC,
1225 device_id: &Self::DeviceId,
1226 addr: &Self::AddressId,
1227 ) {
1228 stop_duplicate_address_detection(
1229 self,
1230 bindings_ctx,
1231 device_id,
1232 addr,
1233 |core_ctx, bindings_ctx, device_id, addr| {
1235 core_ctx.leave_multicast_group(
1241 bindings_ctx,
1242 device_id,
1243 addr.addr().addr().to_solicited_node_address(),
1244 );
1245 },
1246 )
1247 }
1248
1249 fn handle_incoming_packet(
1251 &mut self,
1252 bindings_ctx: &mut BC,
1253 device_id: &Self::DeviceId,
1254 addr: &Self::AddressId,
1255 data: Option<NdpNonce<&[u8]>>,
1256 ) -> DadIncomingPacketResult<Ipv6> {
1257 handle_incoming_packet(self, bindings_ctx, device_id, addr, data)
1258 }
1259}
1260
1261impl<I: IpDeviceIpExt, BC: DadBindingsContext<I, CC::DeviceId>, CC: DadContext<I, BC>>
1262 HandleableTimer<CC, BC> for DadTimerId<I, CC::WeakDeviceId, CC::WeakAddressId>
1263{
1264 fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, timer_id: BC::UniqueTimerId) {
1265 let Self { device_id, addr, _marker } = self;
1266 let Some(device_id) = device_id.upgrade() else {
1267 return;
1268 };
1269 let Some(addr_id) = addr.upgrade() else {
1270 return;
1271 };
1272 do_duplicate_address_detection(core_ctx, bindings_ctx, &device_id, &addr_id, Some(timer_id))
1273 }
1274}
1275
1276const IPV4_PROBE_RANGE: RangeInclusive<Duration> = Duration::from_secs(1)..=Duration::from_secs(2);
1280
1281pub fn ipv4_dad_probe_interval<BC: RngContext>(bindings_ctx: &mut BC) -> NonZeroDuration {
1283 let retrans_interval = bindings_ctx.rng().random_range(IPV4_PROBE_RANGE);
1288 NonZeroDuration::new(retrans_interval).unwrap()
1291}
1292
1293const IPV4_PROBE_WAIT_RANGE: RangeInclusive<Duration> = Duration::ZERO..=Duration::from_secs(1);
1296
1297pub fn ipv4_dad_probe_wait<BC: RngContext>(bindings_ctx: &mut BC) -> Duration {
1299 bindings_ctx.rng().random_range(IPV4_PROBE_WAIT_RANGE)
1304}
1305
1306const IPV4_ANNOUNCE_WAIT: NonZeroDuration = NonZeroDuration::new(Duration::from_secs(2)).unwrap();
1309
1310const IPV4_ANNOUNCE_INTERVAL: NonZeroDuration =
1313 NonZeroDuration::new(Duration::from_secs(2)).unwrap();
1314
1315pub const IPV4_DAD_ANNOUNCE_NUM: NonZeroU16 = NonZeroU16::new(2).unwrap();
1318
1319enum AddressDefenseStrategy {
1341 ForfeitAddress,
1343}
1344
1345const IPV4_ADDRESS_DEFENSE_STRATEGY: AddressDefenseStrategy =
1370 AddressDefenseStrategy::ForfeitAddress;
1371
1372#[cfg(test)]
1373mod tests {
1374 use core::ops::RangeBounds;
1375 use core::time::Duration;
1376
1377 use assert_matches::assert_matches;
1378 use ip_test_macro::ip_test;
1379 use net_types::ip::{AddrSubnet, GenericOverIp, IpAddress as _, IpVersion, Ipv4Addr};
1380 use net_types::{NonMappedAddr, NonMulticastAddr, SpecifiedAddr, UnicastAddr, Witness as _};
1381 use netstack3_base::testutil::{
1382 FakeBindingsCtx, FakeCoreCtx, FakeCryptoRng, FakeDeviceId, FakeInstant,
1383 FakeTimerCtxExt as _, FakeWeakAddressId, FakeWeakDeviceId,
1384 };
1385 use netstack3_base::{
1386 AssignedAddrIpExt, CtxPair, InstantContext as _, Ipv4DeviceAddr, Ipv6DeviceAddr,
1387 SendFrameContext as _, TimerHandler,
1388 };
1389 use netstack3_hashmap::hash_map::{Entry, HashMap};
1390 use packet::EmptyBuf;
1391 use test_case::test_case;
1392
1393 use super::*;
1394
1395 struct FakeDadAddressContext<I: IpDeviceIpExt> {
1396 addr: I::AssignedWitness,
1397 assigned: bool,
1398 groups: HashMap<MulticastAddr<Ipv6Addr>, usize>,
1400 should_perform_dad: bool,
1401 }
1402
1403 trait TestDadIpExt: IpDeviceIpExt {
1404 const DAD_ADDRESS: Self::AssignedWitness;
1405
1406 const DEFAULT_RETRANS_TIMER_DATA: Self::RetransmitTimerData;
1407
1408 const EXPECTED_NUM_ANNOUNCEMENTS: u16;
1409
1410 fn expected_timer_range(now: FakeInstant) -> impl RangeBounds<FakeInstant> + Debug;
1413
1414 type DadHandlerCtx: DadHandler<
1415 Self,
1416 FakeBindingsCtxImpl<Self>,
1417 DeviceId = FakeDeviceId,
1418 AddressId = AddrSubnet<Self::Addr, Self::AssignedWitness>,
1419 > + AsRef<FakeDadContext<Self>>;
1420
1421 fn with_dad_handler<O, F: FnOnce(&mut Self::DadHandlerCtx) -> O>(
1426 core_ctx: &mut FakeCoreCtxImpl<Self>,
1427 cb: F,
1428 ) -> O;
1429 }
1430
1431 impl TestDadIpExt for Ipv4 {
1432 const DAD_ADDRESS: Ipv4DeviceAddr = unsafe {
1433 NonMulticastAddr::new_unchecked(NonMappedAddr::new_unchecked(
1434 SpecifiedAddr::new_unchecked(Ipv4Addr::new([192, 168, 0, 1])),
1435 ))
1436 };
1437
1438 const DEFAULT_RETRANS_TIMER_DATA: () = ();
1439
1440 const EXPECTED_NUM_ANNOUNCEMENTS: u16 = IPV4_DAD_ANNOUNCE_NUM.get();
1441
1442 fn expected_timer_range(now: FakeInstant) -> impl RangeBounds<FakeInstant> + Debug {
1443 let FakeInstant { offset } = now;
1444 FakeInstant::from(offset + *IPV4_PROBE_RANGE.start())
1445 ..=FakeInstant::from(offset + *IPV4_PROBE_RANGE.end())
1446 }
1447
1448 type DadHandlerCtx = FakeCoreCtxImpl<Ipv4>;
1449
1450 fn with_dad_handler<O, F: FnOnce(&mut FakeCoreCtxImpl<Ipv4>) -> O>(
1451 core_ctx: &mut FakeCoreCtxImpl<Ipv4>,
1452 cb: F,
1453 ) -> O {
1454 cb(core_ctx)
1455 }
1456 }
1457
1458 impl TestDadIpExt for Ipv6 {
1459 const DAD_ADDRESS: Ipv6DeviceAddr = unsafe {
1460 NonMappedAddr::new_unchecked(UnicastAddr::new_unchecked(Ipv6Addr::new([
1461 0xa, 0, 0, 0, 0, 0, 0, 1,
1462 ])))
1463 };
1464
1465 const DEFAULT_RETRANS_TIMER_DATA: NonZeroDuration =
1466 NonZeroDuration::new(Duration::from_secs(1)).unwrap();
1467
1468 const EXPECTED_NUM_ANNOUNCEMENTS: u16 = 0;
1470
1471 fn expected_timer_range(now: FakeInstant) -> impl RangeBounds<FakeInstant> + Debug {
1472 let FakeInstant { offset } = now;
1473 let expected_timer = FakeInstant::from(offset + Self::DEFAULT_RETRANS_TIMER_DATA.get());
1474 expected_timer..=expected_timer
1475 }
1476
1477 type DadHandlerCtx = FakeCoreCtxImpl<Ipv6>;
1478
1479 fn with_dad_handler<O, F: FnOnce(&mut FakeCoreCtxImpl<Ipv6>) -> O>(
1480 core_ctx: &mut FakeCoreCtxImpl<Ipv6>,
1481 cb: F,
1482 ) -> O {
1483 cb(core_ctx)
1484 }
1485 }
1486
1487 impl<I: TestDadIpExt> Default for FakeDadAddressContext<I> {
1488 fn default() -> Self {
1489 Self {
1490 addr: I::DAD_ADDRESS,
1491 assigned: false,
1492 groups: Default::default(),
1493 should_perform_dad: true,
1494 }
1495 }
1496 }
1497
1498 type FakeAddressCtxImpl<I> = FakeCoreCtx<FakeDadAddressContext<I>, (), FakeDeviceId>;
1499
1500 impl<I: IpDeviceIpExt> DadAddressContext<I, FakeBindingsCtxImpl<I>> for FakeAddressCtxImpl<I> {
1501 fn with_address_assigned<O, F: FnOnce(&mut bool) -> O>(
1502 &mut self,
1503 &FakeDeviceId: &Self::DeviceId,
1504 request_addr: &Self::AddressId,
1505 cb: F,
1506 ) -> O {
1507 let FakeDadAddressContext { addr, assigned, .. } = &mut self.state;
1508 assert_eq!(request_addr.addr(), *addr);
1509 cb(assigned)
1510 }
1511
1512 fn should_perform_dad(
1513 &mut self,
1514 &FakeDeviceId: &Self::DeviceId,
1515 request_addr: &Self::AddressId,
1516 ) -> bool {
1517 let FakeDadAddressContext { addr, should_perform_dad, .. } = &mut self.state;
1518 assert_eq!(request_addr.addr(), *addr);
1519 *should_perform_dad
1520 }
1521 }
1522
1523 impl Ipv6DadAddressContext<FakeBindingsCtxImpl<Ipv6>> for FakeAddressCtxImpl<Ipv6> {
1524 fn join_multicast_group(
1525 &mut self,
1526 _bindings_ctx: &mut FakeBindingsCtxImpl<Ipv6>,
1527 &FakeDeviceId: &Self::DeviceId,
1528 multicast_addr: MulticastAddr<Ipv6Addr>,
1529 ) {
1530 *self.state.groups.entry(multicast_addr).or_default() += 1;
1531 }
1532
1533 fn leave_multicast_group(
1534 &mut self,
1535 _bindings_ctx: &mut FakeBindingsCtxImpl<Ipv6>,
1536 &FakeDeviceId: &Self::DeviceId,
1537 multicast_addr: MulticastAddr<Ipv6Addr>,
1538 ) {
1539 match self.state.groups.entry(multicast_addr) {
1540 Entry::Vacant(_) => {}
1541 Entry::Occupied(mut e) => {
1542 let v = e.get_mut();
1543 const COUNT_BEFORE_REMOVE: usize = 1;
1544 if *v == COUNT_BEFORE_REMOVE {
1545 assert_eq!(e.remove(), COUNT_BEFORE_REMOVE);
1546 } else {
1547 *v -= 1
1548 }
1549 }
1550 }
1551 }
1552 }
1553
1554 struct FakeDadContext<I: IpDeviceIpExt> {
1555 state: DadState<I, FakeBindingsCtxImpl<I>>,
1556 max_dad_transmits: Option<NonZeroU16>,
1557 address_ctx: FakeAddressCtxImpl<I>,
1558 }
1559
1560 type TestDadTimerId<I> = DadTimerId<
1561 I,
1562 FakeWeakDeviceId<FakeDeviceId>,
1563 FakeWeakAddressId<AddrSubnet<<I as Ip>::Addr, <I as AssignedAddrIpExt>::AssignedWitness>>,
1564 >;
1565
1566 type FakeBindingsCtxImpl<I> =
1567 FakeBindingsCtx<TestDadTimerId<I>, IpDeviceEvent<FakeDeviceId, I, FakeInstant>, (), ()>;
1568
1569 type FakeCoreCtxImpl<I> =
1570 FakeCoreCtx<FakeDadContext<I>, <I as DadIpExt>::SendData, FakeDeviceId>;
1571
1572 fn get_address_id<I: IpDeviceIpExt>(
1573 addr: I::AssignedWitness,
1574 ) -> AddrSubnet<I::Addr, I::AssignedWitness> {
1575 AddrSubnet::from_witness(addr, I::Addr::BYTES * 8).unwrap()
1576 }
1577
1578 impl<I: IpDeviceIpExt> CoreTimerContext<TestDadTimerId<I>, FakeBindingsCtxImpl<I>>
1579 for FakeCoreCtxImpl<I>
1580 {
1581 fn convert_timer(dispatch_id: TestDadTimerId<I>) -> TestDadTimerId<I> {
1582 dispatch_id
1583 }
1584 }
1585
1586 impl<I: TestDadIpExt> DadContext<I, FakeBindingsCtxImpl<I>> for FakeCoreCtxImpl<I> {
1587 type DadAddressCtx<'a> = FakeAddressCtxImpl<I>;
1588
1589 fn with_dad_state<
1590 O,
1591 F: FnOnce(DadStateRef<'_, I, Self::DadAddressCtx<'_>, FakeBindingsCtxImpl<I>>) -> O,
1592 >(
1593 &mut self,
1594 &FakeDeviceId: &FakeDeviceId,
1595 request_addr: &Self::AddressId,
1596 cb: F,
1597 ) -> O {
1598 let FakeDadContext { state, max_dad_transmits, address_ctx } = &mut self.state;
1599 let ctx_addr = address_ctx.state.addr;
1600 let requested_addr = request_addr.addr();
1601 assert!(
1602 ctx_addr == requested_addr,
1603 "invalid address {requested_addr} expected {ctx_addr}"
1604 );
1605 cb(DadStateRef {
1606 state: DadAddressStateRef { dad_state: state, core_ctx: address_ctx },
1607 retrans_timer_data: &I::DEFAULT_RETRANS_TIMER_DATA,
1608 max_dad_transmits,
1609 })
1610 }
1611
1612 fn send_dad_probe(
1613 &mut self,
1614 bindings_ctx: &mut FakeBindingsCtxImpl<I>,
1615 &FakeDeviceId: &FakeDeviceId,
1616 data: I::SendData,
1617 ) {
1618 self.send_frame(bindings_ctx, data, EmptyBuf).unwrap()
1619 }
1620 }
1621
1622 type FakeCtx<I> = CtxPair<FakeCoreCtxImpl<I>, FakeBindingsCtxImpl<I>>;
1623
1624 fn into_state_change_event<I: TestDadIpExt>(
1627 state: IpAddressState,
1628 ) -> IpDeviceEvent<FakeDeviceId, I, FakeInstant> {
1629 IpDeviceEvent::AddressStateChanged {
1630 state,
1631 addr: I::DAD_ADDRESS.into(),
1632 device: FakeDeviceId,
1633 }
1634 }
1635
1636 #[ip_test(I)]
1637 #[should_panic(expected = "cannot do DAD for an already assigned address")]
1638 fn panic_non_tentative_address_handle_timer<I: TestDadIpExt>() {
1639 let FakeCtx::<I> { mut core_ctx, mut bindings_ctx } =
1640 FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1641 state: DadState::Assigned { ran_dad: true },
1642 max_dad_transmits: None,
1643 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1644 }));
1645 TimerHandler::handle_timer(
1646 &mut core_ctx,
1647 &mut bindings_ctx,
1648 dad_timer_id(),
1649 Default::default(),
1650 );
1651 }
1652
1653 #[ip_test(I)]
1654 fn dad_disabled<I: TestDadIpExt>() {
1655 let FakeCtx::<I> { mut core_ctx, mut bindings_ctx } =
1656 FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1657 state: DadState::Uninitialized,
1658 max_dad_transmits: None,
1659 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1660 }));
1661 let address_id = get_address_id::<I>(I::DAD_ADDRESS);
1662 let start_dad = I::with_dad_handler(&mut core_ctx, |core_ctx| {
1663 core_ctx.initialize_duplicate_address_detection(
1664 &mut bindings_ctx,
1665 &FakeDeviceId,
1666 &address_id,
1667 into_state_change_event::<I>,
1668 )
1669 });
1670 assert_matches!(start_dad, NeedsDad::No);
1671 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1672 assert_matches!(*state, DadState::Assigned { ran_dad: false });
1673 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1674 assert!(*assigned);
1675 check_multicast_groups(I::VERSION, groups);
1676 assert_eq!(
1677 bindings_ctx.take_events(),
1678 &[IpDeviceEvent::AddressStateChanged {
1679 state: IpAddressState::Assigned,
1680 device: FakeDeviceId,
1681 addr: I::DAD_ADDRESS.into(),
1682 }][..]
1683 );
1684 }
1685
1686 fn dad_timer_id<I: TestDadIpExt>() -> TestDadTimerId<I> {
1687 DadTimerId {
1688 addr: FakeWeakAddressId(get_address_id::<I>(I::DAD_ADDRESS)),
1689 device_id: FakeWeakDeviceId(FakeDeviceId),
1690 _marker: IpVersionMarker::new(),
1691 }
1692 }
1693
1694 #[track_caller]
1695 fn check_multicast_groups(
1696 ip_version: IpVersion,
1697 groups: &HashMap<MulticastAddr<Ipv6Addr>, usize>,
1698 ) {
1699 match ip_version {
1700 IpVersion::V4 => assert_eq!(groups, &HashMap::new()),
1702 IpVersion::V6 => {
1704 assert_eq!(
1705 groups,
1706 &HashMap::from([(Ipv6::DAD_ADDRESS.to_solicited_node_address(), 1)])
1707 )
1708 }
1709 }
1710 }
1711
1712 fn check_probe_while_tentative<I: TestDadIpExt>(
1714 core_ctx: &FakeCoreCtxImpl<I>,
1715 bindings_ctx: &FakeBindingsCtxImpl<I>,
1716 frames_len: usize,
1717 dad_transmits_remaining: Option<NonZeroU16>,
1718 ) {
1719 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1720 let (got_transmits_remaining, ip_specific_state, probe_wait) = assert_matches!(
1721 state,
1722 DadState::Tentative {
1723 timer: _,
1724 dad_transmits_remaining,
1725 ip_specific_state,
1726 probe_wait,
1727 } => (dad_transmits_remaining, ip_specific_state, probe_wait)
1728 );
1729 assert_eq!(
1730 *got_transmits_remaining, dad_transmits_remaining,
1731 "got dad_transmits_remaining = {got_transmits_remaining:?},
1732 want dad_transmits_remaining = {dad_transmits_remaining:?}"
1733 );
1734 assert_eq!(probe_wait, &None);
1735 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1736 assert!(!*assigned);
1737 check_multicast_groups(I::VERSION, groups);
1738
1739 let frames = core_ctx.frames();
1740 assert_eq!(frames.len(), frames_len, "frames = {:?}", frames);
1741
1742 #[derive(GenericOverIp)]
1743 #[generic_over_ip(I, Ip)]
1744 struct Wrap<'a, I: TestDadIpExt> {
1745 meta: &'a I::SendData,
1746 ip_specific_state: &'a I::TentativeState,
1747 }
1748
1749 let (meta, frame) = frames.last().expect("should have transmitted a frame");
1750 assert_eq!(&frame[..], EmptyBuf.as_ref());
1751
1752 I::map_ip::<_, ()>(
1754 Wrap { meta, ip_specific_state },
1755 |Wrap { meta: Ipv4DadSendData { target_ip, probe_type }, ip_specific_state: () }| {
1756 assert_eq!(*target_ip, Ipv4::DAD_ADDRESS.get());
1757 assert_eq!(*probe_type, Ipv4SentProbeType::Probe);
1758 },
1759 |Wrap {
1760 meta: Ipv6DadSendData { dst_ip, message, nonce },
1761 ip_specific_state:
1762 Ipv6TentativeDadState {
1763 nonces,
1764 added_extra_transmits_after_detecting_looped_back_ns: _,
1765 },
1766 }| {
1767 assert_eq!(*dst_ip, Ipv6::DAD_ADDRESS.to_solicited_node_address());
1768 assert_eq!(*message, NeighborSolicitation::new(Ipv6::DAD_ADDRESS.get()));
1769 assert!(nonces.contains(nonce), "should have stored nonce");
1770 },
1771 );
1772
1773 bindings_ctx.timers.assert_timers_installed_range([(
1774 dad_timer_id(),
1775 I::expected_timer_range(bindings_ctx.now()),
1776 )]);
1777 }
1778
1779 fn check_probe_wait<I: TestDadIpExt>(core_ctx: &FakeDadContext<I>) {
1781 let FakeDadContext { state, .. } = core_ctx;
1782 let probe_wait = assert_matches!(
1783 state,
1784 DadState::Tentative { probe_wait, .. } => probe_wait
1785 );
1786 match I::VERSION {
1788 IpVersion::V4 => assert_matches!(probe_wait, Some(_)),
1789 IpVersion::V6 => assert_matches!(probe_wait, None),
1790 }
1791 }
1792
1793 fn skip_probe_wait<I: TestDadIpExt>(
1795 core_ctx: &mut FakeCoreCtxImpl<I>,
1796 bindings_ctx: &mut FakeBindingsCtxImpl<I>,
1797 ) {
1798 match I::VERSION {
1801 IpVersion::V4 => {
1802 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()))
1803 }
1804 IpVersion::V6 => {}
1805 }
1806 }
1807
1808 fn check_announcement<I: TestDadIpExt>(
1810 core_ctx: &FakeCoreCtxImpl<I>,
1811 frames_len: usize,
1812 announcements_remaining: Option<NonZeroU16>,
1813 ) {
1814 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1815 match announcements_remaining {
1816 None => assert_matches!(state, DadState::Assigned { ran_dad: true }),
1819 Some(announcements_remaining) => {
1821 let state = assert_matches!(
1822 state,
1823 DadState::Announcing {
1824 timer: _,
1825 ip_specific_state,
1826 } => ip_specific_state
1827 );
1828 #[derive(GenericOverIp)]
1829 #[generic_over_ip(I, Ip)]
1830 struct Wrap<'a, I: DadIpExt>(&'a I::AnnouncingState);
1831 let got_announcements_remaining = I::map_ip_in(
1832 Wrap(state),
1833 |Wrap(Ipv4AnnouncingDadState { announcements_remaining })| {
1834 *announcements_remaining
1835 },
1836 |Wrap(never)| match *never {},
1837 );
1838 assert_eq!(
1839 got_announcements_remaining, announcements_remaining,
1840 "got announcements_remaining = {got_announcements_remaining:?},
1841 want announcements_remaining = {announcements_remaining:?}"
1842 );
1843 }
1844 }
1845
1846 let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
1847 assert!(*assigned);
1848
1849 let frames = core_ctx.frames();
1850 assert_eq!(frames.len(), frames_len, "frames = {:?}", frames);
1851 let (meta, frame) = frames.last().expect("should have transmitted a frame");
1852 assert_eq!(&frame[..], EmptyBuf.as_ref());
1853
1854 #[derive(GenericOverIp)]
1855 #[generic_over_ip(I, Ip)]
1856 struct Wrap<'a, I: TestDadIpExt> {
1857 meta: &'a I::SendData,
1858 }
1859
1860 I::map_ip::<_, ()>(
1862 Wrap { meta },
1863 |Wrap { meta: Ipv4DadSendData { target_ip, probe_type } }| {
1864 assert_eq!(*target_ip, Ipv4::DAD_ADDRESS.get());
1865 assert_eq!(*probe_type, Ipv4SentProbeType::Announcement);
1866 },
1867 |_| unreachable!("DAD shouldn't send announcements for IPv6 addresses."),
1868 );
1869 }
1870
1871 #[ip_test(I)]
1872 fn perform_dad<I: TestDadIpExt>() {
1873 const DAD_TRANSMITS_REQUIRED: u16 = 5;
1874
1875 let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1876 state: DadState::Uninitialized,
1877 max_dad_transmits: NonZeroU16::new(DAD_TRANSMITS_REQUIRED),
1878 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1879 }));
1880 let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
1881 let address_id = get_address_id::<I>(I::DAD_ADDRESS);
1882 I::with_dad_handler(core_ctx, |core_ctx| {
1883 let start_dad = core_ctx.initialize_duplicate_address_detection(
1884 bindings_ctx,
1885 &FakeDeviceId,
1886 &address_id,
1887 into_state_change_event::<I>,
1888 );
1889 assert_eq!(
1890 bindings_ctx.take_events(),
1891 &[IpDeviceEvent::AddressStateChanged {
1892 device: FakeDeviceId,
1893 addr: I::DAD_ADDRESS.into(),
1894 state: IpAddressState::Tentative,
1895 }][..]
1896 );
1897 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
1898 check_probe_wait::<I>(core_ctx.as_ref());
1899 core_ctx.start_duplicate_address_detection(bindings_ctx, token);
1900 });
1901
1902 skip_probe_wait::<I>(core_ctx, bindings_ctx);
1903
1904 for count in 0..=(DAD_TRANSMITS_REQUIRED - 1) {
1905 check_probe_while_tentative(
1906 core_ctx,
1907 bindings_ctx,
1908 usize::from(count + 1),
1909 NonZeroU16::new(DAD_TRANSMITS_REQUIRED - count - 1),
1910 );
1911 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
1912 }
1913
1914 let FakeDadContext { address_ctx, .. } = &core_ctx.state;
1917 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1918 assert!(*assigned);
1919 check_multicast_groups(I::VERSION, groups);
1920 assert_eq!(
1921 bindings_ctx.take_events(),
1922 &[IpDeviceEvent::AddressStateChanged {
1923 device: FakeDeviceId,
1924 addr: I::DAD_ADDRESS.into(),
1925 state: IpAddressState::Assigned
1926 }][..]
1927 );
1928
1929 for count in 1..=I::EXPECTED_NUM_ANNOUNCEMENTS {
1930 check_announcement(
1931 core_ctx,
1932 usize::from(DAD_TRANSMITS_REQUIRED + count),
1933 NonZeroU16::new(I::EXPECTED_NUM_ANNOUNCEMENTS - count),
1934 );
1935 if count == I::EXPECTED_NUM_ANNOUNCEMENTS {
1938 bindings_ctx.timers.assert_no_timers_installed();
1939 } else {
1940 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
1941 }
1942 }
1943
1944 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1945 let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
1946 assert_matches!(*state, DadState::Assigned { ran_dad: true });
1947 assert!(*assigned);
1948 }
1949
1950 #[ip_test(I)]
1951 fn stop_dad_while_tentative<I: TestDadIpExt>() {
1952 const DAD_TRANSMITS_REQUIRED: u16 = 2;
1953
1954 let FakeCtx { mut core_ctx, mut bindings_ctx } =
1955 FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
1956 state: DadState::Uninitialized,
1957 max_dad_transmits: NonZeroU16::new(DAD_TRANSMITS_REQUIRED),
1958 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
1959 }));
1960 let address_id = get_address_id::<I>(I::DAD_ADDRESS);
1961 I::with_dad_handler(&mut core_ctx, |core_ctx| {
1962 let start_dad = core_ctx.initialize_duplicate_address_detection(
1963 &mut bindings_ctx,
1964 &FakeDeviceId,
1965 &address_id,
1966 into_state_change_event::<I>,
1967 );
1968 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
1969 core_ctx.start_duplicate_address_detection(&mut bindings_ctx, token);
1970 });
1971
1972 skip_probe_wait::<I>(&mut core_ctx, &mut bindings_ctx);
1973
1974 check_probe_while_tentative(
1975 &core_ctx,
1976 &bindings_ctx,
1977 1,
1978 NonZeroU16::new(DAD_TRANSMITS_REQUIRED - 1),
1979 );
1980
1981 I::with_dad_handler(&mut core_ctx, |core_ctx| {
1982 core_ctx.stop_duplicate_address_detection(
1983 &mut bindings_ctx,
1984 &FakeDeviceId,
1985 &address_id,
1986 );
1987 });
1988
1989 bindings_ctx.timers.assert_no_timers_installed();
1990 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
1991 assert_matches!(*state, DadState::Uninitialized);
1992 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
1993 assert!(!*assigned);
1994 assert_eq!(groups, &HashMap::new());
1995 }
1996
1997 #[test]
1998 fn stop_dad_while_announcing() {
1999 let FakeCtx { mut core_ctx, mut bindings_ctx } =
2002 FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
2003 state: DadState::Uninitialized,
2004 max_dad_transmits: NonZeroU16::new(1),
2005 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2006 }));
2007 let address_id = get_address_id::<Ipv4>(Ipv4::DAD_ADDRESS);
2008
2009 let start_dad = core_ctx.initialize_duplicate_address_detection(
2011 &mut bindings_ctx,
2012 &FakeDeviceId,
2013 &address_id,
2014 into_state_change_event::<Ipv4>,
2015 );
2016 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
2017 core_ctx.start_duplicate_address_detection(&mut bindings_ctx, token);
2018 skip_probe_wait::<Ipv4>(&mut core_ctx, &mut bindings_ctx);
2019 check_probe_while_tentative(
2020 &core_ctx,
2021 &bindings_ctx,
2022 1,
2023 None, );
2025 assert_eq!(bindings_ctx.trigger_next_timer(&mut core_ctx), Some(dad_timer_id()));
2026
2027 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
2029 let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
2030 assert_matches!(*state, DadState::Announcing { .. });
2031 bindings_ctx.timers.assert_timers_installed_range([(dad_timer_id(), ..)]);
2032 assert!(*assigned);
2033
2034 core_ctx.stop_duplicate_address_detection(&mut bindings_ctx, &FakeDeviceId, &address_id);
2036 bindings_ctx.timers.assert_no_timers_installed();
2037 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
2038 let FakeDadAddressContext { assigned, .. } = &address_ctx.state;
2039 assert_matches!(*state, DadState::Uninitialized);
2040 assert!(!*assigned);
2041 }
2042
2043 enum IncomingArpPacketCase {
2044 Unavailable,
2045 Assigned { ran_dad: bool, receive_data: Ipv4DadAddressInfo },
2046 Tentative,
2047 }
2048
2049 #[test_case(IncomingArpPacketCase::Unavailable; "uninitialized")]
2050 #[test_case(IncomingArpPacketCase::Tentative; "tentative")]
2051 #[test_case(IncomingArpPacketCase::Assigned{
2052 ran_dad: true, receive_data: Ipv4DadAddressInfo::SourceAddr
2053 }; "assigned_ran_dad_source_addr")]
2054 #[test_case(IncomingArpPacketCase::Assigned {
2055 ran_dad: true, receive_data: Ipv4DadAddressInfo::TargetAddr
2056 }; "assigned_ran_dad_target_addr")]
2057 #[test_case(IncomingArpPacketCase::Assigned {
2058 ran_dad: false, receive_data: Ipv4DadAddressInfo::SourceAddr
2059 }; "assigned_skipped_dad_source_addr")]
2060 #[test_case(IncomingArpPacketCase::Assigned {
2061 ran_dad: false, receive_data: Ipv4DadAddressInfo::TargetAddr
2062 }; "assigned_skipped_dad_target_addr")]
2063 fn handle_incoming_arp_packet(case: IncomingArpPacketCase) {
2064 let mut ctx = FakeCtx::with_default_bindings_ctx(|bindings_ctx| {
2065 let dad_state = match &case {
2066 IncomingArpPacketCase::Unavailable => DadState::Uninitialized,
2067 IncomingArpPacketCase::Assigned { ran_dad, receive_data: _ } => {
2068 DadState::Assigned { ran_dad: *ran_dad }
2069 }
2070 IncomingArpPacketCase::Tentative => DadState::Tentative {
2071 dad_transmits_remaining: NonZeroU16::new(1),
2072 timer: bindings_ctx.new_timer(dad_timer_id()),
2073 ip_specific_state: Default::default(),
2074 probe_wait: None,
2075 },
2076 };
2077
2078 FakeCoreCtxImpl::with_state(FakeDadContext {
2079 state: dad_state,
2080 max_dad_transmits: NonZeroU16::new(1),
2081 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2082 })
2083 });
2084
2085 let (want_lookup_result, optional_receive_data) = match case {
2086 IncomingArpPacketCase::Unavailable => (DadIncomingPacketResult::Uninitialized, None),
2087 IncomingArpPacketCase::Tentative => {
2088 (DadIncomingPacketResult::Tentative { meta: () }, None)
2089 }
2090 IncomingArpPacketCase::Assigned { ran_dad, receive_data } => {
2091 let should_remove = match (ran_dad, &receive_data) {
2092 (true, Ipv4DadAddressInfo::SourceAddr) => true,
2093 _ => false,
2094 };
2095 (DadIncomingPacketResult::Assigned { should_remove }, Some(receive_data))
2096 }
2097 };
2098
2099 let addr = get_address_id::<Ipv4>(Ipv4::DAD_ADDRESS);
2100 let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
2101 const ARBITRARY_RECEIVE_DATA: Ipv4DadAddressInfo = Ipv4DadAddressInfo::SourceAddr;
2102 assert_eq!(
2103 DadHandler::<Ipv4, _>::handle_incoming_packet(
2104 core_ctx,
2105 bindings_ctx,
2106 &FakeDeviceId,
2107 &addr,
2108 optional_receive_data.unwrap_or(ARBITRARY_RECEIVE_DATA),
2109 ),
2110 want_lookup_result
2111 );
2112 }
2113
2114 #[test_case(true, None ; "assigned with no incoming nonce")]
2115 #[test_case(true, Some([1u8; MIN_NONCE_LENGTH]) ; "assigned with incoming nonce")]
2116 #[test_case(false, None ; "uninitialized with no incoming nonce")]
2117 #[test_case(false, Some([1u8; MIN_NONCE_LENGTH]) ; "uninitialized with incoming nonce")]
2118 fn handle_incoming_dad_neighbor_solicitation_while_not_tentative(
2119 assigned: bool,
2120 nonce: Option<OwnedNdpNonce>,
2121 ) {
2122 const MAX_DAD_TRANSMITS: u16 = 1;
2123
2124 let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
2125 state: if assigned {
2126 DadState::Assigned { ran_dad: true }
2127 } else {
2128 DadState::Uninitialized
2129 },
2130 max_dad_transmits: NonZeroU16::new(MAX_DAD_TRANSMITS),
2131 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2132 }));
2133 let addr = get_address_id::<Ipv6>(Ipv6::DAD_ADDRESS);
2134
2135 let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
2136
2137 let want_lookup_result = if assigned {
2138 DadIncomingPacketResult::Assigned { should_remove: false }
2139 } else {
2140 DadIncomingPacketResult::Uninitialized
2141 };
2142
2143 assert_eq!(
2144 DadHandler::<Ipv6, _>::handle_incoming_packet(
2145 core_ctx,
2146 bindings_ctx,
2147 &FakeDeviceId,
2148 &addr,
2149 nonce.as_ref().map(NdpNonce::from),
2150 ),
2151 want_lookup_result
2152 );
2153 }
2154
2155 #[test_case(true ; "discards looped back NS")]
2156 #[test_case(false ; "acts on non-looped-back NS")]
2157 fn handle_incoming_dad_neighbor_solicitation_during_tentative(looped_back: bool) {
2158 const DAD_TRANSMITS_REQUIRED: u16 = 1;
2159
2160 let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
2161 state: DadState::Uninitialized,
2162 max_dad_transmits: NonZeroU16::new(DAD_TRANSMITS_REQUIRED),
2163 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2164 }));
2165 let addr = get_address_id::<Ipv6>(Ipv6::DAD_ADDRESS);
2166
2167 let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
2168 let address_id = get_address_id::<Ipv6>(Ipv6::DAD_ADDRESS);
2169 let start_dad = DadHandler::<Ipv6, _>::initialize_duplicate_address_detection(
2170 core_ctx,
2171 bindings_ctx,
2172 &FakeDeviceId,
2173 &address_id,
2174 into_state_change_event::<Ipv6>,
2175 );
2176 assert_eq!(
2177 bindings_ctx.take_events(),
2178 &[IpDeviceEvent::AddressStateChanged {
2179 device: FakeDeviceId,
2180 addr: Ipv6::DAD_ADDRESS.into(),
2181 state: IpAddressState::Tentative,
2182 }][..]
2183 );
2184 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
2185 DadHandler::<Ipv6, _>::start_duplicate_address_detection(core_ctx, bindings_ctx, token);
2186
2187 check_probe_while_tentative(core_ctx, bindings_ctx, 1, None);
2188
2189 let sent_nonce: OwnedNdpNonce = {
2190 let (Ipv6DadSendData { dst_ip: _, message: _, nonce }, _frame) =
2191 core_ctx.frames().last().expect("should have transmitted a frame");
2192 *nonce
2193 };
2194
2195 let alternative_nonce = {
2196 let mut nonce = sent_nonce.clone();
2197 nonce[0] = nonce[0].wrapping_add(1);
2198 nonce
2199 };
2200
2201 let incoming_nonce =
2202 NdpNonce::from(if looped_back { &sent_nonce } else { &alternative_nonce });
2203
2204 let matched_nonce = assert_matches!(
2205 DadHandler::<Ipv6, _>::handle_incoming_packet(
2206 core_ctx,
2207 bindings_ctx,
2208 &FakeDeviceId,
2209 &addr,
2210 Some(incoming_nonce),
2211 ),
2212 DadIncomingPacketResult::Tentative {
2213 meta: Ipv6PacketResultMetadata {matched_nonce}
2214 } => matched_nonce
2215 );
2216
2217 assert_eq!(matched_nonce, looped_back);
2218
2219 let frames_len_before_extra_transmits = core_ctx.frames().len();
2220 assert_eq!(frames_len_before_extra_transmits, 1);
2221
2222 let extra_dad_transmits_required =
2223 NonZero::new(if looped_back { DEFAULT_MAX_MULTICAST_SOLICIT.get() } else { 0 });
2224
2225 let (dad_transmits_remaining, added_extra_transmits_after_detecting_looped_back_ns) = assert_matches!(
2226 &core_ctx.state.state,
2227 DadState::Tentative {
2228 dad_transmits_remaining,
2229 timer: _,
2230 ip_specific_state: Ipv6TentativeDadState {
2231 nonces: _,
2232 added_extra_transmits_after_detecting_looped_back_ns
2233 },
2234 probe_wait: None,
2235 } => (dad_transmits_remaining, added_extra_transmits_after_detecting_looped_back_ns),
2236 "DAD state should be Tentative"
2237 );
2238
2239 assert_eq!(dad_transmits_remaining, &extra_dad_transmits_required);
2240 assert_eq!(added_extra_transmits_after_detecting_looped_back_ns, &matched_nonce);
2241
2242 let extra_dad_transmits_required =
2243 extra_dad_transmits_required.map(|n| n.get()).unwrap_or(0);
2244
2245 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
2247
2248 for count in 0..extra_dad_transmits_required {
2251 check_probe_while_tentative(
2252 core_ctx,
2253 bindings_ctx,
2254 usize::from(count) + frames_len_before_extra_transmits + 1,
2255 NonZeroU16::new(extra_dad_transmits_required - count - 1),
2256 );
2257 assert_eq!(bindings_ctx.trigger_next_timer(core_ctx), Some(dad_timer_id()));
2258 }
2259 let FakeDadContext { state, address_ctx, .. } = &core_ctx.state;
2260 assert_matches!(*state, DadState::Assigned { ran_dad: true });
2261 let FakeDadAddressContext { assigned, groups, .. } = &address_ctx.state;
2262 assert!(*assigned);
2263 assert_eq!(groups, &HashMap::from([(Ipv6::DAD_ADDRESS.to_solicited_node_address(), 1)]));
2264 assert_eq!(
2265 bindings_ctx.take_events(),
2266 &[IpDeviceEvent::AddressStateChanged {
2267 device: FakeDeviceId,
2268 addr: Ipv6::DAD_ADDRESS.into(),
2269 state: IpAddressState::Assigned
2270 }][..]
2271 );
2272 }
2273
2274 #[test]
2275 fn ipv4_dad_probe_interval_is_valid() {
2276 FakeCryptoRng::with_fake_rngs(100, |mut bindings_ctx| {
2279 let duration = ipv4_dad_probe_interval(&mut bindings_ctx).get();
2280 assert!(IPV4_PROBE_RANGE.contains(&duration), "actual={duration:?}");
2281 })
2282 }
2283
2284 #[test]
2285 fn ipv4_dad_probe_wait_is_valid() {
2286 FakeCryptoRng::with_fake_rngs(100, |mut bindings_ctx| {
2289 let duration = ipv4_dad_probe_wait(&mut bindings_ctx);
2290 assert!(IPV4_PROBE_WAIT_RANGE.contains(&duration), "actual={duration:?}");
2291 })
2292 }
2293
2294 #[ip_test(I)]
2295 fn dad_ignores_stale_timers<I: TestDadIpExt>() {
2296 const DAD_TRANSMITS_REQUIRED: u16 = 1;
2297
2298 let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtxImpl::with_state(FakeDadContext {
2299 state: DadState::Uninitialized,
2300 max_dad_transmits: NonZeroU16::new(DAD_TRANSMITS_REQUIRED),
2301 address_ctx: FakeAddressCtxImpl::with_state(FakeDadAddressContext::default()),
2302 }));
2303 let FakeCtx { core_ctx, bindings_ctx } = &mut ctx;
2304 let address_id = get_address_id::<I>(I::DAD_ADDRESS);
2305 I::with_dad_handler(core_ctx, |core_ctx| {
2306 let start_dad = core_ctx.initialize_duplicate_address_detection(
2307 bindings_ctx,
2308 &FakeDeviceId,
2309 &address_id,
2310 into_state_change_event::<I>,
2311 );
2312 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
2313 core_ctx.start_duplicate_address_detection(bindings_ctx, token);
2314 });
2315
2316 let old_timer = assert_matches!(
2317 &core_ctx.state.state,
2318 DadState::Tentative { timer, .. } => timer.clone()
2319 );
2320
2321 I::with_dad_handler(core_ctx, |core_ctx| {
2323 core_ctx.stop_duplicate_address_detection(bindings_ctx, &FakeDeviceId, &address_id);
2324 let start_dad = core_ctx.initialize_duplicate_address_detection(
2325 bindings_ctx,
2326 &FakeDeviceId,
2327 &address_id,
2328 into_state_change_event::<I>,
2329 );
2330 let token = assert_matches!(start_dad, NeedsDad::Yes(token) => token);
2331 core_ctx.start_duplicate_address_detection(bindings_ctx, token);
2332 });
2333
2334 skip_probe_wait::<I>(core_ctx, bindings_ctx);
2335
2336 assert_matches!(core_ctx.state.state, DadState::Tentative { .. });
2337 let unique_timer_id = bindings_ctx.unique_timer_id(&old_timer);
2338 old_timer.dispatch_id.handle(core_ctx, bindings_ctx, unique_timer_id);
2339 assert_matches!(core_ctx.state.state, DadState::Tentative { .. });
2340
2341 let mut new_timer = assert_matches!(
2344 &core_ctx.state.state,
2345 DadState::Tentative { timer, .. } => timer.clone()
2346 );
2347 assert_matches!(bindings_ctx.cancel_timer(&mut new_timer), Some(_));
2349 let unique_timer_id = bindings_ctx.unique_timer_id(&new_timer);
2350 new_timer.dispatch_id.handle(core_ctx, bindings_ctx, unique_timer_id);
2351 match I::VERSION {
2352 IpVersion::V4 => assert_matches!(core_ctx.state.state, DadState::Announcing { .. }),
2353 IpVersion::V6 => assert_matches!(core_ctx.state.state, DadState::Assigned { .. }),
2354 };
2355 }
2356}