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