Skip to content

Commit

Permalink
tcp,udp: idle timeout as sockopt where possible
Browse files Browse the repository at this point in the history
  • Loading branch information
ignoramous committed Feb 9, 2025
1 parent 1497cda commit a668364
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 9 deletions.
2 changes: 2 additions & 0 deletions intra/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ func (h *baseHandler) forward(local, remote net.Conn, smm *SocketSummary) {
if r, ok := remote.(rwext); ok {
if r.IsZeroDeadline() {
remote = r.Unwrap()
} else if c, ok := r.SetAsTCPSockOpt(); ok {
remote = c
}
}

Expand Down
37 changes: 31 additions & 6 deletions intra/core/sockopt.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,31 @@ func SetKeepAliveConfig(c MinConn) bool {
return false
}

func SetTimeoutSockOpt(c MinConn, timeoutms int) bool {
if tc, ok := c.(*net.TCPConn); ok {
id := conn2str(tc)
rawConn, err := tc.SyscallConn()
if err != nil || rawConn == nil {
return false
}
ok := true
err = rawConn.Control(func(fd uintptr) {
sock := int(fd)
// code.googlesource.com/google-api-go-client/+/master/transport/grpc/dial_socketopt.go#30
if err := unix.SetsockoptInt(sock, unix.SOL_TCP, unix.TCP_USER_TIMEOUT, timeoutms); err != nil {
log.D("core: sockopt: set TCP_USER_TIMEOUT %s (%d) failed: %dms, %v", id, sock, timeoutms, err)
ok = false
}
})
if err != nil {
log.E("core: sockopt: %s RawConn.Control() err: %v", id, err)
ok = false
}
return ok
}
return false
}

// SetKeepAliveConfigSockOpt sets for a TCP connection, SO_KEEPALIVE,
// TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT, TCP_USER_TIMEOUT.
// args is optional, and should be in the order of idle, interval, count.
Expand Down Expand Up @@ -72,29 +97,29 @@ func SetKeepAliveConfigSockOpt(c MinConn, args ...int) (ok bool) {
err = rawConn.Control(func(fd uintptr) {
sock := int(fd)
if err := syscall.SetsockoptInt(sock, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(true)); err != nil {
log.D("set SO_KEEPALIVE %s failed: %v", id, err)
log.D("core: sockopt: set SO_KEEPALIVE %s (%d) failed: %v", id, sock, err)
ok = false
}
if err := syscall.SetsockoptInt(sock, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, idle); err != nil {
log.D("set TCP_KEEPIDLE %s failed: %ds, %v", id, idle, err)
log.D("core: sockopt: set TCP_KEEPIDLE %s (%d) failed: %ds, %v", id, sock, idle, err)
ok = false
}
if err := syscall.SetsockoptInt(sock, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, interval); err != nil {
log.D("set TCP_KEEPINTVL %s failed: %ds, %v", id, interval, err)
log.D("core: sockopt: set TCP_KEEPINTVL %s (%d) failed: %ds, %v", id, sock, interval, err)
ok = false
}
if err := syscall.SetsockoptInt(sock, syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT, count); err != nil {
log.D("set TCP_KEEPCNT %s failed: #%d, %v", id, count, err)
log.D("core: sockopt: set TCP_KEEPCNT %s (%d) failed: #%d, %v", id, sock, count, err)
ok = false
}
// code.googlesource.com/google-api-go-client/+/master/transport/grpc/dial_socketopt.go#30
if err := unix.SetsockoptInt(sock, unix.SOL_TCP, unix.TCP_USER_TIMEOUT, usertimeoutms); err != nil {
log.D("set TCP_USER_TIMEOUT %s failed: %dms, %v", id, usertimeoutms, err)
log.D("core: sockopt: set TCP_USER_TIMEOUT %s (%d) failed: %dms, %v", id, sock, usertimeoutms, err)
ok = false
}
})
if err != nil {
log.E("dialers: sockopt: %s RawConn.Control() err: %v", id, err)
log.E("core: sockopt: %s RawConn.Control() err: %v", id, err)
ok = false
}
}
Expand Down
16 changes: 13 additions & 3 deletions intra/udp.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,24 @@ var _ netstack.GUDPConnHandler = (*udpHandler)(nil)
// on every read and write.
type rwext struct {
net.Conn // underlying conn
min uint32 // min idle timeout in secs
minidle uint32 // min idle timeout in secs
}

func (rw rwext) IsZeroDeadline() bool {
r, w := rw.deadlines()
return r == 0 && w == 0
}

func (rw rwext) SetAsTCPSockOpt() (core.TCPConn, bool) {
r, _ := rw.deadlines()
c, ok := rw.Conn.(core.TCPConn) // no-op for tcp conns
if ok && r > 0 {
// always returns false for udp conns
ok = core.SetTimeoutSockOpt(c, int(r)*1000)
}
return c, ok
}

func (rw rwext) Unwrap() net.Conn {
return rw.Conn
}
Expand All @@ -99,8 +109,8 @@ func (rw rwext) Write(b []byte) (n int, err error) {
func (rw rwext) deadlines() (r, w uint32) {
dopt := settings.GetDialerOpts()
// -ve ints go higher than 2^31 w/ uint: go.dev/play/p/Rrqk_V8a7W0
return max(rw.min, uint32(dopt.ReadTimeoutSec)),
max(rw.min, uint32(dopt.WriteTimeoutSec))
return max(rw.minidle, uint32(dopt.ReadTimeoutSec)),
max(rw.minidle, uint32(dopt.WriteTimeoutSec))
}

func (rw rwext) extend() {
Expand Down

0 comments on commit a668364

Please sign in to comment.