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