netstack3_ip/
device.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! An IP device.
6
7pub(crate) mod api;
8pub(crate) mod config;
9pub(crate) mod dad;
10pub(crate) mod nud;
11pub(crate) mod opaque_iid;
12pub(crate) mod route_discovery;
13pub(crate) mod router_solicitation;
14pub(crate) mod slaac;
15pub(crate) mod state;
16
17use alloc::vec::Vec;
18use core::fmt::{Debug, Display};
19use core::hash::Hash;
20use core::num::NonZeroU8;
21
22use derivative::Derivative;
23use log::{debug, info};
24use net_types::ip::{
25    AddrSubnet, GenericOverIp, Ip, IpAddress, Ipv4, Ipv4Addr, Ipv4SourceAddr, Ipv6, Ipv6Addr,
26    Ipv6SourceAddr, Mtu, Subnet,
27};
28use net_types::{LinkLocalAddress as _, MulticastAddr, SpecifiedAddr, Witness};
29use netstack3_base::{
30    AnyDevice, AssignedAddrIpExt, Counter, CounterCollectionSpec, DeferredResourceRemovalContext,
31    DeviceIdContext, EventContext, ExistsError, HandleableTimer, Instant, InstantBindingsTypes,
32    InstantContext, IpAddressId, IpDeviceAddr, IpDeviceAddressIdContext, IpExt, Ipv4DeviceAddr,
33    Ipv6DeviceAddr, NotFoundError, RemoveResourceResultWithContext, ResourceCounterContext,
34    RngContext, SendFrameError, StrongDeviceIdentifier, TimerContext, TimerHandler,
35    TxMetadataBindingsTypes, WeakDeviceIdentifier, WeakIpAddressId,
36};
37use netstack3_filter::ProofOfEgressCheck;
38use packet::{BufferMut, Serializer};
39use packet_formats::icmp::mld::MldPacket;
40use packet_formats::icmp::ndp::NonZeroNdpLifetime;
41use packet_formats::utils::NonZeroDuration;
42use zerocopy::SplitByteSlice;
43
44use crate::device::CommonAddressProperties;
45use crate::internal::base::{DeviceIpLayerMetadata, IpDeviceMtuContext, IpPacketDestination};
46use crate::internal::counters::IpCounters;
47use crate::internal::device::config::{
48    IpDeviceConfigurationUpdate, Ipv4DeviceConfigurationUpdate, Ipv6DeviceConfigurationUpdate,
49};
50use crate::internal::device::dad::{
51    DadHandler, DadIncomingPacketResult, DadIpExt, DadTimerId, Ipv4DadAddressInfo,
52    Ipv6PacketResultMetadata, NeedsDad,
53};
54use crate::internal::device::nud::NudIpHandler;
55use crate::internal::device::route_discovery::{
56    Ipv6DiscoveredRoute, Ipv6DiscoveredRouteTimerId, RouteDiscoveryHandler,
57};
58use crate::internal::device::router_solicitation::{RsHandler, RsTimerId};
59use crate::internal::device::slaac::{SlaacHandler, SlaacTimerId};
60use crate::internal::device::state::{
61    IpAddressData, IpAddressFlags, IpDeviceConfiguration, IpDeviceFlags, IpDeviceState,
62    IpDeviceStateBindingsTypes, IpDeviceStateIpExt, Ipv4AddrConfig, Ipv4DeviceConfiguration,
63    Ipv4DeviceState, Ipv6AddrConfig, Ipv6AddrManualConfig, Ipv6DeviceConfiguration,
64    Ipv6DeviceState, Ipv6NetworkLearnedParameters, Lifetime, PreferredLifetime, WeakAddressId,
65};
66use crate::internal::gmp::igmp::{IgmpPacketHandler, IgmpTimerId};
67use crate::internal::gmp::mld::{MldPacketHandler, MldTimerId};
68use crate::internal::gmp::{self, GmpHandler, GroupJoinResult, GroupLeaveResult};
69use crate::internal::local_delivery::{IpHeaderInfo, LocalDeliveryPacketInfo};
70
71/// An IP device timer.
72///
73/// This timer is an indirection to the real types defined by the
74/// [`IpDeviceIpExt`] trait. Having a concrete type parameterized over IP allows
75/// us to provide implementations generic on I for outer timer contexts that
76/// handle `IpDeviceTimerId` timers.
77#[derive(Derivative, GenericOverIp)]
78#[derivative(
79    Clone(bound = ""),
80    Eq(bound = ""),
81    PartialEq(bound = ""),
82    Hash(bound = ""),
83    Debug(bound = "")
84)]
85#[generic_over_ip(I, Ip)]
86pub struct IpDeviceTimerId<
87    I: IpDeviceIpExt,
88    D: WeakDeviceIdentifier,
89    BT: IpDeviceStateBindingsTypes,
90>(I::Timer<D, BT>);
91
92/// A timer ID for IPv4 devices.
93#[derive(Derivative)]
94#[derivative(
95    Clone(bound = ""),
96    Debug(bound = ""),
97    Eq(bound = ""),
98    Hash(bound = ""),
99    PartialEq(bound = "")
100)]
101pub enum Ipv4DeviceTimerId<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> {
102    /// The timer ID is specific to the IGMP protocol suite.
103    Igmp(IgmpTimerId<D>),
104    /// The timer ID is specific to duplicate address detection.
105    Dad(DadTimerId<Ipv4, D, WeakAddressId<Ipv4, BT>>),
106}
107
108impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> Ipv4DeviceTimerId<D, BT> {
109    /// Gets the device ID from this timer IFF the device hasn't been destroyed.
110    fn device_id(&self) -> Option<D::Strong> {
111        match self {
112            Ipv4DeviceTimerId::Igmp(igmp) => igmp.device_id().upgrade(),
113            Ipv4DeviceTimerId::Dad(dad) => dad.device_id().upgrade(),
114        }
115    }
116
117    /// Transforms this timer ID into the common [`IpDeviceTimerId`] version.
118    pub fn into_common(self) -> IpDeviceTimerId<Ipv4, D, BT> {
119        self.into()
120    }
121}
122
123impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<IpDeviceTimerId<Ipv4, D, BT>>
124    for Ipv4DeviceTimerId<D, BT>
125{
126    fn from(IpDeviceTimerId(inner): IpDeviceTimerId<Ipv4, D, BT>) -> Self {
127        inner
128    }
129}
130
131impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<Ipv4DeviceTimerId<D, BT>>
132    for IpDeviceTimerId<Ipv4, D, BT>
133{
134    fn from(value: Ipv4DeviceTimerId<D, BT>) -> Self {
135        Self(value)
136    }
137}
138
139impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<IgmpTimerId<D>>
140    for Ipv4DeviceTimerId<D, BT>
141{
142    fn from(id: IgmpTimerId<D>) -> Ipv4DeviceTimerId<D, BT> {
143        Ipv4DeviceTimerId::Igmp(id)
144    }
145}
146
147impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes>
148    From<DadTimerId<Ipv4, D, WeakAddressId<Ipv4, BT>>> for Ipv4DeviceTimerId<D, BT>
149{
150    fn from(id: DadTimerId<Ipv4, D, WeakAddressId<Ipv4, BT>>) -> Ipv4DeviceTimerId<D, BT> {
151        Ipv4DeviceTimerId::Dad(id)
152    }
153}
154
155impl<
156    D: WeakDeviceIdentifier,
157    BC: IpDeviceStateBindingsTypes,
158    CC: TimerHandler<BC, IgmpTimerId<D>>
159        + TimerHandler<BC, DadTimerId<Ipv4, D, WeakAddressId<Ipv4, BC>>>,
160> HandleableTimer<CC, BC> for Ipv4DeviceTimerId<D, BC>
161{
162    fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, timer: BC::UniqueTimerId) {
163        match self {
164            Ipv4DeviceTimerId::Igmp(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
165            Ipv4DeviceTimerId::Dad(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
166        }
167    }
168}
169
170impl<I, CC, BC> HandleableTimer<CC, BC> for IpDeviceTimerId<I, CC::WeakDeviceId, BC>
171where
172    I: IpDeviceIpExt,
173    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
174    CC: IpDeviceConfigurationContext<I, BC>,
175    for<'a> CC::WithIpDeviceConfigurationInnerCtx<'a>:
176        TimerHandler<BC, I::Timer<CC::WeakDeviceId, BC>>,
177{
178    fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, timer: BC::UniqueTimerId) {
179        let Self(id) = self;
180        let Some(device_id) = I::timer_device_id(&id) else {
181            return;
182        };
183        core_ctx.with_ip_device_configuration(&device_id, |_state, mut core_ctx| {
184            TimerHandler::handle_timer(&mut core_ctx, bindings_ctx, id, timer)
185        })
186    }
187}
188
189/// A timer ID for IPv6 devices.
190#[derive(Derivative)]
191#[derivative(
192    Clone(bound = ""),
193    Debug(bound = ""),
194    Eq(bound = ""),
195    Hash(bound = ""),
196    PartialEq(bound = "")
197)]
198#[allow(missing_docs)]
199pub enum Ipv6DeviceTimerId<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> {
200    Mld(MldTimerId<D>),
201    Dad(DadTimerId<Ipv6, D, WeakAddressId<Ipv6, BT>>),
202    Rs(RsTimerId<D>),
203    RouteDiscovery(Ipv6DiscoveredRouteTimerId<D>),
204    Slaac(SlaacTimerId<D>),
205}
206
207impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<IpDeviceTimerId<Ipv6, D, BT>>
208    for Ipv6DeviceTimerId<D, BT>
209{
210    fn from(IpDeviceTimerId(inner): IpDeviceTimerId<Ipv6, D, BT>) -> Self {
211        inner
212    }
213}
214
215impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<Ipv6DeviceTimerId<D, BT>>
216    for IpDeviceTimerId<Ipv6, D, BT>
217{
218    fn from(value: Ipv6DeviceTimerId<D, BT>) -> Self {
219        Self(value)
220    }
221}
222
223impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> Ipv6DeviceTimerId<D, BT> {
224    /// Gets the device ID from this timer IFF the device hasn't been destroyed.
225    fn device_id(&self) -> Option<D::Strong> {
226        match self {
227            Self::Mld(id) => id.device_id(),
228            Self::Dad(id) => id.device_id(),
229            Self::Rs(id) => id.device_id(),
230            Self::RouteDiscovery(id) => id.device_id(),
231            Self::Slaac(id) => id.device_id(),
232        }
233        .upgrade()
234    }
235
236    /// Transforms this timer ID into the common [`IpDeviceTimerId`] version.
237    pub fn into_common(self) -> IpDeviceTimerId<Ipv6, D, BT> {
238        self.into()
239    }
240}
241
242impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<MldTimerId<D>>
243    for Ipv6DeviceTimerId<D, BT>
244{
245    fn from(id: MldTimerId<D>) -> Ipv6DeviceTimerId<D, BT> {
246        Ipv6DeviceTimerId::Mld(id)
247    }
248}
249
250impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes>
251    From<DadTimerId<Ipv6, D, WeakAddressId<Ipv6, BT>>> for Ipv6DeviceTimerId<D, BT>
252{
253    fn from(id: DadTimerId<Ipv6, D, WeakAddressId<Ipv6, BT>>) -> Ipv6DeviceTimerId<D, BT> {
254        Ipv6DeviceTimerId::Dad(id)
255    }
256}
257
258impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<RsTimerId<D>>
259    for Ipv6DeviceTimerId<D, BT>
260{
261    fn from(id: RsTimerId<D>) -> Ipv6DeviceTimerId<D, BT> {
262        Ipv6DeviceTimerId::Rs(id)
263    }
264}
265
266impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<Ipv6DiscoveredRouteTimerId<D>>
267    for Ipv6DeviceTimerId<D, BT>
268{
269    fn from(id: Ipv6DiscoveredRouteTimerId<D>) -> Ipv6DeviceTimerId<D, BT> {
270        Ipv6DeviceTimerId::RouteDiscovery(id)
271    }
272}
273
274impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<SlaacTimerId<D>>
275    for Ipv6DeviceTimerId<D, BT>
276{
277    fn from(id: SlaacTimerId<D>) -> Ipv6DeviceTimerId<D, BT> {
278        Ipv6DeviceTimerId::Slaac(id)
279    }
280}
281
282impl<
283    D: WeakDeviceIdentifier,
284    BC: IpDeviceStateBindingsTypes,
285    CC: TimerHandler<BC, RsTimerId<D>>
286        + TimerHandler<BC, Ipv6DiscoveredRouteTimerId<D>>
287        + TimerHandler<BC, MldTimerId<D>>
288        + TimerHandler<BC, SlaacTimerId<D>>
289        + TimerHandler<BC, DadTimerId<Ipv6, D, WeakAddressId<Ipv6, BC>>>,
290> HandleableTimer<CC, BC> for Ipv6DeviceTimerId<D, BC>
291{
292    fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, timer: BC::UniqueTimerId) {
293        match self {
294            Ipv6DeviceTimerId::Mld(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
295            Ipv6DeviceTimerId::Dad(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
296            Ipv6DeviceTimerId::Rs(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
297            Ipv6DeviceTimerId::RouteDiscovery(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
298            Ipv6DeviceTimerId::Slaac(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
299        }
300    }
301}
302
303/// An extension trait adding IP device properties.
304pub trait IpDeviceIpExt: IpDeviceStateIpExt + AssignedAddrIpExt + gmp::IpExt + DadIpExt {
305    /// IP layer state kept by the device.
306    type State<BT: IpDeviceStateBindingsTypes>: AsRef<IpDeviceState<Self, BT>>
307        + AsMut<IpDeviceState<Self, BT>>;
308    /// IP layer configuration kept by the device.
309    type Configuration: AsRef<IpDeviceConfiguration>
310        + AsMut<IpDeviceConfiguration>
311        + Clone
312        + Debug
313        + Eq
314        + PartialEq;
315    /// High level IP device timer.
316    type Timer<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes>: Into<IpDeviceTimerId<Self, D, BT>>
317        + From<IpDeviceTimerId<Self, D, BT>>
318        + Clone
319        + Eq
320        + PartialEq
321        + Debug
322        + Hash;
323    /// Manual device address configuration (user-initiated).
324    type ManualAddressConfig<I: Instant>: Default + Debug + Into<Self::AddressConfig<I>>;
325    /// Device configuration update request.
326    type ConfigurationUpdate: From<IpDeviceConfigurationUpdate>
327        + AsRef<IpDeviceConfigurationUpdate>
328        + Debug;
329
330    /// Gets the common properties of an address from its configuration.
331    fn get_common_props<I: Instant>(config: &Self::AddressConfig<I>) -> CommonAddressProperties<I>;
332
333    /// Extracts the device ID from a device timer.
334    fn timer_device_id<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes>(
335        timer: &Self::Timer<D, BT>,
336    ) -> Option<D::Strong>;
337}
338
339impl IpDeviceIpExt for Ipv4 {
340    type State<BT: IpDeviceStateBindingsTypes> = Ipv4DeviceState<BT>;
341    type Configuration = Ipv4DeviceConfiguration;
342    type Timer<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> = Ipv4DeviceTimerId<D, BT>;
343    type ManualAddressConfig<I: Instant> = Ipv4AddrConfig<I>;
344    type ConfigurationUpdate = Ipv4DeviceConfigurationUpdate;
345
346    fn get_common_props<I: Instant>(config: &Self::AddressConfig<I>) -> CommonAddressProperties<I> {
347        config.properties
348    }
349
350    fn timer_device_id<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes>(
351        timer: &Self::Timer<D, BT>,
352    ) -> Option<D::Strong> {
353        timer.device_id()
354    }
355}
356
357impl IpDeviceIpExt for Ipv6 {
358    type State<BT: IpDeviceStateBindingsTypes> = Ipv6DeviceState<BT>;
359    type Configuration = Ipv6DeviceConfiguration;
360    type Timer<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> = Ipv6DeviceTimerId<D, BT>;
361    type ManualAddressConfig<I: Instant> = Ipv6AddrManualConfig<I>;
362    type ConfigurationUpdate = Ipv6DeviceConfigurationUpdate;
363
364    fn get_common_props<I: Instant>(config: &Self::AddressConfig<I>) -> CommonAddressProperties<I> {
365        CommonAddressProperties {
366            valid_until: config.valid_until(),
367            preferred_lifetime: config.preferred_lifetime(),
368        }
369    }
370
371    fn timer_device_id<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes>(
372        timer: &Self::Timer<D, BT>,
373    ) -> Option<D::Strong> {
374        timer.device_id()
375    }
376}
377/// IP address assignment states.
378#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
379pub enum IpAddressState {
380    /// The address is unavailable because it's interface is not IP enabled.
381    Unavailable,
382    /// The address is assigned to an interface and can be considered bound to
383    /// it (all packets destined to the address will be accepted).
384    Assigned,
385    /// The address is considered unassigned to an interface for normal
386    /// operations, but has the intention of being assigned in the future (e.g.
387    /// once Duplicate Address Detection is completed).
388    Tentative,
389}
390
391/// The reason an address was removed.
392#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
393pub enum AddressRemovedReason {
394    /// The address was removed in response to external action.
395    Manual,
396    /// The address was removed because it was detected as a duplicate via DAD.
397    DadFailed,
398    /// The address was voluntarily forfeited because a conflict was detected
399    /// during ongoing address conflict detection.
400    Forfeited,
401}
402
403#[derive(Debug, Eq, Hash, PartialEq, GenericOverIp)]
404#[generic_over_ip(I, Ip)]
405/// Events emitted from IP devices.
406pub enum IpDeviceEvent<DeviceId, I: Ip, Instant> {
407    /// Address was assigned.
408    AddressAdded {
409        /// The device.
410        device: DeviceId,
411        /// The new address.
412        addr: AddrSubnet<I::Addr>,
413        /// Initial address state.
414        state: IpAddressState,
415        /// The lifetime for which the address is valid.
416        valid_until: Lifetime<Instant>,
417        /// The  preferred lifetime information for the address.
418        preferred_lifetime: PreferredLifetime<Instant>,
419    },
420    /// Address was unassigned.
421    AddressRemoved {
422        /// The device.
423        device: DeviceId,
424        /// The removed address.
425        addr: SpecifiedAddr<I::Addr>,
426        /// The reason the address was removed.
427        reason: AddressRemovedReason,
428    },
429    /// Address state changed.
430    AddressStateChanged {
431        /// The device.
432        device: DeviceId,
433        /// The address whose state was changed.
434        addr: SpecifiedAddr<I::Addr>,
435        /// The new address state.
436        state: IpAddressState,
437    },
438    /// Address properties changed.
439    AddressPropertiesChanged {
440        /// The device.
441        device: DeviceId,
442        /// The address whose properties were changed.
443        addr: SpecifiedAddr<I::Addr>,
444        /// The new `valid_until` lifetime.
445        valid_until: Lifetime<Instant>,
446        /// The new preferred lifetime information.
447        preferred_lifetime: PreferredLifetime<Instant>,
448    },
449    /// IP was enabled/disabled on the device
450    EnabledChanged {
451        /// The device.
452        device: DeviceId,
453        /// `true` if IP was enabled on the device; `false` if IP was disabled.
454        ip_enabled: bool,
455    },
456}
457
458impl<DeviceId, I: Ip, Instant> IpDeviceEvent<DeviceId, I, Instant> {
459    /// Changes the device id type with `map`.
460    pub fn map_device<N, F: FnOnce(DeviceId) -> N>(self, map: F) -> IpDeviceEvent<N, I, Instant> {
461        match self {
462            IpDeviceEvent::AddressAdded {
463                device,
464                addr,
465                state,
466                valid_until,
467                preferred_lifetime,
468            } => IpDeviceEvent::AddressAdded {
469                device: map(device),
470                addr,
471                state,
472                valid_until,
473                preferred_lifetime,
474            },
475            IpDeviceEvent::AddressRemoved { device, addr, reason } => {
476                IpDeviceEvent::AddressRemoved { device: map(device), addr, reason }
477            }
478            IpDeviceEvent::AddressStateChanged { device, addr, state } => {
479                IpDeviceEvent::AddressStateChanged { device: map(device), addr, state }
480            }
481            IpDeviceEvent::EnabledChanged { device, ip_enabled } => {
482                IpDeviceEvent::EnabledChanged { device: map(device), ip_enabled }
483            }
484            IpDeviceEvent::AddressPropertiesChanged {
485                device,
486                addr,
487                valid_until,
488                preferred_lifetime,
489            } => IpDeviceEvent::AddressPropertiesChanged {
490                device: map(device),
491                addr,
492                valid_until,
493                preferred_lifetime,
494            },
495        }
496    }
497}
498
499/// The bindings execution context for IP devices.
500pub trait IpDeviceBindingsContext<I: IpDeviceIpExt, D: StrongDeviceIdentifier>:
501    IpDeviceStateBindingsTypes
502    + DeferredResourceRemovalContext
503    + TimerContext
504    + RngContext
505    + EventContext<IpDeviceEvent<D, I, <Self as InstantBindingsTypes>::Instant>>
506{
507}
508impl<
509    D: StrongDeviceIdentifier,
510    I: IpDeviceIpExt,
511    BC: IpDeviceStateBindingsTypes
512        + DeferredResourceRemovalContext
513        + TimerContext
514        + RngContext
515        + EventContext<IpDeviceEvent<D, I, <Self as InstantBindingsTypes>::Instant>>,
516> IpDeviceBindingsContext<I, D> for BC
517{
518}
519
520/// The core context providing access to device IP address state.
521pub trait IpDeviceAddressContext<I: IpDeviceIpExt, BT: InstantBindingsTypes>:
522    IpDeviceAddressIdContext<I>
523{
524    /// Calls the callback with a reference to the address data `addr_id` on
525    /// `device_id`.
526    fn with_ip_address_data<O, F: FnOnce(&IpAddressData<I, BT::Instant>) -> O>(
527        &mut self,
528        device_id: &Self::DeviceId,
529        addr_id: &Self::AddressId,
530        cb: F,
531    ) -> O;
532
533    /// Calls the callback with a mutable reference to the address data
534    /// `addr_id` on `device_id`.
535    fn with_ip_address_data_mut<O, F: FnOnce(&mut IpAddressData<I, BT::Instant>) -> O>(
536        &mut self,
537        device_id: &Self::DeviceId,
538        addr_id: &Self::AddressId,
539        cb: F,
540    ) -> O;
541}
542
543/// Accessor for IP device state.
544pub trait IpDeviceStateContext<I: IpDeviceIpExt, BT: IpDeviceStateBindingsTypes>:
545    IpDeviceAddressContext<I, BT>
546{
547    /// Inner accessor context.
548    type IpDeviceAddressCtx<'a>: IpDeviceAddressContext<I, BT, DeviceId = Self::DeviceId, AddressId = Self::AddressId>;
549
550    /// Calls the function with immutable access to the device's flags.
551    ///
552    /// Note that this trait should only provide immutable access to the flags.
553    /// Changes to the IP device flags must only be performed while synchronizing
554    /// with the IP device configuration, so mutable access to the flags is through
555    /// `WithIpDeviceConfigurationMutInner::with_configuration_and_flags_mut`.
556    fn with_ip_device_flags<O, F: FnOnce(&IpDeviceFlags) -> O>(
557        &mut self,
558        device_id: &Self::DeviceId,
559        cb: F,
560    ) -> O;
561
562    /// Adds an IP address for the device.
563    fn add_ip_address(
564        &mut self,
565        device_id: &Self::DeviceId,
566        addr: AddrSubnet<I::Addr, I::AssignedWitness>,
567        config: I::AddressConfig<BT::Instant>,
568    ) -> Result<Self::AddressId, ExistsError>;
569
570    /// Removes an address from the device identified by the ID.
571    fn remove_ip_address(
572        &mut self,
573        device_id: &Self::DeviceId,
574        addr: Self::AddressId,
575    ) -> RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BT>;
576
577    /// Returns the address ID for the given address value.
578    fn get_address_id(
579        &mut self,
580        device_id: &Self::DeviceId,
581        addr: SpecifiedAddr<I::Addr>,
582    ) -> Result<Self::AddressId, NotFoundError>;
583
584    /// The iterator given to `with_address_ids`.
585    type AddressIdsIter<'a>: Iterator<Item = Self::AddressId> + 'a;
586
587    /// Calls the function with an iterator over all the address IDs associated
588    /// with the device.
589    fn with_address_ids<
590        O,
591        F: FnOnce(Self::AddressIdsIter<'_>, &mut Self::IpDeviceAddressCtx<'_>) -> O,
592    >(
593        &mut self,
594        device_id: &Self::DeviceId,
595        cb: F,
596    ) -> O;
597
598    /// Calls the function with an immutable reference to the device's default
599    /// hop limit for this IP version.
600    fn with_default_hop_limit<O, F: FnOnce(&NonZeroU8) -> O>(
601        &mut self,
602        device_id: &Self::DeviceId,
603        cb: F,
604    ) -> O;
605
606    /// Calls the function with a mutable reference to the device's default
607    /// hop limit for this IP version.
608    fn with_default_hop_limit_mut<O, F: FnOnce(&mut NonZeroU8) -> O>(
609        &mut self,
610        device_id: &Self::DeviceId,
611        cb: F,
612    ) -> O;
613
614    /// Joins the link-layer multicast group associated with the given IP
615    /// multicast group.
616    fn join_link_multicast_group(
617        &mut self,
618        bindings_ctx: &mut BT,
619        device_id: &Self::DeviceId,
620        multicast_addr: MulticastAddr<I::Addr>,
621    );
622
623    /// Leaves the link-layer multicast group associated with the given IP
624    /// multicast group.
625    fn leave_link_multicast_group(
626        &mut self,
627        bindings_ctx: &mut BT,
628        device_id: &Self::DeviceId,
629        multicast_addr: MulticastAddr<I::Addr>,
630    );
631}
632
633/// The context provided to the callback passed to
634/// [`IpDeviceConfigurationContext::with_ip_device_configuration_mut`].
635pub trait WithIpDeviceConfigurationMutInner<I: IpDeviceIpExt, BT: IpDeviceStateBindingsTypes>:
636    DeviceIdContext<AnyDevice>
637{
638    /// The inner device state context.
639    type IpDeviceStateCtx<'s>: IpDeviceStateContext<I, BT, DeviceId = Self::DeviceId>
640        + GmpHandler<I, BT>
641        + NudIpHandler<I, BT>
642        + DadHandler<I, BT>
643        + 's
644    where
645        Self: 's;
646
647    /// Returns an immutable reference to a device's IP configuration and an
648    /// `IpDeviceStateCtx`.
649    fn ip_device_configuration_and_ctx(
650        &mut self,
651    ) -> (&I::Configuration, Self::IpDeviceStateCtx<'_>);
652
653    /// Calls the function with a mutable reference to a device's IP
654    /// configuration and flags.
655    fn with_configuration_and_flags_mut<
656        O,
657        F: FnOnce(&mut I::Configuration, &mut IpDeviceFlags) -> O,
658    >(
659        &mut self,
660        device_id: &Self::DeviceId,
661        cb: F,
662    ) -> O;
663}
664
665/// The execution context for IP devices.
666pub trait IpDeviceConfigurationContext<
667    I: IpDeviceIpExt,
668    BC: IpDeviceBindingsContext<I, Self::DeviceId>,
669>: IpDeviceStateContext<I, BC> + IpDeviceMtuContext<I> + DeviceIdContext<AnyDevice>
670{
671    /// The iterator provided by this context.
672    type DevicesIter<'s>: Iterator<Item = Self::DeviceId> + 's;
673    /// The inner configuration context.
674    type WithIpDeviceConfigurationInnerCtx<'s>: IpDeviceStateContext<I, BC, DeviceId = Self::DeviceId, AddressId = Self::AddressId>
675        + GmpHandler<I, BC>
676        + NudIpHandler<I, BC>
677        + DadHandler<I, BC>
678        + IpAddressRemovalHandler<I, BC>
679        + IpDeviceMtuContext<I>
680        + 's;
681    /// The inner mutable configuration context.
682    type WithIpDeviceConfigurationMutInner<'s>: WithIpDeviceConfigurationMutInner<I, BC, DeviceId = Self::DeviceId>
683        + 's;
684    /// Provides access to device state.
685    type DeviceAddressAndGroupsAccessor<'s>: IpDeviceStateContext<I, BC, DeviceId = Self::DeviceId>
686        + 's;
687
688    /// Calls the function with an immutable reference to the IP device
689    /// configuration and a `WithIpDeviceConfigurationInnerCtx`.
690    fn with_ip_device_configuration<
691        O,
692        F: FnOnce(&I::Configuration, Self::WithIpDeviceConfigurationInnerCtx<'_>) -> O,
693    >(
694        &mut self,
695        device_id: &Self::DeviceId,
696        cb: F,
697    ) -> O;
698
699    /// Calls the function with a `WithIpDeviceConfigurationMutInner`.
700    fn with_ip_device_configuration_mut<
701        O,
702        F: FnOnce(Self::WithIpDeviceConfigurationMutInner<'_>) -> O,
703    >(
704        &mut self,
705        device_id: &Self::DeviceId,
706        cb: F,
707    ) -> O;
708
709    /// Calls the function with an [`Iterator`] of IDs for all initialized
710    /// devices and an accessor for device state.
711    fn with_devices_and_state<
712        O,
713        F: FnOnce(Self::DevicesIter<'_>, Self::DeviceAddressAndGroupsAccessor<'_>) -> O,
714    >(
715        &mut self,
716        cb: F,
717    ) -> O;
718
719    /// Returns the ID of the loopback interface, if one exists on the system
720    /// and is initialized.
721    fn loopback_id(&mut self) -> Option<Self::DeviceId>;
722}
723
724/// The context provided to the callback passed to
725/// [`Ipv6DeviceConfigurationContext::with_ipv6_device_configuration_mut`].
726pub trait WithIpv6DeviceConfigurationMutInner<BC: IpDeviceBindingsContext<Ipv6, Self::DeviceId>>:
727    WithIpDeviceConfigurationMutInner<Ipv6, BC>
728{
729    /// The inner IPv6 device state context.
730    type Ipv6DeviceStateCtx<'s>: Ipv6DeviceContext<BC, DeviceId = Self::DeviceId>
731        + GmpHandler<Ipv6, BC>
732        + NudIpHandler<Ipv6, BC>
733        + DadHandler<Ipv6, BC>
734        + RsHandler<BC>
735        + SlaacHandler<BC>
736        + RouteDiscoveryHandler<BC>
737        + 's
738    where
739        Self: 's;
740
741    /// Returns an immutable reference to a device's IPv6 configuration and an
742    /// `Ipv6DeviceStateCtx`.
743    fn ipv6_device_configuration_and_ctx(
744        &mut self,
745    ) -> (&Ipv6DeviceConfiguration, Self::Ipv6DeviceStateCtx<'_>);
746}
747
748/// The core context for IPv6 device configuration.
749pub trait Ipv6DeviceConfigurationContext<BC: IpDeviceBindingsContext<Ipv6, Self::DeviceId>>:
750    IpDeviceConfigurationContext<Ipv6, BC>
751{
752    /// The context available while holding device configuration.
753    type Ipv6DeviceStateCtx<'s>: Ipv6DeviceContext<BC, DeviceId = Self::DeviceId, AddressId = Self::AddressId>
754        + GmpHandler<Ipv6, BC>
755        + MldPacketHandler<BC, Self::DeviceId>
756        + NudIpHandler<Ipv6, BC>
757        + DadHandler<Ipv6, BC>
758        + RsHandler<BC>
759        + SlaacHandler<BC>
760        + RouteDiscoveryHandler<BC>
761        + 's;
762    /// The context available while holding mutable device configuration.
763    type WithIpv6DeviceConfigurationMutInner<'s>: WithIpv6DeviceConfigurationMutInner<BC, DeviceId = Self::DeviceId>
764        + 's;
765
766    /// Calls the function with an immutable reference to the IPv6 device
767    /// configuration and an `Ipv6DeviceStateCtx`.
768    fn with_ipv6_device_configuration<
769        O,
770        F: FnOnce(&Ipv6DeviceConfiguration, Self::Ipv6DeviceStateCtx<'_>) -> O,
771    >(
772        &mut self,
773        device_id: &Self::DeviceId,
774        cb: F,
775    ) -> O;
776
777    /// Calls the function with a `WithIpv6DeviceConfigurationMutInner`.
778    fn with_ipv6_device_configuration_mut<
779        O,
780        F: FnOnce(Self::WithIpv6DeviceConfigurationMutInner<'_>) -> O,
781    >(
782        &mut self,
783        device_id: &Self::DeviceId,
784        cb: F,
785    ) -> O;
786}
787
788/// A link-layer address that can be used to generate IPv6 addresses.
789pub trait Ipv6LinkLayerAddr {
790    /// Gets the address as a byte slice.
791    fn as_bytes(&self) -> &[u8];
792
793    /// Gets the device's EUI-64 based interface identifier.
794    fn eui64_iid(&self) -> [u8; 8];
795}
796
797/// The execution context for an IPv6 device.
798pub trait Ipv6DeviceContext<BC: IpDeviceBindingsContext<Ipv6, Self::DeviceId>>:
799    IpDeviceStateContext<Ipv6, BC>
800{
801    /// A link-layer address.
802    type LinkLayerAddr: Ipv6LinkLayerAddr;
803
804    /// Gets the device's link-layer address, if the device supports link-layer
805    /// addressing.
806    fn get_link_layer_addr(&mut self, device_id: &Self::DeviceId) -> Option<Self::LinkLayerAddr>;
807
808    /// Sets the link MTU for the device.
809    fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu);
810
811    /// Calls the function with an immutable reference to the retransmit timer.
812    fn with_network_learned_parameters<O, F: FnOnce(&Ipv6NetworkLearnedParameters) -> O>(
813        &mut self,
814        device_id: &Self::DeviceId,
815        cb: F,
816    ) -> O;
817
818    /// Calls the function with a mutable reference to the retransmit timer.
819    fn with_network_learned_parameters_mut<O, F: FnOnce(&mut Ipv6NetworkLearnedParameters) -> O>(
820        &mut self,
821        device_id: &Self::DeviceId,
822        cb: F,
823    ) -> O;
824}
825
826/// An implementation of an IP device.
827pub trait IpDeviceHandler<I: IpDeviceIpExt, BC>: DeviceIdContext<AnyDevice> {
828    /// Returns whether the device is a router.
829    fn is_router_device(&mut self, device_id: &Self::DeviceId) -> bool;
830
831    /// Sets the device's default hop limit.
832    fn set_default_hop_limit(&mut self, device_id: &Self::DeviceId, hop_limit: NonZeroU8);
833
834    /// Handles a received Duplicate Address Detection Packet.
835    ///
836    /// Takes action in response to a received DAD packet for the given address.
837    /// Returns the assignment state of the address on the given interface, if
838    /// there was one before any action was taken. That is, this method returns
839    /// `IpAddressState::Tentative` when the address was tentatively assigned
840    /// (and now removed), `IpAddressState::Assigned` if the address was
841    /// assigned (and possibly removed), otherwise `IpAddressState::Unassigned`.
842    ///
843    /// For IPv4, a DAD packet is either an ARP request or response. For IPv6 a
844    /// DAD packet is either a Neighbor Solicitation or a Neighbor
845    /// Advertisement.
846    fn handle_received_dad_packet(
847        &mut self,
848        bindings_ctx: &mut BC,
849        device_id: &Self::DeviceId,
850        addr: SpecifiedAddr<I::Addr>,
851        packet_data: I::ReceivedPacketData<'_>,
852    ) -> IpAddressState;
853}
854
855impl<
856    I: IpDeviceIpExt,
857    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
858    CC: IpDeviceConfigurationContext<I, BC> + ResourceCounterContext<CC::DeviceId, IpCounters<I>>,
859> IpDeviceHandler<I, BC> for CC
860{
861    fn is_router_device(&mut self, device_id: &Self::DeviceId) -> bool {
862        is_ip_unicast_forwarding_enabled(self, device_id)
863    }
864
865    fn set_default_hop_limit(&mut self, device_id: &Self::DeviceId, hop_limit: NonZeroU8) {
866        self.with_default_hop_limit_mut(device_id, |default_hop_limit| {
867            *default_hop_limit = hop_limit
868        })
869    }
870
871    fn handle_received_dad_packet(
872        &mut self,
873        bindings_ctx: &mut BC,
874        device_id: &Self::DeviceId,
875        addr: SpecifiedAddr<I::Addr>,
876        packet_data: I::ReceivedPacketData<'_>,
877    ) -> IpAddressState {
878        let addr_id = match self.get_address_id(device_id, addr) {
879            Ok(o) => o,
880            Err(NotFoundError) => return IpAddressState::Unavailable,
881        };
882
883        let orig_state =
884            match self.with_ip_device_configuration(device_id, |_config, mut core_ctx| {
885                core_ctx.handle_incoming_packet(bindings_ctx, device_id, &addr_id, packet_data)
886            }) {
887                DadIncomingPacketResult::Assigned { should_remove } => {
888                    if !should_remove {
889                        // Return `Assigned` without removing the address.
890                        return IpAddressState::Assigned;
891                    } else {
892                        // Otherwise fall through to address removal.
893
894                        // Note: RFC 5227 section 2.4 states that:
895                        //   Before abandoning an address due to a conflict,
896                        //   hosts SHOULD actively attempt to reset any existing
897                        //   connections using that address.  This mitigates
898                        //   some security threats posed by address reconfiguration
899                        //
900                        // For now we don't reset any TCP connections, but in
901                        // the future may decide it's appropriate to do so.
902                        IpAddressState::Assigned
903                    }
904                }
905                DadIncomingPacketResult::Tentative { meta } => {
906                    #[derive(GenericOverIp)]
907                    #[generic_over_ip(I, Ip)]
908                    struct Wrapped<I: IpDeviceIpExt>(I::IncomingPacketResultMeta);
909                    let is_looped_back = I::map_ip_in(
910                        Wrapped(meta),
911                        // Note: Looped back ARP probes are handled directly in the
912                        // ARP engine.
913                        |Wrapped(())| false,
914                        // Per RFC 7527 section 4.2:
915                        //   If the node has been configured to use the Enhanced DAD algorithm and
916                        //   an interface on the node receives any NS(DAD) message where the
917                        //   Target Address matches the interface address (in tentative or
918                        //   optimistic state), the receiver compares the nonce included in the
919                        //   message, with any stored nonce on the receiving interface.  If a
920                        //   match is found, the node SHOULD log a system management message,
921                        //   SHOULD update any statistics counter, and MUST drop the received
922                        //   message.  If the received NS(DAD) message includes a nonce and no
923                        //   match is found with any stored nonce, the node SHOULD log a system
924                        //   management message for a DAD-failed state and SHOULD update any
925                        //   statistics counter.
926                        |Wrapped(Ipv6PacketResultMetadata { matched_nonce })| matched_nonce,
927                    );
928
929                    if is_looped_back {
930                        // Increment a counter (IPv6 only).
931                        self.increment_both(device_id, |c| {
932                            #[derive(GenericOverIp)]
933                            #[generic_over_ip(I, Ip)]
934                            struct InCounters<'a, I: IpDeviceIpExt>(
935                                &'a <I::RxCounters as CounterCollectionSpec>::CounterCollection<
936                                    Counter,
937                                >,
938                            );
939                            I::map_ip_in::<_, _>(
940                                InCounters(&c.version_rx),
941                                |_counters| {
942                                    unreachable!("Looped back ARP probes are dropped in ARP")
943                                },
944                                |InCounters(counters)| &counters.drop_looped_back_dad_probe,
945                            )
946                        });
947
948                        // Return `Tentative` without removing the address if the
949                        // probe is looped back.
950                        return IpAddressState::Tentative;
951                    } else {
952                        // Otherwise fall through to address removal.
953                        IpAddressState::Tentative
954                    }
955                }
956                DadIncomingPacketResult::Uninitialized => IpAddressState::Unavailable,
957            };
958
959        // If we're here, we've had a conflicting packet and we should remove the
960        // address.
961        let removal_reason = match orig_state {
962            // Conflicts while assigned are part of ongoing conflict detection
963            // and are considered forfeits.
964            IpAddressState::Assigned => AddressRemovedReason::Forfeited,
965            // Conflicts while tentative or unavailable are considered DAD
966            // failures.
967            IpAddressState::Tentative | IpAddressState::Unavailable => {
968                AddressRemovedReason::DadFailed
969            }
970        };
971        match del_ip_addr(
972            self,
973            bindings_ctx,
974            device_id,
975            DelIpAddr::AddressId(addr_id),
976            removal_reason,
977        ) {
978            Ok(result) => {
979                bindings_ctx.defer_removal_result(result);
980                orig_state
981            }
982            Err(NotFoundError) => {
983                // We may have raced with user removal of this address.
984                IpAddressState::Unavailable
985            }
986        }
987    }
988}
989
990/// Handles receipt of an IGMP packet on `device`.
991pub fn receive_igmp_packet<CC, BC, B, H>(
992    core_ctx: &mut CC,
993    bindings_ctx: &mut BC,
994    device: &CC::DeviceId,
995    src_ip: Ipv4SourceAddr,
996    dst_ip: SpecifiedAddr<Ipv4Addr>,
997    buffer: B,
998    info: &LocalDeliveryPacketInfo<Ipv4, H>,
999) where
1000    CC: IpDeviceConfigurationContext<Ipv4, BC>,
1001    BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
1002    for<'a> CC::WithIpDeviceConfigurationInnerCtx<'a>: IpDeviceStateContext<Ipv4, BC, DeviceId = CC::DeviceId>
1003        + IgmpPacketHandler<BC, CC::DeviceId>,
1004    B: BufferMut,
1005    H: IpHeaderInfo<Ipv4>,
1006{
1007    core_ctx.with_ip_device_configuration(device, |_config, mut core_ctx| {
1008        IgmpPacketHandler::receive_igmp_packet(
1009            &mut core_ctx,
1010            bindings_ctx,
1011            device,
1012            src_ip,
1013            dst_ip,
1014            buffer,
1015            info,
1016        )
1017    })
1018}
1019
1020/// An implementation of an IPv6 device.
1021pub trait Ipv6DeviceHandler<BC>: IpDeviceHandler<Ipv6, BC> {
1022    /// A link-layer address.
1023    type LinkLayerAddr: Ipv6LinkLayerAddr;
1024
1025    /// Gets the device's link-layer address, if the device supports link-layer
1026    /// addressing.
1027    fn get_link_layer_addr(&mut self, device_id: &Self::DeviceId) -> Option<Self::LinkLayerAddr>;
1028
1029    /// Sets the discovered retransmit timer for the device.
1030    fn set_discovered_retrans_timer(
1031        &mut self,
1032        bindings_ctx: &mut BC,
1033        device_id: &Self::DeviceId,
1034        retrans_timer: NonZeroDuration,
1035    );
1036
1037    /// Sets the link MTU for the device.
1038    fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu);
1039
1040    /// Updates a discovered IPv6 route.
1041    fn update_discovered_ipv6_route(
1042        &mut self,
1043        bindings_ctx: &mut BC,
1044        device_id: &Self::DeviceId,
1045        route: Ipv6DiscoveredRoute,
1046        lifetime: Option<NonZeroNdpLifetime>,
1047    );
1048
1049    /// Applies a SLAAC update.
1050    fn apply_slaac_update(
1051        &mut self,
1052        bindings_ctx: &mut BC,
1053        device_id: &Self::DeviceId,
1054        prefix: Subnet<Ipv6Addr>,
1055        preferred_lifetime: Option<NonZeroNdpLifetime>,
1056        valid_lifetime: Option<NonZeroNdpLifetime>,
1057    );
1058
1059    /// Receives an MLD packet for processing.
1060    fn receive_mld_packet<B: SplitByteSlice, H: IpHeaderInfo<Ipv6>>(
1061        &mut self,
1062        bindings_ctx: &mut BC,
1063        device: &Self::DeviceId,
1064        src_ip: Ipv6SourceAddr,
1065        dst_ip: SpecifiedAddr<Ipv6Addr>,
1066        packet: MldPacket<B>,
1067        header_info: &H,
1068    );
1069}
1070
1071impl<
1072    BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1073    CC: Ipv6DeviceContext<BC>
1074        + Ipv6DeviceConfigurationContext<BC>
1075        + ResourceCounterContext<CC::DeviceId, IpCounters<Ipv6>>,
1076> Ipv6DeviceHandler<BC> for CC
1077{
1078    type LinkLayerAddr = CC::LinkLayerAddr;
1079
1080    fn get_link_layer_addr(&mut self, device_id: &Self::DeviceId) -> Option<CC::LinkLayerAddr> {
1081        Ipv6DeviceContext::get_link_layer_addr(self, device_id)
1082    }
1083
1084    fn set_discovered_retrans_timer(
1085        &mut self,
1086        _bindings_ctx: &mut BC,
1087        device_id: &Self::DeviceId,
1088        retrans_timer: NonZeroDuration,
1089    ) {
1090        self.with_network_learned_parameters_mut(device_id, |state| {
1091            state.retrans_timer = Some(retrans_timer)
1092        })
1093    }
1094
1095    fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu) {
1096        Ipv6DeviceContext::set_link_mtu(self, device_id, mtu)
1097    }
1098
1099    fn update_discovered_ipv6_route(
1100        &mut self,
1101        bindings_ctx: &mut BC,
1102        device_id: &Self::DeviceId,
1103        route: Ipv6DiscoveredRoute,
1104        lifetime: Option<NonZeroNdpLifetime>,
1105    ) {
1106        self.with_ipv6_device_configuration(device_id, |_config, mut core_ctx| {
1107            RouteDiscoveryHandler::update_route(
1108                &mut core_ctx,
1109                bindings_ctx,
1110                device_id,
1111                route,
1112                lifetime,
1113            )
1114        })
1115    }
1116
1117    fn apply_slaac_update(
1118        &mut self,
1119        bindings_ctx: &mut BC,
1120        device_id: &Self::DeviceId,
1121        prefix: Subnet<Ipv6Addr>,
1122        preferred_lifetime: Option<NonZeroNdpLifetime>,
1123        valid_lifetime: Option<NonZeroNdpLifetime>,
1124    ) {
1125        self.with_ipv6_device_configuration(device_id, |_config, mut core_ctx| {
1126            SlaacHandler::apply_slaac_update(
1127                &mut core_ctx,
1128                bindings_ctx,
1129                device_id,
1130                prefix,
1131                preferred_lifetime,
1132                valid_lifetime,
1133            )
1134        })
1135    }
1136
1137    fn receive_mld_packet<B: SplitByteSlice, H: IpHeaderInfo<Ipv6>>(
1138        &mut self,
1139        bindings_ctx: &mut BC,
1140        device: &Self::DeviceId,
1141        src_ip: Ipv6SourceAddr,
1142        dst_ip: SpecifiedAddr<Ipv6Addr>,
1143        packet: MldPacket<B>,
1144        header_info: &H,
1145    ) {
1146        self.with_ipv6_device_configuration(device, |_config, mut core_ctx| {
1147            MldPacketHandler::receive_mld_packet(
1148                &mut core_ctx,
1149                bindings_ctx,
1150                device,
1151                src_ip,
1152                dst_ip,
1153                packet,
1154                header_info,
1155            )
1156        })
1157    }
1158}
1159
1160/// The execution context for an IP device with a buffer.
1161pub trait IpDeviceSendContext<I: IpExt, BC: TxMetadataBindingsTypes>:
1162    DeviceIdContext<AnyDevice>
1163{
1164    /// Sends an IP packet through the device.
1165    fn send_ip_frame<S>(
1166        &mut self,
1167        bindings_ctx: &mut BC,
1168        device_id: &Self::DeviceId,
1169        destination: IpPacketDestination<I, &Self::DeviceId>,
1170        ip_layer_metadata: DeviceIpLayerMetadata<BC>,
1171        body: S,
1172        egress_proof: ProofOfEgressCheck,
1173    ) -> Result<(), SendFrameError<S>>
1174    where
1175        S: Serializer,
1176        S::Buffer: BufferMut;
1177}
1178
1179fn enable_ipv6_device_with_config<
1180    BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1181    CC: Ipv6DeviceContext<BC>
1182        + GmpHandler<Ipv6, BC>
1183        + RsHandler<BC>
1184        + DadHandler<Ipv6, BC>
1185        + SlaacHandler<BC>,
1186>(
1187    core_ctx: &mut CC,
1188    bindings_ctx: &mut BC,
1189    device_id: &CC::DeviceId,
1190    config: &Ipv6DeviceConfiguration,
1191) {
1192    // All nodes should join the all-nodes multicast group.
1193    join_ip_multicast_with_config(
1194        core_ctx,
1195        bindings_ctx,
1196        device_id,
1197        Ipv6::ALL_NODES_LINK_LOCAL_MULTICAST_ADDRESS,
1198        config,
1199    );
1200    GmpHandler::gmp_handle_maybe_enabled(core_ctx, bindings_ctx, device_id);
1201
1202    // Perform DAD for all addresses when enabling a device.
1203    //
1204    // We have to do this for all addresses (including ones that had DAD
1205    // performed) as while the device was disabled, another node could have
1206    // assigned the address and we wouldn't have responded to its DAD
1207    // solicitations.
1208    core_ctx
1209        .with_address_ids(device_id, |addrs, _core_ctx| addrs.collect::<Vec<_>>())
1210        .into_iter()
1211        .for_each(|addr_id| {
1212            let needs_dad = DadHandler::initialize_duplicate_address_detection(
1213                core_ctx,
1214                bindings_ctx,
1215                device_id,
1216                &addr_id,
1217                |state| IpDeviceEvent::AddressStateChanged {
1218                    device: device_id.clone(),
1219                    addr: addr_id.addr().into(),
1220                    state,
1221                },
1222            );
1223            match needs_dad {
1224                NeedsDad::Yes(token) => {
1225                    core_ctx.start_duplicate_address_detection(bindings_ctx, token);
1226                }
1227                NeedsDad::No => {}
1228            }
1229        });
1230
1231    // Only generate a link-local address if the device supports link-layer
1232    // addressing.
1233    if core_ctx.get_link_layer_addr(device_id).is_some() {
1234        SlaacHandler::generate_link_local_address(core_ctx, bindings_ctx, device_id);
1235    }
1236
1237    RsHandler::start_router_solicitation(core_ctx, bindings_ctx, device_id);
1238}
1239
1240fn disable_ipv6_device_with_config<
1241    BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1242    CC: Ipv6DeviceContext<BC>
1243        + GmpHandler<Ipv6, BC>
1244        + RsHandler<BC>
1245        + DadHandler<Ipv6, BC>
1246        + RouteDiscoveryHandler<BC>
1247        + SlaacHandler<BC>
1248        + NudIpHandler<Ipv6, BC>,
1249>(
1250    core_ctx: &mut CC,
1251    bindings_ctx: &mut BC,
1252    device_id: &CC::DeviceId,
1253    device_config: &Ipv6DeviceConfiguration,
1254) {
1255    NudIpHandler::flush_neighbor_table(core_ctx, bindings_ctx, device_id);
1256
1257    SlaacHandler::remove_all_slaac_addresses(core_ctx, bindings_ctx, device_id);
1258
1259    RouteDiscoveryHandler::invalidate_routes(core_ctx, bindings_ctx, device_id);
1260
1261    RsHandler::stop_router_solicitation(core_ctx, bindings_ctx, device_id);
1262
1263    // Reset the learned network parameters. If the device is re-enabled in the
1264    // future, there's no guarantee that it's on the same network.
1265    core_ctx.with_network_learned_parameters_mut(device_id, |params| params.reset());
1266
1267    // Delete the link-local address generated when enabling the device and stop
1268    // DAD on the other addresses.
1269    core_ctx
1270        .with_address_ids(device_id, |addrs, core_ctx| {
1271            addrs
1272                .map(|addr_id| {
1273                    core_ctx.with_ip_address_data(
1274                        device_id,
1275                        &addr_id,
1276                        |IpAddressData { flags: _, config }| (addr_id.clone(), *config),
1277                    )
1278                })
1279                .collect::<Vec<_>>()
1280        })
1281        .into_iter()
1282        .for_each(|(addr_id, config)| {
1283            if config
1284                .is_some_and(|config| config.is_slaac() && addr_id.addr().addr().is_link_local())
1285            {
1286                del_ip_addr_inner_and_notify_handler(
1287                    core_ctx,
1288                    bindings_ctx,
1289                    device_id,
1290                    DelIpAddr::AddressId(addr_id),
1291                    AddressRemovedReason::Manual,
1292                    device_config,
1293                )
1294                .map(|remove_result| {
1295                    bindings_ctx.defer_removal_result(remove_result);
1296                })
1297                .unwrap_or_else(|NotFoundError| {
1298                    // We're not holding locks on the addresses anymore we must
1299                    // allow a NotFoundError since the address can be removed as
1300                    // we release the lock.
1301                })
1302            } else {
1303                DadHandler::stop_duplicate_address_detection(
1304                    core_ctx,
1305                    bindings_ctx,
1306                    device_id,
1307                    &addr_id,
1308                );
1309                bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
1310                    device: device_id.clone(),
1311                    addr: addr_id.addr().into(),
1312                    state: IpAddressState::Unavailable,
1313                });
1314            }
1315        });
1316
1317    GmpHandler::gmp_handle_disabled(core_ctx, bindings_ctx, device_id);
1318    leave_ip_multicast_with_config(
1319        core_ctx,
1320        bindings_ctx,
1321        device_id,
1322        Ipv6::ALL_NODES_LINK_LOCAL_MULTICAST_ADDRESS,
1323        device_config,
1324    );
1325}
1326
1327fn enable_ipv4_device_with_config<
1328    BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
1329    CC: IpDeviceStateContext<Ipv4, BC> + GmpHandler<Ipv4, BC> + DadHandler<Ipv4, BC>,
1330>(
1331    core_ctx: &mut CC,
1332    bindings_ctx: &mut BC,
1333    device_id: &CC::DeviceId,
1334    config: &Ipv4DeviceConfiguration,
1335) {
1336    // All systems should join the all-systems multicast group.
1337    join_ip_multicast_with_config(
1338        core_ctx,
1339        bindings_ctx,
1340        device_id,
1341        Ipv4::ALL_SYSTEMS_MULTICAST_ADDRESS,
1342        config,
1343    );
1344    GmpHandler::gmp_handle_maybe_enabled(core_ctx, bindings_ctx, device_id);
1345    core_ctx
1346        .with_address_ids(device_id, |addrs, _core_ctx| addrs.collect::<Vec<_>>())
1347        .into_iter()
1348        .for_each(|addr_id| {
1349            let needs_dad = DadHandler::initialize_duplicate_address_detection(
1350                core_ctx,
1351                bindings_ctx,
1352                device_id,
1353                &addr_id,
1354                |state| IpDeviceEvent::AddressStateChanged {
1355                    device: device_id.clone(),
1356                    addr: addr_id.addr().into(),
1357                    state,
1358                },
1359            );
1360            match needs_dad {
1361                NeedsDad::Yes(token) => {
1362                    core_ctx.start_duplicate_address_detection(bindings_ctx, token);
1363                }
1364                NeedsDad::No => {}
1365            }
1366        })
1367}
1368
1369fn disable_ipv4_device_with_config<
1370    BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
1371    CC: IpDeviceStateContext<Ipv4, BC> + GmpHandler<Ipv4, BC> + NudIpHandler<Ipv4, BC>,
1372>(
1373    core_ctx: &mut CC,
1374    bindings_ctx: &mut BC,
1375    device_id: &CC::DeviceId,
1376    config: &Ipv4DeviceConfiguration,
1377) {
1378    NudIpHandler::flush_neighbor_table(core_ctx, bindings_ctx, device_id);
1379    GmpHandler::gmp_handle_disabled(core_ctx, bindings_ctx, device_id);
1380    leave_ip_multicast_with_config(
1381        core_ctx,
1382        bindings_ctx,
1383        device_id,
1384        Ipv4::ALL_SYSTEMS_MULTICAST_ADDRESS,
1385        config,
1386    );
1387    core_ctx.with_address_ids(device_id, |addrs, _core_ctx| {
1388        addrs.for_each(|addr| {
1389            bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
1390                device: device_id.clone(),
1391                addr: addr.addr().into(),
1392                state: IpAddressState::Unavailable,
1393            });
1394        })
1395    })
1396}
1397
1398/// Gets a single IPv4 address and subnet for a device.
1399pub fn get_ipv4_addr_subnet<BT: IpDeviceStateBindingsTypes, CC: IpDeviceStateContext<Ipv4, BT>>(
1400    core_ctx: &mut CC,
1401    device_id: &CC::DeviceId,
1402) -> Option<AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>> {
1403    core_ctx.with_address_ids(device_id, |mut addrs, _core_ctx| addrs.next().map(|a| a.addr_sub()))
1404}
1405
1406/// Gets the hop limit for new IPv6 packets that will be sent out from `device`.
1407pub fn get_ipv6_hop_limit<BT: IpDeviceStateBindingsTypes, CC: IpDeviceStateContext<Ipv6, BT>>(
1408    core_ctx: &mut CC,
1409    device: &CC::DeviceId,
1410) -> NonZeroU8 {
1411    core_ctx.with_default_hop_limit(device, Clone::clone)
1412}
1413
1414/// Is IP packet unicast forwarding enabled?
1415pub fn is_ip_unicast_forwarding_enabled<
1416    I: IpDeviceIpExt,
1417    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1418    CC: IpDeviceConfigurationContext<I, BC>,
1419>(
1420    core_ctx: &mut CC,
1421    device_id: &CC::DeviceId,
1422) -> bool {
1423    core_ctx.with_ip_device_configuration(device_id, |state, _ctx| {
1424        AsRef::<IpDeviceConfiguration>::as_ref(state).unicast_forwarding_enabled
1425    })
1426}
1427
1428/// Is IP packet multicast forwarding enabled?
1429pub fn is_ip_multicast_forwarding_enabled<
1430    I: IpDeviceIpExt,
1431    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1432    CC: IpDeviceConfigurationContext<I, BC>,
1433>(
1434    core_ctx: &mut CC,
1435    device_id: &CC::DeviceId,
1436) -> bool {
1437    core_ctx.with_ip_device_configuration(device_id, |state, _ctx| {
1438        AsRef::<IpDeviceConfiguration>::as_ref(state).multicast_forwarding_enabled
1439    })
1440}
1441
1442/// Joins the multicast group `multicast_addr` on `device_id`.
1443///
1444/// `_config` is not used but required to make sure that the caller is currently
1445/// holding a a reference to the IP device's IP configuration as a way to prove
1446/// that caller has synchronized this operation with other accesses to the IP
1447/// device configuration.
1448pub fn join_ip_multicast_with_config<
1449    I: IpDeviceIpExt,
1450    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1451    CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC>,
1452>(
1453    core_ctx: &mut CC,
1454    bindings_ctx: &mut BC,
1455    device_id: &CC::DeviceId,
1456    multicast_addr: MulticastAddr<I::Addr>,
1457    _config: &I::Configuration,
1458) {
1459    match core_ctx.gmp_join_group(bindings_ctx, device_id, multicast_addr) {
1460        GroupJoinResult::Joined(()) => {
1461            core_ctx.join_link_multicast_group(bindings_ctx, device_id, multicast_addr)
1462        }
1463        GroupJoinResult::AlreadyMember => {}
1464    }
1465}
1466
1467/// Adds `device_id` to a multicast group `multicast_addr`.
1468///
1469/// Calling `join_ip_multicast` multiple times is completely safe. A counter
1470/// will be kept for the number of times `join_ip_multicast` has been called
1471/// with the same `device_id` and `multicast_addr` pair. To completely leave a
1472/// multicast group, [`leave_ip_multicast`] must be called the same number of
1473/// times `join_ip_multicast` has been called for the same `device_id` and
1474/// `multicast_addr` pair. The first time `join_ip_multicast` is called for a
1475/// new `device` and `multicast_addr` pair, the device will actually join the
1476/// multicast group.
1477pub fn join_ip_multicast<
1478    I: IpDeviceIpExt,
1479    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1480    CC: IpDeviceConfigurationContext<I, BC>,
1481>(
1482    core_ctx: &mut CC,
1483    bindings_ctx: &mut BC,
1484    device_id: &CC::DeviceId,
1485    multicast_addr: MulticastAddr<I::Addr>,
1486) {
1487    core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
1488        join_ip_multicast_with_config(
1489            &mut core_ctx,
1490            bindings_ctx,
1491            device_id,
1492            multicast_addr,
1493            config,
1494        )
1495    })
1496}
1497
1498/// Leaves the multicast group `multicast_addr` on `device_id`.
1499///
1500/// `_config` is not used but required to make sure that the caller is currently
1501/// holding a a reference to the IP device's IP configuration as a way to prove
1502/// that caller has synchronized this operation with other accesses to the IP
1503/// device configuration.
1504pub fn leave_ip_multicast_with_config<
1505    I: IpDeviceIpExt,
1506    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1507    CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC>,
1508>(
1509    core_ctx: &mut CC,
1510    bindings_ctx: &mut BC,
1511    device_id: &CC::DeviceId,
1512    multicast_addr: MulticastAddr<I::Addr>,
1513    _config: &I::Configuration,
1514) {
1515    match core_ctx.gmp_leave_group(bindings_ctx, device_id, multicast_addr) {
1516        GroupLeaveResult::Left(()) => {
1517            core_ctx.leave_link_multicast_group(bindings_ctx, device_id, multicast_addr)
1518        }
1519        GroupLeaveResult::StillMember => {}
1520        GroupLeaveResult::NotMember => panic!(
1521            "attempted to leave IP multicast group we were not a member of: {}",
1522            multicast_addr,
1523        ),
1524    }
1525}
1526
1527/// Removes `device_id` from a multicast group `multicast_addr`.
1528///
1529/// `leave_ip_multicast` will attempt to remove `device_id` from a multicast
1530/// group `multicast_addr`. `device_id` may have "joined" the same multicast
1531/// address multiple times, so `device_id` will only leave the multicast group
1532/// once `leave_ip_multicast` has been called for each corresponding
1533/// [`join_ip_multicast`]. That is, if `join_ip_multicast` gets called 3
1534/// times and `leave_ip_multicast` gets called two times (after all 3
1535/// `join_ip_multicast` calls), `device_id` will still be in the multicast
1536/// group until the next (final) call to `leave_ip_multicast`.
1537///
1538/// # Panics
1539///
1540/// If `device_id` is not currently in the multicast group `multicast_addr`.
1541pub fn leave_ip_multicast<
1542    I: IpDeviceIpExt,
1543    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1544    CC: IpDeviceConfigurationContext<I, BC>,
1545>(
1546    core_ctx: &mut CC,
1547    bindings_ctx: &mut BC,
1548    device_id: &CC::DeviceId,
1549    multicast_addr: MulticastAddr<I::Addr>,
1550) {
1551    core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
1552        leave_ip_multicast_with_config(
1553            &mut core_ctx,
1554            bindings_ctx,
1555            device_id,
1556            multicast_addr,
1557            config,
1558        )
1559    })
1560}
1561
1562/// Adds `addr_sub` to `device_id` with configuration `addr_config`.
1563///
1564/// `_device_config` is not used but required to make sure that the caller is
1565/// currently holding a a reference to the IP device's IP configuration as a way
1566/// to prove that caller has synchronized this operation with other accesses to
1567/// the IP device configuration.
1568pub fn add_ip_addr_subnet_with_config<
1569    I: IpDeviceIpExt,
1570    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1571    CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC> + DadHandler<I, BC>,
1572>(
1573    core_ctx: &mut CC,
1574    bindings_ctx: &mut BC,
1575    device_id: &CC::DeviceId,
1576    addr_sub: AddrSubnet<I::Addr, I::AssignedWitness>,
1577    addr_config: I::AddressConfig<BC::Instant>,
1578    _device_config: &I::Configuration,
1579) -> Result<CC::AddressId, ExistsError> {
1580    info!("adding addr {addr_sub:?} config {addr_config:?} to device {device_id:?}");
1581    let CommonAddressProperties { valid_until, preferred_lifetime } =
1582        I::get_common_props(&addr_config);
1583    let addr_id = core_ctx.add_ip_address(device_id, addr_sub, addr_config)?;
1584    assert_eq!(addr_id.addr().addr(), addr_sub.addr().get());
1585
1586    let ip_enabled =
1587        core_ctx.with_ip_device_flags(device_id, |IpDeviceFlags { ip_enabled }| *ip_enabled);
1588
1589    let needs_dad = if ip_enabled {
1590        DadHandler::initialize_duplicate_address_detection(
1591            core_ctx,
1592            bindings_ctx,
1593            device_id,
1594            &addr_id,
1595            |state| IpDeviceEvent::AddressAdded {
1596                device: device_id.clone(),
1597                addr: addr_sub.to_witness(),
1598                state,
1599                valid_until,
1600                preferred_lifetime,
1601            },
1602        )
1603    } else {
1604        // NB: We don't start DAD if the device is disabled. DAD will be
1605        // performed when the device is enabled for all addresses.
1606        //
1607        // Typically, the `AddressAdded` event would be dispatched by the DAD
1608        // engine, but because we're not calling into the DAD engine we need
1609        // to dispatch the event here.
1610        bindings_ctx.on_event(IpDeviceEvent::AddressAdded {
1611            device: device_id.clone(),
1612            addr: addr_sub.to_witness(),
1613            state: IpAddressState::Unavailable,
1614            valid_until,
1615            preferred_lifetime,
1616        });
1617        NeedsDad::No
1618    };
1619
1620    match needs_dad {
1621        NeedsDad::Yes(token) => {
1622            core_ctx.start_duplicate_address_detection(bindings_ctx, token);
1623        }
1624        NeedsDad::No => {}
1625    }
1626
1627    Ok(addr_id)
1628}
1629
1630/// A handler to abstract side-effects of removing IP device addresses.
1631pub trait IpAddressRemovalHandler<I: IpDeviceIpExt, BC: InstantBindingsTypes>:
1632    DeviceIdContext<AnyDevice>
1633{
1634    /// Notifies the handler that the addr `addr` with `config` has been removed
1635    /// from `device_id` with `reason`.
1636    fn on_address_removed(
1637        &mut self,
1638        bindings_ctx: &mut BC,
1639        device_id: &Self::DeviceId,
1640        addr_sub: AddrSubnet<I::Addr, I::AssignedWitness>,
1641        config: I::AddressConfig<BC::Instant>,
1642        reason: AddressRemovedReason,
1643    );
1644}
1645
1646/// There's no special action to be taken for removed IPv4 addresses.
1647impl<CC: DeviceIdContext<AnyDevice>, BC: InstantBindingsTypes> IpAddressRemovalHandler<Ipv4, BC>
1648    for CC
1649{
1650    fn on_address_removed(
1651        &mut self,
1652        _bindings_ctx: &mut BC,
1653        _device_id: &Self::DeviceId,
1654        _addr_sub: AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>,
1655        _config: Ipv4AddrConfig<BC::Instant>,
1656        _reason: AddressRemovedReason,
1657    ) {
1658        // Nothing to do.
1659    }
1660}
1661
1662/// Provide the IPv6 implementation for all [`SlaacHandler`] implementations.
1663impl<CC: SlaacHandler<BC>, BC: InstantContext> IpAddressRemovalHandler<Ipv6, BC> for CC {
1664    fn on_address_removed(
1665        &mut self,
1666        bindings_ctx: &mut BC,
1667        device_id: &Self::DeviceId,
1668        addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
1669        config: Ipv6AddrConfig<BC::Instant>,
1670        reason: AddressRemovedReason,
1671    ) {
1672        match config {
1673            Ipv6AddrConfig::Slaac(config) => SlaacHandler::on_address_removed(
1674                self,
1675                bindings_ctx,
1676                device_id,
1677                addr_sub,
1678                config,
1679                reason,
1680            ),
1681            Ipv6AddrConfig::Manual(_manual_config) => (),
1682        }
1683    }
1684}
1685
1686/// Possible representations of an IP address that is valid for deletion.
1687#[allow(missing_docs)]
1688pub enum DelIpAddr<Id, A> {
1689    SpecifiedAddr(SpecifiedAddr<A>),
1690    AddressId(Id),
1691}
1692
1693impl<Id: IpAddressId<A>, A: IpAddress<Version: AssignedAddrIpExt>> Display for DelIpAddr<Id, A> {
1694    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1695        match self {
1696            DelIpAddr::SpecifiedAddr(addr) => write!(f, "{}", *addr),
1697            DelIpAddr::AddressId(id) => write!(f, "{}", id.addr()),
1698        }
1699    }
1700}
1701
1702/// Deletes an IP address from a device, returning the address and its
1703/// configuration if it was removed.
1704pub fn del_ip_addr_inner<
1705    I: IpDeviceIpExt,
1706    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1707    CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC> + DadHandler<I, BC>,
1708>(
1709    core_ctx: &mut CC,
1710    bindings_ctx: &mut BC,
1711    device_id: &CC::DeviceId,
1712    addr: DelIpAddr<CC::AddressId, I::Addr>,
1713    reason: AddressRemovedReason,
1714    // Require configuration lock to do this.
1715    _config: &I::Configuration,
1716) -> Result<
1717    (
1718        AddrSubnet<I::Addr, I::AssignedWitness>,
1719        I::AddressConfig<BC::Instant>,
1720        RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>,
1721    ),
1722    NotFoundError,
1723> {
1724    let addr_id = match addr {
1725        DelIpAddr::SpecifiedAddr(addr) => core_ctx.get_address_id(device_id, addr)?,
1726        DelIpAddr::AddressId(id) => id,
1727    };
1728    DadHandler::stop_duplicate_address_detection(core_ctx, bindings_ctx, device_id, &addr_id);
1729    // Extract the configuration out of the address to properly mark it as ready
1730    // for deletion. If the configuration has already been taken, consider as if
1731    // the address is already removed.
1732    let addr_config = core_ctx
1733        .with_ip_address_data_mut(device_id, &addr_id, |addr_data| addr_data.config.take())
1734        .ok_or(NotFoundError)?;
1735
1736    let addr_sub = addr_id.addr_sub();
1737    let result = core_ctx.remove_ip_address(device_id, addr_id);
1738
1739    bindings_ctx.on_event(IpDeviceEvent::AddressRemoved {
1740        device: device_id.clone(),
1741        addr: addr_sub.addr().into(),
1742        reason,
1743    });
1744
1745    Ok((addr_sub, addr_config, result))
1746}
1747
1748/// Removes an IP address and associated subnet from this device.
1749fn del_ip_addr<
1750    I: IpDeviceIpExt,
1751    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1752    CC: IpDeviceConfigurationContext<I, BC>,
1753>(
1754    core_ctx: &mut CC,
1755    bindings_ctx: &mut BC,
1756    device_id: &CC::DeviceId,
1757    addr: DelIpAddr<CC::AddressId, I::Addr>,
1758    reason: AddressRemovedReason,
1759) -> Result<RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>, NotFoundError> {
1760    info!("removing addr {addr} from device {device_id:?}");
1761    core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
1762        del_ip_addr_inner_and_notify_handler(
1763            &mut core_ctx,
1764            bindings_ctx,
1765            device_id,
1766            addr,
1767            reason,
1768            config,
1769        )
1770    })
1771}
1772
1773/// Removes an IP address and associated subnet from this device and notifies
1774/// the address removal handler.
1775fn del_ip_addr_inner_and_notify_handler<
1776    I: IpDeviceIpExt,
1777    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1778    CC: IpDeviceStateContext<I, BC>
1779        + GmpHandler<I, BC>
1780        + DadHandler<I, BC>
1781        + IpAddressRemovalHandler<I, BC>,
1782>(
1783    core_ctx: &mut CC,
1784    bindings_ctx: &mut BC,
1785    device_id: &CC::DeviceId,
1786    addr: DelIpAddr<CC::AddressId, I::Addr>,
1787    reason: AddressRemovedReason,
1788    config: &I::Configuration,
1789) -> Result<RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>, NotFoundError> {
1790    del_ip_addr_inner(core_ctx, bindings_ctx, device_id, addr, reason, config).map(
1791        |(addr_sub, config, result)| {
1792            core_ctx.on_address_removed(bindings_ctx, device_id, addr_sub, config, reason);
1793            result
1794        },
1795    )
1796}
1797
1798/// Returns whether `device_id` is enabled for IP version `I`.
1799pub fn is_ip_device_enabled<
1800    I: IpDeviceIpExt,
1801    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1802    CC: IpDeviceStateContext<I, BC>,
1803>(
1804    core_ctx: &mut CC,
1805    device_id: &CC::DeviceId,
1806) -> bool {
1807    core_ctx.with_ip_device_flags(device_id, |flags| flags.ip_enabled)
1808}
1809
1810/// Removes IPv4 state for the device without emitting events.
1811pub fn clear_ipv4_device_state<
1812    BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
1813    CC: IpDeviceConfigurationContext<Ipv4, BC>,
1814>(
1815    core_ctx: &mut CC,
1816    bindings_ctx: &mut BC,
1817    device_id: &CC::DeviceId,
1818) {
1819    core_ctx.with_ip_device_configuration_mut(device_id, |mut core_ctx| {
1820        let ip_enabled = core_ctx.with_configuration_and_flags_mut(device_id, |_config, flags| {
1821            // Start by force-disabling IPv4 so we're sure we won't handle
1822            // any more packets.
1823            let IpDeviceFlags { ip_enabled } = flags;
1824            core::mem::replace(ip_enabled, false)
1825        });
1826
1827        let (config, mut core_ctx) = core_ctx.ip_device_configuration_and_ctx();
1828        let core_ctx = &mut core_ctx;
1829        if ip_enabled {
1830            disable_ipv4_device_with_config(core_ctx, bindings_ctx, device_id, config);
1831        }
1832    })
1833}
1834
1835/// Removes IPv6 state for the device without emitting events.
1836pub fn clear_ipv6_device_state<
1837    BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1838    CC: Ipv6DeviceConfigurationContext<BC>,
1839>(
1840    core_ctx: &mut CC,
1841    bindings_ctx: &mut BC,
1842    device_id: &CC::DeviceId,
1843) {
1844    core_ctx.with_ipv6_device_configuration_mut(device_id, |mut core_ctx| {
1845        let ip_enabled = core_ctx.with_configuration_and_flags_mut(device_id, |_config, flags| {
1846            // Start by force-disabling IPv6 so we're sure we won't handle
1847            // any more packets.
1848            let IpDeviceFlags { ip_enabled } = flags;
1849            core::mem::replace(ip_enabled, false)
1850        });
1851
1852        let (config, mut core_ctx) = core_ctx.ipv6_device_configuration_and_ctx();
1853        let core_ctx = &mut core_ctx;
1854        if ip_enabled {
1855            disable_ipv6_device_with_config(core_ctx, bindings_ctx, device_id, config);
1856        }
1857    })
1858}
1859
1860/// Dispatches a received ARP packet (Request or Reply) to the IP layer.
1861///
1862/// Returns whether the `target_addr` is assigned on the device.
1863pub fn on_arp_packet<CC, BC>(
1864    core_ctx: &mut CC,
1865    bindings_ctx: &mut BC,
1866    device_id: &CC::DeviceId,
1867    sender_addr: Ipv4Addr,
1868    target_addr: Ipv4Addr,
1869    is_arp_probe: bool,
1870) -> bool
1871where
1872    CC: IpDeviceHandler<Ipv4, BC> + IpDeviceStateContext<Ipv4, BC>,
1873    BC: IpDeviceStateBindingsTypes,
1874{
1875    // As Per RFC 5227, section 2.1.1
1876    //   If [...] the host receives any ARP packet (Request *or* Reply) on the
1877    //   interface where the probe is being performed, where the packet's
1878    //   'sender IP address' is the address being probed for, then the host MUST
1879    //   treat this address as being in use by some other host.
1880    if let Some(sender_addr) = SpecifiedAddr::new(sender_addr) {
1881        let sender_addr_state = IpDeviceHandler::<Ipv4, _>::handle_received_dad_packet(
1882            core_ctx,
1883            bindings_ctx,
1884            &device_id,
1885            sender_addr,
1886            Ipv4DadAddressInfo::SourceAddr,
1887        );
1888        match sender_addr_state {
1889            // As Per RFC 5227 section 2.4:
1890            //   At any time, if a host receives an ARP packet (Request *or*
1891            //   Reply) where the 'sender IP address' is (one of) the host's own
1892            //   IP address(es) configured on that interface, but the 'sender
1893            //   hardware address' does not match any of the host's own
1894            //   interface addresses, then this is a conflicting ARP packet,
1895            //   indicating some other host also thinks it is validly using this
1896            //   address.
1897            IpAddressState::Assigned => {
1898                info!("DAD received conflicting ARP packet for assigned addr=({sender_addr})");
1899            }
1900            s @ IpAddressState::Tentative | s @ IpAddressState::Unavailable => {
1901                debug!("DAD received conflicting ARP packet for addr=({sender_addr}), state={s:?}");
1902            }
1903        }
1904    }
1905
1906    let Some(target_addr) = SpecifiedAddr::new(target_addr) else {
1907        return false;
1908    };
1909
1910    // As Per RFC 5227, section 2.1.1
1911    //  In addition, if during this period the host receives any ARP Probe
1912    //  where the packet's 'target IP address' is the address being probed
1913    //  for, [... ] then the host SHOULD similarly treat this as an address
1914    //  conflict.
1915    if is_arp_probe {
1916        let target_addr_state = IpDeviceHandler::<Ipv4, _>::handle_received_dad_packet(
1917            core_ctx,
1918            bindings_ctx,
1919            &device_id,
1920            target_addr,
1921            Ipv4DadAddressInfo::TargetAddr,
1922        );
1923        let assigned = match target_addr_state {
1924            // Unlike the sender_addr, it's not concerning to receive an ARP
1925            // packet whose target_addr is assigned to us.
1926            IpAddressState::Assigned => true,
1927            s @ IpAddressState::Tentative | s @ IpAddressState::Unavailable => {
1928                debug!("DAD received conflicting ARP packet for addr=({target_addr}), state={s:?}");
1929                false
1930            }
1931        };
1932        assigned
1933    } else {
1934        // Otherwise, don't dispatch the probe to DAD. Instead just return the
1935        // target address' assignment state.
1936        let addr_id = match core_ctx.get_address_id(device_id, target_addr) {
1937            Ok(o) => o,
1938            Err(NotFoundError) => return false,
1939        };
1940
1941        core_ctx.with_ip_address_data(
1942            device_id,
1943            &addr_id,
1944            |IpAddressData { flags: IpAddressFlags { assigned }, config: _ }| *assigned,
1945        )
1946    }
1947}
1948
1949#[cfg(any(test, feature = "testutils"))]
1950pub(crate) mod testutil {
1951    use alloc::boxed::Box;
1952
1953    use crate::device::IpAddressFlags;
1954
1955    use super::*;
1956
1957    /// Calls the callback with an iterator of the IPv4 addresses assigned to
1958    /// `device_id`.
1959    pub fn with_assigned_ipv4_addr_subnets<
1960        BT: IpDeviceStateBindingsTypes,
1961        CC: IpDeviceStateContext<Ipv4, BT>,
1962        O,
1963        F: FnOnce(Box<dyn Iterator<Item = AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>> + '_>) -> O,
1964    >(
1965        core_ctx: &mut CC,
1966        device_id: &CC::DeviceId,
1967        cb: F,
1968    ) -> O {
1969        core_ctx.with_address_ids(device_id, |addrs, _core_ctx| {
1970            cb(Box::new(addrs.map(|a| a.addr_sub())))
1971        })
1972    }
1973
1974    /// Gets the IPv6 address and subnet pairs associated with this device which are
1975    /// in the assigned state.
1976    ///
1977    /// Tentative IP addresses (addresses which are not yet fully bound to a device)
1978    /// and deprecated IP addresses (addresses which have been assigned but should
1979    /// no longer be used for new connections) will not be returned by
1980    /// `get_assigned_ipv6_addr_subnets`.
1981    ///
1982    /// Returns an [`Iterator`] of `AddrSubnet`.
1983    ///
1984    /// See [`Tentative`] and [`AddrSubnet`] for more information.
1985    pub fn with_assigned_ipv6_addr_subnets<
1986        BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1987        CC: Ipv6DeviceContext<BC>,
1988        O,
1989        F: FnOnce(Box<dyn Iterator<Item = AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>> + '_>) -> O,
1990    >(
1991        core_ctx: &mut CC,
1992        device_id: &CC::DeviceId,
1993        cb: F,
1994    ) -> O {
1995        core_ctx.with_address_ids(device_id, |addrs, core_ctx| {
1996            cb(Box::new(addrs.filter_map(|addr_id| {
1997                core_ctx
1998                    .with_ip_address_data(
1999                        device_id,
2000                        &addr_id,
2001                        |IpAddressData { flags: IpAddressFlags { assigned }, config: _ }| *assigned,
2002                    )
2003                    .then(|| addr_id.addr_sub())
2004            })))
2005        })
2006    }
2007}