diff --git a/README.md b/README.md index a66e4f3ef750..127bf0880728 100644 --- a/README.md +++ b/README.md @@ -458,6 +458,9 @@ Example configuration: // TCP_NODELAY "no_delay": false, + // Enables `SO_KEEPALIVE` and set `TCP_KEEPIDLE`, `TCP_KEEPINTVL` to the specified seconds + "keep_alive": 15, + // Soft and Hard limit of file descriptors on *NIX systems "nofile": 10240, diff --git a/crates/shadowsocks-service/src/config.rs b/crates/shadowsocks-service/src/config.rs index c25fffa62290..6af9b966c39b 100644 --- a/crates/shadowsocks-service/src/config.rs +++ b/crates/shadowsocks-service/src/config.rs @@ -123,6 +123,8 @@ struct SSConfig { mode: Option, #[serde(skip_serializing_if = "Option::is_none")] no_delay: Option, + #[serde(skip_serializing_if = "Option::is_none")] + keep_alive: Option, #[cfg(all(unix, not(target_os = "android")))] #[serde(skip_serializing_if = "Option::is_none")] nofile: Option, @@ -769,6 +771,9 @@ pub struct Config { pub no_delay: bool, /// Set `TCP_FASTOPEN` socket option pub fast_open: bool, + /// Set TCP Keep-Alive duration, will set both `TCP_KEEPIDLE` and `TCP_KEEPINTVL` + pub keep_alive: Option, + /// `RLIMIT_NOFILE` option for *nix systems #[cfg(all(unix, not(target_os = "android")))] pub nofile: Option, @@ -883,6 +888,8 @@ impl Config { no_delay: false, fast_open: false, + keep_alive: None, + #[cfg(all(unix, not(target_os = "android")))] nofile: None, @@ -1374,6 +1381,11 @@ impl Config { nconfig.fast_open = b; } + // TCP Keep-Alive + if let Some(d) = config.keep_alive { + nconfig.keep_alive = Some(Duration::from_secs(d)); + } + // UDP nconfig.udp_timeout = config.udp_timeout.map(Duration::from_secs); @@ -1864,6 +1876,10 @@ impl fmt::Display for Config { jconf.fast_open = Some(self.fast_open); } + if let Some(keepalive) = self.keep_alive { + jconf.keep_alive = Some(keepalive.as_secs()); + } + match self.dns { DnsConfig::System => {} #[cfg(feature = "trust-dns")] diff --git a/crates/shadowsocks-service/src/local/mod.rs b/crates/shadowsocks-service/src/local/mod.rs index a2098fff8bc8..901de7a8c63b 100644 --- a/crates/shadowsocks-service/src/local/mod.rs +++ b/crates/shadowsocks-service/src/local/mod.rs @@ -82,7 +82,9 @@ pub async fn run(mut config: Config) -> io::Result<()> { }; connect_opts.tcp.send_buffer_size = config.outbound_send_buffer_size; connect_opts.tcp.recv_buffer_size = config.outbound_recv_buffer_size; + connect_opts.tcp.nodelay = config.no_delay; connect_opts.tcp.fastopen = config.fast_open; + connect_opts.tcp.keepalive = config.keep_alive; context.set_connect_opts(connect_opts); let mut accept_opts = AcceptOpts::default(); @@ -90,6 +92,7 @@ pub async fn run(mut config: Config) -> io::Result<()> { accept_opts.tcp.recv_buffer_size = config.inbound_recv_buffer_size; accept_opts.tcp.nodelay = config.no_delay; accept_opts.tcp.fastopen = config.fast_open; + accept_opts.tcp.keepalive = config.keep_alive; if let Some(resolver) = build_dns_resolver(config.dns, config.ipv6_first, context.connect_opts_ref()).await { context.set_dns_resolver(Arc::new(resolver)); @@ -215,9 +218,6 @@ pub async fn run(mut config: Config) -> io::Result<()> { if let Some(b) = local_config.udp_addr { server.set_udp_bind_addr(b.clone()); } - if config.no_delay { - server.set_nodelay(true); - } vfut.push(async move { server.run(&client_addr, balancer).await }.boxed()); } @@ -236,9 +236,6 @@ pub async fn run(mut config: Config) -> io::Result<()> { server.set_udp_expiry_duration(d); } server.set_mode(local_config.mode); - if config.no_delay { - server.set_nodelay(true); - } let udp_addr = local_config.udp_addr.unwrap_or_else(|| client_addr.clone()); vfut.push(async move { server.run(&client_addr, &udp_addr, balancer).await }.boxed()); @@ -262,9 +259,6 @@ pub async fn run(mut config: Config) -> io::Result<()> { server.set_udp_expiry_duration(d); } server.set_mode(local_config.mode); - if config.no_delay { - server.set_nodelay(true); - } server.set_tcp_redir(local_config.tcp_redir); server.set_udp_redir(local_config.udp_redir); diff --git a/crates/shadowsocks-service/src/local/redir/server.rs b/crates/shadowsocks-service/src/local/redir/server.rs index 28420429342b..34fa1260beea 100644 --- a/crates/shadowsocks-service/src/local/redir/server.rs +++ b/crates/shadowsocks-service/src/local/redir/server.rs @@ -18,7 +18,6 @@ pub struct Redir { mode: Mode, udp_expiry_duration: Option, udp_capacity: Option, - nodelay: bool, tcp_redir: RedirType, udp_redir: RedirType, } @@ -43,7 +42,6 @@ impl Redir { mode: Mode::TcpOnly, udp_expiry_duration: None, udp_capacity: None, - nodelay: false, tcp_redir: RedirType::tcp_default(), udp_redir: RedirType::udp_default(), } @@ -64,11 +62,6 @@ impl Redir { self.mode = mode; } - /// Set `TCP_NODELAY` - pub fn set_nodelay(&mut self, nodelay: bool) { - self.nodelay = nodelay; - } - /// Set transparent proxy type of TCP relay, which is platform dependent pub fn set_tcp_redir(&mut self, ty: RedirType) { self.tcp_redir = ty; @@ -96,14 +89,7 @@ impl Redir { } async fn run_tcp_tunnel(&self, client_config: &ServerAddr, balancer: PingBalancer) -> io::Result<()> { - run_tcp_redir( - self.context.clone(), - client_config, - balancer, - self.tcp_redir, - self.nodelay, - ) - .await + run_tcp_redir(self.context.clone(), client_config, balancer, self.tcp_redir).await } async fn run_udp_tunnel(&self, client_config: &ServerAddr, balancer: PingBalancer) -> io::Result<()> { diff --git a/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs b/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs index 71198df2b655..2272bc0b2cb6 100644 --- a/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs +++ b/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs @@ -39,17 +39,12 @@ async fn establish_client_tcp_redir<'a>( mut stream: TcpStream, peer_addr: SocketAddr, addr: &Address, - nodelay: bool, ) -> io::Result<()> { let server = balancer.best_tcp_server(); let svr_cfg = server.server_config(); let mut remote = AutoProxyClientStream::connect(context, &server, addr).await?; - if nodelay { - remote.set_nodelay(true)?; - } - establish_tcp_tunnel(svr_cfg, &mut stream, &mut remote, peer_addr, addr).await } @@ -59,20 +54,7 @@ async fn handle_redir_client( s: TcpStream, peer_addr: SocketAddr, mut daddr: SocketAddr, - nodelay: bool, ) -> io::Result<()> { - // let svr_cfg = server.server_config(); - // - // if let Err(err) = s.set_keepalive(svr_cfg.timeout()) { - // error!("failed to set keep alive: {:?}", err); - // } - - if nodelay { - if let Err(err) = s.set_nodelay(true) { - error!("failed to set TCP_NODELAY on accepted socket, error: {:?}", err); - } - } - // Get forward address from socket // // Try to convert IPv4 mapped IPv6 address for dual-stack mode. @@ -82,7 +64,7 @@ async fn handle_redir_client( } } let target_addr = Address::from(daddr); - establish_client_tcp_redir(context, balancer, s, peer_addr, &target_addr, nodelay).await + establish_client_tcp_redir(context, balancer, s, peer_addr, &target_addr).await } pub async fn run_tcp_redir( @@ -90,7 +72,6 @@ pub async fn run_tcp_redir( client_config: &ServerAddr, balancer: PingBalancer, redir_ty: RedirType, - nodelay: bool, ) -> io::Result<()> { let listener = match *client_config { ServerAddr::SocketAddr(ref saddr) => TcpListener::bind_redir(redir_ty, *saddr).await?, @@ -137,7 +118,7 @@ pub async fn run_tcp_redir( } }; - if let Err(err) = handle_redir_client(context, balancer, socket, peer_addr, dst_addr, nodelay).await { + if let Err(err) = handle_redir_client(context, balancer, socket, peer_addr, dst_addr).await { debug!("TCP redirect client, error: {:?}", err); } }); diff --git a/crates/shadowsocks-service/src/local/socks/server/mod.rs b/crates/shadowsocks-service/src/local/socks/server/mod.rs index 894cee301d27..cad8cb332a33 100644 --- a/crates/shadowsocks-service/src/local/socks/server/mod.rs +++ b/crates/shadowsocks-service/src/local/socks/server/mod.rs @@ -24,7 +24,6 @@ pub struct Socks { udp_expiry_duration: Option, udp_capacity: Option, udp_bind_addr: Option, - nodelay: bool, } impl Default for Socks { @@ -48,7 +47,6 @@ impl Socks { udp_expiry_duration: None, udp_capacity: None, udp_bind_addr: None, - nodelay: false, } } @@ -75,11 +73,6 @@ impl Socks { self.udp_bind_addr = Some(a); } - /// Set `TCP_NODELAY` - pub fn set_nodelay(&mut self, nodelay: bool) { - self.nodelay = nodelay; - } - /// Start serving pub async fn run(self, client_config: &ServerAddr, balancer: PingBalancer) -> io::Result<()> { let mut vfut = Vec::new(); @@ -133,13 +126,8 @@ impl Socks { } }; - if self.nodelay { - let _ = stream.set_nodelay(true); - } - let balancer = balancer.clone(); let context = self.context.clone(); - let nodelay = self.nodelay; let udp_bind_addr = udp_bind_addr.clone(); let mode = self.mode; @@ -150,7 +138,6 @@ impl Socks { balancer, peer_addr, mode, - nodelay, )); } } @@ -163,7 +150,6 @@ impl Socks { balancer: PingBalancer, peer_addr: SocketAddr, mode: Mode, - nodelay: bool, ) -> io::Result<()> { use std::io::ErrorKind; @@ -175,12 +161,12 @@ impl Socks { match version_buffer[0] { 0x04 => { - let handler = Socks4TcpHandler::new(context, nodelay, balancer, mode); + let handler = Socks4TcpHandler::new(context, balancer, mode); handler.handle_socks4_client(stream, peer_addr).await } 0x05 => { - let handler = Socks5TcpHandler::new(context, udp_bind_addr, nodelay, balancer, mode); + let handler = Socks5TcpHandler::new(context, udp_bind_addr, balancer, mode); handler.handle_socks5_client(stream, peer_addr).await } @@ -200,9 +186,8 @@ impl Socks { balancer: PingBalancer, peer_addr: SocketAddr, mode: Mode, - nodelay: bool, ) -> io::Result<()> { - let handler = Socks5TcpHandler::new(context, udp_bind_addr, nodelay, balancer, mode); + let handler = Socks5TcpHandler::new(context, udp_bind_addr, balancer, mode); handler.handle_socks5_client(stream, peer_addr).await } diff --git a/crates/shadowsocks-service/src/local/socks/server/socks4/tcprelay.rs b/crates/shadowsocks-service/src/local/socks/server/socks4/tcprelay.rs index f4b5798a53a8..d4bdf45a42a7 100644 --- a/crates/shadowsocks-service/src/local/socks/server/socks4/tcprelay.rs +++ b/crates/shadowsocks-service/src/local/socks/server/socks4/tcprelay.rs @@ -24,16 +24,14 @@ use crate::local::socks::socks4::{Address, Command, HandshakeRequest, HandshakeR pub struct Socks4TcpHandler { context: Arc, - nodelay: bool, balancer: PingBalancer, mode: Mode, } impl Socks4TcpHandler { - pub fn new(context: Arc, nodelay: bool, balancer: PingBalancer, mode: Mode) -> Socks4TcpHandler { + pub fn new(context: Arc, balancer: PingBalancer, mode: Mode) -> Socks4TcpHandler { Socks4TcpHandler { context, - nodelay, balancer, mode, } @@ -108,10 +106,6 @@ impl Socks4TcpHandler { } }; - if self.nodelay { - remote.set_nodelay(true)?; - } - // NOTE: Transfer all buffered data before unwrap, or these data will be lost let buffer = stream.buffer(); if !buffer.is_empty() { diff --git a/crates/shadowsocks-service/src/local/socks/server/socks5/tcprelay.rs b/crates/shadowsocks-service/src/local/socks/server/socks5/tcprelay.rs index 008d92bd3d75..2d6331834f12 100644 --- a/crates/shadowsocks-service/src/local/socks/server/socks5/tcprelay.rs +++ b/crates/shadowsocks-service/src/local/socks/server/socks5/tcprelay.rs @@ -36,7 +36,6 @@ use crate::{ pub struct Socks5TcpHandler { context: Arc, udp_bind_addr: Option>, - nodelay: bool, balancer: PingBalancer, mode: Mode, } @@ -45,14 +44,12 @@ impl Socks5TcpHandler { pub fn new( context: Arc, udp_bind_addr: Option>, - nodelay: bool, balancer: PingBalancer, mode: Mode, ) -> Socks5TcpHandler { Socks5TcpHandler { context, udp_bind_addr, - nodelay, balancer, mode, } @@ -163,10 +160,6 @@ impl Socks5TcpHandler { } }; - if self.nodelay { - remote.set_nodelay(true)?; - } - establish_tcp_tunnel(svr_cfg, &mut stream, &mut remote, peer_addr, &target_addr).await } diff --git a/crates/shadowsocks-service/src/local/tunnel/server.rs b/crates/shadowsocks-service/src/local/tunnel/server.rs index 91a03d5f76bf..d44e122ee301 100644 --- a/crates/shadowsocks-service/src/local/tunnel/server.rs +++ b/crates/shadowsocks-service/src/local/tunnel/server.rs @@ -16,7 +16,6 @@ pub struct Tunnel { mode: Mode, udp_expiry_duration: Option, udp_capacity: Option, - nodelay: bool, } impl Tunnel { @@ -34,7 +33,6 @@ impl Tunnel { mode: Mode::TcpOnly, udp_expiry_duration: None, udp_capacity: None, - nodelay: false, } } @@ -53,11 +51,6 @@ impl Tunnel { self.mode = mode; } - /// Set `TCP_NODELAY` - pub fn set_nodelay(&mut self, nodelay: bool) { - self.nodelay = nodelay; - } - /// Start serving pub async fn run(self, tcp_addr: &ServerAddr, udp_addr: &ServerAddr, balancer: PingBalancer) -> io::Result<()> { let mut vfut = Vec::new(); @@ -75,14 +68,7 @@ impl Tunnel { } async fn run_tcp_tunnel(&self, client_config: &ServerAddr, balancer: PingBalancer) -> io::Result<()> { - run_tcp_tunnel( - self.context.clone(), - client_config, - balancer, - &self.forward_addr, - self.nodelay, - ) - .await + run_tcp_tunnel(self.context.clone(), client_config, balancer, &self.forward_addr).await } async fn run_udp_tunnel(&self, client_config: &ServerAddr, balancer: PingBalancer) -> io::Result<()> { diff --git a/crates/shadowsocks-service/src/local/tunnel/tcprelay.rs b/crates/shadowsocks-service/src/local/tunnel/tcprelay.rs index 2b6b5c65a270..88ead2caa395 100644 --- a/crates/shadowsocks-service/src/local/tunnel/tcprelay.rs +++ b/crates/shadowsocks-service/src/local/tunnel/tcprelay.rs @@ -18,7 +18,6 @@ pub async fn run_tcp_tunnel( client_config: &ServerAddr, balancer: PingBalancer, forward_addr: &Address, - nodelay: bool, ) -> io::Result<()> { let listener = match *client_config { ServerAddr::SocketAddr(ref saddr) => ShadowTcpListener::bind_with_opts(saddr, context.accept_opts()).await?, @@ -42,10 +41,6 @@ pub async fn run_tcp_tunnel( } }; - if nodelay { - let _ = stream.set_nodelay(true); - } - let balancer = balancer.clone(); let forward_addr = forward_addr.clone(); @@ -55,7 +50,6 @@ pub async fn run_tcp_tunnel( balancer, peer_addr, forward_addr, - nodelay, )); } } @@ -66,7 +60,6 @@ async fn handle_tcp_client( balancer: PingBalancer, peer_addr: SocketAddr, forward_addr: Address, - nodelay: bool, ) -> io::Result<()> { let server = balancer.best_tcp_server(); let svr_cfg = server.server_config(); @@ -80,9 +73,5 @@ async fn handle_tcp_client( let mut remote = AutoProxyClientStream::connect_proxied(context, &server, &forward_addr).await?; - if nodelay { - remote.set_nodelay(true)?; - } - establish_tcp_tunnel(svr_cfg, &mut stream, &mut remote, peer_addr, &forward_addr).await } diff --git a/crates/shadowsocks-service/src/manager/mod.rs b/crates/shadowsocks-service/src/manager/mod.rs index 5fc394b3ae56..faddc32f632c 100644 --- a/crates/shadowsocks-service/src/manager/mod.rs +++ b/crates/shadowsocks-service/src/manager/mod.rs @@ -51,12 +51,14 @@ pub async fn run(config: Config) -> io::Result<()> { connect_opts.tcp.recv_buffer_size = config.outbound_recv_buffer_size; connect_opts.tcp.nodelay = config.no_delay; connect_opts.tcp.fastopen = config.fast_open; + connect_opts.tcp.keepalive = config.keep_alive; let mut accept_opts = AcceptOpts::default(); accept_opts.tcp.send_buffer_size = config.inbound_send_buffer_size; accept_opts.tcp.recv_buffer_size = config.inbound_recv_buffer_size; accept_opts.tcp.nodelay = config.no_delay; accept_opts.tcp.fastopen = config.fast_open; + accept_opts.tcp.keepalive = config.keep_alive; if let Some(resolver) = build_dns_resolver(config.dns, config.ipv6_first, &connect_opts).await { manager.set_dns_resolver(Arc::new(resolver)); diff --git a/crates/shadowsocks-service/src/server/mod.rs b/crates/shadowsocks-service/src/server/mod.rs index 0c09510b5466..9f93921db777 100644 --- a/crates/shadowsocks-service/src/server/mod.rs +++ b/crates/shadowsocks-service/src/server/mod.rs @@ -64,12 +64,14 @@ pub async fn run(config: Config) -> io::Result<()> { connect_opts.tcp.recv_buffer_size = config.outbound_recv_buffer_size; connect_opts.tcp.nodelay = config.no_delay; connect_opts.tcp.fastopen = config.fast_open; + connect_opts.tcp.keepalive = config.keep_alive; let mut accept_opts = AcceptOpts::default(); accept_opts.tcp.send_buffer_size = config.inbound_send_buffer_size; accept_opts.tcp.recv_buffer_size = config.inbound_recv_buffer_size; accept_opts.tcp.nodelay = config.no_delay; accept_opts.tcp.fastopen = config.fast_open; + accept_opts.tcp.keepalive = config.keep_alive; let resolver = build_dns_resolver(config.dns, config.ipv6_first, &connect_opts) .await diff --git a/crates/shadowsocks/Cargo.toml b/crates/shadowsocks/Cargo.toml index 404a93378e9f..034a750d5942 100644 --- a/crates/shadowsocks/Cargo.toml +++ b/crates/shadowsocks/Cargo.toml @@ -54,7 +54,7 @@ futures = "0.3" async-trait = "0.1" mio = "0.7" -socket2 = "0.4" +socket2 = { version = "0.4", features = ["all"] } tokio = { version = "1.5", features = ["io-util", "macros", "net", "parking_lot", "process", "rt", "sync", "time"] } trust-dns-resolver = { version = "0.20", optional = true } diff --git a/crates/shadowsocks/src/net/option.rs b/crates/shadowsocks/src/net/option.rs index b53b6bace314..ac79acb5d553 100644 --- a/crates/shadowsocks/src/net/option.rs +++ b/crates/shadowsocks/src/net/option.rs @@ -1,6 +1,6 @@ //! Options for connecting to remote server -use std::net::IpAddr; +use std::{net::IpAddr, time::Duration}; /// Options for connecting to TCP remote server #[derive(Debug, Clone)] @@ -17,8 +17,9 @@ pub struct TcpSocketOpts { /// `TCP_FASTOPEN`, enables TFO pub fastopen: bool, - /// `TCP_KEEPALIVE`, enables keep-alive messages on connection-oriented sockets - pub keepalive: bool, + /// `SO_KEEPALIVE` and sets `TCP_KEEPIDLE`, `TCP_KEEPINTVL` and `TCP_KEEPCNT` respectly, + /// enables keep-alive messages on connection-oriented sockets + pub keepalive: Option, } impl Default for TcpSocketOpts { @@ -28,7 +29,7 @@ impl Default for TcpSocketOpts { recv_buffer_size: None, nodelay: false, fastopen: false, - keepalive: true, + keepalive: None, } } } diff --git a/crates/shadowsocks/src/net/sys/mod.rs b/crates/shadowsocks/src/net/sys/mod.rs index 864603008439..dfe69ebfcc2a 100644 --- a/crates/shadowsocks/src/net/sys/mod.rs +++ b/crates/shadowsocks/src/net/sys/mod.rs @@ -55,24 +55,38 @@ fn set_common_sockopt_after_connect(stream: &tokio::net::TcpStream, opts: &Conne #[cfg(unix)] #[inline] fn set_common_sockopt_after_connect_sys(stream: &tokio::net::TcpStream, opts: &ConnectOpts) -> io::Result<()> { - use socket2::Socket; + use socket2::{Socket, TcpKeepalive}; use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}; let socket = unsafe { Socket::from_raw_fd(stream.as_raw_fd()) }; macro_rules! try_sockopt { - ($socket:ident . $func:ident ($($arg:expr),*)) => { - match $socket . $func ($($arg),*) { - Ok(e) => e, - Err(err) => { - let _ = socket.into_raw_fd(); - return Err(err); - } - } - }; + ($socket:ident . $func:ident ($($arg:expr),*)) => { + match $socket . $func ($($arg),*) { + Ok(e) => e, + Err(err) => { + let _ = socket.into_raw_fd(); + return Err(err); + } } + }; + } + + if let Some(keepalive_duration) = opts.tcp.keepalive { + let mut keepalive = TcpKeepalive::new().with_time(keepalive_duration); + #[cfg(any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + ))] + { + keepalive = keepalive.with_interval(keepalive_duration); + } - try_sockopt!(socket.set_keepalive(opts.tcp.keepalive)); + try_sockopt!(socket.set_tcp_keepalive(&keepalive)); + } let _ = socket.into_raw_fd(); Ok(()) @@ -81,24 +95,29 @@ fn set_common_sockopt_after_connect_sys(stream: &tokio::net::TcpStream, opts: &C #[cfg(windows)] #[inline] fn set_common_sockopt_after_connect_sys(stream: &tokio::net::TcpStream, opts: &ConnectOpts) -> io::Result<()> { - use socket2::Socket; + use socket2::{Socket, TcpKeepalive}; use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket}; let socket = unsafe { Socket::from_raw_socket(stream.as_raw_socket()) }; macro_rules! try_sockopt { - ($socket:ident . $func:ident ($($arg:expr),*)) => { - match $socket . $func ($($arg),*) { - Ok(e) => e, - Err(err) => { - let _ = socket.into_raw_socket(); - return Err(err); - } - } - }; + ($socket:ident . $func:ident ($($arg:expr),*)) => { + match $socket . $func ($($arg),*) { + Ok(e) => e, + Err(err) => { + let _ = socket.into_raw_socket(); + return Err(err); + } } + }; + } - try_sockopt!(socket.set_keepalive(opts.tcp.keepalive)); + if let Some(keepalive_duration) = opts.tcp.keepalive { + let keepalive = TcpKeepalive::new() + .with_time(keepalive_duration) + .with_interval(keepalive_duration); + try_sockopt!(socket.set_tcp_keepalive(&keepalive)); + } let _ = socket.into_raw_socket(); Ok(()) diff --git a/crates/shadowsocks/src/net/tcp.rs b/crates/shadowsocks/src/net/tcp.rs index c8c9dbb26ea9..fd2f23a51084 100644 --- a/crates/shadowsocks/src/net/tcp.rs +++ b/crates/shadowsocks/src/net/tcp.rs @@ -15,7 +15,7 @@ use std::{ use futures::{future, ready}; use log::{debug, warn}; use pin_project::pin_project; -use socket2::Socket; +use socket2::{Socket, TcpKeepalive}; use tokio::{ io::{AsyncRead, AsyncWrite, ReadBuf}, net::{TcpListener as TokioTcpListener, TcpSocket, TcpStream as TokioTcpStream}, @@ -265,7 +265,22 @@ fn setsockopt_with_opt(f: &tokio::net::TcpStream, opts: &AcceptOpts) -> io::Resu } try_sockopt!(socket.set_nodelay(opts.tcp.nodelay)); - try_sockopt!(socket.set_keepalive(opts.tcp.keepalive)); + + if let Some(keepalive_duration) = opts.tcp.keepalive { + let mut keepalive = TcpKeepalive::new().with_time(keepalive_duration); + #[cfg(any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + ))] + { + keepalive = keepalive.with_interval(keepalive_duration); + } + + try_sockopt!(socket.set_tcp_keepalive(&keepalive)); + } let _ = socket.into_raw_fd(); Ok(()) @@ -296,7 +311,13 @@ fn setsockopt_with_opt(f: &tokio::net::TcpStream, opts: &AcceptOpts) -> io::Resu } try_sockopt!(socket.set_nodelay(opts.tcp.nodelay)); - try_sockopt!(socket.set_keepalive(opts.tcp.keepalive)); + + if let Some(keepalive_duration) = opts.tcp.keepalive { + let keepalive = TcpKeepalive::new() + .with_time(keepalive_duration) + .with_interval(keepalive_duration); + try_sockopt!(socket.set_tcp_keepalive(&keepalive)); + } let _ = socket.into_raw_socket(); Ok(())