Skip to main content

dope_uring/sockets/reuse/
mod.rs

1use std::{
2    mem,
3    os::fd::{AsRawFd as _, BorrowedFd},
4    ptr,
5};
6
7use dope_sys::io::socket::route::{
8    Attach, Plan, Source,
9    count::Index,
10    install::{Error, Operation},
11};
12
13use crate::Uring;
14
15mod program;
16
17pub struct Installer<'a> {
18    plan: Plan,
19    filter: &'a mut [libc::sock_filter; program::MAX],
20    length: usize,
21}
22
23impl Attach for Installer<'_> {
24    fn try_attach(&self, socket: BorrowedFd<'_>) -> Result<(), Error> {
25        let mut code = [program::EMPTY; program::MAX];
26        let length = program::build(&mut code, self.plan, None);
27        attach(socket, &code[..length], Operation::ProgramAttach)
28    }
29
30    fn try_filter(&mut self, key: Index, socket: BorrowedFd<'_>) -> Result<(), Error> {
31        self.filter[self.length - 3].k = key.into_raw() as u32;
32        attach(socket, &self.filter[..self.length], Operation::FilterAttach)
33    }
34}
35
36impl Source for Uring {
37    type Storage = [libc::sock_filter; program::MAX];
38    type Install<'a> = Installer<'a>;
39    type Ready = ();
40    type Lease<'a> = BorrowedFd<'a>;
41
42    const STORAGE: Self::Storage = [program::EMPTY; program::MAX];
43
44    #[inline]
45    fn try_prepare(
46        filter: &mut Self::Storage,
47        plan: Plan,
48    ) -> Result<(Self::Install<'_>, Self::Ready), Plan> {
49        let length = program::build(filter, plan, Some(Index::ZERO));
50        let installer = Installer {
51            plan,
52            filter,
53            length,
54        };
55        Ok((installer, ()))
56    }
57
58    fn lease<'a>((): &(), socket: BorrowedFd<'a>) -> Self::Lease<'a> {
59        socket
60    }
61}
62
63fn attach(
64    socket: BorrowedFd<'_>,
65    code: &[libc::sock_filter],
66    operation: Operation,
67) -> Result<(), Error> {
68    let program = libc::sock_fprog {
69        len: code.len() as u16,
70        filter: code.as_ptr().cast_mut(),
71    };
72    let option = match operation {
73        Operation::ProgramAttach => libc::SO_ATTACH_REUSEPORT_CBPF,
74        Operation::FilterAttach => libc::SO_ATTACH_FILTER,
75    };
76    // SAFETY: the program and its complete instruction array outlive setsockopt.
77    let result = unsafe {
78        libc::setsockopt(
79            socket.as_raw_fd(),
80            libc::SOL_SOCKET,
81            option,
82            ptr::from_ref(&program).cast(),
83            mem::size_of_val(&program) as libc::socklen_t,
84        )
85    };
86    // SAFETY: setsockopt returned zero or -1; errno has not been changed.
87    unsafe { dope_sys::kernel::raw::Status::new(result).try_zero::<Uring, dope_sys::Errno>() }
88        .map_err(|errno| Error { operation, errno })
89}