Skip to main content

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