#[cfg(test)]
mod tests;
#[cfg(feature = "medium-ethernet")]
mod ethernet;
#[cfg(feature = "medium-ieee802154")]
mod ieee802154;
#[cfg(feature = "proto-ipv4")]
mod ipv4;
#[cfg(feature = "proto-ipv6")]
mod ipv6;
#[cfg(feature = "proto-sixlowpan")]
mod sixlowpan;
#[cfg(feature = "proto-igmp")]
mod igmp;
#[cfg(feature = "proto-igmp")]
pub use igmp::MulticastError;
use core::cmp;
use core::result::Result;
use heapless::{LinearMap, Vec};
#[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))]
use super::fragmentation::PacketAssemblerSet;
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
use super::neighbor::{Answer as NeighborAnswer, Cache as NeighborCache};
use super::socket_set::SocketSet;
use crate::config::{
FRAGMENTATION_BUFFER_SIZE, IFACE_MAX_ADDR_COUNT, IFACE_MAX_MULTICAST_GROUP_COUNT,
IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT,
};
use crate::iface::Routes;
use crate::phy::PacketMeta;
use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken};
use crate::rand::Rand;
#[cfg(feature = "socket-dns")]
use crate::socket::dns;
use crate::socket::*;
use crate::time::{Duration, Instant};
use crate::wire::*;
#[cfg(feature = "_proto-fragmentation")]
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) enum FragKey {
#[cfg(feature = "proto-ipv4-fragmentation")]
Ipv4(Ipv4FragKey),
#[cfg(feature = "proto-sixlowpan-fragmentation")]
Sixlowpan(SixlowpanFragKey),
}
pub(crate) struct FragmentsBuffer {
#[cfg(feature = "proto-sixlowpan")]
decompress_buf: [u8; sixlowpan::MAX_DECOMPRESSED_LEN],
#[cfg(feature = "_proto-fragmentation")]
pub(crate) assembler: PacketAssemblerSet<FragKey>,
#[cfg(feature = "_proto-fragmentation")]
reassembly_timeout: Duration,
}
#[cfg(not(feature = "_proto-fragmentation"))]
pub(crate) struct Fragmenter {}
#[cfg(not(feature = "_proto-fragmentation"))]
impl Fragmenter {
pub(crate) fn new() -> Self {
Self {}
}
}
#[cfg(feature = "_proto-fragmentation")]
pub(crate) struct Fragmenter {
buffer: [u8; FRAGMENTATION_BUFFER_SIZE],
packet_len: usize,
sent_bytes: usize,
#[cfg(feature = "proto-ipv4-fragmentation")]
ipv4: Ipv4Fragmenter,
#[cfg(feature = "proto-sixlowpan-fragmentation")]
sixlowpan: SixlowpanFragmenter,
}
#[cfg(feature = "proto-ipv4-fragmentation")]
pub(crate) struct Ipv4Fragmenter {
repr: Ipv4Repr,
#[cfg(feature = "medium-ethernet")]
dst_hardware_addr: EthernetAddress,
frag_offset: u16,
ident: u16,
}
#[cfg(feature = "proto-sixlowpan-fragmentation")]
pub(crate) struct SixlowpanFragmenter {
datagram_size: u16,
datagram_tag: u16,
datagram_offset: usize,
fragn_size: usize,
ll_dst_addr: Ieee802154Address,
ll_src_addr: Ieee802154Address,
}
#[cfg(feature = "_proto-fragmentation")]
impl Fragmenter {
pub(crate) fn new() -> Self {
Self {
buffer: [0u8; FRAGMENTATION_BUFFER_SIZE],
packet_len: 0,
sent_bytes: 0,
#[cfg(feature = "proto-ipv4-fragmentation")]
ipv4: Ipv4Fragmenter {
repr: Ipv4Repr {
src_addr: Ipv4Address::default(),
dst_addr: Ipv4Address::default(),
next_header: IpProtocol::Unknown(0),
payload_len: 0,
hop_limit: 0,
},
#[cfg(feature = "medium-ethernet")]
dst_hardware_addr: EthernetAddress::default(),
frag_offset: 0,
ident: 0,
},
#[cfg(feature = "proto-sixlowpan-fragmentation")]
sixlowpan: SixlowpanFragmenter {
datagram_size: 0,
datagram_tag: 0,
datagram_offset: 0,
fragn_size: 0,
ll_dst_addr: Ieee802154Address::Absent,
ll_src_addr: Ieee802154Address::Absent,
},
}
}
#[inline]
fn finished(&self) -> bool {
self.packet_len == self.sent_bytes
}
#[inline]
fn is_empty(&self) -> bool {
self.packet_len == 0
}
fn reset(&mut self) {
self.packet_len = 0;
self.sent_bytes = 0;
#[cfg(feature = "proto-ipv4-fragmentation")]
{
self.ipv4.repr = Ipv4Repr {
src_addr: Ipv4Address::default(),
dst_addr: Ipv4Address::default(),
next_header: IpProtocol::Unknown(0),
payload_len: 0,
hop_limit: 0,
};
#[cfg(feature = "medium-ethernet")]
{
self.ipv4.dst_hardware_addr = EthernetAddress::default();
}
}
#[cfg(feature = "proto-sixlowpan-fragmentation")]
{
self.sixlowpan.datagram_size = 0;
self.sixlowpan.datagram_tag = 0;
self.sixlowpan.fragn_size = 0;
self.sixlowpan.ll_dst_addr = Ieee802154Address::Absent;
self.sixlowpan.ll_src_addr = Ieee802154Address::Absent;
}
}
}
macro_rules! check {
($e:expr) => {
match $e {
Ok(x) => x,
Err(_) => {
#[cfg(not(feature = "defmt"))]
net_trace!(concat!("iface: malformed ", stringify!($e)));
#[cfg(feature = "defmt")]
net_trace!("iface: malformed");
return Default::default();
}
}
};
}
use check;
pub struct Interface {
inner: InterfaceInner,
fragments: FragmentsBuffer,
fragmenter: Fragmenter,
}
pub struct InterfaceInner {
caps: DeviceCapabilities,
now: Instant,
rand: Rand,
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
neighbor_cache: NeighborCache,
hardware_addr: HardwareAddress,
#[cfg(feature = "medium-ieee802154")]
sequence_no: u8,
#[cfg(feature = "medium-ieee802154")]
pan_id: Option<Ieee802154Pan>,
#[cfg(feature = "proto-ipv4-fragmentation")]
ipv4_id: u16,
#[cfg(feature = "proto-sixlowpan")]
sixlowpan_address_context:
Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT>,
#[cfg(feature = "proto-sixlowpan-fragmentation")]
tag: u16,
ip_addrs: Vec<IpCidr, IFACE_MAX_ADDR_COUNT>,
#[cfg(feature = "proto-ipv4")]
any_ip: bool,
routes: Routes,
#[cfg(feature = "proto-igmp")]
ipv4_multicast_groups: LinearMap<Ipv4Address, (), IFACE_MAX_MULTICAST_GROUP_COUNT>,
#[cfg(feature = "proto-igmp")]
igmp_report_state: IgmpReportState,
}
#[non_exhaustive]
pub struct Config {
pub random_seed: u64,
pub hardware_addr: HardwareAddress,
#[cfg(feature = "medium-ieee802154")]
pub pan_id: Option<Ieee802154Pan>,
}
impl Config {
pub fn new(hardware_addr: HardwareAddress) -> Self {
Config {
random_seed: 0,
hardware_addr,
#[cfg(feature = "medium-ieee802154")]
pan_id: None,
}
}
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(feature = "medium-ethernet")]
enum EthernetPacket<'a> {
#[cfg(feature = "proto-ipv4")]
Arp(ArpRepr),
Ip(IpPacket<'a>),
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) enum IpPacket<'a> {
#[cfg(feature = "proto-ipv4")]
Icmpv4((Ipv4Repr, Icmpv4Repr<'a>)),
#[cfg(feature = "proto-igmp")]
Igmp((Ipv4Repr, IgmpRepr)),
#[cfg(feature = "proto-ipv6")]
Icmpv6((Ipv6Repr, Icmpv6Repr<'a>)),
#[cfg(feature = "socket-raw")]
Raw((IpRepr, &'a [u8])),
#[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
Udp((IpRepr, UdpRepr, &'a [u8])),
#[cfg(feature = "socket-tcp")]
Tcp((IpRepr, TcpRepr<'a>)),
#[cfg(feature = "socket-dhcpv4")]
Dhcpv4((Ipv4Repr, UdpRepr, DhcpRepr<'a>)),
}
impl<'a> IpPacket<'a> {
pub(crate) fn ip_repr(&self) -> IpRepr {
match self {
#[cfg(feature = "proto-ipv4")]
IpPacket::Icmpv4((ipv4_repr, _)) => IpRepr::Ipv4(*ipv4_repr),
#[cfg(feature = "proto-igmp")]
IpPacket::Igmp((ipv4_repr, _)) => IpRepr::Ipv4(*ipv4_repr),
#[cfg(feature = "proto-ipv6")]
IpPacket::Icmpv6((ipv6_repr, _)) => IpRepr::Ipv6(*ipv6_repr),
#[cfg(feature = "socket-raw")]
IpPacket::Raw((ip_repr, _)) => ip_repr.clone(),
#[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
IpPacket::Udp((ip_repr, _, _)) => ip_repr.clone(),
#[cfg(feature = "socket-tcp")]
IpPacket::Tcp((ip_repr, _)) => ip_repr.clone(),
#[cfg(feature = "socket-dhcpv4")]
IpPacket::Dhcpv4((ipv4_repr, _, _)) => IpRepr::Ipv4(*ipv4_repr),
}
}
pub(crate) fn emit_payload(
&self,
_ip_repr: &IpRepr,
payload: &mut [u8],
caps: &DeviceCapabilities,
) {
match self {
#[cfg(feature = "proto-ipv4")]
IpPacket::Icmpv4((_, icmpv4_repr)) => {
icmpv4_repr.emit(&mut Icmpv4Packet::new_unchecked(payload), &caps.checksum)
}
#[cfg(feature = "proto-igmp")]
IpPacket::Igmp((_, igmp_repr)) => {
igmp_repr.emit(&mut IgmpPacket::new_unchecked(payload))
}
#[cfg(feature = "proto-ipv6")]
IpPacket::Icmpv6((_, icmpv6_repr)) => icmpv6_repr.emit(
&_ip_repr.src_addr(),
&_ip_repr.dst_addr(),
&mut Icmpv6Packet::new_unchecked(payload),
&caps.checksum,
),
#[cfg(feature = "socket-raw")]
IpPacket::Raw((_, raw_packet)) => payload.copy_from_slice(raw_packet),
#[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
IpPacket::Udp((_, udp_repr, inner_payload)) => {
udp_repr.emit(
&mut UdpPacket::new_unchecked(payload),
&_ip_repr.src_addr(),
&_ip_repr.dst_addr(),
inner_payload.len(),
|buf| buf.copy_from_slice(inner_payload),
&caps.checksum,
);
}
#[cfg(feature = "socket-tcp")]
IpPacket::Tcp((_, mut tcp_repr)) => {
if let Some(max_burst_size) = caps.max_burst_size {
let mut max_segment_size = caps.max_transmission_unit;
max_segment_size -= _ip_repr.header_len();
max_segment_size -= tcp_repr.header_len();
let max_window_size = max_burst_size * max_segment_size;
if tcp_repr.window_len as usize > max_window_size {
tcp_repr.window_len = max_window_size as u16;
}
}
tcp_repr.emit(
&mut TcpPacket::new_unchecked(payload),
&_ip_repr.src_addr(),
&_ip_repr.dst_addr(),
&caps.checksum,
);
}
#[cfg(feature = "socket-dhcpv4")]
IpPacket::Dhcpv4((_, udp_repr, dhcp_repr)) => udp_repr.emit(
&mut UdpPacket::new_unchecked(payload),
&_ip_repr.src_addr(),
&_ip_repr.dst_addr(),
dhcp_repr.buffer_len(),
|buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(),
&caps.checksum,
),
};
}
}
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
fn icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize {
cmp::min(len, mtu - header_len * 2 - 8)
}
#[cfg(feature = "proto-igmp")]
enum IgmpReportState {
Inactive,
ToGeneralQuery {
version: IgmpVersion,
timeout: Instant,
interval: Duration,
next_index: usize,
},
ToSpecificQuery {
version: IgmpVersion,
timeout: Instant,
group: Ipv4Address,
},
}
impl Interface {
pub fn new<D>(config: Config, device: &mut D, now: Instant) -> Self
where
D: Device + ?Sized,
{
let caps = device.capabilities();
assert_eq!(
config.hardware_addr.medium(),
caps.medium,
"The hardware address does not match the medium of the interface."
);
let mut rand = Rand::new(config.random_seed);
#[cfg(feature = "medium-ieee802154")]
let mut sequence_no;
#[cfg(feature = "medium-ieee802154")]
loop {
sequence_no = (rand.rand_u32() & 0xff) as u8;
if sequence_no != 0 {
break;
}
}
#[cfg(feature = "proto-sixlowpan")]
let mut tag;
#[cfg(feature = "proto-sixlowpan")]
loop {
tag = rand.rand_u16();
if tag != 0 {
break;
}
}
#[cfg(feature = "proto-ipv4")]
let mut ipv4_id;
#[cfg(feature = "proto-ipv4")]
loop {
ipv4_id = rand.rand_u16();
if ipv4_id != 0 {
break;
}
}
Interface {
fragments: FragmentsBuffer {
#[cfg(feature = "proto-sixlowpan")]
decompress_buf: [0u8; sixlowpan::MAX_DECOMPRESSED_LEN],
#[cfg(feature = "_proto-fragmentation")]
assembler: PacketAssemblerSet::new(),
#[cfg(feature = "_proto-fragmentation")]
reassembly_timeout: Duration::from_secs(60),
},
fragmenter: Fragmenter::new(),
inner: InterfaceInner {
now,
caps,
hardware_addr: config.hardware_addr,
ip_addrs: Vec::new(),
#[cfg(feature = "proto-ipv4")]
any_ip: false,
routes: Routes::new(),
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
neighbor_cache: NeighborCache::new(),
#[cfg(feature = "proto-igmp")]
ipv4_multicast_groups: LinearMap::new(),
#[cfg(feature = "proto-igmp")]
igmp_report_state: IgmpReportState::Inactive,
#[cfg(feature = "medium-ieee802154")]
sequence_no,
#[cfg(feature = "medium-ieee802154")]
pan_id: config.pan_id,
#[cfg(feature = "proto-sixlowpan-fragmentation")]
tag,
#[cfg(feature = "proto-ipv4-fragmentation")]
ipv4_id,
#[cfg(feature = "proto-sixlowpan")]
sixlowpan_address_context: Vec::new(),
rand,
},
}
}
pub fn context(&mut self) -> &mut InterfaceInner {
&mut self.inner
}
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
pub fn hardware_addr(&self) -> HardwareAddress {
#[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))]
assert!(self.inner.caps.medium == Medium::Ethernet);
#[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))]
assert!(self.inner.caps.medium == Medium::Ieee802154);
#[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))]
assert!(
self.inner.caps.medium == Medium::Ethernet
|| self.inner.caps.medium == Medium::Ieee802154
);
self.inner.hardware_addr
}
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
pub fn set_hardware_addr(&mut self, addr: HardwareAddress) {
#[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))]
assert!(self.inner.caps.medium == Medium::Ethernet);
#[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))]
assert!(self.inner.caps.medium == Medium::Ieee802154);
#[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))]
assert!(
self.inner.caps.medium == Medium::Ethernet
|| self.inner.caps.medium == Medium::Ieee802154
);
InterfaceInner::check_hardware_addr(&addr);
self.inner.hardware_addr = addr;
}
pub fn ip_addrs(&self) -> &[IpCidr] {
self.inner.ip_addrs.as_ref()
}
#[cfg(feature = "proto-ipv4")]
pub fn ipv4_addr(&self) -> Option<Ipv4Address> {
self.inner.ipv4_addr()
}
#[cfg(feature = "proto-ipv6")]
pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
self.inner.ipv6_addr()
}
pub fn update_ip_addrs<F: FnOnce(&mut Vec<IpCidr, IFACE_MAX_ADDR_COUNT>)>(&mut self, f: F) {
f(&mut self.inner.ip_addrs);
InterfaceInner::flush_cache(&mut self.inner);
InterfaceInner::check_ip_addrs(&self.inner.ip_addrs)
}
pub fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
self.inner.has_ip_addr(addr)
}
pub fn routes(&self) -> &Routes {
&self.inner.routes
}
pub fn routes_mut(&mut self) -> &mut Routes {
&mut self.inner.routes
}
#[cfg(feature = "proto-ipv4")]
pub fn set_any_ip(&mut self, any_ip: bool) {
self.inner.any_ip = any_ip;
}
#[cfg(feature = "proto-ipv4")]
pub fn any_ip(&self) -> bool {
self.inner.any_ip
}
#[cfg(feature = "proto-sixlowpan")]
pub fn sixlowpan_address_context(
&self,
) -> &Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT> {
&self.inner.sixlowpan_address_context
}
#[cfg(feature = "proto-sixlowpan")]
pub fn sixlowpan_address_context_mut(
&mut self,
) -> &mut Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT> {
&mut self.inner.sixlowpan_address_context
}
#[cfg(feature = "_proto-fragmentation")]
pub fn reassembly_timeout(&self) -> Duration {
self.fragments.reassembly_timeout
}
#[cfg(feature = "_proto-fragmentation")]
pub fn set_reassembly_timeout(&mut self, timeout: Duration) {
if timeout > Duration::from_secs(60) {
net_debug!("RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds");
}
self.fragments.reassembly_timeout = timeout;
}
pub fn poll<D>(
&mut self,
timestamp: Instant,
device: &mut D,
sockets: &mut SocketSet<'_>,
) -> bool
where
D: Device + ?Sized,
{
self.inner.now = timestamp;
#[cfg(feature = "_proto-fragmentation")]
self.fragments.assembler.remove_expired(timestamp);
match self.inner.caps.medium {
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 =>
{
#[cfg(feature = "proto-sixlowpan-fragmentation")]
if self.sixlowpan_egress(device) {
return true;
}
}
#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
_ =>
{
#[cfg(feature = "proto-ipv4-fragmentation")]
if self.ipv4_egress(device) {
return true;
}
}
}
let mut readiness_may_have_changed = false;
loop {
let mut did_something = false;
did_something |= self.socket_ingress(device, sockets);
did_something |= self.socket_egress(device, sockets);
#[cfg(feature = "proto-igmp")]
{
did_something |= self.igmp_egress(device);
}
if did_something {
readiness_may_have_changed = true;
} else {
break;
}
}
readiness_may_have_changed
}
pub fn poll_at(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Instant> {
self.inner.now = timestamp;
#[cfg(feature = "_proto-fragmentation")]
if !self.fragmenter.is_empty() {
return Some(Instant::from_millis(0));
}
let inner = &mut self.inner;
sockets
.items()
.filter_map(move |item| {
let socket_poll_at = item.socket.poll_at(inner);
match item
.meta
.poll_at(socket_poll_at, |ip_addr| inner.has_neighbor(&ip_addr))
{
PollAt::Ingress => None,
PollAt::Time(instant) => Some(instant),
PollAt::Now => Some(Instant::from_millis(0)),
}
})
.min()
}
pub fn poll_delay(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Duration> {
match self.poll_at(timestamp, sockets) {
Some(poll_at) if timestamp < poll_at => Some(poll_at - timestamp),
Some(_) => Some(Duration::from_millis(0)),
_ => None,
}
}
fn socket_ingress<D>(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool
where
D: Device + ?Sized,
{
let mut processed_any = false;
while let Some((rx_token, tx_token)) = device.receive(self.inner.now) {
let rx_meta = rx_token.meta();
rx_token.consume(|frame| {
match self.inner.caps.medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => {
if let Some(packet) = self.inner.process_ethernet(
sockets,
rx_meta,
&frame,
&mut self.fragments,
) {
if let Err(err) =
self.inner.dispatch(tx_token, packet, &mut self.fragmenter)
{
net_debug!("Failed to send response: {:?}", err);
}
}
}
#[cfg(feature = "medium-ip")]
Medium::Ip => {
if let Some(packet) =
self.inner
.process_ip(sockets, rx_meta, &frame, &mut self.fragments)
{
if let Err(err) = self.inner.dispatch_ip(
tx_token,
PacketMeta::default(),
packet,
&mut self.fragmenter,
) {
net_debug!("Failed to send response: {:?}", err);
}
}
}
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => {
if let Some(packet) = self.inner.process_ieee802154(
sockets,
rx_meta,
&frame,
&mut self.fragments,
) {
if let Err(err) = self.inner.dispatch_ip(
tx_token,
PacketMeta::default(),
packet,
&mut self.fragmenter,
) {
net_debug!("Failed to send response: {:?}", err);
}
}
}
}
processed_any = true;
});
}
processed_any
}
fn socket_egress<D>(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool
where
D: Device + ?Sized,
{
let _caps = device.capabilities();
enum EgressError {
Exhausted,
Dispatch(DispatchError),
}
let mut emitted_any = false;
for item in sockets.items_mut() {
if !item
.meta
.egress_permitted(self.inner.now, |ip_addr| self.inner.has_neighbor(&ip_addr))
{
continue;
}
let mut neighbor_addr = None;
let mut respond = |inner: &mut InterfaceInner, meta: PacketMeta, response: IpPacket| {
neighbor_addr = Some(response.ip_repr().dst_addr());
let t = device.transmit(inner.now).ok_or_else(|| {
net_debug!("failed to transmit IP: device exhausted");
EgressError::Exhausted
})?;
inner
.dispatch_ip(t, meta, response, &mut self.fragmenter)
.map_err(EgressError::Dispatch)?;
emitted_any = true;
Ok(())
};
let result = match &mut item.socket {
#[cfg(feature = "socket-raw")]
Socket::Raw(socket) => socket.dispatch(&mut self.inner, |inner, response| {
respond(inner, PacketMeta::default(), IpPacket::Raw(response))
}),
#[cfg(feature = "socket-icmp")]
Socket::Icmp(socket) => {
socket.dispatch(&mut self.inner, |inner, response| match response {
#[cfg(feature = "proto-ipv4")]
(IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => respond(
inner,
PacketMeta::default(),
IpPacket::Icmpv4((ipv4_repr, icmpv4_repr)),
),
#[cfg(feature = "proto-ipv6")]
(IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => respond(
inner,
PacketMeta::default(),
IpPacket::Icmpv6((ipv6_repr, icmpv6_repr)),
),
#[allow(unreachable_patterns)]
_ => unreachable!(),
})
}
#[cfg(feature = "socket-udp")]
Socket::Udp(socket) => socket.dispatch(&mut self.inner, |inner, meta, response| {
respond(inner, meta, IpPacket::Udp(response))
}),
#[cfg(feature = "socket-tcp")]
Socket::Tcp(socket) => socket.dispatch(&mut self.inner, |inner, response| {
respond(inner, PacketMeta::default(), IpPacket::Tcp(response))
}),
#[cfg(feature = "socket-dhcpv4")]
Socket::Dhcpv4(socket) => socket.dispatch(&mut self.inner, |inner, response| {
respond(inner, PacketMeta::default(), IpPacket::Dhcpv4(response))
}),
#[cfg(feature = "socket-dns")]
Socket::Dns(socket) => {
socket.dispatch(&mut self.inner, |inner, (ip, udp, payload)| {
respond(
inner,
PacketMeta::default(),
IpPacket::Udp((ip, udp, payload)),
)
})
}
};
match result {
Err(EgressError::Exhausted) => break, Err(EgressError::Dispatch(_)) => {
item.meta.neighbor_missing(
self.inner.now,
neighbor_addr.expect("non-IP response packet"),
);
}
Ok(()) => {}
}
}
emitted_any
}
#[cfg(feature = "proto-ipv4-fragmentation")]
fn ipv4_egress<D>(&mut self, device: &mut D) -> bool
where
D: Device + ?Sized,
{
if self.fragmenter.finished() {
self.fragmenter.reset();
}
if self.fragmenter.is_empty() {
return false;
}
let pkt = &self.fragmenter;
if pkt.packet_len > pkt.sent_bytes {
if let Some(tx_token) = device.transmit(self.inner.now) {
self.inner
.dispatch_ipv4_frag(tx_token, &mut self.fragmenter);
return true;
}
}
false
}
#[cfg(feature = "proto-sixlowpan-fragmentation")]
fn sixlowpan_egress<D>(&mut self, device: &mut D) -> bool
where
D: Device + ?Sized,
{
if self.fragmenter.finished() {
self.fragmenter.reset();
}
if self.fragmenter.is_empty() {
return false;
}
let pkt = &self.fragmenter;
if pkt.packet_len > pkt.sent_bytes {
if let Some(tx_token) = device.transmit(self.inner.now) {
self.inner
.dispatch_ieee802154_frag(tx_token, &mut self.fragmenter);
return true;
}
}
false
}
}
impl InterfaceInner {
#[allow(unused)] pub(crate) fn now(&self) -> Instant {
self.now
}
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
#[allow(unused)] pub(crate) fn hardware_addr(&self) -> HardwareAddress {
self.hardware_addr
}
#[allow(unused)] pub(crate) fn checksum_caps(&self) -> ChecksumCapabilities {
self.caps.checksum.clone()
}
#[allow(unused)] pub(crate) fn ip_mtu(&self) -> usize {
self.caps.ip_mtu()
}
#[allow(unused)] pub(crate) fn rand(&mut self) -> &mut Rand {
&mut self.rand
}
#[allow(unused)] pub(crate) fn get_source_address(&mut self, dst_addr: IpAddress) -> Option<IpAddress> {
let v = dst_addr.version();
for cidr in self.ip_addrs.iter() {
let addr = cidr.address();
if addr.version() == v {
return Some(addr);
}
}
None
}
#[cfg(feature = "proto-ipv4")]
#[allow(unused)]
pub(crate) fn get_source_address_ipv4(
&mut self,
_dst_addr: Ipv4Address,
) -> Option<Ipv4Address> {
for cidr in self.ip_addrs.iter() {
#[allow(irrefutable_let_patterns)] if let IpCidr::Ipv4(cidr) = cidr {
return Some(cidr.address());
}
}
None
}
#[cfg(feature = "proto-ipv6")]
#[allow(unused)]
pub(crate) fn get_source_address_ipv6(
&mut self,
_dst_addr: Ipv6Address,
) -> Option<Ipv6Address> {
for cidr in self.ip_addrs.iter() {
#[allow(irrefutable_let_patterns)] if let IpCidr::Ipv6(cidr) = cidr {
return Some(cidr.address());
}
}
None
}
#[cfg(test)]
pub(crate) fn mock() -> Self {
Self {
caps: DeviceCapabilities {
#[cfg(feature = "medium-ethernet")]
medium: crate::phy::Medium::Ethernet,
#[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ip"))]
medium: crate::phy::Medium::Ip,
#[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))]
medium: crate::phy::Medium::Ieee802154,
checksum: crate::phy::ChecksumCapabilities {
#[cfg(feature = "proto-ipv4")]
icmpv4: crate::phy::Checksum::Both,
#[cfg(feature = "proto-ipv6")]
icmpv6: crate::phy::Checksum::Both,
ipv4: crate::phy::Checksum::Both,
tcp: crate::phy::Checksum::Both,
udp: crate::phy::Checksum::Both,
},
max_burst_size: None,
#[cfg(feature = "medium-ethernet")]
max_transmission_unit: 1514,
#[cfg(not(feature = "medium-ethernet"))]
max_transmission_unit: 1500,
},
now: Instant::from_millis_const(0),
ip_addrs: Vec::from_slice(&[
#[cfg(feature = "proto-ipv4")]
IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 1), 24)),
#[cfg(feature = "proto-ipv6")]
IpCidr::Ipv6(Ipv6Cidr::new(
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),
64,
)),
])
.unwrap(),
rand: Rand::new(1234),
routes: Routes::new(),
#[cfg(feature = "proto-ipv4")]
any_ip: false,
#[cfg(feature = "medium-ieee802154")]
pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)),
#[cfg(feature = "medium-ieee802154")]
sequence_no: 1,
#[cfg(feature = "proto-sixlowpan-fragmentation")]
tag: 1,
#[cfg(feature = "proto-sixlowpan")]
sixlowpan_address_context: Vec::new(),
#[cfg(feature = "proto-ipv4-fragmentation")]
ipv4_id: 1,
#[cfg(all(
feature = "medium-ip",
not(feature = "medium-ethernet"),
not(feature = "medium-ieee802154")
))]
hardware_addr: crate::wire::HardwareAddress::Ip,
#[cfg(feature = "medium-ethernet")]
hardware_addr: crate::wire::HardwareAddress::Ethernet(crate::wire::EthernetAddress([
0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
])),
#[cfg(all(
not(feature = "medium-ip"),
not(feature = "medium-ethernet"),
feature = "medium-ieee802154"
))]
hardware_addr: crate::wire::HardwareAddress::Ieee802154(
crate::wire::Ieee802154Address::Extended([
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x2, 0x2,
]),
),
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
neighbor_cache: NeighborCache::new(),
#[cfg(feature = "proto-igmp")]
igmp_report_state: IgmpReportState::Inactive,
#[cfg(feature = "proto-igmp")]
ipv4_multicast_groups: LinearMap::new(),
}
}
#[cfg(test)]
#[allow(unused)] pub(crate) fn set_now(&mut self, now: Instant) {
self.now = now
}
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
fn check_hardware_addr(addr: &HardwareAddress) {
if !addr.is_unicast() {
panic!("Hardware address {addr} is not unicast")
}
}
fn check_ip_addrs(addrs: &[IpCidr]) {
for cidr in addrs {
if !cidr.address().is_unicast() && !cidr.address().is_unspecified() {
panic!("IP address {} is not unicast", cidr.address())
}
}
}
#[cfg(feature = "medium-ieee802154")]
fn get_sequence_number(&mut self) -> u8 {
let no = self.sequence_no;
self.sequence_no = self.sequence_no.wrapping_add(1);
no
}
#[cfg(feature = "proto-ipv4-fragmentation")]
fn get_ipv4_ident(&mut self) -> u16 {
let ipv4_id = self.ipv4_id;
self.ipv4_id = self.ipv4_id.wrapping_add(1);
ipv4_id
}
#[cfg(feature = "proto-sixlowpan-fragmentation")]
fn get_sixlowpan_fragment_tag(&mut self) -> u16 {
let tag = self.tag;
self.tag = self.tag.wrapping_add(1);
tag
}
#[cfg(feature = "proto-ipv6")]
pub fn has_solicited_node(&self, addr: Ipv6Address) -> bool {
self.ip_addrs.iter().any(|cidr| {
match *cidr {
IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOOPBACK => {
addr.as_bytes()[14..] == cidr.address().as_bytes()[14..]
}
_ => false,
}
})
}
fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
let addr = addr.into();
self.ip_addrs.iter().any(|probe| probe.address() == addr)
}
#[cfg(feature = "proto-ipv4")]
pub fn ipv4_addr(&self) -> Option<Ipv4Address> {
self.ip_addrs.iter().find_map(|addr| match *addr {
IpCidr::Ipv4(cidr) => Some(cidr.address()),
#[allow(unreachable_patterns)]
_ => None,
})
}
#[cfg(feature = "proto-ipv6")]
pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
self.ip_addrs.iter().find_map(|addr| match *addr {
IpCidr::Ipv6(cidr) => Some(cidr.address()),
#[allow(unreachable_patterns)]
_ => None,
})
}
#[cfg(not(feature = "proto-igmp"))]
fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
false
}
#[cfg(feature = "medium-ip")]
fn process_ip<'frame, T: AsRef<[u8]>>(
&mut self,
sockets: &mut SocketSet,
meta: PacketMeta,
ip_payload: &'frame T,
frag: &'frame mut FragmentsBuffer,
) -> Option<IpPacket<'frame>> {
match IpVersion::of_packet(ip_payload.as_ref()) {
#[cfg(feature = "proto-ipv4")]
Ok(IpVersion::Ipv4) => {
let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload));
self.process_ipv4(sockets, meta, &ipv4_packet, frag)
}
#[cfg(feature = "proto-ipv6")]
Ok(IpVersion::Ipv6) => {
let ipv6_packet = check!(Ipv6Packet::new_checked(ip_payload));
self.process_ipv6(sockets, meta, &ipv6_packet)
}
_ => None,
}
}
#[cfg(feature = "socket-raw")]
fn raw_socket_filter(
&mut self,
sockets: &mut SocketSet,
ip_repr: &IpRepr,
ip_payload: &[u8],
) -> bool {
let mut handled_by_raw_socket = false;
for raw_socket in sockets
.items_mut()
.filter_map(|i| raw::Socket::downcast_mut(&mut i.socket))
{
if raw_socket.accepts(ip_repr) {
raw_socket.process(self, ip_repr, ip_payload);
handled_by_raw_socket = true;
}
}
handled_by_raw_socket
}
pub(crate) fn is_broadcast(&self, address: &IpAddress) -> bool {
match address {
#[cfg(feature = "proto-ipv4")]
IpAddress::Ipv4(address) => self.is_broadcast_v4(*address),
#[cfg(feature = "proto-ipv6")]
IpAddress::Ipv6(_) => false,
}
}
#[cfg(feature = "proto-ipv4")]
pub(crate) fn is_broadcast_v4(&self, address: Ipv4Address) -> bool {
if address.is_broadcast() {
return true;
}
self.ip_addrs
.iter()
.filter_map(|own_cidr| match own_cidr {
IpCidr::Ipv4(own_ip) => Some(own_ip.broadcast()?),
#[cfg(feature = "proto-ipv6")]
IpCidr::Ipv6(_) => None,
})
.any(|broadcast_address| address == broadcast_address)
}
#[cfg(feature = "proto-ipv4")]
fn is_unicast_v4(&self, address: Ipv4Address) -> bool {
address.is_unicast() && !self.is_broadcast_v4(address)
}
#[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
#[allow(clippy::too_many_arguments)]
fn process_udp<'frame>(
&mut self,
sockets: &mut SocketSet,
meta: PacketMeta,
ip_repr: IpRepr,
udp_repr: UdpRepr,
handled_by_raw_socket: bool,
udp_payload: &'frame [u8],
ip_payload: &'frame [u8],
) -> Option<IpPacket<'frame>> {
#[cfg(feature = "socket-udp")]
for udp_socket in sockets
.items_mut()
.filter_map(|i| udp::Socket::downcast_mut(&mut i.socket))
{
if udp_socket.accepts(self, &ip_repr, &udp_repr) {
udp_socket.process(self, meta, &ip_repr, &udp_repr, udp_payload);
return None;
}
}
#[cfg(feature = "socket-dns")]
for dns_socket in sockets
.items_mut()
.filter_map(|i| dns::Socket::downcast_mut(&mut i.socket))
{
if dns_socket.accepts(&ip_repr, &udp_repr) {
dns_socket.process(self, &ip_repr, &udp_repr, udp_payload);
return None;
}
}
match ip_repr {
#[cfg(feature = "proto-ipv4")]
IpRepr::Ipv4(_) if handled_by_raw_socket => None,
#[cfg(feature = "proto-ipv6")]
IpRepr::Ipv6(_) if handled_by_raw_socket => None,
#[cfg(feature = "proto-ipv4")]
IpRepr::Ipv4(ipv4_repr) => {
let payload_len =
icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, ipv4_repr.buffer_len());
let icmpv4_reply_repr = Icmpv4Repr::DstUnreachable {
reason: Icmpv4DstUnreachable::PortUnreachable,
header: ipv4_repr,
data: &ip_payload[0..payload_len],
};
self.icmpv4_reply(ipv4_repr, icmpv4_reply_repr)
}
#[cfg(feature = "proto-ipv6")]
IpRepr::Ipv6(ipv6_repr) => {
let payload_len =
icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len());
let icmpv6_reply_repr = Icmpv6Repr::DstUnreachable {
reason: Icmpv6DstUnreachable::PortUnreachable,
header: ipv6_repr,
data: &ip_payload[0..payload_len],
};
self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)
}
}
}
#[cfg(feature = "socket-tcp")]
pub(crate) fn process_tcp<'frame>(
&mut self,
sockets: &mut SocketSet,
ip_repr: IpRepr,
ip_payload: &'frame [u8],
) -> Option<IpPacket<'frame>> {
let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr());
let tcp_packet = check!(TcpPacket::new_checked(ip_payload));
let tcp_repr = check!(TcpRepr::parse(
&tcp_packet,
&src_addr,
&dst_addr,
&self.caps.checksum
));
for tcp_socket in sockets
.items_mut()
.filter_map(|i| tcp::Socket::downcast_mut(&mut i.socket))
{
if tcp_socket.accepts(self, &ip_repr, &tcp_repr) {
return tcp_socket
.process(self, &ip_repr, &tcp_repr)
.map(IpPacket::Tcp);
}
}
if tcp_repr.control == TcpControl::Rst {
None
} else {
Some(IpPacket::Tcp(tcp::Socket::rst_reply(&ip_repr, &tcp_repr)))
}
}
#[cfg(feature = "medium-ethernet")]
fn dispatch<Tx>(
&mut self,
tx_token: Tx,
packet: EthernetPacket,
frag: &mut Fragmenter,
) -> Result<(), DispatchError>
where
Tx: TxToken,
{
match packet {
#[cfg(feature = "proto-ipv4")]
EthernetPacket::Arp(arp_repr) => {
let dst_hardware_addr = match arp_repr {
ArpRepr::EthernetIpv4 {
target_hardware_addr,
..
} => target_hardware_addr,
};
self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| {
frame.set_dst_addr(dst_hardware_addr);
frame.set_ethertype(EthernetProtocol::Arp);
let mut packet = ArpPacket::new_unchecked(frame.payload_mut());
arp_repr.emit(&mut packet);
})
}
EthernetPacket::Ip(packet) => {
self.dispatch_ip(tx_token, PacketMeta::default(), packet, frag)
}
}
}
fn in_same_network(&self, addr: &IpAddress) -> bool {
self.ip_addrs.iter().any(|cidr| cidr.contains_addr(addr))
}
fn route(&self, addr: &IpAddress, timestamp: Instant) -> Option<IpAddress> {
if self.in_same_network(addr) || addr.is_broadcast() {
return Some(*addr);
}
self.routes.lookup(addr, timestamp)
}
fn has_neighbor(&self, addr: &IpAddress) -> bool {
match self.route(addr, self.now) {
Some(_routed_addr) => match self.caps.medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => self.neighbor_cache.lookup(&_routed_addr, self.now).found(),
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => self.neighbor_cache.lookup(&_routed_addr, self.now).found(),
#[cfg(feature = "medium-ip")]
Medium::Ip => true,
},
None => false,
}
}
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
fn lookup_hardware_addr<Tx>(
&mut self,
tx_token: Tx,
src_addr: &IpAddress,
dst_addr: &IpAddress,
fragmenter: &mut Fragmenter,
) -> Result<(HardwareAddress, Tx), DispatchError>
where
Tx: TxToken,
{
if self.is_broadcast(dst_addr) {
let hardware_addr = match self.caps.medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::BROADCAST),
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST),
#[cfg(feature = "medium-ip")]
Medium::Ip => unreachable!(),
};
return Ok((hardware_addr, tx_token));
}
if dst_addr.is_multicast() {
let b = dst_addr.as_bytes();
let hardware_addr = match *dst_addr {
#[cfg(feature = "proto-ipv4")]
IpAddress::Ipv4(_addr) => {
HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
0x01,
0x00,
0x5e,
b[1] & 0x7F,
b[2],
b[3],
]))
}
#[cfg(feature = "proto-ipv6")]
IpAddress::Ipv6(_addr) => match self.caps.medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
0x33, 0x33, b[12], b[13], b[14], b[15],
])),
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => {
HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST)
}
#[cfg(feature = "medium-ip")]
Medium::Ip => unreachable!(),
},
};
return Ok((hardware_addr, tx_token));
}
let dst_addr = self
.route(dst_addr, self.now)
.ok_or(DispatchError::NoRoute)?;
match self.neighbor_cache.lookup(&dst_addr, self.now) {
NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)),
NeighborAnswer::RateLimited => return Err(DispatchError::NeighborPending),
_ => (), }
match (src_addr, dst_addr) {
#[cfg(feature = "proto-ipv4")]
(&IpAddress::Ipv4(src_addr), IpAddress::Ipv4(dst_addr)) => {
net_debug!(
"address {} not in neighbor cache, sending ARP request",
dst_addr
);
let src_hardware_addr = self.hardware_addr.ethernet_or_panic();
let arp_repr = ArpRepr::EthernetIpv4 {
operation: ArpOperation::Request,
source_hardware_addr: src_hardware_addr,
source_protocol_addr: src_addr,
target_hardware_addr: EthernetAddress::BROADCAST,
target_protocol_addr: dst_addr,
};
if let Err(e) =
self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| {
frame.set_dst_addr(EthernetAddress::BROADCAST);
frame.set_ethertype(EthernetProtocol::Arp);
arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut()))
})
{
net_debug!("Failed to dispatch ARP request: {:?}", e);
return Err(DispatchError::NeighborPending);
}
}
#[cfg(feature = "proto-ipv6")]
(&IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => {
net_debug!(
"address {} not in neighbor cache, sending Neighbor Solicitation",
dst_addr
);
let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit {
target_addr: dst_addr,
lladdr: Some(self.hardware_addr.into()),
});
let packet = IpPacket::Icmpv6((
Ipv6Repr {
src_addr,
dst_addr: dst_addr.solicited_node(),
next_header: IpProtocol::Icmpv6,
payload_len: solicit.buffer_len(),
hop_limit: 0xff,
},
solicit,
));
if let Err(e) =
self.dispatch_ip(tx_token, PacketMeta::default(), packet, fragmenter)
{
net_debug!("Failed to dispatch NDISC solicit: {:?}", e);
return Err(DispatchError::NeighborPending);
}
}
#[allow(unreachable_patterns)]
_ => (),
}
self.neighbor_cache.limit_rate(self.now);
Err(DispatchError::NeighborPending)
}
fn flush_cache(&mut self) {
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
self.neighbor_cache.flush()
}
fn dispatch_ip<Tx: TxToken>(
&mut self,
#[allow(unused_mut)] mut tx_token: Tx,
meta: PacketMeta,
packet: IpPacket,
frag: &mut Fragmenter,
) -> Result<(), DispatchError> {
let mut ip_repr = packet.ip_repr();
assert!(!ip_repr.dst_addr().is_unspecified());
#[cfg(feature = "medium-ieee802154")]
if matches!(self.caps.medium, Medium::Ieee802154) {
let (addr, tx_token) = self.lookup_hardware_addr(
tx_token,
&ip_repr.src_addr(),
&ip_repr.dst_addr(),
frag,
)?;
let addr = addr.ieee802154_or_panic();
self.dispatch_ieee802154(addr, tx_token, meta, packet, frag);
return Ok(());
}
let caps = self.caps.clone();
#[cfg(feature = "proto-ipv4-fragmentation")]
let ipv4_id = self.get_ipv4_ident();
let mut total_len = ip_repr.buffer_len();
#[cfg(feature = "medium-ethernet")]
if matches!(self.caps.medium, Medium::Ethernet) {
total_len = EthernetFrame::<&[u8]>::buffer_len(total_len);
}
#[cfg(feature = "medium-ethernet")]
let (dst_hardware_addr, mut tx_token) = match self.caps.medium {
Medium::Ethernet => {
match self.lookup_hardware_addr(
tx_token,
&ip_repr.src_addr(),
&ip_repr.dst_addr(),
frag,
)? {
(HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token),
(_, _) => unreachable!(),
}
}
_ => (EthernetAddress([0; 6]), tx_token),
};
#[cfg(feature = "medium-ethernet")]
let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| {
let mut frame = EthernetFrame::new_unchecked(tx_buffer);
let src_addr = self.hardware_addr.ethernet_or_panic();
frame.set_src_addr(src_addr);
frame.set_dst_addr(dst_hardware_addr);
match repr.version() {
#[cfg(feature = "proto-ipv4")]
IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4),
#[cfg(feature = "proto-ipv6")]
IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6),
}
Ok(())
};
let emit_ip = |repr: &IpRepr, mut tx_buffer: &mut [u8]| {
repr.emit(&mut tx_buffer, &self.caps.checksum);
let payload = &mut tx_buffer[repr.header_len()..];
packet.emit_payload(repr, payload, &caps)
};
let total_ip_len = ip_repr.buffer_len();
match &mut ip_repr {
#[cfg(feature = "proto-ipv4")]
IpRepr::Ipv4(repr) => {
if total_ip_len > self.caps.max_transmission_unit {
#[cfg(feature = "proto-ipv4-fragmentation")]
{
net_debug!("start fragmentation");
let tx_len = self.caps.max_transmission_unit;
let ip_header_len = repr.buffer_len();
let first_frag_ip_len = self.caps.ip_mtu();
if frag.buffer.len() < total_ip_len {
net_debug!(
"Fragmentation buffer is too small, at least {} needed. Dropping",
total_ip_len
);
return Ok(());
}
#[cfg(feature = "medium-ethernet")]
{
frag.ipv4.dst_hardware_addr = dst_hardware_addr;
}
frag.packet_len = total_ip_len;
frag.ipv4.repr = *repr;
frag.sent_bytes = first_frag_ip_len;
repr.payload_len = first_frag_ip_len - repr.buffer_len();
emit_ip(&ip_repr, &mut frag.buffer);
let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut frag.buffer[..]);
frag.ipv4.ident = ipv4_id;
ipv4_packet.set_ident(ipv4_id);
ipv4_packet.set_more_frags(true);
ipv4_packet.set_dont_frag(false);
ipv4_packet.set_frag_offset(0);
if caps.checksum.ipv4.tx() {
ipv4_packet.fill_checksum();
}
tx_token.consume(tx_len, |mut tx_buffer| {
#[cfg(feature = "medium-ethernet")]
if matches!(self.caps.medium, Medium::Ethernet) {
emit_ethernet(&ip_repr, tx_buffer)?;
tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
}
frag.ipv4.frag_offset = (first_frag_ip_len - ip_header_len) as u16;
tx_buffer[..first_frag_ip_len]
.copy_from_slice(&frag.buffer[..first_frag_ip_len]);
Ok(())
})
}
#[cfg(not(feature = "proto-ipv4-fragmentation"))]
{
net_debug!("Enable the `proto-ipv4-fragmentation` feature for fragmentation support.");
Ok(())
}
} else {
tx_token.set_meta(meta);
tx_token.consume(total_len, |mut tx_buffer| {
#[cfg(feature = "medium-ethernet")]
if matches!(self.caps.medium, Medium::Ethernet) {
emit_ethernet(&ip_repr, tx_buffer)?;
tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
}
emit_ip(&ip_repr, tx_buffer);
Ok(())
})
}
}
#[cfg(feature = "proto-ipv6")]
IpRepr::Ipv6(_) => tx_token.consume(total_len, |mut tx_buffer| {
#[cfg(feature = "medium-ethernet")]
if matches!(self.caps.medium, Medium::Ethernet) {
emit_ethernet(&ip_repr, tx_buffer)?;
tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
}
emit_ip(&ip_repr, tx_buffer);
Ok(())
}),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum DispatchError {
NoRoute,
NeighborPending,
}