netstack3_ip/
device.rs

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