1 //! libc syscalls supporting `rustix::net::sockopt`.
2 
3 use super::ext::{in6_addr_new, in_addr_new};
4 use crate::backend::c;
5 use crate::backend::conv::{borrowed_fd, ret};
6 use crate::fd::BorrowedFd;
7 #[cfg(feature = "alloc")]
8 #[cfg(any(
9     linux_like,
10     target_os = "freebsd",
11     target_os = "fuchsia",
12     target_os = "illumos"
13 ))]
14 use crate::ffi::CStr;
15 use crate::io;
16 use crate::net::sockopt::Timeout;
17 #[cfg(target_os = "linux")]
18 use crate::net::xdp::{XdpMmapOffsets, XdpOptionsFlags, XdpRingOffset, XdpStatistics, XdpUmemReg};
19 #[cfg(not(any(
20     apple,
21     windows,
22     target_os = "aix",
23     target_os = "dragonfly",
24     target_os = "emscripten",
25     target_os = "espidf",
26     target_os = "haiku",
27     target_os = "netbsd",
28     target_os = "nto",
29     target_os = "vita",
30 )))]
31 use crate::net::AddressFamily;
32 #[cfg(any(
33     linux_kernel,
34     target_os = "freebsd",
35     target_os = "fuchsia",
36     target_os = "openbsd",
37     target_os = "redox",
38     target_env = "newlib"
39 ))]
40 use crate::net::Protocol;
41 #[cfg(any(
42     linux_kernel,
43     target_os = "freebsd",
44     target_os = "fuchsia",
45     target_os = "openbsd",
46     target_os = "redox",
47     target_env = "newlib"
48 ))]
49 use crate::net::RawProtocol;
50 use crate::net::{Ipv4Addr, Ipv6Addr, SocketType};
51 #[cfg(any(linux_kernel, target_os = "fuchsia"))]
52 use crate::net::{SocketAddrAny, SocketAddrStorage, SocketAddrV4};
53 #[cfg(linux_kernel)]
54 use crate::net::{SocketAddrV6, UCred};
55 use crate::utils::as_mut_ptr;
56 #[cfg(feature = "alloc")]
57 #[cfg(any(
58     linux_like,
59     target_os = "freebsd",
60     target_os = "fuchsia",
61     target_os = "illumos"
62 ))]
63 use alloc::borrow::ToOwned;
64 #[cfg(feature = "alloc")]
65 #[cfg(any(
66     linux_like,
67     target_os = "freebsd",
68     target_os = "fuchsia",
69     target_os = "illumos"
70 ))]
71 use alloc::string::String;
72 #[cfg(apple)]
73 use c::TCP_KEEPALIVE as TCP_KEEPIDLE;
74 #[cfg(not(any(apple, target_os = "openbsd", target_os = "haiku", target_os = "nto")))]
75 use c::TCP_KEEPIDLE;
76 use core::mem::{size_of, MaybeUninit};
77 use core::time::Duration;
78 #[cfg(target_os = "linux")]
79 use linux_raw_sys::xdp::{xdp_mmap_offsets, xdp_statistics, xdp_statistics_v1};
80 #[cfg(windows)]
81 use windows_sys::Win32::Foundation::BOOL;
82 
83 #[inline]
getsockopt<T: Copy>(fd: BorrowedFd<'_>, level: i32, optname: i32) -> io::Result<T>84 fn getsockopt<T: Copy>(fd: BorrowedFd<'_>, level: i32, optname: i32) -> io::Result<T> {
85     let mut optlen = core::mem::size_of::<T>().try_into().unwrap();
86     debug_assert!(
87         optlen as usize >= core::mem::size_of::<c::c_int>(),
88         "Socket APIs don't ever use `bool` directly"
89     );
90 
91     let mut value = MaybeUninit::<T>::zeroed();
92     getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
93 
94     // On Windows at least, `getsockopt` has been observed writing 1
95     // byte on at least (`IPPROTO_TCP`, `TCP_NODELAY`), even though
96     // Windows' documentation says that should write a 4-byte `BOOL`.
97     // So, we initialize the memory to zeros above, and just assert
98     // that `getsockopt` doesn't write too many bytes here.
99     assert!(
100         optlen as usize <= size_of::<T>(),
101         "unexpected getsockopt size"
102     );
103 
104     unsafe { Ok(value.assume_init()) }
105 }
106 
107 #[inline]
getsockopt_raw<T>( fd: BorrowedFd<'_>, level: i32, optname: i32, value: &mut MaybeUninit<T>, optlen: &mut c::socklen_t, ) -> io::Result<()>108 fn getsockopt_raw<T>(
109     fd: BorrowedFd<'_>,
110     level: i32,
111     optname: i32,
112     value: &mut MaybeUninit<T>,
113     optlen: &mut c::socklen_t,
114 ) -> io::Result<()> {
115     unsafe {
116         ret(c::getsockopt(
117             borrowed_fd(fd),
118             level,
119             optname,
120             as_mut_ptr(value).cast(),
121             optlen,
122         ))
123     }
124 }
125 
126 #[inline]
setsockopt<T: Copy>(fd: BorrowedFd<'_>, level: i32, optname: i32, value: T) -> io::Result<()>127 fn setsockopt<T: Copy>(fd: BorrowedFd<'_>, level: i32, optname: i32, value: T) -> io::Result<()> {
128     let optlen = core::mem::size_of::<T>().try_into().unwrap();
129     debug_assert!(
130         optlen as usize >= core::mem::size_of::<c::c_int>(),
131         "Socket APIs don't ever use `bool` directly"
132     );
133     setsockopt_raw(fd, level, optname, &value, optlen)
134 }
135 
136 #[inline]
setsockopt_raw<T>( fd: BorrowedFd<'_>, level: i32, optname: i32, ptr: *const T, optlen: c::socklen_t, ) -> io::Result<()>137 fn setsockopt_raw<T>(
138     fd: BorrowedFd<'_>,
139     level: i32,
140     optname: i32,
141     ptr: *const T,
142     optlen: c::socklen_t,
143 ) -> io::Result<()> {
144     unsafe {
145         ret(c::setsockopt(
146             borrowed_fd(fd),
147             level,
148             optname,
149             ptr.cast(),
150             optlen,
151         ))
152     }
153 }
154 
155 #[inline]
get_socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType>156 pub(crate) fn get_socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType> {
157     getsockopt(fd, c::SOL_SOCKET, c::SO_TYPE)
158 }
159 
160 #[inline]
set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()>161 pub(crate) fn set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()> {
162     setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEADDR, from_bool(reuseaddr))
163 }
164 
165 #[inline]
get_socket_reuseaddr(fd: BorrowedFd<'_>) -> io::Result<bool>166 pub(crate) fn get_socket_reuseaddr(fd: BorrowedFd<'_>) -> io::Result<bool> {
167     getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEADDR).map(to_bool)
168 }
169 
170 #[inline]
set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()>171 pub(crate) fn set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()> {
172     setsockopt(fd, c::SOL_SOCKET, c::SO_BROADCAST, from_bool(broadcast))
173 }
174 
175 #[inline]
get_socket_broadcast(fd: BorrowedFd<'_>) -> io::Result<bool>176 pub(crate) fn get_socket_broadcast(fd: BorrowedFd<'_>) -> io::Result<bool> {
177     getsockopt(fd, c::SOL_SOCKET, c::SO_BROADCAST).map(to_bool)
178 }
179 
180 #[inline]
set_socket_linger(fd: BorrowedFd<'_>, linger: Option<Duration>) -> io::Result<()>181 pub(crate) fn set_socket_linger(fd: BorrowedFd<'_>, linger: Option<Duration>) -> io::Result<()> {
182     // Convert `linger` to seconds, rounding up.
183     let l_linger = if let Some(linger) = linger {
184         duration_to_secs(linger)?
185     } else {
186         0
187     };
188     let linger = c::linger {
189         l_onoff: linger.is_some().into(),
190         l_linger,
191     };
192     setsockopt(fd, c::SOL_SOCKET, c::SO_LINGER, linger)
193 }
194 
195 #[inline]
get_socket_linger(fd: BorrowedFd<'_>) -> io::Result<Option<Duration>>196 pub(crate) fn get_socket_linger(fd: BorrowedFd<'_>) -> io::Result<Option<Duration>> {
197     let linger: c::linger = getsockopt(fd, c::SOL_SOCKET, c::SO_LINGER)?;
198     Ok((linger.l_onoff != 0).then(|| Duration::from_secs(linger.l_linger as u64)))
199 }
200 
201 #[cfg(linux_kernel)]
202 #[inline]
set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()>203 pub(crate) fn set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()> {
204     setsockopt(fd, c::SOL_SOCKET, c::SO_PASSCRED, from_bool(passcred))
205 }
206 
207 #[cfg(linux_kernel)]
208 #[inline]
get_socket_passcred(fd: BorrowedFd<'_>) -> io::Result<bool>209 pub(crate) fn get_socket_passcred(fd: BorrowedFd<'_>) -> io::Result<bool> {
210     getsockopt(fd, c::SOL_SOCKET, c::SO_PASSCRED).map(to_bool)
211 }
212 
213 #[inline]
set_socket_timeout( fd: BorrowedFd<'_>, id: Timeout, timeout: Option<Duration>, ) -> io::Result<()>214 pub(crate) fn set_socket_timeout(
215     fd: BorrowedFd<'_>,
216     id: Timeout,
217     timeout: Option<Duration>,
218 ) -> io::Result<()> {
219     let optname = match id {
220         Timeout::Recv => c::SO_RCVTIMEO,
221         Timeout::Send => c::SO_SNDTIMEO,
222     };
223 
224     #[cfg(not(windows))]
225     let timeout = match timeout {
226         Some(timeout) => {
227             if timeout == Duration::ZERO {
228                 return Err(io::Errno::INVAL);
229             }
230 
231             // Rust's musl libc bindings deprecated `time_t` while they
232             // transition to 64-bit `time_t`. What we want here is just
233             // “whatever type `timeval`'s `tv_sec` is”, so we're ok using
234             // the deprecated type.
235             #[allow(deprecated)]
236             let tv_sec = timeout.as_secs().try_into().unwrap_or(c::time_t::MAX);
237 
238             // `subsec_micros` rounds down, so we use `subsec_nanos` and
239             // manually round up.
240             let mut timeout = c::timeval {
241                 tv_sec,
242                 tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _,
243             };
244             if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
245                 timeout.tv_usec = 1;
246             }
247             timeout
248         }
249         None => c::timeval {
250             tv_sec: 0,
251             tv_usec: 0,
252         },
253     };
254 
255     #[cfg(windows)]
256     let timeout: u32 = match timeout {
257         Some(timeout) => {
258             if timeout == Duration::ZERO {
259                 return Err(io::Errno::INVAL);
260             }
261 
262             // `as_millis` rounds down, so we use `as_nanos` and
263             // manually round up.
264             let mut timeout: u32 = ((timeout.as_nanos() + 999_999) / 1_000_000)
265                 .try_into()
266                 .map_err(|_convert_err| io::Errno::INVAL)?;
267             if timeout == 0 {
268                 timeout = 1;
269             }
270             timeout
271         }
272         None => 0,
273     };
274 
275     setsockopt(fd, c::SOL_SOCKET, optname, timeout)
276 }
277 
278 #[inline]
get_socket_timeout(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>>279 pub(crate) fn get_socket_timeout(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>> {
280     let optname = match id {
281         Timeout::Recv => c::SO_RCVTIMEO,
282         Timeout::Send => c::SO_SNDTIMEO,
283     };
284 
285     #[cfg(not(windows))]
286     {
287         let timeout: c::timeval = getsockopt(fd, c::SOL_SOCKET, optname)?;
288         if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
289             Ok(None)
290         } else {
291             Ok(Some(
292                 Duration::from_secs(timeout.tv_sec as u64)
293                     + Duration::from_micros(timeout.tv_usec as u64),
294             ))
295         }
296     }
297 
298     #[cfg(windows)]
299     {
300         let timeout: u32 = getsockopt(fd, c::SOL_SOCKET, optname)?;
301         if timeout == 0 {
302             Ok(None)
303         } else {
304             Ok(Some(Duration::from_millis(timeout as u64)))
305         }
306     }
307 }
308 
309 #[cfg(any(apple, freebsdlike, target_os = "netbsd"))]
310 #[inline]
get_socket_nosigpipe(fd: BorrowedFd<'_>) -> io::Result<bool>311 pub(crate) fn get_socket_nosigpipe(fd: BorrowedFd<'_>) -> io::Result<bool> {
312     getsockopt(fd, c::SOL_SOCKET, c::SO_NOSIGPIPE).map(to_bool)
313 }
314 
315 #[cfg(any(apple, freebsdlike, target_os = "netbsd"))]
316 #[inline]
set_socket_nosigpipe(fd: BorrowedFd<'_>, val: bool) -> io::Result<()>317 pub(crate) fn set_socket_nosigpipe(fd: BorrowedFd<'_>, val: bool) -> io::Result<()> {
318     setsockopt(fd, c::SOL_SOCKET, c::SO_NOSIGPIPE, from_bool(val))
319 }
320 
321 #[inline]
get_socket_error(fd: BorrowedFd<'_>) -> io::Result<Result<(), io::Errno>>322 pub(crate) fn get_socket_error(fd: BorrowedFd<'_>) -> io::Result<Result<(), io::Errno>> {
323     let err: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_ERROR)?;
324     Ok(if err == 0 {
325         Ok(())
326     } else {
327         Err(io::Errno::from_raw_os_error(err))
328     })
329 }
330 
331 #[inline]
set_socket_keepalive(fd: BorrowedFd<'_>, keepalive: bool) -> io::Result<()>332 pub(crate) fn set_socket_keepalive(fd: BorrowedFd<'_>, keepalive: bool) -> io::Result<()> {
333     setsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE, from_bool(keepalive))
334 }
335 
336 #[inline]
get_socket_keepalive(fd: BorrowedFd<'_>) -> io::Result<bool>337 pub(crate) fn get_socket_keepalive(fd: BorrowedFd<'_>) -> io::Result<bool> {
338     getsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE).map(to_bool)
339 }
340 
341 #[inline]
set_socket_recv_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()>342 pub(crate) fn set_socket_recv_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> {
343     let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?;
344     setsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF, size)
345 }
346 
347 #[inline]
get_socket_recv_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize>348 pub(crate) fn get_socket_recv_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> {
349     getsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF).map(|size: u32| size as usize)
350 }
351 
352 #[inline]
set_socket_send_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()>353 pub(crate) fn set_socket_send_buffer_size(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_SNDBUF, size)
356 }
357 
358 #[inline]
get_socket_send_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize>359 pub(crate) fn get_socket_send_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> {
360     getsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF).map(|size: u32| size as usize)
361 }
362 
363 #[inline]
364 #[cfg(not(any(
365     apple,
366     windows,
367     target_os = "aix",
368     target_os = "dragonfly",
369     target_os = "emscripten",
370     target_os = "espidf",
371     target_os = "haiku",
372     target_os = "netbsd",
373     target_os = "nto",
374     target_os = "vita",
375 )))]
get_socket_domain(fd: BorrowedFd<'_>) -> io::Result<AddressFamily>376 pub(crate) fn get_socket_domain(fd: BorrowedFd<'_>) -> io::Result<AddressFamily> {
377     let domain: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_DOMAIN)?;
378     Ok(AddressFamily(
379         domain.try_into().map_err(|_| io::Errno::OPNOTSUPP)?,
380     ))
381 }
382 
383 #[inline]
384 #[cfg(not(apple))] // Apple platforms declare the constant, but do not actually implement it.
get_socket_acceptconn(fd: BorrowedFd<'_>) -> io::Result<bool>385 pub(crate) fn get_socket_acceptconn(fd: BorrowedFd<'_>) -> io::Result<bool> {
386     getsockopt(fd, c::SOL_SOCKET, c::SO_ACCEPTCONN).map(to_bool)
387 }
388 
389 #[inline]
set_socket_oobinline(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>390 pub(crate) fn set_socket_oobinline(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
391     setsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE, from_bool(value))
392 }
393 
394 #[inline]
get_socket_oobinline(fd: BorrowedFd<'_>) -> io::Result<bool>395 pub(crate) fn get_socket_oobinline(fd: BorrowedFd<'_>) -> io::Result<bool> {
396     getsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE).map(to_bool)
397 }
398 
399 #[cfg(not(any(solarish, windows)))]
400 #[inline]
set_socket_reuseport(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>401 pub(crate) fn set_socket_reuseport(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
402     setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT, from_bool(value))
403 }
404 
405 #[cfg(not(any(solarish, windows)))]
406 #[inline]
get_socket_reuseport(fd: BorrowedFd<'_>) -> io::Result<bool>407 pub(crate) fn get_socket_reuseport(fd: BorrowedFd<'_>) -> io::Result<bool> {
408     getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT).map(to_bool)
409 }
410 
411 #[cfg(target_os = "freebsd")]
412 #[inline]
set_socket_reuseport_lb(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>413 pub(crate) fn set_socket_reuseport_lb(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
414     setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT_LB, from_bool(value))
415 }
416 
417 #[cfg(target_os = "freebsd")]
418 #[inline]
get_socket_reuseport_lb(fd: BorrowedFd<'_>) -> io::Result<bool>419 pub(crate) fn get_socket_reuseport_lb(fd: BorrowedFd<'_>) -> io::Result<bool> {
420     getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT_LB).map(to_bool)
421 }
422 
423 #[cfg(any(
424     linux_kernel,
425     target_os = "freebsd",
426     target_os = "fuchsia",
427     target_os = "openbsd",
428     target_os = "redox",
429     target_env = "newlib"
430 ))]
431 #[inline]
get_socket_protocol(fd: BorrowedFd<'_>) -> io::Result<Option<Protocol>>432 pub(crate) fn get_socket_protocol(fd: BorrowedFd<'_>) -> io::Result<Option<Protocol>> {
433     getsockopt(fd, c::SOL_SOCKET, c::SO_PROTOCOL)
434         .map(|raw| RawProtocol::new(raw).map(Protocol::from_raw))
435 }
436 
437 #[cfg(target_os = "linux")]
438 #[inline]
get_socket_cookie(fd: BorrowedFd<'_>) -> io::Result<u64>439 pub(crate) fn get_socket_cookie(fd: BorrowedFd<'_>) -> io::Result<u64> {
440     getsockopt(fd, c::SOL_SOCKET, c::SO_COOKIE)
441 }
442 
443 #[cfg(target_os = "linux")]
444 #[inline]
get_socket_incoming_cpu(fd: BorrowedFd<'_>) -> io::Result<u32>445 pub(crate) fn get_socket_incoming_cpu(fd: BorrowedFd<'_>) -> io::Result<u32> {
446     getsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU)
447 }
448 
449 #[cfg(target_os = "linux")]
450 #[inline]
set_socket_incoming_cpu(fd: BorrowedFd<'_>, value: u32) -> io::Result<()>451 pub(crate) fn set_socket_incoming_cpu(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
452     setsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU, value)
453 }
454 
455 #[inline]
set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()>456 pub(crate) fn set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()> {
457     setsockopt(fd, c::IPPROTO_IP, c::IP_TTL, ttl)
458 }
459 
460 #[inline]
get_ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32>461 pub(crate) fn get_ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
462     getsockopt(fd, c::IPPROTO_IP, c::IP_TTL)
463 }
464 
465 #[inline]
set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()>466 pub(crate) fn set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()> {
467     setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY, from_bool(only_v6))
468 }
469 
470 #[inline]
get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool>471 pub(crate) fn get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> {
472     getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY).map(to_bool)
473 }
474 
475 #[inline]
set_ip_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()>476 pub(crate) fn set_ip_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> {
477     setsockopt(
478         fd,
479         c::IPPROTO_IP,
480         c::IP_MULTICAST_LOOP,
481         from_bool(multicast_loop),
482     )
483 }
484 
485 #[inline]
get_ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool>486 pub(crate) fn get_ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
487     getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_LOOP).map(to_bool)
488 }
489 
490 #[inline]
set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()>491 pub(crate) fn set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()> {
492     setsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL, multicast_ttl)
493 }
494 
495 #[inline]
get_ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32>496 pub(crate) fn get_ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
497     getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL)
498 }
499 
500 #[inline]
set_ipv6_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()>501 pub(crate) fn set_ipv6_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> {
502     setsockopt(
503         fd,
504         c::IPPROTO_IPV6,
505         c::IPV6_MULTICAST_LOOP,
506         from_bool(multicast_loop),
507     )
508 }
509 
510 #[inline]
get_ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool>511 pub(crate) fn get_ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
512     getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP).map(to_bool)
513 }
514 
515 #[inline]
set_ipv6_multicast_hops(fd: BorrowedFd<'_>, multicast_hops: u32) -> io::Result<()>516 pub(crate) fn set_ipv6_multicast_hops(fd: BorrowedFd<'_>, multicast_hops: u32) -> io::Result<()> {
517     setsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS, multicast_hops)
518 }
519 
520 #[inline]
get_ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32>521 pub(crate) fn get_ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32> {
522     getsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS)
523 }
524 
525 #[inline]
set_ip_add_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, interface: &Ipv4Addr, ) -> io::Result<()>526 pub(crate) fn set_ip_add_membership(
527     fd: BorrowedFd<'_>,
528     multiaddr: &Ipv4Addr,
529     interface: &Ipv4Addr,
530 ) -> io::Result<()> {
531     let mreq = to_ip_mreq(multiaddr, interface);
532     setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq)
533 }
534 
535 #[cfg(any(
536     apple,
537     freebsdlike,
538     linux_like,
539     target_os = "fuchsia",
540     target_os = "openbsd"
541 ))]
542 #[inline]
set_ip_add_membership_with_ifindex( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32, ) -> io::Result<()>543 pub(crate) fn set_ip_add_membership_with_ifindex(
544     fd: BorrowedFd<'_>,
545     multiaddr: &Ipv4Addr,
546     address: &Ipv4Addr,
547     ifindex: i32,
548 ) -> io::Result<()> {
549     let mreqn = to_ip_mreqn(multiaddr, address, ifindex);
550     setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreqn)
551 }
552 
553 #[cfg(any(apple, freebsdlike, linux_like, solarish, target_os = "aix"))]
554 #[inline]
set_ip_add_source_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, interface: &Ipv4Addr, sourceaddr: &Ipv4Addr, ) -> io::Result<()>555 pub(crate) fn set_ip_add_source_membership(
556     fd: BorrowedFd<'_>,
557     multiaddr: &Ipv4Addr,
558     interface: &Ipv4Addr,
559     sourceaddr: &Ipv4Addr,
560 ) -> io::Result<()> {
561     let mreq_source = to_imr_source(multiaddr, interface, sourceaddr);
562     setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_SOURCE_MEMBERSHIP, mreq_source)
563 }
564 
565 #[cfg(any(apple, freebsdlike, linux_like, solarish, target_os = "aix"))]
566 #[inline]
set_ip_drop_source_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, interface: &Ipv4Addr, sourceaddr: &Ipv4Addr, ) -> io::Result<()>567 pub(crate) fn set_ip_drop_source_membership(
568     fd: BorrowedFd<'_>,
569     multiaddr: &Ipv4Addr,
570     interface: &Ipv4Addr,
571     sourceaddr: &Ipv4Addr,
572 ) -> io::Result<()> {
573     let mreq_source = to_imr_source(multiaddr, interface, sourceaddr);
574     setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_SOURCE_MEMBERSHIP, mreq_source)
575 }
576 
577 #[inline]
set_ipv6_add_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv6Addr, interface: u32, ) -> io::Result<()>578 pub(crate) fn set_ipv6_add_membership(
579     fd: BorrowedFd<'_>,
580     multiaddr: &Ipv6Addr,
581     interface: u32,
582 ) -> io::Result<()> {
583     #[cfg(not(any(
584         bsd,
585         solarish,
586         target_os = "haiku",
587         target_os = "l4re",
588         target_os = "nto"
589     )))]
590     use c::IPV6_ADD_MEMBERSHIP;
591     #[cfg(any(
592         bsd,
593         solarish,
594         target_os = "haiku",
595         target_os = "l4re",
596         target_os = "nto"
597     ))]
598     use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP;
599 
600     let mreq = to_ipv6mr(multiaddr, interface);
601     setsockopt(fd, c::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq)
602 }
603 
604 #[inline]
set_ip_drop_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, interface: &Ipv4Addr, ) -> io::Result<()>605 pub(crate) fn set_ip_drop_membership(
606     fd: BorrowedFd<'_>,
607     multiaddr: &Ipv4Addr,
608     interface: &Ipv4Addr,
609 ) -> io::Result<()> {
610     let mreq = to_ip_mreq(multiaddr, interface);
611     setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq)
612 }
613 
614 #[cfg(any(
615     apple,
616     freebsdlike,
617     linux_like,
618     target_os = "fuchsia",
619     target_os = "openbsd"
620 ))]
621 #[inline]
set_ip_drop_membership_with_ifindex( fd: BorrowedFd<'_>, multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32, ) -> io::Result<()>622 pub(crate) fn set_ip_drop_membership_with_ifindex(
623     fd: BorrowedFd<'_>,
624     multiaddr: &Ipv4Addr,
625     address: &Ipv4Addr,
626     ifindex: i32,
627 ) -> io::Result<()> {
628     let mreqn = to_ip_mreqn(multiaddr, address, ifindex);
629     setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreqn)
630 }
631 
632 #[inline]
set_ipv6_drop_membership( fd: BorrowedFd<'_>, multiaddr: &Ipv6Addr, interface: u32, ) -> io::Result<()>633 pub(crate) fn set_ipv6_drop_membership(
634     fd: BorrowedFd<'_>,
635     multiaddr: &Ipv6Addr,
636     interface: u32,
637 ) -> io::Result<()> {
638     #[cfg(not(any(
639         bsd,
640         solarish,
641         target_os = "haiku",
642         target_os = "l4re",
643         target_os = "nto"
644     )))]
645     use c::IPV6_DROP_MEMBERSHIP;
646     #[cfg(any(
647         bsd,
648         solarish,
649         target_os = "haiku",
650         target_os = "l4re",
651         target_os = "nto"
652     ))]
653     use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP;
654 
655     let mreq = to_ipv6mr(multiaddr, interface);
656     setsockopt(fd, c::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq)
657 }
658 
659 #[inline]
get_ipv6_unicast_hops(fd: BorrowedFd<'_>) -> io::Result<u8>660 pub(crate) fn get_ipv6_unicast_hops(fd: BorrowedFd<'_>) -> io::Result<u8> {
661     getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS).map(|hops: c::c_int| hops as u8)
662 }
663 
664 #[inline]
set_ipv6_unicast_hops(fd: BorrowedFd<'_>, hops: Option<u8>) -> io::Result<()>665 pub(crate) fn set_ipv6_unicast_hops(fd: BorrowedFd<'_>, hops: Option<u8>) -> io::Result<()> {
666     let hops = match hops {
667         Some(hops) => hops as c::c_int,
668         None => -1,
669     };
670     setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS, hops)
671 }
672 
673 #[cfg(any(
674     bsd,
675     linux_like,
676     target_os = "aix",
677     target_os = "fuchsia",
678     target_os = "haiku",
679     target_os = "nto",
680     target_env = "newlib"
681 ))]
682 #[inline]
set_ip_tos(fd: BorrowedFd<'_>, value: u8) -> io::Result<()>683 pub(crate) fn set_ip_tos(fd: BorrowedFd<'_>, value: u8) -> io::Result<()> {
684     setsockopt(fd, c::IPPROTO_IP, c::IP_TOS, i32::from(value))
685 }
686 
687 #[cfg(any(
688     bsd,
689     linux_like,
690     target_os = "aix",
691     target_os = "fuchsia",
692     target_os = "haiku",
693     target_os = "nto",
694     target_env = "newlib"
695 ))]
696 #[inline]
get_ip_tos(fd: BorrowedFd<'_>) -> io::Result<u8>697 pub(crate) fn get_ip_tos(fd: BorrowedFd<'_>) -> io::Result<u8> {
698     let value: i32 = getsockopt(fd, c::IPPROTO_IP, c::IP_TOS)?;
699     Ok(value as u8)
700 }
701 
702 #[cfg(any(apple, linux_like, target_os = "freebsd", target_os = "fuchsia"))]
703 #[inline]
set_ip_recvtos(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>704 pub(crate) fn set_ip_recvtos(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
705     setsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS, from_bool(value))
706 }
707 
708 #[cfg(any(apple, linux_like, target_os = "freebsd", target_os = "fuchsia"))]
709 #[inline]
get_ip_recvtos(fd: BorrowedFd<'_>) -> io::Result<bool>710 pub(crate) fn get_ip_recvtos(fd: BorrowedFd<'_>) -> io::Result<bool> {
711     getsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS).map(to_bool)
712 }
713 
714 #[cfg(any(
715     bsd,
716     linux_like,
717     target_os = "aix",
718     target_os = "fuchsia",
719     target_os = "nto"
720 ))]
721 #[inline]
set_ipv6_recvtclass(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>722 pub(crate) fn set_ipv6_recvtclass(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
723     setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS, from_bool(value))
724 }
725 
726 #[cfg(any(
727     bsd,
728     linux_like,
729     target_os = "aix",
730     target_os = "fuchsia",
731     target_os = "nto"
732 ))]
733 #[inline]
get_ipv6_recvtclass(fd: BorrowedFd<'_>) -> io::Result<bool>734 pub(crate) fn get_ipv6_recvtclass(fd: BorrowedFd<'_>) -> io::Result<bool> {
735     getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS).map(to_bool)
736 }
737 
738 #[cfg(any(linux_kernel, target_os = "fuchsia"))]
739 #[inline]
set_ip_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>740 pub(crate) fn set_ip_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
741     setsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND, from_bool(value))
742 }
743 
744 #[cfg(any(linux_kernel, target_os = "fuchsia"))]
745 #[inline]
get_ip_freebind(fd: BorrowedFd<'_>) -> io::Result<bool>746 pub(crate) fn get_ip_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> {
747     getsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND).map(to_bool)
748 }
749 
750 #[cfg(linux_kernel)]
751 #[inline]
set_ipv6_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>752 pub(crate) fn set_ipv6_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
753     setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND, from_bool(value))
754 }
755 
756 #[cfg(linux_kernel)]
757 #[inline]
get_ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result<bool>758 pub(crate) fn get_ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> {
759     getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND).map(to_bool)
760 }
761 
762 #[cfg(any(linux_kernel, target_os = "fuchsia"))]
763 #[inline]
get_ip_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV4>764 pub(crate) fn get_ip_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV4> {
765     let level = c::IPPROTO_IP;
766     let optname = c::SO_ORIGINAL_DST;
767     let mut value = MaybeUninit::<SocketAddrStorage>::uninit();
768     let mut optlen = core::mem::size_of_val(&value).try_into().unwrap();
769 
770     getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
771 
772     let any = unsafe { SocketAddrAny::read(value.as_ptr(), optlen as usize)? };
773     match any {
774         SocketAddrAny::V4(v4) => Ok(v4),
775         _ => unreachable!(),
776     }
777 }
778 
779 #[cfg(linux_kernel)]
780 #[inline]
get_ipv6_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV6>781 pub(crate) fn get_ipv6_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV6> {
782     let level = c::IPPROTO_IPV6;
783     let optname = c::IP6T_SO_ORIGINAL_DST;
784     let mut value = MaybeUninit::<SocketAddrStorage>::uninit();
785     let mut optlen = core::mem::size_of_val(&value).try_into().unwrap();
786 
787     getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
788 
789     let any = unsafe { SocketAddrAny::read(value.as_ptr(), optlen as usize)? };
790     match any {
791         SocketAddrAny::V6(v6) => Ok(v6),
792         _ => unreachable!(),
793     }
794 }
795 
796 #[cfg(not(any(
797     solarish,
798     windows,
799     target_os = "espidf",
800     target_os = "haiku",
801     target_os = "vita"
802 )))]
803 #[inline]
set_ipv6_tclass(fd: BorrowedFd<'_>, value: u32) -> io::Result<()>804 pub(crate) fn set_ipv6_tclass(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
805     setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS, value)
806 }
807 
808 #[cfg(not(any(
809     solarish,
810     windows,
811     target_os = "espidf",
812     target_os = "haiku",
813     target_os = "vita"
814 )))]
815 #[inline]
get_ipv6_tclass(fd: BorrowedFd<'_>) -> io::Result<u32>816 pub(crate) fn get_ipv6_tclass(fd: BorrowedFd<'_>) -> io::Result<u32> {
817     getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS)
818 }
819 
820 #[inline]
set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()>821 pub(crate) fn set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()> {
822     setsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY, from_bool(nodelay))
823 }
824 
825 #[inline]
get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool>826 pub(crate) fn get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool> {
827     getsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY).map(to_bool)
828 }
829 
830 #[inline]
831 #[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))]
set_tcp_keepcnt(fd: BorrowedFd<'_>, count: u32) -> io::Result<()>832 pub(crate) fn set_tcp_keepcnt(fd: BorrowedFd<'_>, count: u32) -> io::Result<()> {
833     setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT, count)
834 }
835 
836 #[inline]
837 #[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))]
get_tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result<u32>838 pub(crate) fn get_tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result<u32> {
839     getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT)
840 }
841 
842 #[inline]
843 #[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))]
set_tcp_keepidle(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()>844 pub(crate) fn set_tcp_keepidle(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> {
845     let secs: c::c_uint = duration_to_secs(duration)?;
846     setsockopt(fd, c::IPPROTO_TCP, TCP_KEEPIDLE, secs)
847 }
848 
849 #[inline]
850 #[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))]
get_tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result<Duration>851 pub(crate) fn get_tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result<Duration> {
852     let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, TCP_KEEPIDLE)?;
853     Ok(Duration::from_secs(secs as u64))
854 }
855 
856 #[inline]
857 #[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))]
set_tcp_keepintvl(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()>858 pub(crate) fn set_tcp_keepintvl(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> {
859     let secs: c::c_uint = duration_to_secs(duration)?;
860     setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL, secs)
861 }
862 
863 #[inline]
864 #[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))]
get_tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result<Duration>865 pub(crate) fn get_tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result<Duration> {
866     let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL)?;
867     Ok(Duration::from_secs(secs as u64))
868 }
869 
870 #[inline]
871 #[cfg(any(linux_like, target_os = "fuchsia"))]
set_tcp_user_timeout(fd: BorrowedFd<'_>, value: u32) -> io::Result<()>872 pub(crate) fn set_tcp_user_timeout(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
873     setsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT, value)
874 }
875 
876 #[inline]
877 #[cfg(any(linux_like, target_os = "fuchsia"))]
get_tcp_user_timeout(fd: BorrowedFd<'_>) -> io::Result<u32>878 pub(crate) fn get_tcp_user_timeout(fd: BorrowedFd<'_>) -> io::Result<u32> {
879     getsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT)
880 }
881 
882 #[cfg(any(linux_like, target_os = "fuchsia"))]
883 #[inline]
set_tcp_quickack(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>884 pub(crate) fn set_tcp_quickack(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
885     setsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK, from_bool(value))
886 }
887 
888 #[cfg(any(linux_like, target_os = "fuchsia"))]
889 #[inline]
get_tcp_quickack(fd: BorrowedFd<'_>) -> io::Result<bool>890 pub(crate) fn get_tcp_quickack(fd: BorrowedFd<'_>) -> io::Result<bool> {
891     getsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK).map(to_bool)
892 }
893 
894 #[cfg(any(
895     linux_like,
896     target_os = "freebsd",
897     target_os = "fuchsia",
898     target_os = "illumos"
899 ))]
900 #[inline]
set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result<()>901 pub(crate) fn set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result<()> {
902     let level = c::IPPROTO_TCP;
903     let optname = c::TCP_CONGESTION;
904     let optlen = value.len().try_into().unwrap();
905     setsockopt_raw(fd, level, optname, value.as_ptr(), optlen)
906 }
907 
908 #[cfg(feature = "alloc")]
909 #[cfg(any(
910     linux_like,
911     target_os = "freebsd",
912     target_os = "fuchsia",
913     target_os = "illumos"
914 ))]
915 #[inline]
get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result<String>916 pub(crate) fn get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result<String> {
917     let level = c::IPPROTO_TCP;
918     let optname = c::TCP_CONGESTION;
919     const OPTLEN: c::socklen_t = 16;
920     let mut value = MaybeUninit::<[MaybeUninit<u8>; OPTLEN as usize]>::uninit();
921     let mut optlen = OPTLEN;
922     getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
923     unsafe {
924         let value = value.assume_init();
925         let slice: &[u8] = core::mem::transmute(&value[..optlen as usize]);
926         assert!(slice.iter().any(|b| *b == b'\0'));
927         Ok(
928             core::str::from_utf8(CStr::from_ptr(slice.as_ptr().cast()).to_bytes())
929                 .unwrap()
930                 .to_owned(),
931         )
932     }
933 }
934 
935 #[cfg(any(linux_like, target_os = "fuchsia"))]
936 #[inline]
set_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>937 pub(crate) fn set_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
938     setsockopt(
939         fd,
940         c::IPPROTO_TCP,
941         c::TCP_THIN_LINEAR_TIMEOUTS,
942         from_bool(value),
943     )
944 }
945 
946 #[cfg(any(linux_like, target_os = "fuchsia"))]
947 #[inline]
get_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>) -> io::Result<bool>948 pub(crate) fn get_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>) -> io::Result<bool> {
949     getsockopt(fd, c::IPPROTO_TCP, c::TCP_THIN_LINEAR_TIMEOUTS).map(to_bool)
950 }
951 
952 #[cfg(any(linux_like, solarish, target_os = "fuchsia"))]
953 #[inline]
set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>954 pub(crate) fn set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
955     setsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK, from_bool(value))
956 }
957 
958 #[cfg(any(linux_like, solarish, target_os = "fuchsia"))]
959 #[inline]
get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool>960 pub(crate) fn get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool> {
961     getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool)
962 }
963 
964 #[cfg(linux_kernel)]
965 #[inline]
get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred>966 pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> {
967     getsockopt(fd, c::SOL_SOCKET, c::SO_PEERCRED)
968 }
969 
970 #[cfg(target_os = "linux")]
971 #[inline]
set_xdp_umem_reg(fd: BorrowedFd<'_>, value: XdpUmemReg) -> io::Result<()>972 pub(crate) fn set_xdp_umem_reg(fd: BorrowedFd<'_>, value: XdpUmemReg) -> io::Result<()> {
973     setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_REG, value)
974 }
975 
976 #[cfg(target_os = "linux")]
977 #[inline]
set_xdp_umem_fill_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()>978 pub(crate) fn set_xdp_umem_fill_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
979     setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_FILL_RING, value)
980 }
981 
982 #[cfg(target_os = "linux")]
983 #[inline]
set_xdp_umem_completion_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()>984 pub(crate) fn set_xdp_umem_completion_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
985     setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_COMPLETION_RING, value)
986 }
987 
988 #[cfg(target_os = "linux")]
989 #[inline]
set_xdp_tx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()>990 pub(crate) fn set_xdp_tx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
991     setsockopt(fd, c::SOL_XDP, c::XDP_TX_RING, value)
992 }
993 
994 #[cfg(target_os = "linux")]
995 #[inline]
set_xdp_rx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()>996 pub(crate) fn set_xdp_rx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
997     setsockopt(fd, c::SOL_XDP, c::XDP_RX_RING, value)
998 }
999 
1000 #[cfg(target_os = "linux")]
1001 #[inline]
get_xdp_mmap_offsets(fd: BorrowedFd<'_>) -> io::Result<XdpMmapOffsets>1002 pub(crate) fn get_xdp_mmap_offsets(fd: BorrowedFd<'_>) -> io::Result<XdpMmapOffsets> {
1003     // The kernel will write `xdp_mmap_offsets` or `xdp_mmap_offsets_v1` to the supplied pointer,
1004     // depending on the kernel version. Both structs only contain u64 values.
1005     // By using the larger of both as the parameter, we can shuffle the values to the non-v1 version
1006     // returned by `get_xdp_mmap_offsets` while keeping the return type unaffected by the kernel
1007     // version. This works because C will layout all struct members one after the other.
1008 
1009     let mut optlen = core::mem::size_of::<xdp_mmap_offsets>().try_into().unwrap();
1010     debug_assert!(
1011         optlen as usize >= core::mem::size_of::<c::c_int>(),
1012         "Socket APIs don't ever use `bool` directly"
1013     );
1014     let mut value = MaybeUninit::<xdp_mmap_offsets>::zeroed();
1015     getsockopt_raw(fd, c::SOL_XDP, c::XDP_MMAP_OFFSETS, &mut value, &mut optlen)?;
1016 
1017     if optlen as usize == core::mem::size_of::<c::xdp_mmap_offsets_v1>() {
1018         // Safety: All members of xdp_mmap_offsets are u64 and thus are correctly initialized
1019         // by `MaybeUninit::<xdp_statistics>::zeroed()`
1020         let xpd_mmap_offsets = unsafe { value.assume_init() };
1021         Ok(XdpMmapOffsets {
1022             rx: XdpRingOffset {
1023                 producer: xpd_mmap_offsets.rx.producer,
1024                 consumer: xpd_mmap_offsets.rx.consumer,
1025                 desc: xpd_mmap_offsets.rx.desc,
1026                 flags: None,
1027             },
1028             tx: XdpRingOffset {
1029                 producer: xpd_mmap_offsets.rx.flags,
1030                 consumer: xpd_mmap_offsets.tx.producer,
1031                 desc: xpd_mmap_offsets.tx.consumer,
1032                 flags: None,
1033             },
1034             fr: XdpRingOffset {
1035                 producer: xpd_mmap_offsets.tx.desc,
1036                 consumer: xpd_mmap_offsets.tx.flags,
1037                 desc: xpd_mmap_offsets.fr.producer,
1038                 flags: None,
1039             },
1040             cr: XdpRingOffset {
1041                 producer: xpd_mmap_offsets.fr.consumer,
1042                 consumer: xpd_mmap_offsets.fr.desc,
1043                 desc: xpd_mmap_offsets.fr.flags,
1044                 flags: None,
1045             },
1046         })
1047     } else {
1048         assert_eq!(
1049             optlen as usize,
1050             core::mem::size_of::<xdp_mmap_offsets>(),
1051             "unexpected getsockopt size"
1052         );
1053         // Safety: All members of xdp_mmap_offsets are u64 and thus are correctly initialized
1054         // by `MaybeUninit::<xdp_statistics>::zeroed()`
1055         let xpd_mmap_offsets = unsafe { value.assume_init() };
1056         Ok(XdpMmapOffsets {
1057             rx: XdpRingOffset {
1058                 producer: xpd_mmap_offsets.rx.producer,
1059                 consumer: xpd_mmap_offsets.rx.consumer,
1060                 desc: xpd_mmap_offsets.rx.desc,
1061                 flags: Some(xpd_mmap_offsets.rx.flags),
1062             },
1063             tx: XdpRingOffset {
1064                 producer: xpd_mmap_offsets.tx.producer,
1065                 consumer: xpd_mmap_offsets.tx.consumer,
1066                 desc: xpd_mmap_offsets.tx.desc,
1067                 flags: Some(xpd_mmap_offsets.tx.flags),
1068             },
1069             fr: XdpRingOffset {
1070                 producer: xpd_mmap_offsets.fr.producer,
1071                 consumer: xpd_mmap_offsets.fr.consumer,
1072                 desc: xpd_mmap_offsets.fr.desc,
1073                 flags: Some(xpd_mmap_offsets.fr.flags),
1074             },
1075             cr: XdpRingOffset {
1076                 producer: xpd_mmap_offsets.cr.producer,
1077                 consumer: xpd_mmap_offsets.cr.consumer,
1078                 desc: xpd_mmap_offsets.cr.desc,
1079                 flags: Some(xpd_mmap_offsets.cr.flags),
1080             },
1081         })
1082     }
1083 }
1084 
1085 #[cfg(target_os = "linux")]
1086 #[inline]
get_xdp_statistics(fd: BorrowedFd<'_>) -> io::Result<XdpStatistics>1087 pub(crate) fn get_xdp_statistics(fd: BorrowedFd<'_>) -> io::Result<XdpStatistics> {
1088     let mut optlen = core::mem::size_of::<xdp_statistics>().try_into().unwrap();
1089     debug_assert!(
1090         optlen as usize >= core::mem::size_of::<c::c_int>(),
1091         "Socket APIs don't ever use `bool` directly"
1092     );
1093     let mut value = MaybeUninit::<xdp_statistics>::zeroed();
1094     getsockopt_raw(fd, c::SOL_XDP, c::XDP_STATISTICS, &mut value, &mut optlen)?;
1095 
1096     if optlen as usize == core::mem::size_of::<xdp_statistics_v1>() {
1097         // Safety: All members of xdp_statistics are u64 and thus are correctly initialized
1098         // by `MaybeUninit::<xdp_statistics>::zeroed()`
1099         let xdp_statistics = unsafe { value.assume_init() };
1100         Ok(XdpStatistics {
1101             rx_dropped: xdp_statistics.rx_dropped,
1102             rx_invalid_descs: xdp_statistics.rx_dropped,
1103             tx_invalid_descs: xdp_statistics.rx_dropped,
1104             rx_ring_full: None,
1105             rx_fill_ring_empty_descs: None,
1106             tx_ring_empty_descs: None,
1107         })
1108     } else {
1109         assert_eq!(
1110             optlen as usize,
1111             core::mem::size_of::<xdp_statistics>(),
1112             "unexpected getsockopt size"
1113         );
1114         // Safety: All members of xdp_statistics are u64 and thus are correctly initialized
1115         // by `MaybeUninit::<xdp_statistics>::zeroed()`
1116         let xdp_statistics = unsafe { value.assume_init() };
1117         Ok(XdpStatistics {
1118             rx_dropped: xdp_statistics.rx_dropped,
1119             rx_invalid_descs: xdp_statistics.rx_invalid_descs,
1120             tx_invalid_descs: xdp_statistics.tx_invalid_descs,
1121             rx_ring_full: Some(xdp_statistics.rx_ring_full),
1122             rx_fill_ring_empty_descs: Some(xdp_statistics.rx_fill_ring_empty_descs),
1123             tx_ring_empty_descs: Some(xdp_statistics.tx_ring_empty_descs),
1124         })
1125     }
1126 }
1127 
1128 #[cfg(target_os = "linux")]
1129 #[inline]
get_xdp_options(fd: BorrowedFd<'_>) -> io::Result<XdpOptionsFlags>1130 pub(crate) fn get_xdp_options(fd: BorrowedFd<'_>) -> io::Result<XdpOptionsFlags> {
1131     getsockopt(fd, c::SOL_XDP, c::XDP_OPTIONS)
1132 }
1133 
1134 #[inline]
to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq1135 fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
1136     c::ip_mreq {
1137         imr_multiaddr: to_imr_addr(multiaddr),
1138         imr_interface: to_imr_addr(interface),
1139     }
1140 }
1141 
1142 #[cfg(any(
1143     apple,
1144     freebsdlike,
1145     linux_like,
1146     target_os = "fuchsia",
1147     target_os = "openbsd"
1148 ))]
1149 #[inline]
to_ip_mreqn(multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32) -> c::ip_mreqn1150 fn to_ip_mreqn(multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32) -> c::ip_mreqn {
1151     c::ip_mreqn {
1152         imr_multiaddr: to_imr_addr(multiaddr),
1153         imr_address: to_imr_addr(address),
1154         imr_ifindex: ifindex,
1155     }
1156 }
1157 
1158 #[cfg(any(apple, freebsdlike, linux_like, solarish, target_os = "aix"))]
1159 #[inline]
to_imr_source( multiaddr: &Ipv4Addr, interface: &Ipv4Addr, sourceaddr: &Ipv4Addr, ) -> c::ip_mreq_source1160 fn to_imr_source(
1161     multiaddr: &Ipv4Addr,
1162     interface: &Ipv4Addr,
1163     sourceaddr: &Ipv4Addr,
1164 ) -> c::ip_mreq_source {
1165     c::ip_mreq_source {
1166         imr_multiaddr: to_imr_addr(multiaddr),
1167         imr_interface: to_imr_addr(interface),
1168         imr_sourceaddr: to_imr_addr(sourceaddr),
1169     }
1170 }
1171 
1172 #[inline]
to_imr_addr(addr: &Ipv4Addr) -> c::in_addr1173 fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr {
1174     in_addr_new(u32::from_ne_bytes(addr.octets()))
1175 }
1176 
1177 #[inline]
to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq1178 fn to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq {
1179     c::ipv6_mreq {
1180         ipv6mr_multiaddr: to_ipv6mr_multiaddr(multiaddr),
1181         ipv6mr_interface: to_ipv6mr_interface(interface),
1182     }
1183 }
1184 
1185 #[inline]
to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr1186 fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr {
1187     in6_addr_new(multiaddr.octets())
1188 }
1189 
1190 #[cfg(target_os = "android")]
1191 #[inline]
to_ipv6mr_interface(interface: u32) -> c::c_int1192 fn to_ipv6mr_interface(interface: u32) -> c::c_int {
1193     interface as c::c_int
1194 }
1195 
1196 #[cfg(not(target_os = "android"))]
1197 #[inline]
to_ipv6mr_interface(interface: u32) -> c::c_uint1198 fn to_ipv6mr_interface(interface: u32) -> c::c_uint {
1199     interface as c::c_uint
1200 }
1201 
1202 // `getsockopt` and `setsockopt` represent boolean values as integers.
1203 #[cfg(not(windows))]
1204 type RawSocketBool = c::c_int;
1205 #[cfg(windows)]
1206 type RawSocketBool = BOOL;
1207 
1208 // Wrap `RawSocketBool` in a newtype to discourage misuse.
1209 #[repr(transparent)]
1210 #[derive(Copy, Clone)]
1211 struct SocketBool(RawSocketBool);
1212 
1213 // Convert from a `bool` to a `SocketBool`.
1214 #[inline]
from_bool(value: bool) -> SocketBool1215 fn from_bool(value: bool) -> SocketBool {
1216     SocketBool(value.into())
1217 }
1218 
1219 // Convert from a `SocketBool` to a `bool`.
1220 #[inline]
to_bool(value: SocketBool) -> bool1221 fn to_bool(value: SocketBool) -> bool {
1222     value.0 != 0
1223 }
1224 
1225 /// Convert to seconds, rounding up if necessary.
1226 #[inline]
duration_to_secs<T: TryFrom<u64>>(duration: Duration) -> io::Result<T>1227 fn duration_to_secs<T: TryFrom<u64>>(duration: Duration) -> io::Result<T> {
1228     let mut secs = duration.as_secs();
1229     if duration.subsec_nanos() != 0 {
1230         secs = secs.checked_add(1).ok_or(io::Errno::INVAL)?;
1231     }
1232     T::try_from(secs).map_err(|_e| io::Errno::INVAL)
1233 }
1234