From 340234166b2ee994136aa346a25f53b367d28059 Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Sun, 31 Jul 2022 09:55:40 -0400 Subject: [PATCH] Add TCPKeepAliveIdle in Sockopt option (#1166) * Add TCP keep alive idle setting * Add TCP keep alive idle setting: auto generated * Add TCP keep alive support in Linux * Add TCP keep alive support in MacOS, FreeBSD * Add TCP keep alive support in Windows * fix bug introduced in adding tcp keep alive adjustment * embed macOS const to avoid platform inconsistency * embed macOS const to avoid platform inconsistency(again) * add TCP Keep Alive support in config * use sys/unix instead of syscall Suggestion from: https://github.com/v2fly/v2ray-core/pull/1395#issuecomment-974761647 * use sys/unix instead of syscall Suggestion from: https://github.com/v2fly/v2ray-core/pull/1395#issuecomment-974761647 * Separate TcpKeepAliveIdle and TcpKeepAliveInterval check logic * Disable tcp keepAlive when TcpKeepAliveIdle < 0 and TcpKeepAliveInterval <= 0 Co-authored-by: xqzr <34030394+xqzr@users.noreply.github.com> Co-authored-by: ValdikSS Co-authored-by: Shelikhoo Co-authored-by: xqzr <34030394+xqzr@users.noreply.github.com> --- infra/conf/transport_internet.go | 2 + transport/internet/config.pb.go | 57 ++++++++++++++++----------- transport/internet/config.proto | 2 + transport/internet/sockopt_darwin.go | 51 +++++++++++++++++++++--- transport/internet/sockopt_freebsd.go | 38 ++++++++++++++++++ transport/internet/sockopt_linux.go | 40 ++++++++++++++++--- transport/internet/sockopt_windows.go | 18 +++++++++ transport/internet/system_dialer.go | 6 ++- transport/internet/system_listener.go | 4 ++ 9 files changed, 182 insertions(+), 36 deletions(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 9642b792..30626a35 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -532,6 +532,7 @@ type SocketConfig struct { DomainStrategy string `json:"domainStrategy"` DialerProxy string `json:"dialerProxy"` TCPKeepAliveInterval int32 `json:"tcpKeepAliveInterval"` + TCPKeepAliveIdle int32 `json:"tcpKeepAliveIdle"` } // Build implements Buildable. @@ -579,6 +580,7 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) { AcceptProxyProtocol: c.AcceptProxyProtocol, DialerProxy: c.DialerProxy, TcpKeepAliveInterval: c.TCPKeepAliveInterval, + TcpKeepAliveIdle: c.TCPKeepAliveIdle, }, nil } diff --git a/transport/internet/config.pb.go b/transport/internet/config.pb.go index b7cb6fb0..d53e8599 100644 --- a/transport/internet/config.pb.go +++ b/transport/internet/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 +// protoc-gen-go v1.28.1 // protoc v3.18.0 // source: transport/internet/config.proto @@ -423,6 +423,7 @@ type SocketConfig struct { DomainStrategy DomainStrategy `protobuf:"varint,8,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"domain_strategy,omitempty"` DialerProxy string `protobuf:"bytes,9,opt,name=dialer_proxy,json=dialerProxy,proto3" json:"dialer_proxy,omitempty"` TcpKeepAliveInterval int32 `protobuf:"varint,10,opt,name=tcp_keep_alive_interval,json=tcpKeepAliveInterval,proto3" json:"tcp_keep_alive_interval,omitempty"` + TcpKeepAliveIdle int32 `protobuf:"varint,11,opt,name=tcp_keep_alive_idle,json=tcpKeepAliveIdle,proto3" json:"tcp_keep_alive_idle,omitempty"` } func (x *SocketConfig) Reset() { @@ -527,6 +528,13 @@ func (x *SocketConfig) GetTcpKeepAliveInterval() int32 { return 0 } +func (x *SocketConfig) GetTcpKeepAliveIdle() int32 { + if x != nil { + return x.TcpKeepAliveIdle + } + return 0 +} + var File_transport_internet_config_proto protoreflect.FileDescriptor var file_transport_internet_config_proto_rawDesc = []byte{ @@ -579,7 +587,7 @@ var file_transport_internet_config_proto_rawDesc = []byte{ 0x12, 0x30, 0x0a, 0x13, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x22, 0x92, 0x04, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x78, 0x79, 0x22, 0xc1, 0x04, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74, 0x70, 0x72, @@ -609,27 +617,30 @@ var file_transport_internet_config_proto_rawDesc = []byte{ 0x35, 0x0a, 0x17, 0x74, 0x63, 0x70, 0x5f, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x74, 0x63, 0x70, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, - 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, - 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, - 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x2a, 0x5a, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, - 0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x01, 0x12, 0x08, - 0x0a, 0x04, 0x4d, 0x4b, 0x43, 0x50, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x65, 0x62, 0x53, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, - 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63, 0x6b, 0x65, - 0x74, 0x10, 0x05, 0x2a, 0x41, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, - 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, - 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, - 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, - 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, - 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, - 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2d, 0x0a, 0x13, 0x74, 0x63, 0x70, 0x5f, 0x6b, 0x65, + 0x65, 0x70, 0x5f, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x10, 0x74, 0x63, 0x70, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, + 0x65, 0x49, 0x64, 0x6c, 0x65, 0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, + 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, + 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x2a, 0x5a, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, + 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x54, + 0x43, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, + 0x04, 0x4d, 0x4b, 0x43, 0x50, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x65, 0x62, 0x53, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x04, + 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x10, 0x05, 0x2a, 0x41, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, + 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, + 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, + 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, + 0x49, 0x50, 0x36, 0x10, 0x03, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, + 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, + 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/transport/internet/config.proto b/transport/internet/config.proto index 83a559b0..92f70a8b 100644 --- a/transport/internet/config.proto +++ b/transport/internet/config.proto @@ -94,4 +94,6 @@ message SocketConfig { string dialer_proxy = 9; int32 tcp_keep_alive_interval = 10; + + int32 tcp_keep_alive_idle = 11; } diff --git a/transport/internet/sockopt_darwin.go b/transport/internet/sockopt_darwin.go index 399cc88f..2fae2632 100644 --- a/transport/internet/sockopt_darwin.go +++ b/transport/internet/sockopt_darwin.go @@ -1,16 +1,16 @@ package internet import ( - "syscall" + "golang.org/x/sys/unix" ) const ( - // TCP_FASTOPEN is the socket option on darwin for TCP fast open. - TCP_FASTOPEN = 0x105 // TCP_FASTOPEN_SERVER is the value to enable TCP fast open on darwin for server connections. TCP_FASTOPEN_SERVER = 0x01 // TCP_FASTOPEN_CLIENT is the value to enable TCP fast open on darwin for client connections. - TCP_FASTOPEN_CLIENT = 0x02 + TCP_FASTOPEN_CLIENT = 0x02 // nolint: revive,stylecheck + // syscall.TCP_KEEPINTVL is missing on some darwin architectures. + sysTCP_KEEPINTVL = 0x101 // nolint: revive,stylecheck ) func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error { @@ -20,10 +20,30 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf tfo = TCP_FASTOPEN_CLIENT } if tfo >= 0 { - if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, TCP_FASTOPEN, tfo); err != nil { + if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_FASTOPEN, tfo); err != nil { return err } } + + if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 { + if config.TcpKeepAliveIdle > 0 { + if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_KEEPALIVE, int(config.TcpKeepAliveInterval)); err != nil { + return newError("failed to set TCP_KEEPINTVL", err) + } + } + if config.TcpKeepAliveInterval > 0 { + if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, sysTCP_KEEPINTVL, int(config.TcpKeepAliveIdle)); err != nil { + return newError("failed to set TCP_KEEPIDLE", err) + } + } + if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1); err != nil { + return newError("failed to set SO_KEEPALIVE", err) + } + } else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 { + if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 0); err != nil { + return newError("failed to unset SO_KEEPALIVE", err) + } + } } return nil @@ -36,10 +56,29 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) tfo = TCP_FASTOPEN_SERVER } if tfo >= 0 { - if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, TCP_FASTOPEN, tfo); err != nil { + if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_FASTOPEN, tfo); err != nil { return err } } + if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 { + if config.TcpKeepAliveIdle > 0 { + if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_KEEPALIVE, int(config.TcpKeepAliveInterval)); err != nil { + return newError("failed to set TCP_KEEPINTVL", err) + } + } + if config.TcpKeepAliveInterval > 0 { + if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, sysTCP_KEEPINTVL, int(config.TcpKeepAliveIdle)); err != nil { + return newError("failed to set TCP_KEEPIDLE", err) + } + } + if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1); err != nil { + return newError("failed to set SO_KEEPALIVE", err) + } + } else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 { + if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 0); err != nil { + return newError("failed to unset SO_KEEPALIVE", err) + } + } } return nil diff --git a/transport/internet/sockopt_freebsd.go b/transport/internet/sockopt_freebsd.go index 1f10e08b..66db4be3 100644 --- a/transport/internet/sockopt_freebsd.go +++ b/transport/internet/sockopt_freebsd.go @@ -140,6 +140,25 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf return newError("failed to set TCP_FASTOPEN_CONNECT=", tfo).Base(err) } } + if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 { + if config.TcpKeepAliveIdle > 0 { + if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil { + return newError("failed to set TCP_KEEPIDLE", err) + } + } + if config.TcpKeepAliveInterval > 0 { + if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { + return newError("failed to set TCP_KEEPINTVL", err) + } + } + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { + return newError("failed to set SO_KEEPALIVE", err) + } + } else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 { + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil { + return newError("failed to unset SO_KEEPALIVE", err) + } + } } if config.Tproxy.IsEnabled() { @@ -170,6 +189,25 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) return newError("failed to set TCP_FASTOPEN=", tfo).Base(err) } } + if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 { + if config.TcpKeepAliveIdle > 0 { + if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil { + return newError("failed to set TCP_KEEPIDLE", err) + } + } + if config.TcpKeepAliveInterval > 0 { + if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { + return newError("failed to set TCP_KEEPINTVL", err) + } + } + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { + return newError("failed to set SO_KEEPALIVE", err) + } + } else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 { + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil { + return newError("failed to unset SO_KEEPALIVE", err) + } + } } if config.Tproxy.IsEnabled() { diff --git a/transport/internet/sockopt_linux.go b/transport/internet/sockopt_linux.go index 905787f9..f82970d9 100644 --- a/transport/internet/sockopt_linux.go +++ b/transport/internet/sockopt_linux.go @@ -58,9 +58,23 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf } } - if config.TcpKeepAliveInterval != 0 { - if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { - return newError("failed to set TCP_KEEPINTVL", err) + if config.TcpKeepAliveInterval > 0 || config.TcpKeepAliveIdle > 0 { + if config.TcpKeepAliveInterval > 0 { + if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { + return newError("failed to set TCP_KEEPINTVL", err) + } + } + if config.TcpKeepAliveIdle > 0 { + if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil { + return newError("failed to set TCP_KEEPIDLE", err) + } + } + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { + return newError("failed to set SO_KEEPALIVE", err) + } + } else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 { + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil { + return newError("failed to unset SO_KEEPALIVE", err) } } } @@ -88,9 +102,23 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) } } - if config.TcpKeepAliveInterval != 0 { - if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { - return newError("failed to set TCP_KEEPINTVL", err) + if config.TcpKeepAliveInterval > 0 || config.TcpKeepAliveIdle > 0 { + if config.TcpKeepAliveInterval > 0 { + if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { + return newError("failed to set TCP_KEEPINTVL", err) + } + } + if config.TcpKeepAliveIdle > 0 { + if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil { + return newError("failed to set TCP_KEEPIDLE", err) + } + } + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { + return newError("failed to set SO_KEEPALIVE", err) + } + } else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 { + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil { + return newError("failed to unset SO_KEEPALIVE", err) } } } diff --git a/transport/internet/sockopt_windows.go b/transport/internet/sockopt_windows.go index d2ce9ff7..ccc7b039 100644 --- a/transport/internet/sockopt_windows.go +++ b/transport/internet/sockopt_windows.go @@ -25,6 +25,15 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf if err := setTFO(syscall.Handle(fd), config.ParseTFOValue()); err != nil { return err } + if config.TcpKeepAliveIdle > 0 { + if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { + return newError("failed to set SO_KEEPALIVE", err) + } + } else if config.TcpKeepAliveIdle < 0 { + if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil { + return newError("failed to unset SO_KEEPALIVE", err) + } + } } return nil @@ -35,6 +44,15 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) if err := setTFO(syscall.Handle(fd), config.ParseTFOValue()); err != nil { return err } + if config.TcpKeepAliveIdle > 0 { + if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { + return newError("failed to set SO_KEEPALIVE", err) + } + } else if config.TcpKeepAliveIdle < 0 { + if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil { + return newError("failed to unset SO_KEEPALIVE", err) + } + } } return nil diff --git a/transport/internet/system_dialer.go b/transport/internet/system_dialer.go index 8058fa1b..b809618c 100644 --- a/transport/internet/system_dialer.go +++ b/transport/internet/system_dialer.go @@ -69,10 +69,14 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne Dest: destAddr, }, nil } - + goStdKeepAlive := time.Duration(0) + if sockopt != nil && sockopt.TcpKeepAliveIdle != 0 { + goStdKeepAlive = time.Duration(-1) + } dialer := &net.Dialer{ Timeout: time.Second * 16, LocalAddr: resolveSrcAddr(dest.Network, src), + KeepAlive: goStdKeepAlive, } if sockopt != nil || len(d.controllers) > 0 { diff --git a/transport/internet/system_listener.go b/transport/internet/system_listener.go index 730339f5..c4329c9e 100644 --- a/transport/internet/system_listener.go +++ b/transport/internet/system_listener.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" "syscall" + "time" "github.com/pires/go-proxyproto" "github.com/xtls/xray-core/common/net" @@ -50,6 +51,9 @@ func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *S network = addr.Network() address = addr.String() lc.Control = getControlFunc(ctx, sockopt, dl.controllers) + if sockopt != nil && sockopt.TcpKeepAliveIdle != 0 { + lc.KeepAlive = time.Duration(-1) + } case *net.UnixAddr: lc.Control = nil network = addr.Network()