1use alloc::vec::Vec;
8use core::fmt::Debug;
9use core::hash::Hash;
10use core::num::{NonZeroU8, NonZeroU16};
11use core::ops::{Deref, DerefMut};
12use core::time::Duration;
13
14use derivative::Derivative;
15use lock_order::lock::{OrderedLockAccess, OrderedLockRef};
16use net_types::Witness as _;
17use net_types::ip::{AddrSubnet, GenericOverIp, Ip, IpMarked, IpVersionMarker, Ipv4, Ipv6};
18use netstack3_base::sync::{Mutex, PrimaryRc, RwLock, StrongRc, WeakRc};
19use netstack3_base::{
20 AssignedAddrIpExt, BindingsSyncTypes, BroadcastIpExt, CoreTimerContext, ExistsError,
21 Inspectable, InspectableValue, Inspector, Instant, InstantBindingsTypes, IpAddressId,
22 NestedIntoCoreTimerCtx, NotFoundError, ReferenceNotifiers, TimerBindingsTypes, TimerContext,
23 WeakDeviceIdentifier,
24};
25use packet_formats::icmp::ndp::NonZeroNdpLifetime;
26use packet_formats::utils::NonZeroDuration;
27
28use crate::internal::counters::{IpCounters, IpCountersIpExt};
29use crate::internal::device::dad::{DadIpExt, DadState};
30use crate::internal::device::route_discovery::{
31 Ipv6RouteDiscoveryState, RouteDiscoveryConfiguration,
32};
33use crate::internal::device::router_solicitation::RsState;
34use crate::internal::device::slaac::{SlaacConfiguration, SlaacState};
35use crate::internal::device::{
36 IpDeviceAddr, IpDeviceTimerId, Ipv4DeviceTimerId, Ipv6DeviceTimerId, WeakIpAddressId,
37};
38use crate::internal::gmp::igmp::{IgmpConfig, IgmpCounters, IgmpTimerId, IgmpTypeLayout};
39use crate::internal::gmp::mld::{MldConfig, MldCounters, MldTimerId, MldTypeLayout};
40use crate::internal::gmp::{GmpGroupState, GmpState, GmpTimerId, GmpTypeLayout, MulticastGroupSet};
41use crate::internal::types::RawMetric;
42
43const DEFAULT_HOP_LIMIT: NonZeroU8 = NonZeroU8::new(64).unwrap();
46
47pub trait IpDeviceStateIpExt:
49 DadIpExt + BroadcastIpExt + IpCountersIpExt + AssignedAddrIpExt
50{
51 type AddressConfig<I: Instant>: Default + Debug + Inspectable + PartialEq + Send + Sync;
53 type GmpProtoConfig: Default;
55 type GmpTypeLayout<BT: IpDeviceStateBindingsTypes>: GmpTypeLayout<Self, BT>;
57 type GmpTimerId<D: WeakDeviceIdentifier>: From<GmpTimerId<Self, D>>;
59}
60
61impl IpDeviceStateIpExt for Ipv4 {
62 type AddressConfig<I: Instant> = Ipv4AddrConfig<I>;
63 type GmpTimerId<D: WeakDeviceIdentifier> = IgmpTimerId<D>;
64 type GmpProtoConfig = IgmpConfig;
65 type GmpTypeLayout<BT: IpDeviceStateBindingsTypes> = IgmpTypeLayout;
66}
67
68impl IpDeviceStateIpExt for Ipv6 {
69 type AddressConfig<I: Instant> = Ipv6AddrConfig<I>;
70 type GmpTimerId<D: WeakDeviceIdentifier> = MldTimerId<D>;
71 type GmpProtoConfig = MldConfig;
72 type GmpTypeLayout<BT: IpDeviceStateBindingsTypes> = MldTypeLayout;
73}
74
75pub struct PrimaryAddressId<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>(
78 PrimaryRc<IpAddressEntry<I, BT>>,
79);
80
81impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> Debug for PrimaryAddressId<I, BT> {
82 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
83 let Self(rc) = self;
84 write!(f, "PrimaryAddressId({:?} => {})", rc.debug_id(), rc.addr_sub)
85 }
86}
87
88impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> Deref for PrimaryAddressId<I, BT> {
89 type Target = IpAddressEntry<I, BT>;
90
91 fn deref(&self) -> &Self::Target {
92 let Self(inner) = self;
93 inner.deref()
94 }
95}
96
97impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> PrimaryAddressId<I, BT> {
98 fn new_with_strong_clone(addr: IpAddressEntry<I, BT>) -> (Self, AddressId<I, BT>) {
100 let primary = PrimaryRc::new(addr);
101 let strong = PrimaryRc::clone_strong(&primary);
102 (Self(primary), AddressId(strong))
103 }
104
105 pub fn clone_strong(&self) -> AddressId<I, BT> {
107 let Self(inner) = self;
108 AddressId(PrimaryRc::clone_strong(inner))
109 }
110
111 pub fn ptr_eq(&self, other: &AddressId<I, BT>) -> bool {
113 let Self(inner) = self;
114 let AddressId(other) = other;
115 PrimaryRc::ptr_eq(inner, other)
116 }
117
118 pub fn into_inner(self) -> PrimaryRc<IpAddressEntry<I, BT>> {
120 self.0
121 }
122
123 pub fn addr(&self) -> I::AssignedWitness {
125 let Self(inner) = self;
126 inner.addr_sub.addr()
127 }
128}
129
130#[derive(Derivative)]
132#[derivative(Clone(bound = ""), Eq(bound = ""), Hash(bound = ""), PartialEq(bound = ""))]
133pub struct AddressId<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>(
134 StrongRc<IpAddressEntry<I, BT>>,
135);
136
137impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> Debug for AddressId<I, BT> {
138 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
139 let Self(rc) = self;
140 write!(f, "AddressId({:?} => {})", rc.debug_id(), self.addr_sub)
141 }
142}
143
144impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> Deref for AddressId<I, BT> {
145 type Target = IpAddressEntry<I, BT>;
146
147 fn deref(&self) -> &Self::Target {
148 let Self(inner) = self;
149 inner.deref()
150 }
151}
152
153impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> IpAddressId<I::Addr>
154 for AddressId<I, BT>
155{
156 type Weak = WeakAddressId<I, BT>;
157
158 fn downgrade(&self) -> Self::Weak {
159 let Self(inner) = self;
160 WeakAddressId(StrongRc::downgrade(inner))
161 }
162
163 fn addr(&self) -> IpDeviceAddr<I::Addr> {
164 let Self(inner) = self;
165 inner.addr_sub().addr().into()
166 }
167
168 fn addr_sub(&self) -> AddrSubnet<I::Addr, I::AssignedWitness> {
169 let Self(inner) = self;
170 *inner.addr_sub()
171 }
172}
173
174#[derive(Derivative)]
176#[derivative(Clone(bound = ""), Eq(bound = ""), Hash(bound = ""), PartialEq(bound = ""))]
177pub struct WeakAddressId<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>(
178 WeakRc<IpAddressEntry<I, BT>>,
179);
180
181impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> Debug for WeakAddressId<I, BT> {
182 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
183 let Self(rc) = self;
184 if let Some(id) = self.upgrade() {
185 write!(f, "WeakAddressId({:?} => {})", rc.debug_id(), id.addr_sub)
186 } else {
187 write!(f, "WeakAddressId({:?} => {})", rc.debug_id(), I::NAME)
188 }
189 }
190}
191
192impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> InspectableValue
193 for WeakAddressId<I, BT>
194{
195 fn record<II: Inspector>(&self, name: &str, inspector: &mut II) {
196 inspector.record_debug(name, self);
197 }
198}
199
200impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> WeakIpAddressId<I::Addr>
201 for WeakAddressId<I, BT>
202{
203 type Strong = AddressId<I, BT>;
204
205 fn upgrade(&self) -> Option<Self::Strong> {
206 let Self(inner) = self;
207 inner.upgrade().map(AddressId)
208 }
209
210 fn is_assigned(&self) -> bool {
211 let Self(inner) = self;
212 inner.strong_count() != 0
213 }
214}
215
216#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
218pub struct IpDeviceFlags {
219 pub ip_enabled: bool,
221}
222
223pub struct IpDeviceMulticastGroups<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> {
225 pub groups: MulticastGroupSet<I::Addr, GmpGroupState<I, BT>>,
227 pub gmp: GmpState<I, I::GmpTypeLayout<BT>, BT>,
229 pub gmp_config: I::GmpProtoConfig,
231}
232
233#[derive(Copy, Clone, Debug)]
238pub struct DefaultHopLimit<I: Ip>(NonZeroU8, IpVersionMarker<I>);
239
240impl<I: Ip> Deref for DefaultHopLimit<I> {
241 type Target = NonZeroU8;
242 fn deref(&self) -> &NonZeroU8 {
243 let Self(value, IpVersionMarker { .. }) = self;
244 value
245 }
246}
247
248impl<I: Ip> DerefMut for DefaultHopLimit<I> {
249 fn deref_mut(&mut self) -> &mut NonZeroU8 {
250 let Self(value, IpVersionMarker { .. }) = self;
251 value
252 }
253}
254
255impl<I: Ip> Default for DefaultHopLimit<I> {
256 fn default() -> Self {
257 Self(DEFAULT_HOP_LIMIT, IpVersionMarker::new())
258 }
259}
260
261#[derive(GenericOverIp)]
263#[generic_over_ip(I, Ip)]
264pub struct IpDeviceState<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> {
265 addrs: RwLock<IpDeviceAddresses<I, BT>, BT::RwLock>,
272
273 multicast_groups: RwLock<IpDeviceMulticastGroups<I, BT>, BT::RwLock>,
275
276 default_hop_limit: RwLock<DefaultHopLimit<I>, BT::RwLock>,
279
280 flags: Mutex<IpMarked<I, IpDeviceFlags>, BT::Mutex>,
282
283 ip_counters: IpCounters<I>,
285}
286
287impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>
288 OrderedLockAccess<IpDeviceAddresses<I, BT>> for DualStackIpDeviceState<BT>
289{
290 type Lock = RwLock<IpDeviceAddresses<I, BT>, BT::RwLock>;
291 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
292 OrderedLockRef::new(&self.ip_state::<I>().addrs)
293 }
294}
295
296impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>
297 OrderedLockAccess<IpDeviceMulticastGroups<I, BT>> for DualStackIpDeviceState<BT>
298{
299 type Lock = RwLock<IpDeviceMulticastGroups<I, BT>, BT::RwLock>;
300 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
301 OrderedLockRef::new(&self.ip_state::<I>().multicast_groups)
302 }
303}
304
305impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> OrderedLockAccess<DefaultHopLimit<I>>
306 for DualStackIpDeviceState<BT>
307{
308 type Lock = RwLock<DefaultHopLimit<I>, BT::RwLock>;
309 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
310 OrderedLockRef::new(&self.ip_state::<I>().default_hop_limit)
311 }
312}
313
314impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>
315 OrderedLockAccess<IpMarked<I, IpDeviceFlags>> for DualStackIpDeviceState<BT>
316{
317 type Lock = Mutex<IpMarked<I, IpDeviceFlags>, BT::Mutex>;
318 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
319 OrderedLockRef::new(&self.ip_state::<I>().flags)
320 }
321}
322
323impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<SlaacState<BT>>
324 for DualStackIpDeviceState<BT>
325{
326 type Lock = Mutex<SlaacState<BT>, BT::Mutex>;
327 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
328 OrderedLockRef::new(&self.ipv6.slaac_state)
329 }
330}
331
332impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> IpDeviceState<I, BT> {
333 #[cfg(any(test, feature = "testutils"))]
335 pub fn addrs(&self) -> &RwLock<IpDeviceAddresses<I, BT>, BT::RwLock> {
336 &self.addrs
337 }
338}
339
340impl<I: IpDeviceStateIpExt, BC: IpDeviceStateBindingsTypes + TimerContext> IpDeviceState<I, BC> {
341 fn new<D: WeakDeviceIdentifier, CC: CoreTimerContext<I::GmpTimerId<D>, BC>>(
342 bindings_ctx: &mut BC,
343 device_id: D,
344 ) -> IpDeviceState<I, BC> {
345 IpDeviceState {
346 addrs: Default::default(),
347 multicast_groups: RwLock::new(IpDeviceMulticastGroups {
348 groups: Default::default(),
349 gmp: GmpState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(bindings_ctx, device_id),
350 gmp_config: Default::default(),
351 }),
352 default_hop_limit: Default::default(),
353 flags: Default::default(),
354 ip_counters: Default::default(),
355 }
356 }
357}
358
359#[derive(Derivative)]
361#[derivative(Default(bound = ""))]
362#[cfg_attr(test, derive(Debug))]
363pub struct IpDeviceAddresses<I: Ip + IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> {
364 addrs: Vec<PrimaryAddressId<I, BT>>,
365}
366
367impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> IpDeviceAddresses<I, BT> {
371 pub fn iter(
373 &self,
374 ) -> impl ExactSizeIterator<Item = &PrimaryAddressId<I, BT>> + ExactSizeIterator + Clone {
375 self.addrs.iter()
376 }
377
378 pub fn strong_iter(&self) -> AddressIdIter<'_, I, BT> {
380 AddressIdIter(self.addrs.iter())
381 }
382
383 pub fn add(&mut self, addr: IpAddressEntry<I, BT>) -> Result<AddressId<I, BT>, ExistsError> {
385 if self.iter().any(|a| a.addr() == addr.addr_sub.addr()) {
386 return Err(ExistsError);
387 }
388 let (primary, strong) = PrimaryAddressId::new_with_strong_clone(addr);
389 self.addrs.push(primary);
390 Ok(strong)
391 }
392
393 pub fn remove(&mut self, addr: &I::Addr) -> Result<PrimaryAddressId<I, BT>, NotFoundError> {
395 let (index, _entry): (_, &PrimaryAddressId<I, BT>) = self
396 .addrs
397 .iter()
398 .enumerate()
399 .find(|(_, entry)| entry.addr().get() == *addr)
400 .ok_or(NotFoundError)?;
401 Ok(self.addrs.remove(index))
402 }
403}
404
405pub struct AddressIdIter<'a, I: Ip + IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>(
407 core::slice::Iter<'a, PrimaryAddressId<I, BT>>,
408);
409
410impl<'a, I: Ip + IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> Iterator
411 for AddressIdIter<'a, I, BT>
412{
413 type Item = AddressId<I, BT>;
414
415 fn next(&mut self) -> Option<Self::Item> {
416 let Self(inner) = self;
417 inner.next().map(|addr| addr.clone_strong())
418 }
419}
420
421pub struct Ipv4DeviceState<BT: IpDeviceStateBindingsTypes> {
423 ip_state: IpDeviceState<Ipv4, BT>,
424 config: RwLock<Ipv4DeviceConfiguration, BT::RwLock>,
425 igmp_counters: IgmpCounters,
426}
427
428impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv4DeviceConfiguration>
429 for DualStackIpDeviceState<BT>
430{
431 type Lock = RwLock<Ipv4DeviceConfiguration, BT::RwLock>;
432 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
433 OrderedLockRef::new(&self.ipv4.config)
434 }
435}
436
437impl<BC: IpDeviceStateBindingsTypes + TimerContext> Ipv4DeviceState<BC> {
438 fn new<D: WeakDeviceIdentifier, CC: CoreTimerContext<Ipv4DeviceTimerId<D, BC>, BC>>(
439 bindings_ctx: &mut BC,
440 device_id: D,
441 ) -> Ipv4DeviceState<BC> {
442 Ipv4DeviceState {
443 ip_state: IpDeviceState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(
444 bindings_ctx,
445 device_id,
446 ),
447 config: Default::default(),
448 igmp_counters: Default::default(),
449 }
450 }
451}
452
453impl<BT: IpDeviceStateBindingsTypes> Ipv4DeviceState<BT> {
454 fn igmp_counters(&self) -> &IgmpCounters {
455 &self.igmp_counters
456 }
457}
458
459impl<BT: IpDeviceStateBindingsTypes> AsRef<IpDeviceState<Ipv4, BT>> for Ipv4DeviceState<BT> {
460 fn as_ref(&self) -> &IpDeviceState<Ipv4, BT> {
461 &self.ip_state
462 }
463}
464
465impl<BT: IpDeviceStateBindingsTypes> AsMut<IpDeviceState<Ipv4, BT>> for Ipv4DeviceState<BT> {
466 fn as_mut(&mut self) -> &mut IpDeviceState<Ipv4, BT> {
467 &mut self.ip_state
468 }
469}
470
471#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
473pub struct IpDeviceConfiguration {
474 pub gmp_enabled: bool,
482
483 pub unicast_forwarding_enabled: bool,
495
496 pub multicast_forwarding_enabled: bool,
509 pub dad_transmits: Option<NonZeroU16>,
519}
520
521#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
523pub struct Ipv4DeviceConfiguration {
524 pub ip_config: IpDeviceConfiguration,
526}
527
528impl Ipv4DeviceConfiguration {
529 pub const DEFAULT_DUPLICATE_ADDRESS_DETECTION_TRANSMITS: NonZeroU16 =
531 NonZeroU16::new(3).unwrap();
532}
533
534impl AsRef<IpDeviceConfiguration> for Ipv4DeviceConfiguration {
535 fn as_ref(&self) -> &IpDeviceConfiguration {
536 &self.ip_config
537 }
538}
539
540impl AsMut<IpDeviceConfiguration> for Ipv4DeviceConfiguration {
541 fn as_mut(&mut self) -> &mut IpDeviceConfiguration {
542 &mut self.ip_config
543 }
544}
545
546#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
548pub struct Ipv6DeviceConfiguration {
549 pub max_router_solicitations: Option<NonZeroU8>,
558
559 pub slaac_config: SlaacConfiguration,
561
562 pub route_discovery_config: RouteDiscoveryConfiguration,
564
565 pub ip_config: IpDeviceConfiguration,
567}
568
569impl Ipv6DeviceConfiguration {
570 pub const DEFAULT_MAX_RTR_SOLICITATIONS: NonZeroU8 = NonZeroU8::new(3).unwrap();
574
575 pub const DEFAULT_DUPLICATE_ADDRESS_DETECTION_TRANSMITS: NonZeroU16 =
579 NonZeroU16::new(1).unwrap();
580}
581
582impl AsRef<IpDeviceConfiguration> for Ipv6DeviceConfiguration {
583 fn as_ref(&self) -> &IpDeviceConfiguration {
584 &self.ip_config
585 }
586}
587
588impl AsMut<IpDeviceConfiguration> for Ipv6DeviceConfiguration {
589 fn as_mut(&mut self) -> &mut IpDeviceConfiguration {
590 &mut self.ip_config
591 }
592}
593
594impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv6NetworkLearnedParameters>
595 for DualStackIpDeviceState<BT>
596{
597 type Lock = RwLock<Ipv6NetworkLearnedParameters, BT::RwLock>;
598 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
599 OrderedLockRef::new(&self.ipv6.learned_params)
600 }
601}
602
603impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv6RouteDiscoveryState<BT>>
604 for DualStackIpDeviceState<BT>
605{
606 type Lock = Mutex<Ipv6RouteDiscoveryState<BT>, BT::Mutex>;
607 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
608 OrderedLockRef::new(&self.ipv6.route_discovery)
609 }
610}
611
612impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<RsState<BT>> for DualStackIpDeviceState<BT> {
613 type Lock = Mutex<RsState<BT>, BT::Mutex>;
614 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
615 OrderedLockRef::new(&self.ipv6.router_solicitations)
616 }
617}
618
619impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv6DeviceConfiguration>
620 for DualStackIpDeviceState<BT>
621{
622 type Lock = RwLock<Ipv6DeviceConfiguration, BT::RwLock>;
623 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
624 OrderedLockRef::new(&self.ipv6.config)
625 }
626}
627
628#[derive(Default)]
630pub struct Ipv6NetworkLearnedParameters {
631 pub retrans_timer: Option<NonZeroDuration>,
640}
641
642impl Ipv6NetworkLearnedParameters {
643 pub fn retrans_timer(&self) -> Option<NonZeroDuration> {
646 self.retrans_timer.clone()
647 }
648
649 pub fn reset(&mut self) {
651 *self = Default::default()
652 }
653}
654
655pub struct Ipv6DeviceState<BT: IpDeviceStateBindingsTypes> {
657 learned_params: RwLock<Ipv6NetworkLearnedParameters, BT::RwLock>,
658 route_discovery: Mutex<Ipv6RouteDiscoveryState<BT>, BT::Mutex>,
659 router_solicitations: Mutex<RsState<BT>, BT::Mutex>,
660 ip_state: IpDeviceState<Ipv6, BT>,
661 config: RwLock<Ipv6DeviceConfiguration, BT::RwLock>,
662 slaac_state: Mutex<SlaacState<BT>, BT::Mutex>,
663 mld_counters: MldCounters,
664}
665
666impl<BC: IpDeviceStateBindingsTypes + TimerContext> Ipv6DeviceState<BC> {
667 pub fn new<D: WeakDeviceIdentifier, CC: CoreTimerContext<Ipv6DeviceTimerId<D, BC>, BC>>(
668 bindings_ctx: &mut BC,
669 device_id: D,
670 ) -> Self {
671 Ipv6DeviceState {
672 learned_params: Default::default(),
673 route_discovery: Mutex::new(Ipv6RouteDiscoveryState::new::<
674 _,
675 NestedIntoCoreTimerCtx<CC, _>,
676 >(bindings_ctx, device_id.clone())),
677 router_solicitations: Default::default(),
678 ip_state: IpDeviceState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(
679 bindings_ctx,
680 device_id.clone(),
681 ),
682 config: Default::default(),
683 slaac_state: Mutex::new(SlaacState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(
684 bindings_ctx,
685 device_id,
686 )),
687 mld_counters: Default::default(),
688 }
689 }
690}
691
692impl<BT: IpDeviceStateBindingsTypes> Ipv6DeviceState<BT> {
693 fn mld_counters(&self) -> &MldCounters {
694 &self.mld_counters
695 }
696}
697
698impl<BT: IpDeviceStateBindingsTypes> AsRef<IpDeviceState<Ipv6, BT>> for Ipv6DeviceState<BT> {
699 fn as_ref(&self) -> &IpDeviceState<Ipv6, BT> {
700 &self.ip_state
701 }
702}
703
704impl<BT: IpDeviceStateBindingsTypes> AsMut<IpDeviceState<Ipv6, BT>> for Ipv6DeviceState<BT> {
705 fn as_mut(&mut self) -> &mut IpDeviceState<Ipv6, BT> {
706 &mut self.ip_state
707 }
708}
709
710pub trait IpDeviceStateBindingsTypes:
712 InstantBindingsTypes + BindingsSyncTypes + TimerBindingsTypes + ReferenceNotifiers + 'static
713{
714}
715impl<BT> IpDeviceStateBindingsTypes for BT where
716 BT: InstantBindingsTypes
717 + BindingsSyncTypes
718 + TimerBindingsTypes
719 + ReferenceNotifiers
720 + 'static
721{
722}
723
724pub struct DualStackIpDeviceState<BT: IpDeviceStateBindingsTypes> {
726 ipv4: Ipv4DeviceState<BT>,
728
729 ipv6: Ipv6DeviceState<BT>,
731
732 metric: RawMetric,
734}
735
736impl<BC: IpDeviceStateBindingsTypes + TimerContext> DualStackIpDeviceState<BC> {
737 pub fn new<
741 D: WeakDeviceIdentifier,
742 CC: CoreTimerContext<IpDeviceTimerId<Ipv6, D, BC>, BC>
743 + CoreTimerContext<IpDeviceTimerId<Ipv4, D, BC>, BC>,
744 >(
745 bindings_ctx: &mut BC,
746 device_id: D,
747 metric: RawMetric,
748 ) -> Self {
749 let ipv4 = Ipv4DeviceState::new::<
750 D,
751 NestedIntoCoreTimerCtx<CC, IpDeviceTimerId<Ipv4, D, BC>>,
752 >(bindings_ctx, device_id.clone());
753 let ipv6 = Ipv6DeviceState::new::<
754 D,
755 NestedIntoCoreTimerCtx<CC, IpDeviceTimerId<Ipv6, D, BC>>,
756 >(bindings_ctx, device_id);
757 Self { ipv4, ipv6, metric }
758 }
759}
760
761impl<BT: IpDeviceStateBindingsTypes> DualStackIpDeviceState<BT> {
762 pub fn metric(&self) -> &RawMetric {
764 &self.metric
765 }
766
767 pub fn ip_state<I: IpDeviceStateIpExt>(&self) -> &IpDeviceState<I, BT> {
769 I::map_ip_out(
770 self,
771 |dual_stack| &dual_stack.ipv4.ip_state,
772 |dual_stack| &dual_stack.ipv6.ip_state,
773 )
774 }
775
776 pub fn igmp_counters(&self) -> &IgmpCounters {
778 self.ipv4.igmp_counters()
779 }
780
781 pub fn mld_counters(&self) -> &MldCounters {
783 self.ipv6.mld_counters()
784 }
785
786 pub fn ip_counters<I: IpDeviceStateIpExt>(&self) -> &IpCounters<I> {
788 &self.ip_state::<I>().ip_counters
789 }
790}
791
792#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
794pub struct TemporarySlaacConfig<Instant> {
795 pub valid_until: Instant,
797 pub desync_factor: Duration,
799 pub creation_time: Instant,
801 pub dad_counter: u8,
805}
806
807#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
809pub enum Lifetime<I> {
810 Finite(I),
812 Infinite,
814}
815
816impl<I: Instant> InspectableValue for Lifetime<I> {
817 fn record<N: Inspector>(&self, name: &str, inspector: &mut N) {
818 match self {
819 Self::Finite(instant) => inspector.record_inspectable_value(name, instant),
820 Self::Infinite => inspector.record_str(name, "infinite"),
821 }
822 }
823}
824
825impl<I: Instant> Lifetime<I> {
826 pub fn from_ndp(now: I, duration: NonZeroNdpLifetime) -> Self {
829 match duration {
830 NonZeroNdpLifetime::Finite(d) => Self::after(now, d.get()),
831 NonZeroNdpLifetime::Infinite => Self::Infinite,
832 }
833 }
834
835 pub fn after(now: I, duration: Duration) -> Self {
838 match now.checked_add(duration) {
839 Some(i) => Self::Finite(i),
840 None => Self::Infinite,
841 }
842 }
843}
844
845impl<Instant> Lifetime<Instant> {
846 pub fn map_instant<N, F: FnOnce(Instant) -> N>(self, f: F) -> Lifetime<N> {
848 match self {
849 Self::Infinite => Lifetime::Infinite,
850 Self::Finite(i) => Lifetime::Finite(f(i)),
851 }
852 }
853}
854
855#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
857pub enum PreferredLifetime<Instant> {
858 Preferred(Lifetime<Instant>),
863 Deprecated,
865}
866
867impl<Instant> PreferredLifetime<Instant> {
868 pub const fn preferred_until(instant: Instant) -> Self {
870 Self::Preferred(Lifetime::Finite(instant))
871 }
872
873 pub const fn preferred_forever() -> Self {
876 Self::Preferred(Lifetime::Infinite)
877 }
878
879 pub const fn is_deprecated(&self) -> bool {
881 match self {
882 Self::Preferred(_) => false,
883 Self::Deprecated => true,
884 }
885 }
886
887 pub fn map_instant<N, F: FnOnce(Instant) -> N>(self, f: F) -> PreferredLifetime<N> {
889 match self {
890 Self::Deprecated => PreferredLifetime::Deprecated,
891 Self::Preferred(l) => PreferredLifetime::Preferred(l.map_instant(f)),
892 }
893 }
894}
895
896impl<I: Instant> PreferredLifetime<I> {
897 pub fn preferred_for(now: I, duration: NonZeroNdpLifetime) -> Self {
900 Self::Preferred(Lifetime::from_ndp(now, duration))
901 }
902
903 pub fn maybe_preferred_for(now: I, duration: Option<NonZeroNdpLifetime>) -> Self {
906 match duration {
907 Some(d) => Self::preferred_for(now, d),
908 None => Self::Deprecated,
909 }
910 }
911}
912
913impl<Instant> Default for PreferredLifetime<Instant> {
914 fn default() -> Self {
915 Self::Preferred(Lifetime::Infinite)
916 }
917}
918
919impl<I: Instant> InspectableValue for PreferredLifetime<I> {
920 fn record<N: Inspector>(&self, name: &str, inspector: &mut N) {
921 match self {
922 Self::Deprecated => inspector.record_str(name, "deprecated"),
923 Self::Preferred(lifetime) => inspector.record_inspectable_value(name, lifetime),
924 }
925 }
926}
927
928#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
932pub struct CommonAddressProperties<Instant> {
933 pub valid_until: Lifetime<Instant>,
935 pub preferred_lifetime: PreferredLifetime<Instant>,
943}
944
945impl<I> Default for CommonAddressProperties<I> {
946 fn default() -> Self {
947 Self {
948 valid_until: Lifetime::Infinite,
949 preferred_lifetime: PreferredLifetime::preferred_forever(),
950 }
951 }
952}
953
954impl<Inst: Instant> Inspectable for CommonAddressProperties<Inst> {
955 fn record<I: Inspector>(&self, inspector: &mut I) {
956 let Self { valid_until, preferred_lifetime } = self;
957 inspector.record_inspectable_value("ValidUntil", valid_until);
958 inspector.record_inspectable_value("PreferredLifetime", preferred_lifetime);
959 }
960}
961
962#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
967pub struct CommonAddressConfig {
968 pub should_perform_dad: Option<bool>,
971}
972
973impl Inspectable for CommonAddressConfig {
974 fn record<I: Inspector>(&self, inspector: &mut I) {
975 let Self { should_perform_dad } = self;
976 if let Some(should_perform_dad) = should_perform_dad {
977 inspector.record_bool("DadOverride", *should_perform_dad);
978 }
979 }
980}
981
982#[derive(Clone, Copy, Debug, Derivative, PartialEq, Eq, Hash)]
984#[derivative(Default(bound = ""))]
985pub struct Ipv4AddrConfig<Instant> {
986 pub config: CommonAddressConfig,
988 pub properties: CommonAddressProperties<Instant>,
990}
991
992impl<Inst: Instant> Inspectable for Ipv4AddrConfig<Inst> {
993 fn record<I: Inspector>(&self, inspector: &mut I) {
994 let Self { config, properties } = self;
995 inspector.delegate_inspectable(config);
996 inspector.delegate_inspectable(properties);
997 }
998}
999
1000#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1003pub enum SlaacConfig<Instant> {
1004 Stable {
1006 valid_until: Lifetime<Instant>,
1008 creation_time: Instant,
1010 regen_counter: u8,
1013 dad_counter: u8,
1015 },
1016 Temporary(TemporarySlaacConfig<Instant>),
1020}
1021
1022impl<Instant: Copy> SlaacConfig<Instant> {
1023 pub fn valid_until(&self) -> Lifetime<Instant> {
1025 match self {
1026 SlaacConfig::Stable { valid_until, .. } => *valid_until,
1027 SlaacConfig::Temporary(TemporarySlaacConfig { valid_until, .. }) => {
1028 Lifetime::Finite(*valid_until)
1029 }
1030 }
1031 }
1032}
1033
1034#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1036pub enum Ipv6AddrConfig<Instant> {
1037 Slaac(Ipv6AddrSlaacConfig<Instant>),
1039
1040 Manual(Ipv6AddrManualConfig<Instant>),
1042}
1043
1044impl<Instant> Default for Ipv6AddrConfig<Instant> {
1045 fn default() -> Self {
1046 Self::Manual(Default::default())
1047 }
1048}
1049
1050impl<Inst: Instant> Inspectable for Ipv6AddrConfig<Inst> {
1051 fn record<I: Inspector>(&self, inspector: &mut I) {
1052 match self {
1053 Ipv6AddrConfig::Manual(Ipv6AddrManualConfig {
1055 config,
1056 properties: _,
1057 temporary: _,
1058 }) => {
1059 inspector.delegate_inspectable(config);
1060 }
1061 Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig { inner, preferred_lifetime: _ }) => {
1062 match inner {
1063 SlaacConfig::Stable {
1064 valid_until: _,
1065 creation_time,
1066 regen_counter,
1067 dad_counter,
1068 } => {
1069 inspector.record_inspectable_value("CreationTime", creation_time);
1070 inspector.record_uint("RegenCounter", *regen_counter);
1071 inspector.record_uint("DadCounter", *dad_counter);
1072 }
1073 SlaacConfig::Temporary(TemporarySlaacConfig {
1074 valid_until: _,
1075 desync_factor,
1076 creation_time,
1077 dad_counter,
1078 }) => {
1079 inspector.record_double("DesyncFactorSecs", desync_factor.as_secs_f64());
1080 inspector.record_uint("DadCounter", *dad_counter);
1081 inspector.record_inspectable_value("CreationTime", creation_time);
1082 }
1083 }
1084 }
1085 };
1086 inspector.record_bool("IsSlaac", self.is_slaac());
1087 inspector.record_inspectable_value("ValidUntil", &self.valid_until());
1088 inspector.record_inspectable_value("PreferredLifetime", &self.preferred_lifetime());
1089 inspector.record_bool("Temporary", self.is_temporary());
1090 }
1091}
1092
1093#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1095pub struct Ipv6AddrSlaacConfig<Instant> {
1096 pub inner: SlaacConfig<Instant>,
1098 pub preferred_lifetime: PreferredLifetime<Instant>,
1100}
1101
1102#[derive(Clone, Copy, Debug, Derivative, PartialEq, Eq, Hash)]
1104#[derivative(Default(bound = ""))]
1105pub struct Ipv6AddrManualConfig<Instant> {
1106 pub config: CommonAddressConfig,
1108 pub properties: CommonAddressProperties<Instant>,
1110 pub temporary: bool,
1114}
1115
1116impl<Instant> From<Ipv6AddrManualConfig<Instant>> for Ipv6AddrConfig<Instant> {
1117 fn from(value: Ipv6AddrManualConfig<Instant>) -> Self {
1118 Self::Manual(value)
1119 }
1120}
1121
1122impl<Instant: Copy + PartialEq> Ipv6AddrConfig<Instant> {
1123 pub fn valid_until(&self) -> Lifetime<Instant> {
1125 match self {
1126 Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig { inner, .. }) => inner.valid_until(),
1127 Ipv6AddrConfig::Manual(Ipv6AddrManualConfig { properties, .. }) => {
1128 properties.valid_until
1129 }
1130 }
1131 }
1132
1133 pub fn preferred_lifetime(&self) -> PreferredLifetime<Instant> {
1135 match self {
1136 Ipv6AddrConfig::Manual(Ipv6AddrManualConfig { properties, .. }) => {
1137 properties.preferred_lifetime
1138 }
1139 Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig { preferred_lifetime, .. }) => {
1140 *preferred_lifetime
1141 }
1142 }
1143 }
1144
1145 pub fn is_deprecated(&self) -> bool {
1147 self.preferred_lifetime().is_deprecated()
1148 }
1149
1150 pub fn is_temporary(&self) -> bool {
1152 match self {
1153 Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig { inner, .. }) => match inner {
1154 SlaacConfig::Stable { .. } => false,
1155 SlaacConfig::Temporary(_) => true,
1156 },
1157 Ipv6AddrConfig::Manual(Ipv6AddrManualConfig { temporary, .. }) => *temporary,
1158 }
1159 }
1160
1161 pub fn is_slaac(&self) -> bool {
1163 match self {
1164 Ipv6AddrConfig::Slaac(_) => true,
1165 Ipv6AddrConfig::Manual(_) => false,
1166 }
1167 }
1168}
1169
1170#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1172pub struct IpAddressFlags {
1173 pub assigned: bool,
1175}
1176
1177#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1179pub struct IpAddressData<I: IpDeviceStateIpExt, Inst: Instant> {
1180 pub flags: IpAddressFlags,
1182 pub config: Option<I::AddressConfig<Inst>>,
1184}
1185
1186impl<I: IpDeviceStateIpExt, Inst: Instant> IpAddressData<I, Inst> {
1187 pub fn should_perform_dad(&self) -> bool {
1189 let Some(config) = self.config.as_ref() else {
1190 return false;
1193 };
1194
1195 #[derive(GenericOverIp)]
1196 #[generic_over_ip(I, Ip)]
1197 struct Wrap<'a, I: IpDeviceStateIpExt, Inst: Instant>(&'a I::AddressConfig<Inst>);
1198 let should_perform_dad = I::map_ip_in(
1199 Wrap(config),
1200 |Wrap(Ipv4AddrConfig { config, properties: _ })| config.should_perform_dad,
1201 |Wrap(config)| match config {
1202 Ipv6AddrConfig::Slaac(_) => None,
1203 Ipv6AddrConfig::Manual(c) => c.config.should_perform_dad,
1204 },
1205 );
1206
1207 should_perform_dad.unwrap_or(I::DEFAULT_DAD_ENABLED)
1208 }
1209}
1210
1211impl<I: IpDeviceStateIpExt, Inst: Instant> Inspectable for IpAddressData<I, Inst> {
1212 fn record<II: Inspector>(&self, inspector: &mut II) {
1213 let Self { flags: IpAddressFlags { assigned }, config } = self;
1214 inspector.record_bool("Assigned", *assigned);
1215 if let Some(config) = config {
1216 inspector.delegate_inspectable(config);
1217 }
1218 }
1219}
1220
1221#[derive(Derivative)]
1224#[derivative(Debug(bound = ""))]
1225pub struct IpAddressEntry<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> {
1226 addr_sub: AddrSubnet<I::Addr, I::AssignedWitness>,
1227 dad_state: Mutex<DadState<I, BT>, BT::Mutex>,
1228 state: RwLock<IpAddressData<I, BT::Instant>, BT::RwLock>,
1229}
1230
1231impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> IpAddressEntry<I, BT> {
1232 pub fn new(
1234 addr_sub: AddrSubnet<I::Addr, I::AssignedWitness>,
1235 dad_state: DadState<I, BT>,
1236 config: I::AddressConfig<BT::Instant>,
1237 ) -> Self {
1238 let assigned = dad_state.is_assigned();
1239 Self {
1240 addr_sub,
1241 dad_state: Mutex::new(dad_state),
1242 state: RwLock::new(IpAddressData {
1243 config: Some(config),
1244 flags: IpAddressFlags { assigned },
1245 }),
1246 }
1247 }
1248
1249 pub fn addr_sub(&self) -> &AddrSubnet<I::Addr, I::AssignedWitness> {
1251 &self.addr_sub
1252 }
1253}
1254
1255impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> OrderedLockAccess<DadState<I, BT>>
1256 for IpAddressEntry<I, BT>
1257{
1258 type Lock = Mutex<DadState<I, BT>, BT::Mutex>;
1259 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
1260 OrderedLockRef::new(&self.dad_state)
1261 }
1262}
1263
1264impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>
1265 OrderedLockAccess<IpAddressData<I, BT::Instant>> for IpAddressEntry<I, BT>
1266{
1267 type Lock = RwLock<IpAddressData<I, BT::Instant>, BT::RwLock>;
1268 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
1269 OrderedLockRef::new(&self.state)
1270 }
1271}
1272
1273#[cfg(test)]
1274mod tests {
1275 use super::*;
1276
1277 use net_types::ip::{Ipv4Addr, Ipv6Addr};
1278 use netstack3_base::InstantContext as _;
1279 use netstack3_base::testutil::{FakeBindingsCtx, FakeInstant};
1280 use test_case::test_case;
1281
1282 type FakeBindingsCtxImpl = FakeBindingsCtx<(), (), (), ()>;
1283
1284 #[test_case(Lifetime::Infinite ; "with infinite valid_until")]
1285 #[test_case(Lifetime::Finite(FakeInstant::from(Duration::from_secs(1))); "with finite valid_until")]
1286 fn test_add_addr_ipv4(valid_until: Lifetime<FakeInstant>) {
1287 const ADDRESS: Ipv4Addr = Ipv4Addr::new([1, 2, 3, 4]);
1288 const PREFIX_LEN: u8 = 8;
1289
1290 let mut ipv4 = IpDeviceAddresses::<Ipv4, FakeBindingsCtxImpl>::default();
1291 let config = Ipv4AddrConfig {
1292 properties: CommonAddressProperties { valid_until, ..Default::default() },
1293 ..Default::default()
1294 };
1295
1296 let _: AddressId<_, _> = ipv4
1297 .add(IpAddressEntry::new(
1298 AddrSubnet::new(ADDRESS, PREFIX_LEN).unwrap(),
1299 DadState::Uninitialized,
1300 config,
1301 ))
1302 .unwrap();
1303 assert_eq!(
1305 ipv4.add(IpAddressEntry::new(
1306 AddrSubnet::new(ADDRESS, PREFIX_LEN + 1).unwrap(),
1307 DadState::Uninitialized,
1308 config,
1309 ))
1310 .unwrap_err(),
1311 ExistsError
1312 );
1313 }
1314
1315 #[test_case(Lifetime::Infinite ; "with infinite valid_until")]
1316 #[test_case(Lifetime::Finite(FakeInstant::from(Duration::from_secs(1))); "with finite valid_until")]
1317 fn test_add_addr_ipv6(valid_until: Lifetime<FakeInstant>) {
1318 const ADDRESS: Ipv6Addr =
1319 Ipv6Addr::from_bytes([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]);
1320 const PREFIX_LEN: u8 = 8;
1321
1322 let mut ipv6 = IpDeviceAddresses::<Ipv6, FakeBindingsCtxImpl>::default();
1323
1324 let mut bindings_ctx = FakeBindingsCtxImpl::default();
1325
1326 let _: AddressId<_, _> = ipv6
1327 .add(IpAddressEntry::new(
1328 AddrSubnet::new(ADDRESS, PREFIX_LEN).unwrap(),
1329 DadState::Tentative {
1330 dad_transmits_remaining: None,
1331 timer: bindings_ctx.new_timer(()),
1332 ip_specific_state: Default::default(),
1333 probe_wait: None,
1334 },
1335 Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig {
1336 inner: SlaacConfig::Stable {
1337 valid_until,
1338 creation_time: bindings_ctx.now(),
1339 regen_counter: 0,
1340 dad_counter: 0,
1341 },
1342 preferred_lifetime: PreferredLifetime::Preferred(Lifetime::Infinite),
1343 }),
1344 ))
1345 .unwrap();
1346 assert_eq!(
1349 ipv6.add(IpAddressEntry::new(
1350 AddrSubnet::new(ADDRESS, PREFIX_LEN + 1).unwrap(),
1351 DadState::Assigned { ran_dad: false },
1352 Ipv6AddrConfig::Manual(Ipv6AddrManualConfig {
1353 properties: CommonAddressProperties { valid_until, ..Default::default() },
1354 ..Default::default()
1355 }),
1356 ))
1357 .unwrap_err(),
1358 ExistsError,
1359 );
1360 }
1361
1362 #[test_case(None => false; "default")]
1363 #[test_case(Some(false) => false; "disabled")]
1364 #[test_case(Some(true) => true; "enabled")]
1365 fn should_perform_dad_ipv4(setting: Option<bool>) -> bool {
1366 let state = IpAddressData::<Ipv4, FakeInstant> {
1367 flags: IpAddressFlags { assigned: false },
1368 config: Some(Ipv4AddrConfig::<FakeInstant> {
1369 config: CommonAddressConfig { should_perform_dad: setting },
1370 ..Default::default()
1371 }),
1372 };
1373 state.should_perform_dad()
1374 }
1375
1376 #[test_case(None => true; "default")]
1377 #[test_case(Some(false) => false; "disabled")]
1378 #[test_case(Some(true) => true; "enabled")]
1379 fn should_perform_dad_ipv6(setting: Option<bool>) -> bool {
1380 let state = IpAddressData::<Ipv6, FakeInstant> {
1381 flags: IpAddressFlags { assigned: false },
1382 config: Some(Ipv6AddrConfig::Manual(Ipv6AddrManualConfig::<FakeInstant> {
1383 config: CommonAddressConfig { should_perform_dad: setting },
1384 ..Default::default()
1385 })),
1386 };
1387 state.should_perform_dad()
1388 }
1389}