// SPDX-License-Identifier: 0BSD // Copyright (c) 2024-2026 Sudo-Ivan / Quad4.io //go:build linux && !tinygo // +build linux,!tinygo package interfaces import ( "fmt" "net" "syscall" "time" "unsafe" "git.quad4.io/Networks/Reticulum-Go/pkg/debug" ) func (tc *TCPClientInterface) setTimeoutsLinux() error { tcpConn, ok := tc.conn.(*net.TCPConn) if !ok { return fmt.Errorf("not a TCP connection") } rawConn, err := tcpConn.SyscallConn() if err != nil { return fmt.Errorf("failed to get raw connection: %v", err) } var sockoptErr error err = rawConn.Control(func(fd uintptr) { var userTimeout, probeAfter, probeInterval, probeCount int if tc.i2pTunneled { userTimeout = I2P_USER_TIMEOUT_SEC * TCP_MILLISECONDS probeAfter = I2P_PROBE_AFTER_SEC probeInterval = I2P_PROBE_INTERVAL_SEC probeCount = I2P_PROBES_COUNT } else { userTimeout = TCP_USER_TIMEOUT_SEC * TCP_MILLISECONDS probeAfter = TCP_PROBE_AFTER_SEC probeInterval = TCP_PROBE_INTERVAL_SEC probeCount = TCP_PROBES_COUNT } const TCP_USER_TIMEOUT = 18 const TCP_KEEPIDLE = 4 const TCP_KEEPINTVL = 5 const TCP_KEEPCNT = 6 if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, TCP_USER_TIMEOUT, userTimeout); err != nil { debug.Log(debug.DEBUG_VERBOSE, "Failed to set TCP_USER_TIMEOUT", "error", err) } if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, SO_KEEPALIVE_ENABLE); err != nil { sockoptErr = fmt.Errorf("failed to enable SO_KEEPALIVE: %v", err) return } if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, TCP_KEEPIDLE, probeAfter); err != nil { debug.Log(debug.DEBUG_VERBOSE, "Failed to set TCP_KEEPIDLE", "error", err) } if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, TCP_KEEPINTVL, probeInterval); err != nil { debug.Log(debug.DEBUG_VERBOSE, "Failed to set TCP_KEEPINTVL", "error", err) } if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, TCP_KEEPCNT, probeCount); err != nil { debug.Log(debug.DEBUG_VERBOSE, "Failed to set TCP_KEEPCNT", "error", err) } }) if err != nil { return fmt.Errorf("control failed: %v", err) } if sockoptErr != nil { return sockoptErr } debug.Log(debug.DEBUG_VERBOSE, "TCP keepalive configured (Linux)", "i2p", tc.i2pTunneled) return nil } func (tc *TCPClientInterface) setTimeoutsOSX() error { return tc.setTimeoutsLinux() } func platformGetRTT(fd uintptr) time.Duration { var info syscall.TCPInfo // bearer:disable go_gosec_unsafe_unsafe infoLen := uint32(unsafe.Sizeof(info)) const TCP_INFO = 11 // #nosec G103 _, _, errno := syscall.Syscall6( syscall.SYS_GETSOCKOPT, fd, syscall.IPPROTO_TCP, TCP_INFO, // bearer:disable go_gosec_unsafe_unsafe uintptr(unsafe.Pointer(&info)), // bearer:disable go_gosec_unsafe_unsafe uintptr(unsafe.Pointer(&infoLen)), 0, ) if errno != 0 { return 0 } return time.Duration(info.Rtt) * time.Microsecond }