pub(crate) mod api;
pub(crate) mod config;
pub(crate) mod dad;
pub(crate) mod nud;
pub(crate) mod opaque_iid;
pub(crate) mod route_discovery;
pub(crate) mod router_solicitation;
pub(crate) mod slaac;
pub(crate) mod state;
use alloc::vec::Vec;
use core::fmt::{Debug, Display};
use core::hash::Hash;
use core::num::NonZeroU8;
use derivative::Derivative;
use log::info;
use net_types::ip::{
AddrSubnet, GenericOverIp, Ip, IpAddress, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr, Ipv6SourceAddr, Mtu,
Subnet,
};
use net_types::{MulticastAddr, SpecifiedAddr, UnicastAddr, Witness};
use netstack3_base::{
AnyDevice, AssignedAddrIpExt, CounterContext, DeferredResourceRemovalContext, DeviceIdContext,
EventContext, ExistsError, HandleableTimer, Inspectable, Instant, InstantBindingsTypes,
InstantContext, IpAddressId, IpDeviceAddr, IpDeviceAddressIdContext, IpExt, Ipv4DeviceAddr,
Ipv6DeviceAddr, NotFoundError, RemoveResourceResultWithContext, RngContext, SendFrameError,
StrongDeviceIdentifier, TimerBindingsTypes, TimerContext, TimerHandler, WeakDeviceIdentifier,
WeakIpAddressId,
};
use netstack3_filter::ProofOfEgressCheck;
use packet::{BufferMut, Serializer};
use packet_formats::icmp::mld::MldPacket;
use packet_formats::icmp::ndp::options::NdpNonce;
use packet_formats::icmp::ndp::NonZeroNdpLifetime;
use packet_formats::utils::NonZeroDuration;
use zerocopy::SplitByteSlice;
use crate::device::CommonAddressProperties;
use crate::internal::base::{
DeviceIpLayerMetadata, IpCounters, IpDeviceMtuContext, IpPacketDestination,
};
use crate::internal::device::config::{
IpDeviceConfigurationUpdate, Ipv4DeviceConfigurationUpdate, Ipv6DeviceConfigurationUpdate,
};
use crate::internal::device::dad::{DadAddressStateLookupResult, DadHandler, DadTimerId};
use crate::internal::device::nud::NudIpHandler;
use crate::internal::device::route_discovery::{
Ipv6DiscoveredRoute, Ipv6DiscoveredRouteTimerId, RouteDiscoveryHandler,
};
use crate::internal::device::router_solicitation::{RsHandler, RsTimerId};
use crate::internal::device::slaac::{SlaacHandler, SlaacTimerId};
use crate::internal::device::state::{
IpDeviceConfiguration, IpDeviceFlags, IpDeviceState, IpDeviceStateBindingsTypes,
IpDeviceStateIpExt, Ipv4AddrConfig, Ipv4AddressEntry, Ipv4AddressState,
Ipv4DeviceConfiguration, Ipv4DeviceState, Ipv6AddrConfig, Ipv6AddrManualConfig,
Ipv6AddrSlaacConfig, Ipv6AddressEntry, Ipv6AddressFlags, Ipv6AddressState,
Ipv6DeviceConfiguration, Ipv6DeviceState, Ipv6NetworkLearnedParameters, Lifetime,
PreferredLifetime, WeakAddressId,
};
use crate::internal::gmp::igmp::{IgmpPacketHandler, IgmpTimerId};
use crate::internal::gmp::mld::{MldPacketHandler, MldTimerId};
use crate::internal::gmp::{self, GmpHandler, GroupJoinResult, GroupLeaveResult};
use crate::internal::local_delivery::{IpHeaderInfo, LocalDeliveryPacketInfo};
#[derive(Derivative, GenericOverIp)]
#[derivative(
Clone(bound = ""),
Eq(bound = ""),
PartialEq(bound = ""),
Hash(bound = ""),
Debug(bound = "")
)]
#[generic_over_ip(I, Ip)]
pub struct IpDeviceTimerId<I: IpDeviceIpExt, D: WeakDeviceIdentifier, A: IpAddressIdSpec>(
I::Timer<D, A>,
);
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub struct Ipv4DeviceTimerId<D: WeakDeviceIdentifier>(IgmpTimerId<D>);
impl<D: WeakDeviceIdentifier> Ipv4DeviceTimerId<D> {
fn device_id(&self) -> Option<D::Strong> {
let Self(this) = self;
this.device_id().upgrade()
}
pub fn into_common<S: IpAddressIdSpec>(self) -> IpDeviceTimerId<Ipv4, D, S> {
self.into()
}
}
impl<D: WeakDeviceIdentifier, A: IpAddressIdSpec> From<IpDeviceTimerId<Ipv4, D, A>>
for Ipv4DeviceTimerId<D>
{
fn from(IpDeviceTimerId(inner): IpDeviceTimerId<Ipv4, D, A>) -> Self {
inner
}
}
impl<D: WeakDeviceIdentifier, A: IpAddressIdSpec> From<Ipv4DeviceTimerId<D>>
for IpDeviceTimerId<Ipv4, D, A>
{
fn from(value: Ipv4DeviceTimerId<D>) -> Self {
Self(value)
}
}
impl<D: WeakDeviceIdentifier> From<IgmpTimerId<D>> for Ipv4DeviceTimerId<D> {
fn from(id: IgmpTimerId<D>) -> Ipv4DeviceTimerId<D> {
Ipv4DeviceTimerId(id)
}
}
impl<D: WeakDeviceIdentifier, BC: TimerBindingsTypes, CC: TimerHandler<BC, IgmpTimerId<D>>>
HandleableTimer<CC, BC> for Ipv4DeviceTimerId<D>
{
fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, timer: BC::UniqueTimerId) {
let Self(id) = self;
core_ctx.handle_timer(bindings_ctx, id, timer);
}
}
impl<I, CC, BC, A> HandleableTimer<CC, BC> for IpDeviceTimerId<I, CC::WeakDeviceId, A>
where
I: IpDeviceIpExt,
BC: IpDeviceBindingsContext<I, CC::DeviceId>,
CC: IpDeviceConfigurationContext<I, BC>,
A: IpAddressIdSpec,
for<'a> CC::WithIpDeviceConfigurationInnerCtx<'a>:
TimerHandler<BC, I::Timer<CC::WeakDeviceId, A>>,
{
fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, timer: BC::UniqueTimerId) {
let Self(id) = self;
let Some(device_id) = I::timer_device_id(&id) else {
return;
};
core_ctx.with_ip_device_configuration(&device_id, |_state, mut core_ctx| {
TimerHandler::handle_timer(&mut core_ctx, bindings_ctx, id, timer)
})
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
#[allow(missing_docs)]
pub enum Ipv6DeviceTimerId<D: WeakDeviceIdentifier, A: WeakIpAddressId<Ipv6Addr>> {
Mld(MldTimerId<D>),
Dad(DadTimerId<D, A>),
Rs(RsTimerId<D>),
RouteDiscovery(Ipv6DiscoveredRouteTimerId<D>),
Slaac(SlaacTimerId<D>),
}
impl<D: WeakDeviceIdentifier, A: IpAddressIdSpec> From<IpDeviceTimerId<Ipv6, D, A>>
for Ipv6DeviceTimerId<D, A::WeakV6>
{
fn from(IpDeviceTimerId(inner): IpDeviceTimerId<Ipv6, D, A>) -> Self {
inner
}
}
impl<D: WeakDeviceIdentifier, A: IpAddressIdSpec> From<Ipv6DeviceTimerId<D, A::WeakV6>>
for IpDeviceTimerId<Ipv6, D, A>
{
fn from(value: Ipv6DeviceTimerId<D, A::WeakV6>) -> Self {
Self(value)
}
}
impl<D: WeakDeviceIdentifier, A: WeakIpAddressId<Ipv6Addr>> Ipv6DeviceTimerId<D, A> {
fn device_id(&self) -> Option<D::Strong> {
match self {
Self::Mld(id) => id.device_id(),
Self::Dad(id) => id.device_id(),
Self::Rs(id) => id.device_id(),
Self::RouteDiscovery(id) => id.device_id(),
Self::Slaac(id) => id.device_id(),
}
.upgrade()
}
pub fn into_common<S: IpAddressIdSpec<WeakV6 = A>>(self) -> IpDeviceTimerId<Ipv6, D, S> {
self.into()
}
}
impl<D: WeakDeviceIdentifier, A: WeakIpAddressId<Ipv6Addr>> From<MldTimerId<D>>
for Ipv6DeviceTimerId<D, A>
{
fn from(id: MldTimerId<D>) -> Ipv6DeviceTimerId<D, A> {
Ipv6DeviceTimerId::Mld(id)
}
}
impl<D: WeakDeviceIdentifier, A: WeakIpAddressId<Ipv6Addr>> From<DadTimerId<D, A>>
for Ipv6DeviceTimerId<D, A>
{
fn from(id: DadTimerId<D, A>) -> Ipv6DeviceTimerId<D, A> {
Ipv6DeviceTimerId::Dad(id)
}
}
impl<D: WeakDeviceIdentifier, A: WeakIpAddressId<Ipv6Addr>> From<RsTimerId<D>>
for Ipv6DeviceTimerId<D, A>
{
fn from(id: RsTimerId<D>) -> Ipv6DeviceTimerId<D, A> {
Ipv6DeviceTimerId::Rs(id)
}
}
impl<D: WeakDeviceIdentifier, A: WeakIpAddressId<Ipv6Addr>> From<Ipv6DiscoveredRouteTimerId<D>>
for Ipv6DeviceTimerId<D, A>
{
fn from(id: Ipv6DiscoveredRouteTimerId<D>) -> Ipv6DeviceTimerId<D, A> {
Ipv6DeviceTimerId::RouteDiscovery(id)
}
}
impl<D: WeakDeviceIdentifier, A: WeakIpAddressId<Ipv6Addr>> From<SlaacTimerId<D>>
for Ipv6DeviceTimerId<D, A>
{
fn from(id: SlaacTimerId<D>) -> Ipv6DeviceTimerId<D, A> {
Ipv6DeviceTimerId::Slaac(id)
}
}
impl<
D: WeakDeviceIdentifier,
A: WeakIpAddressId<Ipv6Addr>,
BC: TimerBindingsTypes,
CC: TimerHandler<BC, RsTimerId<D>>
+ TimerHandler<BC, Ipv6DiscoveredRouteTimerId<D>>
+ TimerHandler<BC, MldTimerId<D>>
+ TimerHandler<BC, SlaacTimerId<D>>
+ TimerHandler<BC, DadTimerId<D, A>>,
> HandleableTimer<CC, BC> for Ipv6DeviceTimerId<D, A>
{
fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, timer: BC::UniqueTimerId) {
match self {
Ipv6DeviceTimerId::Mld(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
Ipv6DeviceTimerId::Dad(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
Ipv6DeviceTimerId::Rs(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
Ipv6DeviceTimerId::RouteDiscovery(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
Ipv6DeviceTimerId::Slaac(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
}
}
}
pub trait IpDeviceIpExt: IpDeviceStateIpExt + AssignedAddrIpExt + gmp::IpExt {
type State<BT: IpDeviceStateBindingsTypes>: AsRef<IpDeviceState<Self, BT>>
+ AsMut<IpDeviceState<Self, BT>>;
type Configuration: AsRef<IpDeviceConfiguration>
+ AsMut<IpDeviceConfiguration>
+ Clone
+ Debug
+ Eq
+ PartialEq;
type Timer<D: WeakDeviceIdentifier, A: IpAddressIdSpec>: Into<IpDeviceTimerId<Self, D, A>>
+ From<IpDeviceTimerId<Self, D, A>>
+ Clone
+ Eq
+ PartialEq
+ Debug
+ Hash;
type AddressConfig<I: Instant>: Default + Debug;
type ManualAddressConfig<I: Instant>: Default + Debug + Into<Self::AddressConfig<I>>;
type AddressState<I: Instant>: 'static + Inspectable;
type ConfigurationUpdate: From<IpDeviceConfigurationUpdate>
+ AsRef<IpDeviceConfigurationUpdate>
+ Debug;
fn get_common_props<I: Instant>(config: &Self::AddressConfig<I>) -> CommonAddressProperties<I>;
fn is_addr_assigned<I: Instant>(addr_state: &Self::AddressState<I>) -> bool;
fn timer_device_id<D: WeakDeviceIdentifier, A: IpAddressIdSpec>(
timer: &Self::Timer<D, A>,
) -> Option<D::Strong>;
fn take_addr_config_for_removal<I: Instant>(
addr_state: &mut Self::AddressState<I>,
) -> Option<Self::AddressConfig<I>>;
}
impl IpDeviceIpExt for Ipv4 {
type State<BT: IpDeviceStateBindingsTypes> = Ipv4DeviceState<BT>;
type Configuration = Ipv4DeviceConfiguration;
type Timer<D: WeakDeviceIdentifier, A: IpAddressIdSpec> = Ipv4DeviceTimerId<D>;
type AddressConfig<I: Instant> = Ipv4AddrConfig<I>;
type ManualAddressConfig<I: Instant> = Ipv4AddrConfig<I>;
type AddressState<I: Instant> = Ipv4AddressState<I>;
type ConfigurationUpdate = Ipv4DeviceConfigurationUpdate;
fn get_common_props<I: Instant>(config: &Self::AddressConfig<I>) -> CommonAddressProperties<I> {
config.common
}
fn is_addr_assigned<I: Instant>(addr_state: &Ipv4AddressState<I>) -> bool {
let Ipv4AddressState { config: _ } = addr_state;
true
}
fn timer_device_id<D: WeakDeviceIdentifier, A: IpAddressIdSpec>(
timer: &Self::Timer<D, A>,
) -> Option<D::Strong> {
timer.device_id()
}
fn take_addr_config_for_removal<I: Instant>(
addr_state: &mut Self::AddressState<I>,
) -> Option<Self::AddressConfig<I>> {
addr_state.config.take()
}
}
impl IpDeviceIpExt for Ipv6 {
type State<BT: IpDeviceStateBindingsTypes> = Ipv6DeviceState<BT>;
type Configuration = Ipv6DeviceConfiguration;
type Timer<D: WeakDeviceIdentifier, A: IpAddressIdSpec> = Ipv6DeviceTimerId<D, A::WeakV6>;
type AddressConfig<I: Instant> = Ipv6AddrConfig<I>;
type ManualAddressConfig<I: Instant> = Ipv6AddrManualConfig<I>;
type AddressState<I: Instant> = Ipv6AddressState<I>;
type ConfigurationUpdate = Ipv6DeviceConfigurationUpdate;
fn get_common_props<I: Instant>(config: &Self::AddressConfig<I>) -> CommonAddressProperties<I> {
CommonAddressProperties {
valid_until: config.valid_until(),
preferred_lifetime: config.preferred_lifetime(),
}
}
fn is_addr_assigned<I: Instant>(addr_state: &Ipv6AddressState<I>) -> bool {
addr_state.flags.assigned
}
fn timer_device_id<D: WeakDeviceIdentifier, A: IpAddressIdSpec>(
timer: &Self::Timer<D, A>,
) -> Option<D::Strong> {
timer.device_id()
}
fn take_addr_config_for_removal<I: Instant>(
addr_state: &mut Self::AddressState<I>,
) -> Option<Self::AddressConfig<I>> {
addr_state.config.take()
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum IpAddressState {
Unavailable,
Assigned,
Tentative,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum AddressRemovedReason {
Manual,
DadFailed,
}
#[derive(Debug, Eq, Hash, PartialEq, GenericOverIp)]
#[generic_over_ip(I, Ip)]
pub enum IpDeviceEvent<DeviceId, I: Ip, Instant> {
AddressAdded {
device: DeviceId,
addr: AddrSubnet<I::Addr>,
state: IpAddressState,
valid_until: Lifetime<Instant>,
preferred_lifetime: PreferredLifetime<Instant>,
},
AddressRemoved {
device: DeviceId,
addr: SpecifiedAddr<I::Addr>,
reason: AddressRemovedReason,
},
AddressStateChanged {
device: DeviceId,
addr: SpecifiedAddr<I::Addr>,
state: IpAddressState,
},
AddressPropertiesChanged {
device: DeviceId,
addr: SpecifiedAddr<I::Addr>,
valid_until: Lifetime<Instant>,
preferred_lifetime: PreferredLifetime<Instant>,
},
EnabledChanged {
device: DeviceId,
ip_enabled: bool,
},
}
impl<DeviceId, I: Ip, Instant> IpDeviceEvent<DeviceId, I, Instant> {
pub fn map_device<N, F: FnOnce(DeviceId) -> N>(self, map: F) -> IpDeviceEvent<N, I, Instant> {
match self {
IpDeviceEvent::AddressAdded {
device,
addr,
state,
valid_until,
preferred_lifetime,
} => IpDeviceEvent::AddressAdded {
device: map(device),
addr,
state,
valid_until,
preferred_lifetime,
},
IpDeviceEvent::AddressRemoved { device, addr, reason } => {
IpDeviceEvent::AddressRemoved { device: map(device), addr, reason }
}
IpDeviceEvent::AddressStateChanged { device, addr, state } => {
IpDeviceEvent::AddressStateChanged { device: map(device), addr, state }
}
IpDeviceEvent::EnabledChanged { device, ip_enabled } => {
IpDeviceEvent::EnabledChanged { device: map(device), ip_enabled }
}
IpDeviceEvent::AddressPropertiesChanged {
device,
addr,
valid_until,
preferred_lifetime,
} => IpDeviceEvent::AddressPropertiesChanged {
device: map(device),
addr,
valid_until,
preferred_lifetime,
},
}
}
}
pub trait IpDeviceBindingsContext<I: IpDeviceIpExt, D: StrongDeviceIdentifier>:
IpDeviceStateBindingsTypes
+ DeferredResourceRemovalContext
+ TimerContext
+ RngContext
+ EventContext<IpDeviceEvent<D, I, <Self as InstantBindingsTypes>::Instant>>
{
}
impl<
D: StrongDeviceIdentifier,
I: IpDeviceIpExt,
BC: IpDeviceStateBindingsTypes
+ DeferredResourceRemovalContext
+ TimerContext
+ RngContext
+ EventContext<IpDeviceEvent<D, I, <Self as InstantBindingsTypes>::Instant>>,
> IpDeviceBindingsContext<I, D> for BC
{
}
pub trait IpAddressIdSpec {
type WeakV4: WeakIpAddressId<Ipv4Addr>;
type WeakV6: WeakIpAddressId<Ipv6Addr>;
}
pub trait IpAddressIdExt: Ip {
type Weak<BT: IpDeviceStateBindingsTypes>: WeakIpAddressId<Self::Addr>;
}
impl IpAddressIdExt for Ipv4 {
type Weak<BT: IpDeviceStateBindingsTypes> = WeakAddressId<Ipv4AddressEntry<BT>>;
}
impl IpAddressIdExt for Ipv6 {
type Weak<BT: IpDeviceStateBindingsTypes> = WeakAddressId<Ipv6AddressEntry<BT>>;
}
pub trait IpAddressIdSpecContext:
IpDeviceAddressIdContext<Ipv4> + IpDeviceAddressIdContext<Ipv6>
{
type AddressIdSpec: IpAddressIdSpec<
WeakV4 = <Self as IpDeviceAddressIdContext<Ipv4>>::WeakAddressId,
WeakV6 = <Self as IpDeviceAddressIdContext<Ipv6>>::WeakAddressId,
>;
}
pub trait IpDeviceAddressContext<I: IpDeviceIpExt, BT: InstantBindingsTypes>:
IpDeviceAddressIdContext<I>
{
fn with_ip_address_state<O, F: FnOnce(&I::AddressState<BT::Instant>) -> O>(
&mut self,
device_id: &Self::DeviceId,
addr_id: &Self::AddressId,
cb: F,
) -> O;
fn with_ip_address_state_mut<O, F: FnOnce(&mut I::AddressState<BT::Instant>) -> O>(
&mut self,
device_id: &Self::DeviceId,
addr_id: &Self::AddressId,
cb: F,
) -> O;
}
pub trait IpDeviceStateContext<I: IpDeviceIpExt, BT: IpDeviceStateBindingsTypes>:
IpDeviceAddressContext<I, BT>
{
type IpDeviceAddressCtx<'a>: IpDeviceAddressContext<
I,
BT,
DeviceId = Self::DeviceId,
AddressId = Self::AddressId,
>;
fn with_ip_device_flags<O, F: FnOnce(&IpDeviceFlags) -> O>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O;
fn add_ip_address(
&mut self,
device_id: &Self::DeviceId,
addr: AddrSubnet<I::Addr, I::AssignedWitness>,
config: I::AddressConfig<BT::Instant>,
) -> Result<Self::AddressId, ExistsError>;
fn remove_ip_address(
&mut self,
device_id: &Self::DeviceId,
addr: Self::AddressId,
) -> RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BT>;
fn get_address_id(
&mut self,
device_id: &Self::DeviceId,
addr: SpecifiedAddr<I::Addr>,
) -> Result<Self::AddressId, NotFoundError>;
type AddressIdsIter<'a>: Iterator<Item = Self::AddressId> + 'a;
fn with_address_ids<
O,
F: FnOnce(Self::AddressIdsIter<'_>, &mut Self::IpDeviceAddressCtx<'_>) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O;
fn with_default_hop_limit<O, F: FnOnce(&NonZeroU8) -> O>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O;
fn with_default_hop_limit_mut<O, F: FnOnce(&mut NonZeroU8) -> O>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O;
fn join_link_multicast_group(
&mut self,
bindings_ctx: &mut BT,
device_id: &Self::DeviceId,
multicast_addr: MulticastAddr<I::Addr>,
);
fn leave_link_multicast_group(
&mut self,
bindings_ctx: &mut BT,
device_id: &Self::DeviceId,
multicast_addr: MulticastAddr<I::Addr>,
);
}
pub trait WithIpDeviceConfigurationMutInner<I: IpDeviceIpExt, BT: IpDeviceStateBindingsTypes>:
DeviceIdContext<AnyDevice>
{
type IpDeviceStateCtx<'s>: IpDeviceStateContext<I, BT, DeviceId = Self::DeviceId>
+ GmpHandler<I, BT>
+ NudIpHandler<I, BT>
+ 's
where
Self: 's;
fn ip_device_configuration_and_ctx(
&mut self,
) -> (&I::Configuration, Self::IpDeviceStateCtx<'_>);
fn with_configuration_and_flags_mut<
O,
F: FnOnce(&mut I::Configuration, &mut IpDeviceFlags) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O;
}
pub trait IpDeviceConfigurationContext<
I: IpDeviceIpExt,
BC: IpDeviceBindingsContext<I, Self::DeviceId>,
>: IpDeviceStateContext<I, BC> + IpDeviceMtuContext<I> + DeviceIdContext<AnyDevice>
{
type DevicesIter<'s>: Iterator<Item = Self::DeviceId> + 's;
type WithIpDeviceConfigurationInnerCtx<'s>: IpDeviceStateContext<I, BC, DeviceId = Self::DeviceId, AddressId = Self::AddressId>
+ GmpHandler<I, BC>
+ NudIpHandler<I, BC>
+ DadHandler<I, BC>
+ IpAddressRemovalHandler<I, BC>
+ IpDeviceMtuContext<I>
+ 's;
type WithIpDeviceConfigurationMutInner<'s>: WithIpDeviceConfigurationMutInner<I, BC, DeviceId = Self::DeviceId>
+ 's;
type DeviceAddressAndGroupsAccessor<'s>: IpDeviceStateContext<I, BC, DeviceId = Self::DeviceId>
+ 's;
fn with_ip_device_configuration<
O,
F: FnOnce(&I::Configuration, Self::WithIpDeviceConfigurationInnerCtx<'_>) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O;
fn with_ip_device_configuration_mut<
O,
F: FnOnce(Self::WithIpDeviceConfigurationMutInner<'_>) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O;
fn with_devices_and_state<
O,
F: FnOnce(Self::DevicesIter<'_>, Self::DeviceAddressAndGroupsAccessor<'_>) -> O,
>(
&mut self,
cb: F,
) -> O;
fn loopback_id(&mut self) -> Option<Self::DeviceId>;
}
pub trait WithIpv6DeviceConfigurationMutInner<BC: IpDeviceBindingsContext<Ipv6, Self::DeviceId>>:
WithIpDeviceConfigurationMutInner<Ipv6, BC>
{
type Ipv6DeviceStateCtx<'s>: Ipv6DeviceContext<BC, DeviceId = Self::DeviceId>
+ GmpHandler<Ipv6, BC>
+ NudIpHandler<Ipv6, BC>
+ DadHandler<Ipv6, BC>
+ RsHandler<BC>
+ SlaacHandler<BC>
+ RouteDiscoveryHandler<BC>
+ 's
where
Self: 's;
fn ipv6_device_configuration_and_ctx(
&mut self,
) -> (&Ipv6DeviceConfiguration, Self::Ipv6DeviceStateCtx<'_>);
}
pub trait Ipv6DeviceConfigurationContext<BC: IpDeviceBindingsContext<Ipv6, Self::DeviceId>>:
IpDeviceConfigurationContext<Ipv6, BC>
{
type Ipv6DeviceStateCtx<'s>: Ipv6DeviceContext<BC, DeviceId = Self::DeviceId, AddressId = Self::AddressId>
+ GmpHandler<Ipv6, BC>
+ MldPacketHandler<BC, Self::DeviceId>
+ NudIpHandler<Ipv6, BC>
+ DadHandler<Ipv6, BC>
+ RsHandler<BC>
+ SlaacHandler<BC>
+ RouteDiscoveryHandler<BC>
+ 's;
type WithIpv6DeviceConfigurationMutInner<'s>: WithIpv6DeviceConfigurationMutInner<BC, DeviceId = Self::DeviceId>
+ 's;
fn with_ipv6_device_configuration<
O,
F: FnOnce(&Ipv6DeviceConfiguration, Self::Ipv6DeviceStateCtx<'_>) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O;
fn with_ipv6_device_configuration_mut<
O,
F: FnOnce(Self::WithIpv6DeviceConfigurationMutInner<'_>) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O;
}
pub trait Ipv6DeviceContext<BC: IpDeviceBindingsContext<Ipv6, Self::DeviceId>>:
IpDeviceStateContext<Ipv6, BC>
{
type LinkLayerAddr: AsRef<[u8]>;
fn get_link_layer_addr_bytes(
&mut self,
device_id: &Self::DeviceId,
) -> Option<Self::LinkLayerAddr>;
fn get_eui64_iid(&mut self, device_id: &Self::DeviceId) -> Option<[u8; 8]>;
fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu);
fn with_network_learned_parameters<O, F: FnOnce(&Ipv6NetworkLearnedParameters) -> O>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O;
fn with_network_learned_parameters_mut<O, F: FnOnce(&mut Ipv6NetworkLearnedParameters) -> O>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O;
}
pub trait IpDeviceHandler<I: Ip, BC>: DeviceIdContext<AnyDevice> {
fn is_router_device(&mut self, device_id: &Self::DeviceId) -> bool;
fn set_default_hop_limit(&mut self, device_id: &Self::DeviceId, hop_limit: NonZeroU8);
}
impl<
I: IpDeviceIpExt,
BC: IpDeviceBindingsContext<I, CC::DeviceId>,
CC: IpDeviceConfigurationContext<I, BC>,
> IpDeviceHandler<I, BC> for CC
{
fn is_router_device(&mut self, device_id: &Self::DeviceId) -> bool {
is_ip_unicast_forwarding_enabled(self, device_id)
}
fn set_default_hop_limit(&mut self, device_id: &Self::DeviceId, hop_limit: NonZeroU8) {
self.with_default_hop_limit_mut(device_id, |default_hop_limit| {
*default_hop_limit = hop_limit
})
}
}
pub fn receive_igmp_packet<CC, BC, B, H>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device: &CC::DeviceId,
src_ip: Ipv4Addr,
dst_ip: SpecifiedAddr<Ipv4Addr>,
buffer: B,
info: &LocalDeliveryPacketInfo<Ipv4, H>,
) where
CC: IpDeviceConfigurationContext<Ipv4, BC>,
BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
for<'a> CC::WithIpDeviceConfigurationInnerCtx<'a>: IpDeviceStateContext<Ipv4, BC, DeviceId = CC::DeviceId>
+ IgmpPacketHandler<BC, CC::DeviceId>,
B: BufferMut,
H: IpHeaderInfo<Ipv4>,
{
core_ctx.with_ip_device_configuration(device, |_config, mut core_ctx| {
IgmpPacketHandler::receive_igmp_packet(
&mut core_ctx,
bindings_ctx,
device,
src_ip,
dst_ip,
buffer,
info,
)
})
}
pub trait Ipv6DeviceHandler<BC>: IpDeviceHandler<Ipv6, BC> {
type LinkLayerAddr: AsRef<[u8]>;
fn get_link_layer_addr_bytes(
&mut self,
device_id: &Self::DeviceId,
) -> Option<Self::LinkLayerAddr>;
fn set_discovered_retrans_timer(
&mut self,
bindings_ctx: &mut BC,
device_id: &Self::DeviceId,
retrans_timer: NonZeroDuration,
);
fn handle_received_dad_neighbor_solicitation(
&mut self,
bindings_ctx: &mut BC,
device_id: &Self::DeviceId,
addr: UnicastAddr<Ipv6Addr>,
nonce: Option<NdpNonce<&'_ [u8]>>,
) -> IpAddressState;
fn handle_received_neighbor_advertisement(
&mut self,
bindings_ctx: &mut BC,
device_id: &Self::DeviceId,
addr: UnicastAddr<Ipv6Addr>,
) -> IpAddressState;
fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu);
fn update_discovered_ipv6_route(
&mut self,
bindings_ctx: &mut BC,
device_id: &Self::DeviceId,
route: Ipv6DiscoveredRoute,
lifetime: Option<NonZeroNdpLifetime>,
);
fn apply_slaac_update(
&mut self,
bindings_ctx: &mut BC,
device_id: &Self::DeviceId,
prefix: Subnet<Ipv6Addr>,
preferred_lifetime: Option<NonZeroNdpLifetime>,
valid_lifetime: Option<NonZeroNdpLifetime>,
);
fn receive_mld_packet<B: SplitByteSlice>(
&mut self,
bindings_ctx: &mut BC,
device: &Self::DeviceId,
src_ip: Ipv6SourceAddr,
dst_ip: SpecifiedAddr<Ipv6Addr>,
packet: MldPacket<B>,
);
}
impl<
BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
CC: Ipv6DeviceContext<BC>
+ Ipv6DeviceConfigurationContext<BC>
+ CounterContext<IpCounters<Ipv6>>,
> Ipv6DeviceHandler<BC> for CC
{
type LinkLayerAddr = CC::LinkLayerAddr;
fn get_link_layer_addr_bytes(
&mut self,
device_id: &Self::DeviceId,
) -> Option<CC::LinkLayerAddr> {
Ipv6DeviceContext::get_link_layer_addr_bytes(self, device_id)
}
fn set_discovered_retrans_timer(
&mut self,
_bindings_ctx: &mut BC,
device_id: &Self::DeviceId,
retrans_timer: NonZeroDuration,
) {
self.with_network_learned_parameters_mut(device_id, |state| {
state.retrans_timer = Some(retrans_timer)
})
}
fn handle_received_dad_neighbor_solicitation(
&mut self,
bindings_ctx: &mut BC,
device_id: &Self::DeviceId,
addr: UnicastAddr<Ipv6Addr>,
nonce: Option<NdpNonce<&'_ [u8]>>,
) -> IpAddressState {
let addr_id = match self.get_address_id(device_id, addr.into_specified()) {
Ok(o) => o,
Err(NotFoundError) => return IpAddressState::Unavailable,
};
match self.with_ipv6_device_configuration(device_id, |_config, mut core_ctx| {
core_ctx.handle_incoming_dad_neighbor_solicitation(
bindings_ctx,
device_id,
&addr_id,
nonce,
)
}) {
DadAddressStateLookupResult::Assigned => IpAddressState::Assigned,
DadAddressStateLookupResult::Tentative { matched_nonce: true } => {
self.increment(|counters| &counters.version_rx.drop_looped_back_dad_probe);
IpAddressState::Tentative
}
DadAddressStateLookupResult::Uninitialized
| DadAddressStateLookupResult::Tentative { matched_nonce: false } => {
match del_ip_addr(
self,
bindings_ctx,
device_id,
DelIpAddr::AddressId(addr_id),
AddressRemovedReason::DadFailed,
) {
Ok(result) => {
bindings_ctx.defer_removal_result(result);
IpAddressState::Tentative
}
Err(NotFoundError) => {
IpAddressState::Unavailable
}
}
}
}
}
fn handle_received_neighbor_advertisement(
&mut self,
bindings_ctx: &mut BC,
device_id: &Self::DeviceId,
addr: UnicastAddr<Ipv6Addr>,
) -> IpAddressState {
let addr_id = match self.get_address_id(device_id, addr.into_specified()) {
Ok(o) => o,
Err(NotFoundError) => return IpAddressState::Unavailable,
};
let assigned = self.with_ip_address_state(
device_id,
&addr_id,
|Ipv6AddressState { flags: Ipv6AddressFlags { assigned }, config: _ }| *assigned,
);
if assigned {
IpAddressState::Assigned
} else {
match del_ip_addr(
self,
bindings_ctx,
device_id,
DelIpAddr::AddressId(addr_id),
AddressRemovedReason::DadFailed,
) {
Ok(result) => {
bindings_ctx.defer_removal_result(result);
IpAddressState::Tentative
}
Err(NotFoundError) => {
IpAddressState::Unavailable
}
}
}
}
fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu) {
Ipv6DeviceContext::set_link_mtu(self, device_id, mtu)
}
fn update_discovered_ipv6_route(
&mut self,
bindings_ctx: &mut BC,
device_id: &Self::DeviceId,
route: Ipv6DiscoveredRoute,
lifetime: Option<NonZeroNdpLifetime>,
) {
self.with_ipv6_device_configuration(device_id, |_config, mut core_ctx| {
RouteDiscoveryHandler::update_route(
&mut core_ctx,
bindings_ctx,
device_id,
route,
lifetime,
)
})
}
fn apply_slaac_update(
&mut self,
bindings_ctx: &mut BC,
device_id: &Self::DeviceId,
prefix: Subnet<Ipv6Addr>,
preferred_lifetime: Option<NonZeroNdpLifetime>,
valid_lifetime: Option<NonZeroNdpLifetime>,
) {
self.with_ipv6_device_configuration(device_id, |_config, mut core_ctx| {
SlaacHandler::apply_slaac_update(
&mut core_ctx,
bindings_ctx,
device_id,
prefix,
preferred_lifetime,
valid_lifetime,
)
})
}
fn receive_mld_packet<B: SplitByteSlice>(
&mut self,
bindings_ctx: &mut BC,
device: &Self::DeviceId,
src_ip: Ipv6SourceAddr,
dst_ip: SpecifiedAddr<Ipv6Addr>,
packet: MldPacket<B>,
) {
self.with_ipv6_device_configuration(device, |_config, mut core_ctx| {
MldPacketHandler::receive_mld_packet(
&mut core_ctx,
bindings_ctx,
device,
src_ip,
dst_ip,
packet,
)
})
}
}
pub trait IpDeviceSendContext<I: IpExt, BC>: DeviceIdContext<AnyDevice> {
fn send_ip_frame<S>(
&mut self,
bindings_ctx: &mut BC,
device_id: &Self::DeviceId,
destination: IpPacketDestination<I, &Self::DeviceId>,
ip_layer_metadata: DeviceIpLayerMetadata,
body: S,
egress_proof: ProofOfEgressCheck,
) -> Result<(), SendFrameError<S>>
where
S: Serializer,
S::Buffer: BufferMut;
}
fn enable_ipv6_device_with_config<
BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
CC: Ipv6DeviceContext<BC> + GmpHandler<Ipv6, BC> + RsHandler<BC> + DadHandler<Ipv6, BC>,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
config: &Ipv6DeviceConfiguration,
) {
join_ip_multicast_with_config(
core_ctx,
bindings_ctx,
device_id,
Ipv6::ALL_NODES_LINK_LOCAL_MULTICAST_ADDRESS,
config,
);
GmpHandler::gmp_handle_maybe_enabled(core_ctx, bindings_ctx, device_id);
core_ctx
.with_address_ids(device_id, |addrs, _core_ctx| addrs.collect::<Vec<_>>())
.into_iter()
.for_each(|addr_id| {
bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
device: device_id.clone(),
addr: addr_id.addr().into(),
state: IpAddressState::Tentative,
});
DadHandler::start_duplicate_address_detection(
core_ctx,
bindings_ctx,
device_id,
&addr_id,
);
});
if config.slaac_config.enable_stable_addresses {
if let Some(iid) = core_ctx.get_eui64_iid(device_id) {
let link_local_addr_sub = {
let mut addr = [0; 16];
addr[0..2].copy_from_slice(&[0xfe, 0x80]);
addr[(Ipv6::UNICAST_INTERFACE_IDENTIFIER_BITS / 8) as usize..]
.copy_from_slice(&iid);
AddrSubnet::new(
Ipv6Addr::from(addr),
Ipv6Addr::BYTES * 8 - Ipv6::UNICAST_INTERFACE_IDENTIFIER_BITS,
)
.expect("valid link-local address")
};
match add_ip_addr_subnet_with_config(
core_ctx,
bindings_ctx,
device_id,
link_local_addr_sub,
Ipv6AddrConfig::SLAAC_LINK_LOCAL,
config,
) {
Ok(_) => {}
Err(ExistsError) => {
}
}
}
}
RsHandler::start_router_solicitation(core_ctx, bindings_ctx, device_id);
}
fn disable_ipv6_device_with_config<
BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
CC: Ipv6DeviceContext<BC>
+ GmpHandler<Ipv6, BC>
+ RsHandler<BC>
+ DadHandler<Ipv6, BC>
+ RouteDiscoveryHandler<BC>
+ SlaacHandler<BC>
+ NudIpHandler<Ipv6, BC>,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
device_config: &Ipv6DeviceConfiguration,
) {
NudIpHandler::flush_neighbor_table(core_ctx, bindings_ctx, device_id);
SlaacHandler::remove_all_slaac_addresses(core_ctx, bindings_ctx, device_id);
RouteDiscoveryHandler::invalidate_routes(core_ctx, bindings_ctx, device_id);
RsHandler::stop_router_solicitation(core_ctx, bindings_ctx, device_id);
core_ctx
.with_address_ids(device_id, |addrs, core_ctx| {
addrs
.map(|addr_id| {
core_ctx.with_ip_address_state(
device_id,
&addr_id,
|Ipv6AddressState { flags: _, config }| (addr_id.clone(), *config),
)
})
.collect::<Vec<_>>()
})
.into_iter()
.for_each(|(addr_id, config)| {
if config == Some(Ipv6AddrConfig::SLAAC_LINK_LOCAL) {
del_ip_addr_inner_and_notify_handler(
core_ctx,
bindings_ctx,
device_id,
DelIpAddr::AddressId(addr_id),
AddressRemovedReason::Manual,
device_config,
)
.map(|remove_result| {
bindings_ctx.defer_removal_result(remove_result);
})
.unwrap_or_else(|NotFoundError| {
})
} else {
DadHandler::stop_duplicate_address_detection(
core_ctx,
bindings_ctx,
device_id,
&addr_id,
);
bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
device: device_id.clone(),
addr: addr_id.addr().into(),
state: IpAddressState::Unavailable,
});
}
});
GmpHandler::gmp_handle_disabled(core_ctx, bindings_ctx, device_id);
leave_ip_multicast_with_config(
core_ctx,
bindings_ctx,
device_id,
Ipv6::ALL_NODES_LINK_LOCAL_MULTICAST_ADDRESS,
device_config,
);
}
fn enable_ipv4_device_with_config<
BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
CC: IpDeviceStateContext<Ipv4, BC> + GmpHandler<Ipv4, BC>,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
config: &Ipv4DeviceConfiguration,
) {
join_ip_multicast_with_config(
core_ctx,
bindings_ctx,
device_id,
Ipv4::ALL_SYSTEMS_MULTICAST_ADDRESS,
config,
);
GmpHandler::gmp_handle_maybe_enabled(core_ctx, bindings_ctx, device_id);
core_ctx.with_address_ids(device_id, |addrs, _core_ctx| {
addrs.for_each(|addr| {
bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
device: device_id.clone(),
addr: addr.addr().into(),
state: IpAddressState::Assigned,
});
})
})
}
fn disable_ipv4_device_with_config<
BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
CC: IpDeviceStateContext<Ipv4, BC> + GmpHandler<Ipv4, BC> + NudIpHandler<Ipv4, BC>,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
config: &Ipv4DeviceConfiguration,
) {
NudIpHandler::flush_neighbor_table(core_ctx, bindings_ctx, device_id);
GmpHandler::gmp_handle_disabled(core_ctx, bindings_ctx, device_id);
leave_ip_multicast_with_config(
core_ctx,
bindings_ctx,
device_id,
Ipv4::ALL_SYSTEMS_MULTICAST_ADDRESS,
config,
);
core_ctx.with_address_ids(device_id, |addrs, _core_ctx| {
addrs.for_each(|addr| {
bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
device: device_id.clone(),
addr: addr.addr().into(),
state: IpAddressState::Unavailable,
});
})
})
}
pub fn get_ipv4_addr_subnet<BT: IpDeviceStateBindingsTypes, CC: IpDeviceStateContext<Ipv4, BT>>(
core_ctx: &mut CC,
device_id: &CC::DeviceId,
) -> Option<AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>> {
core_ctx.with_address_ids(device_id, |mut addrs, _core_ctx| addrs.next().map(|a| a.addr_sub()))
}
pub fn get_ipv6_hop_limit<BT: IpDeviceStateBindingsTypes, CC: IpDeviceStateContext<Ipv6, BT>>(
core_ctx: &mut CC,
device: &CC::DeviceId,
) -> NonZeroU8 {
core_ctx.with_default_hop_limit(device, Clone::clone)
}
pub fn is_ip_unicast_forwarding_enabled<
I: IpDeviceIpExt,
BC: IpDeviceBindingsContext<I, CC::DeviceId>,
CC: IpDeviceConfigurationContext<I, BC>,
>(
core_ctx: &mut CC,
device_id: &CC::DeviceId,
) -> bool {
core_ctx.with_ip_device_configuration(device_id, |state, _ctx| {
AsRef::<IpDeviceConfiguration>::as_ref(state).unicast_forwarding_enabled
})
}
pub fn is_ip_multicast_forwarding_enabled<
I: IpDeviceIpExt,
BC: IpDeviceBindingsContext<I, CC::DeviceId>,
CC: IpDeviceConfigurationContext<I, BC>,
>(
core_ctx: &mut CC,
device_id: &CC::DeviceId,
) -> bool {
core_ctx.with_ip_device_configuration(device_id, |state, _ctx| {
AsRef::<IpDeviceConfiguration>::as_ref(state).multicast_forwarding_enabled
})
}
pub fn join_ip_multicast_with_config<
I: IpDeviceIpExt,
BC: IpDeviceBindingsContext<I, CC::DeviceId>,
CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC>,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
multicast_addr: MulticastAddr<I::Addr>,
_config: &I::Configuration,
) {
match core_ctx.gmp_join_group(bindings_ctx, device_id, multicast_addr) {
GroupJoinResult::Joined(()) => {
core_ctx.join_link_multicast_group(bindings_ctx, device_id, multicast_addr)
}
GroupJoinResult::AlreadyMember => {}
}
}
pub fn join_ip_multicast<
I: IpDeviceIpExt,
BC: IpDeviceBindingsContext<I, CC::DeviceId>,
CC: IpDeviceConfigurationContext<I, BC>,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
multicast_addr: MulticastAddr<I::Addr>,
) {
core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
join_ip_multicast_with_config(
&mut core_ctx,
bindings_ctx,
device_id,
multicast_addr,
config,
)
})
}
pub fn leave_ip_multicast_with_config<
I: IpDeviceIpExt,
BC: IpDeviceBindingsContext<I, CC::DeviceId>,
CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC>,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
multicast_addr: MulticastAddr<I::Addr>,
_config: &I::Configuration,
) {
match core_ctx.gmp_leave_group(bindings_ctx, device_id, multicast_addr) {
GroupLeaveResult::Left(()) => {
core_ctx.leave_link_multicast_group(bindings_ctx, device_id, multicast_addr)
}
GroupLeaveResult::StillMember => {}
GroupLeaveResult::NotMember => panic!(
"attempted to leave IP multicast group we were not a member of: {}",
multicast_addr,
),
}
}
pub fn leave_ip_multicast<
I: IpDeviceIpExt,
BC: IpDeviceBindingsContext<I, CC::DeviceId>,
CC: IpDeviceConfigurationContext<I, BC>,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
multicast_addr: MulticastAddr<I::Addr>,
) {
core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
leave_ip_multicast_with_config(
&mut core_ctx,
bindings_ctx,
device_id,
multicast_addr,
config,
)
})
}
pub fn add_ip_addr_subnet_with_config<
I: IpDeviceIpExt,
BC: IpDeviceBindingsContext<I, CC::DeviceId>,
CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC> + DadHandler<I, BC>,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
addr_sub: AddrSubnet<I::Addr, I::AssignedWitness>,
addr_config: I::AddressConfig<BC::Instant>,
_device_config: &I::Configuration,
) -> Result<CC::AddressId, ExistsError> {
info!("adding addr {addr_sub:?} config {addr_config:?} to device {device_id:?}");
let CommonAddressProperties { valid_until, preferred_lifetime } =
I::get_common_props(&addr_config);
let addr_id = core_ctx.add_ip_address(device_id, addr_sub, addr_config)?;
assert_eq!(addr_id.addr().addr(), addr_sub.addr().get());
let ip_enabled =
core_ctx.with_ip_device_flags(device_id, |IpDeviceFlags { ip_enabled }| *ip_enabled);
let state = match ip_enabled {
true => CC::INITIAL_ADDRESS_STATE,
false => IpAddressState::Unavailable,
};
bindings_ctx.on_event(IpDeviceEvent::AddressAdded {
device: device_id.clone(),
addr: addr_sub.to_witness(),
state,
valid_until,
preferred_lifetime,
});
if ip_enabled {
DadHandler::start_duplicate_address_detection(core_ctx, bindings_ctx, device_id, &addr_id)
}
Ok(addr_id)
}
pub trait IpAddressRemovalHandler<I: IpDeviceIpExt, BC: InstantBindingsTypes>:
DeviceIdContext<AnyDevice>
{
fn on_address_removed(
&mut self,
bindings_ctx: &mut BC,
device_id: &Self::DeviceId,
addr_sub: AddrSubnet<I::Addr, I::AssignedWitness>,
config: I::AddressConfig<BC::Instant>,
reason: AddressRemovedReason,
);
}
impl<CC: DeviceIdContext<AnyDevice>, BC: InstantBindingsTypes> IpAddressRemovalHandler<Ipv4, BC>
for CC
{
fn on_address_removed(
&mut self,
_bindings_ctx: &mut BC,
_device_id: &Self::DeviceId,
_addr_sub: AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>,
_config: Ipv4AddrConfig<BC::Instant>,
_reason: AddressRemovedReason,
) {
}
}
impl<CC: SlaacHandler<BC>, BC: InstantContext> IpAddressRemovalHandler<Ipv6, BC> for CC {
fn on_address_removed(
&mut self,
bindings_ctx: &mut BC,
device_id: &Self::DeviceId,
addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
config: Ipv6AddrConfig<BC::Instant>,
reason: AddressRemovedReason,
) {
match config {
Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig { inner, preferred_lifetime: _ }) => {
SlaacHandler::on_address_removed(
self,
bindings_ctx,
device_id,
addr_sub,
inner,
reason,
)
}
Ipv6AddrConfig::Manual(_manual_config) => (),
}
}
}
#[allow(missing_docs)]
pub enum DelIpAddr<Id, A> {
SpecifiedAddr(SpecifiedAddr<A>),
AddressId(Id),
}
impl<Id: IpAddressId<A>, A: IpAddress<Version: AssignedAddrIpExt>> Display for DelIpAddr<Id, A> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
DelIpAddr::SpecifiedAddr(addr) => write!(f, "{}", *addr),
DelIpAddr::AddressId(id) => write!(f, "{}", id.addr()),
}
}
}
pub fn del_ip_addr_inner<
I: IpDeviceIpExt,
BC: IpDeviceBindingsContext<I, CC::DeviceId>,
CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC> + DadHandler<I, BC>,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
addr: DelIpAddr<CC::AddressId, I::Addr>,
reason: AddressRemovedReason,
_config: &I::Configuration,
) -> Result<
(
AddrSubnet<I::Addr, I::AssignedWitness>,
I::AddressConfig<BC::Instant>,
RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>,
),
NotFoundError,
> {
let addr_id = match addr {
DelIpAddr::SpecifiedAddr(addr) => core_ctx.get_address_id(device_id, addr)?,
DelIpAddr::AddressId(id) => id,
};
DadHandler::stop_duplicate_address_detection(core_ctx, bindings_ctx, device_id, &addr_id);
let addr_config = core_ctx
.with_ip_address_state_mut(device_id, &addr_id, |addr_state| {
I::take_addr_config_for_removal(addr_state)
})
.ok_or(NotFoundError)?;
let addr_sub = addr_id.addr_sub();
let result = core_ctx.remove_ip_address(device_id, addr_id);
bindings_ctx.on_event(IpDeviceEvent::AddressRemoved {
device: device_id.clone(),
addr: addr_sub.addr().into(),
reason,
});
Ok((addr_sub, addr_config, result))
}
fn del_ip_addr<
I: IpDeviceIpExt,
BC: IpDeviceBindingsContext<I, CC::DeviceId>,
CC: IpDeviceConfigurationContext<I, BC>,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
addr: DelIpAddr<CC::AddressId, I::Addr>,
reason: AddressRemovedReason,
) -> Result<RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>, NotFoundError> {
info!("removing addr {addr} from device {device_id:?}");
core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
del_ip_addr_inner_and_notify_handler(
&mut core_ctx,
bindings_ctx,
device_id,
addr,
reason,
config,
)
})
}
fn del_ip_addr_inner_and_notify_handler<
I: IpDeviceIpExt,
BC: IpDeviceBindingsContext<I, CC::DeviceId>,
CC: IpDeviceStateContext<I, BC>
+ GmpHandler<I, BC>
+ DadHandler<I, BC>
+ IpAddressRemovalHandler<I, BC>,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
addr: DelIpAddr<CC::AddressId, I::Addr>,
reason: AddressRemovedReason,
config: &I::Configuration,
) -> Result<RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>, NotFoundError> {
del_ip_addr_inner(core_ctx, bindings_ctx, device_id, addr, reason, config).map(
|(addr_sub, config, result)| {
core_ctx.on_address_removed(bindings_ctx, device_id, addr_sub, config, reason);
result
},
)
}
pub fn is_ip_device_enabled<
I: IpDeviceIpExt,
BC: IpDeviceBindingsContext<I, CC::DeviceId>,
CC: IpDeviceStateContext<I, BC>,
>(
core_ctx: &mut CC,
device_id: &CC::DeviceId,
) -> bool {
core_ctx.with_ip_device_flags(device_id, |flags| flags.ip_enabled)
}
pub fn clear_ipv4_device_state<
BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
CC: IpDeviceConfigurationContext<Ipv4, BC>,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
) {
core_ctx.with_ip_device_configuration_mut(device_id, |mut core_ctx| {
let ip_enabled = core_ctx.with_configuration_and_flags_mut(device_id, |_config, flags| {
let IpDeviceFlags { ip_enabled } = flags;
core::mem::replace(ip_enabled, false)
});
let (config, mut core_ctx) = core_ctx.ip_device_configuration_and_ctx();
let core_ctx = &mut core_ctx;
if ip_enabled {
disable_ipv4_device_with_config(core_ctx, bindings_ctx, device_id, config);
}
})
}
pub fn clear_ipv6_device_state<
BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
CC: Ipv6DeviceConfigurationContext<BC>,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
) {
core_ctx.with_ipv6_device_configuration_mut(device_id, |mut core_ctx| {
let ip_enabled = core_ctx.with_configuration_and_flags_mut(device_id, |_config, flags| {
let IpDeviceFlags { ip_enabled } = flags;
core::mem::replace(ip_enabled, false)
});
let (config, mut core_ctx) = core_ctx.ipv6_device_configuration_and_ctx();
let core_ctx = &mut core_ctx;
if ip_enabled {
disable_ipv6_device_with_config(core_ctx, bindings_ctx, device_id, config);
}
})
}
#[cfg(any(test, feature = "testutils"))]
pub(crate) mod testutil {
use alloc::boxed::Box;
use super::*;
pub fn with_assigned_ipv4_addr_subnets<
BT: IpDeviceStateBindingsTypes,
CC: IpDeviceStateContext<Ipv4, BT>,
O,
F: FnOnce(Box<dyn Iterator<Item = AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>> + '_>) -> O,
>(
core_ctx: &mut CC,
device_id: &CC::DeviceId,
cb: F,
) -> O {
core_ctx.with_address_ids(device_id, |addrs, _core_ctx| {
cb(Box::new(addrs.map(|a| a.addr_sub())))
})
}
pub fn with_assigned_ipv6_addr_subnets<
BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
CC: Ipv6DeviceContext<BC>,
O,
F: FnOnce(Box<dyn Iterator<Item = AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>> + '_>) -> O,
>(
core_ctx: &mut CC,
device_id: &CC::DeviceId,
cb: F,
) -> O {
core_ctx.with_address_ids(device_id, |addrs, core_ctx| {
cb(Box::new(addrs.filter_map(|addr_id| {
core_ctx
.with_ip_address_state(
device_id,
&addr_id,
|Ipv6AddressState { flags: Ipv6AddressFlags { assigned }, config: _ }| {
*assigned
},
)
.then(|| addr_id.addr_sub())
})))
})
}
}