|
@@ -3,17 +3,59 @@ use core::cmp::min;
|
|
|
use core::task::Waker;
|
|
|
|
|
|
use crate::iface::Context;
|
|
|
+use crate::phy::PacketId;
|
|
|
use crate::socket::PollAt;
|
|
|
#[cfg(feature = "async")]
|
|
|
use crate::socket::WakerRegistration;
|
|
|
use crate::storage::Empty;
|
|
|
use crate::wire::{IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, UdpRepr};
|
|
|
|
|
|
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
|
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
|
+pub struct UdpMetadata {
|
|
|
+ endpoint: IpEndpoint,
|
|
|
+ packet_id: PacketId,
|
|
|
+}
|
|
|
+
|
|
|
+impl UdpMetadata {
|
|
|
+ /// The endpoint of this metadata
|
|
|
+ pub fn endpoint(&self) -> IpEndpoint {
|
|
|
+ self.endpoint
|
|
|
+ }
|
|
|
+
|
|
|
+ /// The packet ID of this metadata
|
|
|
+ pub fn packet_id(self) -> PacketId {
|
|
|
+ self.packet_id
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Create a new metadata instance.
|
|
|
+ ///
|
|
|
+ /// If `packet_id` is `Some`, it can be used to track a datagram
|
|
|
+ /// as it is handled by the networking stack, or other elements of `smoltcp`
|
|
|
+ /// that interact with the specific datagram.
|
|
|
+ pub(crate) fn new(endpoint: IpEndpoint, packet_id: PacketId) -> Self {
|
|
|
+ Self {
|
|
|
+ endpoint,
|
|
|
+ packet_id,
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl core::fmt::Display for UdpMetadata {
|
|
|
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
|
+ #[cfg(feature = "packet-id")]
|
|
|
+ return write!(f, "{}, PacketID: {:?}", self.endpoint, self.packet_id);
|
|
|
+
|
|
|
+ #[cfg(not(feature = "packet-id"))]
|
|
|
+ write!(f, "{}", self.endpoint)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/// A UDP packet metadata.
|
|
|
-pub type PacketMetadata = crate::storage::PacketMetadata<IpEndpoint>;
|
|
|
+pub type PacketMetadata = crate::storage::PacketMetadata<UdpMetadata>;
|
|
|
|
|
|
/// A UDP packet ring buffer.
|
|
|
-pub type PacketBuffer<'a> = crate::storage::PacketBuffer<'a, IpEndpoint>;
|
|
|
+pub type PacketBuffer<'a> = crate::storage::PacketBuffer<'a, UdpMetadata>;
|
|
|
|
|
|
/// Error returned by [`Socket::bind`]
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
@@ -281,7 +323,7 @@ impl<'a> Socket<'a> {
|
|
|
|
|
|
let payload_buf = self
|
|
|
.tx_buffer
|
|
|
- .enqueue(size, remote_endpoint)
|
|
|
+ .enqueue(size, UdpMetadata::new(remote_endpoint, PacketId::empty()))
|
|
|
.map_err(|_| SendError::BufferFull)?;
|
|
|
|
|
|
net_trace!(
|
|
@@ -293,6 +335,44 @@ impl<'a> Socket<'a> {
|
|
|
Ok(payload_buf)
|
|
|
}
|
|
|
|
|
|
+ /// Send a packet, but marked, so that possible metadata about the packet
|
|
|
+ /// may be retrieved from the sending device
|
|
|
+ #[cfg(feature = "packet-id")]
|
|
|
+ pub fn send_marked(
|
|
|
+ &mut self,
|
|
|
+ size: usize,
|
|
|
+ remote_endpoint: IpEndpoint,
|
|
|
+ packet_id: u32,
|
|
|
+ ) -> Result<&mut [u8], SendError> {
|
|
|
+ if self.endpoint.port == 0 {
|
|
|
+ return Err(SendError::Unaddressable);
|
|
|
+ }
|
|
|
+ if remote_endpoint.addr.is_unspecified() {
|
|
|
+ return Err(SendError::Unaddressable);
|
|
|
+ }
|
|
|
+ if remote_endpoint.port == 0 {
|
|
|
+ return Err(SendError::Unaddressable);
|
|
|
+ }
|
|
|
+
|
|
|
+ let payload_buf = self
|
|
|
+ .tx_buffer
|
|
|
+ .enqueue(
|
|
|
+ size,
|
|
|
+ UdpMetadata::new(remote_endpoint, PacketId::new(packet_id)),
|
|
|
+ )
|
|
|
+ .map_err(|_| SendError::BufferFull)?;
|
|
|
+
|
|
|
+ net_trace!(
|
|
|
+ "udp:{}:{}: buffer to send {} octets (marked with ID {})",
|
|
|
+ self.endpoint,
|
|
|
+ remote_endpoint,
|
|
|
+ size,
|
|
|
+ packet_id
|
|
|
+ );
|
|
|
+
|
|
|
+ Ok(payload_buf)
|
|
|
+ }
|
|
|
+
|
|
|
/// Enqueue a packet to be send to a given remote endpoint and pass the buffer
|
|
|
/// to the provided closure. The closure then returns the size of the data written
|
|
|
/// into the buffer.
|
|
@@ -319,7 +399,11 @@ impl<'a> Socket<'a> {
|
|
|
|
|
|
let size = self
|
|
|
.tx_buffer
|
|
|
- .enqueue_with_infallible(max_size, remote_endpoint, f)
|
|
|
+ .enqueue_with_infallible(
|
|
|
+ max_size,
|
|
|
+ UdpMetadata::new(remote_endpoint, PacketId::empty()),
|
|
|
+ f,
|
|
|
+ )
|
|
|
.map_err(|_| SendError::BufferFull)?;
|
|
|
|
|
|
net_trace!(
|
|
@@ -348,14 +432,14 @@ impl<'a> Socket<'a> {
|
|
|
/// as a pointer to the payload.
|
|
|
///
|
|
|
/// This function returns `Err(Error::Exhausted)` if the receive buffer is empty.
|
|
|
- pub fn recv(&mut self) -> Result<(&[u8], IpEndpoint), RecvError> {
|
|
|
+ pub fn recv(&mut self) -> Result<(&[u8], UdpMetadata), RecvError> {
|
|
|
let (remote_endpoint, payload_buf) =
|
|
|
self.rx_buffer.dequeue().map_err(|_| RecvError::Exhausted)?;
|
|
|
|
|
|
net_trace!(
|
|
|
"udp:{}:{}: receive {} buffered octets",
|
|
|
self.endpoint,
|
|
|
- remote_endpoint,
|
|
|
+ remote_endpoint.endpoint,
|
|
|
payload_buf.len()
|
|
|
);
|
|
|
Ok((payload_buf, remote_endpoint))
|
|
@@ -365,7 +449,7 @@ impl<'a> Socket<'a> {
|
|
|
/// and return the amount of octets copied as well as the endpoint.
|
|
|
///
|
|
|
/// See also [recv](#method.recv).
|
|
|
- pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<(usize, IpEndpoint), RecvError> {
|
|
|
+ pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<(usize, UdpMetadata), RecvError> {
|
|
|
let (buffer, endpoint) = self.recv().map_err(|_| RecvError::Exhausted)?;
|
|
|
let length = min(data.len(), buffer.len());
|
|
|
data[..length].copy_from_slice(&buffer[..length]);
|
|
@@ -377,14 +461,14 @@ impl<'a> Socket<'a> {
|
|
|
/// This function otherwise behaves identically to [recv](#method.recv).
|
|
|
///
|
|
|
/// It returns `Err(Error::Exhausted)` if the receive buffer is empty.
|
|
|
- pub fn peek(&mut self) -> Result<(&[u8], &IpEndpoint), RecvError> {
|
|
|
+ pub fn peek(&mut self) -> Result<(&[u8], &UdpMetadata), RecvError> {
|
|
|
let endpoint = self.endpoint;
|
|
|
self.rx_buffer.peek().map_err(|_| RecvError::Exhausted).map(
|
|
|
|(remote_endpoint, payload_buf)| {
|
|
|
net_trace!(
|
|
|
"udp:{}:{}: peek {} buffered octets",
|
|
|
endpoint,
|
|
|
- remote_endpoint,
|
|
|
+ remote_endpoint.endpoint,
|
|
|
payload_buf.len()
|
|
|
);
|
|
|
(payload_buf, remote_endpoint)
|
|
@@ -398,7 +482,7 @@ impl<'a> Socket<'a> {
|
|
|
/// This function otherwise behaves identically to [recv_slice](#method.recv_slice).
|
|
|
///
|
|
|
/// See also [peek](#method.peek).
|
|
|
- pub fn peek_slice(&mut self, data: &mut [u8]) -> Result<(usize, &IpEndpoint), RecvError> {
|
|
|
+ pub fn peek_slice(&mut self, data: &mut [u8]) -> Result<(usize, &UdpMetadata), RecvError> {
|
|
|
let (buffer, endpoint) = self.peek()?;
|
|
|
let length = min(data.len(), buffer.len());
|
|
|
data[..length].copy_from_slice(&buffer[..length]);
|
|
@@ -423,6 +507,7 @@ impl<'a> Socket<'a> {
|
|
|
pub(crate) fn process(
|
|
|
&mut self,
|
|
|
cx: &mut Context,
|
|
|
+ packet_id: PacketId,
|
|
|
ip_repr: &IpRepr,
|
|
|
repr: &UdpRepr,
|
|
|
payload: &[u8],
|
|
@@ -443,7 +528,12 @@ impl<'a> Socket<'a> {
|
|
|
size
|
|
|
);
|
|
|
|
|
|
- match self.rx_buffer.enqueue(size, remote_endpoint) {
|
|
|
+ let metadata = UdpMetadata {
|
|
|
+ endpoint: remote_endpoint,
|
|
|
+ packet_id,
|
|
|
+ };
|
|
|
+
|
|
|
+ match self.rx_buffer.enqueue(size, metadata) {
|
|
|
Ok(buf) => buf.copy_from_slice(payload),
|
|
|
Err(_) => net_trace!(
|
|
|
"udp:{}:{}: buffer full, dropped incoming packet",
|
|
@@ -458,21 +548,21 @@ impl<'a> Socket<'a> {
|
|
|
|
|
|
pub(crate) fn dispatch<F, E>(&mut self, cx: &mut Context, emit: F) -> Result<(), E>
|
|
|
where
|
|
|
- F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8])) -> Result<(), E>,
|
|
|
+ F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8], PacketId)) -> Result<(), E>,
|
|
|
{
|
|
|
let endpoint = self.endpoint;
|
|
|
let hop_limit = self.hop_limit.unwrap_or(64);
|
|
|
|
|
|
- let res = self.tx_buffer.dequeue_with(|remote_endpoint, payload_buf| {
|
|
|
+ let res = self.tx_buffer.dequeue_with(|packet_meta, payload_buf| {
|
|
|
let src_addr = match endpoint.addr {
|
|
|
Some(addr) => addr,
|
|
|
- None => match cx.get_source_address(remote_endpoint.addr) {
|
|
|
+ None => match cx.get_source_address(packet_meta.endpoint.addr) {
|
|
|
Some(addr) => addr,
|
|
|
None => {
|
|
|
net_trace!(
|
|
|
"udp:{}:{}: cannot find suitable source address, dropping.",
|
|
|
endpoint,
|
|
|
- remote_endpoint
|
|
|
+ packet_meta.endpoint
|
|
|
);
|
|
|
return Ok(());
|
|
|
}
|
|
@@ -482,22 +572,23 @@ impl<'a> Socket<'a> {
|
|
|
net_trace!(
|
|
|
"udp:{}:{}: sending {} octets",
|
|
|
endpoint,
|
|
|
- remote_endpoint,
|
|
|
+ packet_meta.endpoint,
|
|
|
payload_buf.len()
|
|
|
);
|
|
|
|
|
|
let repr = UdpRepr {
|
|
|
src_port: endpoint.port,
|
|
|
- dst_port: remote_endpoint.port,
|
|
|
+ dst_port: packet_meta.endpoint.port,
|
|
|
};
|
|
|
let ip_repr = IpRepr::new(
|
|
|
src_addr,
|
|
|
- remote_endpoint.addr,
|
|
|
+ packet_meta.endpoint.addr,
|
|
|
IpProtocol::Udp,
|
|
|
repr.header_len() + payload_buf.len(),
|
|
|
hop_limit,
|
|
|
);
|
|
|
- emit(cx, (ip_repr, repr, payload_buf))
|
|
|
+
|
|
|
+ emit(cx, (ip_repr, repr, payload_buf, packet_meta.packet_id))
|
|
|
});
|
|
|
match res {
|
|
|
Err(Empty) => Ok(()),
|
|
@@ -525,7 +616,12 @@ mod test {
|
|
|
use crate::wire::{IpRepr, UdpRepr};
|
|
|
|
|
|
fn buffer(packets: usize) -> PacketBuffer<'static> {
|
|
|
- PacketBuffer::new(vec![PacketMetadata::EMPTY; packets], vec![0; 16 * packets])
|
|
|
+ PacketBuffer::new(
|
|
|
+ (0..packets)
|
|
|
+ .map(|_| PacketMetadata::EMPTY)
|
|
|
+ .collect::<Vec<_>>(),
|
|
|
+ vec![0; 16 * packets],
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
fn socket(
|
|
@@ -682,7 +778,7 @@ mod test {
|
|
|
assert!(!socket.can_send());
|
|
|
|
|
|
assert_eq!(
|
|
|
- socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload)| {
|
|
|
+ socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload, _packet_id)| {
|
|
|
assert_eq!(ip_repr, LOCAL_IP_REPR);
|
|
|
assert_eq!(udp_repr, LOCAL_UDP_REPR);
|
|
|
assert_eq!(payload, PAYLOAD);
|
|
@@ -693,7 +789,7 @@ mod test {
|
|
|
assert!(!socket.can_send());
|
|
|
|
|
|
assert_eq!(
|
|
|
- socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload)| {
|
|
|
+ socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload, _packet_id)| {
|
|
|
assert_eq!(ip_repr, LOCAL_IP_REPR);
|
|
|
assert_eq!(udp_repr, LOCAL_UDP_REPR);
|
|
|
assert_eq!(payload, PAYLOAD);
|
|
@@ -715,13 +811,31 @@ mod test {
|
|
|
assert_eq!(socket.recv(), Err(RecvError::Exhausted));
|
|
|
|
|
|
assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR));
|
|
|
- socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD);
|
|
|
+ socket.process(
|
|
|
+ &mut cx,
|
|
|
+ PacketId::empty(),
|
|
|
+ &REMOTE_IP_REPR,
|
|
|
+ &REMOTE_UDP_REPR,
|
|
|
+ PAYLOAD,
|
|
|
+ );
|
|
|
assert!(socket.can_recv());
|
|
|
|
|
|
assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR));
|
|
|
- socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD);
|
|
|
+ socket.process(
|
|
|
+ &mut cx,
|
|
|
+ PacketId::empty(),
|
|
|
+ &REMOTE_IP_REPR,
|
|
|
+ &REMOTE_UDP_REPR,
|
|
|
+ PAYLOAD,
|
|
|
+ );
|
|
|
|
|
|
- assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
|
|
|
+ assert_eq!(
|
|
|
+ socket.recv(),
|
|
|
+ Ok((
|
|
|
+ &b"abcdef"[..],
|
|
|
+ UdpMetadata::new(REMOTE_END, PacketId::empty())
|
|
|
+ ))
|
|
|
+ );
|
|
|
assert!(!socket.can_recv());
|
|
|
}
|
|
|
|
|
@@ -734,9 +848,27 @@ mod test {
|
|
|
|
|
|
assert_eq!(socket.peek(), Err(RecvError::Exhausted));
|
|
|
|
|
|
- socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD);
|
|
|
- assert_eq!(socket.peek(), Ok((&b"abcdef"[..], &REMOTE_END)));
|
|
|
- assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
|
|
|
+ socket.process(
|
|
|
+ &mut cx,
|
|
|
+ PacketId::empty(),
|
|
|
+ &REMOTE_IP_REPR,
|
|
|
+ &REMOTE_UDP_REPR,
|
|
|
+ PAYLOAD,
|
|
|
+ );
|
|
|
+ assert_eq!(
|
|
|
+ socket.peek(),
|
|
|
+ Ok((
|
|
|
+ &b"abcdef"[..],
|
|
|
+ &UdpMetadata::new(REMOTE_END, PacketId::empty())
|
|
|
+ ))
|
|
|
+ );
|
|
|
+ assert_eq!(
|
|
|
+ socket.recv(),
|
|
|
+ Ok((
|
|
|
+ &b"abcdef"[..],
|
|
|
+ UdpMetadata::new(REMOTE_END, PacketId::empty())
|
|
|
+ ))
|
|
|
+ );
|
|
|
assert_eq!(socket.peek(), Err(RecvError::Exhausted));
|
|
|
}
|
|
|
|
|
@@ -748,10 +880,19 @@ mod test {
|
|
|
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
|
|
|
|
|
|
assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR));
|
|
|
- socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD);
|
|
|
+ socket.process(
|
|
|
+ &mut cx,
|
|
|
+ PacketId::empty(),
|
|
|
+ &REMOTE_IP_REPR,
|
|
|
+ &REMOTE_UDP_REPR,
|
|
|
+ PAYLOAD,
|
|
|
+ );
|
|
|
|
|
|
let mut slice = [0; 4];
|
|
|
- assert_eq!(socket.recv_slice(&mut slice[..]), Ok((4, REMOTE_END)));
|
|
|
+ assert_eq!(
|
|
|
+ socket.recv_slice(&mut slice[..]),
|
|
|
+ Ok((4, UdpMetadata::new(REMOTE_END, PacketId::empty())))
|
|
|
+ );
|
|
|
assert_eq!(&slice, b"abcd");
|
|
|
}
|
|
|
|
|
@@ -762,12 +903,24 @@ mod test {
|
|
|
|
|
|
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
|
|
|
|
|
|
- socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD);
|
|
|
+ socket.process(
|
|
|
+ &mut cx,
|
|
|
+ PacketId::empty(),
|
|
|
+ &REMOTE_IP_REPR,
|
|
|
+ &REMOTE_UDP_REPR,
|
|
|
+ PAYLOAD,
|
|
|
+ );
|
|
|
|
|
|
let mut slice = [0; 4];
|
|
|
- assert_eq!(socket.peek_slice(&mut slice[..]), Ok((4, &REMOTE_END)));
|
|
|
+ assert_eq!(
|
|
|
+ socket.peek_slice(&mut slice[..]),
|
|
|
+ Ok((4, &UdpMetadata::new(REMOTE_END, PacketId::empty())))
|
|
|
+ );
|
|
|
assert_eq!(&slice, b"abcd");
|
|
|
- assert_eq!(socket.recv_slice(&mut slice[..]), Ok((4, REMOTE_END)));
|
|
|
+ assert_eq!(
|
|
|
+ socket.recv_slice(&mut slice[..]),
|
|
|
+ Ok((4, UdpMetadata::new(REMOTE_END, PacketId::empty())))
|
|
|
+ );
|
|
|
assert_eq!(&slice, b"abcd");
|
|
|
assert_eq!(socket.peek_slice(&mut slice[..]), Err(RecvError::Exhausted));
|
|
|
}
|
|
@@ -782,7 +935,7 @@ mod test {
|
|
|
s.set_hop_limit(Some(0x2a));
|
|
|
assert_eq!(s.send_slice(b"abcdef", REMOTE_END), Ok(()));
|
|
|
assert_eq!(
|
|
|
- s.dispatch(&mut cx, |_, (ip_repr, _, _)| {
|
|
|
+ s.dispatch(&mut cx, |_, (ip_repr, _, _, _)| {
|
|
|
assert_eq!(
|
|
|
ip_repr,
|
|
|
IpReprIpvX(IpvXRepr {
|
|
@@ -841,7 +994,8 @@ mod test {
|
|
|
|
|
|
#[test]
|
|
|
fn test_process_empty_payload() {
|
|
|
- let recv_buffer = PacketBuffer::new(vec![PacketMetadata::EMPTY; 1], vec![]);
|
|
|
+ let meta = Box::leak(Box::new([PacketMetadata::EMPTY]));
|
|
|
+ let recv_buffer = PacketBuffer::new(&mut meta[..], vec![]);
|
|
|
let mut socket = socket(recv_buffer, buffer(0));
|
|
|
let mut cx = Context::mock();
|
|
|
|
|
@@ -851,13 +1005,17 @@ mod test {
|
|
|
src_port: REMOTE_PORT,
|
|
|
dst_port: LOCAL_PORT,
|
|
|
};
|
|
|
- socket.process(&mut cx, &REMOTE_IP_REPR, &repr, &[]);
|
|
|
- assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END)));
|
|
|
+ socket.process(&mut cx, PacketId::empty(), &REMOTE_IP_REPR, &repr, &[]);
|
|
|
+ assert_eq!(
|
|
|
+ socket.recv(),
|
|
|
+ Ok((&[][..], UdpMetadata::new(REMOTE_END, PacketId::empty())))
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn test_closing() {
|
|
|
- let recv_buffer = PacketBuffer::new(vec![PacketMetadata::EMPTY; 1], vec![]);
|
|
|
+ let meta = Box::leak(Box::new([PacketMetadata::EMPTY]));
|
|
|
+ let recv_buffer = PacketBuffer::new(&mut meta[..], vec![]);
|
|
|
let mut socket = socket(recv_buffer, buffer(0));
|
|
|
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
|
|
|
|