1 | //! linux_raw syscalls supporting `rustix::net::sockopt`. |
2 | //! |
3 | //! # Safety |
4 | //! |
5 | //! See the `rustix::backend` module documentation for details. |
6 | #![allow (unsafe_code, clippy::undocumented_unsafe_blocks)] |
7 | |
8 | use crate::backend::c; |
9 | use crate::backend::conv::{by_mut, c_uint, ret, socklen_t}; |
10 | use crate::fd::BorrowedFd; |
11 | #[cfg (feature = "alloc" )] |
12 | use crate::ffi::CStr; |
13 | use crate::io; |
14 | use crate::net::sockopt::Timeout; |
15 | #[cfg (target_os = "linux" )] |
16 | use crate::net::xdp::{XdpMmapOffsets, XdpOptionsFlags, XdpRingOffset, XdpStatistics, XdpUmemReg}; |
17 | use crate::net::{ |
18 | AddressFamily, Ipv4Addr, Ipv6Addr, Protocol, RawProtocol, SocketAddrAny, SocketAddrStorage, |
19 | SocketAddrV4, SocketAddrV6, SocketType, UCred, |
20 | }; |
21 | #[cfg (feature = "alloc" )] |
22 | use alloc::borrow::ToOwned; |
23 | #[cfg (feature = "alloc" )] |
24 | use alloc::string::String; |
25 | use core::mem::MaybeUninit; |
26 | use core::time::Duration; |
27 | use linux_raw_sys::general::{__kernel_old_timeval, __kernel_sock_timeval}; |
28 | #[cfg (target_os = "linux" )] |
29 | use linux_raw_sys::xdp::{xdp_mmap_offsets, xdp_statistics, xdp_statistics_v1}; |
30 | #[cfg (target_arch = "x86" )] |
31 | use { |
32 | crate::backend::conv::{slice_just_addr, x86_sys}, |
33 | crate::backend::reg::{ArgReg, SocketArg}, |
34 | linux_raw_sys::net::{SYS_GETSOCKOPT, SYS_SETSOCKOPT}, |
35 | }; |
36 | |
37 | #[inline ] |
38 | fn getsockopt<T: Copy>(fd: BorrowedFd<'_>, level: u32, optname: u32) -> io::Result<T> { |
39 | let mut optlen: c::socklen_t = core::mem::size_of::<T>().try_into().unwrap(); |
40 | debug_assert!( |
41 | optlen as usize >= core::mem::size_of::<c::c_int>(), |
42 | "Socket APIs don't ever use `bool` directly" |
43 | ); |
44 | |
45 | let mut value: MaybeUninit = MaybeUninit::<T>::uninit(); |
46 | getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; |
47 | |
48 | assert_eq!( |
49 | optlen as usize, |
50 | core::mem::size_of::<T>(), |
51 | "unexpected getsockopt size" |
52 | ); |
53 | |
54 | unsafe { Ok(value.assume_init()) } |
55 | } |
56 | |
57 | #[inline ] |
58 | fn getsockopt_raw<T>( |
59 | fd: BorrowedFd<'_>, |
60 | level: u32, |
61 | optname: u32, |
62 | value: &mut MaybeUninit<T>, |
63 | optlen: &mut c::socklen_t, |
64 | ) -> io::Result<()> { |
65 | #[cfg (not(target_arch = "x86" ))] |
66 | unsafe { |
67 | ret(syscall!( |
68 | __NR_getsockopt, |
69 | fd, |
70 | c_uint(level), |
71 | c_uint(optname), |
72 | value, |
73 | by_mut(optlen) |
74 | )) |
75 | } |
76 | #[cfg (target_arch = "x86" )] |
77 | unsafe { |
78 | ret(syscall!( |
79 | __NR_socketcall, |
80 | x86_sys(SYS_GETSOCKOPT), |
81 | slice_just_addr::<ArgReg<'_, SocketArg>, _>(&[ |
82 | fd.into(), |
83 | c_uint(level), |
84 | c_uint(optname), |
85 | value.into(), |
86 | by_mut(optlen), |
87 | ]) |
88 | )) |
89 | } |
90 | } |
91 | |
92 | #[inline ] |
93 | fn setsockopt<T: Copy>(fd: BorrowedFd<'_>, level: u32, optname: u32, value: T) -> io::Result<()> { |
94 | let optlen: u32 = core::mem::size_of::<T>().try_into().unwrap(); |
95 | debug_assert!( |
96 | optlen as usize >= core::mem::size_of::<c::c_int>(), |
97 | "Socket APIs don't ever use `bool` directly" |
98 | ); |
99 | setsockopt_raw(fd, level, optname, &value, optlen) |
100 | } |
101 | |
102 | #[inline ] |
103 | fn setsockopt_raw<T>( |
104 | fd: BorrowedFd<'_>, |
105 | level: u32, |
106 | optname: u32, |
107 | ptr: *const T, |
108 | optlen: c::socklen_t, |
109 | ) -> io::Result<()> { |
110 | #[cfg (not(target_arch = "x86" ))] |
111 | unsafe { |
112 | ret(syscall_readonly!( |
113 | __NR_setsockopt, |
114 | fd, |
115 | c_uint(level), |
116 | c_uint(optname), |
117 | ptr, |
118 | socklen_t(optlen) |
119 | )) |
120 | } |
121 | #[cfg (target_arch = "x86" )] |
122 | unsafe { |
123 | ret(syscall_readonly!( |
124 | __NR_socketcall, |
125 | x86_sys(SYS_SETSOCKOPT), |
126 | slice_just_addr::<ArgReg<'_, SocketArg>, _>(&[ |
127 | fd.into(), |
128 | c_uint(level), |
129 | c_uint(optname), |
130 | ptr.into(), |
131 | socklen_t(optlen), |
132 | ]) |
133 | )) |
134 | } |
135 | } |
136 | |
137 | #[inline ] |
138 | pub(crate) fn get_socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType> { |
139 | getsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_TYPE) |
140 | } |
141 | |
142 | #[inline ] |
143 | pub(crate) fn set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()> { |
144 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_REUSEADDR, value:from_bool(reuseaddr)) |
145 | } |
146 | |
147 | #[inline ] |
148 | pub(crate) fn get_socket_reuseaddr(fd: BorrowedFd<'_>) -> io::Result<bool> { |
149 | getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEADDR).map(op:to_bool) |
150 | } |
151 | |
152 | #[inline ] |
153 | pub(crate) fn set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()> { |
154 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_BROADCAST, value:from_bool(broadcast)) |
155 | } |
156 | |
157 | #[inline ] |
158 | pub(crate) fn get_socket_broadcast(fd: BorrowedFd<'_>) -> io::Result<bool> { |
159 | getsockopt(fd, c::SOL_SOCKET, c::SO_BROADCAST).map(op:to_bool) |
160 | } |
161 | |
162 | #[inline ] |
163 | pub(crate) fn set_socket_linger(fd: BorrowedFd<'_>, linger: Option<Duration>) -> io::Result<()> { |
164 | // Convert `linger` to seconds, rounding up. |
165 | let l_linger: i32 = if let Some(linger: Duration) = linger { |
166 | duration_to_secs(duration:linger)? |
167 | } else { |
168 | 0 |
169 | }; |
170 | let linger: linger = c::linger { |
171 | l_onoff: c::c_int::from(linger.is_some()), |
172 | l_linger, |
173 | }; |
174 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_LINGER, value:linger) |
175 | } |
176 | |
177 | #[inline ] |
178 | pub(crate) fn get_socket_linger(fd: BorrowedFd<'_>) -> io::Result<Option<Duration>> { |
179 | let linger: c::linger = getsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_LINGER)?; |
180 | Ok((linger.l_onoff != 0).then(|| Duration::from_secs(linger.l_linger as u64))) |
181 | } |
182 | |
183 | #[inline ] |
184 | pub(crate) fn set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()> { |
185 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_PASSCRED, value:from_bool(passcred)) |
186 | } |
187 | |
188 | #[inline ] |
189 | pub(crate) fn get_socket_passcred(fd: BorrowedFd<'_>) -> io::Result<bool> { |
190 | getsockopt(fd, c::SOL_SOCKET, c::SO_PASSCRED).map(op:to_bool) |
191 | } |
192 | |
193 | #[inline ] |
194 | pub(crate) fn set_socket_timeout( |
195 | fd: BorrowedFd<'_>, |
196 | id: Timeout, |
197 | timeout: Option<Duration>, |
198 | ) -> io::Result<()> { |
199 | let time: __kernel_sock_timeval = duration_to_linux_sock_timeval(timeout)?; |
200 | let optname: u32 = match id { |
201 | Timeout::Recv => c::SO_RCVTIMEO_NEW, |
202 | Timeout::Send => c::SO_SNDTIMEO_NEW, |
203 | }; |
204 | match setsockopt(fd, level:c::SOL_SOCKET, optname, value:time) { |
205 | Err(io::Errno::NOPROTOOPT) if c::SO_RCVTIMEO_NEW != c::SO_RCVTIMEO_OLD => { |
206 | set_socket_timeout_old(fd, id, timeout) |
207 | } |
208 | otherwise: Result<(), Errno> => otherwise, |
209 | } |
210 | } |
211 | |
212 | /// Same as `set_socket_timeout` but uses `__kernel_old_timeval` instead of |
213 | /// `__kernel_sock_timeval` and `_OLD` constants instead of `_NEW`. |
214 | fn set_socket_timeout_old( |
215 | fd: BorrowedFd<'_>, |
216 | id: Timeout, |
217 | timeout: Option<Duration>, |
218 | ) -> io::Result<()> { |
219 | let time: __kernel_old_timeval = duration_to_linux_old_timeval(timeout)?; |
220 | let optname: u32 = match id { |
221 | Timeout::Recv => c::SO_RCVTIMEO_OLD, |
222 | Timeout::Send => c::SO_SNDTIMEO_OLD, |
223 | }; |
224 | setsockopt(fd, level:c::SOL_SOCKET, optname, value:time) |
225 | } |
226 | |
227 | #[inline ] |
228 | pub(crate) fn get_socket_timeout(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>> { |
229 | let optname: u32 = match id { |
230 | Timeout::Recv => c::SO_RCVTIMEO_NEW, |
231 | Timeout::Send => c::SO_SNDTIMEO_NEW, |
232 | }; |
233 | let time: __kernel_sock_timeval = match getsockopt(fd, level:c::SOL_SOCKET, optname) { |
234 | Err(io::Errno::NOPROTOOPT) if c::SO_RCVTIMEO_NEW != c::SO_RCVTIMEO_OLD => { |
235 | return get_socket_timeout_old(fd, id) |
236 | } |
237 | otherwise: Result<__kernel_sock_timeval, …> => otherwise?, |
238 | }; |
239 | Ok(duration_from_linux_sock_timeval(time)) |
240 | } |
241 | |
242 | /// Same as `get_socket_timeout` but uses `__kernel_old_timeval` instead of |
243 | /// `__kernel_sock_timeval` and `_OLD` constants instead of `_NEW`. |
244 | fn get_socket_timeout_old(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>> { |
245 | let optname: u32 = match id { |
246 | Timeout::Recv => c::SO_RCVTIMEO_OLD, |
247 | Timeout::Send => c::SO_SNDTIMEO_OLD, |
248 | }; |
249 | let time: __kernel_old_timeval = getsockopt(fd, level:c::SOL_SOCKET, optname)?; |
250 | Ok(duration_from_linux_old_timeval(time)) |
251 | } |
252 | |
253 | /// Convert a `__linux_sock_timeval` to a Rust `Option<Duration>`. |
254 | #[inline ] |
255 | fn duration_from_linux_sock_timeval(time: __kernel_sock_timeval) -> Option<Duration> { |
256 | if time.tv_sec == 0 && time.tv_usec == 0 { |
257 | None |
258 | } else { |
259 | Some(Duration::from_secs(time.tv_sec as u64) + Duration::from_micros(time.tv_usec as u64)) |
260 | } |
261 | } |
262 | |
263 | /// Like `duration_from_linux` but uses Linux's old 32-bit |
264 | /// `__kernel_old_timeval`. |
265 | fn duration_from_linux_old_timeval(time: __kernel_old_timeval) -> Option<Duration> { |
266 | if time.tv_sec == 0 && time.tv_usec == 0 { |
267 | None |
268 | } else { |
269 | Some(Duration::from_secs(time.tv_sec as u64) + Duration::from_micros(time.tv_usec as u64)) |
270 | } |
271 | } |
272 | |
273 | /// Convert a Rust `Option<Duration>` to a `__kernel_sock_timeval`. |
274 | #[inline ] |
275 | fn duration_to_linux_sock_timeval(timeout: Option<Duration>) -> io::Result<__kernel_sock_timeval> { |
276 | Ok(match timeout { |
277 | Some(timeout: Duration) => { |
278 | if timeout == Duration::ZERO { |
279 | return Err(io::Errno::INVAL); |
280 | } |
281 | // `subsec_micros` rounds down, so we use `subsec_nanos` and |
282 | // manually round up. |
283 | let mut timeout: __kernel_sock_timeval = __kernel_sock_timeval { |
284 | tv_sec: timeout.as_secs().try_into().unwrap_or(default:i64::MAX), |
285 | tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _, |
286 | }; |
287 | if timeout.tv_sec == 0 && timeout.tv_usec == 0 { |
288 | timeout.tv_usec = 1; |
289 | } |
290 | timeout |
291 | } |
292 | None => __kernel_sock_timeval { |
293 | tv_sec: 0, |
294 | tv_usec: 0, |
295 | }, |
296 | }) |
297 | } |
298 | |
299 | /// Like `duration_to_linux` but uses Linux's old 32-bit |
300 | /// `__kernel_old_timeval`. |
301 | fn duration_to_linux_old_timeval(timeout: Option<Duration>) -> io::Result<__kernel_old_timeval> { |
302 | Ok(match timeout { |
303 | Some(timeout: Duration) => { |
304 | if timeout == Duration::ZERO { |
305 | return Err(io::Errno::INVAL); |
306 | } |
307 | |
308 | // `subsec_micros` rounds down, so we use `subsec_nanos` and |
309 | // manually round up. |
310 | let mut timeout: __kernel_old_timeval = __kernel_old_timeval { |
311 | tv_sec: timeout.as_secs().try_into().unwrap_or(default:c::c_long::MAX), |
312 | tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _, |
313 | }; |
314 | if timeout.tv_sec == 0 && timeout.tv_usec == 0 { |
315 | timeout.tv_usec = 1; |
316 | } |
317 | timeout |
318 | } |
319 | None => __kernel_old_timeval { |
320 | tv_sec: 0, |
321 | tv_usec: 0, |
322 | }, |
323 | }) |
324 | } |
325 | |
326 | #[inline ] |
327 | pub(crate) fn get_socket_error(fd: BorrowedFd<'_>) -> io::Result<Result<(), io::Errno>> { |
328 | let err: c::c_int = getsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_ERROR)?; |
329 | Ok(if err == 0 { |
330 | Ok(()) |
331 | } else { |
332 | Err(io::Errno::from_raw_os_error(raw:err)) |
333 | }) |
334 | } |
335 | |
336 | #[inline ] |
337 | pub(crate) fn set_socket_keepalive(fd: BorrowedFd<'_>, keepalive: bool) -> io::Result<()> { |
338 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_KEEPALIVE, value:from_bool(keepalive)) |
339 | } |
340 | |
341 | #[inline ] |
342 | pub(crate) fn get_socket_keepalive(fd: BorrowedFd<'_>) -> io::Result<bool> { |
343 | getsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE).map(op:to_bool) |
344 | } |
345 | |
346 | #[inline ] |
347 | pub(crate) fn set_socket_recv_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { |
348 | let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; |
349 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_RCVBUF, value:size) |
350 | } |
351 | |
352 | #[inline ] |
353 | pub(crate) fn set_socket_recv_buffer_size_force(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { |
354 | let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; |
355 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_RCVBUFFORCE, value:size) |
356 | } |
357 | |
358 | #[inline ] |
359 | pub(crate) fn get_socket_recv_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> { |
360 | getsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF).map(|size: u32| size as usize) |
361 | } |
362 | |
363 | #[inline ] |
364 | pub(crate) fn set_socket_send_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { |
365 | let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; |
366 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_SNDBUF, value:size) |
367 | } |
368 | |
369 | #[inline ] |
370 | pub(crate) fn get_socket_send_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> { |
371 | getsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF).map(|size: u32| size as usize) |
372 | } |
373 | |
374 | #[inline ] |
375 | pub(crate) fn get_socket_domain(fd: BorrowedFd<'_>) -> io::Result<AddressFamily> { |
376 | let domain: c::c_int = getsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_DOMAIN)?; |
377 | Ok(AddressFamily( |
378 | domain.try_into().map_err(|_| io::Errno::OPNOTSUPP)?, |
379 | )) |
380 | } |
381 | |
382 | #[inline ] |
383 | pub(crate) fn get_socket_acceptconn(fd: BorrowedFd<'_>) -> io::Result<bool> { |
384 | getsockopt(fd, c::SOL_SOCKET, c::SO_ACCEPTCONN).map(op:to_bool) |
385 | } |
386 | |
387 | #[inline ] |
388 | pub(crate) fn set_socket_oobinline(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
389 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_OOBINLINE, value:from_bool(value)) |
390 | } |
391 | |
392 | #[inline ] |
393 | pub(crate) fn get_socket_oobinline(fd: BorrowedFd<'_>) -> io::Result<bool> { |
394 | getsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE).map(op:to_bool) |
395 | } |
396 | |
397 | #[inline ] |
398 | pub(crate) fn set_socket_reuseport(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
399 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_REUSEPORT, value:from_bool(value)) |
400 | } |
401 | |
402 | #[inline ] |
403 | pub(crate) fn get_socket_reuseport(fd: BorrowedFd<'_>) -> io::Result<bool> { |
404 | getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT).map(op:to_bool) |
405 | } |
406 | |
407 | #[inline ] |
408 | pub(crate) fn get_socket_protocol(fd: BorrowedFd<'_>) -> io::Result<Option<Protocol>> { |
409 | getsockopt(fd, c::SOL_SOCKET, c::SO_PROTOCOL) |
410 | .map(|raw: u32| RawProtocol::new(raw).map(Protocol::from_raw)) |
411 | } |
412 | |
413 | #[inline ] |
414 | pub(crate) fn get_socket_cookie(fd: BorrowedFd<'_>) -> io::Result<u64> { |
415 | getsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_COOKIE) |
416 | } |
417 | |
418 | #[inline ] |
419 | pub(crate) fn get_socket_incoming_cpu(fd: BorrowedFd<'_>) -> io::Result<u32> { |
420 | getsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_INCOMING_CPU) |
421 | } |
422 | |
423 | #[inline ] |
424 | pub(crate) fn set_socket_incoming_cpu(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { |
425 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_INCOMING_CPU, value) |
426 | } |
427 | |
428 | #[inline ] |
429 | pub(crate) fn set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()> { |
430 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_TTL, value:ttl) |
431 | } |
432 | |
433 | #[inline ] |
434 | pub(crate) fn get_ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> { |
435 | getsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_TTL) |
436 | } |
437 | |
438 | #[inline ] |
439 | pub(crate) fn set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()> { |
440 | setsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_V6ONLY, value:from_bool(only_v6)) |
441 | } |
442 | |
443 | #[inline ] |
444 | pub(crate) fn get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> { |
445 | getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY).map(op:to_bool) |
446 | } |
447 | |
448 | #[inline ] |
449 | pub(crate) fn set_ip_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> { |
450 | setsockopt( |
451 | fd, |
452 | level:c::IPPROTO_IP, |
453 | optname:c::IP_MULTICAST_LOOP, |
454 | value:from_bool(multicast_loop), |
455 | ) |
456 | } |
457 | |
458 | #[inline ] |
459 | pub(crate) fn get_ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> { |
460 | getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_LOOP).map(op:to_bool) |
461 | } |
462 | |
463 | #[inline ] |
464 | pub(crate) fn set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()> { |
465 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_MULTICAST_TTL, value:multicast_ttl) |
466 | } |
467 | |
468 | #[inline ] |
469 | pub(crate) fn get_ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> { |
470 | getsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_MULTICAST_TTL) |
471 | } |
472 | |
473 | #[inline ] |
474 | pub(crate) fn set_ipv6_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> { |
475 | setsockopt( |
476 | fd, |
477 | level:c::IPPROTO_IPV6, |
478 | optname:c::IPV6_MULTICAST_LOOP, |
479 | value:from_bool(multicast_loop), |
480 | ) |
481 | } |
482 | |
483 | #[inline ] |
484 | pub(crate) fn get_ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> { |
485 | getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP).map(op:to_bool) |
486 | } |
487 | |
488 | #[inline ] |
489 | pub(crate) fn set_ipv6_multicast_hops(fd: BorrowedFd<'_>, multicast_hops: u32) -> io::Result<()> { |
490 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IPV6_MULTICAST_HOPS, value:multicast_hops) |
491 | } |
492 | |
493 | #[inline ] |
494 | pub(crate) fn get_ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32> { |
495 | getsockopt(fd, level:c::IPPROTO_IP, optname:c::IPV6_MULTICAST_HOPS) |
496 | } |
497 | |
498 | #[inline ] |
499 | pub(crate) fn set_ip_add_membership( |
500 | fd: BorrowedFd<'_>, |
501 | multiaddr: &Ipv4Addr, |
502 | interface: &Ipv4Addr, |
503 | ) -> io::Result<()> { |
504 | let mreq: ip_mreq = to_ip_mreq(multiaddr, interface); |
505 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_ADD_MEMBERSHIP, value:mreq) |
506 | } |
507 | |
508 | #[inline ] |
509 | pub(crate) fn set_ip_add_membership_with_ifindex( |
510 | fd: BorrowedFd<'_>, |
511 | multiaddr: &Ipv4Addr, |
512 | address: &Ipv4Addr, |
513 | ifindex: i32, |
514 | ) -> io::Result<()> { |
515 | let mreqn: ip_mreqn = to_ip_mreqn(multiaddr, address, ifindex); |
516 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_ADD_MEMBERSHIP, value:mreqn) |
517 | } |
518 | |
519 | #[inline ] |
520 | pub(crate) fn set_ip_add_source_membership( |
521 | fd: BorrowedFd<'_>, |
522 | multiaddr: &Ipv4Addr, |
523 | interface: &Ipv4Addr, |
524 | sourceaddr: &Ipv4Addr, |
525 | ) -> io::Result<()> { |
526 | let mreq_source: ip_mreq_source = to_imr_source(multiaddr, interface, sourceaddr); |
527 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_ADD_SOURCE_MEMBERSHIP, value:mreq_source) |
528 | } |
529 | |
530 | #[inline ] |
531 | pub(crate) fn set_ip_drop_source_membership( |
532 | fd: BorrowedFd<'_>, |
533 | multiaddr: &Ipv4Addr, |
534 | interface: &Ipv4Addr, |
535 | sourceaddr: &Ipv4Addr, |
536 | ) -> io::Result<()> { |
537 | let mreq_source: ip_mreq_source = to_imr_source(multiaddr, interface, sourceaddr); |
538 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_DROP_SOURCE_MEMBERSHIP, value:mreq_source) |
539 | } |
540 | |
541 | #[inline ] |
542 | pub(crate) fn set_ipv6_add_membership( |
543 | fd: BorrowedFd<'_>, |
544 | multiaddr: &Ipv6Addr, |
545 | interface: u32, |
546 | ) -> io::Result<()> { |
547 | let mreq: ipv6_mreq = to_ipv6mr(multiaddr, interface); |
548 | setsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_ADD_MEMBERSHIP, value:mreq) |
549 | } |
550 | |
551 | #[inline ] |
552 | pub(crate) fn set_ip_drop_membership( |
553 | fd: BorrowedFd<'_>, |
554 | multiaddr: &Ipv4Addr, |
555 | interface: &Ipv4Addr, |
556 | ) -> io::Result<()> { |
557 | let mreq: ip_mreq = to_ip_mreq(multiaddr, interface); |
558 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_DROP_MEMBERSHIP, value:mreq) |
559 | } |
560 | |
561 | #[inline ] |
562 | pub(crate) fn set_ip_drop_membership_with_ifindex( |
563 | fd: BorrowedFd<'_>, |
564 | multiaddr: &Ipv4Addr, |
565 | address: &Ipv4Addr, |
566 | ifindex: i32, |
567 | ) -> io::Result<()> { |
568 | let mreqn: ip_mreqn = to_ip_mreqn(multiaddr, address, ifindex); |
569 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_DROP_MEMBERSHIP, value:mreqn) |
570 | } |
571 | |
572 | #[inline ] |
573 | pub(crate) fn set_ipv6_drop_membership( |
574 | fd: BorrowedFd<'_>, |
575 | multiaddr: &Ipv6Addr, |
576 | interface: u32, |
577 | ) -> io::Result<()> { |
578 | let mreq: ipv6_mreq = to_ipv6mr(multiaddr, interface); |
579 | setsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_DROP_MEMBERSHIP, value:mreq) |
580 | } |
581 | |
582 | #[inline ] |
583 | pub(crate) fn get_ipv6_unicast_hops(fd: BorrowedFd<'_>) -> io::Result<u8> { |
584 | getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS).map(|hops: c::c_int| hops as u8) |
585 | } |
586 | |
587 | #[inline ] |
588 | pub(crate) fn set_ipv6_unicast_hops(fd: BorrowedFd<'_>, hops: Option<u8>) -> io::Result<()> { |
589 | let hops: i32 = match hops { |
590 | Some(hops: u8) => hops.into(), |
591 | None => -1, |
592 | }; |
593 | setsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_UNICAST_HOPS, value:hops) |
594 | } |
595 | |
596 | #[inline ] |
597 | pub(crate) fn set_ip_tos(fd: BorrowedFd<'_>, value: u8) -> io::Result<()> { |
598 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_TOS, value:i32::from(value)) |
599 | } |
600 | |
601 | #[inline ] |
602 | pub(crate) fn get_ip_tos(fd: BorrowedFd<'_>) -> io::Result<u8> { |
603 | let value: i32 = getsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_TOS)?; |
604 | Ok(value as u8) |
605 | } |
606 | |
607 | #[inline ] |
608 | pub(crate) fn set_ip_recvtos(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
609 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_RECVTOS, value:from_bool(value)) |
610 | } |
611 | |
612 | #[inline ] |
613 | pub(crate) fn get_ip_recvtos(fd: BorrowedFd<'_>) -> io::Result<bool> { |
614 | getsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS).map(op:to_bool) |
615 | } |
616 | |
617 | #[inline ] |
618 | pub(crate) fn set_ipv6_recvtclass(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
619 | setsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_RECVTCLASS, value:from_bool(value)) |
620 | } |
621 | |
622 | #[inline ] |
623 | pub(crate) fn get_ipv6_recvtclass(fd: BorrowedFd<'_>) -> io::Result<bool> { |
624 | getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS).map(op:to_bool) |
625 | } |
626 | |
627 | #[inline ] |
628 | pub(crate) fn set_ip_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
629 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_FREEBIND, value:from_bool(value)) |
630 | } |
631 | |
632 | #[inline ] |
633 | pub(crate) fn get_ip_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> { |
634 | getsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND).map(op:to_bool) |
635 | } |
636 | |
637 | #[inline ] |
638 | pub(crate) fn set_ipv6_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
639 | setsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_FREEBIND, value:from_bool(value)) |
640 | } |
641 | |
642 | #[inline ] |
643 | pub(crate) fn get_ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> { |
644 | getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND).map(op:to_bool) |
645 | } |
646 | |
647 | #[inline ] |
648 | pub(crate) fn get_ip_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV4> { |
649 | let level: u32 = c::IPPROTO_IP; |
650 | let optname: u32 = c::SO_ORIGINAL_DST; |
651 | let mut value: MaybeUninit = MaybeUninit::<SocketAddrStorage>::uninit(); |
652 | let mut optlen: usize = core::mem::size_of_val(&value).try_into().unwrap(); |
653 | |
654 | getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; |
655 | |
656 | let any: SocketAddrAny = unsafe { SocketAddrAny::read(storage:value.as_ptr(), len:optlen as usize)? }; |
657 | match any { |
658 | SocketAddrAny::V4(v4: SocketAddrV4) => Ok(v4), |
659 | _ => unreachable!(), |
660 | } |
661 | } |
662 | |
663 | #[inline ] |
664 | pub(crate) fn get_ipv6_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV6> { |
665 | let level: u32 = c::IPPROTO_IPV6; |
666 | let optname: u32 = c::IP6T_SO_ORIGINAL_DST; |
667 | let mut value: MaybeUninit = MaybeUninit::<SocketAddrStorage>::uninit(); |
668 | let mut optlen: usize = core::mem::size_of_val(&value).try_into().unwrap(); |
669 | |
670 | getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; |
671 | |
672 | let any: SocketAddrAny = unsafe { SocketAddrAny::read(storage:value.as_ptr(), len:optlen as usize)? }; |
673 | match any { |
674 | SocketAddrAny::V6(v6: SocketAddrV6) => Ok(v6), |
675 | _ => unreachable!(), |
676 | } |
677 | } |
678 | |
679 | #[inline ] |
680 | pub(crate) fn set_ipv6_tclass(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { |
681 | setsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_TCLASS, value) |
682 | } |
683 | |
684 | #[inline ] |
685 | pub(crate) fn get_ipv6_tclass(fd: BorrowedFd<'_>) -> io::Result<u32> { |
686 | getsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_TCLASS) |
687 | } |
688 | |
689 | #[inline ] |
690 | pub(crate) fn set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()> { |
691 | setsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_NODELAY, value:from_bool(nodelay)) |
692 | } |
693 | |
694 | #[inline ] |
695 | pub(crate) fn get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool> { |
696 | getsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY).map(op:to_bool) |
697 | } |
698 | |
699 | #[inline ] |
700 | pub(crate) fn set_tcp_keepcnt(fd: BorrowedFd<'_>, count: u32) -> io::Result<()> { |
701 | setsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_KEEPCNT, value:count) |
702 | } |
703 | |
704 | #[inline ] |
705 | pub(crate) fn get_tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result<u32> { |
706 | getsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_KEEPCNT) |
707 | } |
708 | |
709 | #[inline ] |
710 | pub(crate) fn set_tcp_keepidle(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> { |
711 | let secs: c::c_uint = duration_to_secs(duration)?; |
712 | setsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_KEEPIDLE, value:secs) |
713 | } |
714 | |
715 | #[inline ] |
716 | pub(crate) fn get_tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result<Duration> { |
717 | let secs: c::c_uint = getsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_KEEPIDLE)?; |
718 | Ok(Duration::from_secs(secs.into())) |
719 | } |
720 | |
721 | #[inline ] |
722 | pub(crate) fn set_tcp_keepintvl(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> { |
723 | let secs: c::c_uint = duration_to_secs(duration)?; |
724 | setsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_KEEPINTVL, value:secs) |
725 | } |
726 | |
727 | #[inline ] |
728 | pub(crate) fn get_tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result<Duration> { |
729 | let secs: c::c_uint = getsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_KEEPINTVL)?; |
730 | Ok(Duration::from_secs(secs.into())) |
731 | } |
732 | |
733 | #[inline ] |
734 | pub(crate) fn set_tcp_user_timeout(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { |
735 | setsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_USER_TIMEOUT, value) |
736 | } |
737 | |
738 | #[inline ] |
739 | pub(crate) fn get_tcp_user_timeout(fd: BorrowedFd<'_>) -> io::Result<u32> { |
740 | getsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_USER_TIMEOUT) |
741 | } |
742 | |
743 | #[inline ] |
744 | pub(crate) fn set_tcp_quickack(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
745 | setsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_QUICKACK, value:from_bool(value)) |
746 | } |
747 | |
748 | #[inline ] |
749 | pub(crate) fn get_tcp_quickack(fd: BorrowedFd<'_>) -> io::Result<bool> { |
750 | getsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK).map(op:to_bool) |
751 | } |
752 | |
753 | #[inline ] |
754 | pub(crate) fn set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result<()> { |
755 | let level: u32 = c::IPPROTO_TCP; |
756 | let optname: u32 = c::TCP_CONGESTION; |
757 | let optlen: u32 = value.len().try_into().unwrap(); |
758 | setsockopt_raw(fd, level, optname, value.as_ptr(), optlen) |
759 | } |
760 | |
761 | #[cfg (feature = "alloc" )] |
762 | #[inline ] |
763 | pub(crate) fn get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result<String> { |
764 | const OPTLEN: c::socklen_t = 16; |
765 | |
766 | let level: u32 = c::IPPROTO_TCP; |
767 | let optname: u32 = c::TCP_CONGESTION; |
768 | let mut value: MaybeUninit<[MaybeUninit<…>; _]> = MaybeUninit::<[MaybeUninit<u8>; OPTLEN as usize]>::uninit(); |
769 | let mut optlen: u32 = OPTLEN; |
770 | getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; |
771 | unsafe { |
772 | let value: [MaybeUninit; _] = value.assume_init(); |
773 | let slice: &[u8] = core::mem::transmute(&value[..optlen as usize]); |
774 | assert!(slice.iter().any(|b| *b == b' \0' )); |
775 | Ok( |
776 | core&str::str::from_utf8(CStr::from_ptr(slice.as_ptr().cast()).to_bytes()) |
777 | .unwrap() |
778 | .to_owned(), |
779 | ) |
780 | } |
781 | } |
782 | |
783 | #[inline ] |
784 | pub(crate) fn set_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
785 | setsockopt( |
786 | fd, |
787 | level:c::IPPROTO_TCP, |
788 | optname:c::TCP_THIN_LINEAR_TIMEOUTS, |
789 | value:from_bool(value), |
790 | ) |
791 | } |
792 | |
793 | #[inline ] |
794 | pub(crate) fn get_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>) -> io::Result<bool> { |
795 | getsockopt(fd, c::IPPROTO_TCP, c::TCP_THIN_LINEAR_TIMEOUTS).map(op:to_bool) |
796 | } |
797 | |
798 | #[inline ] |
799 | pub(crate) fn set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
800 | setsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_CORK, value:from_bool(value)) |
801 | } |
802 | |
803 | #[inline ] |
804 | pub(crate) fn get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool> { |
805 | getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(op:to_bool) |
806 | } |
807 | |
808 | #[inline ] |
809 | pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> { |
810 | getsockopt(fd, level:c::SOL_SOCKET, optname:linux_raw_sys::net::SO_PEERCRED) |
811 | } |
812 | |
813 | #[cfg (target_os = "linux" )] |
814 | #[inline ] |
815 | pub(crate) fn set_xdp_umem_reg(fd: BorrowedFd<'_>, value: XdpUmemReg) -> io::Result<()> { |
816 | setsockopt(fd, level:c::SOL_XDP, optname:c::XDP_UMEM_REG, value) |
817 | } |
818 | |
819 | #[cfg (target_os = "linux" )] |
820 | #[inline ] |
821 | pub(crate) fn set_xdp_umem_fill_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { |
822 | setsockopt(fd, level:c::SOL_XDP, optname:c::XDP_UMEM_FILL_RING, value) |
823 | } |
824 | |
825 | #[cfg (target_os = "linux" )] |
826 | #[inline ] |
827 | pub(crate) fn set_xdp_umem_completion_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { |
828 | setsockopt(fd, level:c::SOL_XDP, optname:c::XDP_UMEM_COMPLETION_RING, value) |
829 | } |
830 | |
831 | #[cfg (target_os = "linux" )] |
832 | #[inline ] |
833 | pub(crate) fn set_xdp_tx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { |
834 | setsockopt(fd, level:c::SOL_XDP, optname:c::XDP_TX_RING, value) |
835 | } |
836 | |
837 | #[cfg (target_os = "linux" )] |
838 | #[inline ] |
839 | pub(crate) fn set_xdp_rx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { |
840 | setsockopt(fd, level:c::SOL_XDP, optname:c::XDP_RX_RING, value) |
841 | } |
842 | |
843 | #[cfg (target_os = "linux" )] |
844 | #[inline ] |
845 | pub(crate) fn get_xdp_mmap_offsets(fd: BorrowedFd<'_>) -> io::Result<XdpMmapOffsets> { |
846 | // The kernel will write `xdp_mmap_offsets` or `xdp_mmap_offsets_v1` to the |
847 | // supplied pointer, depending on the kernel version. Both structs only |
848 | // contain u64 values. By using the larger of both as the parameter, we can |
849 | // shuffle the values to the non-v1 version returned by |
850 | // `get_xdp_mmap_offsets` while keeping the return type unaffected by the |
851 | // kernel version. This works because C will layout all struct members one |
852 | // after the other. |
853 | |
854 | let mut optlen = core::mem::size_of::<xdp_mmap_offsets>().try_into().unwrap(); |
855 | debug_assert!( |
856 | optlen as usize >= core::mem::size_of::<c::c_int>(), |
857 | "Socket APIs don't ever use `bool` directly" |
858 | ); |
859 | let mut value = MaybeUninit::<xdp_mmap_offsets>::zeroed(); |
860 | getsockopt_raw(fd, c::SOL_XDP, c::XDP_MMAP_OFFSETS, &mut value, &mut optlen)?; |
861 | |
862 | if optlen as usize == core::mem::size_of::<c::xdp_mmap_offsets_v1>() { |
863 | // Safety: All members of xdp_mmap_offsets are u64 and thus are correctly |
864 | // initialized by `MaybeUninit::<xdp_statistics>::zeroed()` |
865 | let xpd_mmap_offsets = unsafe { value.assume_init() }; |
866 | Ok(XdpMmapOffsets { |
867 | rx: XdpRingOffset { |
868 | producer: xpd_mmap_offsets.rx.producer, |
869 | consumer: xpd_mmap_offsets.rx.consumer, |
870 | desc: xpd_mmap_offsets.rx.desc, |
871 | flags: None, |
872 | }, |
873 | tx: XdpRingOffset { |
874 | producer: xpd_mmap_offsets.rx.flags, |
875 | consumer: xpd_mmap_offsets.tx.producer, |
876 | desc: xpd_mmap_offsets.tx.consumer, |
877 | flags: None, |
878 | }, |
879 | fr: XdpRingOffset { |
880 | producer: xpd_mmap_offsets.tx.desc, |
881 | consumer: xpd_mmap_offsets.tx.flags, |
882 | desc: xpd_mmap_offsets.fr.producer, |
883 | flags: None, |
884 | }, |
885 | cr: XdpRingOffset { |
886 | producer: xpd_mmap_offsets.fr.consumer, |
887 | consumer: xpd_mmap_offsets.fr.desc, |
888 | desc: xpd_mmap_offsets.fr.flags, |
889 | flags: None, |
890 | }, |
891 | }) |
892 | } else { |
893 | assert_eq!( |
894 | optlen as usize, |
895 | core::mem::size_of::<xdp_mmap_offsets>(), |
896 | "unexpected getsockopt size" |
897 | ); |
898 | // Safety: All members of xdp_mmap_offsets are u64 and thus are correctly |
899 | // initialized by `MaybeUninit::<xdp_statistics>::zeroed()` |
900 | let xpd_mmap_offsets = unsafe { value.assume_init() }; |
901 | Ok(XdpMmapOffsets { |
902 | rx: XdpRingOffset { |
903 | producer: xpd_mmap_offsets.rx.producer, |
904 | consumer: xpd_mmap_offsets.rx.consumer, |
905 | desc: xpd_mmap_offsets.rx.desc, |
906 | flags: Some(xpd_mmap_offsets.rx.flags), |
907 | }, |
908 | tx: XdpRingOffset { |
909 | producer: xpd_mmap_offsets.tx.producer, |
910 | consumer: xpd_mmap_offsets.tx.consumer, |
911 | desc: xpd_mmap_offsets.tx.desc, |
912 | flags: Some(xpd_mmap_offsets.tx.flags), |
913 | }, |
914 | fr: XdpRingOffset { |
915 | producer: xpd_mmap_offsets.fr.producer, |
916 | consumer: xpd_mmap_offsets.fr.consumer, |
917 | desc: xpd_mmap_offsets.fr.desc, |
918 | flags: Some(xpd_mmap_offsets.fr.flags), |
919 | }, |
920 | cr: XdpRingOffset { |
921 | producer: xpd_mmap_offsets.cr.producer, |
922 | consumer: xpd_mmap_offsets.cr.consumer, |
923 | desc: xpd_mmap_offsets.cr.desc, |
924 | flags: Some(xpd_mmap_offsets.cr.flags), |
925 | }, |
926 | }) |
927 | } |
928 | } |
929 | |
930 | #[cfg (target_os = "linux" )] |
931 | #[inline ] |
932 | pub(crate) fn get_xdp_statistics(fd: BorrowedFd<'_>) -> io::Result<XdpStatistics> { |
933 | let mut optlen = core::mem::size_of::<xdp_statistics>().try_into().unwrap(); |
934 | debug_assert!( |
935 | optlen as usize >= core::mem::size_of::<c::c_int>(), |
936 | "Socket APIs don't ever use `bool` directly" |
937 | ); |
938 | let mut value = MaybeUninit::<xdp_statistics>::zeroed(); |
939 | getsockopt_raw(fd, c::SOL_XDP, c::XDP_STATISTICS, &mut value, &mut optlen)?; |
940 | |
941 | if optlen as usize == core::mem::size_of::<xdp_statistics_v1>() { |
942 | // Safety: All members of xdp_statistics are u64 and thus are correctly |
943 | // initialized by `MaybeUninit::<xdp_statistics>::zeroed()` |
944 | let xdp_statistics = unsafe { value.assume_init() }; |
945 | Ok(XdpStatistics { |
946 | rx_dropped: xdp_statistics.rx_dropped, |
947 | rx_invalid_descs: xdp_statistics.rx_dropped, |
948 | tx_invalid_descs: xdp_statistics.rx_dropped, |
949 | rx_ring_full: None, |
950 | rx_fill_ring_empty_descs: None, |
951 | tx_ring_empty_descs: None, |
952 | }) |
953 | } else { |
954 | assert_eq!( |
955 | optlen as usize, |
956 | core::mem::size_of::<xdp_statistics>(), |
957 | "unexpected getsockopt size" |
958 | ); |
959 | // Safety: All members of xdp_statistics are u64 and thus are correctly |
960 | // initialized by `MaybeUninit::<xdp_statistics>::zeroed()` |
961 | let xdp_statistics = unsafe { value.assume_init() }; |
962 | Ok(XdpStatistics { |
963 | rx_dropped: xdp_statistics.rx_dropped, |
964 | rx_invalid_descs: xdp_statistics.rx_invalid_descs, |
965 | tx_invalid_descs: xdp_statistics.tx_invalid_descs, |
966 | rx_ring_full: Some(xdp_statistics.rx_ring_full), |
967 | rx_fill_ring_empty_descs: Some(xdp_statistics.rx_fill_ring_empty_descs), |
968 | tx_ring_empty_descs: Some(xdp_statistics.tx_ring_empty_descs), |
969 | }) |
970 | } |
971 | } |
972 | |
973 | #[cfg (target_os = "linux" )] |
974 | #[inline ] |
975 | pub(crate) fn get_xdp_options(fd: BorrowedFd<'_>) -> io::Result<XdpOptionsFlags> { |
976 | getsockopt(fd, level:c::SOL_XDP, optname:c::XDP_OPTIONS) |
977 | } |
978 | |
979 | #[inline ] |
980 | fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq { |
981 | c::ip_mreq { |
982 | imr_multiaddr: to_imr_addr(multiaddr), |
983 | imr_interface: to_imr_addr(interface), |
984 | } |
985 | } |
986 | |
987 | #[inline ] |
988 | fn to_ip_mreqn(multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32) -> c::ip_mreqn { |
989 | c::ip_mreqn { |
990 | imr_multiaddr: to_imr_addr(multiaddr), |
991 | imr_address: to_imr_addr(address), |
992 | imr_ifindex: ifindex, |
993 | } |
994 | } |
995 | |
996 | #[inline ] |
997 | fn to_imr_source( |
998 | multiaddr: &Ipv4Addr, |
999 | interface: &Ipv4Addr, |
1000 | sourceaddr: &Ipv4Addr, |
1001 | ) -> c::ip_mreq_source { |
1002 | c::ip_mreq_source { |
1003 | imr_multiaddr: to_imr_addr(multiaddr).s_addr, |
1004 | imr_interface: to_imr_addr(interface).s_addr, |
1005 | imr_sourceaddr: to_imr_addr(sourceaddr).s_addr, |
1006 | } |
1007 | } |
1008 | |
1009 | #[inline ] |
1010 | fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr { |
1011 | c::in_addr { |
1012 | s_addr: u32::from_ne_bytes(addr.octets()), |
1013 | } |
1014 | } |
1015 | |
1016 | #[inline ] |
1017 | fn to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq { |
1018 | c::ipv6_mreq { |
1019 | ipv6mr_multiaddr: to_ipv6mr_multiaddr(multiaddr), |
1020 | ipv6mr_ifindex: to_ipv6mr_interface(interface), |
1021 | } |
1022 | } |
1023 | |
1024 | #[inline ] |
1025 | fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr { |
1026 | c::in6_addr { |
1027 | in6_u: linux_raw_sys::net::in6_addr__bindgen_ty_1 { |
1028 | u6_addr8: multiaddr.octets(), |
1029 | }, |
1030 | } |
1031 | } |
1032 | |
1033 | #[inline ] |
1034 | fn to_ipv6mr_interface(interface: u32) -> c::c_int { |
1035 | interface as c::c_int |
1036 | } |
1037 | |
1038 | #[inline ] |
1039 | fn from_bool(value: bool) -> c::c_uint { |
1040 | c::c_uint::from(value) |
1041 | } |
1042 | |
1043 | #[inline ] |
1044 | fn to_bool(value: c::c_uint) -> bool { |
1045 | value != 0 |
1046 | } |
1047 | |
1048 | /// Convert to seconds, rounding up if necessary. |
1049 | #[inline ] |
1050 | fn duration_to_secs<T: TryFrom<u64>>(duration: Duration) -> io::Result<T> { |
1051 | let mut secs: u64 = duration.as_secs(); |
1052 | if duration.subsec_nanos() != 0 { |
1053 | secs = secs.checked_add(1).ok_or(err:io::Errno::INVAL)?; |
1054 | } |
1055 | T::try_from(secs).map_err(|_e: >::Error| io::Errno::INVAL) |
1056 | } |
1057 | |