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