From 85c2f2da6662001efe02263b995e4093dd59a335 Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Wed, 24 Aug 2022 19:23:03 +0800 Subject: [PATCH 01/14] TCP_FASTOPEN option for Socket - Supported platforms: Linux-like, FreeBSD, macOS, Windows - ref #49 --- src/socket.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++++ src/sys/unix.rs | 11 +++++++ src/sys/windows.rs | 2 ++ 3 files changed, 88 insertions(+) diff --git a/src/socket.rs b/src/socket.rs index 1bc6c9f0..7ea4961a 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -1457,6 +1457,81 @@ impl Socket { .map(|recv_tos| recv_tos > 0) } } + + /// Set `TCP_FASTOPEN` option for this socket. + /// + /// ## Windows + /// + /// Windows supports TCP Fast Open since Windows 10. + /// + /// + /// + /// `value` is a boolean with only `0` and `1`. + /// + /// ## Linux + /// + /// Linux supports TCP Fast Open since 3.7. + /// + /// + /// + /// The option `value`, `qlen`, specifies this server's limit on the size of the queue of TFO requests that have + /// not yet completed the three-way handshake (see the remarks on prevention of resource-exhaustion attacks above). + /// + /// It was recommended to be `5` in this document. + /// + /// ## macOS + /// + /// `value` is a boolean with only `0` and `1`. + /// + /// ## FreeBSD + /// + /// FreeBSD supports TCP Fast Open since 12.0. + /// + /// Example program: + /// + /// `value` is a boolean with only `0` and `1`. + #[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos", + target_os = "windows" + ))] + pub fn set_tcp_fastopen(&self, value: u32) -> io::Result<()> { + unsafe { + setsockopt::( + self.as_raw(), + sys::IPPROTO_TCP, + sys::TCP_FASTOPEN, + value as c_int, + ) + } + } + + /// Get the value of `TCP_FASTOPEN` option for this socket. + /// + /// For more information about this option, see [`set_tcp_fastopen`]. + /// + /// [`set_tcp_fastopen`]: Socket::set_tcp_fastopen + #[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos", + target_os = "windows" + ))] + pub fn tcp_fastopen(&self) -> io::Result { + unsafe { + getsockopt::(self.as_raw(), sys::IPPROTO_TCP, sys::TCP_FASTOPEN) + .map(|c| c as u32) + } + } } /// Socket options for IPv6 sockets, get/set using `IPPROTO_IPV6`. diff --git a/src/sys/unix.rs b/src/sys/unix.rs index 7e1b2755..7e33d304 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -157,6 +157,17 @@ use libc::TCP_KEEPALIVE as KEEPALIVE_TIME; #[cfg(not(any(target_vendor = "apple", target_os = "haiku", target_os = "openbsd")))] use libc::TCP_KEEPIDLE as KEEPALIVE_TIME; +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos" +))] +pub(crate) use libc::TCP_FASTOPEN; + /// Helper macro to execute a system call that returns an `io::Result`. macro_rules! syscall { ($fn: ident ( $($arg: expr),* $(,)* ) ) => {{ diff --git a/src/sys/windows.rs b/src/sys/windows.rs index 03e1a60a..fde7ed46 100644 --- a/src/sys/windows.rs +++ b/src/sys/windows.rs @@ -78,6 +78,8 @@ pub(crate) use windows_sys::Win32::Networking::WinSock::{ }; pub(crate) const IPPROTO_IP: c_int = windows_sys::Win32::Networking::WinSock::IPPROTO_IP as c_int; pub(crate) const SOL_SOCKET: c_int = windows_sys::Win32::Networking::WinSock::SOL_SOCKET as c_int; +pub(crate) const TCP_FASTOPEN: c_int = + windows_sys::Win32::Networking::WinSock::TCP_FASTOPEN as c_int; /// Type used in set/getsockopt to retrieve the `TCP_NODELAY` option. /// From ba56e62a6365372b7b5df3539dfb359ae3183160 Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Thu, 25 Aug 2022 09:54:43 +0800 Subject: [PATCH 02/14] TCP_FASTOPEN on Windows should be an u32 --- src/sys/windows.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sys/windows.rs b/src/sys/windows.rs index fde7ed46..213d0225 100644 --- a/src/sys/windows.rs +++ b/src/sys/windows.rs @@ -78,8 +78,7 @@ pub(crate) use windows_sys::Win32::Networking::WinSock::{ }; pub(crate) const IPPROTO_IP: c_int = windows_sys::Win32::Networking::WinSock::IPPROTO_IP as c_int; pub(crate) const SOL_SOCKET: c_int = windows_sys::Win32::Networking::WinSock::SOL_SOCKET as c_int; -pub(crate) const TCP_FASTOPEN: c_int = - windows_sys::Win32::Networking::WinSock::TCP_FASTOPEN as c_int; +pub(crate) const TCP_FASTOPEN: u32 = windows_sys::Win32::Networking::WinSock::TCP_FASTOPEN as u32; /// Type used in set/getsockopt to retrieve the `TCP_NODELAY` option. /// From b481400f78a60cd493b66f366a73dc152adb0c63 Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Thu, 25 Aug 2022 09:57:30 +0800 Subject: [PATCH 03/14] test for TCP_FASTOPEN --- tests/socket.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/socket.rs b/tests/socket.rs index 7540b5d6..4f6fca7a 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -1281,3 +1281,19 @@ fn header_included() { let got = socket.header_included().expect("failed to get value"); assert_eq!(got, true, "set and get values differ"); } + +#[test] +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos", + target_os = "windows" +))] +fn tcp_fastopen() { + let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); + socket.set_tcp_fastopen(5).unwrap(); +} From 008a844cccf52a88e6d972d7aafdcc8405c1aa32 Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Thu, 25 Aug 2022 10:02:51 +0800 Subject: [PATCH 04/14] bind() before setting TCP_FASTOPEN --- tests/socket.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/socket.rs b/tests/socket.rs index 4f6fca7a..d6e453f6 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -1295,5 +1295,9 @@ fn header_included() { ))] fn tcp_fastopen() { let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); + let baddr = SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 0); + let bsaddr = SockAddr::from(baddr); + socket.bind(&bsaddr).unwrap(); + socket.listen(128).unwrap(); socket.set_tcp_fastopen(5).unwrap(); } From a9fa8060f31317093522a9ff115035396ddbef56 Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Thu, 25 Aug 2022 10:09:25 +0800 Subject: [PATCH 05/14] only Linux can set value > 1 --- tests/socket.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/socket.rs b/tests/socket.rs index d6e453f6..770ee06b 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -1299,5 +1299,10 @@ fn tcp_fastopen() { let bsaddr = SockAddr::from(baddr); socket.bind(&bsaddr).unwrap(); socket.listen(128).unwrap(); - socket.set_tcp_fastopen(5).unwrap(); + + if cfg!(any(target_os = "linux", target_os = "android")) { + socket.set_tcp_fastopen(5).unwrap(); + } else { + socket.set_tcp_fastopen(1).unwrap(); + } } From d10aa55e70546b477cfab8870e66bb389ab5cfc3 Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Thu, 25 Aug 2022 19:32:55 +0800 Subject: [PATCH 06/14] force TCP_FASTOPEN value to be 0,1 excepts Linux --- src/socket.rs | 12 ++++++++---- tests/socket.rs | 5 +++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/socket.rs b/src/socket.rs index 7ea4961a..425bf71e 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -1466,7 +1466,7 @@ impl Socket { /// /// /// - /// `value` is a boolean with only `0` and `1`. + /// `value` is a boolean with only `0` and `1`. Any `value` > `1` will be set to `1`. /// /// ## Linux /// @@ -1481,7 +1481,7 @@ impl Socket { /// /// ## macOS /// - /// `value` is a boolean with only `0` and `1`. + /// `value` is a boolean with only `0` and `1`. Any `value` > `1` will be set to `1`. /// /// ## FreeBSD /// @@ -1489,7 +1489,7 @@ impl Socket { /// /// Example program: /// - /// `value` is a boolean with only `0` and `1`. + /// `value` is a boolean with only `0` and `1`. Any `value` > `1` will be set to `1`. #[cfg(any( target_os = "linux", target_os = "android", @@ -1500,7 +1500,11 @@ impl Socket { target_os = "tvos", target_os = "windows" ))] - pub fn set_tcp_fastopen(&self, value: u32) -> io::Result<()> { + pub fn set_tcp_fastopen(&self, mut value: u32) -> io::Result<()> { + if !cfg!(any(target_os = "linux", target_os = "android")) && value > 1 { + value = 1; + } + unsafe { setsockopt::( self.as_raw(), diff --git a/tests/socket.rs b/tests/socket.rs index 770ee06b..44ab9a73 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -1299,10 +1299,11 @@ fn tcp_fastopen() { let bsaddr = SockAddr::from(baddr); socket.bind(&bsaddr).unwrap(); socket.listen(128).unwrap(); + socket.set_tcp_fastopen(5).unwrap(); if cfg!(any(target_os = "linux", target_os = "android")) { - socket.set_tcp_fastopen(5).unwrap(); + assert_eq!(socket.tcp_fastopen().unwrap(), 5); } else { - socket.set_tcp_fastopen(1).unwrap(); + assert_eq!(socket.tcp_fastopen().unwrap(), 1); } } From 1a2770759fbb98067389b5b033e8b0dd83673bc6 Mon Sep 17 00:00:00 2001 From: ty Date: Fri, 26 Aug 2022 00:35:01 +0800 Subject: [PATCH 07/14] Update src/socket.rs Co-authored-by: Thomas de Zeeuw --- src/socket.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/socket.rs b/src/socket.rs index 425bf71e..b6daaa09 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -1501,7 +1501,8 @@ impl Socket { target_os = "windows" ))] pub fn set_tcp_fastopen(&self, mut value: u32) -> io::Result<()> { - if !cfg!(any(target_os = "linux", target_os = "android")) && value > 1 { + #[cfg(not(any(target_os = "linux", target_os = "android")))] + if value > 1 { value = 1; } From 345b754994e1a2113e8d1d7040fdc91002c22642 Mon Sep 17 00:00:00 2001 From: ty Date: Fri, 26 Aug 2022 00:35:07 +0800 Subject: [PATCH 08/14] Update tests/socket.rs Co-authored-by: Thomas de Zeeuw --- tests/socket.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/socket.rs b/tests/socket.rs index 44ab9a73..69ec169b 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -1301,9 +1301,9 @@ fn tcp_fastopen() { socket.listen(128).unwrap(); socket.set_tcp_fastopen(5).unwrap(); - if cfg!(any(target_os = "linux", target_os = "android")) { - assert_eq!(socket.tcp_fastopen().unwrap(), 5); - } else { - assert_eq!(socket.tcp_fastopen().unwrap(), 1); - } + #[cfg(not(target_os = "linux", target_os = "android"))] + let expected = 5; + #[cfg(not(any(target_os = "linux", target_os = "android")))] + let expected = 1; + assert_eq!(socket.tcp_fastopen().unwrap(), expected); } From 61e2803c97e9a5f084627ccf9d70df90821b92ed Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Fri, 26 Aug 2022 00:39:01 +0800 Subject: [PATCH 09/14] enable tcp_fastopen on FreeBSD --- .cirrus.yml | 3 +++ tests/socket.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index ba330733..a91f096b 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -6,6 +6,9 @@ env: task: name: FreeBSD + enable_tcp_fastopen: + - sysctl net.inet.tcp.fastopen.client_enabled=1 + - sysctl net.inet.tcp.fastopen.server_enabled=1 setup_script: - pkg install -y curl - curl https://sh.rustup.rs -sSf --output rustup.sh diff --git a/tests/socket.rs b/tests/socket.rs index 69ec169b..c9cc8484 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -1301,7 +1301,7 @@ fn tcp_fastopen() { socket.listen(128).unwrap(); socket.set_tcp_fastopen(5).unwrap(); - #[cfg(not(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android"))] let expected = 5; #[cfg(not(any(target_os = "linux", target_os = "android")))] let expected = 1; From 2a2bf8eff882248ea4399975e62da85b6e861a34 Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Fri, 26 Aug 2022 11:08:47 +0800 Subject: [PATCH 10/14] ignore unused mut --- src/socket.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/socket.rs b/src/socket.rs index b6daaa09..17c75368 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -1500,6 +1500,7 @@ impl Socket { target_os = "tvos", target_os = "windows" ))] + #[allow(unused_mut)] pub fn set_tcp_fastopen(&self, mut value: u32) -> io::Result<()> { #[cfg(not(any(target_os = "linux", target_os = "android")))] if value > 1 { From 71f7800fa38eae4e3d429730c05de24a42a8eb0f Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Fri, 26 Aug 2022 12:29:30 +0800 Subject: [PATCH 11/14] TCP_FASTOPEN getsockopt on Windows returns a 1-byte value --- src/socket.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/socket.rs b/src/socket.rs index 17c75368..c8927914 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -1533,10 +1533,15 @@ impl Socket { target_os = "windows" ))] pub fn tcp_fastopen(&self) -> io::Result { + #[cfg(not(target_os = "windows"))] unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_TCP, sys::TCP_FASTOPEN) .map(|c| c as u32) } + #[cfg(target_os = "windows")] + unsafe { + getsockopt::(self.as_raw(), sys::IPPROTO_TCP, sys::TCP_FASTOPEN).map(|c| c as u32) + } } } From 8cb823f60b59aff5ea7147e44e8ec3ff8cd0ab56 Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Fri, 26 Aug 2022 12:50:42 +0800 Subject: [PATCH 12/14] enable tcp_fastopen after build_script --- .cirrus.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index a91f096b..f5a5ae95 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -6,9 +6,6 @@ env: task: name: FreeBSD - enable_tcp_fastopen: - - sysctl net.inet.tcp.fastopen.client_enabled=1 - - sysctl net.inet.tcp.fastopen.server_enabled=1 setup_script: - pkg install -y curl - curl https://sh.rustup.rs -sSf --output rustup.sh @@ -19,6 +16,9 @@ task: - . $HOME/.cargo/env - cargo build - cargo build --no-default-features + enable_tcp_fastopen: + - sysctl net.inet.tcp.fastopen.client_enabled=1 + - sysctl net.inet.tcp.fastopen.server_enabled=1 amd64_test_script: - . $HOME/.cargo/env - cargo test --all-features From b3a1b525dcc126ebaa3cd2019c89aabb98b7dbcd Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Sun, 28 Aug 2022 23:25:51 +0800 Subject: [PATCH 13/14] fixed FreeBSD CI and tests --- .cirrus.yml | 4 ++-- tests/socket.rs | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index f5a5ae95..81dd7bcf 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -17,8 +17,8 @@ task: - cargo build - cargo build --no-default-features enable_tcp_fastopen: - - sysctl net.inet.tcp.fastopen.client_enabled=1 - - sysctl net.inet.tcp.fastopen.server_enabled=1 + - sudo sysctl net.inet.tcp.fastopen.client_enable=1 + - sudo sysctl net.inet.tcp.fastopen.server_enable=1 amd64_test_script: - . $HOME/.cargo/env - cargo test --all-features diff --git a/tests/socket.rs b/tests/socket.rs index c9cc8484..e84785fb 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -1302,8 +1302,12 @@ fn tcp_fastopen() { socket.set_tcp_fastopen(5).unwrap(); #[cfg(any(target_os = "linux", target_os = "android"))] - let expected = 5; + { + assert_eq!(socket.tcp_fastopen().unwrap(), 5); + } + #[cfg(not(any(target_os = "linux", target_os = "android")))] - let expected = 1; - assert_eq!(socket.tcp_fastopen().unwrap(), expected); + { + assert_ne!(socket.tcp_fastopen().unwrap(), 0); + } } From 7199cf675c9a7657091de2f419810fc696fb6de1 Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Mon, 29 Aug 2022 00:25:47 +0800 Subject: [PATCH 14/14] FreeBSD sysctl removed sudo --- .cirrus.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 81dd7bcf..a8e54ad6 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -17,8 +17,8 @@ task: - cargo build - cargo build --no-default-features enable_tcp_fastopen: - - sudo sysctl net.inet.tcp.fastopen.client_enable=1 - - sudo sysctl net.inet.tcp.fastopen.server_enable=1 + - sysctl net.inet.tcp.fastopen.client_enable=1 + - sysctl net.inet.tcp.fastopen.server_enable=1 amd64_test_script: - . $HOME/.cargo/env - cargo test --all-features