summaryrefslogtreecommitdiffstats
path: root/vendor/rustix/src/backend/linux_raw/net/sockopt.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:48 +0000
commitef24de24a82fe681581cc130f342363c47c0969a (patch)
tree0d494f7e1a38b95c92426f58fe6eaa877303a86c /vendor/rustix/src/backend/linux_raw/net/sockopt.rs
parentReleasing progress-linux version 1.74.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-ef24de24a82fe681581cc130f342363c47c0969a.tar.xz
rustc-ef24de24a82fe681581cc130f342363c47c0969a.zip
Merging upstream version 1.75.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/rustix/src/backend/linux_raw/net/sockopt.rs')
-rw-r--r--vendor/rustix/src/backend/linux_raw/net/sockopt.rs879
1 files changed, 879 insertions, 0 deletions
diff --git a/vendor/rustix/src/backend/linux_raw/net/sockopt.rs b/vendor/rustix/src/backend/linux_raw/net/sockopt.rs
new file mode 100644
index 000000000..6a740bbf7
--- /dev/null
+++ b/vendor/rustix/src/backend/linux_raw/net/sockopt.rs
@@ -0,0 +1,879 @@
+//! linux_raw syscalls supporting `rustix::net::sockopt`.
+//!
+//! # Safety
+//!
+//! See the `rustix::backend` module documentation for details.
+#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)]
+
+use crate::backend::c;
+use crate::backend::conv::{by_mut, c_uint, ret, socklen_t};
+use crate::fd::BorrowedFd;
+#[cfg(feature = "alloc")]
+use crate::ffi::CStr;
+use crate::io;
+use crate::net::sockopt::Timeout;
+use crate::net::{
+ AddressFamily, Ipv4Addr, Ipv6Addr, Protocol, RawProtocol, SocketAddrAny, SocketAddrStorage,
+ SocketAddrV4, SocketAddrV6, SocketType, UCred,
+};
+#[cfg(feature = "alloc")]
+use alloc::borrow::ToOwned;
+#[cfg(feature = "alloc")]
+use alloc::string::String;
+use core::mem::MaybeUninit;
+use core::time::Duration;
+use linux_raw_sys::general::{__kernel_old_timeval, __kernel_sock_timeval};
+#[cfg(target_arch = "x86")]
+use {
+ crate::backend::conv::{slice_just_addr, x86_sys},
+ crate::backend::reg::{ArgReg, SocketArg},
+ linux_raw_sys::net::{SYS_GETSOCKOPT, SYS_SETSOCKOPT},
+};
+
+#[inline]
+fn getsockopt<T: Copy>(fd: BorrowedFd<'_>, level: u32, optname: u32) -> io::Result<T> {
+ let mut optlen: c::socklen_t = core::mem::size_of::<T>().try_into().unwrap();
+ debug_assert!(
+ optlen as usize >= core::mem::size_of::<c::c_int>(),
+ "Socket APIs don't ever use `bool` directly"
+ );
+
+ let mut value = MaybeUninit::<T>::uninit();
+ getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
+
+ assert_eq!(
+ optlen as usize,
+ core::mem::size_of::<T>(),
+ "unexpected getsockopt size"
+ );
+
+ unsafe { Ok(value.assume_init()) }
+}
+
+#[inline]
+fn getsockopt_raw<T>(
+ fd: BorrowedFd<'_>,
+ level: u32,
+ optname: u32,
+ value: &mut MaybeUninit<T>,
+ optlen: &mut c::socklen_t,
+) -> io::Result<()> {
+ #[cfg(not(target_arch = "x86"))]
+ unsafe {
+ ret(syscall!(
+ __NR_getsockopt,
+ fd,
+ c_uint(level),
+ c_uint(optname),
+ value,
+ by_mut(optlen)
+ ))
+ }
+ #[cfg(target_arch = "x86")]
+ unsafe {
+ ret(syscall!(
+ __NR_socketcall,
+ x86_sys(SYS_GETSOCKOPT),
+ slice_just_addr::<ArgReg<'_, SocketArg>, _>(&[
+ fd.into(),
+ c_uint(level),
+ c_uint(optname),
+ value.into(),
+ by_mut(optlen),
+ ])
+ ))
+ }
+}
+
+#[inline]
+fn setsockopt<T: Copy>(fd: BorrowedFd<'_>, level: u32, optname: u32, value: T) -> io::Result<()> {
+ let optlen = core::mem::size_of::<T>().try_into().unwrap();
+ debug_assert!(
+ optlen as usize >= core::mem::size_of::<c::c_int>(),
+ "Socket APIs don't ever use `bool` directly"
+ );
+ setsockopt_raw(fd, level, optname, &value, optlen)
+}
+
+#[inline]
+fn setsockopt_raw<T>(
+ fd: BorrowedFd<'_>,
+ level: u32,
+ optname: u32,
+ ptr: *const T,
+ optlen: c::socklen_t,
+) -> io::Result<()> {
+ #[cfg(not(target_arch = "x86"))]
+ unsafe {
+ ret(syscall_readonly!(
+ __NR_setsockopt,
+ fd,
+ c_uint(level),
+ c_uint(optname),
+ ptr,
+ socklen_t(optlen)
+ ))
+ }
+ #[cfg(target_arch = "x86")]
+ unsafe {
+ ret(syscall_readonly!(
+ __NR_socketcall,
+ x86_sys(SYS_SETSOCKOPT),
+ slice_just_addr::<ArgReg<'_, SocketArg>, _>(&[
+ fd.into(),
+ c_uint(level),
+ c_uint(optname),
+ ptr.into(),
+ socklen_t(optlen),
+ ])
+ ))
+ }
+}
+
+#[inline]
+pub(crate) fn get_socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType> {
+ getsockopt(fd, c::SOL_SOCKET, c::SO_TYPE)
+}
+
+#[inline]
+pub(crate) fn set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()> {
+ setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEADDR, from_bool(reuseaddr))
+}
+
+#[inline]
+pub(crate) fn get_socket_reuseaddr(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEADDR).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()> {
+ setsockopt(fd, c::SOL_SOCKET, c::SO_BROADCAST, from_bool(broadcast))
+}
+
+#[inline]
+pub(crate) fn get_socket_broadcast(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::SOL_SOCKET, c::SO_BROADCAST).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_socket_linger(fd: BorrowedFd<'_>, linger: Option<Duration>) -> io::Result<()> {
+ // Convert `linger` to seconds, rounding up.
+ let l_linger = if let Some(linger) = linger {
+ duration_to_secs(linger)?
+ } else {
+ 0
+ };
+ let linger = c::linger {
+ l_onoff: c::c_int::from(linger.is_some()),
+ l_linger,
+ };
+ setsockopt(fd, c::SOL_SOCKET, c::SO_LINGER, linger)
+}
+
+#[inline]
+pub(crate) fn get_socket_linger(fd: BorrowedFd<'_>) -> io::Result<Option<Duration>> {
+ let linger: c::linger = getsockopt(fd, c::SOL_SOCKET, c::SO_LINGER)?;
+ Ok((linger.l_onoff != 0).then(|| Duration::from_secs(linger.l_linger as u64)))
+}
+
+#[inline]
+pub(crate) fn set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()> {
+ setsockopt(fd, c::SOL_SOCKET, c::SO_PASSCRED, from_bool(passcred))
+}
+
+#[inline]
+pub(crate) fn get_socket_passcred(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::SOL_SOCKET, c::SO_PASSCRED).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_socket_timeout(
+ fd: BorrowedFd<'_>,
+ id: Timeout,
+ timeout: Option<Duration>,
+) -> io::Result<()> {
+ let time = duration_to_linux_sock_timeval(timeout)?;
+ let optname = match id {
+ Timeout::Recv => c::SO_RCVTIMEO_NEW,
+ Timeout::Send => c::SO_SNDTIMEO_NEW,
+ };
+ match setsockopt(fd, c::SOL_SOCKET, optname, time) {
+ Err(io::Errno::NOPROTOOPT) if c::SO_RCVTIMEO_NEW != c::SO_RCVTIMEO_OLD => {
+ set_socket_timeout_old(fd, id, timeout)
+ }
+ otherwise => otherwise,
+ }
+}
+
+/// Same as `set_socket_timeout` but uses `__kernel_old_timeval` instead of
+/// `__kernel_sock_timeval` and `_OLD` constants instead of `_NEW`.
+fn set_socket_timeout_old(
+ fd: BorrowedFd<'_>,
+ id: Timeout,
+ timeout: Option<Duration>,
+) -> io::Result<()> {
+ let time = duration_to_linux_old_timeval(timeout)?;
+ let optname = match id {
+ Timeout::Recv => c::SO_RCVTIMEO_OLD,
+ Timeout::Send => c::SO_SNDTIMEO_OLD,
+ };
+ setsockopt(fd, c::SOL_SOCKET, optname, time)
+}
+
+#[inline]
+pub(crate) fn get_socket_timeout(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>> {
+ let optname = match id {
+ Timeout::Recv => c::SO_RCVTIMEO_NEW,
+ Timeout::Send => c::SO_SNDTIMEO_NEW,
+ };
+ let time: __kernel_sock_timeval = match getsockopt(fd, c::SOL_SOCKET, optname) {
+ Err(io::Errno::NOPROTOOPT) if c::SO_RCVTIMEO_NEW != c::SO_RCVTIMEO_OLD => {
+ return get_socket_timeout_old(fd, id)
+ }
+ otherwise => otherwise?,
+ };
+ Ok(duration_from_linux_sock_timeval(time))
+}
+
+/// Same as `get_socket_timeout` but uses `__kernel_old_timeval` instead of
+/// `__kernel_sock_timeval` and `_OLD` constants instead of `_NEW`.
+fn get_socket_timeout_old(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>> {
+ let optname = match id {
+ Timeout::Recv => c::SO_RCVTIMEO_OLD,
+ Timeout::Send => c::SO_SNDTIMEO_OLD,
+ };
+ let time: __kernel_old_timeval = getsockopt(fd, c::SOL_SOCKET, optname)?;
+ Ok(duration_from_linux_old_timeval(time))
+}
+
+/// Convert a `__linux_sock_timeval` to a Rust `Option<Duration>`.
+#[inline]
+fn duration_from_linux_sock_timeval(time: __kernel_sock_timeval) -> Option<Duration> {
+ if time.tv_sec == 0 && time.tv_usec == 0 {
+ None
+ } else {
+ Some(Duration::from_secs(time.tv_sec as u64) + Duration::from_micros(time.tv_usec as u64))
+ }
+}
+
+/// Like `duration_from_linux` but uses Linux's old 32-bit
+/// `__kernel_old_timeval`.
+fn duration_from_linux_old_timeval(time: __kernel_old_timeval) -> Option<Duration> {
+ if time.tv_sec == 0 && time.tv_usec == 0 {
+ None
+ } else {
+ Some(Duration::from_secs(time.tv_sec as u64) + Duration::from_micros(time.tv_usec as u64))
+ }
+}
+
+/// Convert a Rust `Option<Duration>` to a `__kernel_sock_timeval`.
+#[inline]
+fn duration_to_linux_sock_timeval(timeout: Option<Duration>) -> io::Result<__kernel_sock_timeval> {
+ Ok(match timeout {
+ Some(timeout) => {
+ if timeout == Duration::ZERO {
+ return Err(io::Errno::INVAL);
+ }
+ // `subsec_micros` rounds down, so we use `subsec_nanos` and
+ // manually round up.
+ let mut timeout = __kernel_sock_timeval {
+ tv_sec: timeout.as_secs().try_into().unwrap_or(i64::MAX),
+ tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _,
+ };
+ if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
+ timeout.tv_usec = 1;
+ }
+ timeout
+ }
+ None => __kernel_sock_timeval {
+ tv_sec: 0,
+ tv_usec: 0,
+ },
+ })
+}
+
+/// Like `duration_to_linux` but uses Linux's old 32-bit
+/// `__kernel_old_timeval`.
+fn duration_to_linux_old_timeval(timeout: Option<Duration>) -> io::Result<__kernel_old_timeval> {
+ Ok(match timeout {
+ Some(timeout) => {
+ if timeout == Duration::ZERO {
+ return Err(io::Errno::INVAL);
+ }
+
+ // `subsec_micros` rounds down, so we use `subsec_nanos` and
+ // manually round up.
+ let mut timeout = __kernel_old_timeval {
+ tv_sec: timeout.as_secs().try_into().unwrap_or(c::c_long::MAX),
+ tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _,
+ };
+ if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
+ timeout.tv_usec = 1;
+ }
+ timeout
+ }
+ None => __kernel_old_timeval {
+ tv_sec: 0,
+ tv_usec: 0,
+ },
+ })
+}
+
+#[inline]
+pub(crate) fn get_socket_error(fd: BorrowedFd<'_>) -> io::Result<Result<(), io::Errno>> {
+ let err: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_ERROR)?;
+ Ok(if err == 0 {
+ Ok(())
+ } else {
+ Err(io::Errno::from_raw_os_error(err))
+ })
+}
+
+#[inline]
+pub(crate) fn set_socket_keepalive(fd: BorrowedFd<'_>, keepalive: bool) -> io::Result<()> {
+ setsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE, from_bool(keepalive))
+}
+
+#[inline]
+pub(crate) fn get_socket_keepalive(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_socket_recv_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> {
+ let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?;
+ setsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF, size)
+}
+
+#[inline]
+pub(crate) fn get_socket_recv_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> {
+ getsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF).map(|size: u32| size as usize)
+}
+
+#[inline]
+pub(crate) fn set_socket_send_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> {
+ let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?;
+ setsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF, size)
+}
+
+#[inline]
+pub(crate) fn get_socket_send_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> {
+ getsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF).map(|size: u32| size as usize)
+}
+
+#[inline]
+pub(crate) fn get_socket_domain(fd: BorrowedFd<'_>) -> io::Result<AddressFamily> {
+ let domain: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_DOMAIN)?;
+ Ok(AddressFamily(
+ domain.try_into().map_err(|_| io::Errno::OPNOTSUPP)?,
+ ))
+}
+
+#[inline]
+pub(crate) fn get_socket_acceptconn(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::SOL_SOCKET, c::SO_ACCEPTCONN).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_socket_oobinline(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
+ setsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE, from_bool(value))
+}
+
+#[inline]
+pub(crate) fn get_socket_oobinline(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_socket_reuseport(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
+ setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT, from_bool(value))
+}
+
+#[inline]
+pub(crate) fn get_socket_reuseport(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn get_socket_protocol(fd: BorrowedFd<'_>) -> io::Result<Option<Protocol>> {
+ getsockopt(fd, c::SOL_SOCKET, c::SO_PROTOCOL)
+ .map(|raw: u32| RawProtocol::new(raw).map(Protocol::from_raw))
+}
+
+#[inline]
+pub(crate) fn get_socket_cookie(fd: BorrowedFd<'_>) -> io::Result<u64> {
+ getsockopt(fd, c::SOL_SOCKET, c::SO_COOKIE)
+}
+
+#[inline]
+pub(crate) fn get_socket_incoming_cpu(fd: BorrowedFd<'_>) -> io::Result<u32> {
+ getsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU)
+}
+
+#[inline]
+pub(crate) fn set_socket_incoming_cpu(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
+ setsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU, value)
+}
+
+#[inline]
+pub(crate) fn set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_IP, c::IP_TTL, ttl)
+}
+
+#[inline]
+pub(crate) fn get_ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
+ getsockopt(fd, c::IPPROTO_IP, c::IP_TTL)
+}
+
+#[inline]
+pub(crate) fn set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY, from_bool(only_v6))
+}
+
+#[inline]
+pub(crate) fn get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_ip_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> {
+ setsockopt(
+ fd,
+ c::IPPROTO_IP,
+ c::IP_MULTICAST_LOOP,
+ from_bool(multicast_loop),
+ )
+}
+
+#[inline]
+pub(crate) fn get_ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_LOOP).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL, multicast_ttl)
+}
+
+#[inline]
+pub(crate) fn get_ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
+ getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL)
+}
+
+#[inline]
+pub(crate) fn set_ipv6_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> {
+ setsockopt(
+ fd,
+ c::IPPROTO_IPV6,
+ c::IPV6_MULTICAST_LOOP,
+ from_bool(multicast_loop),
+ )
+}
+
+#[inline]
+pub(crate) fn get_ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_ipv6_multicast_hops(fd: BorrowedFd<'_>, multicast_hops: u32) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS, multicast_hops)
+}
+
+#[inline]
+pub(crate) fn get_ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32> {
+ getsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS)
+}
+
+#[inline]
+pub(crate) fn set_ip_add_membership(
+ fd: BorrowedFd<'_>,
+ multiaddr: &Ipv4Addr,
+ interface: &Ipv4Addr,
+) -> io::Result<()> {
+ let mreq = to_ip_mreq(multiaddr, interface);
+ setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq)
+}
+
+#[inline]
+pub(crate) fn set_ip_add_membership_with_ifindex(
+ fd: BorrowedFd<'_>,
+ multiaddr: &Ipv4Addr,
+ address: &Ipv4Addr,
+ ifindex: i32,
+) -> io::Result<()> {
+ let mreqn = to_ip_mreqn(multiaddr, address, ifindex);
+ setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreqn)
+}
+
+#[inline]
+pub(crate) fn set_ip_add_source_membership(
+ fd: BorrowedFd<'_>,
+ multiaddr: &Ipv4Addr,
+ interface: &Ipv4Addr,
+ sourceaddr: &Ipv4Addr,
+) -> io::Result<()> {
+ let mreq_source = to_imr_source(multiaddr, interface, sourceaddr);
+ setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_SOURCE_MEMBERSHIP, mreq_source)
+}
+
+#[inline]
+pub(crate) fn set_ip_drop_source_membership(
+ fd: BorrowedFd<'_>,
+ multiaddr: &Ipv4Addr,
+ interface: &Ipv4Addr,
+ sourceaddr: &Ipv4Addr,
+) -> io::Result<()> {
+ let mreq_source = to_imr_source(multiaddr, interface, sourceaddr);
+ setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_SOURCE_MEMBERSHIP, mreq_source)
+}
+
+#[inline]
+pub(crate) fn set_ipv6_add_membership(
+ fd: BorrowedFd<'_>,
+ multiaddr: &Ipv6Addr,
+ interface: u32,
+) -> io::Result<()> {
+ let mreq = to_ipv6mr(multiaddr, interface);
+ setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_ADD_MEMBERSHIP, mreq)
+}
+
+#[inline]
+pub(crate) fn set_ip_drop_membership(
+ fd: BorrowedFd<'_>,
+ multiaddr: &Ipv4Addr,
+ interface: &Ipv4Addr,
+) -> io::Result<()> {
+ let mreq = to_ip_mreq(multiaddr, interface);
+ setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq)
+}
+
+#[inline]
+pub(crate) fn set_ip_drop_membership_with_ifindex(
+ fd: BorrowedFd<'_>,
+ multiaddr: &Ipv4Addr,
+ address: &Ipv4Addr,
+ ifindex: i32,
+) -> io::Result<()> {
+ let mreqn = to_ip_mreqn(multiaddr, address, ifindex);
+ setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreqn)
+}
+
+#[inline]
+pub(crate) fn set_ipv6_drop_membership(
+ fd: BorrowedFd<'_>,
+ multiaddr: &Ipv6Addr,
+ interface: u32,
+) -> io::Result<()> {
+ let mreq = to_ipv6mr(multiaddr, interface);
+ setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_DROP_MEMBERSHIP, mreq)
+}
+
+#[inline]
+pub(crate) fn get_ipv6_unicast_hops(fd: BorrowedFd<'_>) -> io::Result<u8> {
+ getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS).map(|hops: c::c_int| hops as u8)
+}
+
+#[inline]
+pub(crate) fn set_ipv6_unicast_hops(fd: BorrowedFd<'_>, hops: Option<u8>) -> io::Result<()> {
+ let hops = match hops {
+ Some(hops) => hops.into(),
+ None => -1,
+ };
+ setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS, hops)
+}
+
+#[inline]
+pub(crate) fn set_ip_tos(fd: BorrowedFd<'_>, value: u8) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_IP, c::IP_TOS, i32::from(value))
+}
+
+#[inline]
+pub(crate) fn get_ip_tos(fd: BorrowedFd<'_>) -> io::Result<u8> {
+ let value: i32 = getsockopt(fd, c::IPPROTO_IP, c::IP_TOS)?;
+ Ok(value as u8)
+}
+
+#[inline]
+pub(crate) fn set_ip_recvtos(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS, from_bool(value))
+}
+
+#[inline]
+pub(crate) fn get_ip_recvtos(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_ipv6_recvtclass(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS, from_bool(value))
+}
+
+#[inline]
+pub(crate) fn get_ipv6_recvtclass(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_ip_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND, from_bool(value))
+}
+
+#[inline]
+pub(crate) fn get_ip_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_ipv6_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND, from_bool(value))
+}
+
+#[inline]
+pub(crate) fn get_ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn get_ip_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV4> {
+ let level = c::IPPROTO_IP;
+ let optname = c::SO_ORIGINAL_DST;
+ let mut value = MaybeUninit::<SocketAddrStorage>::uninit();
+ let mut optlen = core::mem::size_of_val(&value).try_into().unwrap();
+
+ getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
+
+ let any = unsafe { SocketAddrAny::read(value.as_ptr(), optlen as usize)? };
+ match any {
+ SocketAddrAny::V4(v4) => Ok(v4),
+ _ => unreachable!(),
+ }
+}
+
+#[inline]
+pub(crate) fn get_ipv6_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV6> {
+ let level = c::IPPROTO_IPV6;
+ let optname = c::IP6T_SO_ORIGINAL_DST;
+ let mut value = MaybeUninit::<SocketAddrStorage>::uninit();
+ let mut optlen = core::mem::size_of_val(&value).try_into().unwrap();
+
+ getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
+
+ let any = unsafe { SocketAddrAny::read(value.as_ptr(), optlen as usize)? };
+ match any {
+ SocketAddrAny::V6(v6) => Ok(v6),
+ _ => unreachable!(),
+ }
+}
+
+#[inline]
+pub(crate) fn set_ipv6_tclass(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS, value)
+}
+
+#[inline]
+pub(crate) fn get_ipv6_tclass(fd: BorrowedFd<'_>) -> io::Result<u32> {
+ getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS)
+}
+
+#[inline]
+pub(crate) fn set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY, from_bool(nodelay))
+}
+
+#[inline]
+pub(crate) fn get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_tcp_keepcnt(fd: BorrowedFd<'_>, count: u32) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT, count)
+}
+
+#[inline]
+pub(crate) fn get_tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result<u32> {
+ getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT)
+}
+
+#[inline]
+pub(crate) fn set_tcp_keepidle(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> {
+ let secs: c::c_uint = duration_to_secs(duration)?;
+ setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPIDLE, secs)
+}
+
+#[inline]
+pub(crate) fn get_tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result<Duration> {
+ let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPIDLE)?;
+ Ok(Duration::from_secs(secs as u64))
+}
+
+#[inline]
+pub(crate) fn set_tcp_keepintvl(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> {
+ let secs: c::c_uint = duration_to_secs(duration)?;
+ setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL, secs)
+}
+
+#[inline]
+pub(crate) fn get_tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result<Duration> {
+ let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL)?;
+ Ok(Duration::from_secs(secs as u64))
+}
+
+#[inline]
+pub(crate) fn set_tcp_user_timeout(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT, value)
+}
+
+#[inline]
+pub(crate) fn get_tcp_user_timeout(fd: BorrowedFd<'_>) -> io::Result<u32> {
+ getsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT)
+}
+
+#[inline]
+pub(crate) fn set_tcp_quickack(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK, from_bool(value))
+}
+
+#[inline]
+pub(crate) fn get_tcp_quickack(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result<()> {
+ let level = c::IPPROTO_TCP;
+ let optname = c::TCP_CONGESTION;
+ let optlen = value.len().try_into().unwrap();
+ setsockopt_raw(fd, level, optname, value.as_ptr(), optlen)
+}
+
+#[cfg(feature = "alloc")]
+#[inline]
+pub(crate) fn get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result<String> {
+ let level = c::IPPROTO_TCP;
+ let optname = c::TCP_CONGESTION;
+ const OPTLEN: c::socklen_t = 16;
+ let mut value = MaybeUninit::<[MaybeUninit<u8>; OPTLEN as usize]>::uninit();
+ let mut optlen = OPTLEN;
+ getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?;
+ unsafe {
+ let value = value.assume_init();
+ let slice: &[u8] = core::mem::transmute(&value[..optlen as usize]);
+ assert!(slice.iter().any(|b| *b == b'\0'));
+ Ok(
+ core::str::from_utf8(CStr::from_ptr(slice.as_ptr().cast()).to_bytes())
+ .unwrap()
+ .to_owned(),
+ )
+ }
+}
+
+#[inline]
+pub(crate) fn set_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
+ setsockopt(
+ fd,
+ c::IPPROTO_TCP,
+ c::TCP_THIN_LINEAR_TIMEOUTS,
+ from_bool(value),
+ )
+}
+
+#[inline]
+pub(crate) fn get_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::IPPROTO_TCP, c::TCP_THIN_LINEAR_TIMEOUTS).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
+ setsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK, from_bool(value))
+}
+
+#[inline]
+pub(crate) fn get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool> {
+ getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool)
+}
+
+#[inline]
+pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> {
+ getsockopt(fd, c::SOL_SOCKET, linux_raw_sys::net::SO_PEERCRED)
+}
+
+#[inline]
+fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
+ c::ip_mreq {
+ imr_multiaddr: to_imr_addr(multiaddr),
+ imr_interface: to_imr_addr(interface),
+ }
+}
+
+#[inline]
+fn to_ip_mreqn(multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32) -> c::ip_mreqn {
+ c::ip_mreqn {
+ imr_multiaddr: to_imr_addr(multiaddr),
+ imr_address: to_imr_addr(address),
+ imr_ifindex: ifindex,
+ }
+}
+
+#[inline]
+fn to_imr_source(
+ multiaddr: &Ipv4Addr,
+ interface: &Ipv4Addr,
+ sourceaddr: &Ipv4Addr,
+) -> c::ip_mreq_source {
+ c::ip_mreq_source {
+ imr_multiaddr: to_imr_addr(multiaddr).s_addr,
+ imr_interface: to_imr_addr(interface).s_addr,
+ imr_sourceaddr: to_imr_addr(sourceaddr).s_addr,
+ }
+}
+
+#[inline]
+fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr {
+ c::in_addr {
+ s_addr: u32::from_ne_bytes(addr.octets()),
+ }
+}
+
+#[inline]
+fn to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq {
+ c::ipv6_mreq {
+ ipv6mr_multiaddr: to_ipv6mr_multiaddr(multiaddr),
+ ipv6mr_ifindex: to_ipv6mr_interface(interface),
+ }
+}
+
+#[inline]
+fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr {
+ c::in6_addr {
+ in6_u: linux_raw_sys::net::in6_addr__bindgen_ty_1 {
+ u6_addr8: multiaddr.octets(),
+ },
+ }
+}
+
+#[inline]
+fn to_ipv6mr_interface(interface: u32) -> c::c_int {
+ interface as c::c_int
+}
+
+#[inline]
+fn from_bool(value: bool) -> c::c_uint {
+ c::c_uint::from(value)
+}
+
+#[inline]
+fn to_bool(value: c::c_uint) -> bool {
+ value != 0
+}
+
+/// Convert to seconds, rounding up if necessary.
+#[inline]
+fn duration_to_secs<T: TryFrom<u64>>(duration: Duration) -> io::Result<T> {
+ let mut secs = duration.as_secs();
+ if duration.subsec_nanos() != 0 {
+ secs = secs.checked_add(1).ok_or(io::Errno::INVAL)?;
+ }
+ T::try_from(secs).map_err(|_e| io::Errno::INVAL)
+}