Support TCP Fast Open
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 870
- Forks
- 305
- PR merge metrics
- No merged PRs in 30d
Description
TcpListener
All of Linux, OS X and Windows are the same, call setsockopt with TCP_FASTOPEN option
// For Linux, value is the queue length of pending packets
int opt = 5;
// For the others, just a boolean value for enable and disable
int opt = 1;
// Call it before listen()
int ret = setsockopt(socket, IPPROTO_TCP, TCP_FASTOPEN, &opt, sizeof(opt));
TcpStream
This is a bit more complicated than TcpListener, because we have to send SYN with the first data packet to make TFO works. APIs are completely different on different platforms:
Linux
Before 4.11, Linux uses MSG_FASTOPEN flag for sendto(), doesn't need to call connect()
// Send SYN with data in buf
ssize_t n = sendto(socket, buf, buf_length, MSG_FASTOPEN, saddr, saddr_len);
After 4.11, Linux provides a TCP_FASTOPEN_CONNECT option
int enable = 1;
// Enable it before connect()
int ret = setsockopt(socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &enable, sizeof(enable));
// Call connect as usual
int ret = connect(socket, saddr, saddr_len);
BSDs (except Darwin)
Uses sendto() as Linux without MSG_FASTOPEN flag
// Set before sendto()
int ret = setsockopt(socket, IPPROTO_TCP, TCP_FASTOPEN, &opt, sizeof(opt));
// Call sendto() without flags
ssize_t n = sendto(socket, buf, buf_length, 0, saddr, saddr_len);
Darwin (OS X)
Darwin supports TFO after OS X 10.11, iOS 9.0, tvOS 9.0 and watchOS 2.0.
It supports with a new syscall connectx.
sa_endpoints_t endpoints;
memset(&endpoints, 0, sizeof(endpoints));
endpoints.sae_dstaddr = &saddr;
endpoints.sae_dstaddrlen = saddr_len;
int ret = connectx(socket,
&endpoints,
SAE_ASSOCID_ANY,
CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE,
NULL,
0,
NULL,
NULL);
SYN will be sent with the first write or send.
Windows
Windows supports with ConnectEx, since Windows 10. https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nc-mswsock-lpfn_connectex
// TCP_FASTOPEN is required
int ret = setsockopt(socket, IPPROTO_TCP, TCP_FASTOPEN, &opt, sizeof(opt));
// Get LPFN_CONNECTEX
LPFN_CONNECTEX ConnectEx = ...;
// Call it with the first packet of data, SYN will send with this packet's data
LPDWORD bytesSent = 0;
int ret = ConnectEx(socket, saddr, saddr_len, buf, buf_length, &bytesSent, &overlapped);
Challenge
As we can see for those OSes' APIs, (except OS X and Linux > 4.10) connections are made with the first write(), which means that if we want to support TFO, we have to put those code in the first call of std::io::Write::write or tokio::AsyncWrite::poll_write.
There is no other way except customizing a TcpStream from socket() and call different APIs on different OSes while sending the first data packet.
I want to open a discussion here for how in Rust's world to support TFO gracefully.
Related API PRs
connectxfor OS X: https://github.com/rust-lang/libc/pull/1635TCP_FASTOPEN_CONNECTfor Linux: https://github.com/rust-lang/libc/pull/1634TCP_FASTOPENfor Windows: https://github.com/retep998/winapi-rs/issues/856
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Review the TcpListener and TcpStream sections first, then compare the platform-specific setsockopt, sendto, connectx, and ConnectEx APIs described in the issue. A complete effort would need a decided cross-platform API and an implementation strategy for sending the first data packet; the issue does not name files or tests to run.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- linux, macos, rust
- Domain
- networking
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100