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