Distinguish PacketBuffer running out of payload space and capacity.

This makes the behavior of UdpSocket resemble that of RawSocket.
v0.7.x
whitequark 2018-02-22 11:54:36 +00:00
parent c474d0c32e
commit 41de9c7ee0
2 changed files with 28 additions and 12 deletions

View File

@ -6,13 +6,15 @@ use storage::{PacketBuffer, PacketMetadata};
use time::Instant;
use wire::{IpProtocol, IpRepr, IpEndpoint, UdpRepr};
/// A UDP packet metadata.
pub type UdpPacketMetadata = PacketMetadata<IpEndpoint>;
/// A UDP packet ring buffer.
pub type UdpSocketBuffer<'a, 'b> = PacketBuffer<'a, 'b, IpEndpoint>;
/// An User Datagram Protocol socket.
/// A User Datagram Protocol socket.
///
/// An UDP socket is bound to a specific endpoint, and owns transmit and receive
/// A UDP socket is bound to a specific endpoint, and owns transmit and receive
/// packet buffers.
#[derive(Debug)]
pub struct UdpSocket<'a, 'b: 'a> {
@ -112,8 +114,10 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
/// Enqueue a packet to be sent to a given remote endpoint, and return a pointer
/// to its payload.
///
/// This function returns `Err(Error::Exhausted)` if the transmit buffer is full and
/// `Err(Error::Unaddressable)` if local or remote port, or remote address are unspecified.
/// This function returns `Err(Error::Exhausted)` if the transmit buffer is full,
/// `Err(Error::Unaddressable)` if local or remote port, or remote address are unspecified,
/// and `Err(Error::Truncated)` if there is not enough transmit buffer capacity
/// to ever send this packet.
pub fn send(&mut self, size: usize, endpoint: IpEndpoint) -> Result<&mut [u8]> {
if self.endpoint.port == 0 { return Err(Error::Unaddressable) }
if !endpoint.is_specified() { return Err(Error::Unaddressable) }
@ -171,9 +175,7 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
let size = repr.payload.len();
let endpoint = IpEndpoint { addr: ip_repr.src_addr(), port: repr.src_port };
let payload_buf = self.rx_buffer.enqueue(size, endpoint)?;
assert_eq!(payload_buf.len(), size);
payload_buf.copy_from_slice(repr.payload);
self.rx_buffer.enqueue(size, endpoint)?.copy_from_slice(repr.payload);
net_trace!("{}:{}:{}: receiving {} octets",
self.meta.handle, self.endpoint,
@ -457,7 +459,7 @@ mod test {
assert_eq!(socket.bind(LOCAL_END), Ok(()));
let too_large = b"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefx";
assert_eq!(socket.send_slice(too_large, REMOTE_END), Err(Error::Exhausted));
assert_eq!(socket.send_slice(too_large, REMOTE_END), Err(Error::Truncated));
assert_eq!(socket.send_slice(&too_large[..16*4], REMOTE_END), Ok(()));
}

View File

@ -72,13 +72,21 @@ impl<'a, 'b, H> PacketBuffer<'a, 'b, H> {
/// Enqueue a single packet with the given header into the buffer, and
/// return a reference to its payload, or return `Err(Error::Exhausted)`
/// if the buffer is full or does not have enough spare payload space.
/// if the buffer is full, or return `Err(Error::Truncated)` if the buffer
/// does not have enough spare payload space.
pub fn enqueue(&mut self, size: usize, header: H) -> Result<&mut [u8]> {
if self.payload_ring.capacity() < size {
return Err(Error::Truncated)
}
if self.metadata_ring.is_full() {
return Err(Error::Exhausted)
}
let window = self.payload_ring.window();
let contig_window = self.payload_ring.contiguous_window();
if self.metadata_ring.is_full() || window < size ||
(window != contig_window && window - contig_window < size) {
if window < size || (window != contig_window && window - contig_window < size) {
return Err(Error::Exhausted)
}
@ -155,7 +163,7 @@ mod test {
fn test_simple() {
let mut buffer = buffer();
buffer.enqueue(6, ()).unwrap().copy_from_slice(b"abcdef");
assert_eq!(buffer.enqueue(32, ()), Err(Error::Exhausted));
assert_eq!(buffer.enqueue(16, ()), Err(Error::Exhausted));
assert_eq!(buffer.metadata_ring.len(), 1);
assert_eq!(buffer.dequeue().unwrap().1, &b"abcdef"[..]);
assert_eq!(buffer.dequeue(), Err(Error::Exhausted));
@ -232,4 +240,10 @@ mod test {
assert_eq!(buffer.enqueue(8, ()), Err(Error::Exhausted));
assert_eq!(buffer.metadata_ring.len(), 1);
}
#[test]
fn test_capacity_too_small() {
let mut buffer = buffer();
assert_eq!(buffer.enqueue(32, ()), Err(Error::Truncated));
}
}