Skip to content

Commit

Permalink
Added TcpFastOpenConnect sockopt (#2247)
Browse files Browse the repository at this point in the history
From [tcp](https://man7.org/linux/man-pages/man7/tcp.7.html) man page:
```
TCP_FASTOPEN_CONNECT (since Linux 4.11)
       This option enables an alternative way to perform Fast
       Open on the active side (client).  When this option is
       enabled, connect(2) would behave differently depending on
       if a Fast Open cookie is available for the destination.

       If a cookie is not available (i.e. first contact to the
       destination), connect(2) behaves as usual by sending a SYN
       immediately, except the SYN would include an empty Fast
       Open cookie option to solicit a cookie.

       If a cookie is available, connect(2) would return 0
       immediately but the SYN transmission is deferred.  A
       subsequent write(2) or sendmsg(2) would trigger a SYN with
       data plus cookie in the Fast Open option.  In other words,
       the actual connect operation is deferred until data is
       supplied.

       Note: While this option is designed for convenience,
       enabling it does change the behaviors and certain system
       calls might set different errno values.  With cookie
       present, write(2) or sendmsg(2) must be called right after
       connect(2) in order to send out SYN+data to complete 3WHS
       and establish connection.  Calling read(2) right after
       connect(2) without write(2) will cause the blocking socket
       to be blocked forever.

       The application should either set TCP_FASTOPEN_CONNECT
       socket option before write(2) or sendmsg(2), or call
       write(2) or sendmsg(2) with MSG_FASTOPEN flag directly,
       instead of both on the same connection.

       Here is the typical call flow with this new option:

           s = socket();
           setsockopt(s, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, 1, ...);
           connect(s);
           write(s); /* write() should always follow connect()
                      * in order to trigger SYN to go out. */
           read(s)/write(s);
           /* ... */
           close(s);
```
  • Loading branch information
xonatius authored Dec 3, 2023
1 parent 9b39cf9 commit db5353c
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog/2247.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added `TcpFastOpenConnect` sockopt to support `TCP_FASTOPEN_CONNECT` available on linux.
20 changes: 20 additions & 0 deletions src/sys/socket/sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,26 @@ sockopt_impl!(
libc::TCP_USER_TIMEOUT,
u32
);
#[cfg(linux_android)]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Enables TCP Fast Open (RFC 7413) on a connecting socket. If a fast open
/// cookie is not available (first attempt to connect), `connect` syscall
/// will behave as usual, except for internally trying to solicit a cookie
/// from remote peer. When cookie is available, the next `connect` syscall
/// will immediately succeed without actually establishing TCP connection.
/// The connection establishment will be defered till the next `write` or
/// `sendmsg` syscalls on the socket, allowing TCP prtocol to establish
/// connection and send data in the same packets. Note: calling `read` right
/// after `connect` without `write` on the socket will cause the blocking
/// socket to be blocked forever.
TcpFastOpenConnect,
Both,
libc::IPPROTO_TCP,
libc::TCP_FASTOPEN_CONNECT,
bool
);
sockopt_impl!(
/// Sets or gets the maximum socket receive buffer in bytes.
RcvBuf,
Expand Down
24 changes: 24 additions & 0 deletions test/sys/test_sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,3 +576,27 @@ fn test_ip_bind_address_no_port() {
"getting IP_BIND_ADDRESS_NO_PORT on an inet stream socket should succeed",
));
}

#[test]
#[cfg(linux_android)]
fn test_tcp_fast_open_connect() {
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
setsockopt(&fd, sockopt::TcpFastOpenConnect, &true).expect(
"setting TCP_FASTOPEN_CONNECT on an inet stream socket should succeed",
);
assert!(getsockopt(&fd, sockopt::TcpFastOpenConnect).expect(
"getting TCP_FASTOPEN_CONNECT on an inet stream socket should succeed",
));
setsockopt(&fd, sockopt::TcpFastOpenConnect, &false).expect(
"unsetting TCP_FASTOPEN_CONNECT on an inet stream socket should succeed",
);
assert!(!getsockopt(&fd, sockopt::TcpFastOpenConnect).expect(
"getting TCP_FASTOPEN_CONNECT on an inet stream socket should succeed",
));
}

0 comments on commit db5353c

Please sign in to comment.