use core::net::SocketAddr;
use smoltcp::{
iface::{SocketHandle, SocketSet},
socket::udp,
wire::IpEndpoint,
};
use crate::udpio::{RecvMeta, SendError, UdpIo};
fn recv_from(socket: &mut udp::Socket<'_>, buf: &mut [u8]) -> Option<RecvMeta> {
match socket.recv_slice(buf) {
Ok((len, meta)) => {
#[cfg(feature = "defmt")]
defmt::trace!("smoltcp recv_from: {} bytes", len);
Some(RecvMeta {
src: meta.endpoint.into(),
local: meta.local_address.map(Into::into),
hop_limit: None,
len,
})
}
Err(udp::RecvError::Exhausted) => None,
Err(udp::RecvError::Truncated) => Some(RecvMeta {
src: SocketAddr::new(core::net::IpAddr::V4(core::net::Ipv4Addr::UNSPECIFIED), 0),
local: None,
hop_limit: None,
len: 0,
}),
}
}
fn send_from(socket: &mut udp::Socket<'_>, buf: &[u8], dst: SocketAddr) -> Result<(), SendError> {
if buf.len() > socket.payload_send_capacity() {
return Err(SendError::TooLarge);
}
#[cfg(feature = "defmt")]
defmt::trace!("smoltcp send_from: {} bytes", buf.len());
socket.set_hop_limit(Some(255));
match socket.send_slice(buf, IpEndpoint::from(dst)) {
Ok(()) => Ok(()),
Err(udp::SendError::BufferFull) => Err(SendError::Busy),
Err(udp::SendError::Unaddressable) => Err(SendError::Unsupported),
}
}
pub struct DualStack<'set, 'sockets> {
sockets: &'set mut SocketSet<'sockets>,
v4: Option<SocketHandle>,
v6: Option<SocketHandle>,
take_v6_first: bool,
}
impl<'set, 'sockets> DualStack<'set, 'sockets> {
pub fn new(
sockets: &'set mut SocketSet<'sockets>,
v4: Option<SocketHandle>,
v6: Option<SocketHandle>,
) -> Self {
Self {
sockets,
v4,
v6,
take_v6_first: false,
}
}
}
impl UdpIo for DualStack<'_, '_> {
fn try_recv(&mut self, buf: &mut [u8]) -> Option<RecvMeta> {
let take_v6_first = self.take_v6_first;
self.take_v6_first = !take_v6_first;
let (first, second) = if take_v6_first {
(self.v6, self.v4)
} else {
(self.v4, self.v6)
};
if let Some(handle) = first
&& let Some(meta) = recv_from(self.sockets.get_mut::<udp::Socket<'_>>(handle), buf)
{
return Some(meta);
}
if let Some(handle) = second {
return recv_from(self.sockets.get_mut::<udp::Socket<'_>>(handle), buf);
}
None
}
fn try_send(&mut self, buf: &[u8], dst: SocketAddr) -> Result<(), SendError> {
let handle = if dst.is_ipv4() { self.v4 } else { self.v6 };
match handle {
Some(handle) => send_from(self.sockets.get_mut::<udp::Socket<'_>>(handle), buf, dst),
None => Err(SendError::Unsupported),
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests;