use alloc::vec::Vec;
use core::fmt::Debug;
use core::num::NonZeroU32;
use lock_order::lock::{OrderedLockAccess, OrderedLockRef};
use const_unwrap::const_unwrap_option;
use log::{debug, trace};
use net_types::ethernet::Mac;
use net_types::ip::{GenericOverIp, Ip, IpMarked, Ipv4, Ipv6, Mtu};
use net_types::{MulticastAddr, UnicastAddr, Witness};
use netstack3_base::ref_counted_hash_map::{InsertResult, RefCountedHashSet, RemoveResult};
use netstack3_base::sync::{Mutex, RwLock};
use netstack3_base::{
trace_duration, BroadcastIpExt, CoreTimerContext, Device, DeviceIdContext, FrameDestination,
HandleableTimer, LinkDevice, NestedIntoCoreTimerCtx, ReceivableFrameMeta, RecvFrameContext,
RecvIpFrameMeta, ResourceCounterContext, RngContext, SendFrameError, SendFrameErrorReason,
SendableFrameMeta, TimerContext, TimerHandler, TracingContext, WeakDeviceIdentifier,
WrapBroadcastMarker,
};
use netstack3_ip::nud::{
LinkResolutionContext, NudBindingsTypes, NudHandler, NudState, NudTimerId, NudUserConfig,
};
use netstack3_ip::{DeviceIpLayerMetadata, IpPacketDestination};
use packet::{Buf, BufferMut, Serializer};
use packet_formats::arp::{peek_arp_types, ArpHardwareType, ArpNetworkType};
use packet_formats::ethernet::{
EtherType, EthernetFrame, EthernetFrameBuilder, EthernetFrameLengthCheck, EthernetIpExt,
ETHERNET_HDR_LEN_NO_TAG,
};
use crate::internal::arp::{ArpFrameMetadata, ArpPacketHandler, ArpState, ArpTimerId};
use crate::internal::base::{
DeviceCounters, DeviceLayerTypes, DeviceReceiveFrameSpec, EthernetDeviceCounters,
};
use crate::internal::id::{DeviceId, EthernetDeviceId};
use crate::internal::queue::tx::{
BufVecU8Allocator, TransmitQueue, TransmitQueueHandler, TransmitQueueState,
};
use crate::internal::queue::{DequeueState, TransmitQueueFrameError};
use crate::internal::socket::{
DeviceSocketHandler, DeviceSocketMetadata, DeviceSocketSendTypes, EthernetHeaderParams,
ReceivedFrame,
};
use crate::internal::state::{DeviceStateSpec, IpLinkDeviceState};
const ETHERNET_HDR_LEN_NO_TAG_U32: u32 = ETHERNET_HDR_LEN_NO_TAG as u32;
pub trait EthernetIpLinkDeviceBindingsContext:
RngContext + TimerContext + DeviceLayerTypes
{
}
impl<BC: RngContext + TimerContext + DeviceLayerTypes> EthernetIpLinkDeviceBindingsContext for BC {}
pub trait EthernetIpLinkDeviceStaticStateContext: DeviceIdContext<EthernetLinkDevice> {
fn with_static_ethernet_device_state<O, F: FnOnce(&StaticEthernetDeviceState) -> O>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O;
}
pub trait EthernetIpLinkDeviceDynamicStateContext<BC: EthernetIpLinkDeviceBindingsContext>:
EthernetIpLinkDeviceStaticStateContext
{
fn with_ethernet_state<
O,
F: FnOnce(&StaticEthernetDeviceState, &DynamicEthernetDeviceState) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O;
fn with_ethernet_state_mut<
O,
F: FnOnce(&StaticEthernetDeviceState, &mut DynamicEthernetDeviceState) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O;
}
pub fn send_as_ethernet_frame_to_dst<S, BC, CC>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
dst_mac: Mac,
body: S,
ether_type: EtherType,
) -> Result<(), SendFrameError<S>>
where
S: Serializer,
S::Buffer: BufferMut,
BC: EthernetIpLinkDeviceBindingsContext,
CC: EthernetIpLinkDeviceDynamicStateContext<BC>
+ TransmitQueueHandler<EthernetLinkDevice, BC, Meta = ()>
+ ResourceCounterContext<CC::DeviceId, DeviceCounters>,
{
const MIN_BODY_LEN: usize = 0;
let local_mac = get_mac(core_ctx, device_id);
let max_frame_size = get_max_frame_size(core_ctx, device_id);
let frame = body
.encapsulate(EthernetFrameBuilder::new(local_mac.get(), dst_mac, ether_type, MIN_BODY_LEN))
.with_size_limit(max_frame_size.into());
send_ethernet_frame(core_ctx, bindings_ctx, device_id, frame)
.map_err(|err| err.into_inner().into_inner())
}
fn send_ethernet_frame<S, BC, CC>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
frame: S,
) -> Result<(), SendFrameError<S>>
where
S: Serializer,
S::Buffer: BufferMut,
BC: EthernetIpLinkDeviceBindingsContext,
CC: EthernetIpLinkDeviceDynamicStateContext<BC>
+ TransmitQueueHandler<EthernetLinkDevice, BC, Meta = ()>
+ ResourceCounterContext<CC::DeviceId, DeviceCounters>,
{
core_ctx.increment(device_id, |counters| &counters.send_total_frames);
match TransmitQueueHandler::<EthernetLinkDevice, _>::queue_tx_frame(
core_ctx,
bindings_ctx,
device_id,
(),
frame,
) {
Ok(()) => {
core_ctx.increment(device_id, |counters| &counters.send_frame);
Ok(())
}
Err(TransmitQueueFrameError::NoQueue(err)) => {
core_ctx.increment(device_id, |counters| &counters.send_dropped_no_queue);
debug!("device {device_id:?} failed to send frame: {err:?}.");
Ok(())
}
Err(TransmitQueueFrameError::QueueFull(serializer)) => {
core_ctx.increment(device_id, |counters| &counters.send_queue_full);
Err(SendFrameError { serializer, error: SendFrameErrorReason::QueueFull })
}
Err(TransmitQueueFrameError::SerializeError(err)) => {
core_ctx.increment(device_id, |counters| &counters.send_serialize_error);
Err(err.err_into())
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct MaxEthernetFrameSize(NonZeroU32);
impl MaxEthernetFrameSize {
pub const MIN: MaxEthernetFrameSize =
MaxEthernetFrameSize(const_unwrap_option(NonZeroU32::new(60)));
pub const fn new(frame_size: u32) -> Option<Self> {
if frame_size < Self::MIN.get().get() {
return None;
}
Some(Self(const_unwrap_option(NonZeroU32::new(frame_size))))
}
const fn get(&self) -> NonZeroU32 {
let Self(frame_size) = *self;
frame_size
}
pub const fn as_mtu(&self) -> Mtu {
Mtu::new(self.get().get().saturating_sub(ETHERNET_HDR_LEN_NO_TAG_U32))
}
pub const fn from_mtu(mtu: Mtu) -> Option<MaxEthernetFrameSize> {
let frame_size = mtu.get().saturating_add(ETHERNET_HDR_LEN_NO_TAG_U32);
Self::new(frame_size)
}
}
impl From<MaxEthernetFrameSize> for usize {
fn from(MaxEthernetFrameSize(v): MaxEthernetFrameSize) -> Self {
v.get().try_into().expect("u32 doesn't fit in usize")
}
}
#[derive(Debug)]
pub struct EthernetCreationProperties {
pub mac: UnicastAddr<Mac>,
pub max_frame_size: MaxEthernetFrameSize,
}
pub struct DynamicEthernetDeviceState {
max_frame_size: MaxEthernetFrameSize,
link_multicast_groups: RefCountedHashSet<MulticastAddr<Mac>>,
}
impl DynamicEthernetDeviceState {
fn new(max_frame_size: MaxEthernetFrameSize) -> Self {
Self { max_frame_size, link_multicast_groups: Default::default() }
}
}
pub struct StaticEthernetDeviceState {
mac: UnicastAddr<Mac>,
max_frame_size: MaxEthernetFrameSize,
}
pub struct EthernetDeviceState<BT: NudBindingsTypes<EthernetLinkDevice>> {
pub counters: EthernetDeviceCounters,
pub static_state: StaticEthernetDeviceState,
pub tx_queue: TransmitQueue<(), Buf<Vec<u8>>, BufVecU8Allocator>,
ipv4_arp: Mutex<ArpState<EthernetLinkDevice, BT>>,
ipv6_nud: Mutex<NudState<Ipv6, EthernetLinkDevice, BT>>,
ipv4_nud_config: RwLock<IpMarked<Ipv4, NudUserConfig>>,
ipv6_nud_config: RwLock<IpMarked<Ipv6, NudUserConfig>>,
dynamic_state: RwLock<DynamicEthernetDeviceState>,
}
impl<BT: DeviceLayerTypes, I: Ip> OrderedLockAccess<IpMarked<I, NudUserConfig>>
for IpLinkDeviceState<EthernetLinkDevice, BT>
{
type Lock = RwLock<IpMarked<I, NudUserConfig>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(I::map_ip(
(),
|()| &self.link.ipv4_nud_config,
|()| &self.link.ipv6_nud_config,
))
}
}
impl<BT: DeviceLayerTypes> OrderedLockAccess<DynamicEthernetDeviceState>
for IpLinkDeviceState<EthernetLinkDevice, BT>
{
type Lock = RwLock<DynamicEthernetDeviceState>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.link.dynamic_state)
}
}
impl<BT: DeviceLayerTypes> OrderedLockAccess<NudState<Ipv6, EthernetLinkDevice, BT>>
for IpLinkDeviceState<EthernetLinkDevice, BT>
{
type Lock = Mutex<NudState<Ipv6, EthernetLinkDevice, BT>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.link.ipv6_nud)
}
}
impl<BT: DeviceLayerTypes> OrderedLockAccess<ArpState<EthernetLinkDevice, BT>>
for IpLinkDeviceState<EthernetLinkDevice, BT>
{
type Lock = Mutex<ArpState<EthernetLinkDevice, BT>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.link.ipv4_arp)
}
}
impl<BT: DeviceLayerTypes>
OrderedLockAccess<TransmitQueueState<(), Buf<Vec<u8>>, BufVecU8Allocator>>
for IpLinkDeviceState<EthernetLinkDevice, BT>
{
type Lock = Mutex<TransmitQueueState<(), Buf<Vec<u8>>, BufVecU8Allocator>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.link.tx_queue.queue)
}
}
impl<BT: DeviceLayerTypes> OrderedLockAccess<DequeueState<(), Buf<Vec<u8>>>>
for IpLinkDeviceState<EthernetLinkDevice, BT>
{
type Lock = Mutex<DequeueState<(), Buf<Vec<u8>>>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.link.tx_queue.deque)
}
}
#[derive(Clone, Eq, PartialEq, Debug, Hash, GenericOverIp)]
#[generic_over_ip()]
#[allow(missing_docs)]
pub enum EthernetTimerId<D: WeakDeviceIdentifier> {
Arp(ArpTimerId<EthernetLinkDevice, D>),
Nudv6(NudTimerId<Ipv6, EthernetLinkDevice, D>),
}
impl<I: Ip, D: WeakDeviceIdentifier> From<NudTimerId<I, EthernetLinkDevice, D>>
for EthernetTimerId<D>
{
fn from(id: NudTimerId<I, EthernetLinkDevice, D>) -> EthernetTimerId<D> {
I::map_ip(id, EthernetTimerId::Arp, EthernetTimerId::Nudv6)
}
}
impl<CC, BC> HandleableTimer<CC, BC> for EthernetTimerId<CC::WeakDeviceId>
where
BC: EthernetIpLinkDeviceBindingsContext,
CC: EthernetIpLinkDeviceDynamicStateContext<BC>
+ TimerHandler<BC, NudTimerId<Ipv6, EthernetLinkDevice, CC::WeakDeviceId>>
+ TimerHandler<BC, ArpTimerId<EthernetLinkDevice, CC::WeakDeviceId>>,
{
fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, timer: BC::UniqueTimerId) {
match self {
EthernetTimerId::Arp(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
EthernetTimerId::Nudv6(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
}
}
}
pub fn send_ip_frame<BC, CC, I, S>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
destination: IpPacketDestination<I, &DeviceId<BC>>,
body: S,
) -> Result<(), SendFrameError<S>>
where
BC: EthernetIpLinkDeviceBindingsContext + LinkResolutionContext<EthernetLinkDevice>,
CC: EthernetIpLinkDeviceDynamicStateContext<BC>
+ NudHandler<I, EthernetLinkDevice, BC>
+ TransmitQueueHandler<EthernetLinkDevice, BC, Meta = ()>
+ ResourceCounterContext<CC::DeviceId, DeviceCounters>,
I: EthernetIpExt + BroadcastIpExt,
S: Serializer,
S::Buffer: BufferMut,
{
core_ctx.increment(device_id, DeviceCounters::send_frame::<I>);
trace!("ethernet::send_ip_frame: destination = {:?}; device = {:?}", destination, device_id);
match destination {
IpPacketDestination::Broadcast(marker) => {
I::map_ip::<_, ()>(
WrapBroadcastMarker(marker),
|WrapBroadcastMarker(())| (),
|WrapBroadcastMarker(never)| match never {},
);
send_as_ethernet_frame_to_dst(
core_ctx,
bindings_ctx,
device_id,
Mac::BROADCAST,
body,
I::ETHER_TYPE,
)
}
IpPacketDestination::Multicast(multicast_ip) => send_as_ethernet_frame_to_dst(
core_ctx,
bindings_ctx,
device_id,
Mac::from(&multicast_ip),
body,
I::ETHER_TYPE,
),
IpPacketDestination::Neighbor(ip) => NudHandler::<I, _, _>::send_ip_packet_to_neighbor(
core_ctx,
bindings_ctx,
device_id,
ip,
body,
),
IpPacketDestination::Loopback(_) => {
unreachable!("Loopback packets must be delivered through the loopback device")
}
}
}
pub struct RecvEthernetFrameMeta<D> {
pub device_id: D,
}
impl DeviceReceiveFrameSpec for EthernetLinkDevice {
type FrameMetadata<D> = RecvEthernetFrameMeta<D>;
}
impl<CC, BC> ReceivableFrameMeta<CC, BC> for RecvEthernetFrameMeta<CC::DeviceId>
where
BC: EthernetIpLinkDeviceBindingsContext + TracingContext,
CC: EthernetIpLinkDeviceDynamicStateContext<BC>
+ RecvFrameContext<RecvIpFrameMeta<CC::DeviceId, DeviceIpLayerMetadata, Ipv4>, BC>
+ RecvFrameContext<RecvIpFrameMeta<CC::DeviceId, DeviceIpLayerMetadata, Ipv6>, BC>
+ ArpPacketHandler<EthernetLinkDevice, BC>
+ DeviceSocketHandler<EthernetLinkDevice, BC>
+ ResourceCounterContext<CC::DeviceId, DeviceCounters>
+ ResourceCounterContext<CC::DeviceId, EthernetDeviceCounters>,
{
fn receive_meta<B: BufferMut + Debug>(
self,
core_ctx: &mut CC,
bindings_ctx: &mut BC,
mut buffer: B,
) {
trace_duration!(bindings_ctx, c"device::ethernet::receive_frame");
let Self { device_id } = self;
trace!("ethernet::receive_frame: device_id = {:?}", device_id);
core_ctx.increment(&device_id, |counters: &DeviceCounters| &counters.recv_frame);
let (ethernet, whole_frame) = if let Ok(frame) =
buffer.parse_with_view::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::NoCheck)
{
frame
} else {
core_ctx.increment(&device_id, |counters: &DeviceCounters| &counters.recv_parse_error);
trace!("ethernet::receive_frame: failed to parse ethernet frame");
return;
};
let dst = ethernet.dst_mac();
let frame_dst = core_ctx.with_static_ethernet_device_state(&device_id, |static_state| {
FrameDestination::from_dest(dst, static_state.mac.get())
});
let ethertype = ethernet.ethertype();
core_ctx.handle_frame(
bindings_ctx,
&device_id,
ReceivedFrame::from_ethernet(ethernet, frame_dst).into(),
whole_frame,
);
match ethertype {
Some(EtherType::Arp) => {
let types = if let Ok(types) = peek_arp_types(buffer.as_ref()) {
types
} else {
return;
};
match types {
(ArpHardwareType::Ethernet, ArpNetworkType::Ipv4) => {
ArpPacketHandler::handle_packet(
core_ctx,
bindings_ctx,
device_id,
frame_dst,
buffer,
)
}
}
}
Some(EtherType::Ipv4) => {
core_ctx.increment(&device_id, |counters: &DeviceCounters| {
&counters.recv_ipv4_delivered
});
core_ctx.receive_frame(
bindings_ctx,
RecvIpFrameMeta::<_, _, Ipv4>::new(
device_id,
Some(frame_dst),
DeviceIpLayerMetadata::default(),
),
buffer,
)
}
Some(EtherType::Ipv6) => {
core_ctx.increment(&device_id, |counters: &DeviceCounters| {
&counters.recv_ipv6_delivered
});
core_ctx.receive_frame(
bindings_ctx,
RecvIpFrameMeta::<_, _, Ipv6>::new(
device_id,
Some(frame_dst),
DeviceIpLayerMetadata::default(),
),
buffer,
)
}
Some(EtherType::Other(_)) => {
core_ctx.increment(&device_id, |counters: &EthernetDeviceCounters| {
&counters.recv_unsupported_ethertype
});
}
None => {
core_ctx.increment(&device_id, |counters: &EthernetDeviceCounters| {
&counters.recv_no_ethertype
});
}
}
}
}
pub fn join_link_multicast<
BC: EthernetIpLinkDeviceBindingsContext,
CC: EthernetIpLinkDeviceDynamicStateContext<BC>,
>(
core_ctx: &mut CC,
_bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
multicast_addr: MulticastAddr<Mac>,
) {
core_ctx.with_ethernet_state_mut(device_id, |_static_state, dynamic_state| {
let groups = &mut dynamic_state.link_multicast_groups;
match groups.insert(multicast_addr) {
InsertResult::Inserted(()) => {
trace!(
"ethernet::join_link_multicast: joining link multicast {:?}",
multicast_addr
);
}
InsertResult::AlreadyPresent => {
trace!(
"ethernet::join_link_multicast: already joined link multicast {:?}",
multicast_addr,
);
}
}
})
}
pub fn leave_link_multicast<
BC: EthernetIpLinkDeviceBindingsContext,
CC: EthernetIpLinkDeviceDynamicStateContext<BC>,
>(
core_ctx: &mut CC,
_bindings_ctx: &mut BC,
device_id: &CC::DeviceId,
multicast_addr: MulticastAddr<Mac>,
) {
core_ctx.with_ethernet_state_mut(device_id, |_static_state, dynamic_state| {
let groups = &mut dynamic_state.link_multicast_groups;
match groups.remove(multicast_addr) {
RemoveResult::Removed(()) => {
trace!("ethernet::leave_link_multicast: leaving link multicast {:?}", multicast_addr);
}
RemoveResult::StillPresent => {
trace!(
"ethernet::leave_link_multicast: not leaving link multicast {:?} as there are still listeners for it",
multicast_addr,
);
}
RemoveResult::NotPresent => {
panic!(
"ethernet::leave_link_multicast: device {:?} has not yet joined link multicast {:?}",
device_id,
multicast_addr,
);
}
}
})
}
pub fn get_max_frame_size<
BC: EthernetIpLinkDeviceBindingsContext,
CC: EthernetIpLinkDeviceDynamicStateContext<BC>,
>(
core_ctx: &mut CC,
device_id: &CC::DeviceId,
) -> MaxEthernetFrameSize {
core_ctx
.with_ethernet_state(device_id, |_static_state, dynamic_state| dynamic_state.max_frame_size)
}
pub fn get_mtu<
BC: EthernetIpLinkDeviceBindingsContext,
CC: EthernetIpLinkDeviceDynamicStateContext<BC>,
>(
core_ctx: &mut CC,
device_id: &CC::DeviceId,
) -> Mtu {
get_max_frame_size(core_ctx, device_id).as_mtu()
}
pub trait UseArpFrameMetadataBlanket {}
impl<
BC: EthernetIpLinkDeviceBindingsContext,
CC: EthernetIpLinkDeviceDynamicStateContext<BC>
+ TransmitQueueHandler<EthernetLinkDevice, BC, Meta = ()>
+ ResourceCounterContext<CC::DeviceId, DeviceCounters>
+ UseArpFrameMetadataBlanket,
> SendableFrameMeta<CC, BC> for ArpFrameMetadata<EthernetLinkDevice, CC::DeviceId>
{
fn send_meta<S>(
self,
core_ctx: &mut CC,
bindings_ctx: &mut BC,
body: S,
) -> Result<(), SendFrameError<S>>
where
S: Serializer,
S::Buffer: BufferMut,
{
let Self { device_id, dst_addr } = self;
send_as_ethernet_frame_to_dst(
core_ctx,
bindings_ctx,
&device_id,
dst_addr,
body,
EtherType::Arp,
)
}
}
impl DeviceSocketSendTypes for EthernetLinkDevice {
type Metadata = Option<EthernetHeaderParams>;
}
impl<
BC: EthernetIpLinkDeviceBindingsContext,
CC: EthernetIpLinkDeviceDynamicStateContext<BC>
+ TransmitQueueHandler<EthernetLinkDevice, BC, Meta = ()>
+ ResourceCounterContext<CC::DeviceId, DeviceCounters>,
> SendableFrameMeta<CC, BC> for DeviceSocketMetadata<EthernetLinkDevice, EthernetDeviceId<BC>>
where
CC: DeviceIdContext<EthernetLinkDevice, DeviceId = EthernetDeviceId<BC>>,
{
fn send_meta<S>(
self,
core_ctx: &mut CC,
bindings_ctx: &mut BC,
body: S,
) -> Result<(), SendFrameError<S>>
where
S: Serializer,
S::Buffer: BufferMut,
{
let Self { device_id, metadata } = self;
match metadata {
Some(EthernetHeaderParams { dest_addr, protocol }) => send_as_ethernet_frame_to_dst(
core_ctx,
bindings_ctx,
&device_id,
dest_addr,
body,
protocol,
),
None => send_ethernet_frame(core_ctx, bindings_ctx, &device_id, body),
}
}
}
pub fn get_mac<
'a,
BC: EthernetIpLinkDeviceBindingsContext,
CC: EthernetIpLinkDeviceDynamicStateContext<BC>,
>(
core_ctx: &'a mut CC,
device_id: &CC::DeviceId,
) -> UnicastAddr<Mac> {
core_ctx.with_static_ethernet_device_state(device_id, |state| state.mac)
}
pub fn set_mtu<
BC: EthernetIpLinkDeviceBindingsContext,
CC: EthernetIpLinkDeviceDynamicStateContext<BC>,
>(
core_ctx: &mut CC,
device_id: &CC::DeviceId,
mtu: Mtu,
) {
core_ctx.with_ethernet_state_mut(device_id, |static_state, dynamic_state| {
if let Some(mut frame_size ) = MaxEthernetFrameSize::from_mtu(mtu) {
if frame_size > static_state.max_frame_size {
trace!("ethernet::ndp_device::set_mtu: MTU of {:?} is greater than the device {:?}'s max MTU of {:?}, using device's max MTU instead", mtu, device_id, static_state.max_frame_size.as_mtu());
frame_size = static_state.max_frame_size;
}
trace!("ethernet::ndp_device::set_mtu: setting link MTU to {:?}", mtu);
dynamic_state.max_frame_size = frame_size;
}
})
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum EthernetLinkDevice {}
impl Device for EthernetLinkDevice {}
impl LinkDevice for EthernetLinkDevice {
type Address = Mac;
}
impl DeviceStateSpec for EthernetLinkDevice {
type Link<BT: DeviceLayerTypes> = EthernetDeviceState<BT>;
type External<BT: DeviceLayerTypes> = BT::EthernetDeviceState;
type CreationProperties = EthernetCreationProperties;
type Counters = EthernetDeviceCounters;
type TimerId<D: WeakDeviceIdentifier> = EthernetTimerId<D>;
fn new_link_state<
CC: CoreTimerContext<Self::TimerId<CC::WeakDeviceId>, BC> + DeviceIdContext<Self>,
BC: DeviceLayerTypes + TimerContext,
>(
bindings_ctx: &mut BC,
self_id: CC::WeakDeviceId,
EthernetCreationProperties { mac, max_frame_size }: Self::CreationProperties,
) -> Self::Link<BC> {
let ipv4_arp = Mutex::new(ArpState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(
bindings_ctx,
self_id.clone(),
));
let ipv6_nud =
Mutex::new(NudState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(bindings_ctx, self_id));
EthernetDeviceState {
counters: Default::default(),
ipv4_arp,
ipv6_nud,
ipv4_nud_config: Default::default(),
ipv6_nud_config: Default::default(),
static_state: StaticEthernetDeviceState { mac, max_frame_size },
dynamic_state: RwLock::new(DynamicEthernetDeviceState::new(max_frame_size)),
tx_queue: Default::default(),
}
}
const IS_LOOPBACK: bool = false;
const DEBUG_TYPE: &'static str = "Ethernet";
}
#[cfg(any(test, feature = "testutils"))]
pub(crate) mod testutil {
use super::*;
pub const IPV6_MIN_IMPLIED_MAX_FRAME_SIZE: MaxEthernetFrameSize =
const_unwrap::const_unwrap_option(MaxEthernetFrameSize::from_mtu(Ipv6::MINIMUM_LINK_MTU));
}
#[cfg(test)]
mod tests {
use alloc::vec;
use core::convert::Infallible as Never;
use net_types::ip::{Ipv4Addr, Ipv6Addr};
use net_types::SpecifiedAddr;
use netstack3_base::testutil::{FakeDeviceId, FakeInstant, FakeWeakDeviceId, TEST_ADDRS_V4};
use netstack3_base::{CounterContext, CtxPair, IntoCoreTimerCtx};
use netstack3_ip::nud::{
self, DelegateNudContext, DynamicNeighborUpdateSource, NeighborApi, UseDelegateNudContext,
};
use packet_formats::testutil::parse_ethernet_frame;
use super::*;
use crate::internal::arp::{
ArpConfigContext, ArpContext, ArpCounters, ArpNudCtx, ArpSenderContext,
};
use crate::internal::base::DeviceSendFrameError;
use crate::internal::ethernet::testutil::IPV6_MIN_IMPLIED_MAX_FRAME_SIZE;
use crate::internal::queue::tx::{
TransmitQueueBindingsContext, TransmitQueueCommon, TransmitQueueContext,
};
use crate::internal::socket::{Frame, ParseSentFrameError, SentFrame};
struct FakeEthernetCtx {
static_state: StaticEthernetDeviceState,
dynamic_state: DynamicEthernetDeviceState,
tx_queue: TransmitQueueState<(), Buf<Vec<u8>>, BufVecU8Allocator>,
counters: DeviceCounters,
per_device_counters: DeviceCounters,
ethernet_counters: EthernetDeviceCounters,
arp_counters: ArpCounters,
}
impl FakeEthernetCtx {
fn new(mac: UnicastAddr<Mac>, max_frame_size: MaxEthernetFrameSize) -> FakeEthernetCtx {
FakeEthernetCtx {
static_state: StaticEthernetDeviceState { max_frame_size, mac },
dynamic_state: DynamicEthernetDeviceState::new(max_frame_size),
tx_queue: Default::default(),
counters: Default::default(),
per_device_counters: Default::default(),
ethernet_counters: Default::default(),
arp_counters: Default::default(),
}
}
}
type FakeBindingsCtx = netstack3_base::testutil::FakeBindingsCtx<
EthernetTimerId<FakeWeakDeviceId<FakeDeviceId>>,
nud::Event<Mac, FakeDeviceId, Ipv4, FakeInstant>,
(),
(),
>;
type FakeInnerCtx =
netstack3_base::testutil::FakeCoreCtx<FakeEthernetCtx, FakeDeviceId, FakeDeviceId>;
struct FakeCoreCtx {
arp_state: ArpState<EthernetLinkDevice, FakeBindingsCtx>,
inner: FakeInnerCtx,
}
fn new_context() -> CtxPair<FakeCoreCtx, FakeBindingsCtx> {
CtxPair::with_default_bindings_ctx(|bindings_ctx| FakeCoreCtx {
arp_state: ArpState::new::<_, IntoCoreTimerCtx>(
bindings_ctx,
FakeWeakDeviceId(FakeDeviceId),
),
inner: FakeInnerCtx::with_state(FakeEthernetCtx::new(
TEST_ADDRS_V4.local_mac,
IPV6_MIN_IMPLIED_MAX_FRAME_SIZE,
)),
})
}
impl DeviceSocketHandler<EthernetLinkDevice, FakeBindingsCtx> for FakeCoreCtx {
fn handle_frame(
&mut self,
bindings_ctx: &mut FakeBindingsCtx,
device: &Self::DeviceId,
frame: Frame<&[u8]>,
whole_frame: &[u8],
) {
self.inner.handle_frame(bindings_ctx, device, frame, whole_frame)
}
}
impl CounterContext<DeviceCounters> for FakeCoreCtx {
fn with_counters<O, F: FnOnce(&DeviceCounters) -> O>(&self, cb: F) -> O {
cb(&self.inner.state.counters)
}
}
impl CounterContext<DeviceCounters> for FakeInnerCtx {
fn with_counters<O, F: FnOnce(&DeviceCounters) -> O>(&self, cb: F) -> O {
cb(&self.state.counters)
}
}
impl ResourceCounterContext<FakeDeviceId, DeviceCounters> for FakeCoreCtx {
fn with_per_resource_counters<O, F: FnOnce(&DeviceCounters) -> O>(
&mut self,
&FakeDeviceId: &FakeDeviceId,
cb: F,
) -> O {
cb(&self.inner.state.per_device_counters)
}
}
impl ResourceCounterContext<FakeDeviceId, DeviceCounters> for FakeInnerCtx {
fn with_per_resource_counters<O, F: FnOnce(&DeviceCounters) -> O>(
&mut self,
&FakeDeviceId: &FakeDeviceId,
cb: F,
) -> O {
cb(&self.state.per_device_counters)
}
}
impl CounterContext<EthernetDeviceCounters> for FakeCoreCtx {
fn with_counters<O, F: FnOnce(&EthernetDeviceCounters) -> O>(&self, cb: F) -> O {
cb(&self.inner.state.ethernet_counters)
}
}
impl CounterContext<EthernetDeviceCounters> for FakeInnerCtx {
fn with_counters<O, F: FnOnce(&EthernetDeviceCounters) -> O>(&self, cb: F) -> O {
cb(&self.state.ethernet_counters)
}
}
impl DeviceSocketHandler<EthernetLinkDevice, FakeBindingsCtx> for FakeInnerCtx {
fn handle_frame(
&mut self,
_bindings_ctx: &mut FakeBindingsCtx,
_device: &Self::DeviceId,
_frame: Frame<&[u8]>,
_whole_frame: &[u8],
) {
}
}
impl EthernetIpLinkDeviceStaticStateContext for FakeCoreCtx {
fn with_static_ethernet_device_state<O, F: FnOnce(&StaticEthernetDeviceState) -> O>(
&mut self,
device_id: &FakeDeviceId,
cb: F,
) -> O {
self.inner.with_static_ethernet_device_state(device_id, cb)
}
}
impl EthernetIpLinkDeviceStaticStateContext for FakeInnerCtx {
fn with_static_ethernet_device_state<O, F: FnOnce(&StaticEthernetDeviceState) -> O>(
&mut self,
&FakeDeviceId: &FakeDeviceId,
cb: F,
) -> O {
cb(&self.state.static_state)
}
}
impl EthernetIpLinkDeviceDynamicStateContext<FakeBindingsCtx> for FakeCoreCtx {
fn with_ethernet_state<
O,
F: FnOnce(&StaticEthernetDeviceState, &DynamicEthernetDeviceState) -> O,
>(
&mut self,
device_id: &FakeDeviceId,
cb: F,
) -> O {
self.inner.with_ethernet_state(device_id, cb)
}
fn with_ethernet_state_mut<
O,
F: FnOnce(&StaticEthernetDeviceState, &mut DynamicEthernetDeviceState) -> O,
>(
&mut self,
device_id: &FakeDeviceId,
cb: F,
) -> O {
self.inner.with_ethernet_state_mut(device_id, cb)
}
}
impl EthernetIpLinkDeviceDynamicStateContext<FakeBindingsCtx> for FakeInnerCtx {
fn with_ethernet_state<
O,
F: FnOnce(&StaticEthernetDeviceState, &DynamicEthernetDeviceState) -> O,
>(
&mut self,
&FakeDeviceId: &FakeDeviceId,
cb: F,
) -> O {
let FakeEthernetCtx { static_state, dynamic_state, .. } = &self.state;
cb(static_state, dynamic_state)
}
fn with_ethernet_state_mut<
O,
F: FnOnce(&StaticEthernetDeviceState, &mut DynamicEthernetDeviceState) -> O,
>(
&mut self,
&FakeDeviceId: &FakeDeviceId,
cb: F,
) -> O {
let FakeEthernetCtx { static_state, dynamic_state, .. } = &mut self.state;
cb(static_state, dynamic_state)
}
}
impl NudHandler<Ipv6, EthernetLinkDevice, FakeBindingsCtx> for FakeCoreCtx {
fn handle_neighbor_update(
&mut self,
_bindings_ctx: &mut FakeBindingsCtx,
_device_id: &Self::DeviceId,
_neighbor: SpecifiedAddr<Ipv6Addr>,
_link_addr: Mac,
_is_confirmation: DynamicNeighborUpdateSource,
) {
unimplemented!()
}
fn flush(&mut self, _bindings_ctx: &mut FakeBindingsCtx, _device_id: &Self::DeviceId) {
unimplemented!()
}
fn send_ip_packet_to_neighbor<S>(
&mut self,
_bindings_ctx: &mut FakeBindingsCtx,
_device_id: &Self::DeviceId,
_neighbor: SpecifiedAddr<Ipv6Addr>,
_body: S,
) -> Result<(), SendFrameError<S>> {
unimplemented!()
}
}
struct FakeCoreCtxWithDeviceId<'a> {
core_ctx: &'a mut FakeInnerCtx,
device_id: &'a FakeDeviceId,
}
impl<'a> DeviceIdContext<EthernetLinkDevice> for FakeCoreCtxWithDeviceId<'a> {
type DeviceId = FakeDeviceId;
type WeakDeviceId = FakeWeakDeviceId<FakeDeviceId>;
}
impl<'a> ArpConfigContext for FakeCoreCtxWithDeviceId<'a> {
fn with_nud_user_config<O, F: FnOnce(&NudUserConfig) -> O>(&mut self, cb: F) -> O {
cb(&NudUserConfig::default())
}
}
impl UseArpFrameMetadataBlanket for FakeCoreCtx {}
impl ArpContext<EthernetLinkDevice, FakeBindingsCtx> for FakeCoreCtx {
type ConfigCtx<'a> = FakeCoreCtxWithDeviceId<'a>;
type ArpSenderCtx<'a> = FakeCoreCtxWithDeviceId<'a>;
fn with_arp_state_mut_and_sender_ctx<
O,
F: FnOnce(
&mut ArpState<EthernetLinkDevice, FakeBindingsCtx>,
&mut Self::ArpSenderCtx<'_>,
) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
let Self { arp_state, inner } = self;
cb(arp_state, &mut FakeCoreCtxWithDeviceId { core_ctx: inner, device_id })
}
fn get_protocol_addr(
&mut self,
_bindings_ctx: &mut FakeBindingsCtx,
_device_id: &Self::DeviceId,
) -> Option<Ipv4Addr> {
unimplemented!()
}
fn get_hardware_addr(
&mut self,
_bindings_ctx: &mut FakeBindingsCtx,
_device_id: &Self::DeviceId,
) -> UnicastAddr<Mac> {
self.inner.state.static_state.mac
}
fn with_arp_state_mut<
O,
F: FnOnce(
&mut ArpState<EthernetLinkDevice, FakeBindingsCtx>,
&mut Self::ConfigCtx<'_>,
) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
let Self { arp_state, inner } = self;
cb(arp_state, &mut FakeCoreCtxWithDeviceId { core_ctx: inner, device_id })
}
fn with_arp_state<O, F: FnOnce(&ArpState<EthernetLinkDevice, FakeBindingsCtx>) -> O>(
&mut self,
FakeDeviceId: &Self::DeviceId,
cb: F,
) -> O {
cb(&mut self.arp_state)
}
}
impl UseDelegateNudContext for FakeCoreCtx {}
impl DelegateNudContext<Ipv4> for FakeCoreCtx {
type Delegate<T> = ArpNudCtx<T>;
}
impl ArpConfigContext for FakeInnerCtx {
fn with_nud_user_config<O, F: FnOnce(&NudUserConfig) -> O>(&mut self, cb: F) -> O {
cb(&NudUserConfig::default())
}
}
impl<'a> ArpSenderContext<EthernetLinkDevice, FakeBindingsCtx> for FakeCoreCtxWithDeviceId<'a> {
fn send_ip_packet_to_neighbor_link_addr<S>(
&mut self,
bindings_ctx: &mut FakeBindingsCtx,
link_addr: Mac,
body: S,
) -> Result<(), SendFrameError<S>>
where
S: Serializer,
S::Buffer: BufferMut,
{
let Self { core_ctx, device_id } = self;
send_as_ethernet_frame_to_dst(
*core_ctx,
bindings_ctx,
device_id,
link_addr,
body,
EtherType::Ipv4,
)
}
}
impl TransmitQueueBindingsContext<FakeDeviceId> for FakeBindingsCtx {
fn wake_tx_task(&mut self, FakeDeviceId: &FakeDeviceId) {
unimplemented!("unused by tests")
}
}
impl TransmitQueueCommon<EthernetLinkDevice, FakeBindingsCtx> for FakeCoreCtx {
type Meta = ();
type Allocator = BufVecU8Allocator;
type Buffer = Buf<Vec<u8>>;
type DequeueContext = Never;
fn parse_outgoing_frame<'a>(
buf: &'a [u8],
meta: &'a Self::Meta,
) -> Result<SentFrame<&'a [u8]>, ParseSentFrameError> {
FakeInnerCtx::parse_outgoing_frame(buf, meta)
}
}
impl TransmitQueueCommon<EthernetLinkDevice, FakeBindingsCtx> for FakeInnerCtx {
type Meta = ();
type Allocator = BufVecU8Allocator;
type Buffer = Buf<Vec<u8>>;
type DequeueContext = Never;
fn parse_outgoing_frame<'a, 'b>(
buf: &'a [u8],
(): &'b Self::Meta,
) -> Result<SentFrame<&'a [u8]>, ParseSentFrameError> {
SentFrame::try_parse_as_ethernet(buf)
}
}
impl TransmitQueueContext<EthernetLinkDevice, FakeBindingsCtx> for FakeCoreCtx {
fn with_transmit_queue_mut<
O,
F: FnOnce(&mut TransmitQueueState<Self::Meta, Self::Buffer, Self::Allocator>) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
self.inner.with_transmit_queue_mut(device_id, cb)
}
fn with_transmit_queue<
O,
F: FnOnce(&TransmitQueueState<Self::Meta, Self::Buffer, Self::Allocator>) -> O,
>(
&mut self,
device_id: &Self::DeviceId,
cb: F,
) -> O {
self.inner.with_transmit_queue(device_id, cb)
}
fn send_frame(
&mut self,
bindings_ctx: &mut FakeBindingsCtx,
device_id: &Self::DeviceId,
dequeue_context: Option<&mut Never>,
(): Self::Meta,
buf: Self::Buffer,
) -> Result<(), DeviceSendFrameError> {
TransmitQueueContext::send_frame(
&mut self.inner,
bindings_ctx,
device_id,
dequeue_context,
(),
buf,
)
}
}
impl TransmitQueueContext<EthernetLinkDevice, FakeBindingsCtx> for FakeInnerCtx {
fn with_transmit_queue_mut<
O,
F: FnOnce(&mut TransmitQueueState<Self::Meta, Self::Buffer, Self::Allocator>) -> O,
>(
&mut self,
_device_id: &Self::DeviceId,
cb: F,
) -> O {
cb(&mut self.state.tx_queue)
}
fn with_transmit_queue<
O,
F: FnOnce(&TransmitQueueState<Self::Meta, Self::Buffer, Self::Allocator>) -> O,
>(
&mut self,
_device_id: &Self::DeviceId,
cb: F,
) -> O {
cb(&self.state.tx_queue)
}
fn send_frame(
&mut self,
_bindings_ctx: &mut FakeBindingsCtx,
device_id: &Self::DeviceId,
dequeue_context: Option<&mut Never>,
(): Self::Meta,
buf: Self::Buffer,
) -> Result<(), DeviceSendFrameError> {
match dequeue_context {
Some(never) => match *never {},
None => (),
}
self.frames.push(device_id.clone(), buf.as_ref().to_vec());
Ok(())
}
}
impl DeviceIdContext<EthernetLinkDevice> for FakeCoreCtx {
type DeviceId = FakeDeviceId;
type WeakDeviceId = FakeWeakDeviceId<FakeDeviceId>;
}
impl DeviceIdContext<EthernetLinkDevice> for FakeInnerCtx {
type DeviceId = FakeDeviceId;
type WeakDeviceId = FakeWeakDeviceId<FakeDeviceId>;
}
impl CounterContext<ArpCounters> for FakeCoreCtx {
fn with_counters<O, F: FnOnce(&ArpCounters) -> O>(&self, cb: F) -> O {
cb(&self.inner.state.arp_counters)
}
}
#[test]
fn test_mtu() {
fn test(size: usize, expect_frames_sent: bool) {
let mut ctx = new_context();
NeighborApi::<Ipv4, EthernetLinkDevice, _>::new(ctx.as_mut())
.insert_static_entry(
&FakeDeviceId,
TEST_ADDRS_V4.remote_ip.get(),
TEST_ADDRS_V4.remote_mac.get(),
)
.unwrap();
let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
let result = send_ip_frame(
core_ctx,
bindings_ctx,
&FakeDeviceId,
IpPacketDestination::<Ipv4, _>::Neighbor(TEST_ADDRS_V4.remote_ip),
Buf::new(&mut vec![0; size], ..),
)
.map_err(|_serializer| ());
let sent_frames = core_ctx.inner.frames().len();
if expect_frames_sent {
assert_eq!(sent_frames, 1);
result.expect("should succeed");
} else {
assert_eq!(sent_frames, 0);
result.expect_err("should fail");
}
}
test(usize::try_from(u32::from(Ipv6::MINIMUM_LINK_MTU)).unwrap(), true);
test(usize::try_from(u32::from(Ipv6::MINIMUM_LINK_MTU)).unwrap() + 1, false);
}
#[test]
fn broadcast() {
let mut ctx = new_context();
let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
send_ip_frame(
core_ctx,
bindings_ctx,
&FakeDeviceId,
IpPacketDestination::<Ipv4, _>::Broadcast(()),
Buf::new(&mut vec![0; 100], ..),
)
.map_err(|_serializer| ())
.expect("send_ip_frame should succeed");
let sent_frames = core_ctx.inner.frames().len();
assert_eq!(sent_frames, 1);
let (FakeDeviceId, frame) = core_ctx.inner.frames()[0].clone();
let (_body, _src_mac, dst_mac, _ether_type) =
parse_ethernet_frame(&frame, EthernetFrameLengthCheck::NoCheck).unwrap();
assert_eq!(dst_mac, Mac::BROADCAST);
}
}