Working UDP loopback.
This commit is contained in:
parent
c672b94ef7
commit
31e44445f5
|
@ -101,7 +101,8 @@ cargo run --example smoltcpserver -- tap0
|
||||||
|
|
||||||
It responds to:
|
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
|
License
|
||||||
-------
|
-------
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
extern crate smoltcp;
|
extern crate smoltcp;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use smoltcp::Error;
|
||||||
use smoltcp::phy::{Tracer, TapInterface};
|
use smoltcp::phy::{Tracer, TapInterface};
|
||||||
use smoltcp::wire::{EthernetFrame, EthernetAddress, InternetAddress, InternetEndpoint};
|
use smoltcp::wire::{EthernetFrame, EthernetAddress, InternetAddress, InternetEndpoint};
|
||||||
use smoltcp::iface::{SliceArpCache, EthernetInterface};
|
use smoltcp::iface::{SliceArpCache, EthernetInterface};
|
||||||
use smoltcp::socket::{Socket, UdpSocket, UdpBuffer, UdpBufferElem};
|
use smoltcp::socket::{UdpSocket, AsSocket, UdpBuffer, UdpBufferElem};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let ifname = env::args().nth(1).unwrap();
|
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_rx_buffer = UdpBuffer::new(vec![UdpBufferElem::new(vec![0; 2048])]);
|
||||||
let udp_tx_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,
|
let mut iface = EthernetInterface::new(device, arp_cache,
|
||||||
hardware_addr, &mut protocol_addrs[..], &mut sockets[..]);
|
hardware_addr, &mut protocol_addrs[..], &mut sockets[..]);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match iface.poll() {
|
match iface.poll() {
|
||||||
Ok(()) => (),
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,21 +21,21 @@ pub struct Interface<'a,
|
||||||
DeviceT: Device,
|
DeviceT: Device,
|
||||||
ArpCacheT: ArpCache,
|
ArpCacheT: ArpCache,
|
||||||
ProtocolAddrsT: BorrowMut<[InternetAddress]>,
|
ProtocolAddrsT: BorrowMut<[InternetAddress]>,
|
||||||
SocketsT: BorrowMut<[&'a mut Socket]>
|
SocketsT: BorrowMut<[Socket<'a>]>
|
||||||
> {
|
> {
|
||||||
device: DeviceT,
|
device: DeviceT,
|
||||||
arp_cache: ArpCacheT,
|
arp_cache: ArpCacheT,
|
||||||
hardware_addr: EthernetAddress,
|
hardware_addr: EthernetAddress,
|
||||||
protocol_addrs: ProtocolAddrsT,
|
protocol_addrs: ProtocolAddrsT,
|
||||||
sockets: SocketsT,
|
sockets: SocketsT,
|
||||||
phantom: PhantomData<&'a mut Socket>
|
phantom: PhantomData<Socket<'a>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a,
|
impl<'a,
|
||||||
DeviceT: Device,
|
DeviceT: Device,
|
||||||
ArpCacheT: ArpCache,
|
ArpCacheT: ArpCache,
|
||||||
ProtocolAddrsT: BorrowMut<[InternetAddress]>,
|
ProtocolAddrsT: BorrowMut<[InternetAddress]>,
|
||||||
SocketsT: BorrowMut<[&'a mut Socket]>
|
SocketsT: BorrowMut<[Socket<'a>]>
|
||||||
> Interface<'a, DeviceT, ArpCacheT, ProtocolAddrsT, SocketsT> {
|
> Interface<'a, DeviceT, ArpCacheT, ProtocolAddrsT, SocketsT> {
|
||||||
/// Create a network interface using the provided network device.
|
/// Create a network interface using the provided network device.
|
||||||
///
|
///
|
||||||
|
@ -106,8 +106,8 @@ impl<'a,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the set of sockets owned by the interface.
|
/// Get the set of sockets owned by the interface.
|
||||||
pub fn with_sockets<R, F: FnOnce(&mut [&'a mut Socket]) -> R>(&mut self, f: F) -> R {
|
pub fn sockets(&mut self) -> &mut [Socket<'a>] {
|
||||||
f(self.sockets.borrow_mut())
|
self.sockets.borrow_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive and process a packet, if available.
|
/// Receive and process a packet, if available.
|
||||||
|
|
|
@ -33,10 +33,9 @@ pub trait PacketRepr {
|
||||||
|
|
||||||
/// A network socket.
|
/// A network socket.
|
||||||
///
|
///
|
||||||
/// This interface abstracts the various types of sockets based on the IP protocol.
|
/// This enumeration 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
|
/// To downcast a `Socket` value down to a concrete socket, use
|
||||||
/// buffer types in sockets assigned to the same interface. To access a socket through this
|
/// the [AsSocket](trait.AsSocket.html) trait, and call e.g. `socket.as_socket::<UdpSocket<_>>()`.
|
||||||
/// interface, cast it using `.as::<T>()`.
|
|
||||||
///
|
///
|
||||||
/// The `collect` and `dispatch` functions are fundamentally asymmetric and thus differ in
|
/// 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,
|
/// 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
|
/// 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
|
/// 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.
|
/// 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.
|
/// Process a packet received from a network interface.
|
||||||
///
|
///
|
||||||
/// This function checks if the packet contained in the payload matches the socket endpoint,
|
/// This function checks if the packet contained in the payload matches the socket endpoint,
|
||||||
|
@ -53,9 +58,15 @@ pub trait Socket {
|
||||||
/// is returned.
|
/// is returned.
|
||||||
///
|
///
|
||||||
/// This function is used internally by the networking stack.
|
/// This function is used internally by the networking stack.
|
||||||
fn collect(&mut self, src_addr: &Address, dst_addr: &Address,
|
pub fn collect(&mut self, src_addr: &Address, dst_addr: &Address,
|
||||||
protocol: ProtocolType, payload: &[u8])
|
protocol: ProtocolType, payload: &[u8])
|
||||||
-> Result<(), Error>;
|
-> 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.
|
/// Prepare a packet to be transmitted to a network interface.
|
||||||
///
|
///
|
||||||
|
@ -64,7 +75,30 @@ pub trait Socket {
|
||||||
/// is returned.
|
/// is returned.
|
||||||
///
|
///
|
||||||
/// This function is used internally by the networking stack.
|
/// This function is used internally by the networking stack.
|
||||||
fn dispatch(&mut self, f: &mut FnMut(&Address, &Address,
|
pub fn dispatch(&mut self, f: &mut FnMut(&Address, &Address,
|
||||||
ProtocolType, &PacketRepr) -> Result<(), Error>)
|
ProtocolType, &PacketRepr) -> Result<(), Error>)
|
||||||
-> 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<T> {
|
||||||
|
fn as_socket(&mut self) -> &mut T;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AsSocket<UdpSocket<'a>> for Socket<'a> {
|
||||||
|
fn as_socket(&mut self) -> &mut UdpSocket<'a> {
|
||||||
|
match self {
|
||||||
|
&mut Socket::Udp(ref mut socket) => socket,
|
||||||
|
_ => panic!(".as_socket::<UdpSocket> called on wrong socket type")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,14 @@ impl<'a> BufferElem<'a> {
|
||||||
payload: payload.into()
|
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.
|
/// An UDP packet buffer.
|
||||||
|
@ -108,12 +116,12 @@ pub struct UdpSocket<'a> {
|
||||||
impl<'a> UdpSocket<'a> {
|
impl<'a> UdpSocket<'a> {
|
||||||
/// Create an UDP socket with the given buffers.
|
/// Create an UDP socket with the given buffers.
|
||||||
pub fn new(endpoint: Endpoint, rx_buffer: Buffer<'a>, tx_buffer: Buffer<'a>)
|
pub fn new(endpoint: Endpoint, rx_buffer: Buffer<'a>, tx_buffer: Buffer<'a>)
|
||||||
-> UdpSocket<'a> {
|
-> Socket<'a> {
|
||||||
UdpSocket {
|
Socket::Udp(UdpSocket {
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
rx_buffer: rx_buffer,
|
rx_buffer: rx_buffer,
|
||||||
tx_buffer: tx_buffer
|
tx_buffer: tx_buffer
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enqueue a packet to be sent to a given remote endpoint, and return a pointer
|
/// 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());
|
let packet_buf = try!(self.tx_buffer.enqueue());
|
||||||
packet_buf.endpoint = endpoint;
|
packet_buf.endpoint = endpoint;
|
||||||
packet_buf.size = size;
|
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
|
/// Dequeue a packet received from a remote endpoint, and return the endpoint as well
|
||||||
/// as a pointer to the payload.
|
/// as a pointer to the payload.
|
||||||
///
|
///
|
||||||
/// This function returns `Err(Error::Exhausted)` if the receive buffer is empty.
|
/// This function returns `Err(Error::Exhausted)` if the receive buffer is empty.
|
||||||
pub fn recv<R, F>(&mut self) -> Result<(Endpoint, &[u8]), Error> {
|
pub fn recv(&mut self) -> Result<(Endpoint, &[u8]), Error> {
|
||||||
let packet_buf = try!(self.rx_buffer.dequeue());
|
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> {
|
/// Dequeue a packet received from a remote endpoint, and return the endpoint as well
|
||||||
fn collect(&mut self, src_addr: &Address, dst_addr: &Address,
|
/// 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])
|
protocol: ProtocolType, payload: &[u8])
|
||||||
-> Result<(), Error> {
|
-> Result<(), Error> {
|
||||||
if protocol != ProtocolType::Udp { return Err(Error::Rejected) }
|
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());
|
let packet_buf = try!(self.rx_buffer.enqueue());
|
||||||
packet_buf.endpoint = Endpoint { addr: *src_addr, port: repr.src_port };
|
packet_buf.endpoint = Endpoint { addr: *src_addr, port: repr.src_port };
|
||||||
packet_buf.size = repr.payload.len();
|
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(())
|
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>)
|
ProtocolType, &PacketRepr) -> Result<(), Error>)
|
||||||
-> Result<(), Error> {
|
-> Result<(), Error> {
|
||||||
let packet_buf = try!(self.tx_buffer.dequeue());
|
let packet_buf = try!(self.tx_buffer.dequeue());
|
||||||
|
@ -169,7 +197,7 @@ impl<'a> Socket for UdpSocket<'a> {
|
||||||
&UdpRepr {
|
&UdpRepr {
|
||||||
src_port: self.endpoint.port,
|
src_port: self.endpoint.port,
|
||||||
dst_port: packet_buf.endpoint.port,
|
dst_port: packet_buf.endpoint.port,
|
||||||
payload: &packet_buf.payload[..]
|
payload: &packet_buf.as_ref()[..]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,18 @@ pub struct Packet<T: AsRef<[u8]>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod field {
|
mod field {
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use wire::field::*;
|
use wire::field::*;
|
||||||
|
|
||||||
pub const SRC_PORT: Field = 0..2;
|
pub const SRC_PORT: Field = 0..2;
|
||||||
pub const DST_PORT: Field = 2..4;
|
pub const DST_PORT: Field = 2..4;
|
||||||
pub const LENGTH: Field = 4..6;
|
pub const LENGTH: Field = 4..6;
|
||||||
pub const CHECKSUM: Field = 6..8;
|
pub const CHECKSUM: Field = 6..8;
|
||||||
pub const PAYLOAD: Rest = 8..;
|
|
||||||
|
pub fn PAYLOAD(length: u16) -> Field {
|
||||||
|
CHECKSUM.end..(length as usize)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>> Packet<T> {
|
impl<T: AsRef<[u8]>> Packet<T> {
|
||||||
|
@ -90,8 +95,9 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
|
||||||
/// Return a pointer to the payload.
|
/// Return a pointer to the payload.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn payload(&self) -> &'a [u8] {
|
pub fn payload(&self) -> &'a [u8] {
|
||||||
|
let length = self.len();
|
||||||
let data = self.buffer.as_ref();
|
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.
|
/// Return a mutable pointer to the type-specific data.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn payload_mut(&mut self) -> &mut [u8] {
|
pub fn payload_mut(&mut self) -> &mut [u8] {
|
||||||
|
let length = self.len();
|
||||||
let mut data = self.buffer.as_mut();
|
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.
|
/// Return the length of a packet that will be emitted from this high-level representation.
|
||||||
pub fn len(&self) -> usize {
|
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.
|
/// 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]> {
|
where T: AsRef<[u8]> + AsMut<[u8]> {
|
||||||
packet.set_src_port(self.src_port);
|
packet.set_src_port(self.src_port);
|
||||||
packet.set_dst_port(self.dst_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.payload_mut().copy_from_slice(self.payload);
|
||||||
packet.fill_checksum(src_addr, dst_addr)
|
packet.fill_checksum(src_addr, dst_addr)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue