wireshift-uring 0.1.1

Native Linux io_uring backend for wireshift
Documentation
#![allow(clippy::unnecessary_wraps)]
use std::os::fd::AsRawFd;

use io_uring::{opcode, types};
use wireshift_core::op::OpDescriptor;
use wireshift_core::{Error, Result};

use super::PreparedEntry;
use crate::file_registry::FileRegistryTable;
use crate::helpers::sockaddr_to_storage;
use crate::types::InflightData;

pub(crate) fn build_connect(
    descriptor: &OpDescriptor,
    id: u64,
    _fixed_files: &FileRegistryTable,
) -> Result<Vec<PreparedEntry>> {
    let OpDescriptor::Connect { addr, .. } = descriptor else {
        unreachable!()
    };
    let domain = match addr {
        std::net::SocketAddr::V4(_) => libc::AF_INET,
        std::net::SocketAddr::V6(_) => libc::AF_INET6,
    };
    // SAFETY: Creating a non-blocking TCP socket. domain is AF_INET or AF_INET6.
    let socket_fd = unsafe {
        libc::socket(
            domain,
            libc::SOCK_STREAM | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC,
            0,
        )
    };
    if socket_fd < 0 {
        return Err(Error::io(
            "failed to create socket for io_uring connect",
            std::io::Error::last_os_error(),
            "ensure the system has available file descriptors",
        ));
    }
    let (storage, addr_len) = sockaddr_to_storage(addr);
    let storage = Box::new(storage);
    let entry = opcode::Connect::new(
        types::Fd(socket_fd),
        std::ptr::from_ref::<libc::sockaddr_storage>(storage.as_ref()).cast::<libc::sockaddr>(),
        addr_len,
    )
    .build()
    .user_data(id);
    Ok(vec![PreparedEntry {
        entry,
        pinned: InflightData::Sockaddr {
            socket_fd,
            storage,
            addr_len,
        },
        link_to_next: None,
    }])
}

pub(crate) fn build_accept(
    descriptor: &OpDescriptor,
    id: u64,
    _fixed_files: &FileRegistryTable,
) -> Result<Vec<PreparedEntry>> {
    let OpDescriptor::Accept { listener } = descriptor else {
        unreachable!()
    };
    let entry = opcode::Accept::new(
        types::Fd(listener.as_raw_fd()),
        std::ptr::null_mut(),
        std::ptr::null_mut(),
    )
    .flags(libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC)
    .build()
    .user_data(id);
    Ok(vec![PreparedEntry {
        entry,
        pinned: InflightData::None,
        link_to_next: None,
    }])
}

pub(crate) fn build_send(
    descriptor: &OpDescriptor,
    id: u64,
    fixed_files: &FileRegistryTable,
) -> Result<Vec<PreparedEntry>> {
    let OpDescriptor::Send { stream, buffer } = descriptor else {
        unreachable!()
    };
    let len = u32::try_from(buffer.filled_len()).map_err(|_| {
        Error::validation(
            "send buffer length exceeds io_uring's u32 limit",
            "use smaller buffers for native io_uring sends",
        )
    })?;
    let entry = if let Some(slot) = fixed_files.lookup(stream.as_raw_fd()) {
        opcode::Send::new(types::Fixed(slot), buffer.backend_ref().as_ptr(), len)
    } else {
        opcode::Send::new(
            types::Fd(stream.as_raw_fd()),
            buffer.backend_ref().as_ptr(),
            len,
        )
    }
    .build()
    .user_data(id);
    Ok(vec![PreparedEntry {
        entry,
        pinned: InflightData::None,
        link_to_next: None,
    }])
}

pub(crate) fn build_recv(
    descriptor: &OpDescriptor,
    id: u64,
    fixed_files: &FileRegistryTable,
) -> Result<Vec<PreparedEntry>> {
    let OpDescriptor::Recv { stream, buffer } = descriptor else {
        unreachable!()
    };
    let len = u32::try_from(buffer.capacity()).map_err(|_| {
        Error::validation(
            "recv buffer length exceeds io_uring's u32 limit",
            "use smaller buffers for native io_uring receives",
        )
    })?;
    let entry = if let Some(slot) = fixed_files.lookup(stream.as_raw_fd()) {
        opcode::Recv::new(
            types::Fixed(slot),
            buffer.backend_ref().as_ptr().cast_mut(),
            len,
        )
    } else {
        opcode::Recv::new(
            types::Fd(stream.as_raw_fd()),
            buffer.backend_ref().as_ptr().cast_mut(),
            len,
        )
    }
    .build()
    .user_data(id);
    Ok(vec![PreparedEntry {
        entry,
        pinned: InflightData::None,
        link_to_next: None,
    }])
}