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    /// Removes an address from the device identified by the ID.
563    fn remove_ip_address(
564        &mut self,
565        device_id: &Self::DeviceId,
566        addr: Self::AddressId,
567    ) -> RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BT>;
568
569    /// Returns the address ID for the given address value.
570    fn get_address_id(
571        &mut self,
572        device_id: &Self::DeviceId,
573        addr: SpecifiedAddr<I::Addr>,
574    ) -> Result<Self::AddressId, NotFoundError>;
575
576    /// The iterator given to `with_address_ids`.
577    type AddressIdsIter<'a>: Iterator<Item = Self::AddressId> + 'a;
578
579    /// Calls the function with an iterator over all the address IDs associated
580    /// with the device.
581    fn with_address_ids<
582        O,
583        F: FnOnce(Self::AddressIdsIter<'_>, &mut Self::IpDeviceAddressCtx<'_>) -> O,
584    >(
585        &mut self,
586        device_id: &Self::DeviceId,
587        cb: F,
588    ) -> O;
589
590    /// Calls the function with an immutable reference to the device's default
591    /// hop limit for this IP version.
592    fn with_default_hop_limit<O, F: FnOnce(&NonZeroU8) -> O>(
593        &mut self,
594        device_id: &Self::DeviceId,
595        cb: F,
596    ) -> O;
597
598    /// Calls the function with a mutable reference to the device's default
599    /// hop limit for this IP version.
600    fn with_default_hop_limit_mut<O, F: FnOnce(&mut NonZeroU8) -> O>(
601        &mut self,
602        device_id: &Self::DeviceId,
603        cb: F,
604    ) -> O;
605
606    /// Joins the link-layer multicast group associated with the given IP
607    /// multicast group.
608    fn join_link_multicast_group(
609        &mut self,
610        bindings_ctx: &mut BT,
611        device_id: &Self::DeviceId,
612        multicast_addr: MulticastAddr<I::Addr>,
613    );
614
615    /// Leaves the link-layer multicast group associated with the given IP
616    /// multicast group.
617    fn leave_link_multicast_group(
618        &mut self,
619        bindings_ctx: &mut BT,
620        device_id: &Self::DeviceId,
621        multicast_addr: MulticastAddr<I::Addr>,
622    );
623}
624
625/// A trait abstracting the ability to add an IP address to a device instance.
626///
627/// This trait is _only_ accessible to callers in this crate via
628/// [`WithIpDeviceConfigurationMutInner`], which guarantees that adding an
629/// address can _only_ happen under exclusive device configuration access.
630pub trait IpDeviceAddAddressContext<I: IpDeviceIpExt, BT: IpDeviceStateBindingsTypes>:
631    IpDeviceAddressContext<I, BT>
632{
633    /// Adds an IP address for the device.
634    fn add_ip_address(
635        &mut self,
636        device_id: &Self::DeviceId,
637        addr: AddrSubnet<I::Addr, I::AssignedWitness>,
638        config: I::AddressConfig<BT::Instant>,
639    ) -> Result<Self::AddressId, ExistsError>;
640}
641
642/// The context provided to the callback passed to
643/// [`IpDeviceConfigurationContext::with_ip_device_configuration_mut`].
644pub trait WithIpDeviceConfigurationMutInner<I: IpDeviceIpExt, BT: IpDeviceStateBindingsTypes>:
645    DeviceIdContext<AnyDevice>
646{
647    /// The inner device state context.
648    type IpDeviceStateCtx<'s>: IpDeviceStateContext<I, BT, DeviceId = Self::DeviceId>
649        + IpDeviceAddAddressContext<I, BT>
650        + GmpHandler<I, BT>
651        + NudIpHandler<I, BT>
652        + DadHandler<I, BT>
653        + 's
654    where
655        Self: 's;
656
657    /// Returns an immutable reference to a device's IP configuration and an
658    /// `IpDeviceStateCtx`.
659    fn ip_device_configuration_and_ctx(
660        &mut self,
661    ) -> (&I::Configuration, Self::IpDeviceStateCtx<'_>);
662
663    /// Calls the function with a mutable reference to a device's IP
664    /// configuration and flags.
665    fn with_configuration_and_flags_mut<
666        O,
667        F: FnOnce(&mut I::Configuration, &mut IpDeviceFlags) -> O,
668    >(
669        &mut self,
670        device_id: &Self::DeviceId,
671        cb: F,
672    ) -> O;
673}
674
675/// The execution context for IP devices.
676pub trait IpDeviceConfigurationContext<
677    I: IpDeviceIpExt,
678    BC: IpDeviceBindingsContext<I, Self::DeviceId>,
679>: IpDeviceStateContext<I, BC> + IpDeviceMtuContext<I> + DeviceIdContext<AnyDevice>
680{
681    /// The iterator provided by this context.
682    type DevicesIter<'s>: Iterator<Item = Self::DeviceId> + 's;
683    /// The inner configuration context.
684    type WithIpDeviceConfigurationInnerCtx<'s>: IpDeviceStateContext<I, BC, DeviceId = Self::DeviceId, AddressId = Self::AddressId>
685        + GmpHandler<I, BC>
686        + NudIpHandler<I, BC>
687        + DadHandler<I, BC>
688        + IpAddressRemovalHandler<I, BC>
689        + IpDeviceMtuContext<I>
690        + 's;
691    /// The inner mutable configuration context.
692    type WithIpDeviceConfigurationMutInner<'s>: WithIpDeviceConfigurationMutInner<I, BC, DeviceId = Self::DeviceId>
693        + 's;
694    /// Provides access to device state.
695    type DeviceAddressAndGroupsAccessor<'s>: IpDeviceStateContext<I, BC, DeviceId = Self::DeviceId>
696        + 's;
697
698    /// Calls the function with an immutable reference to the IP device
699    /// configuration and a `WithIpDeviceConfigurationInnerCtx`.
700    fn with_ip_device_configuration<
701        O,
702        F: FnOnce(&I::Configuration, Self::WithIpDeviceConfigurationInnerCtx<'_>) -> O,
703    >(
704        &mut self,
705        device_id: &Self::DeviceId,
706        cb: F,
707    ) -> O;
708
709    /// Calls the function with a `WithIpDeviceConfigurationMutInner`.
710    fn with_ip_device_configuration_mut<
711        O,
712        F: FnOnce(Self::WithIpDeviceConfigurationMutInner<'_>) -> O,
713    >(
714        &mut self,
715        device_id: &Self::DeviceId,
716        cb: F,
717    ) -> O;
718
719    /// Calls the function with an [`Iterator`] of IDs for all initialized
720    /// devices and an accessor for device state.
721    fn with_devices_and_state<
722        O,
723        F: FnOnce(Self::DevicesIter<'_>, Self::DeviceAddressAndGroupsAccessor<'_>) -> O,
724    >(
725        &mut self,
726        cb: F,
727    ) -> O;
728
729    /// Returns the ID of the loopback interface, if one exists on the system
730    /// and is initialized.
731    fn loopback_id(&mut self) -> Option<Self::DeviceId>;
732}
733
734/// The context provided to the callback passed to
735/// [`Ipv6DeviceConfigurationContext::with_ipv6_device_configuration_mut`].
736pub trait WithIpv6DeviceConfigurationMutInner<BC: IpDeviceBindingsContext<Ipv6, Self::DeviceId>>:
737    WithIpDeviceConfigurationMutInner<Ipv6, BC>
738{
739    /// The inner IPv6 device state context.
740    type Ipv6DeviceStateCtx<'s>: Ipv6DeviceContext<BC, DeviceId = Self::DeviceId>
741        + GmpHandler<Ipv6, BC>
742        + NudIpHandler<Ipv6, BC>
743        + DadHandler<Ipv6, BC>
744        + RsHandler<BC>
745        + SlaacHandler<BC>
746        + RouteDiscoveryHandler<BC>
747        + 's
748    where
749        Self: 's;
750
751    /// Returns an immutable reference to a device's IPv6 configuration and an
752    /// `Ipv6DeviceStateCtx`.
753    fn ipv6_device_configuration_and_ctx(
754        &mut self,
755    ) -> (&Ipv6DeviceConfiguration, Self::Ipv6DeviceStateCtx<'_>);
756}
757
758/// The core context for IPv6 device configuration.
759pub trait Ipv6DeviceConfigurationContext<BC: IpDeviceBindingsContext<Ipv6, Self::DeviceId>>:
760    IpDeviceConfigurationContext<Ipv6, BC>
761{
762    /// The context available while holding device configuration.
763    type Ipv6DeviceStateCtx<'s>: Ipv6DeviceContext<BC, DeviceId = Self::DeviceId, AddressId = Self::AddressId>
764        + GmpHandler<Ipv6, BC>
765        + MldPacketHandler<BC, Self::DeviceId>
766        + NudIpHandler<Ipv6, BC>
767        + DadHandler<Ipv6, BC>
768        + RsHandler<BC>
769        + SlaacHandler<BC>
770        + RouteDiscoveryHandler<BC>
771        + 's;
772    /// The context available while holding mutable device configuration.
773    type WithIpv6DeviceConfigurationMutInner<'s>: WithIpv6DeviceConfigurationMutInner<BC, DeviceId = Self::DeviceId>
774        + 's;
775
776    /// Calls the function with an immutable reference to the IPv6 device
777    /// configuration and an `Ipv6DeviceStateCtx`.
778    fn with_ipv6_device_configuration<
779        O,
780        F: FnOnce(&Ipv6DeviceConfiguration, Self::Ipv6DeviceStateCtx<'_>) -> O,
781    >(
782        &mut self,
783        device_id: &Self::DeviceId,
784        cb: F,
785    ) -> O;
786
787    /// Calls the function with a `WithIpv6DeviceConfigurationMutInner`.
788    fn with_ipv6_device_configuration_mut<
789        O,
790        F: FnOnce(Self::WithIpv6DeviceConfigurationMutInner<'_>) -> O,
791    >(
792        &mut self,
793        device_id: &Self::DeviceId,
794        cb: F,
795    ) -> O;
796}
797
798/// A link-layer address that can be used to generate IPv6 addresses.
799pub trait Ipv6LinkLayerAddr {
800    /// Gets the address as a byte slice.
801    fn as_bytes(&self) -> &[u8];
802
803    /// Gets the device's EUI-64 based interface identifier.
804    fn eui64_iid(&self) -> [u8; 8];
805}
806
807/// The execution context for an IPv6 device.
808pub trait Ipv6DeviceContext<BC: IpDeviceBindingsContext<Ipv6, Self::DeviceId>>:
809    IpDeviceStateContext<Ipv6, BC>
810{
811    /// A link-layer address.
812    type LinkLayerAddr: Ipv6LinkLayerAddr;
813
814    /// Gets the device's link-layer address, if the device supports link-layer
815    /// addressing.
816    fn get_link_layer_addr(&mut self, device_id: &Self::DeviceId) -> Option<Self::LinkLayerAddr>;
817
818    /// Sets the link MTU for the device.
819    fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu);
820
821    /// Calls the function with an immutable reference to the retransmit timer.
822    fn with_network_learned_parameters<O, F: FnOnce(&Ipv6NetworkLearnedParameters) -> O>(
823        &mut self,
824        device_id: &Self::DeviceId,
825        cb: F,
826    ) -> O;
827
828    /// Calls the function with a mutable reference to the retransmit timer.
829    fn with_network_learned_parameters_mut<O, F: FnOnce(&mut Ipv6NetworkLearnedParameters) -> O>(
830        &mut self,
831        device_id: &Self::DeviceId,
832        cb: F,
833    ) -> O;
834}
835
836/// An implementation of an IP device.
837pub trait IpDeviceHandler<I: IpDeviceIpExt, BC>: DeviceIdContext<AnyDevice> {
838    /// Returns whether the device is a router.
839    fn is_router_device(&mut self, device_id: &Self::DeviceId) -> bool;
840
841    /// Sets the device's default hop limit.
842    fn set_default_hop_limit(&mut self, device_id: &Self::DeviceId, hop_limit: NonZeroU8);
843
844    /// Handles a received Duplicate Address Detection Packet.
845    ///
846    /// Takes action in response to a received DAD packet for the given address.
847    /// Returns the assignment state of the address on the given interface, if
848    /// there was one before any action was taken. That is, this method returns
849    /// `IpAddressState::Tentative` when the address was tentatively assigned
850    /// (and now removed), `IpAddressState::Assigned` if the address was
851    /// assigned (and possibly removed), `IpAddressState::Unassigned` if the
852    /// address was uninitialized, and `None` if the address wasn't associated
853    /// with the interface
854    ///
855    /// For IPv4, a DAD packet is either an ARP request or response. For IPv6 a
856    /// DAD packet is either a Neighbor Solicitation or a Neighbor
857    /// Advertisement.
858    fn handle_received_dad_packet(
859        &mut self,
860        bindings_ctx: &mut BC,
861        device_id: &Self::DeviceId,
862        addr: SpecifiedAddr<I::Addr>,
863        packet_data: I::ReceivedPacketData<'_>,
864    ) -> Option<IpAddressState>;
865}
866
867impl<
868    I: IpDeviceIpExt,
869    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
870    CC: IpDeviceConfigurationContext<I, BC> + ResourceCounterContext<CC::DeviceId, IpCounters<I>>,
871> IpDeviceHandler<I, BC> for CC
872{
873    fn is_router_device(&mut self, device_id: &Self::DeviceId) -> bool {
874        is_ip_unicast_forwarding_enabled(self, device_id)
875    }
876
877    fn set_default_hop_limit(&mut self, device_id: &Self::DeviceId, hop_limit: NonZeroU8) {
878        self.with_default_hop_limit_mut(device_id, |default_hop_limit| {
879            *default_hop_limit = hop_limit
880        })
881    }
882
883    fn handle_received_dad_packet(
884        &mut self,
885        bindings_ctx: &mut BC,
886        device_id: &Self::DeviceId,
887        addr: SpecifiedAddr<I::Addr>,
888        packet_data: I::ReceivedPacketData<'_>,
889    ) -> Option<IpAddressState> {
890        let addr_id = match self.get_address_id(device_id, addr) {
891            Ok(o) => o,
892            Err(NotFoundError) => return None,
893        };
894
895        let orig_state =
896            match self.with_ip_device_configuration(device_id, |_config, mut core_ctx| {
897                core_ctx.handle_incoming_packet(bindings_ctx, device_id, &addr_id, packet_data)
898            }) {
899                DadIncomingPacketResult::Assigned { should_remove } => {
900                    if !should_remove {
901                        // Return `Assigned` without removing the address.
902                        return Some(IpAddressState::Assigned);
903                    } else {
904                        // Otherwise fall through to address removal.
905
906                        // Note: RFC 5227 section 2.4 states that:
907                        //   Before abandoning an address due to a conflict,
908                        //   hosts SHOULD actively attempt to reset any existing
909                        //   connections using that address.  This mitigates
910                        //   some security threats posed by address reconfiguration
911                        //
912                        // For now we don't reset any TCP connections, but in
913                        // the future may decide it's appropriate to do so.
914                        IpAddressState::Assigned
915                    }
916                }
917                DadIncomingPacketResult::Tentative { meta } => {
918                    #[derive(GenericOverIp)]
919                    #[generic_over_ip(I, Ip)]
920                    struct Wrapped<I: IpDeviceIpExt>(I::IncomingPacketResultMeta);
921                    let is_looped_back = I::map_ip_in(
922                        Wrapped(meta),
923                        // Note: Looped back ARP probes are handled directly in the
924                        // ARP engine.
925                        |Wrapped(())| false,
926                        // Per RFC 7527 section 4.2:
927                        //   If the node has been configured to use the Enhanced DAD algorithm and
928                        //   an interface on the node receives any NS(DAD) message where the
929                        //   Target Address matches the interface address (in tentative or
930                        //   optimistic state), the receiver compares the nonce included in the
931                        //   message, with any stored nonce on the receiving interface.  If a
932                        //   match is found, the node SHOULD log a system management message,
933                        //   SHOULD update any statistics counter, and MUST drop the received
934                        //   message.  If the received NS(DAD) message includes a nonce and no
935                        //   match is found with any stored nonce, the node SHOULD log a system
936                        //   management message for a DAD-failed state and SHOULD update any
937                        //   statistics counter.
938                        |Wrapped(Ipv6PacketResultMetadata { matched_nonce })| matched_nonce,
939                    );
940
941                    if is_looped_back {
942                        // Increment a counter (IPv6 only).
943                        self.increment_both(device_id, |c| {
944                            #[derive(GenericOverIp)]
945                            #[generic_over_ip(I, Ip)]
946                            struct InCounters<'a, I: IpDeviceIpExt>(
947                                &'a <I::RxCounters as CounterCollectionSpec>::CounterCollection<
948                                    Counter,
949                                >,
950                            );
951                            I::map_ip_in::<_, _>(
952                                InCounters(&c.version_rx),
953                                |_counters| {
954                                    unreachable!("Looped back ARP probes are dropped in ARP")
955                                },
956                                |InCounters(counters)| &counters.drop_looped_back_dad_probe,
957                            )
958                        });
959
960                        // Return `Tentative` without removing the address if the
961                        // probe is looped back.
962                        return Some(IpAddressState::Tentative);
963                    } else {
964                        // Otherwise fall through to address removal.
965                        IpAddressState::Tentative
966                    }
967                }
968                DadIncomingPacketResult::Uninitialized => IpAddressState::Unavailable,
969            };
970
971        // If we're here, we've had a conflicting packet and we should remove the
972        // address.
973        let removal_reason = match orig_state {
974            // Conflicts while assigned are part of ongoing conflict detection
975            // and are considered forfeits.
976            IpAddressState::Assigned => AddressRemovedReason::Forfeited,
977            // Conflicts while tentative or unavailable are considered DAD
978            // failures.
979            IpAddressState::Tentative | IpAddressState::Unavailable => {
980                AddressRemovedReason::DadFailed
981            }
982        };
983        match del_ip_addr(
984            self,
985            bindings_ctx,
986            device_id,
987            DelIpAddr::AddressId(addr_id),
988            removal_reason,
989        ) {
990            Ok(result) => {
991                bindings_ctx.defer_removal_result(result);
992                Some(orig_state)
993            }
994            Err(NotFoundError) => {
995                // We may have raced with user removal of this address.
996                None
997            }
998        }
999    }
1000}
1001
1002/// Handles receipt of an IGMP packet on `device`.
1003pub fn receive_igmp_packet<CC, BC, B, H>(
1004    core_ctx: &mut CC,
1005    bindings_ctx: &mut BC,
1006    device: &CC::DeviceId,
1007    src_ip: Ipv4SourceAddr,
1008    dst_ip: SpecifiedAddr<Ipv4Addr>,
1009    buffer: B,
1010    info: &LocalDeliveryPacketInfo<Ipv4, H>,
1011) where
1012    CC: IpDeviceConfigurationContext<Ipv4, BC>,
1013    BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
1014    for<'a> CC::WithIpDeviceConfigurationInnerCtx<'a>: IpDeviceStateContext<Ipv4, BC, DeviceId = CC::DeviceId>
1015        + IgmpPacketHandler<BC, CC::DeviceId>,
1016    B: BufferMut,
1017    H: IpHeaderInfo<Ipv4>,
1018{
1019    core_ctx.with_ip_device_configuration(device, |_config, mut core_ctx| {
1020        IgmpPacketHandler::receive_igmp_packet(
1021            &mut core_ctx,
1022            bindings_ctx,
1023            device,
1024            src_ip,
1025            dst_ip,
1026            buffer,
1027            info,
1028        )
1029    })
1030}
1031
1032/// An implementation of an IPv6 device.
1033pub trait Ipv6DeviceHandler<BC>: IpDeviceHandler<Ipv6, BC> {
1034    /// A link-layer address.
1035    type LinkLayerAddr: Ipv6LinkLayerAddr;
1036
1037    /// Gets the device's link-layer address, if the device supports link-layer
1038    /// addressing.
1039    fn get_link_layer_addr(&mut self, device_id: &Self::DeviceId) -> Option<Self::LinkLayerAddr>;
1040
1041    /// Sets the discovered retransmit timer for the device.
1042    fn set_discovered_retrans_timer(
1043        &mut self,
1044        bindings_ctx: &mut BC,
1045        device_id: &Self::DeviceId,
1046        retrans_timer: NonZeroDuration,
1047    );
1048
1049    /// Sets the link MTU for the device.
1050    fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu);
1051
1052    /// Updates a discovered IPv6 route.
1053    fn update_discovered_ipv6_route(
1054        &mut self,
1055        bindings_ctx: &mut BC,
1056        device_id: &Self::DeviceId,
1057        route: Ipv6DiscoveredRoute,
1058        lifetime: Option<NonZeroNdpLifetime>,
1059    );
1060
1061    /// Applies a SLAAC update.
1062    fn apply_slaac_update(
1063        &mut self,
1064        bindings_ctx: &mut BC,
1065        device_id: &Self::DeviceId,
1066        prefix: Subnet<Ipv6Addr>,
1067        preferred_lifetime: Option<NonZeroNdpLifetime>,
1068        valid_lifetime: Option<NonZeroNdpLifetime>,
1069    );
1070
1071    /// Receives an MLD packet for processing.
1072    fn receive_mld_packet<B: SplitByteSlice, H: IpHeaderInfo<Ipv6>>(
1073        &mut self,
1074        bindings_ctx: &mut BC,
1075        device: &Self::DeviceId,
1076        src_ip: Ipv6SourceAddr,
1077        dst_ip: SpecifiedAddr<Ipv6Addr>,
1078        packet: MldPacket<B>,
1079        header_info: &H,
1080    );
1081}
1082
1083impl<
1084    BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1085    CC: Ipv6DeviceContext<BC>
1086        + Ipv6DeviceConfigurationContext<BC>
1087        + ResourceCounterContext<CC::DeviceId, IpCounters<Ipv6>>,
1088> Ipv6DeviceHandler<BC> for CC
1089{
1090    type LinkLayerAddr = CC::LinkLayerAddr;
1091
1092    fn get_link_layer_addr(&mut self, device_id: &Self::DeviceId) -> Option<CC::LinkLayerAddr> {
1093        Ipv6DeviceContext::get_link_layer_addr(self, device_id)
1094    }
1095
1096    fn set_discovered_retrans_timer(
1097        &mut self,
1098        _bindings_ctx: &mut BC,
1099        device_id: &Self::DeviceId,
1100        retrans_timer: NonZeroDuration,
1101    ) {
1102        self.with_network_learned_parameters_mut(device_id, |state| {
1103            state.retrans_timer = Some(retrans_timer)
1104        })
1105    }
1106
1107    fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu) {
1108        Ipv6DeviceContext::set_link_mtu(self, device_id, mtu)
1109    }
1110
1111    fn update_discovered_ipv6_route(
1112        &mut self,
1113        bindings_ctx: &mut BC,
1114        device_id: &Self::DeviceId,
1115        route: Ipv6DiscoveredRoute,
1116        lifetime: Option<NonZeroNdpLifetime>,
1117    ) {
1118        self.with_ipv6_device_configuration(device_id, |config, mut core_ctx| {
1119            RouteDiscoveryHandler::update_route(
1120                &mut core_ctx,
1121                bindings_ctx,
1122                device_id,
1123                route,
1124                lifetime,
1125                &config.route_discovery_config,
1126            )
1127        })
1128    }
1129
1130    fn apply_slaac_update(
1131        &mut self,
1132        bindings_ctx: &mut BC,
1133        device_id: &Self::DeviceId,
1134        prefix: Subnet<Ipv6Addr>,
1135        preferred_lifetime: Option<NonZeroNdpLifetime>,
1136        valid_lifetime: Option<NonZeroNdpLifetime>,
1137    ) {
1138        self.with_ipv6_device_configuration(device_id, |_config, mut core_ctx| {
1139            SlaacHandler::apply_slaac_update(
1140                &mut core_ctx,
1141                bindings_ctx,
1142                device_id,
1143                prefix,
1144                preferred_lifetime,
1145                valid_lifetime,
1146            )
1147        })
1148    }
1149
1150    fn receive_mld_packet<B: SplitByteSlice, H: IpHeaderInfo<Ipv6>>(
1151        &mut self,
1152        bindings_ctx: &mut BC,
1153        device: &Self::DeviceId,
1154        src_ip: Ipv6SourceAddr,
1155        dst_ip: SpecifiedAddr<Ipv6Addr>,
1156        packet: MldPacket<B>,
1157        header_info: &H,
1158    ) {
1159        self.with_ipv6_device_configuration(device, |_config, mut core_ctx| {
1160            MldPacketHandler::receive_mld_packet(
1161                &mut core_ctx,
1162                bindings_ctx,
1163                device,
1164                src_ip,
1165                dst_ip,
1166                packet,
1167                header_info,
1168            )
1169        })
1170    }
1171}
1172
1173/// The execution context for an IP device with a buffer.
1174pub trait IpDeviceSendContext<I: IpExt, BC: TxMetadataBindingsTypes>:
1175    DeviceIdContext<AnyDevice>
1176{
1177    /// Sends an IP packet through the device.
1178    fn send_ip_frame<S>(
1179        &mut self,
1180        bindings_ctx: &mut BC,
1181        device_id: &Self::DeviceId,
1182        destination: IpPacketDestination<I, &Self::DeviceId>,
1183        ip_layer_metadata: DeviceIpLayerMetadata<BC>,
1184        body: S,
1185        egress_proof: ProofOfEgressCheck,
1186    ) -> Result<(), SendFrameError<S>>
1187    where
1188        S: Serializer,
1189        S::Buffer: BufferMut;
1190}
1191
1192fn enable_ipv6_device_with_config<
1193    BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1194    CC: Ipv6DeviceContext<BC>
1195        + GmpHandler<Ipv6, BC>
1196        + RsHandler<BC>
1197        + DadHandler<Ipv6, BC>
1198        + SlaacHandler<BC>,
1199>(
1200    core_ctx: &mut CC,
1201    bindings_ctx: &mut BC,
1202    device_id: &CC::DeviceId,
1203    config: &Ipv6DeviceConfiguration,
1204) {
1205    // All nodes should join the all-nodes multicast group.
1206    join_ip_multicast_with_config(
1207        core_ctx,
1208        bindings_ctx,
1209        device_id,
1210        Ipv6::ALL_NODES_LINK_LOCAL_MULTICAST_ADDRESS,
1211        config,
1212    );
1213    GmpHandler::gmp_handle_maybe_enabled(core_ctx, bindings_ctx, device_id);
1214
1215    // Perform DAD for all addresses when enabling a device.
1216    //
1217    // We have to do this for all addresses (including ones that had DAD
1218    // performed) as while the device was disabled, another node could have
1219    // assigned the address and we wouldn't have responded to its DAD
1220    // solicitations.
1221    core_ctx
1222        .with_address_ids(device_id, |addrs, _core_ctx| addrs.collect::<Vec<_>>())
1223        .into_iter()
1224        .for_each(|addr_id| {
1225            let needs_dad = DadHandler::initialize_duplicate_address_detection(
1226                core_ctx,
1227                bindings_ctx,
1228                device_id,
1229                &addr_id,
1230                |state| IpDeviceEvent::AddressStateChanged {
1231                    device: device_id.clone(),
1232                    addr: addr_id.addr().into(),
1233                    state,
1234                },
1235            );
1236            match needs_dad {
1237                NeedsDad::Yes(token) => {
1238                    core_ctx.start_duplicate_address_detection(bindings_ctx, token);
1239                }
1240                NeedsDad::No => {}
1241            }
1242        });
1243
1244    // Only generate a link-local address if the device supports link-layer
1245    // addressing.
1246    if core_ctx.get_link_layer_addr(device_id).is_some() {
1247        SlaacHandler::generate_link_local_address(core_ctx, bindings_ctx, device_id);
1248    }
1249
1250    RsHandler::start_router_solicitation(core_ctx, bindings_ctx, device_id);
1251}
1252
1253fn disable_ipv6_device_with_config<
1254    BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1255    CC: Ipv6DeviceContext<BC>
1256        + GmpHandler<Ipv6, BC>
1257        + RsHandler<BC>
1258        + DadHandler<Ipv6, BC>
1259        + RouteDiscoveryHandler<BC>
1260        + SlaacHandler<BC>
1261        + NudIpHandler<Ipv6, BC>,
1262>(
1263    core_ctx: &mut CC,
1264    bindings_ctx: &mut BC,
1265    device_id: &CC::DeviceId,
1266    device_config: &Ipv6DeviceConfiguration,
1267) {
1268    NudIpHandler::flush_neighbor_table(core_ctx, bindings_ctx, device_id);
1269
1270    SlaacHandler::remove_all_slaac_addresses(core_ctx, bindings_ctx, device_id);
1271
1272    RouteDiscoveryHandler::invalidate_routes(core_ctx, bindings_ctx, device_id);
1273
1274    RsHandler::stop_router_solicitation(core_ctx, bindings_ctx, device_id);
1275
1276    // Reset the learned network parameters. If the device is re-enabled in the
1277    // future, there's no guarantee that it's on the same network.
1278    core_ctx.with_network_learned_parameters_mut(device_id, |params| params.reset());
1279
1280    // Delete the link-local address generated when enabling the device and stop
1281    // DAD on the other addresses.
1282    core_ctx
1283        .with_address_ids(device_id, |addrs, core_ctx| {
1284            addrs
1285                .map(|addr_id| {
1286                    core_ctx.with_ip_address_data(
1287                        device_id,
1288                        &addr_id,
1289                        |IpAddressData { flags: _, config }| (addr_id.clone(), *config),
1290                    )
1291                })
1292                .collect::<Vec<_>>()
1293        })
1294        .into_iter()
1295        .for_each(|(addr_id, config)| {
1296            if config
1297                .is_some_and(|config| config.is_slaac() && addr_id.addr().addr().is_link_local())
1298            {
1299                del_ip_addr_inner_and_notify_handler(
1300                    core_ctx,
1301                    bindings_ctx,
1302                    device_id,
1303                    DelIpAddr::AddressId(addr_id),
1304                    AddressRemovedReason::Manual,
1305                    device_config,
1306                )
1307                .map(|remove_result| {
1308                    bindings_ctx.defer_removal_result(remove_result);
1309                })
1310                .unwrap_or_else(|NotFoundError| {
1311                    // We're not holding locks on the addresses anymore we must
1312                    // allow a NotFoundError since the address can be removed as
1313                    // we release the lock.
1314                })
1315            } else {
1316                DadHandler::stop_duplicate_address_detection(
1317                    core_ctx,
1318                    bindings_ctx,
1319                    device_id,
1320                    &addr_id,
1321                );
1322                bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
1323                    device: device_id.clone(),
1324                    addr: addr_id.addr().into(),
1325                    state: IpAddressState::Unavailable,
1326                });
1327            }
1328        });
1329
1330    GmpHandler::gmp_handle_disabled(core_ctx, bindings_ctx, device_id);
1331    leave_ip_multicast_with_config(
1332        core_ctx,
1333        bindings_ctx,
1334        device_id,
1335        Ipv6::ALL_NODES_LINK_LOCAL_MULTICAST_ADDRESS,
1336        device_config,
1337    );
1338}
1339
1340fn enable_ipv4_device_with_config<
1341    BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
1342    CC: IpDeviceStateContext<Ipv4, BC> + GmpHandler<Ipv4, BC> + DadHandler<Ipv4, BC>,
1343>(
1344    core_ctx: &mut CC,
1345    bindings_ctx: &mut BC,
1346    device_id: &CC::DeviceId,
1347    config: &Ipv4DeviceConfiguration,
1348) {
1349    // All systems should join the all-systems multicast group.
1350    join_ip_multicast_with_config(
1351        core_ctx,
1352        bindings_ctx,
1353        device_id,
1354        Ipv4::ALL_SYSTEMS_MULTICAST_ADDRESS,
1355        config,
1356    );
1357    GmpHandler::gmp_handle_maybe_enabled(core_ctx, bindings_ctx, device_id);
1358    core_ctx
1359        .with_address_ids(device_id, |addrs, _core_ctx| addrs.collect::<Vec<_>>())
1360        .into_iter()
1361        .for_each(|addr_id| {
1362            let needs_dad = DadHandler::initialize_duplicate_address_detection(
1363                core_ctx,
1364                bindings_ctx,
1365                device_id,
1366                &addr_id,
1367                |state| IpDeviceEvent::AddressStateChanged {
1368                    device: device_id.clone(),
1369                    addr: addr_id.addr().into(),
1370                    state,
1371                },
1372            );
1373            match needs_dad {
1374                NeedsDad::Yes(token) => {
1375                    core_ctx.start_duplicate_address_detection(bindings_ctx, token);
1376                }
1377                NeedsDad::No => {}
1378            }
1379        })
1380}
1381
1382fn disable_ipv4_device_with_config<
1383    BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
1384    CC: IpDeviceStateContext<Ipv4, BC> + GmpHandler<Ipv4, BC> + NudIpHandler<Ipv4, BC>,
1385>(
1386    core_ctx: &mut CC,
1387    bindings_ctx: &mut BC,
1388    device_id: &CC::DeviceId,
1389    config: &Ipv4DeviceConfiguration,
1390) {
1391    NudIpHandler::flush_neighbor_table(core_ctx, bindings_ctx, device_id);
1392    GmpHandler::gmp_handle_disabled(core_ctx, bindings_ctx, device_id);
1393    leave_ip_multicast_with_config(
1394        core_ctx,
1395        bindings_ctx,
1396        device_id,
1397        Ipv4::ALL_SYSTEMS_MULTICAST_ADDRESS,
1398        config,
1399    );
1400    core_ctx.with_address_ids(device_id, |addrs, _core_ctx| {
1401        addrs.for_each(|addr| {
1402            bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
1403                device: device_id.clone(),
1404                addr: addr.addr().into(),
1405                state: IpAddressState::Unavailable,
1406            });
1407        })
1408    })
1409}
1410
1411/// Gets a single IPv4 address and subnet for a device.
1412pub fn get_ipv4_addr_subnet<BT: IpDeviceStateBindingsTypes, CC: IpDeviceStateContext<Ipv4, BT>>(
1413    core_ctx: &mut CC,
1414    device_id: &CC::DeviceId,
1415) -> Option<AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>> {
1416    core_ctx.with_address_ids(device_id, |mut addrs, _core_ctx| addrs.next().map(|a| a.addr_sub()))
1417}
1418
1419/// Gets the hop limit for new IPv6 packets that will be sent out from `device`.
1420pub fn get_ipv6_hop_limit<BT: IpDeviceStateBindingsTypes, CC: IpDeviceStateContext<Ipv6, BT>>(
1421    core_ctx: &mut CC,
1422    device: &CC::DeviceId,
1423) -> NonZeroU8 {
1424    core_ctx.with_default_hop_limit(device, Clone::clone)
1425}
1426
1427/// Is IP packet unicast forwarding enabled?
1428pub fn is_ip_unicast_forwarding_enabled<
1429    I: IpDeviceIpExt,
1430    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1431    CC: IpDeviceConfigurationContext<I, BC>,
1432>(
1433    core_ctx: &mut CC,
1434    device_id: &CC::DeviceId,
1435) -> bool {
1436    core_ctx.with_ip_device_configuration(device_id, |state, _ctx| {
1437        AsRef::<IpDeviceConfiguration>::as_ref(state).unicast_forwarding_enabled
1438    })
1439}
1440
1441/// Is IP packet multicast forwarding enabled?
1442pub fn is_ip_multicast_forwarding_enabled<
1443    I: IpDeviceIpExt,
1444    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1445    CC: IpDeviceConfigurationContext<I, BC>,
1446>(
1447    core_ctx: &mut CC,
1448    device_id: &CC::DeviceId,
1449) -> bool {
1450    core_ctx.with_ip_device_configuration(device_id, |state, _ctx| {
1451        AsRef::<IpDeviceConfiguration>::as_ref(state).multicast_forwarding_enabled
1452    })
1453}
1454
1455/// Joins the multicast group `multicast_addr` on `device_id`.
1456///
1457/// `_config` is not used but required to make sure that the caller is currently
1458/// holding a a reference to the IP device's IP configuration as a way to prove
1459/// that caller has synchronized this operation with other accesses to the IP
1460/// device configuration.
1461pub fn join_ip_multicast_with_config<
1462    I: IpDeviceIpExt,
1463    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1464    CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC>,
1465>(
1466    core_ctx: &mut CC,
1467    bindings_ctx: &mut BC,
1468    device_id: &CC::DeviceId,
1469    multicast_addr: MulticastAddr<I::Addr>,
1470    _config: &I::Configuration,
1471) {
1472    match core_ctx.gmp_join_group(bindings_ctx, device_id, multicast_addr) {
1473        GroupJoinResult::Joined(()) => {
1474            core_ctx.join_link_multicast_group(bindings_ctx, device_id, multicast_addr)
1475        }
1476        GroupJoinResult::AlreadyMember => {}
1477    }
1478}
1479
1480/// Adds `device_id` to a multicast group `multicast_addr`.
1481///
1482/// Calling `join_ip_multicast` multiple times is completely safe. A counter
1483/// will be kept for the number of times `join_ip_multicast` has been called
1484/// with the same `device_id` and `multicast_addr` pair. To completely leave a
1485/// multicast group, [`leave_ip_multicast`] must be called the same number of
1486/// times `join_ip_multicast` has been called for the same `device_id` and
1487/// `multicast_addr` pair. The first time `join_ip_multicast` is called for a
1488/// new `device` and `multicast_addr` pair, the device will actually join the
1489/// multicast group.
1490pub fn join_ip_multicast<
1491    I: IpDeviceIpExt,
1492    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1493    CC: IpDeviceConfigurationContext<I, BC>,
1494>(
1495    core_ctx: &mut CC,
1496    bindings_ctx: &mut BC,
1497    device_id: &CC::DeviceId,
1498    multicast_addr: MulticastAddr<I::Addr>,
1499) {
1500    core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
1501        join_ip_multicast_with_config(
1502            &mut core_ctx,
1503            bindings_ctx,
1504            device_id,
1505            multicast_addr,
1506            config,
1507        )
1508    })
1509}
1510
1511/// Leaves the multicast group `multicast_addr` on `device_id`.
1512///
1513/// `_config` is not used but required to make sure that the caller is currently
1514/// holding a a reference to the IP device's IP configuration as a way to prove
1515/// that caller has synchronized this operation with other accesses to the IP
1516/// device configuration.
1517pub fn leave_ip_multicast_with_config<
1518    I: IpDeviceIpExt,
1519    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1520    CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC>,
1521>(
1522    core_ctx: &mut CC,
1523    bindings_ctx: &mut BC,
1524    device_id: &CC::DeviceId,
1525    multicast_addr: MulticastAddr<I::Addr>,
1526    _config: &I::Configuration,
1527) {
1528    match core_ctx.gmp_leave_group(bindings_ctx, device_id, multicast_addr) {
1529        GroupLeaveResult::Left(()) => {
1530            core_ctx.leave_link_multicast_group(bindings_ctx, device_id, multicast_addr)
1531        }
1532        GroupLeaveResult::StillMember => {}
1533        GroupLeaveResult::NotMember => panic!(
1534            "attempted to leave IP multicast group we were not a member of: {}",
1535            multicast_addr,
1536        ),
1537    }
1538}
1539
1540/// Removes `device_id` from a multicast group `multicast_addr`.
1541///
1542/// `leave_ip_multicast` will attempt to remove `device_id` from a multicast
1543/// group `multicast_addr`. `device_id` may have "joined" the same multicast
1544/// address multiple times, so `device_id` will only leave the multicast group
1545/// once `leave_ip_multicast` has been called for each corresponding
1546/// [`join_ip_multicast`]. That is, if `join_ip_multicast` gets called 3
1547/// times and `leave_ip_multicast` gets called two times (after all 3
1548/// `join_ip_multicast` calls), `device_id` will still be in the multicast
1549/// group until the next (final) call to `leave_ip_multicast`.
1550///
1551/// # Panics
1552///
1553/// If `device_id` is not currently in the multicast group `multicast_addr`.
1554pub fn leave_ip_multicast<
1555    I: IpDeviceIpExt,
1556    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1557    CC: IpDeviceConfigurationContext<I, BC>,
1558>(
1559    core_ctx: &mut CC,
1560    bindings_ctx: &mut BC,
1561    device_id: &CC::DeviceId,
1562    multicast_addr: MulticastAddr<I::Addr>,
1563) {
1564    core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
1565        leave_ip_multicast_with_config(
1566            &mut core_ctx,
1567            bindings_ctx,
1568            device_id,
1569            multicast_addr,
1570            config,
1571        )
1572    })
1573}
1574
1575/// Adds `addr_sub` to `device_id` with configuration `addr_config`.
1576///
1577/// `_device_config` is not used but required to make sure that the caller is
1578/// currently holding a a reference to the IP device's IP configuration as a way
1579/// to prove that caller has synchronized this operation with other accesses to
1580/// the IP device configuration.
1581///
1582/// See [`IpDeviceAddAddressContext`] for further synchronization guarantees.
1583pub fn add_ip_addr_subnet_with_config<
1584    I: IpDeviceIpExt,
1585    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1586    CC: IpDeviceStateContext<I, BC>
1587        + IpDeviceAddAddressContext<I, BC>
1588        + GmpHandler<I, BC>
1589        + DadHandler<I, BC>,
1590>(
1591    core_ctx: &mut CC,
1592    bindings_ctx: &mut BC,
1593    device_id: &CC::DeviceId,
1594    addr_sub: AddrSubnet<I::Addr, I::AssignedWitness>,
1595    addr_config: I::AddressConfig<BC::Instant>,
1596    _device_config: &I::Configuration,
1597) -> Result<CC::AddressId, ExistsError> {
1598    info!("adding addr {addr_sub:?} config {addr_config:?} to device {device_id:?}");
1599    let CommonAddressProperties { valid_until, preferred_lifetime } =
1600        I::get_common_props(&addr_config);
1601    let addr_id = core_ctx.add_ip_address(device_id, addr_sub, addr_config)?;
1602    assert_eq!(addr_id.addr().addr(), addr_sub.addr().get());
1603
1604    let ip_enabled =
1605        core_ctx.with_ip_device_flags(device_id, |IpDeviceFlags { ip_enabled }| *ip_enabled);
1606
1607    let needs_dad = if ip_enabled {
1608        DadHandler::initialize_duplicate_address_detection(
1609            core_ctx,
1610            bindings_ctx,
1611            device_id,
1612            &addr_id,
1613            |state| IpDeviceEvent::AddressAdded {
1614                device: device_id.clone(),
1615                addr: addr_sub.to_witness(),
1616                state,
1617                valid_until,
1618                preferred_lifetime,
1619            },
1620        )
1621    } else {
1622        // NB: We don't start DAD if the device is disabled. DAD will be
1623        // performed when the device is enabled for all addresses.
1624        //
1625        // Typically, the `AddressAdded` event would be dispatched by the DAD
1626        // engine, but because we're not calling into the DAD engine we need
1627        // to dispatch the event here.
1628        bindings_ctx.on_event(IpDeviceEvent::AddressAdded {
1629            device: device_id.clone(),
1630            addr: addr_sub.to_witness(),
1631            state: IpAddressState::Unavailable,
1632            valid_until,
1633            preferred_lifetime,
1634        });
1635        NeedsDad::No
1636    };
1637
1638    match needs_dad {
1639        NeedsDad::Yes(token) => {
1640            core_ctx.start_duplicate_address_detection(bindings_ctx, token);
1641        }
1642        NeedsDad::No => {}
1643    }
1644
1645    Ok(addr_id)
1646}
1647
1648/// A handler to abstract side-effects of removing IP device addresses.
1649pub trait IpAddressRemovalHandler<I: IpDeviceIpExt, BC: InstantBindingsTypes>:
1650    DeviceIdContext<AnyDevice>
1651{
1652    /// Notifies the handler that the addr `addr` with `config` has been removed
1653    /// from `device_id` with `reason`.
1654    fn on_address_removed(
1655        &mut self,
1656        bindings_ctx: &mut BC,
1657        device_id: &Self::DeviceId,
1658        addr_sub: AddrSubnet<I::Addr, I::AssignedWitness>,
1659        config: I::AddressConfig<BC::Instant>,
1660        reason: AddressRemovedReason,
1661    );
1662}
1663
1664/// There's no special action to be taken for removed IPv4 addresses.
1665impl<CC: DeviceIdContext<AnyDevice>, BC: InstantBindingsTypes> IpAddressRemovalHandler<Ipv4, BC>
1666    for CC
1667{
1668    fn on_address_removed(
1669        &mut self,
1670        _bindings_ctx: &mut BC,
1671        _device_id: &Self::DeviceId,
1672        _addr_sub: AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>,
1673        _config: Ipv4AddrConfig<BC::Instant>,
1674        _reason: AddressRemovedReason,
1675    ) {
1676        // Nothing to do.
1677    }
1678}
1679
1680/// Provide the IPv6 implementation for all [`SlaacHandler`] implementations.
1681impl<CC: SlaacHandler<BC>, BC: InstantContext> IpAddressRemovalHandler<Ipv6, BC> for CC {
1682    fn on_address_removed(
1683        &mut self,
1684        bindings_ctx: &mut BC,
1685        device_id: &Self::DeviceId,
1686        addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
1687        config: Ipv6AddrConfig<BC::Instant>,
1688        reason: AddressRemovedReason,
1689    ) {
1690        match config {
1691            Ipv6AddrConfig::Slaac(config) => SlaacHandler::on_address_removed(
1692                self,
1693                bindings_ctx,
1694                device_id,
1695                addr_sub,
1696                config,
1697                reason,
1698            ),
1699            Ipv6AddrConfig::Manual(_manual_config) => (),
1700        }
1701    }
1702}
1703
1704/// Possible representations of an IP address that is valid for deletion.
1705#[allow(missing_docs)]
1706pub enum DelIpAddr<Id, A> {
1707    SpecifiedAddr(SpecifiedAddr<A>),
1708    AddressId(Id),
1709}
1710
1711impl<Id: IpAddressId<A>, A: IpAddress<Version: AssignedAddrIpExt>> Display for DelIpAddr<Id, A> {
1712    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1713        match self {
1714            DelIpAddr::SpecifiedAddr(addr) => write!(f, "{}", *addr),
1715            DelIpAddr::AddressId(id) => write!(f, "{}", id.addr()),
1716        }
1717    }
1718}
1719
1720/// Deletes an IP address from a device, returning the address and its
1721/// configuration if it was removed.
1722pub fn del_ip_addr_inner<
1723    I: IpDeviceIpExt,
1724    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1725    CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC> + DadHandler<I, BC>,
1726>(
1727    core_ctx: &mut CC,
1728    bindings_ctx: &mut BC,
1729    device_id: &CC::DeviceId,
1730    addr: DelIpAddr<CC::AddressId, I::Addr>,
1731    reason: AddressRemovedReason,
1732    // Require configuration lock to do this.
1733    //
1734    // This ensures a fixed device configuration, but also synchronization with
1735    // address addition and removal.
1736    //
1737    // See `IpDeviceAddAddressContext` for further synchronization guarantees.
1738    _config: &I::Configuration,
1739) -> Result<
1740    (
1741        AddrSubnet<I::Addr, I::AssignedWitness>,
1742        I::AddressConfig<BC::Instant>,
1743        RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>,
1744    ),
1745    NotFoundError,
1746> {
1747    let addr_id = match addr {
1748        DelIpAddr::SpecifiedAddr(addr) => core_ctx.get_address_id(device_id, addr)?,
1749        DelIpAddr::AddressId(id) => id,
1750    };
1751    DadHandler::stop_duplicate_address_detection(core_ctx, bindings_ctx, device_id, &addr_id);
1752    // Extract the configuration out of the address to properly mark it as ready
1753    // for deletion. If the configuration has already been taken, consider as if
1754    // the address is already removed.
1755    let addr_config = core_ctx
1756        .with_ip_address_data_mut(device_id, &addr_id, |addr_data| addr_data.config.take())
1757        .ok_or(NotFoundError)?;
1758
1759    let addr_sub = addr_id.addr_sub();
1760    let result = core_ctx.remove_ip_address(device_id, addr_id);
1761
1762    bindings_ctx.on_event(IpDeviceEvent::AddressRemoved {
1763        device: device_id.clone(),
1764        addr: addr_sub.addr().into(),
1765        reason,
1766    });
1767
1768    Ok((addr_sub, addr_config, result))
1769}
1770
1771/// Removes an IP address and associated subnet from this device.
1772fn del_ip_addr<
1773    I: IpDeviceIpExt,
1774    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1775    CC: IpDeviceConfigurationContext<I, BC>,
1776>(
1777    core_ctx: &mut CC,
1778    bindings_ctx: &mut BC,
1779    device_id: &CC::DeviceId,
1780    addr: DelIpAddr<CC::AddressId, I::Addr>,
1781    reason: AddressRemovedReason,
1782) -> Result<RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>, NotFoundError> {
1783    info!("removing addr {addr} from device {device_id:?}");
1784    core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
1785        del_ip_addr_inner_and_notify_handler(
1786            &mut core_ctx,
1787            bindings_ctx,
1788            device_id,
1789            addr,
1790            reason,
1791            config,
1792        )
1793    })
1794}
1795
1796/// Removes an IP address and associated subnet from this device and notifies
1797/// the address removal handler.
1798fn del_ip_addr_inner_and_notify_handler<
1799    I: IpDeviceIpExt,
1800    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1801    CC: IpDeviceStateContext<I, BC>
1802        + GmpHandler<I, BC>
1803        + DadHandler<I, BC>
1804        + IpAddressRemovalHandler<I, BC>,
1805>(
1806    core_ctx: &mut CC,
1807    bindings_ctx: &mut BC,
1808    device_id: &CC::DeviceId,
1809    addr: DelIpAddr<CC::AddressId, I::Addr>,
1810    reason: AddressRemovedReason,
1811    config: &I::Configuration,
1812) -> Result<RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>, NotFoundError> {
1813    del_ip_addr_inner(core_ctx, bindings_ctx, device_id, addr, reason, config).map(
1814        |(addr_sub, config, result)| {
1815            core_ctx.on_address_removed(bindings_ctx, device_id, addr_sub, config, reason);
1816            result
1817        },
1818    )
1819}
1820
1821/// Returns whether `device_id` is enabled for IP version `I`.
1822pub fn is_ip_device_enabled<
1823    I: IpDeviceIpExt,
1824    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1825    CC: IpDeviceStateContext<I, BC>,
1826>(
1827    core_ctx: &mut CC,
1828    device_id: &CC::DeviceId,
1829) -> bool {
1830    core_ctx.with_ip_device_flags(device_id, |flags| flags.ip_enabled)
1831}
1832
1833/// Removes IPv4 state for the device without emitting events.
1834pub fn clear_ipv4_device_state<
1835    BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
1836    CC: IpDeviceConfigurationContext<Ipv4, BC>,
1837>(
1838    core_ctx: &mut CC,
1839    bindings_ctx: &mut BC,
1840    device_id: &CC::DeviceId,
1841) {
1842    core_ctx.with_ip_device_configuration_mut(device_id, |mut core_ctx| {
1843        let ip_enabled = core_ctx.with_configuration_and_flags_mut(device_id, |_config, flags| {
1844            // Start by force-disabling IPv4 so we're sure we won't handle
1845            // any more packets.
1846            let IpDeviceFlags { ip_enabled } = flags;
1847            core::mem::replace(ip_enabled, false)
1848        });
1849
1850        let (config, mut core_ctx) = core_ctx.ip_device_configuration_and_ctx();
1851        let core_ctx = &mut core_ctx;
1852        if ip_enabled {
1853            disable_ipv4_device_with_config(core_ctx, bindings_ctx, device_id, config);
1854        }
1855    })
1856}
1857
1858/// Removes IPv6 state for the device without emitting events.
1859pub fn clear_ipv6_device_state<
1860    BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1861    CC: Ipv6DeviceConfigurationContext<BC>,
1862>(
1863    core_ctx: &mut CC,
1864    bindings_ctx: &mut BC,
1865    device_id: &CC::DeviceId,
1866) {
1867    core_ctx.with_ipv6_device_configuration_mut(device_id, |mut core_ctx| {
1868        let ip_enabled = core_ctx.with_configuration_and_flags_mut(device_id, |_config, flags| {
1869            // Start by force-disabling IPv6 so we're sure we won't handle
1870            // any more packets.
1871            let IpDeviceFlags { ip_enabled } = flags;
1872            core::mem::replace(ip_enabled, false)
1873        });
1874
1875        let (config, mut core_ctx) = core_ctx.ipv6_device_configuration_and_ctx();
1876        let core_ctx = &mut core_ctx;
1877        if ip_enabled {
1878            disable_ipv6_device_with_config(core_ctx, bindings_ctx, device_id, config);
1879        }
1880    })
1881}
1882
1883/// Dispatches a received ARP packet (Request or Reply) to the IP layer.
1884///
1885/// Returns whether the `target_addr` is assigned on the device.
1886pub fn on_arp_packet<CC, BC>(
1887    core_ctx: &mut CC,
1888    bindings_ctx: &mut BC,
1889    device_id: &CC::DeviceId,
1890    sender_addr: Ipv4Addr,
1891    target_addr: Ipv4Addr,
1892    is_arp_probe: bool,
1893) -> bool
1894where
1895    CC: IpDeviceHandler<Ipv4, BC> + IpDeviceStateContext<Ipv4, BC>,
1896    BC: IpDeviceStateBindingsTypes,
1897{
1898    // As Per RFC 5227, section 2.1.1
1899    //   If [...] the host receives any ARP packet (Request *or* Reply) on the
1900    //   interface where the probe is being performed, where the packet's
1901    //   'sender IP address' is the address being probed for, then the host MUST
1902    //   treat this address as being in use by some other host.
1903    if let Some(sender_addr) = SpecifiedAddr::new(sender_addr) {
1904        let sender_addr_state = IpDeviceHandler::<Ipv4, _>::handle_received_dad_packet(
1905            core_ctx,
1906            bindings_ctx,
1907            &device_id,
1908            sender_addr,
1909            Ipv4DadAddressInfo::SourceAddr,
1910        );
1911        match sender_addr_state {
1912            None => {}
1913            // As Per RFC 5227 section 2.4:
1914            //   At any time, if a host receives an ARP packet (Request *or*
1915            //   Reply) where the 'sender IP address' is (one of) the host's own
1916            //   IP address(es) configured on that interface, but the 'sender
1917            //   hardware address' does not match any of the host's own
1918            //   interface addresses, then this is a conflicting ARP packet,
1919            //   indicating some other host also thinks it is validly using this
1920            //   address.
1921            Some(IpAddressState::Assigned) => {
1922                info!("DAD received conflicting ARP packet for assigned addr=({sender_addr})");
1923            }
1924            Some(IpAddressState::Tentative) => {
1925                debug!("DAD received conflicting ARP packet for tentative addr=({sender_addr})");
1926            }
1927            Some(IpAddressState::Unavailable) => {
1928                debug!("DAD received conflicting ARP packet for unavailable addr=({sender_addr})");
1929            }
1930        }
1931    }
1932
1933    let Some(target_addr) = SpecifiedAddr::new(target_addr) else {
1934        return false;
1935    };
1936
1937    // As Per RFC 5227, section 2.1.1
1938    //  In addition, if during this period the host receives any ARP Probe
1939    //  where the packet's 'target IP address' is the address being probed
1940    //  for, [... ] then the host SHOULD similarly treat this as an address
1941    //  conflict.
1942    if is_arp_probe {
1943        let target_addr_state = IpDeviceHandler::<Ipv4, _>::handle_received_dad_packet(
1944            core_ctx,
1945            bindings_ctx,
1946            &device_id,
1947            target_addr,
1948            Ipv4DadAddressInfo::TargetAddr,
1949        );
1950        let assigned = match target_addr_state {
1951            None => false,
1952            // Unlike the sender_addr, it's not concerning to receive an ARP
1953            // packet whose target_addr is assigned to us.
1954            Some(IpAddressState::Assigned) => true,
1955            Some(IpAddressState::Tentative) => {
1956                debug!("DAD received conflicting ARP packet for tentative addr=({target_addr})");
1957                false
1958            }
1959            Some(IpAddressState::Unavailable) => {
1960                debug!("DAD received conflicting ARP packet for unavailable addr=({target_addr})");
1961                false
1962            }
1963        };
1964        assigned
1965    } else {
1966        // Otherwise, don't dispatch the probe to DAD. Instead just return the
1967        // target address' assignment state.
1968        let addr_id = match core_ctx.get_address_id(device_id, target_addr) {
1969            Ok(o) => o,
1970            Err(NotFoundError) => return false,
1971        };
1972
1973        core_ctx.with_ip_address_data(
1974            device_id,
1975            &addr_id,
1976            |IpAddressData { flags: IpAddressFlags { assigned }, config: _ }| *assigned,
1977        )
1978    }
1979}
1980
1981#[cfg(any(test, feature = "testutils"))]
1982pub(crate) mod testutil {
1983    use alloc::boxed::Box;
1984
1985    use crate::device::IpAddressFlags;
1986
1987    use super::*;
1988
1989    /// Calls the callback with an iterator of the IPv4 addresses assigned to
1990    /// `device_id`.
1991    pub fn with_assigned_ipv4_addr_subnets<
1992        BT: IpDeviceStateBindingsTypes,
1993        CC: IpDeviceStateContext<Ipv4, BT>,
1994        O,
1995        F: FnOnce(Box<dyn Iterator<Item = AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>> + '_>) -> O,
1996    >(
1997        core_ctx: &mut CC,
1998        device_id: &CC::DeviceId,
1999        cb: F,
2000    ) -> O {
2001        core_ctx.with_address_ids(device_id, |addrs, _core_ctx| {
2002            cb(Box::new(addrs.map(|a| a.addr_sub())))
2003        })
2004    }
2005
2006    /// Gets the IPv6 address and subnet pairs associated with this device which are
2007    /// in the assigned state.
2008    ///
2009    /// Tentative IP addresses (addresses which are not yet fully bound to a device)
2010    /// and deprecated IP addresses (addresses which have been assigned but should
2011    /// no longer be used for new connections) will not be returned by
2012    /// `get_assigned_ipv6_addr_subnets`.
2013    ///
2014    /// Returns an [`Iterator`] of `AddrSubnet`.
2015    ///
2016    /// See [`Tentative`] and [`AddrSubnet`] for more information.
2017    pub fn with_assigned_ipv6_addr_subnets<
2018        BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
2019        CC: Ipv6DeviceContext<BC>,
2020        O,
2021        F: FnOnce(Box<dyn Iterator<Item = AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>> + '_>) -> O,
2022    >(
2023        core_ctx: &mut CC,
2024        device_id: &CC::DeviceId,
2025        cb: F,
2026    ) -> O {
2027        core_ctx.with_address_ids(device_id, |addrs, core_ctx| {
2028            cb(Box::new(addrs.filter_map(|addr_id| {
2029                core_ctx
2030                    .with_ip_address_data(
2031                        device_id,
2032                        &addr_id,
2033                        |IpAddressData { flags: IpAddressFlags { assigned }, config: _ }| *assigned,
2034                    )
2035                    .then(|| addr_id.addr_sub())
2036            })))
2037        })
2038    }
2039}