From 450bc9a6b8a70904aa58614999c5400eed6d273c Mon Sep 17 00:00:00 2001 From: Percy Wegmann Date: Thu, 23 Jan 2025 14:32:22 -0600 Subject: [PATCH] cmd/derper,derp: make TCP write timeout configurable The timeout still defaults to 2 seconds, but can now be changed via command-line flag. Updates tailscale/corp#26045 Signed-off-by: Percy Wegmann --- cmd/derper/derper.go | 3 +++ derp/derp_server.go | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/cmd/derper/derper.go b/cmd/derper/derper.go index 46ff644b2..2c6ecd175 100644 --- a/cmd/derper/derper.go +++ b/cmd/derper/derper.go @@ -77,6 +77,8 @@ var ( tcpKeepAlive = flag.Duration("tcp-keepalive-time", 10*time.Minute, "TCP keepalive time") // tcpUserTimeout is intentionally short, so that hung connections are cleaned up promptly. DERPs should be nearby users. tcpUserTimeout = flag.Duration("tcp-user-timeout", 15*time.Second, "TCP user timeout") + // tcpWriteTimeout is the timeout for writing to client TCP connections. It does not apply to mesh connections. + tcpWriteTimeout = flag.Duration("tcp-write-timeout", derp.DefaultTCPWiteTimeout, "TCP write timeout; 0 results in no timeout being set on writes") ) var ( @@ -173,6 +175,7 @@ func main() { s.SetVerifyClient(*verifyClients) s.SetVerifyClientURL(*verifyClientURL) s.SetVerifyClientURLFailOpen(*verifyFailOpen) + s.SetTCPWriteTimeout(*tcpWriteTimeout) if *meshPSKFile != "" { b, err := os.ReadFile(*meshPSKFile) diff --git a/derp/derp_server.go b/derp/derp_server.go index 4b5cc2f78..0389eed64 100644 --- a/derp/derp_server.go +++ b/derp/derp_server.go @@ -84,7 +84,7 @@ func init() { const ( defaultPerClientSendQueueDepth = 32 // default packets buffered for sending - writeTimeout = 2 * time.Second + DefaultTCPWiteTimeout = 2 * time.Second privilegedWriteTimeout = 30 * time.Second // for clients with the mesh key ) @@ -201,6 +201,8 @@ type Server struct { // Sets the client send queue depth for the server. perClientSendQueueDepth int + tcpWriteTimeout time.Duration + clock tstime.Clock } @@ -377,6 +379,7 @@ func NewServer(privateKey key.NodePrivate, logf logger.Logf) *Server { bufferedWriteFrames: metrics.NewHistogram([]float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 50, 100}), keyOfAddr: map[netip.AddrPort]key.NodePublic{}, clock: tstime.StdClock{}, + tcpWriteTimeout: DefaultTCPWiteTimeout, } s.initMetacert() s.packetsRecvDisco = s.packetsRecvByKind.Get(string(packetKindDisco)) @@ -481,6 +484,13 @@ func (s *Server) SetVerifyClientURLFailOpen(v bool) { s.verifyClientsURLFailOpen = v } +// SetTCPWriteTimeout sets the timeout for writing to connected clients. +// This timeout does not apply to mesh connections. +// Defaults to 2 seconds. +func (s *Server) SetTCPWriteTimeout(d time.Duration) { + s.tcpWriteTimeout = d +} + // HasMeshKey reports whether the server is configured with a mesh key. func (s *Server) HasMeshKey() bool { return s.meshKey != "" } @@ -1805,7 +1815,7 @@ func (c *sclient) sendLoop(ctx context.Context) error { } func (c *sclient) setWriteDeadline() { - d := writeTimeout + d := c.s.tcpWriteTimeout if c.canMesh { // Trusted peers get more tolerance. // @@ -1817,7 +1827,10 @@ func (c *sclient) setWriteDeadline() { // of connected peers. d = privilegedWriteTimeout } - c.nc.SetWriteDeadline(time.Now().Add(d)) + // Ignore the error from setting the write deadline. In practice, + // setting the deadline will only fail if the connection is closed + // or closing, so the subsequent Write() will fail anyway. + _ = c.nc.SetWriteDeadline(time.Now().Add(d)) } // sendKeepAlive sends a keep-alive frame, without flushing.