Skip to main content

rustix/backend/linux_raw/net/
sockopt.rs

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