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