use std::io;
use std::net::SocketAddr;
use crate::backend::Backend;
use crate::driver::DriverContext;
use crate::io::fd::Fd;
use crate::io::socket::ListenerConfig;
pub(crate) trait BootstrapBackend {
fn bind_listener_slot<'d>(
driver: &mut DriverContext<'_, 'd>,
addr: SocketAddr,
backlog: i32,
config: &ListenerConfig,
) -> io::Result<(Fd<'d>, SocketAddr)>;
fn bind_datagram_slot<'d>(
driver: &mut DriverContext<'_, 'd>,
addr: SocketAddr,
) -> io::Result<(Fd<'d>, SocketAddr)>;
}
#[cfg(target_os = "linux")]
mod linux {
use std::io::Error;
use std::os::fd::{AsRawFd, RawFd};
use io_uring::opcode::FilesUpdate;
use crate::backend::uring::sqe::Sqe;
use crate::driver::token::{Epoch, ROUTE_FRAMEWORK, SlotIndex, Token};
use crate::io::fd::FdSlot;
use crate::io::ffi::Handle;
use crate::io::socket::addr::Addr;
use crate::io::socket::{Domain, Kind};
use super::{Backend, BootstrapBackend, DriverContext, Fd, ListenerConfig, SocketAddr, io};
const BOOTSTRAP_UD: Token = Token::new(ROUTE_FRAMEWORK, SlotIndex::new(0), Epoch::ZERO);
impl BootstrapBackend for Backend {
fn bind_listener_slot<'d>(
driver: &mut DriverContext<'_, 'd>,
addr: SocketAddr,
backlog: i32,
config: &ListenerConfig,
) -> io::Result<(Fd<'d>, SocketAddr)> {
let reference = driver.driver_ref();
let (idx, bound) = if addr.port() == 0 {
bootstrap_bound_via_syscall(driver, addr, Kind::Stream, config, Some(backlog))
} else {
let idx = bootstrap_bind_slot(
driver,
Domain::for_addr(&addr),
Kind::Stream,
addr,
Some(config),
)?;
bootstrap_perform(
driver,
Sqe::listen_at(FdSlot::new(idx), backlog, BOOTSTRAP_UD),
)?;
Ok((idx, addr))
}?;
Ok((
unsafe { Fd::from_raw_slot(FdSlot::new(idx), reference) },
bound,
))
}
fn bind_datagram_slot<'d>(
driver: &mut DriverContext<'_, 'd>,
addr: SocketAddr,
) -> io::Result<(Fd<'d>, SocketAddr)> {
let reference = driver.driver_ref();
let config = ListenerConfig::for_datagram(&addr);
let (idx, bound) = if addr.port() == 0 {
bootstrap_bound_via_syscall(driver, addr, Kind::Dgram, &config, None)
} else {
let idx = bootstrap_bind_slot(
driver,
Domain::for_addr(&addr),
Kind::Dgram,
addr,
Some(&config),
)?;
Ok((idx, addr))
}?;
Ok((
unsafe { Fd::from_raw_slot(FdSlot::new(idx), reference) },
bound,
))
}
}
fn bootstrap_await(
driver: &mut DriverContext<'_, '_>,
min: i32,
fallback: i32,
) -> io::Result<()> {
let rc = driver.backend().await_one()?;
if rc < min {
return Err(Error::from_raw_os_error(if rc < 0 {
-rc
} else {
fallback
}));
}
Ok(())
}
fn bootstrap_perform(driver: &mut DriverContext<'_, '_>, sqe: Sqe) -> io::Result<()> {
crate::driver::submission::Submission::push(driver, sqe)?;
bootstrap_await(driver, 0, 0)
}
fn bootstrap_bind_slot(
driver: &mut DriverContext<'_, '_>,
domain: Domain,
kind: Kind,
addr: SocketAddr,
config: Option<&ListenerConfig>,
) -> io::Result<u32> {
let idx = driver.backend().alloc_fixed_range(1)?;
let slot = FdSlot::new(idx);
bootstrap_perform(
driver,
Sqe::socket_at(domain.raw(), kind.raw(), 0, slot, BOOTSTRAP_UD)?,
)?;
if let Some(config) = config {
bootstrap_apply_config(driver, idx, config)?;
}
let bound = Addr::from_std(addr);
bootstrap_perform(
driver,
Sqe::bind_at(slot, bound.ptr(), bound.socklen(), BOOTSTRAP_UD),
)?;
Ok(idx)
}
fn bootstrap_apply_config(
driver: &mut DriverContext<'_, '_>,
slot: u32,
config: &ListenerConfig,
) -> io::Result<()> {
if config.reuse_addr {
bootstrap_setsockopt(
driver,
slot,
libc::SOL_SOCKET as u32,
libc::SO_REUSEADDR as u32,
1,
)?;
}
if config.reuse_port {
bootstrap_setsockopt(
driver,
slot,
libc::SOL_SOCKET as u32,
libc::SO_REUSEPORT as u32,
1,
)?;
}
if let Some(qlen) = config.fast_open_backlog {
bootstrap_setsockopt(
driver,
slot,
libc::IPPROTO_TCP as u32,
libc::TCP_FASTOPEN as u32,
qlen as i32,
)?;
}
if let Some(secs) = config.defer_accept_secs {
bootstrap_setsockopt(
driver,
slot,
libc::IPPROTO_TCP as u32,
libc::TCP_DEFER_ACCEPT as u32,
secs as i32,
)?;
}
Ok(())
}
fn bootstrap_setsockopt(
driver: &mut DriverContext<'_, '_>,
slot: u32,
level: u32,
optname: u32,
value: i32,
) -> io::Result<()> {
crate::driver::control::ContextControl::set(driver, slot, level, optname, value)?;
bootstrap_await(driver, 0, 0)
}
fn bootstrap_register_raw(
driver: &mut DriverContext<'_, '_>,
raw: RawFd,
slot: u32,
) -> io::Result<()> {
let mut fds = [raw];
let entry = FilesUpdate::new(fds.as_mut_ptr().cast_const(), 1)
.offset(slot as i32)
.build()
.user_data(BOOTSTRAP_UD.raw());
crate::driver::submission::Submission::push(driver, Sqe::from_entry(entry))?;
bootstrap_await(driver, 1, libc::EMFILE)?;
driver.backend().files.set_live(FdSlot::new(slot));
Ok(())
}
fn bootstrap_bound_via_syscall(
driver: &mut DriverContext<'_, '_>,
addr: SocketAddr,
kind: Kind,
config: &ListenerConfig,
backlog: Option<i32>,
) -> io::Result<(u32, SocketAddr)> {
let handle = Handle::open(Domain::for_addr(&addr), kind)?;
handle.apply_reuse(config)?;
handle.bind(&Addr::from_std(addr))?;
match backlog {
Some(backlog) => handle.listen(backlog)?,
None => handle.set_nonblocking()?,
}
let actual = handle.local_addr()?;
let slot = driver.backend().alloc_fixed_range(1)?;
bootstrap_register_raw(driver, handle.as_raw_fd(), slot)?;
drop(handle);
Ok((slot, actual))
}
}
#[cfg(not(target_os = "linux"))]
mod kqueue {
use std::os::fd::IntoRawFd;
use crate::io::fd::FdSlot;
use crate::io::ffi::Handle;
use crate::io::socket::addr::Addr;
use crate::io::socket::{Domain, Kind};
use super::{Backend, BootstrapBackend, DriverContext, Fd, ListenerConfig, SocketAddr, io};
impl BootstrapBackend for Backend {
fn bind_listener_slot<'d>(
driver: &mut DriverContext<'_, 'd>,
addr: SocketAddr,
backlog: i32,
config: &ListenerConfig,
) -> io::Result<(Fd<'d>, SocketAddr)> {
let reference = driver.driver_ref();
let handle = Handle::open(Domain::for_addr(&addr), Kind::Stream)?;
handle.apply_reuse(config)?;
handle.bind(&Addr::from_std(addr))?;
handle.listen(backlog)?;
let actual = handle.local_addr()?;
let idx = register(driver.backend(), handle)?;
Ok((
unsafe { Fd::from_raw_slot(FdSlot::new(idx), reference) },
actual,
))
}
fn bind_datagram_slot<'d>(
driver: &mut DriverContext<'_, 'd>,
addr: SocketAddr,
) -> io::Result<(Fd<'d>, SocketAddr)> {
let reference = driver.driver_ref();
let handle = Handle::open(Domain::for_addr(&addr), Kind::Dgram)?;
handle.set_nonblocking()?;
handle.apply_reuse(&ListenerConfig::for_datagram(&addr))?;
handle.bind(&Addr::from_std(addr))?;
let actual = handle.local_addr()?;
let idx = register(driver.backend(), handle)?;
Ok((
unsafe { Fd::from_raw_slot(FdSlot::new(idx), reference) },
actual,
))
}
}
fn register(backend: &mut Backend, handle: Handle) -> io::Result<u32> {
let slot = backend.alloc_fixed_range(1)?;
backend.register_raw_fd(slot, handle.into_raw_fd())?;
Ok(slot)
}
}