Skip to content

Commit

Permalink
TCP fast open server side support.
Browse files Browse the repository at this point in the history
Provide default and non_exhaustive for TcpSocketOptions to future proof
its changes.

Also fix an issue that TcpScoketOptions can't be used for IPv4 sockets
at all.
  • Loading branch information
eaufavor authored and andrewhavck committed May 17, 2024
1 parent 2272112 commit 34b2a35
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .bleep
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6047acc79584d167765d8a3a557df7d4d3ceac88
2c9d4c55853235e908a1acd20454ebe7b979d246
27 changes: 21 additions & 6 deletions pingora-core/src/listeners/l4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use std::os::unix::net::UnixListener as StdUnixListener;
use std::time::Duration;
use tokio::net::TcpSocket;

use crate::protocols::l4::ext::set_tcp_fastopen_backlog;
use crate::protocols::l4::listener::Listener;
pub use crate::protocols::l4::stream::Stream;
use crate::server::ListenFds;
Expand All @@ -51,12 +52,16 @@ impl AsRef<str> for ServerAddress {
}

/// TCP socket configuration options.
#[derive(Clone, Debug)]
#[non_exhaustive]
#[derive(Clone, Debug, Default)]
pub struct TcpSocketOptions {
/// IPV6_V6ONLY flag (if true, limit socket to IPv6 communication only).
/// This is mostly useful when binding to `[::]`, which on most Unix distributions
/// will bind to both IPv4 and IPv6 addresses by default.
pub ipv6_only: bool,
/// Enable TCP fast open and set the backlog size of it.
/// See the [man page](https://man7.org/linux/man-pages/man7/tcp.7.html) for more information.
pub tcp_fastopen: Option<usize>,
// TODO: allow configuring reuseaddr, backlog, etc. from here?
}

Expand Down Expand Up @@ -121,10 +126,17 @@ fn apply_tcp_socket_options(sock: &TcpSocket, opt: Option<&TcpSocketOptions>) ->
let Some(opt) = opt else {
return Ok(());
};
let socket_ref = socket2::SockRef::from(sock);
socket_ref
.set_only_v6(opt.ipv6_only)
.or_err(BindError, "failed to set IPV6_V6ONLY")
if opt.ipv6_only {
let socket_ref = socket2::SockRef::from(sock);
socket_ref
.set_only_v6(opt.ipv6_only)
.or_err(BindError, "failed to set IPV6_V6ONLY")?;
}

if let Some(backlog) = opt.tcp_fastopen {
set_tcp_fastopen_backlog(sock.as_raw_fd(), backlog)?;
}
Ok(())
}

fn from_raw_fd(address: &ServerAddress, fd: i32) -> Result<Listener> {
Expand Down Expand Up @@ -279,7 +291,10 @@ mod test {

#[tokio::test]
async fn test_listen_tcp_ipv6_only() {
let sock_opt = Some(TcpSocketOptions { ipv6_only: true });
let sock_opt = Some(TcpSocketOptions {
ipv6_only: true,
..Default::default()
});
let mut listener = ListenerEndpoint::new(ServerAddress::Tcp("[::]:7101".into(), sock_opt));
listener.listen(None).await.unwrap();
tokio::spawn(async move {
Expand Down
12 changes: 12 additions & 0 deletions pingora-core/src/protocols/l4/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,18 @@ pub fn set_tcp_fastopen_connect(_fd: RawFd) -> Result<()> {
Ok(())
}

/// Enable server side TCP fast open.
#[cfg(target_os = "linux")]
pub fn set_tcp_fastopen_backlog(fd: RawFd, backlog: usize) -> Result<()> {
set_opt(fd, libc::IPPROTO_TCP, libc::TCP_FASTOPEN, backlog as c_int)
.or_err(ConnectError, "failed to set TCP_FASTOPEN")
}

#[cfg(not(target_os = "linux"))]
pub fn set_tcp_fastopen_backlog(_fd: RawFd, _backlog: usize) -> Result<()> {
Ok(())
}

/// connect() to the given address while optionally binding to the specific source address.
///
/// The `set_socket` callback can be used to tune the socket before `connect()` is called.
Expand Down
4 changes: 3 additions & 1 deletion pingora/examples/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ pub fn main() {
.unwrap();

let mut echo_service_http = service::echo::echo_service_http();
echo_service_http.add_tcp("0.0.0.0:6145");
let mut options = pingora::listeners::TcpSocketOptions::default();
options.tcp_fastopen = Some(10);
echo_service_http.add_tcp_with_settings("0.0.0.0:6145", options);
echo_service_http.add_uds("/tmp/echo.sock", None);

let dynamic_cert = DynamicCert::new(&cert_path, &key_path);
Expand Down

0 comments on commit 34b2a35

Please sign in to comment.