From 1aa75b1c9ea2cbcedc08a45460dbb882a307feb4 Mon Sep 17 00:00:00 2001 From: James Tucker Date: Tue, 26 Apr 2022 17:13:05 -0700 Subject: [PATCH] wgengine/netstack: always set TCP keepalive Setting keepalive ensures that idle connections will eventually be closed. In userspace mode, any application configured TCP keepalive is effectively swallowed by the host kernel, and is not easy to detect. Failure to close connections when a peer tailscaled goes offline or restarts may result in an otherwise indefinite connection for any protocol endpoint that does not initiate new traffic. This patch does not take any new opinion on a sensible default for the keepalive timers, though as noted in the TODO, doing so likely deserves further consideration. Update #4522 Signed-off-by: James Tucker --- wgengine/netstack/netstack.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/wgengine/netstack/netstack.go b/wgengine/netstack/netstack.go index 852c2a720..7733ffa6b 100644 --- a/wgengine/netstack/netstack.go +++ b/wgengine/netstack/netstack.go @@ -651,6 +651,21 @@ func (ns *Impl) acceptTCP(r *tcp.ForwarderRequest) { } r.Complete(false) + // SetKeepAlive so that idle connections to peers that have forgotten about + // the connection or gone completely offline eventually time out. + // Applications might be setting this on a forwarded connection, but from + // userspace we can not see those, so the best we can do is to always + // perform them with conservative timing. + // TODO(tailscale/tailscale#4522): Netstack defaults match the Linux + // defaults, and results in a little over two hours before the socket would + // be closed due to keepalive. A shorter default might be better, or seeking + // a default from the host IP stack. This also might be a useful + // user-tunable, as in userspace mode this can have broad implications such + // as lingering connections to fork style daemons. On the other side of the + // fence, the long duration timers are low impact values for battery powered + // peers. + ep.SocketOptions().SetKeepAlive(true) + // The ForwarderRequest.CreateEndpoint above asynchronously // starts the TCP handshake. Note that the gonet.TCPConn // methods c.RemoteAddr() and c.LocalAddr() will return nil