diff --git a/src/backend/libc/net/sockopt.rs b/src/backend/libc/net/sockopt.rs index 29c87f541..de884eb6d 100644 --- a/src/backend/libc/net/sockopt.rs +++ b/src/backend/libc/net/sockopt.rs @@ -4,6 +4,8 @@ use super::ext::{in6_addr_new, in_addr_new}; use crate::backend::c; use crate::backend::conv::{borrowed_fd, ret}; use crate::fd::BorrowedFd; +#[cfg(any(linux_like, solarish, target_os = "freebsd", target_os = "fuchsia"))] +use crate::ffi::CStr; use crate::io; use crate::net::sockopt::Timeout; #[cfg(not(any( @@ -18,13 +20,35 @@ use crate::net::sockopt::Timeout; target_os = "nto", )))] use crate::net::AddressFamily; +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "openbsd", + target_os = "redox", + target_env = "newlib" +))] +use crate::net::Protocol; +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "openbsd", + target_os = "redox" +))] +use crate::net::RawProtocol; +#[cfg(linux_kernel)] +use crate::net::SocketAddrV6; use crate::net::{Ipv4Addr, Ipv6Addr, SocketType}; -use crate::utils::{as_mut_ptr, as_ptr}; +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +use crate::net::{SocketAddrAny, SocketAddrStorage, SocketAddrV4}; +use crate::utils::as_mut_ptr; #[cfg(apple)] use c::TCP_KEEPALIVE as TCP_KEEPIDLE; #[cfg(not(any(apple, target_os = "openbsd", target_os = "haiku", target_os = "nto")))] use c::TCP_KEEPIDLE; use core::mem::size_of; +use core::mem::MaybeUninit; use core::time::Duration; #[cfg(windows)] use windows_sys::Win32::Foundation::BOOL; @@ -37,25 +61,38 @@ fn getsockopt(fd: BorrowedFd<'_>, level: i32, optname: i32) -> io::Resu "Socket APIs don't ever use `bool` directly" ); + let mut value = MaybeUninit::::uninit(); + getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; + + // On Windows at least, `getsockopt` has been observed writing 1 + // byte on at least (`IPPROTO_TCP`, `TCP_NODELAY`), even though + // Windows' documentation says that should write a 4-byte `BOOL`. + // So, we initialize the memory to zeros above, and just assert + // that `getsockopt` doesn't write too many bytes here. + assert!( + optlen as usize <= size_of::(), + "unexpected getsockopt size" + ); + + unsafe { Ok(value.assume_init()) } +} + +#[inline] +fn getsockopt_raw( + fd: BorrowedFd<'_>, + level: i32, + optname: i32, + value: &mut MaybeUninit, + optlen: &mut c::socklen_t, +) -> io::Result<()> { unsafe { - let mut value = core::mem::zeroed::(); ret(c::getsockopt( borrowed_fd(fd), level, optname, - as_mut_ptr(&mut value).cast(), - &mut optlen, - ))?; - // On Windows at least, `getsockopt` has been observed writing 1 - // byte on at least (`IPPROTO_TCP`, `TCP_NODELAY`), even though - // Windows' documentation says that should write a 4-byte `BOOL`. - // So, we initialize the memory to zeros above, and just assert - // that `getsockopt` doesn't write too many bytes here. - assert!( - optlen as usize <= size_of::(), - "unexpected getsockopt size" - ); - Ok(value) + as_mut_ptr(value).cast(), + optlen, + )) } } @@ -66,13 +103,23 @@ fn setsockopt(fd: BorrowedFd<'_>, level: i32, optname: i32, value: T) - optlen as usize >= core::mem::size_of::(), "Socket APIs don't ever use `bool` directly" ); + setsockopt_raw(fd, level, optname, &value, optlen) +} +#[inline] +fn setsockopt_raw( + fd: BorrowedFd<'_>, + level: i32, + optname: i32, + ptr: *const T, + optlen: c::socklen_t, +) -> io::Result<()> { unsafe { ret(c::setsockopt( borrowed_fd(fd), level, optname, - as_ptr(&value).cast(), + ptr.cast(), optlen, )) } @@ -321,6 +368,67 @@ pub(crate) fn get_socket_oobinline(fd: BorrowedFd<'_>) -> io::Result { getsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE).map(to_bool) } +#[cfg(not(any(solarish, windows)))] +#[inline] +pub(crate) fn set_socket_reuseport(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT, from_bool(value)) +} + +#[cfg(not(any(solarish, windows)))] +#[inline] +pub(crate) fn get_socket_reuseport(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT).map(to_bool) +} + +#[cfg(target_os = "freebsd")] +#[inline] +pub(crate) fn set_socket_reuseport_lb(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT_LB, from_bool(value)) +} + +#[cfg(target_os = "freebsd")] +#[inline] +pub(crate) fn get_socket_reuseport_lb(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT_LB).map(to_bool) +} + +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "openbsd", + target_os = "redox", + target_env = "newlib" +))] +#[inline] +pub(crate) fn get_socket_protocol(fd: BorrowedFd<'_>) -> io::Result> { + getsockopt(fd, c::SOL_SOCKET, c::SO_PROTOCOL).map(|raw| { + if let Some(raw) = RawProtocol::new(raw) { + Some(Protocol::from_raw(raw)) + } else { + None + } + }) +} + +#[cfg(linux_like)] +#[inline] +pub(crate) fn get_socket_cookie(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_COOKIE) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn get_socket_incoming_cpu(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU) +} + +#[cfg(target_os = "linux")] +#[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) @@ -604,6 +712,76 @@ pub(crate) fn get_ipv6_recvtclass(fd: BorrowedFd<'_>) -> io::Result { getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS).map(to_bool) } +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[inline] +pub(crate) fn set_ip_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND, from_bool(value)) +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[inline] +pub(crate) fn get_ip_freebind(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND).map(to_bool) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn set_ipv6_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND, from_bool(value)) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn get_ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND).map(to_bool) +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[inline] +pub(crate) fn get_ip_original_dst(fd: BorrowedFd<'_>) -> io::Result { + let level = c::IPPROTO_IP; + let optname = c::SO_ORIGINAL_DST; + let mut value = MaybeUninit::::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!(), + } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn get_ipv6_original_dst(fd: BorrowedFd<'_>) -> io::Result { + let level = c::IPPROTO_IPV6; + let optname = c::IP6T_SO_ORIGINAL_DST; + let mut value = MaybeUninit::::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!(), + } +} + +#[cfg(not(solarish))] +#[inline] +pub(crate) fn set_ipv6_tclass(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS, value) +} + +#[cfg(not(solarish))] +#[inline] +pub(crate) fn get_ipv6_tclass(fd: BorrowedFd<'_>) -> io::Result { + 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)) @@ -666,6 +844,76 @@ pub(crate) fn get_tcp_user_timeout(fd: BorrowedFd<'_>) -> io::Result { getsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT) } +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +pub(crate) fn set_tcp_quickack(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK, from_bool(value)) +} + +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +pub(crate) fn get_tcp_quickack(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK).map(to_bool) +} + +#[cfg(any(linux_like, solarish, target_os = "freebsd", target_os = "fuchsia"))] +#[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(any(linux_like, solarish, target_os = "freebsd", target_os = "fuchsia"))] +#[inline] +pub(crate) fn get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result { + let level = c::IPPROTO_TCP; + let optname = c::TCP_CONGESTION; + const OPTLEN: c::socklen_t = 16; + let mut value = MaybeUninit::<[MaybeUninit; 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]); + Ok( + core::str::from_utf8(CStr::from_bytes_until_nul(slice).unwrap().to_bytes()) + .unwrap() + .to_owned(), + ) + } +} + +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[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), + ) +} + +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +pub(crate) fn get_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_THIN_LINEAR_TIMEOUTS).map(to_bool) +} + +#[cfg(any(linux_like, solarish, target_os = "fuchsia"))] +#[inline] +pub(crate) fn set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK, from_bool(value)) +} + +#[cfg(any(linux_like, solarish, target_os = "fuchsia"))] +#[inline] +pub(crate) fn get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool) +} + #[inline] fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq { c::ip_mreq { diff --git a/src/backend/linux_raw/c.rs b/src/backend/linux_raw/c.rs index 80fbbc141..73467534b 100644 --- a/src/backend/linux_raw/c.rs +++ b/src/backend/linux_raw/c.rs @@ -59,21 +59,27 @@ pub(crate) use linux_raw_sys::{ AF_NETBEUI, AF_NETLINK, AF_NETROM, AF_PACKET, AF_PHONET, AF_PPPOX, AF_RDS, AF_ROSE, AF_RXRPC, AF_SECURITY, AF_SNA, AF_TIPC, AF_UNIX, AF_UNSPEC, AF_WANPIPE, AF_X25, IPPROTO_FRAGMENT, IPPROTO_ICMPV6, IPPROTO_MH, IPPROTO_ROUTING, IPV6_ADD_MEMBERSHIP, - IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_HOPS, IPV6_MULTICAST_LOOP, IPV6_RECVTCLASS, - IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP, IP_ADD_SOURCE_MEMBERSHIP, - IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, - IP_RECVTOS, IP_TOS, IP_TTL, MSG_CMSG_CLOEXEC, MSG_CONFIRM, MSG_DONTROUTE, MSG_DONTWAIT, - MSG_EOR, MSG_ERRQUEUE, MSG_MORE, MSG_NOSIGNAL, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, - SCM_CREDENTIALS, SCM_RIGHTS, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM, SOCK_RAW, SOCK_RDM, - SOCK_SEQPACKET, SOCK_STREAM, SOL_SOCKET, SO_ACCEPTCONN, SO_BROADCAST, SO_DOMAIN, SO_ERROR, - SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_PASSCRED, SO_RCVBUF, SO_RCVTIMEO_NEW, - SO_RCVTIMEO_NEW as SO_RCVTIMEO, SO_RCVTIMEO_OLD, SO_REUSEADDR, SO_SNDBUF, SO_SNDTIMEO_NEW, - SO_SNDTIMEO_NEW as SO_SNDTIMEO, SO_SNDTIMEO_OLD, SO_TYPE, TCP_KEEPCNT, TCP_KEEPIDLE, - TCP_KEEPINTVL, TCP_NODELAY, TCP_USER_TIMEOUT, + IPV6_DROP_MEMBERSHIP, IPV6_FREEBIND, IPV6_MULTICAST_HOPS, IPV6_MULTICAST_LOOP, + IPV6_RECVTCLASS, IPV6_TCLASS, IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP, + IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, IP_FREEBIND, + IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_RECVTOS, IP_TOS, IP_TTL, MSG_CMSG_CLOEXEC, + MSG_CONFIRM, MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, MSG_ERRQUEUE, MSG_MORE, MSG_NOSIGNAL, + MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, SCM_CREDENTIALS, SCM_RIGHTS, SHUT_RD, SHUT_RDWR, + SHUT_WR, SOCK_DGRAM, SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET, SOCK_STREAM, SOL_SOCKET, + SO_ACCEPTCONN, SO_BROADCAST, SO_COOKIE, SO_DOMAIN, SO_ERROR, SO_INCOMING_CPU, SO_KEEPALIVE, + SO_LINGER, SO_OOBINLINE, SO_PASSCRED, SO_PROTOCOL, SO_RCVBUF, SO_RCVTIMEO_NEW, + SO_RCVTIMEO_NEW as SO_RCVTIMEO, SO_RCVTIMEO_OLD, SO_REUSEADDR, SO_REUSEPORT, SO_SNDBUF, + SO_SNDTIMEO_NEW, SO_SNDTIMEO_NEW as SO_SNDTIMEO, SO_SNDTIMEO_OLD, SO_TYPE, TCP_CONGESTION, + TCP_CORK, TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_NODELAY, TCP_QUICKACK, + TCP_THIN_LINEAR_TIMEOUTS, TCP_USER_TIMEOUT, }, netlink::*, }; +// TODO: Modify linux-raw-sys to include these. +pub(crate) const IP6T_SO_ORIGINAL_DST: u32 = 80; +pub(crate) const SO_ORIGINAL_DST: u32 = 80; + // Cast away bindgen's `enum` type to make these consistent with the other // `setsockopt`/`getsockopt` level values. #[cfg(feature = "net")] diff --git a/src/backend/linux_raw/net/sockopt.rs b/src/backend/linux_raw/net/sockopt.rs index 24cabbe5e..6eb57fd75 100644 --- a/src/backend/linux_raw/net/sockopt.rs +++ b/src/backend/linux_raw/net/sockopt.rs @@ -6,11 +6,15 @@ #![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] use crate::backend::c; -use crate::backend::conv::{by_mut, by_ref, c_uint, ret, socklen_t}; +use crate::backend::conv::{by_mut, c_uint, ret, socklen_t}; use crate::fd::BorrowedFd; +use crate::ffi::CStr; use crate::io; use crate::net::sockopt::Timeout; -use crate::net::{AddressFamily, Ipv4Addr, Ipv6Addr, SocketType}; +use crate::net::{ + AddressFamily, Ipv4Addr, Ipv6Addr, Protocol, RawProtocol, SocketAddrAny, SocketAddrStorage, + SocketAddrV4, SocketAddrV6, SocketType, +}; use core::mem::MaybeUninit; use core::time::Duration; use linux_raw_sys::general::{__kernel_old_timeval, __kernel_sock_timeval}; @@ -23,30 +27,42 @@ use { #[inline] fn getsockopt(fd: BorrowedFd<'_>, level: u32, optname: u32) -> io::Result { - let mut optlen = core::mem::size_of::(); + let mut optlen: c::socklen_t = core::mem::size_of::().try_into().unwrap(); debug_assert!( optlen as usize >= core::mem::size_of::(), "Socket APIs don't ever use `bool` directly" ); + let mut value = MaybeUninit::::uninit(); + getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; + + assert_eq!( + optlen as usize, + core::mem::size_of::(), + "unexpected getsockopt size" + ); + + unsafe { Ok(value.assume_init()) } +} + +#[inline] +fn getsockopt_raw( + fd: BorrowedFd<'_>, + level: u32, + optname: u32, + value: &mut MaybeUninit, + optlen: &mut c::socklen_t, +) -> io::Result<()> { #[cfg(not(target_arch = "x86"))] unsafe { - let mut value = MaybeUninit::::uninit(); ret(syscall!( __NR_getsockopt, fd, c_uint(level), c_uint(optname), - &mut value, - by_mut(&mut optlen) - ))?; - - assert_eq!( - optlen as usize, - core::mem::size_of::(), - "unexpected getsockopt size" - ); - Ok(value.assume_init()) + value, + by_mut(optlen) + )) } #[cfg(target_arch = "x86")] unsafe { @@ -61,13 +77,7 @@ fn getsockopt(fd: BorrowedFd<'_>, level: u32, optname: u32) -> io::Resu (&mut value).into(), by_mut(&mut optlen), ]) - ))?; - assert_eq!( - optlen as usize, - core::mem::size_of::(), - "unexpected getsockopt size" - ); - Ok(value.assume_init()) + )) } } @@ -78,7 +88,17 @@ fn setsockopt(fd: BorrowedFd<'_>, level: u32, optname: u32, value: T) - optlen as usize >= core::mem::size_of::(), "Socket APIs don't ever use `bool` directly" ); + setsockopt_raw(fd, level, optname, &value, optlen) +} +#[inline] +fn setsockopt_raw( + fd: BorrowedFd<'_>, + level: u32, + optname: u32, + ptr: *const T, + optlen: c::socklen_t, +) -> io::Result<()> { #[cfg(not(target_arch = "x86"))] unsafe { ret(syscall_readonly!( @@ -86,7 +106,7 @@ fn setsockopt(fd: BorrowedFd<'_>, level: u32, optname: u32, value: T) - fd, c_uint(level), c_uint(optname), - by_ref(&value), + ptr, socklen_t(optlen) )) } @@ -99,7 +119,7 @@ fn setsockopt(fd: BorrowedFd<'_>, level: u32, optname: u32, value: T) - fd.into(), c_uint(level), c_uint(optname), - by_ref(&value), + ptr, socklen_t(optlen), ]) )) @@ -360,6 +380,42 @@ pub(crate) fn get_socket_oobinline(fd: BorrowedFd<'_>) -> io::Result { 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 { + getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT).map(to_bool) +} + +#[inline] +pub(crate) fn get_socket_protocol(fd: BorrowedFd<'_>) -> io::Result> { + getsockopt(fd, c::SOL_SOCKET, c::SO_PROTOCOL).map(|raw: u32| { + if let Some(raw) = RawProtocol::new(raw) { + Some(Protocol::from_raw(raw)) + } else { + None + } + }) +} + +#[inline] +pub(crate) fn get_socket_cookie(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::SOL_SOCKET, c::SO_COOKIE) +} + +#[inline] +pub(crate) fn get_socket_incoming_cpu(fd: BorrowedFd<'_>) -> io::Result { + 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) @@ -559,6 +615,68 @@ pub(crate) fn get_ipv6_recvtclass(fd: BorrowedFd<'_>) -> io::Result { 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 { + 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 { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND).map(to_bool) +} + +#[inline] +pub(crate) fn get_ip_original_dst(fd: BorrowedFd<'_>) -> io::Result { + let level = c::IPPROTO_IP; + let optname = c::SO_ORIGINAL_DST; + let mut value = MaybeUninit::::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 { + let level = c::IPPROTO_IPV6; + let optname = c::IP6T_SO_ORIGINAL_DST; + let mut value = MaybeUninit::::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 { + 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)) @@ -613,6 +731,67 @@ pub(crate) fn get_tcp_user_timeout(fd: BorrowedFd<'_>) -> io::Result { 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 { + 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) +} + +#[inline] +pub(crate) fn get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result { + let level = c::IPPROTO_TCP; + let optname = c::TCP_CONGESTION; + const OPTLEN: c::socklen_t = 16; + let mut value = MaybeUninit::<[MaybeUninit; 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]); + Ok( + core::str::from_utf8(CStr::from_bytes_until_nul(slice).unwrap().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 { + 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 { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool) +} #[inline] fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq { c::ip_mreq { diff --git a/src/net/sockopt.rs b/src/net/sockopt.rs index ea736c2af..f13729452 100644 --- a/src/net/sockopt.rs +++ b/src/net/sockopt.rs @@ -155,6 +155,19 @@ target_os = "nto", )))] use crate::net::AddressFamily; +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "openbsd", + target_os = "redox", + target_env = "newlib" +))] +use crate::net::Protocol; +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +use crate::net::SocketAddrV4; +#[cfg(linux_kernel)] +use crate::net::SocketAddrV6; use crate::net::{Ipv4Addr, Ipv6Addr, SocketType}; use crate::{backend, io}; use backend::c; @@ -207,7 +220,7 @@ pub fn get_socket_reuseaddr(fd: Fd) -> io::Result { backend::net::sockopt::get_socket_reuseaddr(fd.as_fd()) } -/// `setsockopt(fd, SOL_SOCKET, SO_BROADCAST, broadcast)` +/// `setsockopt(fd, SOL_SOCKET, SO_BROADCAST, value)` /// /// See the [module-level documentation] for more. /// @@ -275,7 +288,7 @@ pub fn get_socket_passcred(fd: Fd) -> io::Result { backend::net::sockopt::get_socket_passcred(fd.as_fd()) } -/// `setsockopt(fd, SOL_SOCKET, id, timeout)`—Set the sending or receiving +/// `setsockopt(fd, SOL_SOCKET, id, value)`—Set the sending or receiving /// timeout. /// /// See the [module-level documentation] for more. @@ -287,9 +300,9 @@ pub fn get_socket_passcred(fd: Fd) -> io::Result { pub fn set_socket_timeout( fd: Fd, id: Timeout, - timeout: Option, + value: Option, ) -> io::Result<()> { - backend::net::sockopt::set_socket_timeout(fd.as_fd(), id, timeout) + backend::net::sockopt::set_socket_timeout(fd.as_fd(), id, value) } /// `getsockopt(fd, SOL_SOCKET, id)`—Get the sending or receiving timeout. @@ -461,6 +474,110 @@ pub fn get_socket_oobinline(fd: Fd) -> io::Result { backend::net::sockopt::get_socket_oobinline(fd.as_fd()) } +/// `setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, value)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_socket_-and-set_socket_-functions +#[cfg(not(any(solarish, windows)))] +#[cfg(not(windows))] +#[inline] +#[doc(alias = "SO_REUSEPORT")] +pub fn set_socket_reuseport(fd: Fd, value: bool) -> io::Result<()> { + backend::net::sockopt::set_socket_reuseport(fd.as_fd(), value) +} + +/// `getsockopt(fd, SOL_SOCKET, SO_REUSEPORT)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_socket_-and-set_socket_-functions +#[cfg(not(any(solarish, windows)))] +#[inline] +#[doc(alias = "SO_REUSEPORT")] +pub fn get_socket_reuseport(fd: Fd) -> io::Result { + backend::net::sockopt::get_socket_reuseport(fd.as_fd()) +} + +/// `setsockopt(fd, SOL_SOCKET, SO_REUSEPORT_LB, value)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_socket_-and-set_socket_-functions +#[cfg(target_os = "freebsd")] +#[inline] +#[doc(alias = "SO_REUSEPORT_LB")] +pub fn set_socket_reuseport_lb(fd: Fd, value: bool) -> io::Result<()> { + backend::net::sockopt::set_socket_reuseport_lb(fd.as_fd(), value) +} + +/// `getsockopt(fd, SOL_SOCKET, SO_REUSEPORT_LB)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_socket_-and-set_socket_-functions +#[cfg(target_os = "freebsd")] +#[inline] +#[doc(alias = "SO_REUSEPORT_LB")] +pub fn get_socket_reuseport_lb(fd: Fd) -> io::Result { + backend::net::sockopt::get_socket_reuseport_lb(fd.as_fd()) +} + +/// `getsockopt(fd, SOL_SOCKET, SO_PROTOCOL)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_socket_-and-set_socket_-functions +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "openbsd", + target_os = "redox", + target_env = "newlib" +))] +#[inline] +#[doc(alias = "SO_PROTOCOL")] +pub fn get_socket_protocol(fd: Fd) -> io::Result> { + backend::net::sockopt::get_socket_protocol(fd.as_fd()) +} + +/// `getsockopt(fd, SOL_SOCKET, SO_COOKIE)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_socket_-and-set_socket_-functions +#[cfg(linux_like)] +#[inline] +#[doc(alias = "SO_COOKIE")] +pub fn get_socket_cookie(fd: Fd) -> io::Result { + backend::net::sockopt::get_socket_cookie(fd.as_fd()) +} + +/// `getsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_socket_-and-set_socket_-functions +#[cfg(target_os = "linux")] +#[inline] +#[doc(alias = "SO_INCOMING_CPU")] +pub fn get_socket_incoming_cpu(fd: Fd) -> io::Result { + backend::net::sockopt::get_socket_incoming_cpu(fd.as_fd()) +} + +/// `setsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, value)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_socket_-and-set_socket_-functions +#[cfg(target_os = "linux")] +#[inline] +#[doc(alias = "SO_INCOMING_CPU")] +pub fn set_socket_incoming_cpu(fd: Fd, value: u32) -> io::Result<()> { + backend::net::sockopt::set_socket_incoming_cpu(fd.as_fd(), value) +} + /// `setsockopt(fd, IPPROTO_IP, IP_TTL, value)` /// /// See the [module-level documentation] for more. @@ -483,7 +600,7 @@ pub fn get_ip_ttl(fd: Fd) -> io::Result { backend::net::sockopt::get_ip_ttl(fd.as_fd()) } -/// `setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, only_v6)` +/// `setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, value)` /// /// See the [module-level documentation] for more. /// @@ -664,7 +781,7 @@ pub fn set_ip_add_membership_with_ifindex( ) } -/// `setsockopt(fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP)` +/// `setsockopt(fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, value)` /// /// See the [module-level documentation] for more. /// @@ -686,7 +803,7 @@ pub fn set_ip_add_source_membership( ) } -/// `setsockopt(fd, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP)` +/// `setsockopt(fd, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, value)` /// /// See the [module-level documentation] for more. /// @@ -889,6 +1006,108 @@ pub fn get_ipv6_recvtclass(fd: Fd) -> io::Result { backend::net::sockopt::get_ipv6_recvtclass(fd.as_fd()) } +/// `setsockopt(fd, IPPROTO_IP, IP_FREEBIND, value)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_ipv6_-and-set_ipv6_-functions +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[inline] +#[doc(alias = "IP_FREEBIND")] +pub fn set_ip_freebind(fd: Fd, value: bool) -> io::Result<()> { + backend::net::sockopt::set_ip_freebind(fd.as_fd(), value) +} + +/// `getsockopt(fd, IPPROTO_IP, IP_FREEBIND)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_ipv6_-and-set_ipv6_-functions +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[inline] +#[doc(alias = "IP_FREEBIND")] +pub fn get_ip_freebind(fd: Fd) -> io::Result { + backend::net::sockopt::get_ip_freebind(fd.as_fd()) +} + +/// `setsockopt(fd, IPPROTO_IPV6, IPV6_FREEBIND, value)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_ipv6_-and-set_ipv6_-functions +#[cfg(linux_kernel)] +#[inline] +#[doc(alias = "IPV6_FREEBIND")] +pub fn set_ipv6_freebind(fd: Fd, value: bool) -> io::Result<()> { + backend::net::sockopt::set_ipv6_freebind(fd.as_fd(), value) +} + +/// `getsockopt(fd, IPPROTO_IPV6, IPV6_FREEBIND)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_ipv6_-and-set_ipv6_-functions +#[cfg(linux_kernel)] +#[inline] +#[doc(alias = "IPV6_FREEBIND")] +pub fn get_ipv6_freebind(fd: Fd) -> io::Result { + backend::net::sockopt::get_ipv6_freebind(fd.as_fd()) +} + +/// `getsockopt(fd, IPPROTO_IP, SO_ORIGINAL_DST)` +/// +/// Even though this corresponnds to a `SO_*` constant, it is an +/// `IPPROTO_IP` option. +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_ipv6_-and-set_ipv6_-functions +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[inline] +#[doc(alias = "SO_ORIGINAL_DST")] +pub fn get_ip_original_dst(fd: Fd) -> io::Result { + backend::net::sockopt::get_ip_original_dst(fd.as_fd()) +} + +/// `getsockopt(fd, IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST)` +/// +/// Even though this corresponnds to a `IP6T_*` constant, it is an +/// `IPPROTO_IPV6` option. +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_ipv6_-and-set_ipv6_-functions +#[cfg(linux_kernel)] +#[inline] +#[doc(alias = "IP6T_SO_ORIGINAL_DST")] +pub fn get_ipv6_original_dst(fd: Fd) -> io::Result { + backend::net::sockopt::get_ipv6_original_dst(fd.as_fd()) +} + +/// `setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, value)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_ipv6_-and-set_ipv6_-functions +#[cfg(not(solarish))] +#[inline] +#[doc(alias = "IPV6_TCLASS")] +pub fn set_ipv6_tclass(fd: Fd, value: u32) -> io::Result<()> { + backend::net::sockopt::set_ipv6_tclass(fd.as_fd(), value) +} + +/// `getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_ipv6_-and-set_ipv6_-functions +#[cfg(not(solarish))] +#[inline] +#[doc(alias = "IPV6_TCLASS")] +pub fn get_ipv6_tclass(fd: Fd) -> io::Result { + backend::net::sockopt::get_ipv6_tclass(fd.as_fd()) +} + /// `setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, value)` /// /// See the [module-level documentation] for more. @@ -916,8 +1135,8 @@ pub fn get_tcp_nodelay(fd: Fd) -> io::Result { /// See the [module-level documentation] for more. /// /// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions -#[inline] #[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +#[inline] #[doc(alias = "TCP_KEEPCNT")] pub fn set_tcp_keepcnt(fd: Fd, value: u32) -> io::Result<()> { backend::net::sockopt::set_tcp_keepcnt(fd.as_fd(), value) @@ -928,8 +1147,8 @@ pub fn set_tcp_keepcnt(fd: Fd, value: u32) -> io::Result<()> { /// See the [module-level documentation] for more. /// /// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions -#[inline] #[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +#[inline] #[doc(alias = "TCP_KEEPCNT")] pub fn get_tcp_keepcnt(fd: Fd) -> io::Result { backend::net::sockopt::get_tcp_keepcnt(fd.as_fd()) @@ -942,8 +1161,8 @@ pub fn get_tcp_keepcnt(fd: Fd) -> io::Result { /// See the [module-level documentation] for more. /// /// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions -#[inline] #[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +#[inline] #[doc(alias = "TCP_KEEPIDLE")] pub fn set_tcp_keepidle(fd: Fd, value: Duration) -> io::Result<()> { backend::net::sockopt::set_tcp_keepidle(fd.as_fd(), value) @@ -956,8 +1175,8 @@ pub fn set_tcp_keepidle(fd: Fd, value: Duration) -> io::Result<()> { /// See the [module-level documentation] for more. /// /// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions -#[inline] #[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +#[inline] #[doc(alias = "TCP_KEEPIDLE")] pub fn get_tcp_keepidle(fd: Fd) -> io::Result { backend::net::sockopt::get_tcp_keepidle(fd.as_fd()) @@ -968,8 +1187,8 @@ pub fn get_tcp_keepidle(fd: Fd) -> io::Result { /// See the [module-level documentation] for more. /// /// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions -#[inline] #[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +#[inline] #[doc(alias = "TCP_KEEPINTVL")] pub fn set_tcp_keepintvl(fd: Fd, value: Duration) -> io::Result<()> { backend::net::sockopt::set_tcp_keepintvl(fd.as_fd(), value) @@ -980,8 +1199,8 @@ pub fn set_tcp_keepintvl(fd: Fd, value: Duration) -> io::Result<()> { /// See the [module-level documentation] for more. /// /// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions -#[inline] #[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +#[inline] #[doc(alias = "TCP_KEEPINTVL")] pub fn get_tcp_keepintvl(fd: Fd) -> io::Result { backend::net::sockopt::get_tcp_keepintvl(fd.as_fd()) @@ -992,8 +1211,8 @@ pub fn get_tcp_keepintvl(fd: Fd) -> io::Result { /// See the [module-level documentation] for more. /// /// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions -#[inline] #[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] #[doc(alias = "TCP_USER_TIMEOUT")] pub fn set_tcp_user_timeout(fd: Fd, value: u32) -> io::Result<()> { backend::net::sockopt::set_tcp_user_timeout(fd.as_fd(), value) @@ -1004,13 +1223,109 @@ pub fn set_tcp_user_timeout(fd: Fd, value: u32) -> io::Result<()> { /// See the [module-level documentation] for more. /// /// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions -#[inline] #[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] #[doc(alias = "TCP_USER_TIMEOUT")] pub fn get_tcp_user_timeout(fd: Fd) -> io::Result { backend::net::sockopt::get_tcp_user_timeout(fd.as_fd()) } +/// `setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, value)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +#[doc(alias = "TCP_QUICKACK")] +pub fn set_tcp_quickack(fd: Fd, value: bool) -> io::Result<()> { + backend::net::sockopt::set_tcp_quickack(fd.as_fd(), value) +} + +/// `getsockopt(fd, IPPROTO_TCP, TCP_QUICKACK)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +#[doc(alias = "TCP_QUICKACK")] +pub fn get_tcp_quickack(fd: Fd) -> io::Result { + backend::net::sockopt::get_tcp_quickack(fd.as_fd()) +} + +/// `setsockopt(fd, IPPROTO_TCP, TCP_CONGESTION, value)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions +#[cfg(any(linux_like, solarish, target_os = "freebsd", target_os = "fuchsia"))] +#[inline] +#[doc(alias = "TCP_CONGESTION")] +pub fn set_tcp_congestion(fd: Fd, value: &str) -> io::Result<()> { + backend::net::sockopt::set_tcp_congestion(fd.as_fd(), value) +} + +/// `getsockopt(fd, IPPROTO_TCP, TCP_CONGESTION)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions +#[cfg(any(linux_like, solarish, target_os = "freebsd", target_os = "fuchsia"))] +#[inline] +#[doc(alias = "TCP_CONGESTION")] +pub fn get_tcp_congestion(fd: Fd) -> io::Result { + backend::net::sockopt::get_tcp_congestion(fd.as_fd()) +} + +/// `setsockopt(fd, IPPROTO_TCP, TCP_THIN_LINEAR_TIMEOUTS, value)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +#[doc(alias = "TCP_THIN_LINEAR_TIMEOUTS")] +pub fn set_tcp_thin_linear_timeouts(fd: Fd, value: bool) -> io::Result<()> { + backend::net::sockopt::set_tcp_thin_linear_timeouts(fd.as_fd(), value) +} + +/// `getsockopt(fd, IPPROTO_TCP, TCP_THIN_LINEAR_TIMEOUTS)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +#[doc(alias = "TCP_THIN_LINEAR_TIMEOUTS")] +pub fn get_tcp_thin_linear_timeouts(fd: Fd) -> io::Result { + backend::net::sockopt::get_tcp_thin_linear_timeouts(fd.as_fd()) +} + +/// `setsockopt(fd, IPPROTO_TCP, TCP_CORK, value)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions +#[cfg(any(linux_like, solarish, target_os = "fuchsia"))] +#[inline] +#[doc(alias = "TCP_CORK")] +pub fn set_tcp_cork(fd: Fd, value: bool) -> io::Result<()> { + backend::net::sockopt::set_tcp_cork(fd.as_fd(), value) +} + +/// `getsockopt(fd, IPPROTO_TCP, TCP_CORK)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_tcp_-and-set_tcp_-functions +#[cfg(any(linux_like, solarish, target_os = "fuchsia"))] +#[inline] +#[doc(alias = "TCP_CORK")] +pub fn get_tcp_cork(fd: Fd) -> io::Result { + backend::net::sockopt::get_tcp_cork(fd.as_fd()) +} + #[test] fn test_sizes() { use c::c_int; diff --git a/tests/net/sockopt.rs b/tests/net/sockopt.rs index 69b7be6aa..5a71d84ae 100644 --- a/tests/net/sockopt.rs +++ b/tests/net/sockopt.rs @@ -1,6 +1,7 @@ use rustix::fd::OwnedFd; +use rustix::io; use rustix::net::sockopt; -use rustix::net::{AddressFamily, SocketType}; +use rustix::net::{ipproto, AddressFamily, SocketType}; use std::time::Duration; // Test `socket` socket options. @@ -10,6 +11,10 @@ fn test_sockopts_socket(s: &OwnedFd) { .unwrap() .is_none()); assert_eq!(sockopt::get_socket_type(&s).unwrap(), SocketType::STREAM); + assert_eq!( + sockopt::get_socket_protocol(&s).unwrap(), + Some(ipproto::TCP) + ); assert!(!sockopt::get_socket_reuseaddr(&s).unwrap()); #[cfg(not(windows))] assert!(!sockopt::get_socket_broadcast(&s).unwrap()); @@ -87,7 +92,7 @@ fn test_sockopts_socket(s: &OwnedFd) { sockopt::set_socket_linger(&s, Some(Duration::new(1, 1))).unwrap(); // Check that we have a linger of at least the time we set. - assert!(dbg!(sockopt::get_socket_linger(&s).unwrap().unwrap()) >= Duration::new(1, 1)); + assert!(sockopt::get_socket_linger(&s).unwrap().unwrap() >= Duration::new(1, 1)); #[cfg(linux_kernel)] { @@ -120,6 +125,37 @@ fn test_sockopts_socket(s: &OwnedFd) { // Check that the oobinline flag is set. assert!(sockopt::get_socket_oobinline(&s).unwrap()); + + // Check the initial value of SO_REUSEPORT, set it, and check it. + #[cfg(not(any(solarish, windows)))] + { + assert!(!sockopt::get_socket_reuseport(&s).unwrap()); + sockopt::set_socket_reuseport(&s, true).unwrap(); + assert!(sockopt::get_socket_reuseport(&s).unwrap()); + } + + // Check the initial value of SO_REUSEPORT_LB, set it, and check it. + #[cfg(target_os = "freebsd")] + { + assert_eq!(!sockopt::get_socket_reuseport_lb(&s).unwrap()); + sockopt::set_socket_reuseport_lb(&s, true).unwrap(); + assert_eq!(sockopt::get_socket_reuseport_lb(&s).unwrap()); + } + + // Not much we can check with `get_socket_cookie`, but make sure we can + // call it and that it returns the same value if called twice. + assert_eq!( + sockopt::get_socket_cookie(&s).unwrap(), + sockopt::get_socket_cookie(&s).unwrap() + ); + + // Check the initial value of SO_INCOMING_CPU, set it, and check it. + #[cfg(target_os = "linux")] + { + assert_eq!(sockopt::get_socket_incoming_cpu(&s).unwrap(), u32::MAX); + sockopt::set_socket_incoming_cpu(&s, 3).unwrap(); + assert_eq!(sockopt::get_socket_incoming_cpu(&s).unwrap(), 3); + } } // Test `tcp` socket options. @@ -164,6 +200,39 @@ fn test_sockopts_tcp(s: &OwnedFd) { Duration::from_secs(61) ); } + + // Check the initial value of TCP_QUICKACK, set it, and check it. + #[cfg(any(linux_like, target_os = "fuchsia"))] + { + assert!(sockopt::get_tcp_quickack(&s).unwrap()); + sockopt::set_tcp_quickack(&s, false).unwrap(); + assert!(!sockopt::get_tcp_quickack(&s).unwrap()); + } + + // Check the initial value of TCP_CONGESTION, set it, and check it. + #[cfg(any(linux_like, solarish, target_os = "freebsd", target_os = "fuchsia"))] + { + assert_eq!(sockopt::get_tcp_congestion(&s).unwrap(), "cubic"); + sockopt::set_tcp_congestion(&s, "reno").unwrap(); + assert_eq!(sockopt::get_tcp_congestion(&s).unwrap(), "reno"); + } + + // Check the initial value of TCP_THIN_LINEAR_TIMEOUTS, set it, and check + // it. + #[cfg(any(linux_like, target_os = "fuchsia"))] + { + assert!(!sockopt::get_tcp_thin_linear_timeouts(&s).unwrap()); + sockopt::set_tcp_thin_linear_timeouts(&s, true).unwrap(); + assert!(sockopt::get_tcp_thin_linear_timeouts(&s).unwrap()); + } + + // Check the initial value of TCP_CORK, set it, and check it. + #[cfg(any(linux_like, solarish, target_os = "fuchsia"))] + { + assert!(!sockopt::get_tcp_cork(&s).unwrap()); + sockopt::set_tcp_cork(&s, true).unwrap(); + assert!(sockopt::get_tcp_cork(&s).unwrap()); + } } #[test] @@ -233,6 +302,20 @@ fn test_sockopts_ipv4() { assert!(sockopt::get_ip_recvtos(&s).unwrap()); } + // Check the initial value of IP_FREEBIND, set it, and check it. + #[cfg(any(linux_kernel, target_os = "fuchsia"))] + { + assert!(!sockopt::get_ip_freebind(&s).unwrap()); + sockopt::set_ip_freebind(&s, true).unwrap(); + assert!(sockopt::get_ip_freebind(&s).unwrap()); + } + + // Check that we can query SO_ORIGINAL_DST. + #[cfg(any(linux_kernel, target_os = "fuchsia"))] + { + assert_eq!(sockopt::get_ip_original_dst(&s), Err(io::Errno::NOPROTOOPT)); + } + test_sockopts_tcp(&s); } @@ -325,5 +408,30 @@ fn test_sockopts_ipv6() { assert!(sockopt::get_ipv6_recvtclass(&s).unwrap()); } + // Check the initial value of IPV6_FREEBIND, set it, and check it. + #[cfg(linux_kernel)] + { + assert!(!sockopt::get_ipv6_freebind(&s).unwrap()); + sockopt::set_ipv6_freebind(&s, true).unwrap(); + assert!(sockopt::get_ipv6_freebind(&s).unwrap()); + } + + // Check the initial value of IPV6_TCLASS, set it, and check it. + #[cfg(not(solarish))] + { + assert_eq!(sockopt::get_ipv6_tclass(&s).unwrap(), 0); + sockopt::set_ipv6_tclass(&s, 12).unwrap(); + assert_eq!(sockopt::get_ipv6_tclass(&s).unwrap(), 12); + } + + // Check that we can query IP6T_SO_ORIGINAL_DST. + #[cfg(linux_kernel)] + { + assert_eq!( + sockopt::get_ipv6_original_dst(&s), + Err(io::Errno::NOPROTOOPT) + ); + } + test_sockopts_tcp(&s); }