diff --git a/README.md b/README.md index b80a09c..a0ea697 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,8 @@ cargo run --example smoltcpserver -- tap0 It responds to: - * pings (`ping 192.168.69.1`). + * pings (`ping 192.168.69.1`), + * UDP packets on port 6969 (`socat stdio udp4-connect:192.168.69.1:6969 <<<"abcdefg"`). License ------- diff --git a/examples/smoltcpserver.rs b/examples/smoltcpserver.rs index a9d0a5d..97df7dd 100644 --- a/examples/smoltcpserver.rs +++ b/examples/smoltcpserver.rs @@ -2,10 +2,11 @@ extern crate smoltcp; use std::env; +use smoltcp::Error; use smoltcp::phy::{Tracer, TapInterface}; use smoltcp::wire::{EthernetFrame, EthernetAddress, InternetAddress, InternetEndpoint}; use smoltcp::iface::{SliceArpCache, EthernetInterface}; -use smoltcp::socket::{Socket, UdpSocket, UdpBuffer, UdpBufferElem}; +use smoltcp::socket::{UdpSocket, AsSocket, UdpBuffer, UdpBufferElem}; fn main() { let ifname = env::args().nth(1).unwrap(); @@ -22,16 +23,34 @@ fn main() { let udp_rx_buffer = UdpBuffer::new(vec![UdpBufferElem::new(vec![0; 2048])]); let udp_tx_buffer = UdpBuffer::new(vec![UdpBufferElem::new(vec![0; 2048])]); - let mut udp_socket = UdpSocket::new(endpoint, udp_rx_buffer, udp_tx_buffer); + let udp_socket = UdpSocket::new(endpoint, udp_rx_buffer, udp_tx_buffer); - let mut sockets: [&mut Socket; 1] = [&mut udp_socket]; + let mut sockets = [udp_socket]; let mut iface = EthernetInterface::new(device, arp_cache, hardware_addr, &mut protocol_addrs[..], &mut sockets[..]); loop { match iface.poll() { Ok(()) => (), - Err(e) => println!("{}", e) + Err(e) => println!("error {}", e) + } + + let udp_socket = iface.sockets()[0].as_socket(); + let client = match udp_socket.recv() { + Ok((endpoint, data)) => { + println!("data {:?} from {}", &data[..8], endpoint); + Some(endpoint) + } + Err(Error::Exhausted) => { + None + } + Err(e) => { + println!("error {}", e); + None + } + }; + if let Some(endpoint) = client { + udp_socket.send_slice(endpoint, "hihihi".as_bytes()).unwrap() } } } diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 2d41eb1..e19e550 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -21,21 +21,21 @@ pub struct Interface<'a, DeviceT: Device, ArpCacheT: ArpCache, ProtocolAddrsT: BorrowMut<[InternetAddress]>, - SocketsT: BorrowMut<[&'a mut Socket]> + SocketsT: BorrowMut<[Socket<'a>]> > { device: DeviceT, arp_cache: ArpCacheT, hardware_addr: EthernetAddress, protocol_addrs: ProtocolAddrsT, sockets: SocketsT, - phantom: PhantomData<&'a mut Socket> + phantom: PhantomData> } impl<'a, DeviceT: Device, ArpCacheT: ArpCache, ProtocolAddrsT: BorrowMut<[InternetAddress]>, - SocketsT: BorrowMut<[&'a mut Socket]> + SocketsT: BorrowMut<[Socket<'a>]> > Interface<'a, DeviceT, ArpCacheT, ProtocolAddrsT, SocketsT> { /// Create a network interface using the provided network device. /// @@ -106,8 +106,8 @@ impl<'a, } /// Get the set of sockets owned by the interface. - pub fn with_sockets R>(&mut self, f: F) -> R { - f(self.sockets.borrow_mut()) + pub fn sockets(&mut self) -> &mut [Socket<'a>] { + self.sockets.borrow_mut() } /// Receive and process a packet, if available. diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 74f5f00..81cc67f 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -33,10 +33,9 @@ pub trait PacketRepr { /// A network socket. /// -/// This interface abstracts the various types of sockets based on the IP protocol. -/// It is necessarily implemented as a trait, and not as an enumeration, to allow using different -/// buffer types in sockets assigned to the same interface. To access a socket through this -/// interface, cast it using `.as::()`. +/// This enumeration abstracts the various types of sockets based on the IP protocol. +/// To downcast a `Socket` value down to a concrete socket, use +/// the [AsSocket](trait.AsSocket.html) trait, and call e.g. `socket.as_socket::>()`. /// /// The `collect` and `dispatch` functions are fundamentally asymmetric and thus differ in /// their use of the [trait PacketRepr](trait.PacketRepr.html). When `collect` is called, @@ -45,7 +44,13 @@ pub trait PacketRepr { /// which is rather inelegant. Conversely, when `dispatch` is called, the packet length is /// not yet known and the packet storage has to be allocated; but the `&PacketRepr` is sufficient /// since the lower layers treat the packet as an opaque octet sequence. -pub trait Socket { +pub enum Socket<'a> { + Udp(UdpSocket<'a>), + #[doc(hidden)] + __Nonexhaustive +} + +impl<'a> Socket<'a> { /// Process a packet received from a network interface. /// /// This function checks if the packet contained in the payload matches the socket endpoint, @@ -53,9 +58,15 @@ pub trait Socket { /// is returned. /// /// This function is used internally by the networking stack. - fn collect(&mut self, src_addr: &Address, dst_addr: &Address, - protocol: ProtocolType, payload: &[u8]) - -> Result<(), Error>; + pub fn collect(&mut self, src_addr: &Address, dst_addr: &Address, + protocol: ProtocolType, payload: &[u8]) + -> Result<(), Error> { + match self { + &mut Socket::Udp(ref mut socket) => + socket.collect(src_addr, dst_addr, protocol, payload), + &mut Socket::__Nonexhaustive => unreachable!() + } + } /// Prepare a packet to be transmitted to a network interface. /// @@ -64,7 +75,30 @@ pub trait Socket { /// is returned. /// /// This function is used internally by the networking stack. - fn dispatch(&mut self, f: &mut FnMut(&Address, &Address, - ProtocolType, &PacketRepr) -> Result<(), Error>) - -> Result<(), Error>; + pub fn dispatch(&mut self, f: &mut FnMut(&Address, &Address, + ProtocolType, &PacketRepr) -> Result<(), Error>) + -> Result<(), Error> { + match self { + &mut Socket::Udp(ref mut socket) => + socket.dispatch(f), + &mut Socket::__Nonexhaustive => unreachable!() + } + } +} + +/// A conversion trait for network sockets. +/// +/// This trait is used to concisely downcast [Socket](trait.Socket.html) values to their +/// concrete types. +pub trait AsSocket { + fn as_socket(&mut self) -> &mut T; +} + +impl<'a> AsSocket> for Socket<'a> { + fn as_socket(&mut self) -> &mut UdpSocket<'a> { + match self { + &mut Socket::Udp(ref mut socket) => socket, + _ => panic!(".as_socket:: called on wrong socket type") + } + } } diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 584efbe..d778fdb 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -25,6 +25,14 @@ impl<'a> BufferElem<'a> { 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] + } } /// An UDP packet buffer. @@ -108,12 +116,12 @@ pub struct UdpSocket<'a> { impl<'a> UdpSocket<'a> { /// Create an UDP socket with the given buffers. pub fn new(endpoint: Endpoint, rx_buffer: Buffer<'a>, tx_buffer: Buffer<'a>) - -> UdpSocket<'a> { - UdpSocket { + -> Socket<'a> { + Socket::Udp(UdpSocket { endpoint: endpoint, rx_buffer: rx_buffer, tx_buffer: tx_buffer - } + }) } /// Enqueue a packet to be sent to a given remote endpoint, and return a pointer @@ -125,21 +133,40 @@ impl<'a> UdpSocket<'a> { let packet_buf = try!(self.tx_buffer.enqueue()); packet_buf.endpoint = endpoint; packet_buf.size = size; - Ok(&mut packet_buf.payload.borrow_mut()[..size]) + Ok(&mut packet_buf.as_mut()[..size]) + } + + /// Enqueue a packete to be sent to a given remote endpoint, and fill it from a slice. + /// + /// See also [send](#method.send). + pub fn send_slice(&mut self, endpoint: Endpoint, data: &[u8]) -> Result<(), Error> { + let buffer = try!(self.send(endpoint, data.len())); + Ok(buffer.copy_from_slice(data)) } /// Dequeue a packet received from a remote endpoint, and return the endpoint as well /// as a pointer to the payload. /// /// This function returns `Err(Error::Exhausted)` if the receive buffer is empty. - pub fn recv(&mut self) -> Result<(Endpoint, &[u8]), Error> { + pub fn recv(&mut self) -> Result<(Endpoint, &[u8]), Error> { let packet_buf = try!(self.rx_buffer.dequeue()); - Ok((packet_buf.endpoint, &packet_buf.payload[..packet_buf.size])) + Ok((packet_buf.endpoint, &packet_buf.as_ref()[..packet_buf.size])) } -} -impl<'a> Socket for UdpSocket<'a> { - fn collect(&mut self, src_addr: &Address, dst_addr: &Address, + /// Dequeue a packet received from a remote endpoint, and return the endpoint as well + /// as copy the payload into the given slice. + /// + /// This function returns `Err(Error::Exhausted)` if the received packet has payload + /// larger than the provided slice. See also [recv](#method.recv). + pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<(Endpoint, usize), Error> { + let (endpoint, buffer) = try!(self.recv()); + if data.len() < buffer.len() { return Err(Error::Exhausted) } + data[..buffer.len()].copy_from_slice(buffer); + Ok((endpoint, buffer.len())) + } + + /// See [Socket::collect](enum.Socket.html#method.collect). + pub fn collect(&mut self, src_addr: &Address, dst_addr: &Address, protocol: ProtocolType, payload: &[u8]) -> Result<(), Error> { if protocol != ProtocolType::Udp { return Err(Error::Rejected) } @@ -155,11 +182,12 @@ impl<'a> Socket for UdpSocket<'a> { let packet_buf = try!(self.rx_buffer.enqueue()); packet_buf.endpoint = Endpoint { addr: *src_addr, port: repr.src_port }; packet_buf.size = repr.payload.len(); - packet_buf.payload.borrow_mut()[..repr.payload.len()].copy_from_slice(repr.payload); + packet_buf.as_mut()[..repr.payload.len()].copy_from_slice(repr.payload); Ok(()) } - fn dispatch(&mut self, f: &mut FnMut(&Address, &Address, + /// See [Socket::dispatch](enum.Socket.html#method.dispatch). + pub fn dispatch(&mut self, f: &mut FnMut(&Address, &Address, ProtocolType, &PacketRepr) -> Result<(), Error>) -> Result<(), Error> { let packet_buf = try!(self.tx_buffer.dequeue()); @@ -169,7 +197,7 @@ impl<'a> Socket for UdpSocket<'a> { &UdpRepr { src_port: self.endpoint.port, dst_port: packet_buf.endpoint.port, - payload: &packet_buf.payload[..] + payload: &packet_buf.as_ref()[..] }) } } diff --git a/src/wire/udp.rs b/src/wire/udp.rs index b9201cc..a028866 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -12,13 +12,18 @@ pub struct Packet> { } mod field { + #![allow(non_snake_case)] + use wire::field::*; pub const SRC_PORT: Field = 0..2; pub const DST_PORT: Field = 2..4; pub const LENGTH: Field = 4..6; pub const CHECKSUM: Field = 6..8; - pub const PAYLOAD: Rest = 8..; + + pub fn PAYLOAD(length: u16) -> Field { + CHECKSUM.end..(length as usize) + } } impl> Packet { @@ -90,8 +95,9 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> { /// Return a pointer to the payload. #[inline(always)] pub fn payload(&self) -> &'a [u8] { + let length = self.len(); let data = self.buffer.as_ref(); - &data[field::PAYLOAD] + &data[field::PAYLOAD(length)] } } @@ -147,8 +153,9 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> { /// Return a mutable pointer to the type-specific data. #[inline(always)] pub fn payload_mut(&mut self) -> &mut [u8] { + let length = self.len(); let mut data = self.buffer.as_mut(); - &mut data[field::PAYLOAD] + &mut data[field::PAYLOAD(length)] } } @@ -191,7 +198,7 @@ impl<'a> Repr<'a> { /// Return the length of a packet that will be emitted from this high-level representation. pub fn len(&self) -> usize { - field::PAYLOAD.start + self.payload.len() + field::CHECKSUM.end + self.payload.len() } /// Emit a high-level representation into an User Datagram Protocol packet. @@ -201,7 +208,7 @@ impl<'a> Repr<'a> { where T: AsRef<[u8]> + AsMut<[u8]> { packet.set_src_port(self.src_port); packet.set_dst_port(self.dst_port); - packet.set_len((field::PAYLOAD.start + self.payload.len()) as u16); + packet.set_len((field::CHECKSUM.end + self.payload.len()) as u16); packet.payload_mut().copy_from_slice(self.payload); packet.fill_checksum(src_addr, dst_addr) }