#[cfg(target_os = "linux")]
mod linux {
use crate::arq::FrameSetPacket;
use std::{io, net::SocketAddr, os::fd::AsRawFd};
use tokio::{io::Interest, net::UdpSocket};
const BATCH: usize = 16;
const BUFFER_SIZE: usize = 2048;
struct BatchReceiver {
buffers: Vec<[u8; BUFFER_SIZE]>,
lengths: [usize; BATCH],
addresses: [SocketAddr; BATCH],
next: usize,
count: usize,
}
impl BatchReceiver {
pub(crate) fn new() -> Self {
Self {
buffers: vec![[0; BUFFER_SIZE]; BATCH],
lengths: [0; BATCH],
addresses: [SocketAddr::from(([0, 0, 0, 0], 0)); BATCH],
next: 0,
count: 0,
}
}
pub(crate) async fn recv_from(
&mut self,
socket: &UdpSocket,
output: &mut [u8],
) -> io::Result<(usize, SocketAddr)> {
if self.next == self.count {
self.count = socket
.async_io(Interest::READABLE, || {
loop {
match self.receive_ready(socket) {
Err(error) if error.kind() == io::ErrorKind::Interrupted => {
continue;
}
result => break result,
}
}
})
.await?;
self.next = 0;
}
let index = self.next;
self.next += 1;
let length = self.lengths[index].min(output.len());
output[..length].copy_from_slice(&self.buffers[index][..length]);
Ok((length, self.addresses[index]))
}
fn receive_ready(&mut self, socket: &UdpSocket) -> io::Result<usize> {
let mut addresses: [libc::sockaddr_storage; BATCH] = unsafe { std::mem::zeroed() };
let mut vectors: [libc::iovec; BATCH] = unsafe { std::mem::zeroed() };
let mut messages: [libc::mmsghdr; BATCH] = unsafe { std::mem::zeroed() };
for index in 0..BATCH {
vectors[index].iov_base = self.buffers[index].as_mut_ptr().cast();
vectors[index].iov_len = BUFFER_SIZE;
messages[index].msg_hdr.msg_name =
(&mut addresses[index] as *mut libc::sockaddr_storage).cast();
messages[index].msg_hdr.msg_namelen =
std::mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
messages[index].msg_hdr.msg_iov = &mut vectors[index];
messages[index].msg_hdr.msg_iovlen = 1;
}
let count = unsafe {
libc::recvmmsg(
socket.as_raw_fd(),
messages.as_mut_ptr(),
BATCH as u32,
libc::MSG_DONTWAIT,
std::ptr::null_mut(),
)
};
if count < 0 {
return Err(io::Error::last_os_error());
}
for index in 0..count as usize {
let length = messages[index].msg_hdr.msg_namelen;
let minimum = match addresses[index].ss_family as i32 {
libc::AF_INET => std::mem::size_of::<libc::sockaddr_in>(),
libc::AF_INET6 => std::mem::size_of::<libc::sockaddr_in6>(),
_ => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"unsupported UDP peer address",
));
}
};
if (length as usize) < minimum
|| length as usize > std::mem::size_of::<libc::sockaddr_storage>()
{
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid UDP peer address",
));
}
let mut storage = socket2::SockAddrStorage::zeroed();
unsafe {
*storage.view_as::<libc::sockaddr_storage>() = addresses[index];
}
let address = unsafe { socket2::SockAddr::new(storage, length) };
self.addresses[index] = address.as_socket().ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "unsupported UDP peer address")
})?;
self.lengths[index] = (messages[index].msg_len as usize).min(BUFFER_SIZE);
}
Ok(count as usize)
}
}
pub(crate) struct DatagramReceiver {
batch: Option<Box<BatchReceiver>>,
}
impl DatagramReceiver {
pub(crate) fn new(batching: bool) -> Self {
Self {
batch: batching.then(|| Box::new(BatchReceiver::new())),
}
}
pub(crate) async fn recv_from(
&mut self,
socket: &UdpSocket,
output: &mut [u8],
) -> io::Result<(usize, SocketAddr)> {
match &mut self.batch {
Some(batch) => batch.recv_from(socket, output).await,
None => socket.recv_from(output).await,
}
}
}
pub(crate) async fn send_frames(
socket: &UdpSocket,
frames: &[FrameSetPacket],
peer: &SocketAddr,
) -> io::Result<()> {
let mut sent = 0;
while sent < frames.len() {
let batch = &frames[sent..frames.len().min(sent + BATCH)];
let count = socket
.async_io(Interest::WRITABLE, || {
loop {
match send_ready(socket, batch, peer) {
Err(error) if error.kind() == io::ErrorKind::Interrupted => continue,
result => break result,
}
}
})
.await?;
if count == 0 {
return Err(io::Error::new(
io::ErrorKind::WriteZero,
"empty UDP send batch",
));
}
sent += count;
}
Ok(())
}
fn send_ready(
socket: &UdpSocket,
frames: &[FrameSetPacket],
peer: &SocketAddr,
) -> io::Result<usize> {
let mut headers = [[0u8; 32]; BATCH];
let mut vectors: [[libc::iovec; 2]; BATCH] = unsafe { std::mem::zeroed() };
let mut messages: [libc::mmsghdr; BATCH] = unsafe { std::mem::zeroed() };
let address = socket2::SockAddr::from(*peer);
for (index, frame) in frames.iter().enumerate() {
let length = frame
.encode_header(&mut headers[index])
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
vectors[index][0] = libc::iovec {
iov_base: headers[index].as_mut_ptr().cast(),
iov_len: length,
};
vectors[index][1] = libc::iovec {
iov_base: frame.data.as_ptr().cast_mut().cast(),
iov_len: frame.data.len(),
};
messages[index].msg_hdr.msg_name = address.as_ptr().cast_mut().cast();
messages[index].msg_hdr.msg_namelen = address.len();
messages[index].msg_hdr.msg_iov = vectors[index].as_mut_ptr();
messages[index].msg_hdr.msg_iovlen = 2;
}
let count = unsafe {
libc::sendmmsg(
socket.as_raw_fd(),
messages.as_mut_ptr(),
frames.len() as u32,
libc::MSG_DONTWAIT,
)
};
if count < 0 {
Err(io::Error::last_os_error())
} else {
Ok(count as usize)
}
}
}
#[cfg(target_os = "linux")]
pub(crate) use linux::{DatagramReceiver, send_frames};
#[cfg(not(target_os = "linux"))]
pub(crate) struct DatagramReceiver;
#[cfg(not(target_os = "linux"))]
impl DatagramReceiver {
pub(crate) fn new(_batching: bool) -> Self {
Self
}
pub(crate) async fn recv_from(
&mut self,
socket: &tokio::net::UdpSocket,
output: &mut [u8],
) -> std::io::Result<(usize, std::net::SocketAddr)> {
socket.recv_from(output).await
}
}
#[cfg(all(test, target_os = "linux"))]
mod tests {
use super::*;
use crate::arq::{FrameSetPacket, Reliability};
use tokio::net::UdpSocket;
#[tokio::test]
async fn batches_preserve_every_datagram_and_peer_on_both_address_families() {
for host in ["127.0.0.1:0", "[::1]:0"] {
let sender = UdpSocket::bind(host).await.unwrap();
let receiver = UdpSocket::bind(host).await.unwrap();
let peer = receiver.local_addr().unwrap();
let frames: Vec<_> = (0..40)
.map(|index| {
let mut frame =
FrameSetPacket::new(Reliability::ReliableOrdered, vec![0xfe, index]);
frame.sequence_number = index as u32;
frame
})
.collect();
send_frames(&sender, &frames, &peer).await.unwrap();
let mut input = DatagramReceiver::new(true);
let mut bytes = [0; 2048];
for frame in frames {
let (length, address) = input.recv_from(&receiver, &mut bytes).await.unwrap();
assert_eq!(address, sender.local_addr().unwrap());
assert_eq!(&bytes[..length], frame.serialize().unwrap());
}
assert!(receiver.try_recv_from(&mut bytes).is_err());
}
}
}