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, SocketAddrAny, SocketAddrStorage,
19 SocketAddrV4, SocketAddrV6, SocketType, UCred,
20};
21#[cfg(feature = "alloc")]
22use alloc::borrow::ToOwned;
23#[cfg(feature = "alloc")]
24use alloc::string::String;
25use core::mem::MaybeUninit;
26use core::time::Duration;
27use linux_raw_sys::general::{__kernel_old_timeval, __kernel_sock_timeval};
28#[cfg(target_os = "linux")]
29use linux_raw_sys::xdp::{xdp_mmap_offsets, xdp_statistics, xdp_statistics_v1};
30#[cfg(target_arch = "x86")]
31use {
32 crate::backend::conv::{slice_just_addr, x86_sys},
33 crate::backend::reg::{ArgReg, SocketArg},
34 linux_raw_sys::net::{SYS_GETSOCKOPT, SYS_SETSOCKOPT},
35};
36
37#[inline]
38fn getsockopt<T: Copy>(fd: BorrowedFd<'_>, level: u32, optname: u32) -> io::Result<T> {
39 let mut optlen: c::socklen_t = core::mem::size_of::<T>().try_into().unwrap();
40 debug_assert!(
41 optlen as usize >= core::mem::size_of::<c::c_int>(),
42 "Socket APIs don't ever use `bool` directly"
43 );
44
45 let mut value = MaybeUninit::<T>::uninit();
46 getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
47
48 assert_eq!(
49 optlen as usize,
50 core::mem::size_of::<T>(),
51 "unexpected getsockopt size"
52 );
53
54 unsafe { Ok(value.assume_init()) }
55}
56
57#[inline]
58fn getsockopt_raw<T>(
59 fd: BorrowedFd<'_>,
60 level: u32,
61 optname: u32,
62 value: &mut MaybeUninit<T>,
63 optlen: &mut c::socklen_t,
64) -> io::Result<()> {
65 #[cfg(not(target_arch = "x86"))]
66 unsafe {
67 ret(syscall!(
68 __NR_getsockopt,
69 fd,
70 c_uint(level),
71 c_uint(optname),
72 value,
73 by_mut(optlen)
74 ))
75 }
76 #[cfg(target_arch = "x86")]
77 unsafe {
78 ret(syscall!(
79 __NR_socketcall,
80 x86_sys(SYS_GETSOCKOPT),
81 slice_just_addr::<ArgReg<'_, SocketArg>, _>(&[
82 fd.into(),
83 c_uint(level),
84 c_uint(optname),
85 value.into(),
86 by_mut(optlen),
87 ])
88 ))
89 }
90}
91
92#[inline]
93fn setsockopt<T: Copy>(fd: BorrowedFd<'_>, level: u32, optname: u32, value: T) -> io::Result<()> {
94 let optlen = core::mem::size_of::<T>().try_into().unwrap();
95 debug_assert!(
96 optlen as usize >= core::mem::size_of::<c::c_int>(),
97 "Socket APIs don't ever use `bool` directly"
98 );
99 setsockopt_raw(fd, level, optname, &value, optlen)
100}
101
102#[inline]
103fn setsockopt_raw<T>(
104 fd: BorrowedFd<'_>,
105 level: u32,
106 optname: u32,
107 ptr: *const T,
108 optlen: c::socklen_t,
109) -> io::Result<()> {
110 #[cfg(not(target_arch = "x86"))]
111 unsafe {
112 ret(syscall_readonly!(
113 __NR_setsockopt,
114 fd,
115 c_uint(level),
116 c_uint(optname),
117 ptr,
118 socklen_t(optlen)
119 ))
120 }
121 #[cfg(target_arch = "x86")]
122 unsafe {
123 ret(syscall_readonly!(
124 __NR_socketcall,
125 x86_sys(SYS_SETSOCKOPT),
126 slice_just_addr::<ArgReg<'_, SocketArg>, _>(&[
127 fd.into(),
128 c_uint(level),
129 c_uint(optname),
130 ptr.into(),
131 socklen_t(optlen),
132 ])
133 ))
134 }
135}
136
137#[inline]
138pub(crate) fn get_socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType> {
139 getsockopt(fd, c::SOL_SOCKET, c::SO_TYPE)
140}
141
142#[inline]
143pub(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]
148pub(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]
153pub(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]
158pub(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]
163pub(crate) fn set_socket_linger(fd: BorrowedFd<'_>, linger: Option<Duration>) -> io::Result<()> {
164 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]
178pub(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]
184pub(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]
189pub(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]
194pub(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
212fn 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]
228pub(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
242fn 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#[inline]
255fn duration_from_linux_sock_timeval(time: __kernel_sock_timeval) -> Option<Duration> {
256 if time.tv_sec == 0 && time.tv_usec == 0 {
257 None
258 } else {
259 Some(Duration::from_secs(time.tv_sec as u64) + Duration::from_micros(time.tv_usec as u64))
260 }
261}
262
263fn 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#[inline]
275fn 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 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
299fn 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 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]
327pub(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]
337pub(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]
342pub(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]
347pub(crate) fn set_socket_recv_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> {
348 let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?;
349 setsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF, size)
350}
351
352#[inline]
353pub(crate) fn set_socket_recv_buffer_size_force(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_RCVBUFFORCE, size)
356}
357
358#[inline]
359pub(crate) fn get_socket_recv_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> {
360 getsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF).map(|size: u32| size as usize)
361}
362
363#[inline]
364pub(crate) fn set_socket_send_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> {
365 let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?;
366 setsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF, size)
367}
368
369#[inline]
370pub(crate) fn get_socket_send_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> {
371 getsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF).map(|size: u32| size as usize)
372}
373
374#[inline]
375pub(crate) fn get_socket_domain(fd: BorrowedFd<'_>) -> io::Result<AddressFamily> {
376 let domain: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_DOMAIN)?;
377 Ok(AddressFamily(
378 domain.try_into().map_err(|_| io::Errno::OPNOTSUPP)?,
379 ))
380}
381
382#[inline]
383pub(crate) fn get_socket_acceptconn(fd: BorrowedFd<'_>) -> io::Result<bool> {
384 getsockopt(fd, c::SOL_SOCKET, c::SO_ACCEPTCONN).map(to_bool)
385}
386
387#[inline]
388pub(crate) fn set_socket_oobinline(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
389 setsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE, from_bool(value))
390}
391
392#[inline]
393pub(crate) fn get_socket_oobinline(fd: BorrowedFd<'_>) -> io::Result<bool> {
394 getsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE).map(to_bool)
395}
396
397#[inline]
398pub(crate) fn set_socket_reuseport(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
399 setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT, from_bool(value))
400}
401
402#[inline]
403pub(crate) fn get_socket_reuseport(fd: BorrowedFd<'_>) -> io::Result<bool> {
404 getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT).map(to_bool)
405}
406
407#[inline]
408pub(crate) fn get_socket_protocol(fd: BorrowedFd<'_>) -> io::Result<Option<Protocol>> {
409 getsockopt(fd, c::SOL_SOCKET, c::SO_PROTOCOL)
410 .map(|raw: u32| RawProtocol::new(raw).map(Protocol::from_raw))
411}
412
413#[inline]
414pub(crate) fn get_socket_cookie(fd: BorrowedFd<'_>) -> io::Result<u64> {
415 getsockopt(fd, c::SOL_SOCKET, c::SO_COOKIE)
416}
417
418#[inline]
419pub(crate) fn get_socket_incoming_cpu(fd: BorrowedFd<'_>) -> io::Result<u32> {
420 getsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU)
421}
422
423#[inline]
424pub(crate) fn set_socket_incoming_cpu(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
425 setsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU, value)
426}
427
428#[inline]
429pub(crate) fn set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()> {
430 setsockopt(fd, c::IPPROTO_IP, c::IP_TTL, ttl)
431}
432
433#[inline]
434pub(crate) fn get_ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
435 getsockopt(fd, c::IPPROTO_IP, c::IP_TTL)
436}
437
438#[inline]
439pub(crate) fn set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()> {
440 setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY, from_bool(only_v6))
441}
442
443#[inline]
444pub(crate) fn get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> {
445 getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY).map(to_bool)
446}
447
448#[inline]
449pub(crate) fn set_ip_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> {
450 setsockopt(
451 fd,
452 c::IPPROTO_IP,
453 c::IP_MULTICAST_LOOP,
454 from_bool(multicast_loop),
455 )
456}
457
458#[inline]
459pub(crate) fn get_ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
460 getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_LOOP).map(to_bool)
461}
462
463#[inline]
464pub(crate) fn set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()> {
465 setsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL, multicast_ttl)
466}
467
468#[inline]
469pub(crate) fn get_ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
470 getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL)
471}
472
473#[inline]
474pub(crate) fn set_ipv6_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> {
475 setsockopt(
476 fd,
477 c::IPPROTO_IPV6,
478 c::IPV6_MULTICAST_LOOP,
479 from_bool(multicast_loop),
480 )
481}
482
483#[inline]
484pub(crate) fn get_ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
485 getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP).map(to_bool)
486}
487
488#[inline]
489pub(crate) fn set_ipv6_multicast_hops(fd: BorrowedFd<'_>, multicast_hops: u32) -> io::Result<()> {
490 setsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS, multicast_hops)
491}
492
493#[inline]
494pub(crate) fn get_ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32> {
495 getsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS)
496}
497
498#[inline]
499pub(crate) fn set_ip_add_membership(
500 fd: BorrowedFd<'_>,
501 multiaddr: &Ipv4Addr,
502 interface: &Ipv4Addr,
503) -> io::Result<()> {
504 let mreq = to_ip_mreq(multiaddr, interface);
505 setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq)
506}
507
508#[inline]
509pub(crate) fn set_ip_add_membership_with_ifindex(
510 fd: BorrowedFd<'_>,
511 multiaddr: &Ipv4Addr,
512 address: &Ipv4Addr,
513 ifindex: i32,
514) -> io::Result<()> {
515 let mreqn = to_ip_mreqn(multiaddr, address, ifindex);
516 setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreqn)
517}
518
519#[inline]
520pub(crate) fn set_ip_add_source_membership(
521 fd: BorrowedFd<'_>,
522 multiaddr: &Ipv4Addr,
523 interface: &Ipv4Addr,
524 sourceaddr: &Ipv4Addr,
525) -> io::Result<()> {
526 let mreq_source = to_imr_source(multiaddr, interface, sourceaddr);
527 setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_SOURCE_MEMBERSHIP, mreq_source)
528}
529
530#[inline]
531pub(crate) fn set_ip_drop_source_membership(
532 fd: BorrowedFd<'_>,
533 multiaddr: &Ipv4Addr,
534 interface: &Ipv4Addr,
535 sourceaddr: &Ipv4Addr,
536) -> io::Result<()> {
537 let mreq_source = to_imr_source(multiaddr, interface, sourceaddr);
538 setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_SOURCE_MEMBERSHIP, mreq_source)
539}
540
541#[inline]
542pub(crate) fn set_ipv6_add_membership(
543 fd: BorrowedFd<'_>,
544 multiaddr: &Ipv6Addr,
545 interface: u32,
546) -> io::Result<()> {
547 let mreq = to_ipv6mr(multiaddr, interface);
548 setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_ADD_MEMBERSHIP, mreq)
549}
550
551#[inline]
552pub(crate) fn set_ip_drop_membership(
553 fd: BorrowedFd<'_>,
554 multiaddr: &Ipv4Addr,
555 interface: &Ipv4Addr,
556) -> io::Result<()> {
557 let mreq = to_ip_mreq(multiaddr, interface);
558 setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq)
559}
560
561#[inline]
562pub(crate) fn set_ip_drop_membership_with_ifindex(
563 fd: BorrowedFd<'_>,
564 multiaddr: &Ipv4Addr,
565 address: &Ipv4Addr,
566 ifindex: i32,
567) -> io::Result<()> {
568 let mreqn = to_ip_mreqn(multiaddr, address, ifindex);
569 setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreqn)
570}
571
572#[inline]
573pub(crate) fn set_ipv6_drop_membership(
574 fd: BorrowedFd<'_>,
575 multiaddr: &Ipv6Addr,
576 interface: u32,
577) -> io::Result<()> {
578 let mreq = to_ipv6mr(multiaddr, interface);
579 setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_DROP_MEMBERSHIP, mreq)
580}
581
582#[inline]
583pub(crate) fn get_ipv6_unicast_hops(fd: BorrowedFd<'_>) -> io::Result<u8> {
584 getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS).map(|hops: c::c_int| hops as u8)
585}
586
587#[inline]
588pub(crate) fn set_ipv6_unicast_hops(fd: BorrowedFd<'_>, hops: Option<u8>) -> io::Result<()> {
589 let hops = match hops {
590 Some(hops) => hops.into(),
591 None => -1,
592 };
593 setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS, hops)
594}
595
596#[inline]
597pub(crate) fn set_ip_tos(fd: BorrowedFd<'_>, value: u8) -> io::Result<()> {
598 setsockopt(fd, c::IPPROTO_IP, c::IP_TOS, i32::from(value))
599}
600
601#[inline]
602pub(crate) fn get_ip_tos(fd: BorrowedFd<'_>) -> io::Result<u8> {
603 let value: i32 = getsockopt(fd, c::IPPROTO_IP, c::IP_TOS)?;
604 Ok(value as u8)
605}
606
607#[inline]
608pub(crate) fn set_ip_recvtos(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
609 setsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS, from_bool(value))
610}
611
612#[inline]
613pub(crate) fn get_ip_recvtos(fd: BorrowedFd<'_>) -> io::Result<bool> {
614 getsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS).map(to_bool)
615}
616
617#[inline]
618pub(crate) fn set_ipv6_recvtclass(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
619 setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS, from_bool(value))
620}
621
622#[inline]
623pub(crate) fn get_ipv6_recvtclass(fd: BorrowedFd<'_>) -> io::Result<bool> {
624 getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS).map(to_bool)
625}
626
627#[inline]
628pub(crate) fn set_ip_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
629 setsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND, from_bool(value))
630}
631
632#[inline]
633pub(crate) fn get_ip_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> {
634 getsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND).map(to_bool)
635}
636
637#[inline]
638pub(crate) fn set_ipv6_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
639 setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND, from_bool(value))
640}
641
642#[inline]
643pub(crate) fn get_ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> {
644 getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND).map(to_bool)
645}
646
647#[inline]
648pub(crate) fn get_ip_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV4> {
649 let level = c::IPPROTO_IP;
650 let optname = c::SO_ORIGINAL_DST;
651 let mut value = MaybeUninit::<SocketAddrStorage>::uninit();
652 let mut optlen = core::mem::size_of_val(&value).try_into().unwrap();
653
654 getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
655
656 let any = unsafe { SocketAddrAny::read(value.as_ptr(), optlen as usize)? };
657 match any {
658 SocketAddrAny::V4(v4) => Ok(v4),
659 _ => unreachable!(),
660 }
661}
662
663#[inline]
664pub(crate) fn get_ipv6_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV6> {
665 let level = c::IPPROTO_IPV6;
666 let optname = c::IP6T_SO_ORIGINAL_DST;
667 let mut value = MaybeUninit::<SocketAddrStorage>::uninit();
668 let mut optlen = core::mem::size_of_val(&value).try_into().unwrap();
669
670 getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
671
672 let any = unsafe { SocketAddrAny::read(value.as_ptr(), optlen as usize)? };
673 match any {
674 SocketAddrAny::V6(v6) => Ok(v6),
675 _ => unreachable!(),
676 }
677}
678
679#[inline]
680pub(crate) fn set_ipv6_tclass(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
681 setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS, value)
682}
683
684#[inline]
685pub(crate) fn get_ipv6_tclass(fd: BorrowedFd<'_>) -> io::Result<u32> {
686 getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS)
687}
688
689#[inline]
690pub(crate) fn set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()> {
691 setsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY, from_bool(nodelay))
692}
693
694#[inline]
695pub(crate) fn get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool> {
696 getsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY).map(to_bool)
697}
698
699#[inline]
700pub(crate) fn set_tcp_keepcnt(fd: BorrowedFd<'_>, count: u32) -> io::Result<()> {
701 setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT, count)
702}
703
704#[inline]
705pub(crate) fn get_tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result<u32> {
706 getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT)
707}
708
709#[inline]
710pub(crate) fn set_tcp_keepidle(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> {
711 let secs: c::c_uint = duration_to_secs(duration)?;
712 setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPIDLE, secs)
713}
714
715#[inline]
716pub(crate) fn get_tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result<Duration> {
717 let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPIDLE)?;
718 Ok(Duration::from_secs(secs.into()))
719}
720
721#[inline]
722pub(crate) fn set_tcp_keepintvl(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> {
723 let secs: c::c_uint = duration_to_secs(duration)?;
724 setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL, secs)
725}
726
727#[inline]
728pub(crate) fn get_tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result<Duration> {
729 let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL)?;
730 Ok(Duration::from_secs(secs.into()))
731}
732
733#[inline]
734pub(crate) fn set_tcp_user_timeout(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
735 setsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT, value)
736}
737
738#[inline]
739pub(crate) fn get_tcp_user_timeout(fd: BorrowedFd<'_>) -> io::Result<u32> {
740 getsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT)
741}
742
743#[inline]
744pub(crate) fn set_tcp_quickack(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
745 setsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK, from_bool(value))
746}
747
748#[inline]
749pub(crate) fn get_tcp_quickack(fd: BorrowedFd<'_>) -> io::Result<bool> {
750 getsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK).map(to_bool)
751}
752
753#[inline]
754pub(crate) fn set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result<()> {
755 let level = c::IPPROTO_TCP;
756 let optname = c::TCP_CONGESTION;
757 let optlen = value.len().try_into().unwrap();
758 setsockopt_raw(fd, level, optname, value.as_ptr(), optlen)
759}
760
761#[cfg(feature = "alloc")]
762#[inline]
763pub(crate) fn get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result<String> {
764 const OPTLEN: c::socklen_t = 16;
765
766 let level = c::IPPROTO_TCP;
767 let optname = c::TCP_CONGESTION;
768 let mut value = MaybeUninit::<[MaybeUninit<u8>; OPTLEN as usize]>::uninit();
769 let mut optlen = OPTLEN;
770 getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
771 unsafe {
772 let value = value.assume_init();
773 let slice: &[u8] = core::mem::transmute(&value[..optlen as usize]);
774 assert!(slice.iter().any(|b| *b == b'\0'));
775 Ok(
776 core::str::from_utf8(CStr::from_ptr(slice.as_ptr().cast()).to_bytes())
777 .unwrap()
778 .to_owned(),
779 )
780 }
781}
782
783#[inline]
784pub(crate) fn set_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
785 setsockopt(
786 fd,
787 c::IPPROTO_TCP,
788 c::TCP_THIN_LINEAR_TIMEOUTS,
789 from_bool(value),
790 )
791}
792
793#[inline]
794pub(crate) fn get_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>) -> io::Result<bool> {
795 getsockopt(fd, c::IPPROTO_TCP, c::TCP_THIN_LINEAR_TIMEOUTS).map(to_bool)
796}
797
798#[inline]
799pub(crate) fn set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
800 setsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK, from_bool(value))
801}
802
803#[inline]
804pub(crate) fn get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool> {
805 getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool)
806}
807
808#[inline]
809pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> {
810 getsockopt(fd, c::SOL_SOCKET, linux_raw_sys::net::SO_PEERCRED)
811}
812
813#[cfg(target_os = "linux")]
814#[inline]
815pub(crate) fn set_xdp_umem_reg(fd: BorrowedFd<'_>, value: XdpUmemReg) -> io::Result<()> {
816 setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_REG, value)
817}
818
819#[cfg(target_os = "linux")]
820#[inline]
821pub(crate) fn set_xdp_umem_fill_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
822 setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_FILL_RING, value)
823}
824
825#[cfg(target_os = "linux")]
826#[inline]
827pub(crate) fn set_xdp_umem_completion_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
828 setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_COMPLETION_RING, value)
829}
830
831#[cfg(target_os = "linux")]
832#[inline]
833pub(crate) fn set_xdp_tx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
834 setsockopt(fd, c::SOL_XDP, c::XDP_TX_RING, value)
835}
836
837#[cfg(target_os = "linux")]
838#[inline]
839pub(crate) fn set_xdp_rx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
840 setsockopt(fd, c::SOL_XDP, c::XDP_RX_RING, value)
841}
842
843#[cfg(target_os = "linux")]
844#[inline]
845pub(crate) fn get_xdp_mmap_offsets(fd: BorrowedFd<'_>) -> io::Result<XdpMmapOffsets> {
846 let mut optlen = core::mem::size_of::<xdp_mmap_offsets>().try_into().unwrap();
855 debug_assert!(
856 optlen as usize >= core::mem::size_of::<c::c_int>(),
857 "Socket APIs don't ever use `bool` directly"
858 );
859 let mut value = MaybeUninit::<xdp_mmap_offsets>::zeroed();
860 getsockopt_raw(fd, c::SOL_XDP, c::XDP_MMAP_OFFSETS, &mut value, &mut optlen)?;
861
862 if optlen as usize == core::mem::size_of::<c::xdp_mmap_offsets_v1>() {
863 let xpd_mmap_offsets = unsafe { value.assume_init() };
866 Ok(XdpMmapOffsets {
867 rx: XdpRingOffset {
868 producer: xpd_mmap_offsets.rx.producer,
869 consumer: xpd_mmap_offsets.rx.consumer,
870 desc: xpd_mmap_offsets.rx.desc,
871 flags: None,
872 },
873 tx: XdpRingOffset {
874 producer: xpd_mmap_offsets.rx.flags,
875 consumer: xpd_mmap_offsets.tx.producer,
876 desc: xpd_mmap_offsets.tx.consumer,
877 flags: None,
878 },
879 fr: XdpRingOffset {
880 producer: xpd_mmap_offsets.tx.desc,
881 consumer: xpd_mmap_offsets.tx.flags,
882 desc: xpd_mmap_offsets.fr.producer,
883 flags: None,
884 },
885 cr: XdpRingOffset {
886 producer: xpd_mmap_offsets.fr.consumer,
887 consumer: xpd_mmap_offsets.fr.desc,
888 desc: xpd_mmap_offsets.fr.flags,
889 flags: None,
890 },
891 })
892 } else {
893 assert_eq!(
894 optlen as usize,
895 core::mem::size_of::<xdp_mmap_offsets>(),
896 "unexpected getsockopt size"
897 );
898 let xpd_mmap_offsets = unsafe { value.assume_init() };
901 Ok(XdpMmapOffsets {
902 rx: XdpRingOffset {
903 producer: xpd_mmap_offsets.rx.producer,
904 consumer: xpd_mmap_offsets.rx.consumer,
905 desc: xpd_mmap_offsets.rx.desc,
906 flags: Some(xpd_mmap_offsets.rx.flags),
907 },
908 tx: XdpRingOffset {
909 producer: xpd_mmap_offsets.tx.producer,
910 consumer: xpd_mmap_offsets.tx.consumer,
911 desc: xpd_mmap_offsets.tx.desc,
912 flags: Some(xpd_mmap_offsets.tx.flags),
913 },
914 fr: XdpRingOffset {
915 producer: xpd_mmap_offsets.fr.producer,
916 consumer: xpd_mmap_offsets.fr.consumer,
917 desc: xpd_mmap_offsets.fr.desc,
918 flags: Some(xpd_mmap_offsets.fr.flags),
919 },
920 cr: XdpRingOffset {
921 producer: xpd_mmap_offsets.cr.producer,
922 consumer: xpd_mmap_offsets.cr.consumer,
923 desc: xpd_mmap_offsets.cr.desc,
924 flags: Some(xpd_mmap_offsets.cr.flags),
925 },
926 })
927 }
928}
929
930#[cfg(target_os = "linux")]
931#[inline]
932pub(crate) fn get_xdp_statistics(fd: BorrowedFd<'_>) -> io::Result<XdpStatistics> {
933 let mut optlen = core::mem::size_of::<xdp_statistics>().try_into().unwrap();
934 debug_assert!(
935 optlen as usize >= core::mem::size_of::<c::c_int>(),
936 "Socket APIs don't ever use `bool` directly"
937 );
938 let mut value = MaybeUninit::<xdp_statistics>::zeroed();
939 getsockopt_raw(fd, c::SOL_XDP, c::XDP_STATISTICS, &mut value, &mut optlen)?;
940
941 if optlen as usize == core::mem::size_of::<xdp_statistics_v1>() {
942 let xdp_statistics = unsafe { value.assume_init() };
945 Ok(XdpStatistics {
946 rx_dropped: xdp_statistics.rx_dropped,
947 rx_invalid_descs: xdp_statistics.rx_dropped,
948 tx_invalid_descs: xdp_statistics.rx_dropped,
949 rx_ring_full: None,
950 rx_fill_ring_empty_descs: None,
951 tx_ring_empty_descs: None,
952 })
953 } else {
954 assert_eq!(
955 optlen as usize,
956 core::mem::size_of::<xdp_statistics>(),
957 "unexpected getsockopt size"
958 );
959 let xdp_statistics = unsafe { value.assume_init() };
962 Ok(XdpStatistics {
963 rx_dropped: xdp_statistics.rx_dropped,
964 rx_invalid_descs: xdp_statistics.rx_invalid_descs,
965 tx_invalid_descs: xdp_statistics.tx_invalid_descs,
966 rx_ring_full: Some(xdp_statistics.rx_ring_full),
967 rx_fill_ring_empty_descs: Some(xdp_statistics.rx_fill_ring_empty_descs),
968 tx_ring_empty_descs: Some(xdp_statistics.tx_ring_empty_descs),
969 })
970 }
971}
972
973#[cfg(target_os = "linux")]
974#[inline]
975pub(crate) fn get_xdp_options(fd: BorrowedFd<'_>) -> io::Result<XdpOptionsFlags> {
976 getsockopt(fd, c::SOL_XDP, c::XDP_OPTIONS)
977}
978
979#[inline]
980fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
981 c::ip_mreq {
982 imr_multiaddr: to_imr_addr(multiaddr),
983 imr_interface: to_imr_addr(interface),
984 }
985}
986
987#[inline]
988fn to_ip_mreqn(multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32) -> c::ip_mreqn {
989 c::ip_mreqn {
990 imr_multiaddr: to_imr_addr(multiaddr),
991 imr_address: to_imr_addr(address),
992 imr_ifindex: ifindex,
993 }
994}
995
996#[inline]
997fn to_imr_source(
998 multiaddr: &Ipv4Addr,
999 interface: &Ipv4Addr,
1000 sourceaddr: &Ipv4Addr,
1001) -> c::ip_mreq_source {
1002 c::ip_mreq_source {
1003 imr_multiaddr: to_imr_addr(multiaddr).s_addr,
1004 imr_interface: to_imr_addr(interface).s_addr,
1005 imr_sourceaddr: to_imr_addr(sourceaddr).s_addr,
1006 }
1007}
1008
1009#[inline]
1010fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr {
1011 c::in_addr {
1012 s_addr: u32::from_ne_bytes(addr.octets()),
1013 }
1014}
1015
1016#[inline]
1017fn to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq {
1018 c::ipv6_mreq {
1019 ipv6mr_multiaddr: to_ipv6mr_multiaddr(multiaddr),
1020 ipv6mr_ifindex: to_ipv6mr_interface(interface),
1021 }
1022}
1023
1024#[inline]
1025fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr {
1026 c::in6_addr {
1027 in6_u: linux_raw_sys::net::in6_addr__bindgen_ty_1 {
1028 u6_addr8: multiaddr.octets(),
1029 },
1030 }
1031}
1032
1033#[inline]
1034fn to_ipv6mr_interface(interface: u32) -> c::c_int {
1035 interface as c::c_int
1036}
1037
1038#[inline]
1039fn from_bool(value: bool) -> c::c_uint {
1040 c::c_uint::from(value)
1041}
1042
1043#[inline]
1044fn to_bool(value: c::c_uint) -> bool {
1045 value != 0
1046}
1047
1048#[inline]
1050fn duration_to_secs<T: TryFrom<u64>>(duration: Duration) -> io::Result<T> {
1051 let mut secs = duration.as_secs();
1052 if duration.subsec_nanos() != 0 {
1053 secs = secs.checked_add(1).ok_or(io::Errno::INVAL)?;
1054 }
1055 T::try_from(secs).map_err(|_e| io::Errno::INVAL)
1056}