use super::*;
impl Interface {
#[cfg(feature = "proto-ipv4-fragmentation")]
pub(super) fn ipv4_egress(&mut self, device: &mut (impl Device + ?Sized)) {
if self.fragmenter.finished() {
self.fragmenter.reset();
}
if self.fragmenter.is_empty() {
return;
}
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);
}
}
}
}
impl InterfaceInner {
#[cfg(feature = "proto-ipv4-fragmentation")]
pub(super) fn next_ipv4_frag_ident(&mut self) -> u16 {
let ipv4_id = self.ipv4_id;
self.ipv4_id = self.ipv4_id.wrapping_add(1);
ipv4_id
}
#[allow(unused)]
pub(crate) fn get_source_address_ipv4(&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
}
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)
}
fn is_unicast_v4(&self, address: Ipv4Address) -> bool {
address.x_is_unicast() && !self.is_broadcast_v4(address)
}
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,
})
}
pub(super) fn process_ipv4<'a>(
&mut self,
sockets: &mut SocketSet,
meta: PacketMeta,
source_hardware_addr: HardwareAddress,
ipv4_packet: &Ipv4Packet<&'a [u8]>,
frag: &'a mut FragmentsBuffer,
) -> Option<Packet<'a>> {
let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum));
if !self.is_unicast_v4(ipv4_repr.src_addr) && !ipv4_repr.src_addr.is_unspecified() {
net_debug!("non-unicast or unspecified source address");
return None;
}
#[cfg(feature = "proto-ipv4-fragmentation")]
let ip_payload = {
if ipv4_packet.more_frags() || ipv4_packet.frag_offset() != 0 {
let key = FragKey::Ipv4(ipv4_packet.get_key());
let f = match frag.assembler.get(&key, self.now + frag.reassembly_timeout) {
Ok(f) => f,
Err(_) => {
net_debug!("No available packet assembler for fragmented packet");
return None;
}
};
if !ipv4_packet.more_frags() {
check!(f.set_total_size(
ipv4_packet.total_len() as usize - ipv4_packet.header_len() as usize
+ ipv4_packet.frag_offset() as usize,
));
}
if let Err(e) = f.add(ipv4_packet.payload(), ipv4_packet.frag_offset() as usize) {
net_debug!("fragmentation error: {:?}", e);
return None;
}
match f.assemble() {
Some(payload) => payload,
None => return None,
}
} else {
ipv4_packet.payload()
}
};
#[cfg(not(feature = "proto-ipv4-fragmentation"))]
let ip_payload = ipv4_packet.payload();
let ip_repr = IpRepr::Ipv4(ipv4_repr);
#[cfg(feature = "socket-raw")]
let handled_by_raw_socket = self.raw_socket_filter(sockets, &ip_repr, ip_payload);
#[cfg(not(feature = "socket-raw"))]
let handled_by_raw_socket = false;
#[cfg(feature = "socket-dhcpv4")]
{
use crate::socket::dhcpv4::Socket as Dhcpv4Socket;
if ipv4_repr.next_header == IpProtocol::Udp
&& matches!(self.caps.medium, Medium::Ethernet)
{
let udp_packet = check!(UdpPacket::new_checked(ip_payload));
if let Some(dhcp_socket) = sockets
.items_mut()
.find_map(|i| Dhcpv4Socket::downcast_mut(&mut i.socket))
{
if udp_packet.src_port() == dhcp_socket.server_port
&& udp_packet.dst_port() == dhcp_socket.client_port
{
let udp_repr = check!(UdpRepr::parse(
&udp_packet,
&ipv4_repr.src_addr.into(),
&ipv4_repr.dst_addr.into(),
&self.caps.checksum
));
dhcp_socket.process(self, &ipv4_repr, &udp_repr, udp_packet.payload());
return None;
}
}
}
}
if !self.has_ip_addr(ipv4_repr.dst_addr)
&& !self.has_multicast_group(ipv4_repr.dst_addr)
&& !self.is_broadcast_v4(ipv4_repr.dst_addr)
{
if !self.any_ip {
net_trace!("Rejecting IPv4 packet; any_ip=false");
return None;
}
if !ipv4_repr.dst_addr.x_is_unicast() {
net_trace!(
"Rejecting IPv4 packet; {} is not a unicast address",
ipv4_repr.dst_addr
);
return None;
}
if self
.routes
.lookup(&IpAddress::Ipv4(ipv4_repr.dst_addr), self.now)
.map_or(true, |router_addr| !self.has_ip_addr(router_addr))
{
net_trace!("Rejecting IPv4 packet; no matching routes");
return None;
}
}
#[cfg(feature = "medium-ethernet")]
if self.is_unicast_v4(ipv4_repr.dst_addr) {
self.neighbor_cache.reset_expiry_if_existing(
IpAddress::Ipv4(ipv4_repr.src_addr),
source_hardware_addr,
self.now,
);
}
match ipv4_repr.next_header {
IpProtocol::Icmp => self.process_icmpv4(sockets, ipv4_repr, ip_payload),
#[cfg(feature = "multicast")]
IpProtocol::Igmp => self.process_igmp(ipv4_repr, ip_payload),
#[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
IpProtocol::Udp => {
self.process_udp(sockets, meta, handled_by_raw_socket, ip_repr, ip_payload)
}
#[cfg(feature = "socket-tcp")]
IpProtocol::Tcp => self.process_tcp(sockets, ip_repr, ip_payload),
_ if handled_by_raw_socket => None,
_ => {
let payload_len =
icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, ipv4_repr.buffer_len());
let icmp_reply_repr = Icmpv4Repr::DstUnreachable {
reason: Icmpv4DstUnreachable::ProtoUnreachable,
header: ipv4_repr,
data: &ip_payload[0..payload_len],
};
self.icmpv4_reply(ipv4_repr, icmp_reply_repr)
}
}
}
#[cfg(feature = "medium-ethernet")]
pub(super) fn process_arp<'frame>(
&mut self,
timestamp: Instant,
eth_frame: &EthernetFrame<&'frame [u8]>,
) -> Option<EthernetPacket<'frame>> {
let arp_packet = check!(ArpPacket::new_checked(eth_frame.payload()));
let arp_repr = check!(ArpRepr::parse(&arp_packet));
match arp_repr {
ArpRepr::EthernetIpv4 {
operation,
source_hardware_addr,
source_protocol_addr,
target_protocol_addr,
..
} => {
if !self.has_ip_addr(target_protocol_addr) && !self.any_ip {
return None;
}
if let ArpOperation::Unknown(_) = operation {
net_debug!("arp: unknown operation code");
return None;
}
if !source_protocol_addr.x_is_unicast() || !source_hardware_addr.is_unicast() {
net_debug!("arp: non-unicast source address");
return None;
}
if !self.in_same_network(&IpAddress::Ipv4(source_protocol_addr)) {
net_debug!("arp: source IP address not in same network as us");
return None;
}
self.neighbor_cache.fill(
source_protocol_addr.into(),
source_hardware_addr.into(),
timestamp,
);
if operation == ArpOperation::Request {
let src_hardware_addr = self.hardware_addr.ethernet_or_panic();
Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 {
operation: ArpOperation::Reply,
source_hardware_addr: src_hardware_addr,
source_protocol_addr: target_protocol_addr,
target_hardware_addr: source_hardware_addr,
target_protocol_addr: source_protocol_addr,
}))
} else {
None
}
}
}
}
pub(super) fn process_icmpv4<'frame>(
&mut self,
_sockets: &mut SocketSet,
ip_repr: Ipv4Repr,
ip_payload: &'frame [u8],
) -> Option<Packet<'frame>> {
let icmp_packet = check!(Icmpv4Packet::new_checked(ip_payload));
let icmp_repr = check!(Icmpv4Repr::parse(&icmp_packet, &self.caps.checksum));
#[cfg(feature = "socket-icmp")]
let mut handled_by_icmp_socket = false;
#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
for icmp_socket in _sockets
.items_mut()
.filter_map(|i| icmp::Socket::downcast_mut(&mut i.socket))
{
if icmp_socket.accepts_v4(self, &ip_repr, &icmp_repr) {
icmp_socket.process_v4(self, &ip_repr, &icmp_repr);
handled_by_icmp_socket = true;
}
}
match icmp_repr {
#[cfg(feature = "proto-ipv4")]
Icmpv4Repr::EchoRequest {
ident,
seq_no,
data,
} => {
let icmp_reply_repr = Icmpv4Repr::EchoReply {
ident,
seq_no,
data,
};
self.icmpv4_reply(ip_repr, icmp_reply_repr)
}
Icmpv4Repr::EchoReply { .. } => None,
#[cfg(feature = "socket-icmp")]
_ if handled_by_icmp_socket => None,
_ => None,
}
}
pub(super) fn icmpv4_reply<'frame, 'icmp: 'frame>(
&self,
ipv4_repr: Ipv4Repr,
icmp_repr: Icmpv4Repr<'icmp>,
) -> Option<Packet<'frame>> {
if !self.is_unicast_v4(ipv4_repr.src_addr) {
None
} else if self.is_unicast_v4(ipv4_repr.dst_addr) {
let ipv4_reply_repr = Ipv4Repr {
src_addr: ipv4_repr.dst_addr,
dst_addr: ipv4_repr.src_addr,
next_header: IpProtocol::Icmp,
payload_len: icmp_repr.buffer_len(),
hop_limit: 64,
};
Some(Packet::new_ipv4(
ipv4_reply_repr,
IpPayload::Icmpv4(icmp_repr),
))
} else if self.is_broadcast_v4(ipv4_repr.dst_addr) {
match icmp_repr {
Icmpv4Repr::EchoReply { .. } => match self.ipv4_addr() {
Some(src_addr) => {
let ipv4_reply_repr = Ipv4Repr {
src_addr,
dst_addr: ipv4_repr.src_addr,
next_header: IpProtocol::Icmp,
payload_len: icmp_repr.buffer_len(),
hop_limit: 64,
};
Some(Packet::new_ipv4(
ipv4_reply_repr,
IpPayload::Icmpv4(icmp_repr),
))
}
None => None,
},
_ => None,
}
} else {
None
}
}
#[cfg(feature = "proto-ipv4-fragmentation")]
pub(super) fn dispatch_ipv4_frag<Tx: TxToken>(&mut self, tx_token: Tx, frag: &mut Fragmenter) {
let caps = self.caps.clone();
let mtu_max = self.ip_mtu();
let ip_len = (frag.packet_len - frag.sent_bytes + frag.ipv4.repr.buffer_len()).min(mtu_max);
let payload_len = ip_len - frag.ipv4.repr.buffer_len();
let more_frags = (frag.packet_len - frag.sent_bytes) != payload_len;
frag.ipv4.repr.payload_len = payload_len;
frag.sent_bytes += payload_len;
let mut tx_len = ip_len;
#[cfg(feature = "medium-ethernet")]
if matches!(caps.medium, Medium::Ethernet) {
tx_len += EthernetFrame::<&[u8]>::header_len();
}
#[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(frag.ipv4.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),
}
};
tx_token.consume(tx_len, |mut tx_buffer| {
#[cfg(feature = "medium-ethernet")]
if matches!(self.caps.medium, Medium::Ethernet) {
emit_ethernet(&IpRepr::Ipv4(frag.ipv4.repr), tx_buffer);
tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
}
let mut packet =
Ipv4Packet::new_unchecked(&mut tx_buffer[..frag.ipv4.repr.buffer_len()]);
frag.ipv4.repr.emit(&mut packet, &caps.checksum);
packet.set_ident(frag.ipv4.ident);
packet.set_more_frags(more_frags);
packet.set_dont_frag(false);
packet.set_frag_offset(frag.ipv4.frag_offset);
if caps.checksum.ipv4.tx() {
packet.fill_checksum();
}
tx_buffer[frag.ipv4.repr.buffer_len()..][..payload_len].copy_from_slice(
&frag.buffer[frag.ipv4.frag_offset as usize + frag.ipv4.repr.buffer_len()..]
[..payload_len],
);
frag.ipv4.frag_offset += payload_len as u16;
})
}
}