use std::{
mem,
os::fd::{AsRawFd as _, BorrowedFd},
ptr,
};
use dope_sys::io::socket::route::{
Attach, Plan, Source,
count::Index,
install::{Error, Operation},
};
use crate::Uring;
mod program;
pub struct Installer<'a> {
plan: Plan,
filter: &'a mut [libc::sock_filter],
}
impl Attach for Installer<'_> {
fn try_attach(&self, socket: BorrowedFd<'_>) -> Result<(), Error> {
let mut code = [program::EMPTY; program::MAX];
attach(
socket,
program::build(&mut code, self.plan, None),
Operation::ProgramAttach,
)
}
fn try_filter(&mut self, key: Index, socket: BorrowedFd<'_>) -> Result<(), Error> {
let index = self.filter.len() - 3;
self.filter[index].k = key.into_raw() as u32;
attach(socket, self.filter, Operation::FilterAttach)
}
}
impl Source for Uring {
type Storage = [libc::sock_filter; program::MAX];
type Install<'a> = Installer<'a>;
type Ready = ();
type Lease<'a> = BorrowedFd<'a>;
const STORAGE: Self::Storage = [program::EMPTY; program::MAX];
#[inline]
fn try_prepare(
filter: &mut Self::Storage,
plan: Plan,
) -> Result<(Self::Install<'_>, Self::Ready), Plan> {
let installer = Installer {
plan,
filter: program::build(filter, plan, Some(Index::ZERO)),
};
Ok((installer, ()))
}
fn lease<'a>((): &(), socket: BorrowedFd<'a>) -> Self::Lease<'a> {
socket
}
}
fn attach(
socket: BorrowedFd<'_>,
code: &[libc::sock_filter],
operation: Operation,
) -> Result<(), Error> {
let program = libc::sock_fprog {
len: code.len() as u16,
filter: code.as_ptr().cast_mut(),
};
let option = match operation {
Operation::ProgramAttach => libc::SO_ATTACH_REUSEPORT_CBPF,
Operation::FilterAttach => libc::SO_ATTACH_FILTER,
};
let result = unsafe {
libc::setsockopt(
socket.as_raw_fd(),
libc::SOL_SOCKET,
option,
ptr::from_ref(&program).cast(),
mem::size_of_val(&program) as libc::socklen_t,
)
};
unsafe { dope_sys::kernel::raw::Status::new(result).try_zero::<Uring, dope_sys::Errno>() }
.map_err(|errno| Error { operation, errno })
}