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            // `subsec_micros` rounds down, so we use `subsec_nanos` and
289            // manually round up.
290            let mut timeout = __kernel_sock_timeval {
291                tv_sec: timeout.as_secs().try_into().unwrap_or(i64::MAX),
292                tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _,
293            };
294            if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
295                timeout.tv_usec = 1;
296            }
297            timeout
298        }
299        None => __kernel_sock_timeval {
300            tv_sec: 0,
301            tv_usec: 0,
302        },
303    })
304}
305
306/// Like `duration_to_linux_sock_timeval` but uses Linux's old 32-bit
307/// `__kernel_old_timeval`.
308fn duration_to_linux_old_timeval(timeout: Option<Duration>) -> io::Result<__kernel_old_timeval> {
309    Ok(match timeout {
310        Some(timeout) => {
311            if timeout == Duration::ZERO {
312                return Err(io::Errno::INVAL);
313            }
314
315            // `subsec_micros` rounds down, so we use `subsec_nanos` and
316            // manually round up.
317            let mut timeout = __kernel_old_timeval {
318                tv_sec: timeout.as_secs().try_into().unwrap_or(c::c_long::MAX),
319                tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _,
320            };
321            if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
322                timeout.tv_usec = 1;
323            }
324            timeout
325        }
326        None => __kernel_old_timeval {
327            tv_sec: 0,
328            tv_usec: 0,
329        },
330    })
331}
332
333#[inline]
334pub(crate) fn socket_error(fd: BorrowedFd<'_>) -> io::Result<Result<(), io::Errno>> {
335    let err: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_ERROR)?;
336    Ok(if err == 0 {
337        Ok(())
338    } else {
339        Err(io::Errno::from_raw_os_error(err))
340    })
341}
342
343#[inline]
344pub(crate) fn set_socket_keepalive(fd: BorrowedFd<'_>, keepalive: bool) -> io::Result<()> {
345    setsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE, from_bool(keepalive))
346}
347
348#[inline]
349pub(crate) fn socket_keepalive(fd: BorrowedFd<'_>) -> io::Result<bool> {
350    getsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE).map(to_bool)
351}
352
353#[inline]
354pub(crate) fn set_socket_recv_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> {
355    let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?;
356    setsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF, size)
357}
358
359#[inline]
360pub(crate) fn set_socket_recv_buffer_size_force(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> {
361    let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?;
362    setsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUFFORCE, size)
363}
364
365#[inline]
366pub(crate) fn socket_recv_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> {
367    getsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF).map(|size: u32| size as usize)
368}
369
370#[inline]
371pub(crate) fn set_socket_send_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> {
372    let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?;
373    setsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF, size)
374}
375
376#[inline]
377pub(crate) fn set_socket_send_buffer_size_force(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> {
378    let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?;
379    setsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUFFORCE, size)
380}
381
382#[inline]
383pub(crate) fn socket_send_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> {
384    getsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF).map(|size: u32| size as usize)
385}
386
387#[inline]
388pub(crate) fn socket_domain(fd: BorrowedFd<'_>) -> io::Result<AddressFamily> {
389    let domain: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_DOMAIN)?;
390    Ok(AddressFamily(
391        domain.try_into().map_err(|_| io::Errno::OPNOTSUPP)?,
392    ))
393}
394
395#[inline]
396pub(crate) fn socket_acceptconn(fd: BorrowedFd<'_>) -> io::Result<bool> {
397    getsockopt(fd, c::SOL_SOCKET, c::SO_ACCEPTCONN).map(to_bool)
398}
399
400#[inline]
401pub(crate) fn set_socket_oobinline(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
402    setsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE, from_bool(value))
403}
404
405#[inline]
406pub(crate) fn socket_oobinline(fd: BorrowedFd<'_>) -> io::Result<bool> {
407    getsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE).map(to_bool)
408}
409
410#[inline]
411pub(crate) fn set_socket_reuseport(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
412    setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT, from_bool(value))
413}
414
415#[inline]
416pub(crate) fn socket_reuseport(fd: BorrowedFd<'_>) -> io::Result<bool> {
417    getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT).map(to_bool)
418}
419
420#[inline]
421pub(crate) fn socket_protocol(fd: BorrowedFd<'_>) -> io::Result<Option<Protocol>> {
422    getsockopt(fd, c::SOL_SOCKET, c::SO_PROTOCOL)
423        .map(|raw: u32| RawProtocol::new(raw).map(Protocol::from_raw))
424}
425
426#[inline]
427pub(crate) fn socket_cookie(fd: BorrowedFd<'_>) -> io::Result<u64> {
428    getsockopt(fd, c::SOL_SOCKET, c::SO_COOKIE)
429}
430
431#[inline]
432pub(crate) fn socket_incoming_cpu(fd: BorrowedFd<'_>) -> io::Result<u32> {
433    getsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU)
434}
435
436#[inline]
437pub(crate) fn set_socket_incoming_cpu(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
438    setsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU, value)
439}
440
441#[inline]
442pub(crate) fn set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()> {
443    setsockopt(fd, c::IPPROTO_IP, c::IP_TTL, ttl)
444}
445
446#[inline]
447pub(crate) fn ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
448    getsockopt(fd, c::IPPROTO_IP, c::IP_TTL)
449}
450
451#[inline]
452pub(crate) fn set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()> {
453    setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY, from_bool(only_v6))
454}
455
456#[inline]
457pub(crate) fn ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> {
458    getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY).map(to_bool)
459}
460
461#[inline]
462pub(crate) fn ip_mtu(fd: BorrowedFd<'_>) -> io::Result<u32> {
463    getsockopt(fd, c::IPPROTO_IP, IP_MTU)
464}
465
466#[inline]
467pub(crate) fn ipv6_mtu(fd: BorrowedFd<'_>) -> io::Result<u32> {
468    getsockopt(fd, c::IPPROTO_IPV6, IPV6_MTU)
469}
470
471#[inline]
472pub(crate) fn set_ip_mtu_discover(
473    fd: BorrowedFd<'_>,
474    value: Ipv4PathMtuDiscovery,
475) -> io::Result<()> {
476    setsockopt(fd, c::IPPROTO_IP, IP_MTU_DISCOVER, value)
477}
478
479#[inline]
480pub(crate) fn ip_mtu_discover(fd: BorrowedFd<'_>) -> io::Result<Ipv4PathMtuDiscovery> {
481    getsockopt(fd, c::IPPROTO_IP, IP_MTU_DISCOVER)
482}
483
484#[inline]
485pub(crate) fn set_ipv6_mtu_discover(
486    fd: BorrowedFd<'_>,
487    value: Ipv6PathMtuDiscovery,
488) -> io::Result<()> {
489    setsockopt(fd, c::IPPROTO_IPV6, IPV6_MTU_DISCOVER, value)
490}
491
492#[inline]
493pub(crate) fn ipv6_mtu_discover(fd: BorrowedFd<'_>) -> io::Result<Ipv6PathMtuDiscovery> {
494    getsockopt(fd, c::IPPROTO_IPV6, IPV6_MTU_DISCOVER)
495}
496
497#[inline]
498pub(crate) fn set_ip_multicast_if_with_ifindex(
499    fd: BorrowedFd<'_>,
500    multiaddr: &Ipv4Addr,
501    address: &Ipv4Addr,
502    ifindex: u32,
503) -> io::Result<()> {
504    let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32);
505    setsockopt(fd, c::IPPROTO_IP, IP_MULTICAST_IF, mreqn)
506}
507
508#[inline]
509pub(crate) fn set_ip_multicast_if(fd: BorrowedFd<'_>, value: &Ipv4Addr) -> io::Result<()> {
510    setsockopt(fd, c::IPPROTO_IP, IP_MULTICAST_IF, to_imr_addr(value))
511}
512
513#[inline]
514pub(crate) fn ip_multicast_if(fd: BorrowedFd<'_>) -> io::Result<Ipv4Addr> {
515    getsockopt(fd, c::IPPROTO_IP, IP_MULTICAST_IF).map(from_in_addr)
516}
517
518#[inline]
519pub(crate) fn set_ipv6_multicast_if(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
520    setsockopt(fd, c::IPPROTO_IPV6, IPV6_MULTICAST_IF, value as c::c_int)
521}
522
523#[inline]
524pub(crate) fn ipv6_multicast_if(fd: BorrowedFd<'_>) -> io::Result<u32> {
525    getsockopt(fd, c::IPPROTO_IPV6, IPV6_MULTICAST_IF)
526}
527
528#[inline]
529pub(crate) fn set_ip_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> {
530    setsockopt(
531        fd,
532        c::IPPROTO_IP,
533        c::IP_MULTICAST_LOOP,
534        from_bool(multicast_loop),
535    )
536}
537
538#[inline]
539pub(crate) fn ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
540    getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_LOOP).map(to_bool)
541}
542
543#[inline]
544pub(crate) fn set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()> {
545    setsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL, multicast_ttl)
546}
547
548#[inline]
549pub(crate) fn ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
550    getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL)
551}
552
553#[inline]
554pub(crate) fn set_ipv6_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> {
555    setsockopt(
556        fd,
557        c::IPPROTO_IPV6,
558        c::IPV6_MULTICAST_LOOP,
559        from_bool(multicast_loop),
560    )
561}
562
563#[inline]
564pub(crate) fn ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
565    getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP).map(to_bool)
566}
567
568#[inline]
569pub(crate) fn set_ipv6_multicast_hops(fd: BorrowedFd<'_>, multicast_hops: u32) -> io::Result<()> {
570    setsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS, multicast_hops)
571}
572
573#[inline]
574pub(crate) fn ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32> {
575    getsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS)
576}
577
578#[inline]
579pub(crate) fn set_ip_add_membership(
580    fd: BorrowedFd<'_>,
581    multiaddr: &Ipv4Addr,
582    interface: &Ipv4Addr,
583) -> io::Result<()> {
584    let mreq = to_ip_mreq(multiaddr, interface);
585    setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq)
586}
587
588#[inline]
589pub(crate) fn set_ip_add_membership_with_ifindex(
590    fd: BorrowedFd<'_>,
591    multiaddr: &Ipv4Addr,
592    address: &Ipv4Addr,
593    ifindex: u32,
594) -> io::Result<()> {
595    let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32);
596    setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreqn)
597}
598
599#[inline]
600pub(crate) fn set_ip_add_source_membership(
601    fd: BorrowedFd<'_>,
602    multiaddr: &Ipv4Addr,
603    interface: &Ipv4Addr,
604    sourceaddr: &Ipv4Addr,
605) -> io::Result<()> {
606    let mreq_source = to_imr_source(multiaddr, interface, sourceaddr);
607    setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_SOURCE_MEMBERSHIP, mreq_source)
608}
609
610#[inline]
611pub(crate) fn set_ip_drop_source_membership(
612    fd: BorrowedFd<'_>,
613    multiaddr: &Ipv4Addr,
614    interface: &Ipv4Addr,
615    sourceaddr: &Ipv4Addr,
616) -> io::Result<()> {
617    let mreq_source = to_imr_source(multiaddr, interface, sourceaddr);
618    setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_SOURCE_MEMBERSHIP, mreq_source)
619}
620
621#[inline]
622pub(crate) fn set_ipv6_add_membership(
623    fd: BorrowedFd<'_>,
624    multiaddr: &Ipv6Addr,
625    interface: u32,
626) -> io::Result<()> {
627    let mreq = to_ipv6mr(multiaddr, interface);
628    setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_ADD_MEMBERSHIP, mreq)
629}
630
631#[inline]
632pub(crate) fn set_ip_drop_membership(
633    fd: BorrowedFd<'_>,
634    multiaddr: &Ipv4Addr,
635    interface: &Ipv4Addr,
636) -> io::Result<()> {
637    let mreq = to_ip_mreq(multiaddr, interface);
638    setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq)
639}
640
641#[inline]
642pub(crate) fn set_ip_drop_membership_with_ifindex(
643    fd: BorrowedFd<'_>,
644    multiaddr: &Ipv4Addr,
645    address: &Ipv4Addr,
646    ifindex: u32,
647) -> io::Result<()> {
648    let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32);
649    setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreqn)
650}
651
652#[inline]
653pub(crate) fn set_ipv6_drop_membership(
654    fd: BorrowedFd<'_>,
655    multiaddr: &Ipv6Addr,
656    interface: u32,
657) -> io::Result<()> {
658    let mreq = to_ipv6mr(multiaddr, interface);
659    setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_DROP_MEMBERSHIP, mreq)
660}
661
662#[inline]
663pub(crate) fn ipv6_unicast_hops(fd: BorrowedFd<'_>) -> io::Result<u8> {
664    getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS).map(|hops: c::c_int| hops as u8)
665}
666
667#[inline]
668pub(crate) fn set_ipv6_unicast_hops(fd: BorrowedFd<'_>, hops: Option<u8>) -> io::Result<()> {
669    let hops = match hops {
670        Some(hops) => hops.into(),
671        None => -1,
672    };
673    setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS, hops)
674}
675
676#[inline]
677pub(crate) fn set_ip_tos(fd: BorrowedFd<'_>, value: u8) -> io::Result<()> {
678    setsockopt(fd, c::IPPROTO_IP, c::IP_TOS, i32::from(value))
679}
680
681#[inline]
682pub(crate) fn ip_tos(fd: BorrowedFd<'_>) -> io::Result<u8> {
683    let value: i32 = getsockopt(fd, c::IPPROTO_IP, c::IP_TOS)?;
684    Ok(value as u8)
685}
686
687#[inline]
688pub(crate) fn set_ip_recvtos(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
689    setsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS, from_bool(value))
690}
691
692#[inline]
693pub(crate) fn ip_recvtos(fd: BorrowedFd<'_>) -> io::Result<bool> {
694    getsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS).map(to_bool)
695}
696
697#[inline]
698pub(crate) fn set_ipv6_recvtclass(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
699    setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS, from_bool(value))
700}
701
702#[inline]
703pub(crate) fn ipv6_recvtclass(fd: BorrowedFd<'_>) -> io::Result<bool> {
704    getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS).map(to_bool)
705}
706
707#[inline]
708pub(crate) fn set_ip_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
709    setsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND, from_bool(value))
710}
711
712#[inline]
713pub(crate) fn ip_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> {
714    getsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND).map(to_bool)
715}
716
717#[inline]
718pub(crate) fn set_ipv6_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
719    setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND, from_bool(value))
720}
721
722#[inline]
723pub(crate) fn ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> {
724    getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND).map(to_bool)
725}
726
727#[inline]
728pub(crate) fn ip_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV4> {
729    let level = c::IPPROTO_IP;
730    let optname = c::SO_ORIGINAL_DST;
731    let mut addr = SocketAddrBuf::new();
732
733    getsockopt_raw(fd, level, optname, &mut addr.storage, &mut addr.len)?;
734
735    Ok(unsafe { addr.into_any() }.try_into().unwrap())
736}
737
738#[inline]
739pub(crate) fn ipv6_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV6> {
740    let level = c::IPPROTO_IPV6;
741    let optname = c::IP6T_SO_ORIGINAL_DST;
742    let mut addr = SocketAddrBuf::new();
743
744    getsockopt_raw(fd, level, optname, &mut addr.storage, &mut addr.len)?;
745
746    Ok(unsafe { addr.into_any() }.try_into().unwrap())
747}
748
749#[inline]
750pub(crate) fn set_ipv6_tclass(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
751    setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS, value)
752}
753
754#[inline]
755pub(crate) fn ipv6_tclass(fd: BorrowedFd<'_>) -> io::Result<u32> {
756    getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS)
757}
758
759#[inline]
760pub(crate) fn set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()> {
761    setsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY, from_bool(nodelay))
762}
763
764#[inline]
765pub(crate) fn tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool> {
766    getsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY).map(to_bool)
767}
768
769#[inline]
770pub(crate) fn set_tcp_keepcnt(fd: BorrowedFd<'_>, count: u32) -> io::Result<()> {
771    setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT, count)
772}
773
774#[inline]
775pub(crate) fn tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result<u32> {
776    getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT)
777}
778
779#[inline]
780pub(crate) fn set_tcp_keepidle(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> {
781    let secs: c::c_uint = duration_to_secs(duration)?;
782    setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPIDLE, secs)
783}
784
785#[inline]
786pub(crate) fn tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result<Duration> {
787    let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPIDLE)?;
788    Ok(Duration::from_secs(secs.into()))
789}
790
791#[inline]
792pub(crate) fn set_tcp_keepintvl(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> {
793    let secs: c::c_uint = duration_to_secs(duration)?;
794    setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL, secs)
795}
796
797#[inline]
798pub(crate) fn tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result<Duration> {
799    let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL)?;
800    Ok(Duration::from_secs(secs.into()))
801}
802
803#[inline]
804pub(crate) fn set_tcp_user_timeout(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
805    setsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT, value)
806}
807
808#[inline]
809pub(crate) fn tcp_user_timeout(fd: BorrowedFd<'_>) -> io::Result<u32> {
810    getsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT)
811}
812
813#[inline]
814pub(crate) fn set_tcp_quickack(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
815    setsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK, from_bool(value))
816}
817
818#[inline]
819pub(crate) fn tcp_quickack(fd: BorrowedFd<'_>) -> io::Result<bool> {
820    getsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK).map(to_bool)
821}
822
823#[inline]
824pub(crate) fn set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result<()> {
825    let level = c::IPPROTO_TCP;
826    let optname = c::TCP_CONGESTION;
827    let optlen = value.len().try_into().unwrap();
828    setsockopt_raw(fd, level, optname, value.as_ptr(), optlen)
829}
830
831#[cfg(feature = "alloc")]
832#[inline]
833pub(crate) fn tcp_congestion(fd: BorrowedFd<'_>) -> io::Result<String> {
834    const OPTLEN: c::socklen_t = 16;
835
836    let level = c::IPPROTO_TCP;
837    let optname = c::TCP_CONGESTION;
838    let mut value = MaybeUninit::<[MaybeUninit<u8>; OPTLEN as usize]>::uninit();
839    let mut optlen = OPTLEN;
840    getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
841    unsafe {
842        let value = value.assume_init();
843        let slice: &[u8] = core::mem::transmute(&value[..optlen as usize]);
844        assert!(slice.contains(&b'\0'));
845        Ok(
846            core::str::from_utf8(CStr::from_ptr(slice.as_ptr().cast()).to_bytes())
847                .unwrap()
848                .to_owned(),
849        )
850    }
851}
852
853#[inline]
854pub(crate) fn set_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
855    setsockopt(
856        fd,
857        c::IPPROTO_TCP,
858        c::TCP_THIN_LINEAR_TIMEOUTS,
859        from_bool(value),
860    )
861}
862
863#[inline]
864pub(crate) fn tcp_thin_linear_timeouts(fd: BorrowedFd<'_>) -> io::Result<bool> {
865    getsockopt(fd, c::IPPROTO_TCP, c::TCP_THIN_LINEAR_TIMEOUTS).map(to_bool)
866}
867
868#[inline]
869pub(crate) fn set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
870    setsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK, from_bool(value))
871}
872
873#[inline]
874pub(crate) fn tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool> {
875    getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool)
876}
877
878#[inline]
879pub(crate) fn socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> {
880    getsockopt(fd, c::SOL_SOCKET, linux_raw_sys::net::SO_PEERCRED)
881}
882
883#[cfg(all(target_os = "linux", feature = "time"))]
884#[inline]
885pub(crate) fn set_txtime(
886    fd: BorrowedFd<'_>,
887    clockid: ClockId,
888    flags: TxTimeFlags,
889) -> io::Result<()> {
890    setsockopt(
891        fd,
892        c::SOL_SOCKET,
893        c::SO_TXTIME,
894        c::sock_txtime {
895            clockid: clockid as _,
896            flags: flags.bits(),
897        },
898    )
899}
900
901#[cfg(all(target_os = "linux", feature = "time"))]
902#[inline]
903pub(crate) fn get_txtime(fd: BorrowedFd<'_>) -> io::Result<(ClockId, TxTimeFlags)> {
904    let txtime: c::sock_txtime = getsockopt(fd, c::SOL_SOCKET, c::SO_TXTIME)?;
905
906    Ok((
907        txtime.clockid.try_into().map_err(|_| io::Errno::RANGE)?,
908        TxTimeFlags::from_bits(txtime.flags).ok_or(io::Errno::RANGE)?,
909    ))
910}
911
912#[cfg(target_os = "linux")]
913#[inline]
914pub(crate) fn set_xdp_umem_reg(fd: BorrowedFd<'_>, value: XdpUmemReg) -> io::Result<()> {
915    setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_REG, value)
916}
917
918#[cfg(target_os = "linux")]
919#[inline]
920pub(crate) fn set_xdp_umem_fill_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
921    setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_FILL_RING, value)
922}
923
924#[cfg(target_os = "linux")]
925#[inline]
926pub(crate) fn set_xdp_umem_completion_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
927    setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_COMPLETION_RING, value)
928}
929
930#[cfg(target_os = "linux")]
931#[inline]
932pub(crate) fn set_xdp_tx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
933    setsockopt(fd, c::SOL_XDP, c::XDP_TX_RING, value)
934}
935
936#[cfg(target_os = "linux")]
937#[inline]
938pub(crate) fn set_xdp_rx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
939    setsockopt(fd, c::SOL_XDP, c::XDP_RX_RING, value)
940}
941
942#[cfg(target_os = "linux")]
943#[inline]
944pub(crate) fn xdp_mmap_offsets(fd: BorrowedFd<'_>) -> io::Result<XdpMmapOffsets> {
945    // The kernel will write `xdp_mmap_offsets` or `xdp_mmap_offsets_v1` to the
946    // supplied pointer, depending on the kernel version. Both structs only
947    // contain u64 values. By using the larger of both as the parameter, we can
948    // shuffle the values to the non-v1 version returned by
949    // `get_xdp_mmap_offsets` while keeping the return type unaffected by the
950    // kernel version. This works because C will layout all struct members one
951    // after the other.
952
953    let mut optlen = size_of::<xdp_mmap_offsets>().try_into().unwrap();
954    debug_assert!(
955        optlen as usize >= size_of::<c::c_int>(),
956        "Socket APIs don't ever use `bool` directly"
957    );
958    let mut value = MaybeUninit::<xdp_mmap_offsets>::zeroed();
959    getsockopt_raw(fd, c::SOL_XDP, c::XDP_MMAP_OFFSETS, &mut value, &mut optlen)?;
960
961    if optlen as usize == size_of::<c::xdp_mmap_offsets_v1>() {
962        // SAFETY: All members of xdp_mmap_offsets are `u64` and thus are
963        // correctly initialized by `MaybeUninit::<xdp_statistics>::zeroed()`.
964        let xpd_mmap_offsets = unsafe { value.assume_init() };
965        Ok(XdpMmapOffsets {
966            rx: XdpRingOffset {
967                producer: xpd_mmap_offsets.rx.producer,
968                consumer: xpd_mmap_offsets.rx.consumer,
969                desc: xpd_mmap_offsets.rx.desc,
970                flags: None,
971            },
972            tx: XdpRingOffset {
973                producer: xpd_mmap_offsets.rx.flags,
974                consumer: xpd_mmap_offsets.tx.producer,
975                desc: xpd_mmap_offsets.tx.consumer,
976                flags: None,
977            },
978            fr: XdpRingOffset {
979                producer: xpd_mmap_offsets.tx.desc,
980                consumer: xpd_mmap_offsets.tx.flags,
981                desc: xpd_mmap_offsets.fr.producer,
982                flags: None,
983            },
984            cr: XdpRingOffset {
985                producer: xpd_mmap_offsets.fr.consumer,
986                consumer: xpd_mmap_offsets.fr.desc,
987                desc: xpd_mmap_offsets.fr.flags,
988                flags: None,
989            },
990        })
991    } else {
992        assert_eq!(
993            optlen as usize,
994            size_of::<xdp_mmap_offsets>(),
995            "unexpected getsockopt size"
996        );
997        // SAFETY: All members of xdp_mmap_offsets are `u64` and thus are
998        // correctly initialized by `MaybeUninit::<xdp_statistics>::zeroed()`.
999        let xpd_mmap_offsets = unsafe { value.assume_init() };
1000        Ok(XdpMmapOffsets {
1001            rx: XdpRingOffset {
1002                producer: xpd_mmap_offsets.rx.producer,
1003                consumer: xpd_mmap_offsets.rx.consumer,
1004                desc: xpd_mmap_offsets.rx.desc,
1005                flags: Some(xpd_mmap_offsets.rx.flags),
1006            },
1007            tx: XdpRingOffset {
1008                producer: xpd_mmap_offsets.tx.producer,
1009                consumer: xpd_mmap_offsets.tx.consumer,
1010                desc: xpd_mmap_offsets.tx.desc,
1011                flags: Some(xpd_mmap_offsets.tx.flags),
1012            },
1013            fr: XdpRingOffset {
1014                producer: xpd_mmap_offsets.fr.producer,
1015                consumer: xpd_mmap_offsets.fr.consumer,
1016                desc: xpd_mmap_offsets.fr.desc,
1017                flags: Some(xpd_mmap_offsets.fr.flags),
1018            },
1019            cr: XdpRingOffset {
1020                producer: xpd_mmap_offsets.cr.producer,
1021                consumer: xpd_mmap_offsets.cr.consumer,
1022                desc: xpd_mmap_offsets.cr.desc,
1023                flags: Some(xpd_mmap_offsets.cr.flags),
1024            },
1025        })
1026    }
1027}
1028
1029#[cfg(target_os = "linux")]
1030#[inline]
1031pub(crate) fn xdp_statistics(fd: BorrowedFd<'_>) -> io::Result<XdpStatistics> {
1032    let mut optlen = size_of::<xdp_statistics>().try_into().unwrap();
1033    debug_assert!(
1034        optlen as usize >= size_of::<c::c_int>(),
1035        "Socket APIs don't ever use `bool` directly"
1036    );
1037    let mut value = MaybeUninit::<xdp_statistics>::zeroed();
1038    getsockopt_raw(fd, c::SOL_XDP, c::XDP_STATISTICS, &mut value, &mut optlen)?;
1039
1040    if optlen as usize == size_of::<xdp_statistics_v1>() {
1041        // SAFETY: All members of xdp_statistics are `u64` and thus are
1042        // correctly initialized by `MaybeUninit::<xdp_statistics>::zeroed()`.
1043        let xdp_statistics = unsafe { value.assume_init() };
1044        Ok(XdpStatistics {
1045            rx_dropped: xdp_statistics.rx_dropped,
1046            rx_invalid_descs: xdp_statistics.rx_dropped,
1047            tx_invalid_descs: xdp_statistics.rx_dropped,
1048            rx_ring_full: None,
1049            rx_fill_ring_empty_descs: None,
1050            tx_ring_empty_descs: None,
1051        })
1052    } else {
1053        assert_eq!(
1054            optlen as usize,
1055            size_of::<xdp_statistics>(),
1056            "unexpected getsockopt size"
1057        );
1058        // SAFETY: All members of xdp_statistics are `u64` and thus are
1059        // correctly initialized by `MaybeUninit::<xdp_statistics>::zeroed()`.
1060        let xdp_statistics = unsafe { value.assume_init() };
1061        Ok(XdpStatistics {
1062            rx_dropped: xdp_statistics.rx_dropped,
1063            rx_invalid_descs: xdp_statistics.rx_invalid_descs,
1064            tx_invalid_descs: xdp_statistics.tx_invalid_descs,
1065            rx_ring_full: Some(xdp_statistics.rx_ring_full),
1066            rx_fill_ring_empty_descs: Some(xdp_statistics.rx_fill_ring_empty_descs),
1067            tx_ring_empty_descs: Some(xdp_statistics.tx_ring_empty_descs),
1068        })
1069    }
1070}
1071
1072#[cfg(target_os = "linux")]
1073#[inline]
1074pub(crate) fn xdp_options(fd: BorrowedFd<'_>) -> io::Result<XdpOptionsFlags> {
1075    getsockopt(fd, c::SOL_XDP, c::XDP_OPTIONS)
1076}
1077
1078#[inline]
1079fn from_in_addr(in_addr: c::in_addr) -> Ipv4Addr {
1080    Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())
1081}
1082
1083#[inline]
1084fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
1085    c::ip_mreq {
1086        imr_multiaddr: to_imr_addr(multiaddr),
1087        imr_interface: to_imr_addr(interface),
1088    }
1089}
1090
1091#[inline]
1092fn to_ip_mreqn(multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32) -> c::ip_mreqn {
1093    c::ip_mreqn {
1094        imr_multiaddr: to_imr_addr(multiaddr),
1095        imr_address: to_imr_addr(address),
1096        imr_ifindex: ifindex,
1097    }
1098}
1099
1100#[inline]
1101fn to_imr_source(
1102    multiaddr: &Ipv4Addr,
1103    interface: &Ipv4Addr,
1104    sourceaddr: &Ipv4Addr,
1105) -> c::ip_mreq_source {
1106    c::ip_mreq_source {
1107        imr_multiaddr: to_imr_addr(multiaddr).s_addr,
1108        imr_interface: to_imr_addr(interface).s_addr,
1109        imr_sourceaddr: to_imr_addr(sourceaddr).s_addr,
1110    }
1111}
1112
1113#[inline]
1114fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr {
1115    c::in_addr {
1116        s_addr: u32::from_ne_bytes(addr.octets()),
1117    }
1118}
1119
1120#[inline]
1121fn to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq {
1122    c::ipv6_mreq {
1123        ipv6mr_multiaddr: to_ipv6mr_multiaddr(multiaddr),
1124        ipv6mr_ifindex: to_ipv6mr_interface(interface),
1125    }
1126}
1127
1128#[inline]
1129fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr {
1130    c::in6_addr {
1131        in6_u: linux_raw_sys::net::in6_addr__bindgen_ty_1 {
1132            u6_addr8: multiaddr.octets(),
1133        },
1134    }
1135}
1136
1137#[inline]
1138fn to_ipv6mr_interface(interface: u32) -> c::c_int {
1139    interface as c::c_int
1140}
1141
1142#[inline]
1143fn from_bool(value: bool) -> c::c_uint {
1144    c::c_uint::from(value)
1145}
1146
1147#[inline]
1148fn to_bool(value: c::c_uint) -> bool {
1149    value != 0
1150}
1151
1152/// Convert to seconds, rounding up if necessary.
1153#[inline]
1154fn duration_to_secs<T: TryFrom<u64>>(duration: Duration) -> io::Result<T> {
1155    let mut secs = duration.as_secs();
1156    if duration.subsec_nanos() != 0 {
1157        secs = secs.checked_add(1).ok_or(io::Errno::INVAL)?;
1158    }
1159    T::try_from(secs).map_err(|_e| io::Errno::INVAL)
1160}