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]
getsockopt<T: Copy>(fd: BorrowedFd<'_>, level: u32, optname: u32) -> io::Result<T>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::<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]
getsockopt_raw<T>( fd: BorrowedFd<'_>, level: u32, optname: u32, value: &mut MaybeUninit<T>, optlen: &mut c::socklen_t, ) -> io::Result<()>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]
setsockopt<T: Copy>(fd: BorrowedFd<'_>, level: u32, optname: u32, value: T) -> io::Result<()>93 fn setsockopt<T: Copy>(fd: BorrowedFd<'_>, level: u32, optname: u32, value: T) -> io::Result<()> {
94     let optlen = 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]
setsockopt_raw<T>( fd: BorrowedFd<'_>, level: u32, optname: u32, ptr: *const T, optlen: c::socklen_t, ) -> io::Result<()>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]
get_socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType>138 pub(crate) fn get_socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType> {
139     getsockopt(fd, c::SOL_SOCKET, c::SO_TYPE)
140 }
141 
142 #[inline]
set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()>143 pub(crate) fn set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()> {
144     setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEADDR, from_bool(reuseaddr))
145 }
146 
147 #[inline]
get_socket_reuseaddr(fd: BorrowedFd<'_>) -> io::Result<bool>148 pub(crate) fn get_socket_reuseaddr(fd: BorrowedFd<'_>) -> io::Result<bool> {
149     getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEADDR).map(to_bool)
150 }
151 
152 #[inline]
set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()>153 pub(crate) fn set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()> {
154     setsockopt(fd, c::SOL_SOCKET, c::SO_BROADCAST, from_bool(broadcast))
155 }
156 
157 #[inline]
get_socket_broadcast(fd: BorrowedFd<'_>) -> io::Result<bool>158 pub(crate) fn get_socket_broadcast(fd: BorrowedFd<'_>) -> io::Result<bool> {
159     getsockopt(fd, c::SOL_SOCKET, c::SO_BROADCAST).map(to_bool)
160 }
161 
162 #[inline]
set_socket_linger(fd: BorrowedFd<'_>, linger: Option<Duration>) -> io::Result<()>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 = if let Some(linger) = linger {
166         duration_to_secs(linger)?
167     } else {
168         0
169     };
170     let linger = c::linger {
171         l_onoff: c::c_int::from(linger.is_some()),
172         l_linger,
173     };
174     setsockopt(fd, c::SOL_SOCKET, c::SO_LINGER, linger)
175 }
176 
177 #[inline]
get_socket_linger(fd: BorrowedFd<'_>) -> io::Result<Option<Duration>>178 pub(crate) fn get_socket_linger(fd: BorrowedFd<'_>) -> io::Result<Option<Duration>> {
179     let linger: c::linger = getsockopt(fd, c::SOL_SOCKET, c::SO_LINGER)?;
180     Ok((linger.l_onoff != 0).then(|| Duration::from_secs(linger.l_linger as u64)))
181 }
182 
183 #[inline]
set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()>184 pub(crate) fn set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()> {
185     setsockopt(fd, c::SOL_SOCKET, c::SO_PASSCRED, from_bool(passcred))
186 }
187 
188 #[inline]
get_socket_passcred(fd: BorrowedFd<'_>) -> io::Result<bool>189 pub(crate) fn get_socket_passcred(fd: BorrowedFd<'_>) -> io::Result<bool> {
190     getsockopt(fd, c::SOL_SOCKET, c::SO_PASSCRED).map(to_bool)
191 }
192 
193 #[inline]
set_socket_timeout( fd: BorrowedFd<'_>, id: Timeout, timeout: Option<Duration>, ) -> io::Result<()>194 pub(crate) fn set_socket_timeout(
195     fd: BorrowedFd<'_>,
196     id: Timeout,
197     timeout: Option<Duration>,
198 ) -> io::Result<()> {
199     let time = duration_to_linux_sock_timeval(timeout)?;
200     let optname = match id {
201         Timeout::Recv => c::SO_RCVTIMEO_NEW,
202         Timeout::Send => c::SO_SNDTIMEO_NEW,
203     };
204     match setsockopt(fd, c::SOL_SOCKET, optname, 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 => 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`.
set_socket_timeout_old( fd: BorrowedFd<'_>, id: Timeout, timeout: Option<Duration>, ) -> io::Result<()>214 fn set_socket_timeout_old(
215     fd: BorrowedFd<'_>,
216     id: Timeout,
217     timeout: Option<Duration>,
218 ) -> io::Result<()> {
219     let time = duration_to_linux_old_timeval(timeout)?;
220     let optname = match id {
221         Timeout::Recv => c::SO_RCVTIMEO_OLD,
222         Timeout::Send => c::SO_SNDTIMEO_OLD,
223     };
224     setsockopt(fd, c::SOL_SOCKET, optname, time)
225 }
226 
227 #[inline]
get_socket_timeout(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>>228 pub(crate) fn get_socket_timeout(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>> {
229     let optname = 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, 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 => 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`.
get_socket_timeout_old(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>>244 fn get_socket_timeout_old(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>> {
245     let optname = 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, 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]
duration_from_linux_sock_timeval(time: __kernel_sock_timeval) -> Option<Duration>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`.
duration_from_linux_old_timeval(time: __kernel_old_timeval) -> Option<Duration>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]
duration_to_linux_sock_timeval(timeout: Option<Duration>) -> io::Result<__kernel_sock_timeval>275 fn duration_to_linux_sock_timeval(timeout: Option<Duration>) -> io::Result<__kernel_sock_timeval> {
276     Ok(match timeout {
277         Some(timeout) => {
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 {
284                 tv_sec: timeout.as_secs().try_into().unwrap_or(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`.
duration_to_linux_old_timeval(timeout: Option<Duration>) -> io::Result<__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) => {
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 {
311                 tv_sec: timeout.as_secs().try_into().unwrap_or(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]
get_socket_error(fd: BorrowedFd<'_>) -> io::Result<Result<(), io::Errno>>327 pub(crate) fn get_socket_error(fd: BorrowedFd<'_>) -> io::Result<Result<(), io::Errno>> {
328     let err: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_ERROR)?;
329     Ok(if err == 0 {
330         Ok(())
331     } else {
332         Err(io::Errno::from_raw_os_error(err))
333     })
334 }
335 
336 #[inline]
set_socket_keepalive(fd: BorrowedFd<'_>, keepalive: bool) -> io::Result<()>337 pub(crate) fn set_socket_keepalive(fd: BorrowedFd<'_>, keepalive: bool) -> io::Result<()> {
338     setsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE, from_bool(keepalive))
339 }
340 
341 #[inline]
get_socket_keepalive(fd: BorrowedFd<'_>) -> io::Result<bool>342 pub(crate) fn get_socket_keepalive(fd: BorrowedFd<'_>) -> io::Result<bool> {
343     getsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE).map(to_bool)
344 }
345 
346 #[inline]
set_socket_recv_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()>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, c::SOL_SOCKET, c::SO_RCVBUF, size)
350 }
351 
352 #[inline]
get_socket_recv_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize>353 pub(crate) fn get_socket_recv_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> {
354     getsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF).map(|size: u32| size as usize)
355 }
356 
357 #[inline]
set_socket_send_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()>358 pub(crate) fn set_socket_send_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> {
359     let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?;
360     setsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF, size)
361 }
362 
363 #[inline]
get_socket_send_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize>364 pub(crate) fn get_socket_send_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> {
365     getsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF).map(|size: u32| size as usize)
366 }
367 
368 #[inline]
get_socket_domain(fd: BorrowedFd<'_>) -> io::Result<AddressFamily>369 pub(crate) fn get_socket_domain(fd: BorrowedFd<'_>) -> io::Result<AddressFamily> {
370     let domain: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_DOMAIN)?;
371     Ok(AddressFamily(
372         domain.try_into().map_err(|_| io::Errno::OPNOTSUPP)?,
373     ))
374 }
375 
376 #[inline]
get_socket_acceptconn(fd: BorrowedFd<'_>) -> io::Result<bool>377 pub(crate) fn get_socket_acceptconn(fd: BorrowedFd<'_>) -> io::Result<bool> {
378     getsockopt(fd, c::SOL_SOCKET, c::SO_ACCEPTCONN).map(to_bool)
379 }
380 
381 #[inline]
set_socket_oobinline(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>382 pub(crate) fn set_socket_oobinline(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
383     setsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE, from_bool(value))
384 }
385 
386 #[inline]
get_socket_oobinline(fd: BorrowedFd<'_>) -> io::Result<bool>387 pub(crate) fn get_socket_oobinline(fd: BorrowedFd<'_>) -> io::Result<bool> {
388     getsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE).map(to_bool)
389 }
390 
391 #[inline]
set_socket_reuseport(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>392 pub(crate) fn set_socket_reuseport(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
393     setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT, from_bool(value))
394 }
395 
396 #[inline]
get_socket_reuseport(fd: BorrowedFd<'_>) -> io::Result<bool>397 pub(crate) fn get_socket_reuseport(fd: BorrowedFd<'_>) -> io::Result<bool> {
398     getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT).map(to_bool)
399 }
400 
401 #[inline]
get_socket_protocol(fd: BorrowedFd<'_>) -> io::Result<Option<Protocol>>402 pub(crate) fn get_socket_protocol(fd: BorrowedFd<'_>) -> io::Result<Option<Protocol>> {
403     getsockopt(fd, c::SOL_SOCKET, c::SO_PROTOCOL)
404         .map(|raw: u32| RawProtocol::new(raw).map(Protocol::from_raw))
405 }
406 
407 #[inline]
get_socket_cookie(fd: BorrowedFd<'_>) -> io::Result<u64>408 pub(crate) fn get_socket_cookie(fd: BorrowedFd<'_>) -> io::Result<u64> {
409     getsockopt(fd, c::SOL_SOCKET, c::SO_COOKIE)
410 }
411 
412 #[inline]
get_socket_incoming_cpu(fd: BorrowedFd<'_>) -> io::Result<u32>413 pub(crate) fn get_socket_incoming_cpu(fd: BorrowedFd<'_>) -> io::Result<u32> {
414     getsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU)
415 }
416 
417 #[inline]
set_socket_incoming_cpu(fd: BorrowedFd<'_>, value: u32) -> io::Result<()>418 pub(crate) fn set_socket_incoming_cpu(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
419     setsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU, value)
420 }
421 
422 #[inline]
set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()>423 pub(crate) fn set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()> {
424     setsockopt(fd, c::IPPROTO_IP, c::IP_TTL, ttl)
425 }
426 
427 #[inline]
get_ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32>428 pub(crate) fn get_ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
429     getsockopt(fd, c::IPPROTO_IP, c::IP_TTL)
430 }
431 
432 #[inline]
set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()>433 pub(crate) fn set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()> {
434     setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY, from_bool(only_v6))
435 }
436 
437 #[inline]
get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool>438 pub(crate) fn get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> {
439     getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY).map(to_bool)
440 }
441 
442 #[inline]
set_ip_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()>443 pub(crate) fn set_ip_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> {
444     setsockopt(
445         fd,
446         c::IPPROTO_IP,
447         c::IP_MULTICAST_LOOP,
448         from_bool(multicast_loop),
449     )
450 }
451 
452 #[inline]
get_ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool>453 pub(crate) fn get_ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
454     getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_LOOP).map(to_bool)
455 }
456 
457 #[inline]
set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()>458 pub(crate) fn set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()> {
459     setsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL, multicast_ttl)
460 }
461 
462 #[inline]
get_ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32>463 pub(crate) fn get_ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
464     getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL)
465 }
466 
467 #[inline]
set_ipv6_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()>468 pub(crate) fn set_ipv6_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> {
469     setsockopt(
470         fd,
471         c::IPPROTO_IPV6,
472         c::IPV6_MULTICAST_LOOP,
473         from_bool(multicast_loop),
474     )
475 }
476 
477 #[inline]
get_ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool>478 pub(crate) fn get_ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
479     getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP).map(to_bool)
480 }
481 
482 #[inline]
set_ipv6_multicast_hops(fd: BorrowedFd<'_>, multicast_hops: u32) -> io::Result<()>483 pub(crate) fn set_ipv6_multicast_hops(fd: BorrowedFd<'_>, multicast_hops: u32) -> io::Result<()> {
484     setsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS, multicast_hops)
485 }
486 
487 #[inline]
get_ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32>488 pub(crate) fn get_ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32> {
489     getsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS)
490 }
491 
492 #[inline]
set_ip_add_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, interface: &Ipv4Addr, ) -> io::Result<()>493 pub(crate) fn set_ip_add_membership(
494     fd: BorrowedFd<'_>,
495     multiaddr: &Ipv4Addr,
496     interface: &Ipv4Addr,
497 ) -> io::Result<()> {
498     let mreq = to_ip_mreq(multiaddr, interface);
499     setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq)
500 }
501 
502 #[inline]
set_ip_add_membership_with_ifindex( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32, ) -> io::Result<()>503 pub(crate) fn set_ip_add_membership_with_ifindex(
504     fd: BorrowedFd<'_>,
505     multiaddr: &Ipv4Addr,
506     address: &Ipv4Addr,
507     ifindex: i32,
508 ) -> io::Result<()> {
509     let mreqn = to_ip_mreqn(multiaddr, address, ifindex);
510     setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreqn)
511 }
512 
513 #[inline]
set_ip_add_source_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, interface: &Ipv4Addr, sourceaddr: &Ipv4Addr, ) -> io::Result<()>514 pub(crate) fn set_ip_add_source_membership(
515     fd: BorrowedFd<'_>,
516     multiaddr: &Ipv4Addr,
517     interface: &Ipv4Addr,
518     sourceaddr: &Ipv4Addr,
519 ) -> io::Result<()> {
520     let mreq_source = to_imr_source(multiaddr, interface, sourceaddr);
521     setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_SOURCE_MEMBERSHIP, mreq_source)
522 }
523 
524 #[inline]
set_ip_drop_source_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, interface: &Ipv4Addr, sourceaddr: &Ipv4Addr, ) -> io::Result<()>525 pub(crate) fn set_ip_drop_source_membership(
526     fd: BorrowedFd<'_>,
527     multiaddr: &Ipv4Addr,
528     interface: &Ipv4Addr,
529     sourceaddr: &Ipv4Addr,
530 ) -> io::Result<()> {
531     let mreq_source = to_imr_source(multiaddr, interface, sourceaddr);
532     setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_SOURCE_MEMBERSHIP, mreq_source)
533 }
534 
535 #[inline]
set_ipv6_add_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv6Addr, interface: u32, ) -> io::Result<()>536 pub(crate) fn set_ipv6_add_membership(
537     fd: BorrowedFd<'_>,
538     multiaddr: &Ipv6Addr,
539     interface: u32,
540 ) -> io::Result<()> {
541     let mreq = to_ipv6mr(multiaddr, interface);
542     setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_ADD_MEMBERSHIP, mreq)
543 }
544 
545 #[inline]
set_ip_drop_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, interface: &Ipv4Addr, ) -> io::Result<()>546 pub(crate) fn set_ip_drop_membership(
547     fd: BorrowedFd<'_>,
548     multiaddr: &Ipv4Addr,
549     interface: &Ipv4Addr,
550 ) -> io::Result<()> {
551     let mreq = to_ip_mreq(multiaddr, interface);
552     setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq)
553 }
554 
555 #[inline]
set_ip_drop_membership_with_ifindex( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32, ) -> io::Result<()>556 pub(crate) fn set_ip_drop_membership_with_ifindex(
557     fd: BorrowedFd<'_>,
558     multiaddr: &Ipv4Addr,
559     address: &Ipv4Addr,
560     ifindex: i32,
561 ) -> io::Result<()> {
562     let mreqn = to_ip_mreqn(multiaddr, address, ifindex);
563     setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreqn)
564 }
565 
566 #[inline]
set_ipv6_drop_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv6Addr, interface: u32, ) -> io::Result<()>567 pub(crate) fn set_ipv6_drop_membership(
568     fd: BorrowedFd<'_>,
569     multiaddr: &Ipv6Addr,
570     interface: u32,
571 ) -> io::Result<()> {
572     let mreq = to_ipv6mr(multiaddr, interface);
573     setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_DROP_MEMBERSHIP, mreq)
574 }
575 
576 #[inline]
get_ipv6_unicast_hops(fd: BorrowedFd<'_>) -> io::Result<u8>577 pub(crate) fn get_ipv6_unicast_hops(fd: BorrowedFd<'_>) -> io::Result<u8> {
578     getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS).map(|hops: c::c_int| hops as u8)
579 }
580 
581 #[inline]
set_ipv6_unicast_hops(fd: BorrowedFd<'_>, hops: Option<u8>) -> io::Result<()>582 pub(crate) fn set_ipv6_unicast_hops(fd: BorrowedFd<'_>, hops: Option<u8>) -> io::Result<()> {
583     let hops = match hops {
584         Some(hops) => hops.into(),
585         None => -1,
586     };
587     setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS, hops)
588 }
589 
590 #[inline]
set_ip_tos(fd: BorrowedFd<'_>, value: u8) -> io::Result<()>591 pub(crate) fn set_ip_tos(fd: BorrowedFd<'_>, value: u8) -> io::Result<()> {
592     setsockopt(fd, c::IPPROTO_IP, c::IP_TOS, i32::from(value))
593 }
594 
595 #[inline]
get_ip_tos(fd: BorrowedFd<'_>) -> io::Result<u8>596 pub(crate) fn get_ip_tos(fd: BorrowedFd<'_>) -> io::Result<u8> {
597     let value: i32 = getsockopt(fd, c::IPPROTO_IP, c::IP_TOS)?;
598     Ok(value as u8)
599 }
600 
601 #[inline]
set_ip_recvtos(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>602 pub(crate) fn set_ip_recvtos(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
603     setsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS, from_bool(value))
604 }
605 
606 #[inline]
get_ip_recvtos(fd: BorrowedFd<'_>) -> io::Result<bool>607 pub(crate) fn get_ip_recvtos(fd: BorrowedFd<'_>) -> io::Result<bool> {
608     getsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS).map(to_bool)
609 }
610 
611 #[inline]
set_ipv6_recvtclass(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>612 pub(crate) fn set_ipv6_recvtclass(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
613     setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS, from_bool(value))
614 }
615 
616 #[inline]
get_ipv6_recvtclass(fd: BorrowedFd<'_>) -> io::Result<bool>617 pub(crate) fn get_ipv6_recvtclass(fd: BorrowedFd<'_>) -> io::Result<bool> {
618     getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS).map(to_bool)
619 }
620 
621 #[inline]
set_ip_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>622 pub(crate) fn set_ip_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
623     setsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND, from_bool(value))
624 }
625 
626 #[inline]
get_ip_freebind(fd: BorrowedFd<'_>) -> io::Result<bool>627 pub(crate) fn get_ip_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> {
628     getsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND).map(to_bool)
629 }
630 
631 #[inline]
set_ipv6_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>632 pub(crate) fn set_ipv6_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
633     setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND, from_bool(value))
634 }
635 
636 #[inline]
get_ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result<bool>637 pub(crate) fn get_ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> {
638     getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND).map(to_bool)
639 }
640 
641 #[inline]
get_ip_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV4>642 pub(crate) fn get_ip_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV4> {
643     let level = c::IPPROTO_IP;
644     let optname = c::SO_ORIGINAL_DST;
645     let mut value = MaybeUninit::<SocketAddrStorage>::uninit();
646     let mut optlen = core::mem::size_of_val(&value).try_into().unwrap();
647 
648     getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
649 
650     let any = unsafe { SocketAddrAny::read(value.as_ptr(), optlen as usize)? };
651     match any {
652         SocketAddrAny::V4(v4) => Ok(v4),
653         _ => unreachable!(),
654     }
655 }
656 
657 #[inline]
get_ipv6_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV6>658 pub(crate) fn get_ipv6_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV6> {
659     let level = c::IPPROTO_IPV6;
660     let optname = c::IP6T_SO_ORIGINAL_DST;
661     let mut value = MaybeUninit::<SocketAddrStorage>::uninit();
662     let mut optlen = core::mem::size_of_val(&value).try_into().unwrap();
663 
664     getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
665 
666     let any = unsafe { SocketAddrAny::read(value.as_ptr(), optlen as usize)? };
667     match any {
668         SocketAddrAny::V6(v6) => Ok(v6),
669         _ => unreachable!(),
670     }
671 }
672 
673 #[inline]
set_ipv6_tclass(fd: BorrowedFd<'_>, value: u32) -> io::Result<()>674 pub(crate) fn set_ipv6_tclass(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
675     setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS, value)
676 }
677 
678 #[inline]
get_ipv6_tclass(fd: BorrowedFd<'_>) -> io::Result<u32>679 pub(crate) fn get_ipv6_tclass(fd: BorrowedFd<'_>) -> io::Result<u32> {
680     getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS)
681 }
682 
683 #[inline]
set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()>684 pub(crate) fn set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()> {
685     setsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY, from_bool(nodelay))
686 }
687 
688 #[inline]
get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool>689 pub(crate) fn get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool> {
690     getsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY).map(to_bool)
691 }
692 
693 #[inline]
set_tcp_keepcnt(fd: BorrowedFd<'_>, count: u32) -> io::Result<()>694 pub(crate) fn set_tcp_keepcnt(fd: BorrowedFd<'_>, count: u32) -> io::Result<()> {
695     setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT, count)
696 }
697 
698 #[inline]
get_tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result<u32>699 pub(crate) fn get_tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result<u32> {
700     getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT)
701 }
702 
703 #[inline]
set_tcp_keepidle(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()>704 pub(crate) fn set_tcp_keepidle(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> {
705     let secs: c::c_uint = duration_to_secs(duration)?;
706     setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPIDLE, secs)
707 }
708 
709 #[inline]
get_tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result<Duration>710 pub(crate) fn get_tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result<Duration> {
711     let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPIDLE)?;
712     Ok(Duration::from_secs(secs as u64))
713 }
714 
715 #[inline]
set_tcp_keepintvl(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()>716 pub(crate) fn set_tcp_keepintvl(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> {
717     let secs: c::c_uint = duration_to_secs(duration)?;
718     setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL, secs)
719 }
720 
721 #[inline]
get_tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result<Duration>722 pub(crate) fn get_tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result<Duration> {
723     let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL)?;
724     Ok(Duration::from_secs(secs as u64))
725 }
726 
727 #[inline]
set_tcp_user_timeout(fd: BorrowedFd<'_>, value: u32) -> io::Result<()>728 pub(crate) fn set_tcp_user_timeout(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
729     setsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT, value)
730 }
731 
732 #[inline]
get_tcp_user_timeout(fd: BorrowedFd<'_>) -> io::Result<u32>733 pub(crate) fn get_tcp_user_timeout(fd: BorrowedFd<'_>) -> io::Result<u32> {
734     getsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT)
735 }
736 
737 #[inline]
set_tcp_quickack(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>738 pub(crate) fn set_tcp_quickack(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
739     setsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK, from_bool(value))
740 }
741 
742 #[inline]
get_tcp_quickack(fd: BorrowedFd<'_>) -> io::Result<bool>743 pub(crate) fn get_tcp_quickack(fd: BorrowedFd<'_>) -> io::Result<bool> {
744     getsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK).map(to_bool)
745 }
746 
747 #[inline]
set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result<()>748 pub(crate) fn set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result<()> {
749     let level = c::IPPROTO_TCP;
750     let optname = c::TCP_CONGESTION;
751     let optlen = value.len().try_into().unwrap();
752     setsockopt_raw(fd, level, optname, value.as_ptr(), optlen)
753 }
754 
755 #[cfg(feature = "alloc")]
756 #[inline]
get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result<String>757 pub(crate) fn get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result<String> {
758     let level = c::IPPROTO_TCP;
759     let optname = c::TCP_CONGESTION;
760     const OPTLEN: c::socklen_t = 16;
761     let mut value = MaybeUninit::<[MaybeUninit<u8>; OPTLEN as usize]>::uninit();
762     let mut optlen = OPTLEN;
763     getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
764     unsafe {
765         let value = value.assume_init();
766         let slice: &[u8] = core::mem::transmute(&value[..optlen as usize]);
767         assert!(slice.iter().any(|b| *b == b'\0'));
768         Ok(
769             core::str::from_utf8(CStr::from_ptr(slice.as_ptr().cast()).to_bytes())
770                 .unwrap()
771                 .to_owned(),
772         )
773     }
774 }
775 
776 #[inline]
set_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>777 pub(crate) fn set_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
778     setsockopt(
779         fd,
780         c::IPPROTO_TCP,
781         c::TCP_THIN_LINEAR_TIMEOUTS,
782         from_bool(value),
783     )
784 }
785 
786 #[inline]
get_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>) -> io::Result<bool>787 pub(crate) fn get_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>) -> io::Result<bool> {
788     getsockopt(fd, c::IPPROTO_TCP, c::TCP_THIN_LINEAR_TIMEOUTS).map(to_bool)
789 }
790 
791 #[inline]
set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>792 pub(crate) fn set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
793     setsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK, from_bool(value))
794 }
795 
796 #[inline]
get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool>797 pub(crate) fn get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool> {
798     getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool)
799 }
800 
801 #[inline]
get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred>802 pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> {
803     getsockopt(fd, c::SOL_SOCKET, linux_raw_sys::net::SO_PEERCRED)
804 }
805 
806 #[cfg(target_os = "linux")]
807 #[inline]
set_xdp_umem_reg(fd: BorrowedFd<'_>, value: XdpUmemReg) -> io::Result<()>808 pub(crate) fn set_xdp_umem_reg(fd: BorrowedFd<'_>, value: XdpUmemReg) -> io::Result<()> {
809     setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_REG, value)
810 }
811 
812 #[cfg(target_os = "linux")]
813 #[inline]
set_xdp_umem_fill_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()>814 pub(crate) fn set_xdp_umem_fill_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
815     setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_FILL_RING, value)
816 }
817 
818 #[cfg(target_os = "linux")]
819 #[inline]
set_xdp_umem_completion_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()>820 pub(crate) fn set_xdp_umem_completion_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
821     setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_COMPLETION_RING, value)
822 }
823 
824 #[cfg(target_os = "linux")]
825 #[inline]
set_xdp_tx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()>826 pub(crate) fn set_xdp_tx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
827     setsockopt(fd, c::SOL_XDP, c::XDP_TX_RING, value)
828 }
829 
830 #[cfg(target_os = "linux")]
831 #[inline]
set_xdp_rx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()>832 pub(crate) fn set_xdp_rx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
833     setsockopt(fd, c::SOL_XDP, c::XDP_RX_RING, value)
834 }
835 
836 #[cfg(target_os = "linux")]
837 #[inline]
get_xdp_mmap_offsets(fd: BorrowedFd<'_>) -> io::Result<XdpMmapOffsets>838 pub(crate) fn get_xdp_mmap_offsets(fd: BorrowedFd<'_>) -> io::Result<XdpMmapOffsets> {
839     // The kernel will write `xdp_mmap_offsets` or `xdp_mmap_offsets_v1` to the supplied pointer,
840     // depending on the kernel version. Both structs only contain u64 values.
841     // By using the larger of both as the parameter, we can shuffle the values to the non-v1 version
842     // returned by `get_xdp_mmap_offsets` while keeping the return type unaffected by the kernel
843     // version. This works because C will layout all struct members one after the other.
844 
845     let mut optlen = core::mem::size_of::<xdp_mmap_offsets>().try_into().unwrap();
846     debug_assert!(
847         optlen as usize >= core::mem::size_of::<c::c_int>(),
848         "Socket APIs don't ever use `bool` directly"
849     );
850     let mut value = MaybeUninit::<xdp_mmap_offsets>::zeroed();
851     getsockopt_raw(fd, c::SOL_XDP, c::XDP_MMAP_OFFSETS, &mut value, &mut optlen)?;
852 
853     if optlen as usize == core::mem::size_of::<c::xdp_mmap_offsets_v1>() {
854         // Safety: All members of xdp_mmap_offsets are u64 and thus are correctly initialized
855         // by `MaybeUninit::<xdp_statistics>::zeroed()`
856         let xpd_mmap_offsets = unsafe { value.assume_init() };
857         Ok(XdpMmapOffsets {
858             rx: XdpRingOffset {
859                 producer: xpd_mmap_offsets.rx.producer,
860                 consumer: xpd_mmap_offsets.rx.consumer,
861                 desc: xpd_mmap_offsets.rx.desc,
862                 flags: None,
863             },
864             tx: XdpRingOffset {
865                 producer: xpd_mmap_offsets.rx.flags,
866                 consumer: xpd_mmap_offsets.tx.producer,
867                 desc: xpd_mmap_offsets.tx.consumer,
868                 flags: None,
869             },
870             fr: XdpRingOffset {
871                 producer: xpd_mmap_offsets.tx.desc,
872                 consumer: xpd_mmap_offsets.tx.flags,
873                 desc: xpd_mmap_offsets.fr.producer,
874                 flags: None,
875             },
876             cr: XdpRingOffset {
877                 producer: xpd_mmap_offsets.fr.consumer,
878                 consumer: xpd_mmap_offsets.fr.desc,
879                 desc: xpd_mmap_offsets.fr.flags,
880                 flags: None,
881             },
882         })
883     } else {
884         assert_eq!(
885             optlen as usize,
886             core::mem::size_of::<xdp_mmap_offsets>(),
887             "unexpected getsockopt size"
888         );
889         // Safety: All members of xdp_mmap_offsets are u64 and thus are correctly initialized
890         // by `MaybeUninit::<xdp_statistics>::zeroed()`
891         let xpd_mmap_offsets = unsafe { value.assume_init() };
892         Ok(XdpMmapOffsets {
893             rx: XdpRingOffset {
894                 producer: xpd_mmap_offsets.rx.producer,
895                 consumer: xpd_mmap_offsets.rx.consumer,
896                 desc: xpd_mmap_offsets.rx.desc,
897                 flags: Some(xpd_mmap_offsets.rx.flags),
898             },
899             tx: XdpRingOffset {
900                 producer: xpd_mmap_offsets.tx.producer,
901                 consumer: xpd_mmap_offsets.tx.consumer,
902                 desc: xpd_mmap_offsets.tx.desc,
903                 flags: Some(xpd_mmap_offsets.tx.flags),
904             },
905             fr: XdpRingOffset {
906                 producer: xpd_mmap_offsets.fr.producer,
907                 consumer: xpd_mmap_offsets.fr.consumer,
908                 desc: xpd_mmap_offsets.fr.desc,
909                 flags: Some(xpd_mmap_offsets.fr.flags),
910             },
911             cr: XdpRingOffset {
912                 producer: xpd_mmap_offsets.cr.producer,
913                 consumer: xpd_mmap_offsets.cr.consumer,
914                 desc: xpd_mmap_offsets.cr.desc,
915                 flags: Some(xpd_mmap_offsets.cr.flags),
916             },
917         })
918     }
919 }
920 
921 #[cfg(target_os = "linux")]
922 #[inline]
get_xdp_statistics(fd: BorrowedFd<'_>) -> io::Result<XdpStatistics>923 pub(crate) fn get_xdp_statistics(fd: BorrowedFd<'_>) -> io::Result<XdpStatistics> {
924     let mut optlen = core::mem::size_of::<xdp_statistics>().try_into().unwrap();
925     debug_assert!(
926         optlen as usize >= core::mem::size_of::<c::c_int>(),
927         "Socket APIs don't ever use `bool` directly"
928     );
929     let mut value = MaybeUninit::<xdp_statistics>::zeroed();
930     getsockopt_raw(fd, c::SOL_XDP, c::XDP_STATISTICS, &mut value, &mut optlen)?;
931 
932     if optlen as usize == core::mem::size_of::<xdp_statistics_v1>() {
933         // Safety: All members of xdp_statistics are u64 and thus are correctly initialized
934         // by `MaybeUninit::<xdp_statistics>::zeroed()`
935         let xdp_statistics = unsafe { value.assume_init() };
936         Ok(XdpStatistics {
937             rx_dropped: xdp_statistics.rx_dropped,
938             rx_invalid_descs: xdp_statistics.rx_dropped,
939             tx_invalid_descs: xdp_statistics.rx_dropped,
940             rx_ring_full: None,
941             rx_fill_ring_empty_descs: None,
942             tx_ring_empty_descs: None,
943         })
944     } else {
945         assert_eq!(
946             optlen as usize,
947             core::mem::size_of::<xdp_statistics>(),
948             "unexpected getsockopt size"
949         );
950         // Safety: All members of xdp_statistics are u64 and thus are correctly initialized
951         // by `MaybeUninit::<xdp_statistics>::zeroed()`
952         let xdp_statistics = unsafe { value.assume_init() };
953         Ok(XdpStatistics {
954             rx_dropped: xdp_statistics.rx_dropped,
955             rx_invalid_descs: xdp_statistics.rx_invalid_descs,
956             tx_invalid_descs: xdp_statistics.tx_invalid_descs,
957             rx_ring_full: Some(xdp_statistics.rx_ring_full),
958             rx_fill_ring_empty_descs: Some(xdp_statistics.rx_fill_ring_empty_descs),
959             tx_ring_empty_descs: Some(xdp_statistics.tx_ring_empty_descs),
960         })
961     }
962 }
963 
964 #[cfg(target_os = "linux")]
965 #[inline]
get_xdp_options(fd: BorrowedFd<'_>) -> io::Result<XdpOptionsFlags>966 pub(crate) fn get_xdp_options(fd: BorrowedFd<'_>) -> io::Result<XdpOptionsFlags> {
967     getsockopt(fd, c::SOL_XDP, c::XDP_OPTIONS)
968 }
969 
970 #[inline]
to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq971 fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
972     c::ip_mreq {
973         imr_multiaddr: to_imr_addr(multiaddr),
974         imr_interface: to_imr_addr(interface),
975     }
976 }
977 
978 #[inline]
to_ip_mreqn(multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32) -> c::ip_mreqn979 fn to_ip_mreqn(multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32) -> c::ip_mreqn {
980     c::ip_mreqn {
981         imr_multiaddr: to_imr_addr(multiaddr),
982         imr_address: to_imr_addr(address),
983         imr_ifindex: ifindex,
984     }
985 }
986 
987 #[inline]
to_imr_source( multiaddr: &Ipv4Addr, interface: &Ipv4Addr, sourceaddr: &Ipv4Addr, ) -> c::ip_mreq_source988 fn to_imr_source(
989     multiaddr: &Ipv4Addr,
990     interface: &Ipv4Addr,
991     sourceaddr: &Ipv4Addr,
992 ) -> c::ip_mreq_source {
993     c::ip_mreq_source {
994         imr_multiaddr: to_imr_addr(multiaddr).s_addr,
995         imr_interface: to_imr_addr(interface).s_addr,
996         imr_sourceaddr: to_imr_addr(sourceaddr).s_addr,
997     }
998 }
999 
1000 #[inline]
to_imr_addr(addr: &Ipv4Addr) -> c::in_addr1001 fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr {
1002     c::in_addr {
1003         s_addr: u32::from_ne_bytes(addr.octets()),
1004     }
1005 }
1006 
1007 #[inline]
to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq1008 fn to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq {
1009     c::ipv6_mreq {
1010         ipv6mr_multiaddr: to_ipv6mr_multiaddr(multiaddr),
1011         ipv6mr_ifindex: to_ipv6mr_interface(interface),
1012     }
1013 }
1014 
1015 #[inline]
to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr1016 fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr {
1017     c::in6_addr {
1018         in6_u: linux_raw_sys::net::in6_addr__bindgen_ty_1 {
1019             u6_addr8: multiaddr.octets(),
1020         },
1021     }
1022 }
1023 
1024 #[inline]
to_ipv6mr_interface(interface: u32) -> c::c_int1025 fn to_ipv6mr_interface(interface: u32) -> c::c_int {
1026     interface as c::c_int
1027 }
1028 
1029 #[inline]
from_bool(value: bool) -> c::c_uint1030 fn from_bool(value: bool) -> c::c_uint {
1031     c::c_uint::from(value)
1032 }
1033 
1034 #[inline]
to_bool(value: c::c_uint) -> bool1035 fn to_bool(value: c::c_uint) -> bool {
1036     value != 0
1037 }
1038 
1039 /// Convert to seconds, rounding up if necessary.
1040 #[inline]
duration_to_secs<T: TryFrom<u64>>(duration: Duration) -> io::Result<T>1041 fn duration_to_secs<T: TryFrom<u64>>(duration: Duration) -> io::Result<T> {
1042     let mut secs = duration.as_secs();
1043     if duration.subsec_nanos() != 0 {
1044         secs = secs.checked_add(1).ok_or(io::Errno::INVAL)?;
1045     }
1046     T::try_from(secs).map_err(|_e| io::Errno::INVAL)
1047 }
1048