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