Skip to content

Commit 3402341

Browse files
yuhan6665ValdikSSxiaokangwangxqzr
authored
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: v2fly/v2ray-core#1395 (comment) * use sys/unix instead of syscall Suggestion from: v2fly/v2ray-core#1395 (comment) * Separate TcpKeepAliveIdle and TcpKeepAliveInterval check logic * Disable tcp keepAlive when TcpKeepAliveIdle < 0 and TcpKeepAliveInterval <= 0 Co-authored-by: xqzr <[email protected]> Co-authored-by: ValdikSS <[email protected]> Co-authored-by: Shelikhoo <[email protected]> Co-authored-by: xqzr <[email protected]>
1 parent 50b5ea5 commit 3402341

9 files changed

+182
-36
lines changed

infra/conf/transport_internet.go

+2
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@ type SocketConfig struct {
532532
DomainStrategy string `json:"domainStrategy"`
533533
DialerProxy string `json:"dialerProxy"`
534534
TCPKeepAliveInterval int32 `json:"tcpKeepAliveInterval"`
535+
TCPKeepAliveIdle int32 `json:"tcpKeepAliveIdle"`
535536
}
536537

537538
// Build implements Buildable.
@@ -579,6 +580,7 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
579580
AcceptProxyProtocol: c.AcceptProxyProtocol,
580581
DialerProxy: c.DialerProxy,
581582
TcpKeepAliveInterval: c.TCPKeepAliveInterval,
583+
TcpKeepAliveIdle: c.TCPKeepAliveIdle,
582584
}, nil
583585
}
584586

transport/internet/config.pb.go

+34-23
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

transport/internet/config.proto

+2
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,6 @@ message SocketConfig {
9494
string dialer_proxy = 9;
9595

9696
int32 tcp_keep_alive_interval = 10;
97+
98+
int32 tcp_keep_alive_idle = 11;
9799
}

transport/internet/sockopt_darwin.go

+45-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
package internet
22

33
import (
4-
"syscall"
4+
"golang.org/x/sys/unix"
55
)
66

77
const (
8-
// TCP_FASTOPEN is the socket option on darwin for TCP fast open.
9-
TCP_FASTOPEN = 0x105
108
// TCP_FASTOPEN_SERVER is the value to enable TCP fast open on darwin for server connections.
119
TCP_FASTOPEN_SERVER = 0x01
1210
// TCP_FASTOPEN_CLIENT is the value to enable TCP fast open on darwin for client connections.
13-
TCP_FASTOPEN_CLIENT = 0x02
11+
TCP_FASTOPEN_CLIENT = 0x02 // nolint: revive,stylecheck
12+
// syscall.TCP_KEEPINTVL is missing on some darwin architectures.
13+
sysTCP_KEEPINTVL = 0x101 // nolint: revive,stylecheck
1414
)
1515

1616
func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error {
@@ -20,10 +20,30 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
2020
tfo = TCP_FASTOPEN_CLIENT
2121
}
2222
if tfo >= 0 {
23-
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, TCP_FASTOPEN, tfo); err != nil {
23+
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_FASTOPEN, tfo); err != nil {
2424
return err
2525
}
2626
}
27+
28+
if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 {
29+
if config.TcpKeepAliveIdle > 0 {
30+
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_KEEPALIVE, int(config.TcpKeepAliveInterval)); err != nil {
31+
return newError("failed to set TCP_KEEPINTVL", err)
32+
}
33+
}
34+
if config.TcpKeepAliveInterval > 0 {
35+
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, sysTCP_KEEPINTVL, int(config.TcpKeepAliveIdle)); err != nil {
36+
return newError("failed to set TCP_KEEPIDLE", err)
37+
}
38+
}
39+
if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1); err != nil {
40+
return newError("failed to set SO_KEEPALIVE", err)
41+
}
42+
} else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 {
43+
if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 0); err != nil {
44+
return newError("failed to unset SO_KEEPALIVE", err)
45+
}
46+
}
2747
}
2848

2949
return nil
@@ -36,10 +56,29 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig)
3656
tfo = TCP_FASTOPEN_SERVER
3757
}
3858
if tfo >= 0 {
39-
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, TCP_FASTOPEN, tfo); err != nil {
59+
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_FASTOPEN, tfo); err != nil {
4060
return err
4161
}
4262
}
63+
if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 {
64+
if config.TcpKeepAliveIdle > 0 {
65+
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_KEEPALIVE, int(config.TcpKeepAliveInterval)); err != nil {
66+
return newError("failed to set TCP_KEEPINTVL", err)
67+
}
68+
}
69+
if config.TcpKeepAliveInterval > 0 {
70+
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, sysTCP_KEEPINTVL, int(config.TcpKeepAliveIdle)); err != nil {
71+
return newError("failed to set TCP_KEEPIDLE", err)
72+
}
73+
}
74+
if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1); err != nil {
75+
return newError("failed to set SO_KEEPALIVE", err)
76+
}
77+
} else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 {
78+
if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 0); err != nil {
79+
return newError("failed to unset SO_KEEPALIVE", err)
80+
}
81+
}
4382
}
4483

4584
return nil

transport/internet/sockopt_freebsd.go

+38
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,25 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
140140
return newError("failed to set TCP_FASTOPEN_CONNECT=", tfo).Base(err)
141141
}
142142
}
143+
if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 {
144+
if config.TcpKeepAliveIdle > 0 {
145+
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil {
146+
return newError("failed to set TCP_KEEPIDLE", err)
147+
}
148+
}
149+
if config.TcpKeepAliveInterval > 0 {
150+
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil {
151+
return newError("failed to set TCP_KEEPINTVL", err)
152+
}
153+
}
154+
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil {
155+
return newError("failed to set SO_KEEPALIVE", err)
156+
}
157+
} else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 {
158+
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil {
159+
return newError("failed to unset SO_KEEPALIVE", err)
160+
}
161+
}
143162
}
144163

145164
if config.Tproxy.IsEnabled() {
@@ -170,6 +189,25 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig)
170189
return newError("failed to set TCP_FASTOPEN=", tfo).Base(err)
171190
}
172191
}
192+
if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 {
193+
if config.TcpKeepAliveIdle > 0 {
194+
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil {
195+
return newError("failed to set TCP_KEEPIDLE", err)
196+
}
197+
}
198+
if config.TcpKeepAliveInterval > 0 {
199+
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil {
200+
return newError("failed to set TCP_KEEPINTVL", err)
201+
}
202+
}
203+
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil {
204+
return newError("failed to set SO_KEEPALIVE", err)
205+
}
206+
} else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 {
207+
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil {
208+
return newError("failed to unset SO_KEEPALIVE", err)
209+
}
210+
}
173211
}
174212

175213
if config.Tproxy.IsEnabled() {

transport/internet/sockopt_linux.go

+34-6
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,23 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
5858
}
5959
}
6060

61-
if config.TcpKeepAliveInterval != 0 {
62-
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil {
63-
return newError("failed to set TCP_KEEPINTVL", err)
61+
if config.TcpKeepAliveInterval > 0 || config.TcpKeepAliveIdle > 0 {
62+
if config.TcpKeepAliveInterval > 0 {
63+
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil {
64+
return newError("failed to set TCP_KEEPINTVL", err)
65+
}
66+
}
67+
if config.TcpKeepAliveIdle > 0 {
68+
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil {
69+
return newError("failed to set TCP_KEEPIDLE", err)
70+
}
71+
}
72+
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil {
73+
return newError("failed to set SO_KEEPALIVE", err)
74+
}
75+
} else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 {
76+
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil {
77+
return newError("failed to unset SO_KEEPALIVE", err)
6478
}
6579
}
6680
}
@@ -88,9 +102,23 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig)
88102
}
89103
}
90104

91-
if config.TcpKeepAliveInterval != 0 {
92-
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil {
93-
return newError("failed to set TCP_KEEPINTVL", err)
105+
if config.TcpKeepAliveInterval > 0 || config.TcpKeepAliveIdle > 0 {
106+
if config.TcpKeepAliveInterval > 0 {
107+
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil {
108+
return newError("failed to set TCP_KEEPINTVL", err)
109+
}
110+
}
111+
if config.TcpKeepAliveIdle > 0 {
112+
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil {
113+
return newError("failed to set TCP_KEEPIDLE", err)
114+
}
115+
}
116+
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil {
117+
return newError("failed to set SO_KEEPALIVE", err)
118+
}
119+
} else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 {
120+
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil {
121+
return newError("failed to unset SO_KEEPALIVE", err)
94122
}
95123
}
96124
}

transport/internet/sockopt_windows.go

+18
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
2525
if err := setTFO(syscall.Handle(fd), config.ParseTFOValue()); err != nil {
2626
return err
2727
}
28+
if config.TcpKeepAliveIdle > 0 {
29+
if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil {
30+
return newError("failed to set SO_KEEPALIVE", err)
31+
}
32+
} else if config.TcpKeepAliveIdle < 0 {
33+
if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil {
34+
return newError("failed to unset SO_KEEPALIVE", err)
35+
}
36+
}
2837
}
2938

3039
return nil
@@ -35,6 +44,15 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig)
3544
if err := setTFO(syscall.Handle(fd), config.ParseTFOValue()); err != nil {
3645
return err
3746
}
47+
if config.TcpKeepAliveIdle > 0 {
48+
if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil {
49+
return newError("failed to set SO_KEEPALIVE", err)
50+
}
51+
} else if config.TcpKeepAliveIdle < 0 {
52+
if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil {
53+
return newError("failed to unset SO_KEEPALIVE", err)
54+
}
55+
}
3856
}
3957

4058
return nil

transport/internet/system_dialer.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,14 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne
6969
Dest: destAddr,
7070
}, nil
7171
}
72-
72+
goStdKeepAlive := time.Duration(0)
73+
if sockopt != nil && sockopt.TcpKeepAliveIdle != 0 {
74+
goStdKeepAlive = time.Duration(-1)
75+
}
7376
dialer := &net.Dialer{
7477
Timeout: time.Second * 16,
7578
LocalAddr: resolveSrcAddr(dest.Network, src),
79+
KeepAlive: goStdKeepAlive,
7680
}
7781

7882
if sockopt != nil || len(d.controllers) > 0 {

transport/internet/system_listener.go

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strconv"
88
"strings"
99
"syscall"
10+
"time"
1011

1112
"github.com/pires/go-proxyproto"
1213
"github.com/xtls/xray-core/common/net"
@@ -50,6 +51,9 @@ func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *S
5051
network = addr.Network()
5152
address = addr.String()
5253
lc.Control = getControlFunc(ctx, sockopt, dl.controllers)
54+
if sockopt != nil && sockopt.TcpKeepAliveIdle != 0 {
55+
lc.KeepAlive = time.Duration(-1)
56+
}
5357
case *net.UnixAddr:
5458
lc.Control = nil
5559
network = addr.Network()

0 commit comments

Comments
 (0)