From ebf568293cdd9cc696d323f4ea3d622d38b6e646 Mon Sep 17 00:00:00 2001 From: Dave Bakker Date: Sun, 17 Sep 2023 21:58:57 +0200 Subject: [PATCH] Add TCP_KEEPCNT, TCP_KEEPIDLE and TCP_KEEPINTVL --- src/backend/libc/net/syscalls.rs | 54 ++++- src/backend/linux_raw/net/syscalls.rs | 52 ++++- src/net/sockopt.rs | 280 ++++++++++++++++++++++++++ tests/net/sockopt.rs | 13 ++ 4 files changed, 388 insertions(+), 11 deletions(-) diff --git a/src/backend/libc/net/syscalls.rs b/src/backend/libc/net/syscalls.rs index 954f7f349..5a0175ad7 100644 --- a/src/backend/libc/net/syscalls.rs +++ b/src/backend/libc/net/syscalls.rs @@ -536,6 +536,10 @@ pub(crate) mod sockopt { use crate::net::AddressFamily; use crate::net::{Ipv4Addr, Ipv6Addr, SocketType}; use crate::utils::as_mut_ptr; + #[cfg(apple)] + use c::TCP_KEEPALIVE as TCP_KEEPIDLE; + #[cfg(not(apple))] + use c::TCP_KEEPIDLE; use core::time::Duration; #[cfg(windows)] use windows_sys::Win32::Foundation::BOOL; @@ -640,11 +644,7 @@ pub(crate) mod sockopt { ) -> io::Result<()> { // Convert `linger` to seconds, rounding up. let l_linger = if let Some(linger) = linger { - let mut l_linger = linger.as_secs(); - if linger.subsec_nanos() != 0 { - l_linger = l_linger.checked_add(1).ok_or(io::Errno::INVAL)?; - } - l_linger.try_into().map_err(|_e| io::Errno::INVAL)? + duration_to_secs(linger)? } else { 0 }; @@ -1039,6 +1039,40 @@ pub(crate) mod sockopt { getsockopt(fd, c::IPPROTO_TCP as _, c::TCP_NODELAY).map(to_bool) } + #[inline] + pub(crate) fn set_tcp_keepcnt(fd: BorrowedFd<'_>, count: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP as _, c::TCP_KEEPCNT, count) + } + + #[inline] + pub(crate) fn get_tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_TCP as _, 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 as _, TCP_KEEPIDLE, secs) + } + + #[inline] + pub(crate) fn get_tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result { + let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP as _, 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 as _, c::TCP_KEEPINTVL, secs) + } + + #[inline] + pub(crate) fn get_tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result { + let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP as _, c::TCP_KEEPINTVL)?; + Ok(Duration::from_secs(secs as u64)) + } + #[inline] fn to_imr(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq { c::ip_mreq { @@ -1099,4 +1133,14 @@ pub(crate) mod sockopt { fn to_bool(value: SocketBool) -> bool { value.0 != 0 } + + /// Convert to seconds, rounding up if necessary. + #[inline] + fn duration_to_secs>(duration: Duration) -> io::Result { + 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) + } } diff --git a/src/backend/linux_raw/net/syscalls.rs b/src/backend/linux_raw/net/syscalls.rs index 083bd15e6..b0e155205 100644 --- a/src/backend/linux_raw/net/syscalls.rs +++ b/src/backend/linux_raw/net/syscalls.rs @@ -929,7 +929,7 @@ pub(crate) mod sockopt { use c::{SO_RCVTIMEO_NEW, SO_RCVTIMEO_OLD, SO_SNDTIMEO_NEW, SO_SNDTIMEO_OLD}; use core::time::Duration; use linux_raw_sys::general::{__kernel_timespec, timeval}; - use linux_raw_sys::net::SO_ACCEPTCONN; + use linux_raw_sys::net::{SO_ACCEPTCONN, TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL}; #[inline] fn getsockopt(fd: BorrowedFd<'_>, level: u32, optname: u32) -> io::Result { @@ -1067,11 +1067,7 @@ pub(crate) mod sockopt { ) -> io::Result<()> { // Convert `linger` to seconds, rounding up. let l_linger = if let Some(linger) = linger { - let mut l_linger = linger.as_secs(); - if linger.subsec_nanos() != 0 { - l_linger = l_linger.checked_add(1).ok_or(io::Errno::INVAL)?; - } - l_linger.try_into().map_err(|_e| io::Errno::INVAL)? + duration_to_secs(linger)? } else { 0 }; @@ -1443,6 +1439,40 @@ pub(crate) mod sockopt { getsockopt(fd, c::IPPROTO_TCP as _, c::TCP_NODELAY).map(to_bool) } + #[inline] + pub(crate) fn set_tcp_keepcnt(fd: BorrowedFd<'_>, count: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP as _, TCP_KEEPCNT, count) + } + + #[inline] + pub(crate) fn get_tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_TCP as _, 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 as _, TCP_KEEPIDLE, secs) + } + + #[inline] + pub(crate) fn get_tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result { + let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP as _, 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 as _, TCP_KEEPINTVL, secs) + } + + #[inline] + pub(crate) fn get_tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result { + let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP as _, TCP_KEEPINTVL)?; + Ok(Duration::from_secs(secs as u64)) + } + #[inline] fn to_imr(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq { c::ip_mreq { @@ -1489,4 +1519,14 @@ pub(crate) mod sockopt { fn to_bool(value: c::c_uint) -> bool { value != 0 } + + /// Convert to seconds, rounding up if necessary. + #[inline] + fn duration_to_secs>(duration: Duration) -> io::Result { + 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) + } } diff --git a/src/net/sockopt.rs b/src/net/sockopt.rs index 1234c1f1d..cef370566 100644 --- a/src/net/sockopt.rs +++ b/src/net/sockopt.rs @@ -1770,6 +1770,286 @@ pub fn get_tcp_nodelay(fd: Fd) -> io::Result { backend::net::syscalls::sockopt::get_tcp_nodelay(fd.as_fd()) } +/// `getsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT)` +/// +/// # References +/// - [POSIX `getsockopt`] +/// - [POSIX `netinet/tcp.h`] +/// - [Linux `getsockopt`] +/// - [Linux `tcp`] +/// - [Winsock2 `getsockopt`] +/// - [Winsock2 `IPPROTO_TCP` options] +/// - [Apple `getsockopt`] +/// - [Apple `tcp`] +/// - [FreeBSD `getsockopt`] +/// - [FreeBSD `tcp`] +/// - [NetBSD `getsockopt`] +/// - [NetBSD `tcp`] +/// - [OpenBSD `getsockopt`] +/// - [OpenBSD `tcp`] +/// - [DragonFly BSD `getsockopt`] +/// - [DragonFly BSD `tcp`] +/// - [illumos `getsockopt`] +/// - [illumos `tcp`] +/// +/// [POSIX `getsockopt`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html +/// [POSIX `netinet/tcp.h`]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/netinet_tcp.h.html +/// [Linux `getsockopt`]: https://man7.org/linux/man-pages/man2/getsockopt.2.html +/// [Linux `tcp`]: https://man7.org/linux/man-pages/man7/tcp.7.html +/// [Winsock2 `getsockopt`]: https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-getsockopt +/// [Winsock2 `IPPROTO_TCP` options]: https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-options +/// [Apple `getsockopt`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getsockopt.2.html +/// [Apple `tcp`]: https://opensource.apple.com/source/xnu/xnu-7195.81.3/bsd/man/man4/tcp.4.auto.html +/// [FreeBSD `getsockopt`]: https://man.freebsd.org/cgi/man.cgi?query=getsockopt&sektion=2 +/// [FreeBSD `tcp`]: https://man.freebsd.org/cgi/man.cgi?query=tcp&sektion=4 +/// [NetBSD `getsockopt`]: https://man.netbsd.org/getsockopt.2 +/// [NetBSD `tcp`]: https://man.netbsd.org/tcp.4 +/// [OpenBSD `getsockopt`]: https://man.openbsd.org/getsockopt.2 +/// [OpenBSD `tcp`]: https://man.openbsd.org/tcp.4 +/// [DragonFly BSD `getsockopt`]: https://man.dragonflybsd.org/?command=getsockopt§ion=2 +/// [DragonFly BSD `tcp`]: https://man.dragonflybsd.org/?command=tcp§ion=4 +/// [illumos `getsockopt`]: https://illumos.org/man/3SOCKET/getsockopt +/// [illumos `tcp`]: https://illumos.org/man/4P/tcp +#[inline] +#[doc(alias = "TCP_KEEPCNT")] +pub fn set_tcp_keepcnt(fd: Fd, count: u32) -> io::Result<()> { + backend::net::syscalls::sockopt::set_tcp_keepcnt(fd.as_fd(), count) +} + +/// `getsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT)` +/// +/// # References +/// - [POSIX `getsockopt`] +/// - [POSIX `netinet/tcp.h`] +/// - [Linux `getsockopt`] +/// - [Linux `tcp`] +/// - [Winsock2 `getsockopt`] +/// - [Winsock2 `IPPROTO_TCP` options] +/// - [Apple `getsockopt`] +/// - [Apple `tcp`] +/// - [FreeBSD `getsockopt`] +/// - [FreeBSD `tcp`] +/// - [NetBSD `getsockopt`] +/// - [NetBSD `tcp`] +/// - [OpenBSD `getsockopt`] +/// - [OpenBSD `tcp`] +/// - [DragonFly BSD `getsockopt`] +/// - [DragonFly BSD `tcp`] +/// - [illumos `getsockopt`] +/// - [illumos `tcp`] +/// +/// [POSIX `getsockopt`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html +/// [POSIX `netinet/tcp.h`]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/netinet_tcp.h.html +/// [Linux `getsockopt`]: https://man7.org/linux/man-pages/man2/getsockopt.2.html +/// [Linux `tcp`]: https://man7.org/linux/man-pages/man7/tcp.7.html +/// [Winsock2 `getsockopt`]: https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-getsockopt +/// [Winsock2 `IPPROTO_TCP` options]: https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-options +/// [Apple `getsockopt`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getsockopt.2.html +/// [Apple `tcp`]: https://opensource.apple.com/source/xnu/xnu-7195.81.3/bsd/man/man4/tcp.4.auto.html +/// [FreeBSD `getsockopt`]: https://man.freebsd.org/cgi/man.cgi?query=getsockopt&sektion=2 +/// [FreeBSD `tcp`]: https://man.freebsd.org/cgi/man.cgi?query=tcp&sektion=4 +/// [NetBSD `getsockopt`]: https://man.netbsd.org/getsockopt.2 +/// [NetBSD `tcp`]: https://man.netbsd.org/tcp.4 +/// [OpenBSD `getsockopt`]: https://man.openbsd.org/getsockopt.2 +/// [OpenBSD `tcp`]: https://man.openbsd.org/tcp.4 +/// [DragonFly BSD `getsockopt`]: https://man.dragonflybsd.org/?command=getsockopt§ion=2 +/// [DragonFly BSD `tcp`]: https://man.dragonflybsd.org/?command=tcp§ion=4 +/// [illumos `getsockopt`]: https://illumos.org/man/3SOCKET/getsockopt +/// [illumos `tcp`]: https://illumos.org/man/4P/tcp +#[inline] +#[doc(alias = "TCP_KEEPCNT")] +pub fn get_tcp_keepcnt(fd: Fd) -> io::Result { + backend::net::syscalls::sockopt::get_tcp_keepcnt(fd.as_fd()) +} + +/// `getsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE)` +/// +/// `TCP_KEEPALIVE` on Apple platforms. +/// +/// # References +/// - [POSIX `getsockopt`] +/// - [POSIX `netinet/tcp.h`] +/// - [Linux `getsockopt`] +/// - [Linux `tcp`] +/// - [Winsock2 `getsockopt`] +/// - [Winsock2 `IPPROTO_TCP` options] +/// - [Apple `getsockopt`] +/// - [Apple `tcp`] +/// - [FreeBSD `getsockopt`] +/// - [FreeBSD `tcp`] +/// - [NetBSD `getsockopt`] +/// - [NetBSD `tcp`] +/// - [OpenBSD `getsockopt`] +/// - [OpenBSD `tcp`] +/// - [DragonFly BSD `getsockopt`] +/// - [DragonFly BSD `tcp`] +/// - [illumos `getsockopt`] +/// - [illumos `tcp`] +/// +/// [POSIX `getsockopt`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html +/// [POSIX `netinet/tcp.h`]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/netinet_tcp.h.html +/// [Linux `getsockopt`]: https://man7.org/linux/man-pages/man2/getsockopt.2.html +/// [Linux `tcp`]: https://man7.org/linux/man-pages/man7/tcp.7.html +/// [Winsock2 `getsockopt`]: https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-getsockopt +/// [Winsock2 `IPPROTO_TCP` options]: https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-options +/// [Apple `getsockopt`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getsockopt.2.html +/// [Apple `tcp`]: https://opensource.apple.com/source/xnu/xnu-7195.81.3/bsd/man/man4/tcp.4.auto.html +/// [FreeBSD `getsockopt`]: https://man.freebsd.org/cgi/man.cgi?query=getsockopt&sektion=2 +/// [FreeBSD `tcp`]: https://man.freebsd.org/cgi/man.cgi?query=tcp&sektion=4 +/// [NetBSD `getsockopt`]: https://man.netbsd.org/getsockopt.2 +/// [NetBSD `tcp`]: https://man.netbsd.org/tcp.4 +/// [OpenBSD `getsockopt`]: https://man.openbsd.org/getsockopt.2 +/// [OpenBSD `tcp`]: https://man.openbsd.org/tcp.4 +/// [DragonFly BSD `getsockopt`]: https://man.dragonflybsd.org/?command=getsockopt§ion=2 +/// [DragonFly BSD `tcp`]: https://man.dragonflybsd.org/?command=tcp§ion=4 +/// [illumos `getsockopt`]: https://illumos.org/man/3SOCKET/getsockopt +/// [illumos `tcp`]: https://illumos.org/man/4P/tcp +#[inline] +#[doc(alias = "TCP_KEEPIDLE")] +pub fn set_tcp_keepidle(fd: Fd, duration: Duration) -> io::Result<()> { + backend::net::syscalls::sockopt::set_tcp_keepidle(fd.as_fd(), duration) +} + +/// `getsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE)` +/// +/// `TCP_KEEPALIVE` on Apple platforms. +/// +/// # References +/// - [POSIX `getsockopt`] +/// - [POSIX `netinet/tcp.h`] +/// - [Linux `getsockopt`] +/// - [Linux `tcp`] +/// - [Winsock2 `getsockopt`] +/// - [Winsock2 `IPPROTO_TCP` options] +/// - [Apple `getsockopt`] +/// - [Apple `tcp`] +/// - [FreeBSD `getsockopt`] +/// - [FreeBSD `tcp`] +/// - [NetBSD `getsockopt`] +/// - [NetBSD `tcp`] +/// - [OpenBSD `getsockopt`] +/// - [OpenBSD `tcp`] +/// - [DragonFly BSD `getsockopt`] +/// - [DragonFly BSD `tcp`] +/// - [illumos `getsockopt`] +/// - [illumos `tcp`] +/// +/// [POSIX `getsockopt`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html +/// [POSIX `netinet/tcp.h`]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/netinet_tcp.h.html +/// [Linux `getsockopt`]: https://man7.org/linux/man-pages/man2/getsockopt.2.html +/// [Linux `tcp`]: https://man7.org/linux/man-pages/man7/tcp.7.html +/// [Winsock2 `getsockopt`]: https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-getsockopt +/// [Winsock2 `IPPROTO_TCP` options]: https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-options +/// [Apple `getsockopt`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getsockopt.2.html +/// [Apple `tcp`]: https://opensource.apple.com/source/xnu/xnu-7195.81.3/bsd/man/man4/tcp.4.auto.html +/// [FreeBSD `getsockopt`]: https://man.freebsd.org/cgi/man.cgi?query=getsockopt&sektion=2 +/// [FreeBSD `tcp`]: https://man.freebsd.org/cgi/man.cgi?query=tcp&sektion=4 +/// [NetBSD `getsockopt`]: https://man.netbsd.org/getsockopt.2 +/// [NetBSD `tcp`]: https://man.netbsd.org/tcp.4 +/// [OpenBSD `getsockopt`]: https://man.openbsd.org/getsockopt.2 +/// [OpenBSD `tcp`]: https://man.openbsd.org/tcp.4 +/// [DragonFly BSD `getsockopt`]: https://man.dragonflybsd.org/?command=getsockopt§ion=2 +/// [DragonFly BSD `tcp`]: https://man.dragonflybsd.org/?command=tcp§ion=4 +/// [illumos `getsockopt`]: https://illumos.org/man/3SOCKET/getsockopt +/// [illumos `tcp`]: https://illumos.org/man/4P/tcp +#[inline] +#[doc(alias = "TCP_KEEPIDLE")] +pub fn get_tcp_keepidle(fd: Fd) -> io::Result { + backend::net::syscalls::sockopt::get_tcp_keepidle(fd.as_fd()) +} + +/// `getsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL)` +/// +/// # References +/// - [POSIX `getsockopt`] +/// - [POSIX `netinet/tcp.h`] +/// - [Linux `getsockopt`] +/// - [Linux `tcp`] +/// - [Winsock2 `getsockopt`] +/// - [Winsock2 `IPPROTO_TCP` options] +/// - [Apple `getsockopt`] +/// - [Apple `tcp`] +/// - [FreeBSD `getsockopt`] +/// - [FreeBSD `tcp`] +/// - [NetBSD `getsockopt`] +/// - [NetBSD `tcp`] +/// - [OpenBSD `getsockopt`] +/// - [OpenBSD `tcp`] +/// - [DragonFly BSD `getsockopt`] +/// - [DragonFly BSD `tcp`] +/// - [illumos `getsockopt`] +/// - [illumos `tcp`] +/// +/// [POSIX `getsockopt`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html +/// [POSIX `netinet/tcp.h`]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/netinet_tcp.h.html +/// [Linux `getsockopt`]: https://man7.org/linux/man-pages/man2/getsockopt.2.html +/// [Linux `tcp`]: https://man7.org/linux/man-pages/man7/tcp.7.html +/// [Winsock2 `getsockopt`]: https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-getsockopt +/// [Winsock2 `IPPROTO_TCP` options]: https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-options +/// [Apple `getsockopt`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getsockopt.2.html +/// [Apple `tcp`]: https://opensource.apple.com/source/xnu/xnu-7195.81.3/bsd/man/man4/tcp.4.auto.html +/// [FreeBSD `getsockopt`]: https://man.freebsd.org/cgi/man.cgi?query=getsockopt&sektion=2 +/// [FreeBSD `tcp`]: https://man.freebsd.org/cgi/man.cgi?query=tcp&sektion=4 +/// [NetBSD `getsockopt`]: https://man.netbsd.org/getsockopt.2 +/// [NetBSD `tcp`]: https://man.netbsd.org/tcp.4 +/// [OpenBSD `getsockopt`]: https://man.openbsd.org/getsockopt.2 +/// [OpenBSD `tcp`]: https://man.openbsd.org/tcp.4 +/// [DragonFly BSD `getsockopt`]: https://man.dragonflybsd.org/?command=getsockopt§ion=2 +/// [DragonFly BSD `tcp`]: https://man.dragonflybsd.org/?command=tcp§ion=4 +/// [illumos `getsockopt`]: https://illumos.org/man/3SOCKET/getsockopt +/// [illumos `tcp`]: https://illumos.org/man/4P/tcp +#[inline] +#[doc(alias = "TCP_KEEPINTVL")] +pub fn set_tcp_keepintvl(fd: Fd, duration: Duration) -> io::Result<()> { + backend::net::syscalls::sockopt::set_tcp_keepintvl(fd.as_fd(), duration) +} + +/// `getsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL)` +/// +/// # References +/// - [POSIX `getsockopt`] +/// - [POSIX `netinet/tcp.h`] +/// - [Linux `getsockopt`] +/// - [Linux `tcp`] +/// - [Winsock2 `getsockopt`] +/// - [Winsock2 `IPPROTO_TCP` options] +/// - [Apple `getsockopt`] +/// - [Apple `tcp`] +/// - [FreeBSD `getsockopt`] +/// - [FreeBSD `tcp`] +/// - [NetBSD `getsockopt`] +/// - [NetBSD `tcp`] +/// - [OpenBSD `getsockopt`] +/// - [OpenBSD `tcp`] +/// - [DragonFly BSD `getsockopt`] +/// - [DragonFly BSD `tcp`] +/// - [illumos `getsockopt`] +/// - [illumos `tcp`] +/// +/// [POSIX `getsockopt`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html +/// [POSIX `netinet/tcp.h`]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/netinet_tcp.h.html +/// [Linux `getsockopt`]: https://man7.org/linux/man-pages/man2/getsockopt.2.html +/// [Linux `tcp`]: https://man7.org/linux/man-pages/man7/tcp.7.html +/// [Winsock2 `getsockopt`]: https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-getsockopt +/// [Winsock2 `IPPROTO_TCP` options]: https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-options +/// [Apple `getsockopt`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getsockopt.2.html +/// [Apple `tcp`]: https://opensource.apple.com/source/xnu/xnu-7195.81.3/bsd/man/man4/tcp.4.auto.html +/// [FreeBSD `getsockopt`]: https://man.freebsd.org/cgi/man.cgi?query=getsockopt&sektion=2 +/// [FreeBSD `tcp`]: https://man.freebsd.org/cgi/man.cgi?query=tcp&sektion=4 +/// [NetBSD `getsockopt`]: https://man.netbsd.org/getsockopt.2 +/// [NetBSD `tcp`]: https://man.netbsd.org/tcp.4 +/// [OpenBSD `getsockopt`]: https://man.openbsd.org/getsockopt.2 +/// [OpenBSD `tcp`]: https://man.openbsd.org/tcp.4 +/// [DragonFly BSD `getsockopt`]: https://man.dragonflybsd.org/?command=getsockopt§ion=2 +/// [DragonFly BSD `tcp`]: https://man.dragonflybsd.org/?command=tcp§ion=4 +/// [illumos `getsockopt`]: https://illumos.org/man/3SOCKET/getsockopt +/// [illumos `tcp`]: https://illumos.org/man/4P/tcp +#[inline] +#[doc(alias = "TCP_KEEPINTVL")] +pub fn get_tcp_keepintvl(fd: Fd) -> io::Result { + backend::net::syscalls::sockopt::get_tcp_keepintvl(fd.as_fd()) +} + #[test] fn test_sizes() { use c::c_int; diff --git a/tests/net/sockopt.rs b/tests/net/sockopt.rs index 11d48dd2f..ab093d95d 100644 --- a/tests/net/sockopt.rs +++ b/tests/net/sockopt.rs @@ -31,6 +31,9 @@ fn test_sockopts_ipv4() { #[cfg(not(any(bsd, windows, target_os = "illumos")))] assert_eq!(rustix::net::sockopt::get_ip_multicast_ttl(&s).unwrap(), 1); assert!(!rustix::net::sockopt::get_tcp_nodelay(&s).unwrap()); + assert!(rustix::net::sockopt::get_tcp_keepcnt(&s).is_ok()); + assert!(rustix::net::sockopt::get_tcp_keepidle(&s).is_ok()); + assert!(rustix::net::sockopt::get_tcp_keepintvl(&s).is_ok()); // On a new socket we shouldn't have an error yet. assert_eq!(rustix::net::sockopt::get_socket_error(&s).unwrap(), Ok(())); assert!(!rustix::net::sockopt::get_socket_keepalive(&s).unwrap()); @@ -150,6 +153,16 @@ fn test_sockopts_ipv4() { // Check that the nodelay flag is set. assert!(rustix::net::sockopt::get_tcp_nodelay(&s).unwrap()); + // Set keepalive values: + rustix::net::sockopt::set_tcp_keepcnt(&s, 42).unwrap(); + rustix::net::sockopt::set_tcp_keepidle(&s, Duration::from_secs(3601)).unwrap(); + rustix::net::sockopt::set_tcp_keepintvl(&s, Duration::from_secs(61)).unwrap(); + + // Check keepalive values: + assert_eq!(rustix::net::sockopt::get_tcp_keepcnt(&s).unwrap(), 42); + assert_eq!(rustix::net::sockopt::get_tcp_keepidle(&s).unwrap(), Duration::from_secs(3601)); + assert_eq!(rustix::net::sockopt::get_tcp_keepintvl(&s).unwrap(), Duration::from_secs(61)); + // Set the receive buffer size. let size = rustix::net::sockopt::get_socket_recv_buffer_size(&s).unwrap(); rustix::net::sockopt::set_socket_recv_buffer_size(&s, size * 2).unwrap();