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