pub(crate) mod arp;
pub mod ethernet;
pub(crate) mod link;
pub mod loopback;
pub mod ndp;
pub mod queue;
pub mod socket;
mod state;
use alloc::{boxed::Box, collections::HashMap, vec::Vec};
use core::{
convert::Infallible as Never,
fmt::{self, Debug, Formatter},
hash::Hash,
marker::PhantomData,
num::NonZeroU8,
ops::Deref as _,
};
use derivative::Derivative;
use lock_order::{
lock::{RwLockFor, UnlockedAccess},
relation::LockBefore,
Locked,
};
use net_types::{
ethernet::Mac,
ip::{AddrSubnet, Ip, IpAddr, IpAddress, IpInvariant, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr, Mtu},
BroadcastAddr, MulticastAddr, SpecifiedAddr, UnicastAddr, Witness as _,
};
use packet::{Buf, BufferMut, Serializer};
use packet_formats::{ethernet::EthernetIpExt, utils::NonZeroDuration};
use smallvec::SmallVec;
use tracing::{debug, trace};
use crate::{
context::{InstantBindingsTypes, InstantContext, RecvFrameContext, SendFrameContext},
device::{
ethernet::{
EthernetDeviceState, EthernetDeviceStateBuilder,
EthernetIpLinkDeviceDynamicStateContext, EthernetLinkDevice, EthernetTimerId,
SyncCtxWithDeviceId,
},
loopback::{LoopbackDevice, LoopbackDeviceId, LoopbackDeviceState, LoopbackWeakDeviceId},
queue::{
rx::ReceiveQueueHandler,
tx::{BufferTransmitQueueHandler, TransmitQueueConfiguration, TransmitQueueHandler},
},
socket::HeldSockets,
state::IpLinkDeviceState,
},
error::{
ExistsError, NotFoundError, NotSupportedError, SetIpAddressPropertiesError,
StaticNeighborInsertionError,
},
ip::{
device::{
integration::SyncCtxWithIpDeviceConfiguration,
nud::{
BufferNudHandler, ConfirmationFlags, DynamicNeighborUpdateSource,
LinkResolutionContext, NeighborStateInspect, NudHandler, NudIpHandler,
},
state::{
AddrSubnetAndManualConfigEither, AssignedAddress as _, DualStackIpDeviceState,
IpDeviceFlags, Ipv4AddressEntry, Ipv4AddressState, Ipv4DeviceConfiguration,
Ipv4DeviceConfigurationAndFlags, Ipv6AddressEntry, Ipv6AddressState, Ipv6DadState,
Ipv6DeviceConfiguration, Ipv6DeviceConfigurationAndFlags, Lifetime,
},
BufferIpDeviceContext, DelIpv6Addr, DualStackDeviceContext, DualStackDeviceStateRef,
IpDeviceAddressContext, IpDeviceAddressIdContext, IpDeviceConfigurationContext,
IpDeviceIpExt, IpDeviceStateContext, Ipv4DeviceConfigurationUpdate,
Ipv6DeviceConfigurationContext, Ipv6DeviceConfigurationUpdate, Ipv6DeviceContext,
},
forwarding::IpForwardingDeviceContext,
types::RawMetric,
},
sync::{PrimaryRc, RwLock, StrongRc, WeakRc},
trace_duration, BufferNonSyncContext, Instant, NonSyncContext, SyncCtx,
};
pub trait Device: 'static {}
pub(crate) enum AnyDevice {}
impl Device for AnyDevice {}
pub(crate) trait Id: Clone + Debug + Eq + Hash + PartialEq + Send + Sync {
fn is_loopback(&self) -> bool;
}
pub(crate) trait StrongId: Id {
type Weak: WeakId<Strong = Self>;
}
pub(crate) trait WeakId: Id + PartialEq<Self::Strong> {
type Strong: StrongId<Weak = Self>;
}
pub(crate) trait DeviceIdContext<D: Device> {
type DeviceId: StrongId<Weak = Self::WeakDeviceId> + 'static;
type WeakDeviceId: WeakId<Strong = Self::DeviceId> + 'static;
fn downgrade_device_id(&self, device_id: &Self::DeviceId) -> Self::WeakDeviceId;
fn upgrade_weak_device_id(&self, weak_device_id: &Self::WeakDeviceId)
-> Option<Self::DeviceId>;
}
struct RecvIpFrameMeta<D, I: Ip> {
device: D,
frame_dst: FrameDestination,
_marker: PhantomData<I>,
}
impl<D, I: Ip> RecvIpFrameMeta<D, I> {
fn new(device: D, frame_dst: FrameDestination) -> RecvIpFrameMeta<D, I> {
RecvIpFrameMeta { device, frame_dst, _marker: PhantomData }
}
}
impl<
B: BufferMut,
NonSyncCtx: BufferNonSyncContext<B>,
L: LockBefore<crate::lock_ordering::EthernetRxDequeue>,
> RecvFrameContext<NonSyncCtx, B, RecvIpFrameMeta<EthernetDeviceId<NonSyncCtx>, Ipv4>>
for Locked<&SyncCtx<NonSyncCtx>, L>
{
fn receive_frame(
&mut self,
ctx: &mut NonSyncCtx,
metadata: RecvIpFrameMeta<EthernetDeviceId<NonSyncCtx>, Ipv4>,
frame: B,
) {
crate::ip::receive_ipv4_packet(
self,
ctx,
&metadata.device.into(),
metadata.frame_dst,
frame,
);
}
}
impl<
B: BufferMut,
NonSyncCtx: BufferNonSyncContext<B>,
L: LockBefore<crate::lock_ordering::EthernetRxDequeue>,
> RecvFrameContext<NonSyncCtx, B, RecvIpFrameMeta<EthernetDeviceId<NonSyncCtx>, Ipv6>>
for Locked<&SyncCtx<NonSyncCtx>, L>
{
fn receive_frame(
&mut self,
ctx: &mut NonSyncCtx,
metadata: RecvIpFrameMeta<EthernetDeviceId<NonSyncCtx>, Ipv6>,
frame: B,
) {
crate::ip::receive_ipv6_packet(
self,
ctx,
&metadata.device.into(),
metadata.frame_dst,
frame,
);
}
}
impl<NonSyncCtx: NonSyncContext> UnlockedAccess<crate::lock_ordering::DeviceLayerStateOrigin>
for SyncCtx<NonSyncCtx>
{
type Data = OriginTracker;
type Guard<'l> = &'l OriginTracker where Self: 'l;
fn access(&self) -> Self::Guard<'_> {
&self.state.device.origin
}
}
fn with_ethernet_state_and_sync_ctx<
NonSyncCtx: NonSyncContext,
O,
F: FnOnce(
Locked<&EthernetReferenceState<NonSyncCtx>, L>,
&mut Locked<&SyncCtx<NonSyncCtx>, L>,
) -> O,
L,
>(
sync_ctx: &mut Locked<&SyncCtx<NonSyncCtx>, L>,
EthernetDeviceId(state): &EthernetDeviceId<NonSyncCtx>,
cb: F,
) -> O {
assert_eq!(
*sync_ctx.unlocked_access::<crate::lock_ordering::DeviceLayerStateOrigin>(),
state.origin
);
cb(Locked::new_locked(&state), sync_ctx)
}
fn with_ethernet_state<
NonSyncCtx: NonSyncContext,
O,
F: FnOnce(Locked<&EthernetReferenceState<NonSyncCtx>, L>) -> O,
L,
>(
sync_ctx: &mut Locked<&SyncCtx<NonSyncCtx>, L>,
device_id: &EthernetDeviceId<NonSyncCtx>,
cb: F,
) -> O {
with_ethernet_state_and_sync_ctx(sync_ctx, device_id, |ip_device_state, _sync_ctx| {
cb(ip_device_state)
})
}
fn with_loopback_state<
NonSyncCtx: NonSyncContext,
O,
F: FnOnce(Locked<&'_ LoopbackReferenceState<NonSyncCtx>, L>) -> O,
L,
>(
sync_ctx: &mut Locked<&SyncCtx<NonSyncCtx>, L>,
device_id: &LoopbackDeviceId<NonSyncCtx>,
cb: F,
) -> O {
with_loopback_state_and_sync_ctx(sync_ctx, device_id, |ip_device_state, _sync_ctx| {
cb(ip_device_state)
})
}
fn with_loopback_state_and_sync_ctx<
NonSyncCtx: NonSyncContext,
O,
F: FnOnce(
Locked<
&IpLinkDeviceState<NonSyncCtx, NonSyncCtx::LoopbackDeviceState, LoopbackDeviceState>,
L,
>,
&mut Locked<&SyncCtx<NonSyncCtx>, L>,
) -> O,
L,
>(
sync_ctx: &mut Locked<&SyncCtx<NonSyncCtx>, L>,
LoopbackDeviceId(state): &LoopbackDeviceId<NonSyncCtx>,
cb: F,
) -> O {
assert_eq!(
*sync_ctx.unlocked_access::<crate::lock_ordering::DeviceLayerStateOrigin>(),
state.origin
);
cb(Locked::new_locked(&state), sync_ctx)
}
pub(crate) fn with_ip_device_state<
NonSyncCtx: NonSyncContext,
O,
F: FnOnce(Locked<&DualStackIpDeviceState<NonSyncCtx::Instant>, L>) -> O,
L,
>(
ctx: &mut Locked<&SyncCtx<NonSyncCtx>, L>,
device: &DeviceId<NonSyncCtx>,
cb: F,
) -> O {
match device {
DeviceId::Ethernet(id) => with_ethernet_state(ctx, id, |mut state| cb(state.cast())),
DeviceId::Loopback(id) => with_loopback_state(ctx, id, |mut state| cb(state.cast())),
}
}
pub(crate) fn with_ip_device_state_and_sync_ctx<
NonSyncCtx: NonSyncContext,
O,
F: FnOnce(
Locked<&DualStackIpDeviceState<NonSyncCtx::Instant>, L>,
&mut Locked<&SyncCtx<NonSyncCtx>, L>,
) -> O,
L,
>(
ctx: &mut Locked<&SyncCtx<NonSyncCtx>, L>,
device: &DeviceId<NonSyncCtx>,
cb: F,
) -> O {
match device {
DeviceId::Ethernet(id) => {
with_ethernet_state_and_sync_ctx(ctx, id, |mut state, ctx| cb(state.cast(), ctx))
}
DeviceId::Loopback(id) => {
with_loopback_state_and_sync_ctx(ctx, id, |mut state, ctx| cb(state.cast(), ctx))
}
}
}
fn get_mtu<NonSyncCtx: NonSyncContext, L: LockBefore<crate::lock_ordering::DeviceLayerState>>(
ctx: &mut Locked<&SyncCtx<NonSyncCtx>, L>,
device: &DeviceId<NonSyncCtx>,
) -> Mtu {
match device {
DeviceId::Ethernet(id) => self::ethernet::get_mtu(ctx, &id),
DeviceId::Loopback(id) => self::loopback::get_mtu(ctx, id),
}
}
fn join_link_multicast_group<
NonSyncCtx: NonSyncContext,
A: IpAddress,
L: LockBefore<crate::lock_ordering::EthernetDeviceDynamicState>,
>(
sync_ctx: &mut Locked<&SyncCtx<NonSyncCtx>, L>,
ctx: &mut NonSyncCtx,
device_id: &DeviceId<NonSyncCtx>,
multicast_addr: MulticastAddr<A>,
) {
match device_id {
DeviceId::Ethernet(id) => self::ethernet::join_link_multicast(
sync_ctx,
ctx,
&id,
MulticastAddr::from(&multicast_addr),
),
DeviceId::Loopback(LoopbackDeviceId(_)) => {}
}
}
fn leave_link_multicast_group<
NonSyncCtx: NonSyncContext,
A: IpAddress,
L: LockBefore<crate::lock_ordering::EthernetDeviceDynamicState>,
>(
sync_ctx: &mut Locked<&SyncCtx<NonSyncCtx>, L>,
ctx: &mut NonSyncCtx,
device_id: &DeviceId<NonSyncCtx>,
multicast_addr: MulticastAddr<A>,
) {
match device_id {
DeviceId::Ethernet(id) => self::ethernet::leave_link_multicast(
sync_ctx,
ctx,
&id,
MulticastAddr::from(&multicast_addr),
),
DeviceId::Loopback(LoopbackDeviceId(_)) => {}
}
}
impl<NonSyncCtx: NonSyncContext> DualStackDeviceContext<NonSyncCtx>
for Locked<&SyncCtx<NonSyncCtx>, crate::lock_ordering::Unlocked>
{
fn with_dual_stack_device_state<
O,
F: FnOnce(DualStackDeviceStateRef<'_, NonSyncCtx::Instant>) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state(self, device_id, |mut state| {
let (ipv4, mut locked) =
state.read_lock_and::<crate::lock_ordering::IpDeviceAddresses<Ipv4>>();
let ipv6 = locked.read_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv6>>();
cb(DualStackDeviceStateRef { ipv4: &ipv4, ipv6: &ipv6 })
})
}
}
pub(crate) struct DevicesIter<'s, C: NonSyncContext> {
ethernet: alloc::collections::hash_map::Values<
's,
StrongRc<EthernetReferenceState<C>>,
PrimaryRc<EthernetReferenceState<C>>,
>,
loopback: core::option::Iter<'s, PrimaryRc<LoopbackReferenceState<C>>>,
}
impl<'s, C: NonSyncContext> Iterator for DevicesIter<'s, C> {
type Item = DeviceId<C>;
fn next(&mut self) -> Option<Self::Item> {
let Self { ethernet, loopback } = self;
ethernet
.map(|rc| EthernetDeviceId(PrimaryRc::clone_strong(rc)).into())
.chain(
loopback.map(|state| {
DeviceId::Loopback(LoopbackDeviceId(PrimaryRc::clone_strong(state)))
}),
)
.next()
}
}
impl<I: IpDeviceIpExt, NonSyncCtx: NonSyncContext, L> IpForwardingDeviceContext<I>
for Locked<&SyncCtx<NonSyncCtx>, L>
where
Self: IpDeviceStateContext<I, NonSyncCtx, DeviceId = DeviceId<NonSyncCtx>>,
{
fn get_routing_metric(&mut self, device_id: &Self::DeviceId) -> RawMetric {
match device_id {
DeviceId::Ethernet(id) => self::ethernet::get_routing_metric(self, id),
DeviceId::Loopback(id) => self::loopback::get_routing_metric(self, id),
}
}
fn is_ip_device_enabled(&mut self, device_id: &Self::DeviceId) -> bool {
IpDeviceStateContext::<I, _>::with_ip_device_flags(
self,
device_id,
|IpDeviceFlags { ip_enabled }| *ip_enabled,
)
}
}
impl<
NonSyncCtx: NonSyncContext,
L: LockBefore<crate::lock_ordering::IpDeviceConfiguration<Ipv4>>,
> IpDeviceConfigurationContext<Ipv4, NonSyncCtx> for Locked<&SyncCtx<NonSyncCtx>, L>
{
type DevicesIter<'s> = DevicesIter<'s, NonSyncCtx>;
type WithIpDeviceConfigurationInnerCtx<'s> = SyncCtxWithIpDeviceConfiguration<
's,
&'s Ipv4DeviceConfiguration,
crate::lock_ordering::IpDeviceConfiguration<Ipv4>,
NonSyncCtx,
>;
type WithIpDeviceConfigurationMutInner<'s> = SyncCtxWithIpDeviceConfiguration<
's,
&'s mut Ipv4DeviceConfiguration,
crate::lock_ordering::IpDeviceConfiguration<Ipv4>,
NonSyncCtx,
>;
type DeviceAddressAndGroupsAccessor<'s> =
Locked<&'s SyncCtx<NonSyncCtx>, crate::lock_ordering::DeviceLayerState>;
fn with_ip_device_configuration<
O,
F: FnOnce(&Ipv4DeviceConfiguration, Self::WithIpDeviceConfigurationInnerCtx<'_>) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state_and_sync_ctx(self, device_id, |mut state, sync_ctx| {
let state = state.read_lock::<crate::lock_ordering::IpDeviceConfiguration<Ipv4>>();
cb(
&state,
SyncCtxWithIpDeviceConfiguration {
config: &state,
sync_ctx: sync_ctx
.cast_locked::<crate::lock_ordering::IpDeviceConfiguration<Ipv4>>(),
},
)
})
}
fn with_ip_device_configuration_mut<
O,
F: FnOnce(Self::WithIpDeviceConfigurationMutInner<'_>) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state_and_sync_ctx(self, device_id, |mut state, sync_ctx| {
let mut state = state.write_lock::<crate::lock_ordering::IpDeviceConfiguration<Ipv4>>();
cb(SyncCtxWithIpDeviceConfiguration {
config: &mut state,
sync_ctx: sync_ctx
.cast_locked::<crate::lock_ordering::IpDeviceConfiguration<Ipv4>>(),
})
})
}
fn with_devices_and_state<
O,
F: FnOnce(Self::DevicesIter<'_>, Self::DeviceAddressAndGroupsAccessor<'_>) -> O,
>(
&mut self,
cb: F,
) -> O {
let (devices, locked) = self.read_lock_and::<crate::lock_ordering::DeviceLayerState>();
let Devices { ethernet_counter: _, ethernet, loopback } = &*devices;
cb(DevicesIter { ethernet: ethernet.values(), loopback: loopback.iter() }, locked)
}
fn get_mtu(&mut self, device_id: &Self::DeviceId) -> Mtu {
get_mtu(self, device_id)
}
fn loopback_id(&mut self) -> Option<Self::DeviceId> {
let mut locked = self.cast_with(|s| &s.state.device);
let devices = &*locked.read_lock::<crate::lock_ordering::DeviceLayerState>();
devices
.loopback
.as_ref()
.map(|state| DeviceId::Loopback(LoopbackDeviceId(PrimaryRc::clone_strong(state))))
}
}
pub trait NeighborVisitor<C: NonSyncContext, T: Instant> {
fn visit_neighbors<LinkAddress: Debug>(
&self,
device: DeviceId<C>,
neighbors: impl Iterator<Item = NeighborStateInspect<LinkAddress, T>>,
);
}
pub(crate) fn snapshot_device_ids<T, C: NonSyncContext, F: FnMut(DeviceId<C>) -> Option<T>>(
sync_ctx: &SyncCtx<C>,
filter_map: F,
) -> impl IntoIterator<Item = T> {
let mut sync_ctx = Locked::new(sync_ctx);
let devices = sync_ctx.read_lock::<crate::lock_ordering::DeviceLayerState>();
let Devices { ethernet, loopback, ethernet_counter: _ } = &*devices;
DevicesIter { ethernet: ethernet.values(), loopback: loopback.iter() }
.filter_map(filter_map)
.collect::<SmallVec<[T; 32]>>()
}
pub fn inspect_neighbors<C, V>(sync_ctx: &SyncCtx<C>, visitor: &V)
where
C: NonSyncContext,
V: NeighborVisitor<C, <C as InstantBindingsTypes>::Instant>,
{
let device_ids = snapshot_device_ids(sync_ctx, |device| match device {
DeviceId::Ethernet(d) => Some(d),
DeviceId::Loopback(_) => None,
});
let mut sync_ctx = Locked::new(sync_ctx);
for device in device_ids {
let id = device.clone();
with_ethernet_state(&mut sync_ctx, &id, |mut device_state| {
let (arp, mut device_state) =
device_state.lock_and::<crate::lock_ordering::EthernetIpv4Arp>();
let nud = device_state.lock::<crate::lock_ordering::EthernetIpv6Nud>();
visitor.visit_neighbors(
DeviceId::from(device),
arp.nud.state_iter().chain(nud.state_iter()),
);
})
}
}
impl<NonSyncCtx: NonSyncContext, L> IpDeviceAddressIdContext<Ipv4>
for Locked<&SyncCtx<NonSyncCtx>, L>
{
type AddressId = StrongRc<Ipv4AddressEntry<NonSyncCtx::Instant>>;
}
impl<NonSyncCtx: NonSyncContext, L: LockBefore<crate::lock_ordering::Ipv4DeviceAddressState>>
IpDeviceAddressContext<Ipv4, NonSyncCtx> for Locked<&SyncCtx<NonSyncCtx>, L>
{
fn with_ip_address_state<O, F: FnOnce(&Ipv4AddressState<NonSyncCtx::Instant>) -> O>(
&mut self,
_: &Self::DeviceId,
addr_id: &Self::AddressId,
cb: F,
) -> O {
let mut entry = Locked::<_, L>::new_locked(addr_id.deref());
let let_binding_needed_for_lifetimes =
cb(&entry.read_lock::<crate::lock_ordering::Ipv4DeviceAddressState>());
let_binding_needed_for_lifetimes
}
fn with_ip_address_state_mut<O, F: FnOnce(&mut Ipv4AddressState<NonSyncCtx::Instant>) -> O>(
&mut self,
_: &Self::DeviceId,
addr_id: &Self::AddressId,
cb: F,
) -> O {
let mut entry = Locked::<_, L>::new_locked(addr_id.deref());
let let_binding_needed_for_lifetimes =
cb(&mut entry.write_lock::<crate::lock_ordering::Ipv4DeviceAddressState>());
let_binding_needed_for_lifetimes
}
}
impl<NonSyncCtx: NonSyncContext, L: LockBefore<crate::lock_ordering::IpDeviceAddresses<Ipv4>>>
IpDeviceStateContext<Ipv4, NonSyncCtx> for Locked<&SyncCtx<NonSyncCtx>, L>
{
type IpDeviceAddressCtx<'a> =
Locked<&'a SyncCtx<NonSyncCtx>, crate::lock_ordering::IpDeviceAddresses<Ipv4>>;
fn with_ip_device_flags<O, F: FnOnce(&IpDeviceFlags) -> O>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state(self, device_id, |mut state| {
cb(&*state.lock::<crate::lock_ordering::IpDeviceFlags<Ipv4>>())
})
}
fn add_ip_address(
&mut self,
device_id: &Self::DeviceId,
addr: AddrSubnet<Ipv4Addr>,
config: <Ipv4 as IpDeviceIpExt>::AddressConfig<NonSyncCtx::Instant>,
) -> Result<Self::AddressId, ExistsError> {
with_ip_device_state(self, device_id, |mut state| {
state
.write_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv4>>()
.add(Ipv4AddressEntry::new(addr, config))
})
}
fn remove_ip_address(
&mut self,
device_id: &Self::DeviceId,
addr: Self::AddressId,
) -> (AddrSubnet<Ipv4Addr>, <Ipv4 as IpDeviceIpExt>::AddressConfig<NonSyncCtx::Instant>) {
let primary = with_ip_device_state(self, device_id, |mut state| {
state.write_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv4>>().remove(&addr.addr())
})
.expect("should exist when address ID exists");
assert!(PrimaryRc::ptr_eq(&primary, &addr));
core::mem::drop(addr);
let Ipv4AddressEntry { addr_sub, state } = PrimaryRc::unwrap(primary);
let Ipv4AddressState { config } = state.into_inner();
(addr_sub, config)
}
fn get_address_id(
&mut self,
device_id: &Self::DeviceId,
addr: SpecifiedAddr<Ipv4Addr>,
) -> Result<Self::AddressId, NotFoundError> {
with_ip_device_state(self, device_id, |mut state| {
state
.read_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv4>>()
.iter()
.find(|a| a.addr() == addr)
.map(PrimaryRc::clone_strong)
.ok_or(NotFoundError)
})
}
fn with_address_ids<
O,
F: FnOnce(
Box<dyn Iterator<Item = Self::AddressId> + '_>,
&mut Self::IpDeviceAddressCtx<'_>,
) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state_and_sync_ctx(self, device_id, |mut state, sync_ctx| {
cb(
Box::new(
state
.read_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv4>>()
.iter()
.map(PrimaryRc::clone_strong),
),
&mut sync_ctx.cast_locked::<crate::lock_ordering::IpDeviceAddresses<Ipv4>>(),
)
})
}
fn with_default_hop_limit<O, F: FnOnce(&NonZeroU8) -> O>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state(self, device_id, |mut state| {
let mut state =
state.read_lock::<crate::lock_ordering::IpDeviceDefaultHopLimit<Ipv4>>();
cb(&mut state)
})
}
fn with_default_hop_limit_mut<O, F: FnOnce(&mut NonZeroU8) -> O>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state(self, device_id, |mut state| {
let mut state =
state.write_lock::<crate::lock_ordering::IpDeviceDefaultHopLimit<Ipv4>>();
cb(&mut state)
})
}
fn join_link_multicast_group(
&mut self,
ctx: &mut NonSyncCtx,
device_id: &Self::DeviceId,
multicast_addr: MulticastAddr<Ipv4Addr>,
) {
join_link_multicast_group(self, ctx, device_id, multicast_addr)
}
fn leave_link_multicast_group(
&mut self,
ctx: &mut NonSyncCtx,
device_id: &Self::DeviceId,
multicast_addr: MulticastAddr<Ipv4Addr>,
) {
leave_link_multicast_group(self, ctx, device_id, multicast_addr)
}
}
fn send_ip_frame<
B: BufferMut,
NonSyncCtx: BufferNonSyncContext<B>,
S: Serializer<Buffer = B>,
A: IpAddress,
L: LockBefore<crate::lock_ordering::IpState<A::Version>>
+ LockBefore<crate::lock_ordering::LoopbackTxQueue>,
>(
sync_ctx: &mut Locked<&SyncCtx<NonSyncCtx>, L>,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
local_addr: SpecifiedAddr<A>,
body: S,
) -> Result<(), S>
where
A::Version: EthernetIpExt,
for<'a> Locked<&'a SyncCtx<NonSyncCtx>, L>: EthernetIpLinkDeviceDynamicStateContext<NonSyncCtx, DeviceId = EthernetDeviceId<NonSyncCtx>>
+ BufferNudHandler<B, A::Version, EthernetLinkDevice, NonSyncCtx>
+ BufferTransmitQueueHandler<EthernetLinkDevice, B, NonSyncCtx, Meta = ()>,
{
match device {
DeviceId::Ethernet(id) => {
self::ethernet::send_ip_frame::<_, _, _, A, _>(sync_ctx, ctx, &id, local_addr, body)
}
DeviceId::Loopback(id) => {
self::loopback::send_ip_frame::<_, _, A, _, _>(sync_ctx, ctx, id, local_addr, body)
}
}
}
fn bytes_to_mac(b: &[u8]) -> Option<Mac> {
(b.len() >= Mac::BYTES).then(|| {
Mac::new({
let mut bytes = [0; Mac::BYTES];
bytes.copy_from_slice(&b[..Mac::BYTES]);
bytes
})
})
}
impl<
I: Ip,
C: NonSyncContext,
L: LockBefore<crate::lock_ordering::EthernetIpv4Arp>
+ LockBefore<crate::lock_ordering::EthernetIpv6Nud>,
> NudIpHandler<I, C> for Locked<&SyncCtx<C>, L>
where
Self: NudHandler<I, EthernetLinkDevice, C>
+ DeviceIdContext<EthernetLinkDevice, DeviceId = EthernetDeviceId<C>>,
{
fn handle_neighbor_probe(
&mut self,
ctx: &mut C,
device_id: &DeviceId<C>,
neighbor: SpecifiedAddr<I::Addr>,
link_addr: &[u8],
) {
match device_id {
DeviceId::Ethernet(id) => {
if let Some(link_addr) = bytes_to_mac(link_addr) {
NudHandler::<I, EthernetLinkDevice, _>::handle_neighbor_update(
self,
ctx,
&id,
neighbor,
link_addr,
DynamicNeighborUpdateSource::Probe,
)
}
}
DeviceId::Loopback(LoopbackDeviceId(_)) => {}
}
}
fn handle_neighbor_confirmation(
&mut self,
ctx: &mut C,
device_id: &DeviceId<C>,
neighbor: SpecifiedAddr<I::Addr>,
link_addr: &[u8],
flags: ConfirmationFlags,
) {
match device_id {
DeviceId::Ethernet(id) => {
if let Some(link_addr) = bytes_to_mac(link_addr) {
NudHandler::<I, EthernetLinkDevice, _>::handle_neighbor_update(
self,
ctx,
&id,
neighbor,
link_addr,
DynamicNeighborUpdateSource::Confirmation(flags),
)
}
}
DeviceId::Loopback(LoopbackDeviceId(_)) => {}
}
}
fn flush_neighbor_table(&mut self, ctx: &mut C, device_id: &DeviceId<C>) {
match device_id {
DeviceId::Ethernet(id) => NudHandler::<I, EthernetLinkDevice, _>::flush(self, ctx, &id),
DeviceId::Loopback(LoopbackDeviceId(_)) => {}
}
}
}
impl<
B: BufferMut,
NonSyncCtx: BufferNonSyncContext<B>,
L: LockBefore<crate::lock_ordering::IpState<Ipv4>>,
> BufferIpDeviceContext<Ipv4, NonSyncCtx, B> for Locked<&SyncCtx<NonSyncCtx>, L>
{
fn send_ip_frame<S: Serializer<Buffer = B>>(
&mut self,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
local_addr: SpecifiedAddr<Ipv4Addr>,
body: S,
) -> Result<(), S> {
send_ip_frame(self, ctx, device, local_addr, body)
}
}
impl<
NonSyncCtx: NonSyncContext,
L: LockBefore<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>,
> Ipv6DeviceConfigurationContext<NonSyncCtx> for Locked<&SyncCtx<NonSyncCtx>, L>
{
type Ipv6DeviceStateCtx<'s> = SyncCtxWithIpDeviceConfiguration<
's,
&'s Ipv6DeviceConfiguration,
crate::lock_ordering::IpDeviceConfiguration<Ipv6>,
NonSyncCtx,
>;
type WithIpv6DeviceConfigurationMutInner<'s> = SyncCtxWithIpDeviceConfiguration<
's,
&'s mut Ipv6DeviceConfiguration,
crate::lock_ordering::IpDeviceConfiguration<Ipv6>,
NonSyncCtx,
>;
fn with_ipv6_device_configuration<
O,
F: FnOnce(&Ipv6DeviceConfiguration, Self::Ipv6DeviceStateCtx<'_>) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
IpDeviceConfigurationContext::<Ipv6, _>::with_ip_device_configuration(self, device_id, cb)
}
fn with_ipv6_device_configuration_mut<
O,
F: FnOnce(Self::WithIpv6DeviceConfigurationMutInner<'_>) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
IpDeviceConfigurationContext::<Ipv6, _>::with_ip_device_configuration_mut(
self, device_id, cb,
)
}
}
impl<
NonSyncCtx: NonSyncContext,
L: LockBefore<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>,
> IpDeviceConfigurationContext<Ipv6, NonSyncCtx> for Locked<&SyncCtx<NonSyncCtx>, L>
{
type DevicesIter<'s> = DevicesIter<'s, NonSyncCtx>;
type WithIpDeviceConfigurationInnerCtx<'s> = SyncCtxWithIpDeviceConfiguration<
's,
&'s Ipv6DeviceConfiguration,
crate::lock_ordering::IpDeviceConfiguration<Ipv6>,
NonSyncCtx,
>;
type WithIpDeviceConfigurationMutInner<'s> = SyncCtxWithIpDeviceConfiguration<
's,
&'s mut Ipv6DeviceConfiguration,
crate::lock_ordering::IpDeviceConfiguration<Ipv6>,
NonSyncCtx,
>;
type DeviceAddressAndGroupsAccessor<'s> =
Locked<&'s SyncCtx<NonSyncCtx>, crate::lock_ordering::DeviceLayerState>;
fn with_ip_device_configuration<
O,
F: FnOnce(&Ipv6DeviceConfiguration, Self::WithIpDeviceConfigurationInnerCtx<'_>) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state_and_sync_ctx(self, device_id, |mut state, sync_ctx| {
let state = state.read_lock::<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>();
cb(
&state,
SyncCtxWithIpDeviceConfiguration {
config: &state,
sync_ctx: sync_ctx
.cast_locked::<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>(),
},
)
})
}
fn with_ip_device_configuration_mut<
O,
F: FnOnce(Self::WithIpDeviceConfigurationMutInner<'_>) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state_and_sync_ctx(self, device_id, |mut state, sync_ctx| {
let mut state = state.write_lock::<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>();
cb(SyncCtxWithIpDeviceConfiguration {
config: &mut state,
sync_ctx: sync_ctx
.cast_locked::<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>(),
})
})
}
fn with_devices_and_state<
O,
F: FnOnce(Self::DevicesIter<'_>, Self::DeviceAddressAndGroupsAccessor<'_>) -> O,
>(
&mut self,
cb: F,
) -> O {
let (devices, locked) = self.read_lock_and::<crate::lock_ordering::DeviceLayerState>();
let Devices { ethernet_counter: _, ethernet, loopback } = &*devices;
cb(DevicesIter { ethernet: ethernet.values(), loopback: loopback.iter() }, locked)
}
fn get_mtu(&mut self, device_id: &Self::DeviceId) -> Mtu {
get_mtu(self, device_id)
}
fn loopback_id(&mut self) -> Option<Self::DeviceId> {
let mut locked = self.cast_with(|s| &s.state.device);
let devices = &*locked.read_lock::<crate::lock_ordering::DeviceLayerState>();
devices
.loopback
.as_ref()
.map(|state| DeviceId::Loopback(LoopbackDeviceId(PrimaryRc::clone_strong(state))))
}
}
impl<NonSyncCtx: NonSyncContext, L> IpDeviceAddressIdContext<Ipv6>
for Locked<&SyncCtx<NonSyncCtx>, L>
{
type AddressId = StrongRc<Ipv6AddressEntry<NonSyncCtx::Instant>>;
}
impl<NonSyncCtx: NonSyncContext, L: LockBefore<crate::lock_ordering::Ipv6DeviceAddressState>>
IpDeviceAddressContext<Ipv6, NonSyncCtx> for Locked<&SyncCtx<NonSyncCtx>, L>
{
fn with_ip_address_state<O, F: FnOnce(&Ipv6AddressState<NonSyncCtx::Instant>) -> O>(
&mut self,
_device_id: &Self::DeviceId,
addr_id: &Self::AddressId,
cb: F,
) -> O {
let mut entry = Locked::<_, L>::new_locked(addr_id.deref());
let x = cb(&entry.read_lock::<crate::lock_ordering::Ipv6DeviceAddressState>());
x
}
fn with_ip_address_state_mut<O, F: FnOnce(&mut Ipv6AddressState<NonSyncCtx::Instant>) -> O>(
&mut self,
_device_id: &Self::DeviceId,
addr_id: &Self::AddressId,
cb: F,
) -> O {
let mut entry = Locked::<_, L>::new_locked(addr_id.deref());
let x = cb(&mut entry.write_lock::<crate::lock_ordering::Ipv6DeviceAddressState>());
x
}
}
impl<NonSyncCtx: NonSyncContext, L: LockBefore<crate::lock_ordering::IpDeviceAddresses<Ipv6>>>
IpDeviceStateContext<Ipv6, NonSyncCtx> for Locked<&SyncCtx<NonSyncCtx>, L>
{
type IpDeviceAddressCtx<'a> =
Locked<&'a SyncCtx<NonSyncCtx>, crate::lock_ordering::IpDeviceAddresses<Ipv6>>;
fn with_ip_device_flags<O, F: FnOnce(&IpDeviceFlags) -> O>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state(self, device_id, |mut state| {
cb(&*state.lock::<crate::lock_ordering::IpDeviceFlags<Ipv6>>())
})
}
fn add_ip_address(
&mut self,
device_id: &Self::DeviceId,
addr: AddrSubnet<Ipv6Addr, UnicastAddr<Ipv6Addr>>,
config: <Ipv6 as IpDeviceIpExt>::AddressConfig<NonSyncCtx::Instant>,
) -> Result<Self::AddressId, ExistsError> {
with_ip_device_state(self, device_id, |mut state| {
state
.write_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv6>>()
.add(Ipv6AddressEntry::new(addr, Ipv6DadState::Uninitialized, config))
})
}
fn remove_ip_address(
&mut self,
device_id: &Self::DeviceId,
addr: Self::AddressId,
) -> (
AddrSubnet<Ipv6Addr, UnicastAddr<Ipv6Addr>>,
<Ipv6 as IpDeviceIpExt>::AddressConfig<NonSyncCtx::Instant>,
) {
let primary = with_ip_device_state(self, device_id, |mut state| {
state.write_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv6>>().remove(&addr.addr())
})
.expect("should exist when address ID exists");
assert!(PrimaryRc::ptr_eq(&primary, &addr));
core::mem::drop(addr);
let Ipv6AddressEntry { addr_sub, dad_state: _, state } = PrimaryRc::unwrap(primary);
let Ipv6AddressState { flags: _, config } = state.into_inner();
(addr_sub, config)
}
fn get_address_id(
&mut self,
device_id: &Self::DeviceId,
addr: SpecifiedAddr<Ipv6Addr>,
) -> Result<Self::AddressId, NotFoundError> {
with_ip_device_state(self, device_id, |mut state| {
state
.read_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv6>>()
.iter()
.find_map(|a| (a.addr() == addr).then(|| PrimaryRc::clone_strong(a)))
.ok_or(NotFoundError)
})
}
fn with_address_ids<
O,
F: FnOnce(
Box<dyn Iterator<Item = Self::AddressId> + '_>,
&mut Self::IpDeviceAddressCtx<'_>,
) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state_and_sync_ctx(self, device_id, |mut state, sync_ctx| {
cb(
Box::new(
state
.read_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv6>>()
.iter()
.map(PrimaryRc::clone_strong),
),
&mut sync_ctx.cast_locked::<crate::lock_ordering::IpDeviceAddresses<Ipv6>>(),
)
})
}
fn with_default_hop_limit<O, F: FnOnce(&NonZeroU8) -> O>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state(self, device_id, |mut state| {
let mut state =
state.read_lock::<crate::lock_ordering::IpDeviceDefaultHopLimit<Ipv6>>();
cb(&mut state)
})
}
fn with_default_hop_limit_mut<O, F: FnOnce(&mut NonZeroU8) -> O>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state(self, device_id, |mut state| {
let mut state =
state.write_lock::<crate::lock_ordering::IpDeviceDefaultHopLimit<Ipv6>>();
cb(&mut state)
})
}
fn join_link_multicast_group(
&mut self,
ctx: &mut NonSyncCtx,
device_id: &Self::DeviceId,
multicast_addr: MulticastAddr<Ipv6Addr>,
) {
join_link_multicast_group(self, ctx, device_id, multicast_addr)
}
fn leave_link_multicast_group(
&mut self,
ctx: &mut NonSyncCtx,
device_id: &Self::DeviceId,
multicast_addr: MulticastAddr<Ipv6Addr>,
) {
leave_link_multicast_group(self, ctx, device_id, multicast_addr)
}
}
pub(crate) enum Ipv6DeviceLinkLayerAddr {
Mac(Mac),
}
impl AsRef<[u8]> for Ipv6DeviceLinkLayerAddr {
fn as_ref(&self) -> &[u8] {
match self {
Ipv6DeviceLinkLayerAddr::Mac(a) => a.as_ref(),
}
}
}
impl<NonSyncCtx: NonSyncContext, L: LockBefore<crate::lock_ordering::IpDeviceAddresses<Ipv6>>>
Ipv6DeviceContext<NonSyncCtx> for Locked<&SyncCtx<NonSyncCtx>, L>
{
type LinkLayerAddr = Ipv6DeviceLinkLayerAddr;
fn get_link_layer_addr_bytes(
&mut self,
device_id: &Self::DeviceId,
) -> Option<Ipv6DeviceLinkLayerAddr> {
match device_id {
DeviceId::Ethernet(id) => {
Some(Ipv6DeviceLinkLayerAddr::Mac(ethernet::get_mac(self, &id).get()))
}
DeviceId::Loopback(LoopbackDeviceId(_)) => None,
}
}
fn get_eui64_iid(&mut self, device_id: &Self::DeviceId) -> Option<[u8; 8]> {
match device_id {
DeviceId::Ethernet(id) => {
Some(ethernet::get_mac(self, &id).to_eui64_with_magic(Mac::DEFAULT_EUI_MAGIC))
}
DeviceId::Loopback(LoopbackDeviceId(_)) => None,
}
}
fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu) {
if mtu < Ipv6::MINIMUM_LINK_MTU {
return;
}
match device_id {
DeviceId::Ethernet(id) => ethernet::set_mtu(self, &id, mtu),
DeviceId::Loopback(LoopbackDeviceId(_)) => {}
}
}
fn with_retrans_timer<O, F: FnOnce(&NonZeroDuration) -> O>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state(self, device_id, |mut state| {
let state = state.read_lock::<crate::lock_ordering::Ipv6DeviceRetransTimeout>();
cb(&state)
})
}
fn with_retrans_timer_mut<O, F: FnOnce(&mut NonZeroDuration) -> O>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
with_ip_device_state(self, device_id, |mut state| {
let mut state = state.write_lock::<crate::lock_ordering::Ipv6DeviceRetransTimeout>();
cb(&mut state)
})
}
}
impl<
B: BufferMut,
NonSyncCtx: BufferNonSyncContext<B>,
L: LockBefore<crate::lock_ordering::IpState<Ipv6>>,
> BufferIpDeviceContext<Ipv6, NonSyncCtx, B> for Locked<&SyncCtx<NonSyncCtx>, L>
{
fn send_ip_frame<S: Serializer<Buffer = B>>(
&mut self,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
local_addr: SpecifiedAddr<Ipv6Addr>,
body: S,
) -> Result<(), S> {
send_ip_frame(self, ctx, device, local_addr, body)
}
}
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Hash(bound = ""))]
pub struct EthernetWeakDeviceId<C: DeviceLayerTypes>(usize, WeakRc<EthernetReferenceState<C>>);
impl<C: DeviceLayerTypes> PartialEq for EthernetWeakDeviceId<C> {
fn eq(&self, EthernetWeakDeviceId(_, other_ptr): &EthernetWeakDeviceId<C>) -> bool {
let EthernetWeakDeviceId(_, me_ptr) = self;
WeakRc::ptr_eq(me_ptr, other_ptr)
}
}
impl<C: DeviceLayerTypes> PartialEq<EthernetDeviceId<C>> for EthernetWeakDeviceId<C> {
fn eq(&self, other: &EthernetDeviceId<C>) -> bool {
<EthernetDeviceId<C> as PartialEq<EthernetWeakDeviceId<C>>>::eq(other, self)
}
}
impl<C: DeviceLayerTypes> Eq for EthernetWeakDeviceId<C> {}
impl<C: DeviceLayerTypes> Debug for EthernetWeakDeviceId<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let Self(id, _ptr) = self;
write!(f, "WeakEthernet({id})")
}
}
impl<C: DeviceLayerTypes> Id for EthernetWeakDeviceId<C>
where
C::EthernetDeviceState: Send + Sync,
{
fn is_loopback(&self) -> bool {
false
}
}
impl<C: DeviceLayerTypes> WeakId for EthernetWeakDeviceId<C>
where
C::EthernetDeviceState: Send + Sync,
{
type Strong = EthernetDeviceId<C>;
}
impl<C: DeviceLayerTypes> EthernetWeakDeviceId<C> {
pub fn upgrade(&self) -> Option<EthernetDeviceId<C>> {
let Self(_id, rc) = self;
rc.upgrade().map(|rc| EthernetDeviceId(rc))
}
}
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Hash(bound = ""), Eq(bound = ""), PartialEq(bound = ""))]
pub struct EthernetDeviceId<C: DeviceLayerTypes>(StrongRc<EthernetReferenceState<C>>);
impl<C: DeviceLayerTypes> PartialEq<EthernetWeakDeviceId<C>> for EthernetDeviceId<C> {
fn eq(&self, EthernetWeakDeviceId(_id, other_ptr): &EthernetWeakDeviceId<C>) -> bool {
let EthernetDeviceId(me_ptr) = self;
StrongRc::weak_ptr_eq(me_ptr, other_ptr)
}
}
impl<C: DeviceLayerTypes> PartialOrd for EthernetDeviceId<C> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<C: DeviceLayerTypes> Ord for EthernetDeviceId<C> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
let Self(me) = self;
let Self(other) = other;
StrongRc::ptr_cmp(me, other)
}
}
impl<C: DeviceLayerTypes> Debug for EthernetDeviceId<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let Self(rc) = self;
let id = rc.link.debug_id();
write!(f, "Ethernet({id}{{")?;
rc.external_state.id_debug_tag(f)?;
write!(f, "}})")
}
}
impl<C: DeviceLayerTypes> Id for EthernetDeviceId<C>
where
C::EthernetDeviceState: Send + Sync,
{
fn is_loopback(&self) -> bool {
false
}
}
impl<C: DeviceLayerTypes> StrongId for EthernetDeviceId<C>
where
C::EthernetDeviceState: Send + Sync,
{
type Weak = EthernetWeakDeviceId<C>;
}
impl<C: DeviceLayerTypes> EthernetDeviceId<C> {
pub fn external_state(&self) -> &C::EthernetDeviceState {
let Self(rc) = self;
&rc.external_state
}
pub fn downgrade(&self) -> EthernetWeakDeviceId<C> {
let Self(rc) = self;
EthernetWeakDeviceId(rc.link.debug_id(), StrongRc::downgrade(rc))
}
}
#[derive(Derivative)]
#[derivative(
Clone(bound = ""),
Eq(bound = ""),
PartialEq(bound = ""),
Hash(bound = ""),
Debug(bound = "")
)]
pub(crate) struct DeviceLayerTimerId<C: DeviceLayerTypes>(DeviceLayerTimerIdInner<C>);
#[derive(Derivative)]
#[derivative(
Clone(bound = ""),
Eq(bound = ""),
PartialEq(bound = ""),
Hash(bound = ""),
Debug(bound = "")
)]
enum DeviceLayerTimerIdInner<C: DeviceLayerTypes> {
Ethernet(EthernetTimerId<EthernetDeviceId<C>>),
}
impl<C: DeviceLayerTypes> From<EthernetTimerId<EthernetDeviceId<C>>> for DeviceLayerTimerId<C> {
fn from(id: EthernetTimerId<EthernetDeviceId<C>>) -> DeviceLayerTimerId<C> {
DeviceLayerTimerId(DeviceLayerTimerIdInner::Ethernet(id))
}
}
impl<NonSyncCtx: NonSyncContext, L> DeviceIdContext<EthernetLinkDevice>
for Locked<&SyncCtx<NonSyncCtx>, L>
{
type DeviceId = EthernetDeviceId<NonSyncCtx>;
type WeakDeviceId = EthernetWeakDeviceId<NonSyncCtx>;
fn downgrade_device_id(&self, device_id: &Self::DeviceId) -> Self::WeakDeviceId {
device_id.downgrade()
}
fn upgrade_weak_device_id(
&self,
weak_device_id: &Self::WeakDeviceId,
) -> Option<Self::DeviceId> {
weak_device_id.upgrade()
}
}
impl<'a, SC: DeviceIdContext<EthernetLinkDevice>> DeviceIdContext<EthernetLinkDevice>
for SyncCtxWithDeviceId<'a, SC>
{
type DeviceId = SC::DeviceId;
type WeakDeviceId = SC::WeakDeviceId;
fn downgrade_device_id(&self, device_id: &Self::DeviceId) -> Self::WeakDeviceId {
let Self { sync_ctx, device_id: _ } = self;
SC::downgrade_device_id(sync_ctx, device_id)
}
fn upgrade_weak_device_id(
&self,
weak_device_id: &Self::WeakDeviceId,
) -> Option<Self::DeviceId> {
let Self { sync_ctx, device_id: _ } = self;
SC::upgrade_weak_device_id(sync_ctx, weak_device_id)
}
}
impl<C: socket::NonSyncContext<DeviceId<C>> + DeviceLayerEventDispatcher>
socket::NonSyncContext<EthernetDeviceId<C>> for C
{
fn receive_frame(
&self,
state: &Self::SocketState,
device: &EthernetDeviceId<C>,
frame: socket::Frame<&[u8]>,
whole_frame: &[u8],
) {
self.receive_frame(state, &device.clone().into(), frame, whole_frame)
}
}
impl<C: NonSyncContext, B: BufferMut, L>
SendFrameContext<C, B, socket::DeviceSocketMetadata<DeviceId<C>>> for Locked<&SyncCtx<C>, L>
where
Self: SendFrameContext<C, B, socket::DeviceSocketMetadata<EthernetDeviceId<C>>>
+ SendFrameContext<C, B, socket::DeviceSocketMetadata<LoopbackDeviceId<C>>>,
{
fn send_frame<S: Serializer<Buffer = B>>(
&mut self,
ctx: &mut C,
metadata: socket::DeviceSocketMetadata<DeviceId<C>>,
frame: S,
) -> Result<(), S> {
let socket::DeviceSocketMetadata { device_id, header } = metadata;
match device_id {
DeviceId::Ethernet(device_id) => SendFrameContext::send_frame(
self,
ctx,
socket::DeviceSocketMetadata { device_id, header },
frame,
),
DeviceId::Loopback(device_id) => SendFrameContext::send_frame(
self,
ctx,
socket::DeviceSocketMetadata { device_id, header },
frame,
),
}
}
}
impl_timer_context!(
C: NonSyncContext,
DeviceLayerTimerId<C>,
EthernetTimerId<EthernetDeviceId<C>>,
DeviceLayerTimerId(DeviceLayerTimerIdInner::Ethernet(id)),
id
);
pub(crate) fn handle_timer<NonSyncCtx: NonSyncContext>(
sync_ctx: &mut Locked<&SyncCtx<NonSyncCtx>, crate::lock_ordering::Unlocked>,
ctx: &mut NonSyncCtx,
DeviceLayerTimerId(id): DeviceLayerTimerId<NonSyncCtx>,
) {
match id {
DeviceLayerTimerIdInner::Ethernet(id) => ethernet::handle_timer(sync_ctx, ctx, id),
}
}
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Hash(bound = ""))]
#[allow(missing_docs)]
pub enum WeakDeviceId<C: DeviceLayerTypes> {
Ethernet(EthernetWeakDeviceId<C>),
Loopback(LoopbackWeakDeviceId<C>),
}
impl<C: DeviceLayerTypes> PartialEq<DeviceId<C>> for WeakDeviceId<C> {
fn eq(&self, other: &DeviceId<C>) -> bool {
<DeviceId<C> as PartialEq<WeakDeviceId<C>>>::eq(other, self)
}
}
impl<C: DeviceLayerTypes> From<EthernetWeakDeviceId<C>> for WeakDeviceId<C> {
fn from(id: EthernetWeakDeviceId<C>) -> WeakDeviceId<C> {
WeakDeviceId::Ethernet(id)
}
}
impl<C: DeviceLayerTypes> From<LoopbackWeakDeviceId<C>> for WeakDeviceId<C> {
fn from(id: LoopbackWeakDeviceId<C>) -> WeakDeviceId<C> {
WeakDeviceId::Loopback(id)
}
}
impl<C: DeviceLayerTypes> WeakDeviceId<C> {
pub fn upgrade(&self) -> Option<DeviceId<C>> {
match self {
WeakDeviceId::Ethernet(id) => id.upgrade().map(Into::into),
WeakDeviceId::Loopback(id) => id.upgrade().map(Into::into),
}
}
pub fn debug_references(&self) -> DebugReferences<C> {
DebugReferences(match self {
Self::Loopback(LoopbackWeakDeviceId(w)) => {
DebugReferencesInner::Loopback(w.debug_references())
}
Self::Ethernet(EthernetWeakDeviceId(_id, w)) => {
DebugReferencesInner::Ethernet(w.debug_references())
}
})
}
}
enum DebugReferencesInner<C: DeviceLayerTypes> {
Loopback(crate::sync::DebugReferences<LoopbackReferenceState<C>>),
Ethernet(crate::sync::DebugReferences<EthernetReferenceState<C>>),
}
pub struct DebugReferences<C: DeviceLayerTypes>(DebugReferencesInner<C>);
impl<C: DeviceLayerTypes> Debug for DebugReferences<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let Self(inner) = self;
match inner {
DebugReferencesInner::Loopback(d) => write!(f, "Loopback({d:?})"),
DebugReferencesInner::Ethernet(d) => write!(f, "Ethernet({d:?})"),
}
}
}
impl<C: DeviceLayerTypes> Id for WeakDeviceId<C> {
fn is_loopback(&self) -> bool {
match self {
WeakDeviceId::Loopback(LoopbackWeakDeviceId(_)) => true,
WeakDeviceId::Ethernet(_) => false,
}
}
}
impl<C: DeviceLayerTypes> WeakId for WeakDeviceId<C> {
type Strong = DeviceId<C>;
}
impl<C: DeviceLayerTypes> Debug for WeakDeviceId<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
match self {
WeakDeviceId::Ethernet(id) => Debug::fmt(id, f),
WeakDeviceId::Loopback(id) => Debug::fmt(id, f),
}
}
}
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Hash(bound = ""))]
#[allow(missing_docs)]
pub enum DeviceId<C: DeviceLayerTypes> {
Ethernet(EthernetDeviceId<C>),
Loopback(LoopbackDeviceId<C>),
}
impl<C: DeviceLayerTypes> PartialEq<WeakDeviceId<C>> for DeviceId<C> {
fn eq(&self, other: &WeakDeviceId<C>) -> bool {
match (self, other) {
(DeviceId::Ethernet(strong), WeakDeviceId::Ethernet(weak)) => strong == weak,
(DeviceId::Loopback(strong), WeakDeviceId::Loopback(weak)) => strong == weak,
(DeviceId::Loopback(_), WeakDeviceId::Ethernet(_))
| (DeviceId::Ethernet(_), WeakDeviceId::Loopback(_)) => false,
}
}
}
impl<C: DeviceLayerTypes> PartialOrd for DeviceId<C> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<C: DeviceLayerTypes> Ord for DeviceId<C> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
match (self, other) {
(DeviceId::Ethernet(me), DeviceId::Ethernet(other)) => me.cmp(other),
(DeviceId::Loopback(me), DeviceId::Loopback(other)) => me.cmp(other),
(DeviceId::Loopback(_), DeviceId::Ethernet(_)) => core::cmp::Ordering::Less,
(DeviceId::Ethernet(_), DeviceId::Loopback(_)) => core::cmp::Ordering::Greater,
}
}
}
impl<C: DeviceLayerTypes> From<EthernetDeviceId<C>> for DeviceId<C> {
fn from(id: EthernetDeviceId<C>) -> DeviceId<C> {
DeviceId::Ethernet(id)
}
}
impl<C: DeviceLayerTypes> From<LoopbackDeviceId<C>> for DeviceId<C> {
fn from(id: LoopbackDeviceId<C>) -> DeviceId<C> {
DeviceId::Loopback(id)
}
}
impl<C: DeviceLayerTypes> DeviceId<C> {
pub fn downgrade(&self) -> WeakDeviceId<C> {
match self {
DeviceId::Ethernet(id) => id.downgrade().into(),
DeviceId::Loopback(id) => id.downgrade().into(),
}
}
}
impl<C: DeviceLayerTypes> Id for DeviceId<C> {
fn is_loopback(&self) -> bool {
match self {
DeviceId::Loopback(LoopbackDeviceId(_)) => true,
DeviceId::Ethernet(_) => false,
}
}
}
impl<C: DeviceLayerTypes> StrongId for DeviceId<C> {
type Weak = WeakDeviceId<C>;
}
impl<C: DeviceLayerTypes> Debug for DeviceId<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
match self {
DeviceId::Ethernet(id) => Debug::fmt(id, f),
DeviceId::Loopback(id) => Debug::fmt(id, f),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FrameDestination {
Individual {
local: bool,
},
Multicast,
Broadcast,
}
impl FrameDestination {
pub(crate) fn is_multicast(self) -> bool {
self == FrameDestination::Multicast
}
pub(crate) fn is_broadcast(self) -> bool {
self == FrameDestination::Broadcast
}
pub(crate) fn from_dest(destination: Mac, local_mac: Mac) -> Self {
BroadcastAddr::new(destination)
.map(Into::into)
.or_else(|| MulticastAddr::new(destination).map(Into::into))
.unwrap_or_else(|| FrameDestination::Individual { local: destination == local_mac })
}
}
impl From<BroadcastAddr<Mac>> for FrameDestination {
fn from(_value: BroadcastAddr<Mac>) -> Self {
Self::Broadcast
}
}
impl From<MulticastAddr<Mac>> for FrameDestination {
fn from(_value: MulticastAddr<Mac>) -> Self {
Self::Multicast
}
}
type EthernetReferenceState<C> = IpLinkDeviceState<
C,
<C as DeviceLayerStateTypes>::EthernetDeviceState,
EthernetDeviceState<
<C as InstantBindingsTypes>::Instant,
<C as LinkResolutionContext<EthernetLinkDevice>>::Notifier,
>,
>;
type LoopbackReferenceState<C> =
IpLinkDeviceState<C, <C as DeviceLayerStateTypes>::LoopbackDeviceState, LoopbackDeviceState>;
#[derive(Derivative)]
#[derivative(Default(bound = ""))]
pub(crate) struct Devices<C: DeviceLayerTypes> {
ethernet_counter: usize,
ethernet: HashMap<StrongRc<EthernetReferenceState<C>>, PrimaryRc<EthernetReferenceState<C>>>,
loopback: Option<PrimaryRc<LoopbackReferenceState<C>>>,
}
pub(crate) struct DeviceLayerState<C: DeviceLayerTypes> {
devices: RwLock<Devices<C>>,
origin: OriginTracker,
shared_sockets: HeldSockets<C>,
}
impl<NonSyncCtx: NonSyncContext> RwLockFor<crate::lock_ordering::DeviceLayerState>
for SyncCtx<NonSyncCtx>
{
type Data = Devices<NonSyncCtx>;
type ReadGuard<'l> = crate::sync::RwLockReadGuard<'l, Devices<NonSyncCtx>>
where
Self: 'l ;
type WriteGuard<'l> = crate::sync::RwLockWriteGuard<'l, Devices<NonSyncCtx>>
where
Self: 'l ;
fn read_lock(&self) -> Self::ReadGuard<'_> {
self.state.device.devices.read()
}
fn write_lock(&self) -> Self::WriteGuard<'_> {
self.state.device.devices.write()
}
}
impl<C: DeviceLayerTypes + socket::NonSyncContext<DeviceId<C>>>
RwLockFor<crate::lock_ordering::DeviceLayerState> for DeviceLayerState<C>
{
type Data = Devices<C>;
type ReadGuard<'l> = crate::sync::RwLockReadGuard<'l, Devices<C>>
where
Self: 'l ;
type WriteGuard<'l> = crate::sync::RwLockWriteGuard<'l, Devices<C>>
where
Self: 'l ;
fn read_lock(&self) -> Self::ReadGuard<'_> {
self.devices.read()
}
fn write_lock(&self) -> Self::WriteGuard<'_> {
self.devices.write()
}
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct OriginTracker(#[cfg(debug_assertions)] u64);
impl OriginTracker {
#[cfg_attr(not(debug_assertions), inline)]
fn new() -> Self {
Self(
#[cfg(debug_assertions)]
{
static COUNTER: core::sync::atomic::AtomicU64 =
core::sync::atomic::AtomicU64::new(0);
COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
},
)
}
}
impl<C: DeviceLayerTypes + socket::NonSyncContext<DeviceId<C>>> DeviceLayerState<C> {
pub(crate) fn new() -> Self {
Self {
devices: Default::default(),
origin: OriginTracker::new(),
shared_sockets: Default::default(),
}
}
pub(crate) fn add_ethernet_device<F: FnOnce() -> C::EthernetDeviceState>(
&self,
mac: UnicastAddr<Mac>,
max_frame_size: ethernet::MaxFrameSize,
metric: RawMetric,
external_state: F,
) -> EthernetDeviceId<C> {
let Devices { ethernet_counter, ethernet, loopback: _ } = &mut *self.devices.write();
let id = *ethernet_counter;
*ethernet_counter += 1;
let ptr = PrimaryRc::new(IpLinkDeviceState::new(
EthernetDeviceStateBuilder::new(id, mac, max_frame_size, metric).build(),
external_state(),
self.origin.clone(),
));
let strong_ptr = PrimaryRc::clone_strong(&ptr);
assert!(ethernet.insert(strong_ptr.clone(), ptr).is_none());
debug!("adding Ethernet device with ID {} and MTU {:?}", id, max_frame_size);
EthernetDeviceId(strong_ptr)
}
pub(crate) fn add_loopback_device<F: FnOnce() -> C::LoopbackDeviceState>(
&self,
mtu: Mtu,
metric: RawMetric,
external_state: F,
) -> Result<LoopbackDeviceId<C>, ExistsError> {
let Devices { ethernet_counter: _, ethernet: _, loopback } = &mut *self.devices.write();
if let Some(_) = loopback {
return Err(ExistsError);
}
let ptr = PrimaryRc::new(IpLinkDeviceState::new(
LoopbackDeviceState::new(mtu, metric),
external_state(),
self.origin.clone(),
));
let id = PrimaryRc::clone_strong(&ptr);
*loopback = Some(ptr);
debug!("added loopback device");
Ok(LoopbackDeviceId(id))
}
}
pub trait DeviceLayerStateTypes: InstantContext {
type LoopbackDeviceState: DeviceIdDebugTag + Send + Sync;
type EthernetDeviceState: DeviceIdDebugTag + Send + Sync;
}
pub trait DeviceIdDebugTag {
fn id_debug_tag(&self, f: &mut Formatter<'_>) -> fmt::Result;
}
impl DeviceIdDebugTag for () {
fn id_debug_tag(&self, _: &mut Formatter<'_>) -> fmt::Result {
Ok(())
}
}
pub trait DeviceLayerTypes:
DeviceLayerStateTypes + socket::DeviceSocketTypes + LinkResolutionContext<EthernetLinkDevice>
{
}
impl<
C: DeviceLayerStateTypes
+ socket::DeviceSocketTypes
+ LinkResolutionContext<EthernetLinkDevice>,
> DeviceLayerTypes for C
{
}
pub trait DeviceLayerEventDispatcher: DeviceLayerTypes + Sized {
fn wake_rx_task(&mut self, device: &LoopbackDeviceId<Self>);
fn wake_tx_task(&mut self, device: &DeviceId<Self>);
fn send_frame(
&mut self,
device: &EthernetDeviceId<Self>,
frame: Buf<Vec<u8>>,
) -> Result<(), DeviceSendFrameError<Buf<Vec<u8>>>>;
}
#[derive(Debug, PartialEq, Eq)]
pub enum DeviceSendFrameError<T> {
DeviceNotReady(T),
}
pub fn set_tx_queue_configuration<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
config: TransmitQueueConfiguration,
) {
let sync_ctx = &mut Locked::new(sync_ctx);
match device {
DeviceId::Ethernet(id) => TransmitQueueHandler::<EthernetLinkDevice, _>::set_configuration(
sync_ctx, ctx, id, config,
),
DeviceId::Loopback(id) => {
TransmitQueueHandler::<LoopbackDevice, _>::set_configuration(sync_ctx, ctx, id, config)
}
}
}
pub fn transmit_queued_tx_frames<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
) -> Result<crate::WorkQueueReport, DeviceSendFrameError<()>> {
let sync_ctx = &mut Locked::new(sync_ctx);
match device {
DeviceId::Ethernet(id) => {
TransmitQueueHandler::<EthernetLinkDevice, _>::transmit_queued_frames(sync_ctx, ctx, id)
}
DeviceId::Loopback(id) => {
TransmitQueueHandler::<LoopbackDevice, _>::transmit_queued_frames(sync_ctx, ctx, id)
}
}
}
pub fn handle_queued_rx_packets<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: &LoopbackDeviceId<NonSyncCtx>,
) -> crate::WorkQueueReport {
ReceiveQueueHandler::<LoopbackDevice, _>::handle_queued_rx_frames(
&mut Locked::new(sync_ctx),
ctx,
device,
)
}
trait RemovableDeviceId<C: NonSyncContext>: Into<DeviceId<C>> + Clone {
type ExternalState: Send;
type ReferenceState;
fn remove(
self,
sync_ctx: &SyncCtx<C>,
) -> (PrimaryRc<Self::ReferenceState>, StrongRc<Self::ReferenceState>);
fn take_external_state(reference_state: Self::ReferenceState) -> Self::ExternalState;
}
impl<C: NonSyncContext> RemovableDeviceId<C> for EthernetDeviceId<C> {
type ExternalState = C::EthernetDeviceState;
type ReferenceState = EthernetReferenceState<C>;
fn remove(
self,
sync_ctx: &SyncCtx<C>,
) -> (PrimaryRc<Self::ReferenceState>, StrongRc<Self::ReferenceState>) {
let mut devices = sync_ctx.state.device.devices.write();
debug!("removing Ethernet device with ID {self:?}");
let EthernetDeviceId(rc) = self;
let removed = devices
.ethernet
.remove(&rc)
.unwrap_or_else(|| panic!("no such Ethernet device: {}", rc.link.debug_id()));
(removed, rc)
}
fn take_external_state(reference_state: Self::ReferenceState) -> Self::ExternalState {
reference_state.external_state
}
}
impl<C: NonSyncContext> RemovableDeviceId<C> for LoopbackDeviceId<C> {
type ExternalState = C::LoopbackDeviceState;
type ReferenceState = LoopbackReferenceState<C>;
fn remove(
self,
sync_ctx: &SyncCtx<C>,
) -> (PrimaryRc<Self::ReferenceState>, StrongRc<Self::ReferenceState>) {
let mut devices = sync_ctx.state.device.devices.write();
let LoopbackDeviceId(rc) = self;
let removed = devices.loopback.take().expect("loopback device not installed");
debug!("removing Loopback device");
(removed, rc)
}
fn take_external_state(reference_state: Self::ReferenceState) -> Self::ExternalState {
reference_state.external_state
}
}
#[derive(Debug)]
pub enum RemoveDeviceResult<R, D> {
Removed(R),
Deferred(D),
}
impl<R> RemoveDeviceResult<R, Never> {
pub fn into_removed(self) -> R {
match self {
Self::Removed(r) => r,
Self::Deferred(never) => match never {},
}
}
}
pub type RemoveDeviceResultWithContext<S, C> =
RemoveDeviceResult<S, <C as crate::ReferenceNotifiers>::ReferenceReceiver<S>>;
fn remove_device<NonSyncCtx: NonSyncContext, D: RemovableDeviceId<NonSyncCtx>>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: D,
) -> RemoveDeviceResultWithContext<D::ExternalState, NonSyncCtx> {
{
let mut sync_ctx = Locked::new(sync_ctx);
let device = device.clone().into();
crate::ip::device::clear_ipv4_device_state(&mut sync_ctx, ctx, &device);
crate::ip::device::clear_ipv6_device_state(&mut sync_ctx, ctx, &device);
crate::ip::forwarding::del_device_routes::<Ipv4, _, _>(&mut sync_ctx, ctx, &device);
crate::ip::forwarding::del_device_routes::<Ipv6, _, _>(&mut sync_ctx, ctx, &device);
}
let (primary, strong) = device.remove(sync_ctx);
assert!(PrimaryRc::ptr_eq(&primary, &strong));
let debug_references = PrimaryRc::debug_references(&primary);
core::mem::drop(strong);
match PrimaryRc::unwrap_or_notify_with(primary, || {
let (notifier, receiver) =
NonSyncCtx::new_reference_notifier::<D::ExternalState, _>(debug_references);
let notifier =
crate::sync::MapRcNotifier::new(notifier, |state| D::take_external_state(state));
(notifier, receiver)
}) {
Ok(s) => RemoveDeviceResult::Removed(D::take_external_state(s)),
Err(receiver) => RemoveDeviceResult::Deferred(receiver),
}
}
pub fn remove_ethernet_device<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: EthernetDeviceId<NonSyncCtx>,
) -> RemoveDeviceResultWithContext<NonSyncCtx::EthernetDeviceState, NonSyncCtx> {
remove_device(sync_ctx, ctx, device)
}
pub fn remove_loopback_device<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: LoopbackDeviceId<NonSyncCtx>,
) -> RemoveDeviceResultWithContext<NonSyncCtx::LoopbackDeviceState, NonSyncCtx> {
remove_device(sync_ctx, ctx, device)
}
pub fn add_ethernet_device_with_state<
NonSyncCtx: NonSyncContext,
F: FnOnce() -> NonSyncCtx::EthernetDeviceState,
>(
sync_ctx: &SyncCtx<NonSyncCtx>,
mac: UnicastAddr<Mac>,
max_frame_size: ethernet::MaxFrameSize,
metric: RawMetric,
external_state: F,
) -> EthernetDeviceId<NonSyncCtx> {
sync_ctx.state.device.add_ethernet_device(mac, max_frame_size, metric, external_state)
}
#[cfg(any(test, feature = "testutils"))]
pub(crate) fn add_ethernet_device<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
mac: UnicastAddr<Mac>,
max_frame_size: ethernet::MaxFrameSize,
metric: RawMetric,
) -> EthernetDeviceId<NonSyncCtx>
where
NonSyncCtx::EthernetDeviceState: Default,
{
add_ethernet_device_with_state(sync_ctx, mac, max_frame_size, metric, Default::default)
}
pub fn add_loopback_device_with_state<
NonSyncCtx: NonSyncContext,
F: FnOnce() -> NonSyncCtx::LoopbackDeviceState,
>(
sync_ctx: &SyncCtx<NonSyncCtx>,
mtu: Mtu,
metric: RawMetric,
external_state: F,
) -> Result<LoopbackDeviceId<NonSyncCtx>, crate::error::ExistsError> {
sync_ctx.state.device.add_loopback_device(mtu, metric, external_state)
}
#[cfg(test)]
pub(crate) fn add_loopback_device<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
mtu: Mtu,
metric: RawMetric,
) -> Result<LoopbackDeviceId<NonSyncCtx>, crate::error::ExistsError>
where
NonSyncCtx::LoopbackDeviceState: Default,
{
add_loopback_device_with_state(sync_ctx, mtu, metric, Default::default)
}
pub fn receive_frame<B: BufferMut, NonSyncCtx: BufferNonSyncContext<B>>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: &EthernetDeviceId<NonSyncCtx>,
buffer: B,
) {
trace_duration!(ctx, "device::receive_frame");
self::ethernet::receive_frame(&mut Locked::new(sync_ctx), ctx, device, buffer)
}
#[allow(dead_code)]
pub(crate) fn set_promiscuous_mode<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
enabled: bool,
) -> Result<(), NotSupportedError> {
match device {
DeviceId::Ethernet(id) => {
Ok(self::ethernet::set_promiscuous_mode(&mut Locked::new(sync_ctx), ctx, id, enabled))
}
DeviceId::Loopback(LoopbackDeviceId(_)) => Err(NotSupportedError),
}
}
pub(crate) fn add_ip_addr_subnet<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
addr_sub_and_config: impl Into<AddrSubnetAndManualConfigEither<NonSyncCtx::Instant>>,
) -> Result<(), ExistsError> {
let addr_sub_and_config = addr_sub_and_config.into();
trace!(
"add_ip_addr_subnet: adding addr_sub_and_config {:?} to device {:?}",
addr_sub_and_config,
device
);
let mut sync_ctx = Locked::new(sync_ctx);
match addr_sub_and_config {
AddrSubnetAndManualConfigEither::V4(addr_sub, config) => {
crate::ip::device::add_ipv4_addr_subnet(&mut sync_ctx, ctx, device, addr_sub, config)
}
AddrSubnetAndManualConfigEither::V6(addr_sub, config) => {
crate::ip::device::add_ipv6_addr_subnet(&mut sync_ctx, ctx, device, addr_sub, config)
}
}
}
pub fn set_ip_addr_properties<NonSyncCtx: NonSyncContext, A: IpAddress>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
address: SpecifiedAddr<A>,
next_valid_until: Lifetime<NonSyncCtx::Instant>,
) -> Result<(), SetIpAddressPropertiesError> {
trace!(
"set_ip_addr_properties: setting valid_until={:?} for addr={}",
next_valid_until,
address
);
let mut sync_ctx = Locked::new(sync_ctx);
match address.into() {
IpAddr::V4(address) => crate::ip::device::set_ipv4_addr_properties(
&mut sync_ctx,
ctx,
device,
address,
next_valid_until,
),
IpAddr::V6(address) => crate::ip::device::set_ipv6_addr_properties(
&mut sync_ctx,
ctx,
device,
address,
next_valid_until,
),
}
}
pub(crate) fn del_ip_addr<NonSyncCtx: NonSyncContext, A: IpAddress>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
addr: &SpecifiedAddr<A>,
) -> Result<(), NotFoundError> {
trace!("del_ip_addr: removing addr {:?} from device {:?}", addr, device);
let mut sync_ctx = Locked::new(sync_ctx);
match Into::into(*addr) {
IpAddr::V4(addr) => crate::ip::device::del_ipv4_addr(&mut sync_ctx, ctx, &device, &addr),
IpAddr::V6(addr) => crate::ip::device::del_ipv6_addr_with_reason(
&mut sync_ctx,
ctx,
&device,
DelIpv6Addr::SpecifiedAddr(addr),
crate::ip::device::state::DelIpv6AddrReason::ManualAction,
),
}
}
impl<NonSyncCtx: NonSyncContext, L> DeviceIdContext<AnyDevice> for Locked<&SyncCtx<NonSyncCtx>, L> {
type DeviceId = DeviceId<NonSyncCtx>;
type WeakDeviceId = WeakDeviceId<NonSyncCtx>;
fn downgrade_device_id(&self, device_id: &DeviceId<NonSyncCtx>) -> WeakDeviceId<NonSyncCtx> {
device_id.downgrade()
}
fn upgrade_weak_device_id(
&self,
weak_device_id: &WeakDeviceId<NonSyncCtx>,
) -> Option<DeviceId<NonSyncCtx>> {
weak_device_id.upgrade()
}
}
pub fn insert_static_neighbor_entry<
I: Ip,
B: BufferMut,
Id,
C: BufferNonSyncContext<B> + crate::TimerContext<Id>,
>(
sync_ctx: &SyncCtx<C>,
ctx: &mut C,
device: &DeviceId<C>,
addr: I::Addr,
mac: Mac,
) -> Result<(), StaticNeighborInsertionError> {
let IpInvariant(result) = I::map_ip(
(IpInvariant((sync_ctx, ctx, device, mac)), addr),
|(IpInvariant((sync_ctx, ctx, device, mac)), addr)| {
let result = UnicastAddr::new(mac)
.ok_or(StaticNeighborInsertionError::AddressNotUnicast)
.and_then(|mac| {
insert_static_arp_table_entry(sync_ctx, ctx, device, addr, mac)
.map_err(StaticNeighborInsertionError::NotSupported)
});
IpInvariant(result)
},
|(IpInvariant((sync_ctx, ctx, device, mac)), addr)| {
let result = UnicastAddr::new(addr)
.ok_or(StaticNeighborInsertionError::AddressNotUnicast)
.and_then(|addr| {
insert_ndp_table_entry(sync_ctx, ctx, device, addr, mac)
.map_err(StaticNeighborInsertionError::NotSupported)
});
IpInvariant(result)
},
);
result
}
pub fn insert_static_arp_table_entry<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
addr: Ipv4Addr,
mac: UnicastAddr<Mac>,
) -> Result<(), NotSupportedError> {
match device {
DeviceId::Ethernet(id) => Ok(self::ethernet::insert_static_arp_table_entry(
&mut Locked::new(sync_ctx),
ctx,
id,
addr,
mac.into(),
)),
DeviceId::Loopback(LoopbackDeviceId(_)) => Err(NotSupportedError),
}
}
pub fn insert_ndp_table_entry<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
addr: UnicastAddr<Ipv6Addr>,
mac: Mac,
) -> Result<(), NotSupportedError> {
match device {
DeviceId::Ethernet(id) => Ok(self::ethernet::insert_ndp_table_entry(
&mut Locked::new(sync_ctx),
ctx,
id,
addr,
mac,
)),
DeviceId::Loopback(LoopbackDeviceId(_)) => Err(NotSupportedError),
}
}
pub fn get_ipv4_configuration_and_flags<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
device: &DeviceId<NonSyncCtx>,
) -> Ipv4DeviceConfigurationAndFlags {
crate::ip::device::get_ipv4_configuration_and_flags(&mut Locked::new(sync_ctx), device)
}
pub fn get_ipv6_configuration_and_flags<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
device: &DeviceId<NonSyncCtx>,
) -> Ipv6DeviceConfigurationAndFlags {
crate::ip::device::get_ipv6_configuration_and_flags(&mut Locked::new(sync_ctx), device)
}
pub fn update_ipv4_configuration<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
config: Ipv4DeviceConfigurationUpdate,
) -> Result<Ipv4DeviceConfigurationUpdate, NotSupportedError> {
crate::ip::device::update_ipv4_configuration(&mut Locked::new(sync_ctx), ctx, device, config)
}
pub fn update_ipv6_configuration<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
config: Ipv6DeviceConfigurationUpdate,
) -> Result<Ipv6DeviceConfigurationUpdate, NotSupportedError> {
crate::ip::device::update_ipv6_configuration(&mut Locked::new(sync_ctx), ctx, device, config)
}
#[cfg(any(test, feature = "testutils"))]
pub(crate) mod testutil {
use super::*;
#[cfg(test)]
use net_types::ip::IpVersion;
use crate::ip::device::IpDeviceConfigurationUpdate;
#[cfg(test)]
use crate::testutil::Ctx;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub(crate) struct FakeWeakDeviceId<D>(pub(crate) D);
impl<D: PartialEq> PartialEq<D> for FakeWeakDeviceId<D> {
fn eq(&self, other: &D) -> bool {
let Self(this) = self;
this == other
}
}
impl<D: StrongId<Weak = Self>> WeakId for FakeWeakDeviceId<D> {
type Strong = D;
}
impl<D: Id> Id for FakeWeakDeviceId<D> {
fn is_loopback(&self) -> bool {
let Self(inner) = self;
inner.is_loopback()
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
pub(crate) struct FakeDeviceId;
impl StrongId for FakeDeviceId {
type Weak = FakeWeakDeviceId<Self>;
}
impl Id for FakeDeviceId {
fn is_loopback(&self) -> bool {
false
}
}
pub(crate) trait FakeStrongDeviceId:
StrongId<Weak = FakeWeakDeviceId<Self>> + 'static + Ord
{
}
impl<D: StrongId<Weak = FakeWeakDeviceId<Self>> + 'static + Ord> FakeStrongDeviceId for D {}
#[cfg(test)]
pub(crate) fn receive_frame<B: BufferMut, NonSyncCtx: BufferNonSyncContext<B>>(
Ctx { sync_ctx, non_sync_ctx }: &mut Ctx<NonSyncCtx>,
device: EthernetDeviceId<NonSyncCtx>,
buffer: B,
) {
crate::device::receive_frame(sync_ctx, non_sync_ctx, &device, buffer)
}
pub fn enable_device<NonSyncCtx: NonSyncContext>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
) {
let ip_config =
Some(IpDeviceConfigurationUpdate { ip_enabled: Some(true), ..Default::default() });
let _: Ipv4DeviceConfigurationUpdate = update_ipv4_configuration(
sync_ctx,
ctx,
device,
Ipv4DeviceConfigurationUpdate { ip_config, ..Default::default() },
)
.unwrap();
let _: Ipv6DeviceConfigurationUpdate = update_ipv6_configuration(
sync_ctx,
ctx,
device,
Ipv6DeviceConfigurationUpdate { ip_config, ..Default::default() },
)
.unwrap();
}
#[cfg(test)]
pub(crate) fn set_forwarding_enabled<NonSyncCtx: NonSyncContext, I: Ip>(
sync_ctx: &SyncCtx<NonSyncCtx>,
ctx: &mut NonSyncCtx,
device: &DeviceId<NonSyncCtx>,
enabled: bool,
) -> Result<(), NotSupportedError> {
let ip_config = Some(IpDeviceConfigurationUpdate {
forwarding_enabled: Some(enabled),
..Default::default()
});
match I::VERSION {
IpVersion::V4 => {
let _: Ipv4DeviceConfigurationUpdate = update_ipv4_configuration(
sync_ctx,
ctx,
device,
Ipv4DeviceConfigurationUpdate { ip_config, ..Default::default() },
)
.unwrap();
}
IpVersion::V6 => {
let _: Ipv6DeviceConfigurationUpdate = update_ipv6_configuration(
sync_ctx,
ctx,
device,
Ipv6DeviceConfigurationUpdate { ip_config, ..Default::default() },
)
.unwrap();
}
}
Ok(())
}
#[cfg(test)]
pub(crate) fn is_forwarding_enabled<NonSyncCtx: NonSyncContext, I: Ip>(
sync_ctx: &SyncCtx<NonSyncCtx>,
device: &DeviceId<NonSyncCtx>,
) -> bool {
let mut sync_ctx = Locked::new(sync_ctx);
match I::VERSION {
IpVersion::V4 => {
crate::ip::device::is_ip_forwarding_enabled::<Ipv4, _, _>(&mut sync_ctx, device)
}
IpVersion::V6 => {
crate::ip::device::is_ip_forwarding_enabled::<Ipv6, _, _>(&mut sync_ctx, device)
}
}
}
#[cfg(test)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd)]
pub(crate) enum MultipleDevicesId {
A,
B,
C,
}
#[cfg(test)]
impl MultipleDevicesId {
pub(crate) fn all() -> [Self; 3] {
[Self::A, Self::B, Self::C]
}
}
#[cfg(test)]
impl Id for MultipleDevicesId {
fn is_loopback(&self) -> bool {
false
}
}
#[cfg(test)]
impl StrongId for MultipleDevicesId {
type Weak = FakeWeakDeviceId<Self>;
}
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use const_unwrap::const_unwrap_option;
use net_declare::net_mac;
use test_case::test_case;
use super::*;
use crate::{
ip::device::{slaac::SlaacConfiguration, IpDeviceConfigurationUpdate},
testutil::{
Ctx, TestIpExt as _, DEFAULT_INTERFACE_METRIC, IPV6_MIN_IMPLIED_MAX_FRAME_SIZE,
},
};
#[test]
fn test_origin_tracker() {
let tracker = OriginTracker::new();
if cfg!(debug_assertions) {
assert_ne!(tracker, OriginTracker::new());
} else {
assert_eq!(tracker, OriginTracker::new());
}
assert_eq!(tracker.clone(), tracker);
}
#[test]
fn frame_destination_from_dest() {
const LOCAL_ADDR: Mac = net_mac!("88:88:88:88:88:88");
assert_eq!(
FrameDestination::from_dest(
UnicastAddr::new(net_mac!("00:11:22:33:44:55")).unwrap().get(),
LOCAL_ADDR
),
FrameDestination::Individual { local: false }
);
assert_eq!(
FrameDestination::from_dest(LOCAL_ADDR, LOCAL_ADDR),
FrameDestination::Individual { local: true }
);
assert_eq!(
FrameDestination::from_dest(Mac::BROADCAST, LOCAL_ADDR),
FrameDestination::Broadcast,
);
assert_eq!(
FrameDestination::from_dest(
MulticastAddr::new(net_mac!("11:11:11:11:11:11")).unwrap().get(),
LOCAL_ADDR
),
FrameDestination::Multicast
);
}
#[test]
fn test_no_default_routes() {
let Ctx { sync_ctx, non_sync_ctx: _ } = crate::testutil::FakeCtx::default();
let _loopback_device: LoopbackDeviceId<_> =
crate::device::add_loopback_device(&sync_ctx, Mtu::new(55), DEFAULT_INTERFACE_METRIC)
.expect("error adding loopback device");
assert_eq!(crate::ip::get_all_routes(&sync_ctx), []);
let _ethernet_device: EthernetDeviceId<_> = crate::device::add_ethernet_device(
&sync_ctx,
UnicastAddr::new(net_mac!("aa:bb:cc:dd:ee:ff")).expect("MAC is unicast"),
ethernet::MaxFrameSize::MIN,
DEFAULT_INTERFACE_METRIC,
);
assert_eq!(crate::ip::get_all_routes(&sync_ctx), []);
}
#[test]
fn remove_ethernet_device_disables_timers() {
let Ctx { sync_ctx, mut non_sync_ctx } = crate::testutil::FakeCtx::default();
let ethernet_device = crate::device::add_ethernet_device(
&sync_ctx,
UnicastAddr::new(net_mac!("aa:bb:cc:dd:ee:ff")).expect("MAC is unicast"),
ethernet::MaxFrameSize::from_mtu(Mtu::new(1500)).unwrap(),
DEFAULT_INTERFACE_METRIC,
);
{
let device = ethernet_device.clone().into();
let ip_config = Some(IpDeviceConfigurationUpdate {
ip_enabled: Some(true),
gmp_enabled: Some(true),
..Default::default()
});
let _: Ipv4DeviceConfigurationUpdate = update_ipv4_configuration(
&sync_ctx,
&mut non_sync_ctx,
&device,
Ipv4DeviceConfigurationUpdate { ip_config, ..Default::default() },
)
.unwrap();
let _: Ipv6DeviceConfigurationUpdate = update_ipv6_configuration(
&sync_ctx,
&mut non_sync_ctx,
&device,
Ipv6DeviceConfigurationUpdate {
max_router_solicitations: Some(Some(const_unwrap_option(NonZeroU8::new(2)))),
slaac_config: Some(SlaacConfiguration {
enable_stable_addresses: true,
..Default::default()
}),
ip_config,
..Default::default()
},
)
.unwrap();
}
crate::device::remove_ethernet_device(&sync_ctx, &mut non_sync_ctx, ethernet_device)
.into_removed();
assert_eq!(non_sync_ctx.timer_ctx().timers(), &[]);
}
fn add_ethernet(
sync_ctx: &mut &crate::testutil::FakeSyncCtx,
_non_sync_ctx: &mut crate::testutil::FakeNonSyncCtx,
) -> DeviceId<crate::testutil::FakeNonSyncCtx> {
crate::device::add_ethernet_device(
sync_ctx,
Ipv6::FAKE_CONFIG.local_mac,
IPV6_MIN_IMPLIED_MAX_FRAME_SIZE,
DEFAULT_INTERFACE_METRIC,
)
.into()
}
fn add_loopback(
sync_ctx: &mut &crate::testutil::FakeSyncCtx,
non_sync_ctx: &mut crate::testutil::FakeNonSyncCtx,
) -> DeviceId<crate::testutil::FakeNonSyncCtx> {
let device = crate::device::add_loopback_device(
sync_ctx,
Ipv6::MINIMUM_LINK_MTU,
DEFAULT_INTERFACE_METRIC,
)
.unwrap()
.into();
crate::device::add_ip_addr_subnet(
sync_ctx,
non_sync_ctx,
&device,
AddrSubnet::from_witness(Ipv6::LOOPBACK_ADDRESS, Ipv6::LOOPBACK_SUBNET.prefix())
.unwrap(),
)
.unwrap();
device
}
fn check_transmitted_ethernet(
non_sync_ctx: &mut crate::testutil::FakeNonSyncCtx,
_device_id: &DeviceId<crate::testutil::FakeNonSyncCtx>,
count: usize,
) {
assert_eq!(non_sync_ctx.frames_sent().len(), count);
}
fn check_transmitted_loopback(
non_sync_ctx: &mut crate::testutil::FakeNonSyncCtx,
device_id: &DeviceId<crate::testutil::FakeNonSyncCtx>,
count: usize,
) {
let rx_available = core::mem::take(&mut non_sync_ctx.state_mut().rx_available);
if count == 0 {
assert_eq!(rx_available, <[LoopbackDeviceId::<_>; 0]>::default());
} else {
assert_eq!(
rx_available.into_iter().map(DeviceId::Loopback).collect::<Vec<_>>(),
[device_id.clone()]
);
}
}
#[test_case(add_ethernet, check_transmitted_ethernet, true; "ethernet with queue")]
#[test_case(add_ethernet, check_transmitted_ethernet, false; "ethernet without queue")]
#[test_case(add_loopback, check_transmitted_loopback, true; "loopback with queue")]
#[test_case(add_loopback, check_transmitted_loopback, false; "loopback without queue")]
fn tx_queue(
add_device: fn(
&mut &crate::testutil::FakeSyncCtx,
&mut crate::testutil::FakeNonSyncCtx,
) -> DeviceId<crate::testutil::FakeNonSyncCtx>,
check_transmitted: fn(
&mut crate::testutil::FakeNonSyncCtx,
&DeviceId<crate::testutil::FakeNonSyncCtx>,
usize,
),
with_tx_queue: bool,
) {
let Ctx { sync_ctx, mut non_sync_ctx } = crate::testutil::FakeCtx::default();
let mut sync_ctx = &sync_ctx;
let device = add_device(&mut sync_ctx, &mut non_sync_ctx);
if with_tx_queue {
crate::device::set_tx_queue_configuration(
&sync_ctx,
&mut non_sync_ctx,
&device,
TransmitQueueConfiguration::Fifo,
);
}
let _: Ipv6DeviceConfigurationUpdate = update_ipv6_configuration(
&sync_ctx,
&mut non_sync_ctx,
&device,
Ipv6DeviceConfigurationUpdate {
dad_transmits: Some(Some(const_unwrap_option(NonZeroU8::new(1)))),
slaac_config: Some(SlaacConfiguration {
enable_stable_addresses: true,
..Default::default()
}),
ip_config: Some(IpDeviceConfigurationUpdate {
ip_enabled: Some(true),
..Default::default()
}),
..Default::default()
},
)
.unwrap();
if with_tx_queue {
check_transmitted(&mut non_sync_ctx, &device, 0);
assert_eq!(
core::mem::take(&mut non_sync_ctx.state_mut().tx_available),
[device.clone()]
);
assert_eq!(
crate::device::transmit_queued_tx_frames(&sync_ctx, &mut non_sync_ctx, &device),
Ok(crate::WorkQueueReport::AllDone)
);
}
check_transmitted(&mut non_sync_ctx, &device, 1);
assert_eq!(non_sync_ctx.state_mut().tx_available, <[DeviceId::<_>; 0]>::default());
}
}