Skip to main content

dope_core/driver/
bootstrap.rs

1use std::io;
2use std::net::SocketAddr;
3
4use super::DriverContext;
5use crate::io::fd::{Fd, FdSlot};
6use crate::io::ffi::Handle;
7use crate::io::socket::addr::Addr;
8use crate::io::socket::{Domain, Kind, ListenerConfig};
9
10pub trait Bootstrap<'d> {
11    fn bind_listener_slot(
12        &mut self,
13        addr: SocketAddr,
14        backlog: i32,
15        config: &ListenerConfig,
16    ) -> io::Result<(Fd<'d>, SocketAddr)>;
17    fn bind_datagram_slot(&mut self, addr: SocketAddr) -> io::Result<(Fd<'d>, SocketAddr)>;
18}
19
20cfg_select! {
21    target_os = "linux" => {
22        use std::io::Error;
23        use std::os::fd::{AsRawFd, RawFd};
24
25        use io_uring::opcode::FilesUpdate;
26
27        use super::control::ContextControl;
28        use super::submission::Submission;
29        use super::token::{Epoch, ROUTE_FRAMEWORK, SlotIndex, Token};
30        use crate::backend::uring::sqe::{self, Sqe};
31
32        const BOOTSTRAP_UD: Token = Token::new(ROUTE_FRAMEWORK, SlotIndex::new(0), Epoch::ZERO);
33
34        impl<'a, 'd> Bootstrap<'d> for DriverContext<'a, 'd> {
35            fn bind_listener_slot(
36                &mut self,
37                addr: SocketAddr,
38                backlog: i32,
39                config: &ListenerConfig,
40            ) -> io::Result<(Fd<'d>, SocketAddr)> {
41                let reference = self.driver_ref();
42                let (idx, bound) = if addr.port() == 0 {
43                    bootstrap_bound_via_syscall(self, addr, Kind::Stream, config, Some(backlog))
44                } else {
45                    let idx = bootstrap_bind_slot(
46                        self,
47                        Domain::for_addr(&addr),
48                        Kind::Stream,
49                        addr,
50                        Some(config),
51                    )?;
52                    bootstrap_perform(
53                        self,
54                        Sqe::listen_at(FdSlot::new(idx), backlog, BOOTSTRAP_UD),
55                    )?;
56                    Ok((idx, addr))
57                }?;
58                Ok((
59                    unsafe { Fd::from_raw_slot(FdSlot::new(idx), reference) },
60                    bound,
61                ))
62            }
63
64            fn bind_datagram_slot(&mut self, addr: SocketAddr) -> io::Result<(Fd<'d>, SocketAddr)> {
65                let reference = self.driver_ref();
66                let config = ListenerConfig::for_datagram(&addr);
67                let (idx, bound) = if addr.port() == 0 {
68                    bootstrap_bound_via_syscall(self, addr, Kind::Dgram, &config, None)
69                } else {
70                    let idx = bootstrap_bind_slot(
71                        self,
72                        Domain::for_addr(&addr),
73                        Kind::Dgram,
74                        addr,
75                        Some(&config),
76                    )?;
77                    Ok((idx, addr))
78                }?;
79                Ok((
80                    unsafe { Fd::from_raw_slot(FdSlot::new(idx), reference) },
81                    bound,
82                ))
83            }
84        }
85
86        fn bootstrap_await(
87            driver: &mut DriverContext<'_, '_>,
88            min: i32,
89            fallback: i32,
90        ) -> io::Result<()> {
91            let rc = driver.backend().await_one()?;
92            if rc < min {
93                return Err(Error::from_raw_os_error(if rc < 0 {
94                    -rc
95                } else {
96                    fallback
97                }));
98            }
99            Ok(())
100        }
101
102        fn bootstrap_perform(driver: &mut DriverContext<'_, '_>, sqe: sqe::Sqe) -> io::Result<()> {
103            Submission::push(driver, sqe)?;
104            bootstrap_await(driver, 0, 0)
105        }
106
107        fn bootstrap_bind_slot(
108            driver: &mut DriverContext<'_, '_>,
109            domain: Domain,
110            kind: Kind,
111            addr: SocketAddr,
112            config: Option<&ListenerConfig>,
113        ) -> io::Result<u32> {
114            let idx = driver.backend().alloc_fixed_range(1)?;
115            let slot = FdSlot::new(idx);
116            bootstrap_perform(
117                driver,
118                Sqe::socket_at(domain.raw(), kind.raw(), 0, slot, BOOTSTRAP_UD)?,
119            )?;
120            if let Some(config) = config {
121                bootstrap_apply_config(driver, idx, config)?;
122            }
123            let bound = Addr::from_std(addr);
124            bootstrap_perform(
125                driver,
126                Sqe::bind_at(slot, bound.ptr(), bound.socklen(), BOOTSTRAP_UD),
127            )?;
128            Ok(idx)
129        }
130
131        fn bootstrap_apply_config(
132            driver: &mut DriverContext<'_, '_>,
133            slot: u32,
134            config: &ListenerConfig,
135        ) -> io::Result<()> {
136            if config.reuse_addr {
137                bootstrap_setsockopt(
138                    driver,
139                    slot,
140                    libc::SOL_SOCKET as u32,
141                    libc::SO_REUSEADDR as u32,
142                    1,
143                )?;
144            }
145            if config.reuse_port {
146                bootstrap_setsockopt(
147                    driver,
148                    slot,
149                    libc::SOL_SOCKET as u32,
150                    libc::SO_REUSEPORT as u32,
151                    1,
152                )?;
153            }
154            if let Some(qlen) = config.fast_open_backlog {
155                bootstrap_setsockopt(
156                    driver,
157                    slot,
158                    libc::IPPROTO_TCP as u32,
159                    libc::TCP_FASTOPEN as u32,
160                    qlen as i32,
161                )?;
162            }
163            if let Some(secs) = config.defer_accept_secs {
164                bootstrap_setsockopt(
165                    driver,
166                    slot,
167                    libc::IPPROTO_TCP as u32,
168                    libc::TCP_DEFER_ACCEPT as u32,
169                    secs as i32,
170                )?;
171            }
172            Ok(())
173        }
174
175        fn bootstrap_setsockopt(
176            driver: &mut DriverContext<'_, '_>,
177            slot: u32,
178            level: u32,
179            optname: u32,
180            value: i32,
181        ) -> io::Result<()> {
182            ContextControl::set(driver, slot, level, optname, value)?;
183            bootstrap_await(driver, 0, 0)
184        }
185
186        fn bootstrap_register_raw(
187            driver: &mut DriverContext<'_, '_>,
188            raw: RawFd,
189            slot: u32,
190        ) -> io::Result<()> {
191            let mut fds = [raw];
192            let entry = FilesUpdate::new(fds.as_mut_ptr().cast_const(), 1)
193                .offset(slot as i32)
194                .build()
195                .user_data(BOOTSTRAP_UD.raw());
196            Submission::push(driver, Sqe::from_entry(entry))?;
197            bootstrap_await(driver, 1, libc::EMFILE)?;
198            driver.backend().files.set_live(FdSlot::new(slot));
199            Ok(())
200        }
201
202        fn bootstrap_bound_via_syscall(
203            driver: &mut DriverContext<'_, '_>,
204            addr: SocketAddr,
205            kind: Kind,
206            config: &ListenerConfig,
207            backlog: Option<i32>,
208        ) -> io::Result<(u32, SocketAddr)> {
209            let handle = Handle::open(Domain::for_addr(&addr), kind)?;
210            handle.apply_reuse(config)?;
211            handle.bind(&Addr::from_std(addr))?;
212            match backlog {
213                Some(backlog) => handle.listen(backlog)?,
214                None => handle.set_nonblocking()?,
215            }
216            let actual = handle.local_addr()?;
217            let slot = driver.backend().alloc_fixed_range(1)?;
218            bootstrap_register_raw(driver, handle.as_raw_fd(), slot)?;
219            drop(handle);
220            Ok((slot, actual))
221        }
222    }
223    _ => {
224        use std::os::fd::IntoRawFd;
225
226        use crate::backend::kqueue::driver::Kqueue;
227
228        impl<'a, 'd> Bootstrap<'d> for DriverContext<'a, 'd> {
229            fn bind_listener_slot(
230                &mut self,
231                addr: SocketAddr,
232                backlog: i32,
233                config: &ListenerConfig,
234            ) -> io::Result<(Fd<'d>, SocketAddr)> {
235                let reference = self.driver_ref();
236                let state = self.backend();
237                let handle = Handle::open(Domain::for_addr(&addr), Kind::Stream)?;
238                handle.apply_reuse(config)?;
239                handle.bind(&Addr::from_std(addr))?;
240                handle.listen(backlog)?;
241                let actual = handle.local_addr()?;
242                let idx = register(state, handle)?;
243                Ok((
244                    unsafe { Fd::from_raw_slot(FdSlot::new(idx), reference) },
245                    actual,
246                ))
247            }
248
249            fn bind_datagram_slot(
250                &mut self,
251                addr: SocketAddr,
252            ) -> io::Result<(Fd<'d>, SocketAddr)> {
253                let reference = self.driver_ref();
254                let state = self.backend();
255                let handle = Handle::open(Domain::for_addr(&addr), Kind::Dgram)?;
256                handle.set_nonblocking()?;
257                handle.apply_reuse(&ListenerConfig::for_datagram(&addr))?;
258                handle.bind(&Addr::from_std(addr))?;
259                let actual = handle.local_addr()?;
260                let idx = register(state, handle)?;
261                Ok((
262                    unsafe { Fd::from_raw_slot(FdSlot::new(idx), reference) },
263                    actual,
264                ))
265            }
266        }
267
268        fn register(state: &mut Kqueue, handle: Handle) -> io::Result<u32> {
269            let slot = state.alloc_fixed_range(1)?;
270            state.register_raw_fd(slot, handle.into_raw_fd())?;
271            Ok(slot)
272        }
273    }
274}