mirror of
https://github.com/golang/go.git
synced 2024-09-21 10:28:27 +00:00
68a508cdaf
The net package currently uses windows.SupportFullTCPKeepAlive to know if TCP_KEEPIDLE, TCP_KEEPINTVL, and TCP_KEEPCNT are available. This function is a wrapper over the undocumented RtlGetNtVersionNumbers API, which tests if the Windows version is at least 10.0.16299. This approach artificially limits the use of TCP_KEEPCNT, which is available since Windows 10.0.15063. It also uses an undocumented API, which is not something we want to rely on. This CL removes windows.SupportFullTCPKeepAlive in favor of dedicated proves for each option which are not based on the Windows version. While here, remove some assertions in setKeepAliveCount. It is better to let the system decide if the value is valid or not. Updates #65817. Cq-Include-Trybots: luci.golang.try:gotip-windows-arm64 Change-Id: I0fe70d46c8675eab06c0e4628cf68571b6e50b80 Reviewed-on: https://go-review.googlesource.com/c/go/+/570077 Reviewed-by: Than McIntosh <thanm@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
119 lines
3.8 KiB
Go
119 lines
3.8 KiB
Go
// Copyright 2009 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.
|
|
|
|
package net
|
|
|
|
import (
|
|
"internal/syscall/windows"
|
|
"os"
|
|
"runtime"
|
|
"syscall"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
// Default values of KeepAliveTime and KeepAliveInterval on Windows,
|
|
// check out https://learn.microsoft.com/en-us/windows/win32/winsock/sio-keepalive-vals#remarks for details.
|
|
const (
|
|
defaultKeepAliveIdle = 2 * time.Hour
|
|
defaultKeepAliveInterval = time.Second
|
|
)
|
|
|
|
func setKeepAliveIdle(fd *netFD, d time.Duration) error {
|
|
if !windows.SupportTCPKeepAliveIdle() {
|
|
return setKeepAliveIdleAndInterval(fd, d, -1)
|
|
}
|
|
|
|
if d == 0 {
|
|
d = defaultTCPKeepAliveIdle
|
|
} else if d < 0 {
|
|
return nil
|
|
}
|
|
// The kernel expects seconds so round to next highest second.
|
|
secs := int(roundDurationUp(d, time.Second))
|
|
err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, windows.TCP_KEEPIDLE, secs)
|
|
runtime.KeepAlive(fd)
|
|
return os.NewSyscallError("setsockopt", err)
|
|
}
|
|
|
|
func setKeepAliveInterval(fd *netFD, d time.Duration) error {
|
|
if !windows.SupportTCPKeepAliveInterval() {
|
|
return setKeepAliveIdleAndInterval(fd, -1, d)
|
|
}
|
|
|
|
if d == 0 {
|
|
d = defaultTCPKeepAliveInterval
|
|
} else if d < 0 {
|
|
return nil
|
|
}
|
|
// The kernel expects seconds so round to next highest second.
|
|
secs := int(roundDurationUp(d, time.Second))
|
|
err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, windows.TCP_KEEPINTVL, secs)
|
|
runtime.KeepAlive(fd)
|
|
return os.NewSyscallError("setsockopt", err)
|
|
}
|
|
|
|
func setKeepAliveCount(fd *netFD, n int) error {
|
|
if n == 0 {
|
|
n = defaultTCPKeepAliveCount
|
|
} else if n < 0 {
|
|
return nil
|
|
}
|
|
|
|
err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, windows.TCP_KEEPCNT, n)
|
|
runtime.KeepAlive(fd)
|
|
return os.NewSyscallError("setsockopt", err)
|
|
}
|
|
|
|
// setKeepAliveIdleAndInterval serves for kernels prior to Windows 10, version 1709.
|
|
func setKeepAliveIdleAndInterval(fd *netFD, idle, interval time.Duration) error {
|
|
// WSAIoctl with SIO_KEEPALIVE_VALS control code requires all fields in
|
|
// `tcp_keepalive` struct to be provided.
|
|
// Otherwise, if any of the fields were not provided, just leaving them
|
|
// zero will knock off any existing values of keep-alive.
|
|
// Unfortunately, Windows doesn't support retrieving current keep-alive
|
|
// settings in any form programmatically, which disable us to first retrieve
|
|
// the current keep-alive settings, then set it without unwanted corruption.
|
|
switch {
|
|
case idle < 0 && interval >= 0:
|
|
// Given that we can't set KeepAliveInterval alone, and this code path
|
|
// is new, it doesn't exist before, so we just return an error.
|
|
return syscall.WSAENOPROTOOPT
|
|
case idle >= 0 && interval < 0:
|
|
// Although we can't set KeepAliveTime alone either, this existing code
|
|
// path had been backing up [SetKeepAlivePeriod] which used to be set both
|
|
// KeepAliveTime and KeepAliveInterval to 15 seconds.
|
|
// Now we will use the default of KeepAliveInterval on Windows if user doesn't
|
|
// provide one.
|
|
interval = defaultKeepAliveInterval
|
|
case idle < 0 && interval < 0:
|
|
// Nothing to do, just bail out.
|
|
return nil
|
|
case idle >= 0 && interval >= 0:
|
|
// Go ahead.
|
|
}
|
|
|
|
if idle == 0 {
|
|
idle = defaultTCPKeepAliveIdle
|
|
}
|
|
if interval == 0 {
|
|
interval = defaultTCPKeepAliveInterval
|
|
}
|
|
|
|
// The kernel expects milliseconds so round to next highest
|
|
// millisecond.
|
|
tcpKeepAliveIdle := uint32(roundDurationUp(idle, time.Millisecond))
|
|
tcpKeepAliveInterval := uint32(roundDurationUp(interval, time.Millisecond))
|
|
ka := syscall.TCPKeepalive{
|
|
OnOff: 1,
|
|
Time: tcpKeepAliveIdle,
|
|
Interval: tcpKeepAliveInterval,
|
|
}
|
|
ret := uint32(0)
|
|
size := uint32(unsafe.Sizeof(ka))
|
|
err := fd.pfd.WSAIoctl(syscall.SIO_KEEPALIVE_VALS, (*byte)(unsafe.Pointer(&ka)), size, nil, 0, &ret, nil, 0)
|
|
runtime.KeepAlive(fd)
|
|
return os.NewSyscallError("wsaioctl", err)
|
|
}
|