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, NetworkSerializer, NotFoundError, RemoveResourceResultWithContext,
34    ResourceCounterContext, RngContext, SendFrameError, StrongDeviceIdentifier, TimerContext,
35    TimerHandler, TxMetadataBindingsTypes, WeakDeviceIdentifier, WeakIpAddressId,
36};
37use netstack3_filter::ProofOfEgressCheck;
38use packet::BufferMut;
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: NetworkSerializer,
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>
1389        + GmpHandler<Ipv4, BC>
1390        + DadHandler<Ipv4, BC>
1391        + NudIpHandler<Ipv4, BC>,
1392>(
1393    core_ctx: &mut CC,
1394    bindings_ctx: &mut BC,
1395    device_id: &CC::DeviceId,
1396    config: &Ipv4DeviceConfiguration,
1397) {
1398    NudIpHandler::flush_neighbor_table(core_ctx, bindings_ctx, device_id);
1399    GmpHandler::gmp_handle_disabled(core_ctx, bindings_ctx, device_id);
1400    leave_ip_multicast_with_config(
1401        core_ctx,
1402        bindings_ctx,
1403        device_id,
1404        Ipv4::ALL_SYSTEMS_MULTICAST_ADDRESS,
1405        config,
1406    );
1407    core_ctx
1408        .with_address_ids(device_id, |addrs, _core_ctx| addrs.collect::<Vec<_>>())
1409        .into_iter()
1410        .for_each(|addr_id| {
1411            DadHandler::stop_duplicate_address_detection(
1412                core_ctx,
1413                bindings_ctx,
1414                device_id,
1415                &addr_id,
1416            );
1417            bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
1418                device: device_id.clone(),
1419                addr: addr_id.addr().into(),
1420                state: IpAddressState::Unavailable,
1421            });
1422        });
1423}
1424
1425/// Gets a single IPv4 address and subnet for a device.
1426pub fn get_ipv4_addr_subnet<BT: IpDeviceStateBindingsTypes, CC: IpDeviceStateContext<Ipv4, BT>>(
1427    core_ctx: &mut CC,
1428    device_id: &CC::DeviceId,
1429) -> Option<AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>> {
1430    core_ctx.with_address_ids(device_id, |mut addrs, _core_ctx| addrs.next().map(|a| a.addr_sub()))
1431}
1432
1433/// Gets the hop limit for new IPv6 packets that will be sent out from `device`.
1434pub fn get_ipv6_hop_limit<BT: IpDeviceStateBindingsTypes, CC: IpDeviceStateContext<Ipv6, BT>>(
1435    core_ctx: &mut CC,
1436    device: &CC::DeviceId,
1437) -> NonZeroU8 {
1438    core_ctx.with_default_hop_limit(device, Clone::clone)
1439}
1440
1441/// Is IP packet unicast forwarding enabled?
1442pub fn is_ip_unicast_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).unicast_forwarding_enabled
1452    })
1453}
1454
1455/// Is IP packet multicast forwarding enabled?
1456pub fn is_ip_multicast_forwarding_enabled<
1457    I: IpDeviceIpExt,
1458    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1459    CC: IpDeviceConfigurationContext<I, BC>,
1460>(
1461    core_ctx: &mut CC,
1462    device_id: &CC::DeviceId,
1463) -> bool {
1464    core_ctx.with_ip_device_configuration(device_id, |state, _ctx| {
1465        AsRef::<IpDeviceConfiguration>::as_ref(state).multicast_forwarding_enabled
1466    })
1467}
1468
1469/// Joins the multicast group `multicast_addr` on `device_id`.
1470///
1471/// `_config` is not used but required to make sure that the caller is currently
1472/// holding a a reference to the IP device's IP configuration as a way to prove
1473/// that caller has synchronized this operation with other accesses to the IP
1474/// device configuration.
1475pub fn join_ip_multicast_with_config<
1476    I: IpDeviceIpExt,
1477    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1478    CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC>,
1479>(
1480    core_ctx: &mut CC,
1481    bindings_ctx: &mut BC,
1482    device_id: &CC::DeviceId,
1483    multicast_addr: MulticastAddr<I::Addr>,
1484    _config: &I::Configuration,
1485) {
1486    match core_ctx.gmp_join_group(bindings_ctx, device_id, multicast_addr) {
1487        GroupJoinResult::Joined(()) => {
1488            debug!("joined IP Multicast Group {multicast_addr} on device {device_id:?}");
1489            core_ctx.join_link_multicast_group(bindings_ctx, device_id, multicast_addr)
1490        }
1491        GroupJoinResult::AlreadyMember => {}
1492    }
1493}
1494
1495/// Adds `device_id` to a multicast group `multicast_addr`.
1496///
1497/// Calling `join_ip_multicast` multiple times is completely safe. A counter
1498/// will be kept for the number of times `join_ip_multicast` has been called
1499/// with the same `device_id` and `multicast_addr` pair. To completely leave a
1500/// multicast group, [`leave_ip_multicast`] must be called the same number of
1501/// times `join_ip_multicast` has been called for the same `device_id` and
1502/// `multicast_addr` pair. The first time `join_ip_multicast` is called for a
1503/// new `device` and `multicast_addr` pair, the device will actually join the
1504/// multicast group.
1505pub fn join_ip_multicast<
1506    I: IpDeviceIpExt,
1507    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1508    CC: IpDeviceConfigurationContext<I, BC>,
1509>(
1510    core_ctx: &mut CC,
1511    bindings_ctx: &mut BC,
1512    device_id: &CC::DeviceId,
1513    multicast_addr: MulticastAddr<I::Addr>,
1514) {
1515    core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
1516        join_ip_multicast_with_config(
1517            &mut core_ctx,
1518            bindings_ctx,
1519            device_id,
1520            multicast_addr,
1521            config,
1522        )
1523    })
1524}
1525
1526/// Leaves the multicast group `multicast_addr` on `device_id`.
1527///
1528/// `_config` is not used but required to make sure that the caller is currently
1529/// holding a a reference to the IP device's IP configuration as a way to prove
1530/// that caller has synchronized this operation with other accesses to the IP
1531/// device configuration.
1532pub fn leave_ip_multicast_with_config<
1533    I: IpDeviceIpExt,
1534    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1535    CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC>,
1536>(
1537    core_ctx: &mut CC,
1538    bindings_ctx: &mut BC,
1539    device_id: &CC::DeviceId,
1540    multicast_addr: MulticastAddr<I::Addr>,
1541    _config: &I::Configuration,
1542) {
1543    match core_ctx.gmp_leave_group(bindings_ctx, device_id, multicast_addr) {
1544        GroupLeaveResult::Left(()) => {
1545            core_ctx.leave_link_multicast_group(bindings_ctx, device_id, multicast_addr)
1546        }
1547        GroupLeaveResult::StillMember => {}
1548        GroupLeaveResult::NotMember => panic!(
1549            "attempted to leave IP multicast group we were not a member of: {}",
1550            multicast_addr,
1551        ),
1552    }
1553}
1554
1555/// Removes `device_id` from a multicast group `multicast_addr`.
1556///
1557/// `leave_ip_multicast` will attempt to remove `device_id` from a multicast
1558/// group `multicast_addr`. `device_id` may have "joined" the same multicast
1559/// address multiple times, so `device_id` will only leave the multicast group
1560/// once `leave_ip_multicast` has been called for each corresponding
1561/// [`join_ip_multicast`]. That is, if `join_ip_multicast` gets called 3
1562/// times and `leave_ip_multicast` gets called two times (after all 3
1563/// `join_ip_multicast` calls), `device_id` will still be in the multicast
1564/// group until the next (final) call to `leave_ip_multicast`.
1565///
1566/// # Panics
1567///
1568/// If `device_id` is not currently in the multicast group `multicast_addr`.
1569pub fn leave_ip_multicast<
1570    I: IpDeviceIpExt,
1571    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1572    CC: IpDeviceConfigurationContext<I, BC>,
1573>(
1574    core_ctx: &mut CC,
1575    bindings_ctx: &mut BC,
1576    device_id: &CC::DeviceId,
1577    multicast_addr: MulticastAddr<I::Addr>,
1578) {
1579    core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
1580        leave_ip_multicast_with_config(
1581            &mut core_ctx,
1582            bindings_ctx,
1583            device_id,
1584            multicast_addr,
1585            config,
1586        )
1587    })
1588}
1589
1590/// Adds `addr_sub` to `device_id` with configuration `addr_config`.
1591///
1592/// `_device_config` is not used but required to make sure that the caller is
1593/// currently holding a a reference to the IP device's IP configuration as a way
1594/// to prove that caller has synchronized this operation with other accesses to
1595/// the IP device configuration.
1596///
1597/// See [`IpDeviceAddAddressContext`] for further synchronization guarantees.
1598pub fn add_ip_addr_subnet_with_config<
1599    I: IpDeviceIpExt,
1600    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1601    CC: IpDeviceStateContext<I, BC>
1602        + IpDeviceAddAddressContext<I, BC>
1603        + GmpHandler<I, BC>
1604        + DadHandler<I, BC>,
1605>(
1606    core_ctx: &mut CC,
1607    bindings_ctx: &mut BC,
1608    device_id: &CC::DeviceId,
1609    addr_sub: AddrSubnet<I::Addr, I::AssignedWitness>,
1610    addr_config: I::AddressConfig<BC::Instant>,
1611    _device_config: &I::Configuration,
1612) -> Result<CC::AddressId, ExistsError> {
1613    // LINT.IfChange(netstack_ip_added_tefmo)
1614    info!("adding addr {addr_sub:?} config {addr_config:?} to device {device_id:?}");
1615    // LINT.ThenChange(//tools/testing/tefmocheck/cdc_ethernet_state_check.go:netstack_ip_added_tefmo)
1616    let CommonAddressProperties { valid_until, preferred_lifetime } =
1617        I::get_common_props(&addr_config);
1618    let addr_id = core_ctx.add_ip_address(device_id, addr_sub, addr_config)?;
1619    assert_eq!(addr_id.addr().addr(), addr_sub.addr().get());
1620
1621    let ip_enabled =
1622        core_ctx.with_ip_device_flags(device_id, |IpDeviceFlags { ip_enabled }| *ip_enabled);
1623
1624    let needs_dad = if ip_enabled {
1625        DadHandler::initialize_duplicate_address_detection(
1626            core_ctx,
1627            bindings_ctx,
1628            device_id,
1629            &addr_id,
1630            |state| IpDeviceEvent::AddressAdded {
1631                device: device_id.clone(),
1632                addr: addr_sub.to_witness(),
1633                state,
1634                valid_until,
1635                preferred_lifetime,
1636            },
1637        )
1638    } else {
1639        // NB: We don't start DAD if the device is disabled. DAD will be
1640        // performed when the device is enabled for all addresses.
1641        //
1642        // Typically, the `AddressAdded` event would be dispatched by the DAD
1643        // engine, but because we're not calling into the DAD engine we need
1644        // to dispatch the event here.
1645        bindings_ctx.on_event(IpDeviceEvent::AddressAdded {
1646            device: device_id.clone(),
1647            addr: addr_sub.to_witness(),
1648            state: IpAddressState::Unavailable,
1649            valid_until,
1650            preferred_lifetime,
1651        });
1652        NeedsDad::No
1653    };
1654
1655    match needs_dad {
1656        NeedsDad::Yes(token) => {
1657            core_ctx.start_duplicate_address_detection(bindings_ctx, token);
1658        }
1659        NeedsDad::No => {}
1660    }
1661
1662    Ok(addr_id)
1663}
1664
1665/// A handler to abstract side-effects of removing IP device addresses.
1666pub trait IpAddressRemovalHandler<I: IpDeviceIpExt, BC: InstantBindingsTypes>:
1667    DeviceIdContext<AnyDevice>
1668{
1669    /// Notifies the handler that the addr `addr` with `config` has been removed
1670    /// from `device_id` with `reason`.
1671    fn on_address_removed(
1672        &mut self,
1673        bindings_ctx: &mut BC,
1674        device_id: &Self::DeviceId,
1675        addr_sub: AddrSubnet<I::Addr, I::AssignedWitness>,
1676        config: I::AddressConfig<BC::Instant>,
1677        reason: AddressRemovedReason,
1678    );
1679}
1680
1681/// There's no special action to be taken for removed IPv4 addresses.
1682impl<CC: DeviceIdContext<AnyDevice>, BC: InstantBindingsTypes> IpAddressRemovalHandler<Ipv4, BC>
1683    for CC
1684{
1685    fn on_address_removed(
1686        &mut self,
1687        _bindings_ctx: &mut BC,
1688        _device_id: &Self::DeviceId,
1689        _addr_sub: AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>,
1690        _config: Ipv4AddrConfig<BC::Instant>,
1691        _reason: AddressRemovedReason,
1692    ) {
1693        // Nothing to do.
1694    }
1695}
1696
1697/// Provide the IPv6 implementation for all [`SlaacHandler`] implementations.
1698impl<CC: SlaacHandler<BC>, BC: InstantContext> IpAddressRemovalHandler<Ipv6, BC> for CC {
1699    fn on_address_removed(
1700        &mut self,
1701        bindings_ctx: &mut BC,
1702        device_id: &Self::DeviceId,
1703        addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
1704        config: Ipv6AddrConfig<BC::Instant>,
1705        reason: AddressRemovedReason,
1706    ) {
1707        match config {
1708            Ipv6AddrConfig::Slaac(config) => SlaacHandler::on_address_removed(
1709                self,
1710                bindings_ctx,
1711                device_id,
1712                addr_sub,
1713                config,
1714                reason,
1715            ),
1716            Ipv6AddrConfig::Manual(_manual_config) => (),
1717        }
1718    }
1719}
1720
1721/// Possible representations of an IP address that is valid for deletion.
1722#[allow(missing_docs)]
1723pub enum DelIpAddr<Id, A> {
1724    SpecifiedAddr(SpecifiedAddr<A>),
1725    AddressId(Id),
1726}
1727
1728impl<Id: IpAddressId<A>, A: IpAddress<Version: AssignedAddrIpExt>> Display for DelIpAddr<Id, A> {
1729    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1730        match self {
1731            DelIpAddr::SpecifiedAddr(addr) => write!(f, "{}", *addr),
1732            DelIpAddr::AddressId(id) => write!(f, "{}", id.addr()),
1733        }
1734    }
1735}
1736
1737/// Deletes an IP address from a device, returning the address and its
1738/// configuration if it was removed.
1739pub fn del_ip_addr_inner<
1740    I: IpDeviceIpExt,
1741    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1742    CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC> + DadHandler<I, BC>,
1743>(
1744    core_ctx: &mut CC,
1745    bindings_ctx: &mut BC,
1746    device_id: &CC::DeviceId,
1747    addr: DelIpAddr<CC::AddressId, I::Addr>,
1748    reason: AddressRemovedReason,
1749    // Require configuration lock to do this.
1750    //
1751    // This ensures a fixed device configuration, but also synchronization with
1752    // address addition and removal.
1753    //
1754    // See `IpDeviceAddAddressContext` for further synchronization guarantees.
1755    _config: &I::Configuration,
1756) -> Result<
1757    (
1758        AddrSubnet<I::Addr, I::AssignedWitness>,
1759        I::AddressConfig<BC::Instant>,
1760        RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>,
1761    ),
1762    NotFoundError,
1763> {
1764    let addr_id = match addr {
1765        DelIpAddr::SpecifiedAddr(addr) => core_ctx.get_address_id(device_id, addr)?,
1766        DelIpAddr::AddressId(id) => id,
1767    };
1768    DadHandler::stop_duplicate_address_detection(core_ctx, bindings_ctx, device_id, &addr_id);
1769    // Extract the configuration out of the address to properly mark it as ready
1770    // for deletion. If the configuration has already been taken, consider as if
1771    // the address is already removed.
1772    let addr_config = core_ctx
1773        .with_ip_address_data_mut(device_id, &addr_id, |addr_data| addr_data.config.take())
1774        .ok_or(NotFoundError)?;
1775
1776    let addr_sub = addr_id.addr_sub();
1777    let result = core_ctx.remove_ip_address(device_id, addr_id);
1778
1779    bindings_ctx.on_event(IpDeviceEvent::AddressRemoved {
1780        device: device_id.clone(),
1781        addr: addr_sub.addr().into(),
1782        reason,
1783    });
1784
1785    Ok((addr_sub, addr_config, result))
1786}
1787
1788/// Removes an IP address and associated subnet from this device.
1789fn del_ip_addr<
1790    I: IpDeviceIpExt,
1791    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1792    CC: IpDeviceConfigurationContext<I, BC>,
1793>(
1794    core_ctx: &mut CC,
1795    bindings_ctx: &mut BC,
1796    device_id: &CC::DeviceId,
1797    addr: DelIpAddr<CC::AddressId, I::Addr>,
1798    reason: AddressRemovedReason,
1799) -> Result<RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>, NotFoundError> {
1800    info!("removing addr {addr} from device {device_id:?}");
1801    core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
1802        del_ip_addr_inner_and_notify_handler(
1803            &mut core_ctx,
1804            bindings_ctx,
1805            device_id,
1806            addr,
1807            reason,
1808            config,
1809        )
1810    })
1811}
1812
1813/// Removes an IP address and associated subnet from this device and notifies
1814/// the address removal handler.
1815fn del_ip_addr_inner_and_notify_handler<
1816    I: IpDeviceIpExt,
1817    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1818    CC: IpDeviceStateContext<I, BC>
1819        + GmpHandler<I, BC>
1820        + DadHandler<I, BC>
1821        + IpAddressRemovalHandler<I, BC>,
1822>(
1823    core_ctx: &mut CC,
1824    bindings_ctx: &mut BC,
1825    device_id: &CC::DeviceId,
1826    addr: DelIpAddr<CC::AddressId, I::Addr>,
1827    reason: AddressRemovedReason,
1828    config: &I::Configuration,
1829) -> Result<RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>, NotFoundError> {
1830    del_ip_addr_inner(core_ctx, bindings_ctx, device_id, addr, reason, config).map(
1831        |(addr_sub, config, result)| {
1832            core_ctx.on_address_removed(bindings_ctx, device_id, addr_sub, config, reason);
1833            result
1834        },
1835    )
1836}
1837
1838/// Returns whether `device_id` is enabled for IP version `I`.
1839pub fn is_ip_device_enabled<
1840    I: IpDeviceIpExt,
1841    BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1842    CC: IpDeviceStateContext<I, BC>,
1843>(
1844    core_ctx: &mut CC,
1845    device_id: &CC::DeviceId,
1846) -> bool {
1847    core_ctx.with_ip_device_flags(device_id, |flags| flags.ip_enabled)
1848}
1849
1850/// Removes IPv4 state for the device without emitting events.
1851pub fn clear_ipv4_device_state<
1852    BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
1853    CC: IpDeviceConfigurationContext<Ipv4, BC>,
1854>(
1855    core_ctx: &mut CC,
1856    bindings_ctx: &mut BC,
1857    device_id: &CC::DeviceId,
1858) {
1859    core_ctx.with_ip_device_configuration_mut(device_id, |mut core_ctx| {
1860        let ip_enabled = core_ctx.with_configuration_and_flags_mut(device_id, |_config, flags| {
1861            // Start by force-disabling IPv4 so we're sure we won't handle
1862            // any more packets.
1863            let IpDeviceFlags { ip_enabled } = flags;
1864            core::mem::replace(ip_enabled, false)
1865        });
1866
1867        let (config, mut core_ctx) = core_ctx.ip_device_configuration_and_ctx();
1868        let core_ctx = &mut core_ctx;
1869        if ip_enabled {
1870            disable_ipv4_device_with_config(core_ctx, bindings_ctx, device_id, config);
1871        }
1872    })
1873}
1874
1875/// Removes IPv6 state for the device without emitting events.
1876pub fn clear_ipv6_device_state<
1877    BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1878    CC: Ipv6DeviceConfigurationContext<BC>,
1879>(
1880    core_ctx: &mut CC,
1881    bindings_ctx: &mut BC,
1882    device_id: &CC::DeviceId,
1883) {
1884    core_ctx.with_ipv6_device_configuration_mut(device_id, |mut core_ctx| {
1885        let ip_enabled = core_ctx.with_configuration_and_flags_mut(device_id, |_config, flags| {
1886            // Start by force-disabling IPv6 so we're sure we won't handle
1887            // any more packets.
1888            let IpDeviceFlags { ip_enabled } = flags;
1889            core::mem::replace(ip_enabled, false)
1890        });
1891
1892        let (config, mut core_ctx) = core_ctx.ipv6_device_configuration_and_ctx();
1893        let core_ctx = &mut core_ctx;
1894        if ip_enabled {
1895            disable_ipv6_device_with_config(core_ctx, bindings_ctx, device_id, config);
1896        }
1897    })
1898}
1899
1900/// Dispatches a received ARP packet (Request or Reply) to the IP layer.
1901///
1902/// Returns whether the `target_addr` is assigned on the device.
1903pub fn on_arp_packet<CC, BC>(
1904    core_ctx: &mut CC,
1905    bindings_ctx: &mut BC,
1906    device_id: &CC::DeviceId,
1907    frame_src: impl Debug,
1908    sender_hwaddr: impl Debug,
1909    sender_addr: Ipv4Addr,
1910    target_addr: Ipv4Addr,
1911    is_arp_probe: bool,
1912) -> bool
1913where
1914    CC: IpDeviceHandler<Ipv4, BC> + IpDeviceStateContext<Ipv4, BC>,
1915    BC: IpDeviceStateBindingsTypes,
1916{
1917    // As Per RFC 5227, section 2.1.1
1918    //   If [...] the host receives any ARP packet (Request *or* Reply) on the
1919    //   interface where the probe is being performed, where the packet's
1920    //   'sender IP address' is the address being probed for, then the host MUST
1921    //   treat this address as being in use by some other host.
1922    if let Some(sender_addr) = SpecifiedAddr::new(sender_addr) {
1923        let sender_addr_state = IpDeviceHandler::<Ipv4, _>::handle_received_dad_packet(
1924            core_ctx,
1925            bindings_ctx,
1926            &device_id,
1927            sender_addr,
1928            Ipv4DadAddressInfo::SourceAddr,
1929        );
1930        match sender_addr_state {
1931            None => {}
1932            // As Per RFC 5227 section 2.4:
1933            //   At any time, if a host receives an ARP packet (Request *or*
1934            //   Reply) where the 'sender IP address' is (one of) the host's own
1935            //   IP address(es) configured on that interface, but the 'sender
1936            //   hardware address' does not match any of the host's own
1937            //   interface addresses, then this is a conflicting ARP packet,
1938            //   indicating some other host also thinks it is validly using this
1939            //   address.
1940            Some(state) => {
1941                info!(
1942                    "DAD received conflicting ARP packet (sender) for {sender_addr} \
1943                    (state={state:?}). arp_sha={sender_hwaddr:?}, frame_src={frame_src:?}"
1944                );
1945            }
1946        }
1947    }
1948
1949    let Some(target_addr) = SpecifiedAddr::new(target_addr) else {
1950        return false;
1951    };
1952
1953    // As Per RFC 5227, section 2.1.1
1954    //  In addition, if during this period the host receives any ARP Probe
1955    //  where the packet's 'target IP address' is the address being probed
1956    //  for, [... ] then the host SHOULD similarly treat this as an address
1957    //  conflict.
1958    if is_arp_probe {
1959        let target_addr_state = IpDeviceHandler::<Ipv4, _>::handle_received_dad_packet(
1960            core_ctx,
1961            bindings_ctx,
1962            &device_id,
1963            target_addr,
1964            Ipv4DadAddressInfo::TargetAddr,
1965        );
1966        let assigned = match target_addr_state {
1967            None => false,
1968            // Unlike the sender_addr, it's not concerning to receive an
1969            // ARP packet whose target_addr is assigned to us.
1970            Some(IpAddressState::Assigned) => true,
1971            Some(state @ (IpAddressState::Tentative | IpAddressState::Unavailable)) => {
1972                info!(
1973                    "DAD received conflicting ARP packet (target) for {target_addr} \
1974                    (state={state:?}). arp_sha={sender_hwaddr:?}, frame_src={frame_src:?}"
1975                );
1976                false
1977            }
1978        };
1979        assigned
1980    } else {
1981        // Otherwise, don't dispatch the probe to DAD. Instead just return the
1982        // target address' assignment state.
1983        let addr_id = match core_ctx.get_address_id(device_id, target_addr) {
1984            Ok(o) => o,
1985            Err(NotFoundError) => return false,
1986        };
1987
1988        core_ctx.with_ip_address_data(
1989            device_id,
1990            &addr_id,
1991            |IpAddressData { flags: IpAddressFlags { assigned }, config: _ }| *assigned,
1992        )
1993    }
1994}
1995
1996#[cfg(any(test, feature = "testutils"))]
1997pub(crate) mod testutil {
1998    use alloc::boxed::Box;
1999
2000    use crate::device::IpAddressFlags;
2001
2002    use super::*;
2003
2004    /// Calls the callback with an iterator of the IPv4 addresses assigned to
2005    /// `device_id`.
2006    pub fn with_assigned_ipv4_addr_subnets<
2007        BT: IpDeviceStateBindingsTypes,
2008        CC: IpDeviceStateContext<Ipv4, BT>,
2009        O,
2010        F: FnOnce(Box<dyn Iterator<Item = AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>> + '_>) -> O,
2011    >(
2012        core_ctx: &mut CC,
2013        device_id: &CC::DeviceId,
2014        cb: F,
2015    ) -> O {
2016        core_ctx.with_address_ids(device_id, |addrs, _core_ctx| {
2017            cb(Box::new(addrs.map(|a| a.addr_sub())))
2018        })
2019    }
2020
2021    /// Gets the IPv6 address and subnet pairs associated with this device which are
2022    /// in the assigned state.
2023    ///
2024    /// Tentative IP addresses (addresses which are not yet fully bound to a device)
2025    /// and deprecated IP addresses (addresses which have been assigned but should
2026    /// no longer be used for new connections) will not be returned by
2027    /// `get_assigned_ipv6_addr_subnets`.
2028    ///
2029    /// Returns an [`Iterator`] of `AddrSubnet`.
2030    ///
2031    /// See [`Tentative`] and [`AddrSubnet`] for more information.
2032    pub fn with_assigned_ipv6_addr_subnets<
2033        BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
2034        CC: Ipv6DeviceContext<BC>,
2035        O,
2036        F: FnOnce(Box<dyn Iterator<Item = AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>> + '_>) -> O,
2037    >(
2038        core_ctx: &mut CC,
2039        device_id: &CC::DeviceId,
2040        cb: F,
2041    ) -> O {
2042        core_ctx.with_address_ids(device_id, |addrs, core_ctx| {
2043            cb(Box::new(addrs.filter_map(|addr_id| {
2044                core_ctx
2045                    .with_ip_address_data(
2046                        device_id,
2047                        &addr_id,
2048                        |IpAddressData { flags: IpAddressFlags { assigned }, config: _ }| *assigned,
2049                    )
2050                    .then(|| addr_id.addr_sub())
2051            })))
2052        })
2053    }
2054}