From 3d12690009d89fe92e07bf6232409d1a089a31a9 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 22 Feb 2018 12:23:35 +0000 Subject: [PATCH] Use storage::PacketBuffer for implementing socket::IcmpSocket. This substantially increases its space efficiency. --- examples/ping.rs | 6 +- src/iface/ethernet.rs | 6 +- src/socket/icmp.rs | 128 ++++++++++++++---------------------------- src/socket/mod.rs | 4 +- 4 files changed, 49 insertions(+), 95 deletions(-) diff --git a/examples/ping.rs b/examples/ping.rs index b95ad99..4499b2f 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -17,7 +17,7 @@ use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Icmpv4Repr, Icmpv4Packet}; use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; -use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketBuffer, IcmpEndpoint}; +use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata, IcmpEndpoint}; use std::collections::HashMap; use byteorder::{ByteOrder, NetworkEndian}; @@ -54,8 +54,8 @@ fn main() { let remote_addr = address; let local_addr = Ipv4Address::new(192, 168, 69, 1); - let icmp_rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 256])]); - let icmp_tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 256])]); + let icmp_rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::empty()], vec![0; 256]); + let icmp_tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::empty()], vec![0; 256]); let icmp_socket = IcmpSocket::new(icmp_rx_buffer, icmp_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 411067a..636f298 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1564,13 +1564,13 @@ mod test { #[test] #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] fn test_icmpv4_socket() { - use socket::{IcmpPacketBuffer, IcmpSocket, IcmpSocketBuffer, IcmpEndpoint}; + use socket::{IcmpSocket, IcmpEndpoint, IcmpSocketBuffer, IcmpPacketMetadata}; use wire::Icmpv4Packet; let (iface, mut socket_set) = create_loopback(); - let rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 24])]); - let tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 24])]); + let rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::empty()], vec![0; 24]); + let tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::empty()], vec![0; 24]); let icmpv4_socket = IcmpSocket::new(rx_buffer, tx_buffer); diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 107942b..578fd54 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -1,10 +1,9 @@ use core::cmp; -use managed::Managed; use {Error, Result}; use phy::{ChecksumCapabilities, DeviceCapabilities}; use socket::{Socket, SocketMeta, SocketHandle}; -use storage::{Resettable, RingBuffer}; +use storage::{PacketBuffer, PacketMetadata}; use time::Instant; use wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr}; use wire::{Ipv4Address, Ipv4Repr}; @@ -36,51 +35,11 @@ impl Default for Endpoint { fn default() -> Endpoint { Endpoint::Unspecified } } -/// A buffered ICMPv4 packet. -#[derive(Debug)] -pub struct PacketBuffer<'a> { - endpoint: IpAddress, - size: usize, - payload: Managed<'a, [u8]> -} - -impl<'a> PacketBuffer<'a> { - /// Create a buffered packet. - pub fn new(payload: T) -> PacketBuffer<'a> - where T: Into> { - PacketBuffer { - endpoint: IpAddress::default(), - size: 0, - payload: payload.into() - } - } - - fn as_ref<'b>(&'b self) -> &'b [u8] { - &self.payload[..self.size] - } - - fn as_mut<'b>(&'b mut self) -> &'b mut [u8] { - &mut self.payload[..self.size] - } - - fn resize<'b>(&'b mut self, size: usize) -> Result<&'b mut Self> { - if self.payload.len() >= size { - self.size = size; - Ok(self) - } else { - Err(Error::Truncated) - } - } -} - -impl<'a> Resettable for PacketBuffer<'a> { - fn reset(&mut self) { - self.size = 0; - } -} +/// An ICMPv4 packet metadata. +pub type IcmpPacketMetadata = PacketMetadata; /// An ICMPv4 packet ring buffer. -pub type SocketBuffer<'a, 'b: 'a> = RingBuffer<'a, PacketBuffer<'b>>; +pub type IcmpSocketBuffer<'a, 'b> = PacketBuffer<'a, 'b, IpAddress>; /// An ICMPv4 socket /// @@ -94,8 +53,8 @@ pub type SocketBuffer<'a, 'b: 'a> = RingBuffer<'a, PacketBuffer<'b>>; #[derive(Debug)] pub struct IcmpSocket<'a, 'b: 'a> { pub(crate) meta: SocketMeta, - rx_buffer: SocketBuffer<'a, 'b>, - tx_buffer: SocketBuffer<'a, 'b>, + rx_buffer: IcmpSocketBuffer<'a, 'b>, + tx_buffer: IcmpSocketBuffer<'a, 'b>, /// The endpoint this socket is communicating with endpoint: Endpoint, /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. @@ -104,8 +63,8 @@ pub struct IcmpSocket<'a, 'b: 'a> { impl<'a, 'b> IcmpSocket<'a, 'b> { /// Create an ICMPv4 socket with the given buffers. - pub fn new(rx_buffer: SocketBuffer<'a, 'b>, - tx_buffer: SocketBuffer<'a, 'b>) -> IcmpSocket<'a, 'b> { + pub fn new(rx_buffer: IcmpSocketBuffer<'a, 'b>, + tx_buffer: IcmpSocketBuffer<'a, 'b>) -> IcmpSocket<'a, 'b> { IcmpSocket { meta: SocketMeta::default(), rx_buffer: rx_buffer, @@ -164,9 +123,9 @@ impl<'a, 'b> IcmpSocket<'a, 'b> { /// diagnose connection problems. /// /// ``` - /// # use smoltcp::socket::{Socket, IcmpSocket, IcmpPacketBuffer, IcmpSocketBuffer}; - /// # let rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 20])]); - /// # let tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 20])]); + /// # use smoltcp::socket::{Socket, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata}; + /// # let rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::empty()], vec![0; 20]); + /// # let tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::empty()], vec![0; 20]); /// use smoltcp::wire::IpEndpoint; /// use smoltcp::socket::IcmpEndpoint; /// @@ -186,9 +145,9 @@ impl<'a, 'b> IcmpSocket<'a, 'b> { /// messages. /// /// ``` - /// # use smoltcp::socket::{Socket, IcmpSocket, IcmpPacketBuffer, IcmpSocketBuffer}; - /// # let rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 20])]); - /// # let tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 20])]); + /// # use smoltcp::socket::{Socket, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata}; + /// # let rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::empty()], vec![0; 20]); + /// # let tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::empty()], vec![0; 20]); /// use smoltcp::socket::IcmpEndpoint; /// /// let mut icmp_socket = // ... @@ -244,11 +203,11 @@ impl<'a, 'b> IcmpSocket<'a, 'b> { return Err(Error::Unaddressable) } - let packet_buf = self.tx_buffer.enqueue_one_with(|buf| buf.resize(size))?; - packet_buf.endpoint = endpoint; + let packet_buf = self.tx_buffer.enqueue(size, endpoint)?; + net_trace!("{}:{}: buffer to send {} octets", - self.meta.handle, packet_buf.endpoint, size); - Ok(&mut packet_buf.as_mut()[..size]) + self.meta.handle, endpoint, size); + Ok(packet_buf) } /// Enqueue a packet to be sent to a given remote address, and fill it from a slice. @@ -265,10 +224,11 @@ impl<'a, 'b> IcmpSocket<'a, 'b> { /// /// This function returns `Err(Error::Exhausted)` if the receive buffer is empty. pub fn recv(&mut self) -> Result<(&[u8], IpAddress)> { - let packet_buf = self.rx_buffer.dequeue_one()?; + let (endpoint, packet_buf) = self.rx_buffer.dequeue()?; + net_trace!("{}:{}: receive {} buffered octets", - self.meta.handle, packet_buf.endpoint, packet_buf.size); - Ok((&packet_buf.as_ref(), packet_buf.endpoint)) + self.meta.handle, endpoint, packet_buf.len()); + Ok((packet_buf, endpoint)) } /// Dequeue a packet received from a remote endpoint, copy the payload into the given slice, @@ -309,29 +269,27 @@ impl<'a, 'b> IcmpSocket<'a, 'b> { } pub(crate) fn process(&mut self, ip_repr: &IpRepr, icmp_repr: &Icmpv4Repr, - cksum: &ChecksumCapabilities) -> Result<()> { - let packet_buf = self.rx_buffer.enqueue_one_with(|buf| buf.resize(icmp_repr.buffer_len()))?; - packet_buf.endpoint = ip_repr.src_addr(); + _cksum: &ChecksumCapabilities) -> Result<()> { + let packet_buf = self.rx_buffer.enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?; + icmp_repr.emit(&mut Icmpv4Packet::new(packet_buf), &ChecksumCapabilities::default()); + net_trace!("{}:{}: receiving {} octets", - self.meta.handle, packet_buf.endpoint, packet_buf.size); - let mut packet = Icmpv4Packet::new(packet_buf.as_mut()); - icmp_repr.emit(&mut packet, cksum); + self.meta.handle, icmp_repr.buffer_len(), packet_buf.len()); Ok(()) } - pub(crate) fn dispatch(&mut self, caps: &DeviceCapabilities, emit: F) -> Result<()> + pub(crate) fn dispatch(&mut self, _caps: &DeviceCapabilities, emit: F) -> Result<()> where F: FnOnce((IpRepr, Icmpv4Repr)) -> Result<()> { - let handle = self.meta.handle; + let handle = self.meta.handle; let hop_limit = self.hop_limit.unwrap_or(64); - let checksum = &caps.checksum; - self.tx_buffer.dequeue_one_with(|packet_buf| { + self.tx_buffer.dequeue_with(|remote_endpoint, packet_buf| { net_trace!("{}:{}: sending {} octets", - handle, packet_buf.endpoint, packet_buf.size); - match packet_buf.endpoint { + handle, remote_endpoint, packet_buf.len()); + match *remote_endpoint { IpAddress::Ipv4(ipv4_addr) => { - let packet = Icmpv4Packet::new(packet_buf.as_ref()); - let repr = Icmpv4Repr::parse(&packet, checksum)?; + let packet = Icmpv4Packet::new(&*packet_buf); + let repr = Icmpv4Repr::parse(&packet, &ChecksumCapabilities::default())?; let ip_repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address::default(), dst_addr: ipv4_addr, @@ -367,23 +325,19 @@ mod test { use wire::{IpAddress, Icmpv4DstUnreachable}; use super::*; - fn buffer(packets: usize) -> SocketBuffer<'static, 'static> { - let mut storage = vec![]; - for _ in 0..packets { - storage.push(PacketBuffer::new(vec![0; 46])) - } - SocketBuffer::new(storage) + fn buffer(packets: usize) -> IcmpSocketBuffer<'static, 'static> { + IcmpSocketBuffer::new(vec![IcmpPacketMetadata::empty(); packets], vec![0; 46 * packets]) } - fn socket(rx_buffer: SocketBuffer<'static, 'static>, - tx_buffer: SocketBuffer<'static, 'static>) -> IcmpSocket<'static, 'static> { + fn socket(rx_buffer: IcmpSocketBuffer<'static, 'static>, + tx_buffer: IcmpSocketBuffer<'static, 'static>) -> IcmpSocket<'static, 'static> { IcmpSocket::new(rx_buffer, tx_buffer) } const REMOTE_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); - const LOCAL_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); - const REMOTE_IP: IpAddress = IpAddress::Ipv4(REMOTE_IPV4); - const LOCAL_IP: IpAddress = IpAddress::Ipv4(LOCAL_IPV4); + const LOCAL_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); + const REMOTE_IP: IpAddress = IpAddress::Ipv4(REMOTE_IPV4); + const LOCAL_IP: IpAddress = IpAddress::Ipv4(LOCAL_IPV4); const LOCAL_PORT: u16 = 53; const LOCAL_END: IpEndpoint = IpEndpoint { addr: LOCAL_IP, port: LOCAL_PORT }; diff --git a/src/socket/mod.rs b/src/socket/mod.rs index c09e6d0..8c4833c 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -34,8 +34,8 @@ pub use self::raw::{RawPacketMetadata, RawSocket}; #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] -pub use self::icmp::{PacketBuffer as IcmpPacketBuffer, - SocketBuffer as IcmpSocketBuffer, +pub use self::icmp::{IcmpPacketMetadata, + IcmpSocketBuffer, Endpoint as IcmpEndpoint, IcmpSocket};