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