245 lines
8.0 KiB
Rust
245 lines
8.0 KiB
Rust
|
use managed::Managed;
|
||
|
|
||
|
use Error;
|
||
|
use phy::DeviceLimits;
|
||
|
use wire::{IpVersion, IpProtocol, Ipv4Repr, Ipv4Packet};
|
||
|
use socket::{IpRepr, IpPayload, Socket};
|
||
|
use storage::{Resettable, RingBuffer};
|
||
|
|
||
|
/// A buffered raw IP packet.
|
||
|
#[derive(Debug)]
|
||
|
pub struct PacketBuffer<'a> {
|
||
|
size: usize,
|
||
|
payload: Managed<'a, [u8]>,
|
||
|
}
|
||
|
|
||
|
impl<'a> PacketBuffer<'a> {
|
||
|
/// Create a buffered packet.
|
||
|
pub fn new<T>(payload: T) -> PacketBuffer<'a>
|
||
|
where T: Into<Managed<'a, [u8]>> {
|
||
|
PacketBuffer {
|
||
|
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]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<'a> Resettable for PacketBuffer<'a> {
|
||
|
fn reset(&mut self) {
|
||
|
self.size = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// A raw IP packet ring buffer.
|
||
|
pub type SocketBuffer<'a, 'b: 'a> = RingBuffer<'a, PacketBuffer<'b>>;
|
||
|
|
||
|
/// A raw IP socket.
|
||
|
///
|
||
|
/// A raw socket is bound to a specific IP protocol, and owns
|
||
|
/// transmit and receive packet buffers.
|
||
|
#[derive(Debug)]
|
||
|
pub struct RawSocket<'a, 'b: 'a> {
|
||
|
ip_version: IpVersion,
|
||
|
ip_protocol: IpProtocol,
|
||
|
rx_buffer: SocketBuffer<'a, 'b>,
|
||
|
tx_buffer: SocketBuffer<'a, 'b>,
|
||
|
debug_id: usize,
|
||
|
}
|
||
|
|
||
|
impl<'a, 'b> RawSocket<'a, 'b> {
|
||
|
/// Create a raw IP socket bound to the given IP version and datagram protocol,
|
||
|
/// with the given buffers.
|
||
|
pub fn new(ip_version: IpVersion, ip_protocol: IpProtocol,
|
||
|
rx_buffer: SocketBuffer<'a, 'b>,
|
||
|
tx_buffer: SocketBuffer<'a, 'b>) -> Socket<'a, 'b> {
|
||
|
Socket::Raw(RawSocket {
|
||
|
ip_version,
|
||
|
ip_protocol,
|
||
|
rx_buffer,
|
||
|
tx_buffer,
|
||
|
debug_id: 0,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
/// Return the debug identifier.
|
||
|
pub fn debug_id(&self) -> usize {
|
||
|
self.debug_id
|
||
|
}
|
||
|
|
||
|
/// Set the debug identifier.
|
||
|
///
|
||
|
/// The debug identifier is a number printed in socket trace messages.
|
||
|
/// It could as well be used by the user code.
|
||
|
pub fn set_debug_id(&mut self, id: usize) {
|
||
|
self.debug_id = id;
|
||
|
}
|
||
|
|
||
|
/// Return the IP version the socket is bound to.
|
||
|
pub fn ip_version(&self) -> IpVersion {
|
||
|
self.ip_version
|
||
|
}
|
||
|
|
||
|
/// Return the IP protocol the socket is bound to.
|
||
|
pub fn ip_protocol(&self) -> IpProtocol {
|
||
|
self.ip_protocol
|
||
|
}
|
||
|
|
||
|
/// Check whether the transmit buffer is full.
|
||
|
pub fn can_send(&self) -> bool {
|
||
|
!self.tx_buffer.full()
|
||
|
}
|
||
|
|
||
|
/// Check whether the receive buffer is not empty.
|
||
|
pub fn can_recv(&self) -> bool {
|
||
|
!self.rx_buffer.empty()
|
||
|
}
|
||
|
|
||
|
/// Enqueue a packet to send, and return a pointer to its payload.
|
||
|
///
|
||
|
/// This function returns `Err(())` if the size is greater than what
|
||
|
/// the transmit buffer can accomodate.
|
||
|
pub fn send(&mut self, size: usize) -> Result<&mut [u8], ()> {
|
||
|
let packet_buf = self.tx_buffer.enqueue()?;
|
||
|
packet_buf.size = size;
|
||
|
net_trace!("[{}]:{}:{}: buffer to send {} octets",
|
||
|
self.debug_id, self.ip_version, self.ip_protocol,
|
||
|
packet_buf.size);
|
||
|
Ok(&mut packet_buf.as_mut()[..size])
|
||
|
}
|
||
|
|
||
|
/// Enqueue a packet to send, and fill it from a slice.
|
||
|
///
|
||
|
/// See also [send](#method.send).
|
||
|
pub fn send_slice(&mut self, data: &[u8]) -> Result<usize, ()> {
|
||
|
let buffer = self.send(data.len())?;
|
||
|
let data = &data[..buffer.len()];
|
||
|
buffer.copy_from_slice(data);
|
||
|
Ok(data.len())
|
||
|
}
|
||
|
|
||
|
/// Dequeue a packet, and return a pointer to the payload.
|
||
|
///
|
||
|
/// This function returns `Err(())` if the receive buffer is empty.
|
||
|
pub fn recv(&mut self) -> Result<&[u8], ()> {
|
||
|
let packet_buf = self.rx_buffer.dequeue()?;
|
||
|
net_trace!("[{}]:{}:{}: receive {} buffered octets",
|
||
|
self.debug_id, self.ip_version, self.ip_protocol,
|
||
|
packet_buf.size);
|
||
|
Ok(&packet_buf.as_ref()[..packet_buf.size])
|
||
|
}
|
||
|
|
||
|
/// Dequeue a packet, and copy the payload into the given slice.
|
||
|
///
|
||
|
/// See also [recv](#method.recv).
|
||
|
pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<usize, ()> {
|
||
|
let buffer = self.recv()?;
|
||
|
data[..buffer.len()].copy_from_slice(buffer);
|
||
|
Ok(buffer.len())
|
||
|
}
|
||
|
|
||
|
/// See [Socket::process](enum.Socket.html#method.process).
|
||
|
pub fn process(&mut self, _timestamp: u64, ip_repr: &IpRepr,
|
||
|
payload: &[u8]) -> Result<(), Error> {
|
||
|
match self.ip_version {
|
||
|
IpVersion::Ipv4 => {
|
||
|
if ip_repr.protocol() != self.ip_protocol {
|
||
|
return Err(Error::Rejected);
|
||
|
}
|
||
|
let header_len = ip_repr.buffer_len();
|
||
|
let packet_buf = self.rx_buffer.enqueue().map_err(|()| Error::Exhausted)?;
|
||
|
packet_buf.size = header_len + payload.len();
|
||
|
ip_repr.emit(&mut packet_buf.as_mut()[..header_len]);
|
||
|
packet_buf.as_mut()[header_len..header_len + payload.len()]
|
||
|
.copy_from_slice(payload);
|
||
|
net_trace!("[{}]:{}:{}: receiving {} octets",
|
||
|
self.debug_id, self.ip_version, self.ip_protocol,
|
||
|
packet_buf.size);
|
||
|
Ok(())
|
||
|
}
|
||
|
IpVersion::__Nonexhaustive => unreachable!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// See [Socket::dispatch](enum.Socket.html#method.dispatch).
|
||
|
pub fn dispatch<F, R>(&mut self, _timestamp: u64, _limits: &DeviceLimits,
|
||
|
emit: &mut F) -> Result<R, Error>
|
||
|
where F: FnMut(&IpRepr, &IpPayload) -> Result<R, Error> {
|
||
|
let mut packet_buf = self.tx_buffer.dequeue_mut().map_err(|()| Error::Exhausted)?;
|
||
|
net_trace!("[{}]:{}:{}: sending {} octets",
|
||
|
self.debug_id, self.ip_version, self.ip_protocol,
|
||
|
packet_buf.size);
|
||
|
|
||
|
match self.ip_version {
|
||
|
IpVersion::Ipv4 => {
|
||
|
let mut ipv4_packet = Ipv4Packet::new(packet_buf.as_mut())?;
|
||
|
ipv4_packet.fill_checksum();
|
||
|
|
||
|
let ipv4_packet = Ipv4Packet::new(&*ipv4_packet.into_inner())?;
|
||
|
let raw_repr = RawRepr(ipv4_packet.payload());
|
||
|
let ipv4_repr = Ipv4Repr::parse(&ipv4_packet)?;
|
||
|
emit(&IpRepr::Ipv4(ipv4_repr), &raw_repr)
|
||
|
}
|
||
|
IpVersion::__Nonexhaustive => unreachable!()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct RawRepr<'a>(&'a [u8]);
|
||
|
|
||
|
impl<'a> IpPayload for RawRepr<'a> {
|
||
|
fn buffer_len(&self) -> usize {
|
||
|
self.0.len()
|
||
|
}
|
||
|
|
||
|
fn emit(&self, _repr: &IpRepr, payload: &mut [u8]) {
|
||
|
payload.copy_from_slice(self.0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod test {
|
||
|
use super::*;
|
||
|
|
||
|
#[test]
|
||
|
pub fn test_buffer() {
|
||
|
let mut storage = vec![];
|
||
|
for _ in 0..5 {
|
||
|
storage.push(PacketBuffer::new(vec![0]))
|
||
|
}
|
||
|
let mut buffer = SocketBuffer::new(&mut storage[..]);
|
||
|
|
||
|
assert_eq!(buffer.empty(), true);
|
||
|
assert_eq!(buffer.full(), false);
|
||
|
buffer.enqueue().unwrap().size = 1;
|
||
|
assert_eq!(buffer.empty(), false);
|
||
|
assert_eq!(buffer.full(), false);
|
||
|
buffer.enqueue().unwrap().size = 2;
|
||
|
buffer.enqueue().unwrap().size = 3;
|
||
|
assert_eq!(buffer.dequeue().unwrap().size, 1);
|
||
|
assert_eq!(buffer.dequeue().unwrap().size, 2);
|
||
|
buffer.enqueue().unwrap().size = 4;
|
||
|
buffer.enqueue().unwrap().size = 5;
|
||
|
buffer.enqueue().unwrap().size = 6;
|
||
|
buffer.enqueue().unwrap().size = 7;
|
||
|
assert_eq!(buffer.enqueue().unwrap_err(), ());
|
||
|
assert_eq!(buffer.empty(), false);
|
||
|
assert_eq!(buffer.full(), true);
|
||
|
assert_eq!(buffer.dequeue().unwrap().size, 3);
|
||
|
assert_eq!(buffer.dequeue().unwrap().size, 4);
|
||
|
assert_eq!(buffer.dequeue().unwrap().size, 5);
|
||
|
assert_eq!(buffer.dequeue().unwrap().size, 6);
|
||
|
assert_eq!(buffer.dequeue().unwrap().size, 7);
|
||
|
assert_eq!(buffer.dequeue().unwrap_err(), ());
|
||
|
assert_eq!(buffer.empty(), true);
|
||
|
assert_eq!(buffer.full(), false);
|
||
|
}
|
||
|
}
|