From: Andy Pan Date: Thu, 28 Mar 2024 07:08:38 +0000 (+0800) Subject: net: bifurcate the TCP Keep-Alive mechanism into Solaris and illumos X-Git-Tag: go1.23rc1~632 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=16df5330e410cfc702d942eb0cf3707ccdfd2c1d;p=gostls13.git 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 Run-TryBot: Andy Pan TryBot-Result: Gopher Robot Reviewed-by: Damien Neil Commit-Queue: Ian Lance Taylor Reviewed-by: Dmitri Shuralyov LUCI-TryBot-Result: Go LUCI --- diff --git a/src/net/tcpconn_keepalive_conf_posix_test.go b/src/net/tcpconn_keepalive_conf_posix_test.go index 5b57504926..3cf5f7e66c 100644 --- a/src/net/tcpconn_keepalive_conf_posix_test.go +++ b/src/net/tcpconn_keepalive_conf_posix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || windows +//go:build aix || darwin || dragonfly || freebsd || illumos || linux || netbsd || windows package net diff --git a/src/net/tcpconn_keepalive_illumos_test.go b/src/net/tcpconn_keepalive_illumos_test.go new file mode 100644 index 0000000000..56863309b6 --- /dev/null +++ b/src/net/tcpconn_keepalive_illumos_test.go @@ -0,0 +1,120 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build illumos + +package net + +import ( + "syscall" + "testing" + "time" +) + +func getCurrentKeepAliveSettings(fd fdType) (cfg KeepAliveConfig, err error) { + tcpKeepAlive, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE) + if err != nil { + return + } + tcpKeepAliveIdle, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall_TCP_KEEPIDLE) + if err != nil { + return + } + tcpKeepAliveInterval, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall_TCP_KEEPINTVL) + if err != nil { + return + } + tcpKeepAliveCount, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall_TCP_KEEPCNT) + if err != nil { + return + } + cfg = KeepAliveConfig{ + Enable: tcpKeepAlive != 0, + Idle: time.Duration(tcpKeepAliveIdle) * time.Second, + Interval: time.Duration(tcpKeepAliveInterval) * time.Second, + Count: tcpKeepAliveCount, + } + return +} + +func verifyKeepAliveSettings(t *testing.T, fd fdType, oldCfg, cfg KeepAliveConfig) { + const defaultTcpKeepAliveAbortThreshold = 8 * time.Minute // default value on illumos + + if cfg.Idle == 0 { + cfg.Idle = defaultTCPKeepAliveIdle + } + if cfg.Interval == 0 { + cfg.Interval = defaultTCPKeepAliveInterval + } + if cfg.Count == 0 { + cfg.Count = defaultTCPKeepAliveCount + } + + if cfg.Idle == -1 { + cfg.Idle = oldCfg.Idle + } + // Check out the comment on KeepAliveConfig and the illumos code: + // https://github.com/illumos/illumos-gate/blob/0886dcadf4b2cd677c3b944167f0d16ccb243616/usr/src/uts/common/inet/tcp/tcp_opt_data.c#L786-L861 + tcpKeepAliveAbortThreshold := defaultTcpKeepAliveAbortThreshold + switch { + case cfg.Interval == -1 && cfg.Count == -1: + cfg.Interval = oldCfg.Interval + cfg.Count = oldCfg.Count + case cfg.Interval == -1 && cfg.Count > 0: + cfg.Interval = defaultTcpKeepAliveAbortThreshold / time.Duration(cfg.Count) + case cfg.Count == -1 && cfg.Interval > 0: + cfg.Count = int(defaultTcpKeepAliveAbortThreshold / cfg.Interval) + case cfg.Interval > 0 && cfg.Count > 0: + // TCP_KEEPALIVE_ABORT_THRESHOLD will be recalculated only when both TCP_KEEPINTVL + // and TCP_KEEPCNT are set, otherwise it will remain the default value. + tcpKeepAliveAbortThreshold = cfg.Interval * time.Duration(cfg.Count) + } + + tcpKeepAlive, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE) + if err != nil { + t.Fatal(err) + } + if (tcpKeepAlive != 0) != cfg.Enable { + t.Fatalf("SO_KEEPALIVE: got %t; want %t", tcpKeepAlive != 0, cfg.Enable) + } + + tcpKeepAliveIdle, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall_TCP_KEEPIDLE) + if err != nil { + t.Fatal(err) + } + if time.Duration(tcpKeepAliveIdle)*time.Second != cfg.Idle { + t.Fatalf("TCP_KEEPIDLE: got %ds; want %v", tcpKeepAliveIdle, cfg.Idle) + } + tcpKeepAliveThreshold, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE_THRESHOLD) + if err != nil { + t.Fatal(err) + } + if time.Duration(tcpKeepAliveThreshold)*time.Millisecond != cfg.Idle { + t.Fatalf("TCP_KEEPALIVE_THRESHOLD: got %dms; want %v", tcpKeepAliveThreshold, cfg.Idle) + } + + tcpKeepAliveInterval, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall_TCP_KEEPINTVL) + if err != nil { + t.Fatal(err) + } + if time.Duration(tcpKeepAliveInterval)*time.Second != cfg.Interval { + t.Fatalf("TCP_KEEPINTVL: got %ds; want %v", tcpKeepAliveInterval, cfg.Interval) + } + + tcpKeepAliveCount, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall_TCP_KEEPCNT) + if err != nil { + t.Fatal(err) + } + if tcpKeepAliveCount != cfg.Count { + t.Fatalf("TCP_KEEPCNT: got %d; want %d", tcpKeepAliveCount, cfg.Count) + } + + tcpKeepAliveAbortInterval, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE_ABORT_THRESHOLD) + if err != nil { + t.Fatal(err) + } + if time.Duration(tcpKeepAliveAbortInterval)*time.Millisecond != tcpKeepAliveAbortThreshold { + t.Fatalf("TCP_KEEPALIVE_ABORT_THRESHOLD: got %dms; want %v", tcpKeepAliveAbortInterval, tcpKeepAliveAbortThreshold) + } +} diff --git a/src/net/tcpconn_keepalive_solaris_test.go b/src/net/tcpconn_keepalive_solaris_test.go index bd9dca7c5b..bb0d851aba 100644 --- a/src/net/tcpconn_keepalive_solaris_test.go +++ b/src/net/tcpconn_keepalive_solaris_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build solaris +//go:build solaris && !illumos package net diff --git a/src/net/tcpconn_keepalive_test.go b/src/net/tcpconn_keepalive_test.go index 8eb6f2ea4e..665307d1cd 100644 --- a/src/net/tcpconn_keepalive_test.go +++ b/src/net/tcpconn_keepalive_test.go @@ -138,18 +138,15 @@ func TestTCPConnKeepAliveConfig(t *testing.T) { if err := ls.buildup(handler); err != nil { t.Fatal(err) } - ra, err := ResolveTCPAddr("tcp", ls.Listener.Addr().String()) - if err != nil { - t.Fatal(err) - } for _, cfg := range testConfigs { - c, err := DialTCP("tcp", nil, ra) + d := Dialer{KeepAlive: -1} // avoid setting default values before the test + c, err := d.Dial("tcp", ls.Listener.Addr().String()) if err != nil { t.Fatal(err) } defer c.Close() - sc, err := c.SyscallConn() + sc, err := c.(*TCPConn).SyscallConn() if err != nil { t.Fatal(err) } @@ -167,7 +164,7 @@ func TestTCPConnKeepAliveConfig(t *testing.T) { t.Fatal(errHook) } - if err := c.SetKeepAliveConfig(cfg); err != nil { + if err := c.(*TCPConn).SetKeepAliveConfig(cfg); err != nil { t.Fatal(err) } diff --git a/src/net/tcpsock.go b/src/net/tcpsock.go index 701048896c..0b7984f5f7 100644 --- a/src/net/tcpsock.go +++ b/src/net/tcpsock.go @@ -126,6 +126,13 @@ type TCPConn struct { // By contrast, if only one of Idle and Interval is set to a non-negative value, // the other will be set to the system default value, and ultimately, // set both Idle and Interval to negative values if you want to leave them unchanged. +// +// Also note that on illumos distributions like OmniOS that support TCP Keep-Alive, +// setting only one of Idle and Interval to a non-negative value along with the +// negative other one will result in the negative one being recalculated as the +// quotient of tcp_keepalive_abort_interval(eight minutes as default) and the +// non-negative one. Thus, you may as well set the other one to a non-negative +// value if you've already set one of Idle and Interval. type KeepAliveConfig struct { // If Enable is true, keep-alive probes are enabled. Enable bool diff --git a/src/net/tcpsockopt_solaris.go b/src/net/tcpsockopt_solaris.go index 44eb9cd09e..ba27e13b21 100644 --- a/src/net/tcpsockopt_solaris.go +++ b/src/net/tcpsockopt_solaris.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !illumos + package net import ( diff --git a/src/net/tcpsockopt_unix.go b/src/net/tcpsockopt_unix.go index f3526e4962..1f7617897a 100644 --- a/src/net/tcpsockopt_unix.go +++ b/src/net/tcpsockopt_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || dragonfly || freebsd || linux || netbsd +//go:build aix || dragonfly || freebsd || illumos || linux || netbsd package net