Skip to content

Commit 16df533

Browse files
panjf2000gopherbot
authored andcommitted
net: bifurcate the TCP Keep-Alive mechanism into Solaris and illumos
Fixes #65812 Change-Id: I63facb32eeddbe9b6e0279f1c039779ba2e2ab7d Reviewed-on: https://go-review.googlesource.com/c/go/+/575015 Auto-Submit: Ian Lance Taylor <[email protected]> Run-TryBot: Andy Pan <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Damien Neil <[email protected]> Commit-Queue: Ian Lance Taylor <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 519f6a0 commit 16df533

7 files changed

+136
-10
lines changed

src/net/tcpconn_keepalive_conf_posix_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || windows
5+
//go:build aix || darwin || dragonfly || freebsd || illumos || linux || netbsd || windows
66

77
package net
88

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build illumos
6+
7+
package net
8+
9+
import (
10+
"syscall"
11+
"testing"
12+
"time"
13+
)
14+
15+
func getCurrentKeepAliveSettings(fd fdType) (cfg KeepAliveConfig, err error) {
16+
tcpKeepAlive, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE)
17+
if err != nil {
18+
return
19+
}
20+
tcpKeepAliveIdle, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall_TCP_KEEPIDLE)
21+
if err != nil {
22+
return
23+
}
24+
tcpKeepAliveInterval, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall_TCP_KEEPINTVL)
25+
if err != nil {
26+
return
27+
}
28+
tcpKeepAliveCount, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall_TCP_KEEPCNT)
29+
if err != nil {
30+
return
31+
}
32+
cfg = KeepAliveConfig{
33+
Enable: tcpKeepAlive != 0,
34+
Idle: time.Duration(tcpKeepAliveIdle) * time.Second,
35+
Interval: time.Duration(tcpKeepAliveInterval) * time.Second,
36+
Count: tcpKeepAliveCount,
37+
}
38+
return
39+
}
40+
41+
func verifyKeepAliveSettings(t *testing.T, fd fdType, oldCfg, cfg KeepAliveConfig) {
42+
const defaultTcpKeepAliveAbortThreshold = 8 * time.Minute // default value on illumos
43+
44+
if cfg.Idle == 0 {
45+
cfg.Idle = defaultTCPKeepAliveIdle
46+
}
47+
if cfg.Interval == 0 {
48+
cfg.Interval = defaultTCPKeepAliveInterval
49+
}
50+
if cfg.Count == 0 {
51+
cfg.Count = defaultTCPKeepAliveCount
52+
}
53+
54+
if cfg.Idle == -1 {
55+
cfg.Idle = oldCfg.Idle
56+
}
57+
// Check out the comment on KeepAliveConfig and the illumos code:
58+
// https://github.com/illumos/illumos-gate/blob/0886dcadf4b2cd677c3b944167f0d16ccb243616/usr/src/uts/common/inet/tcp/tcp_opt_data.c#L786-L861
59+
tcpKeepAliveAbortThreshold := defaultTcpKeepAliveAbortThreshold
60+
switch {
61+
case cfg.Interval == -1 && cfg.Count == -1:
62+
cfg.Interval = oldCfg.Interval
63+
cfg.Count = oldCfg.Count
64+
case cfg.Interval == -1 && cfg.Count > 0:
65+
cfg.Interval = defaultTcpKeepAliveAbortThreshold / time.Duration(cfg.Count)
66+
case cfg.Count == -1 && cfg.Interval > 0:
67+
cfg.Count = int(defaultTcpKeepAliveAbortThreshold / cfg.Interval)
68+
case cfg.Interval > 0 && cfg.Count > 0:
69+
// TCP_KEEPALIVE_ABORT_THRESHOLD will be recalculated only when both TCP_KEEPINTVL
70+
// and TCP_KEEPCNT are set, otherwise it will remain the default value.
71+
tcpKeepAliveAbortThreshold = cfg.Interval * time.Duration(cfg.Count)
72+
}
73+
74+
tcpKeepAlive, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE)
75+
if err != nil {
76+
t.Fatal(err)
77+
}
78+
if (tcpKeepAlive != 0) != cfg.Enable {
79+
t.Fatalf("SO_KEEPALIVE: got %t; want %t", tcpKeepAlive != 0, cfg.Enable)
80+
}
81+
82+
tcpKeepAliveIdle, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall_TCP_KEEPIDLE)
83+
if err != nil {
84+
t.Fatal(err)
85+
}
86+
if time.Duration(tcpKeepAliveIdle)*time.Second != cfg.Idle {
87+
t.Fatalf("TCP_KEEPIDLE: got %ds; want %v", tcpKeepAliveIdle, cfg.Idle)
88+
}
89+
tcpKeepAliveThreshold, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE_THRESHOLD)
90+
if err != nil {
91+
t.Fatal(err)
92+
}
93+
if time.Duration(tcpKeepAliveThreshold)*time.Millisecond != cfg.Idle {
94+
t.Fatalf("TCP_KEEPALIVE_THRESHOLD: got %dms; want %v", tcpKeepAliveThreshold, cfg.Idle)
95+
}
96+
97+
tcpKeepAliveInterval, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall_TCP_KEEPINTVL)
98+
if err != nil {
99+
t.Fatal(err)
100+
}
101+
if time.Duration(tcpKeepAliveInterval)*time.Second != cfg.Interval {
102+
t.Fatalf("TCP_KEEPINTVL: got %ds; want %v", tcpKeepAliveInterval, cfg.Interval)
103+
}
104+
105+
tcpKeepAliveCount, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall_TCP_KEEPCNT)
106+
if err != nil {
107+
t.Fatal(err)
108+
}
109+
if tcpKeepAliveCount != cfg.Count {
110+
t.Fatalf("TCP_KEEPCNT: got %d; want %d", tcpKeepAliveCount, cfg.Count)
111+
}
112+
113+
tcpKeepAliveAbortInterval, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE_ABORT_THRESHOLD)
114+
if err != nil {
115+
t.Fatal(err)
116+
}
117+
if time.Duration(tcpKeepAliveAbortInterval)*time.Millisecond != tcpKeepAliveAbortThreshold {
118+
t.Fatalf("TCP_KEEPALIVE_ABORT_THRESHOLD: got %dms; want %v", tcpKeepAliveAbortInterval, tcpKeepAliveAbortThreshold)
119+
}
120+
}

src/net/tcpconn_keepalive_solaris_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
//go:build solaris
5+
//go:build solaris && !illumos
66

77
package net
88

src/net/tcpconn_keepalive_test.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -138,18 +138,15 @@ func TestTCPConnKeepAliveConfig(t *testing.T) {
138138
if err := ls.buildup(handler); err != nil {
139139
t.Fatal(err)
140140
}
141-
ra, err := ResolveTCPAddr("tcp", ls.Listener.Addr().String())
142-
if err != nil {
143-
t.Fatal(err)
144-
}
145141
for _, cfg := range testConfigs {
146-
c, err := DialTCP("tcp", nil, ra)
142+
d := Dialer{KeepAlive: -1} // avoid setting default values before the test
143+
c, err := d.Dial("tcp", ls.Listener.Addr().String())
147144
if err != nil {
148145
t.Fatal(err)
149146
}
150147
defer c.Close()
151148

152-
sc, err := c.SyscallConn()
149+
sc, err := c.(*TCPConn).SyscallConn()
153150
if err != nil {
154151
t.Fatal(err)
155152
}
@@ -167,7 +164,7 @@ func TestTCPConnKeepAliveConfig(t *testing.T) {
167164
t.Fatal(errHook)
168165
}
169166

170-
if err := c.SetKeepAliveConfig(cfg); err != nil {
167+
if err := c.(*TCPConn).SetKeepAliveConfig(cfg); err != nil {
171168
t.Fatal(err)
172169
}
173170

src/net/tcpsock.go

+7
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ type TCPConn struct {
126126
// By contrast, if only one of Idle and Interval is set to a non-negative value,
127127
// the other will be set to the system default value, and ultimately,
128128
// set both Idle and Interval to negative values if you want to leave them unchanged.
129+
//
130+
// Also note that on illumos distributions like OmniOS that support TCP Keep-Alive,
131+
// setting only one of Idle and Interval to a non-negative value along with the
132+
// negative other one will result in the negative one being recalculated as the
133+
// quotient of tcp_keepalive_abort_interval(eight minutes as default) and the
134+
// non-negative one. Thus, you may as well set the other one to a non-negative
135+
// value if you've already set one of Idle and Interval.
129136
type KeepAliveConfig struct {
130137
// If Enable is true, keep-alive probes are enabled.
131138
Enable bool

src/net/tcpsockopt_solaris.go

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5+
//go:build !illumos
6+
57
package net
68

79
import (

src/net/tcpsockopt_unix.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
//go:build aix || dragonfly || freebsd || linux || netbsd
5+
//go:build aix || dragonfly || freebsd || illumos || linux || netbsd
66

77
package net
88

0 commit comments

Comments
 (0)