How does tcp-keepalive work in ssh?

You probably want to use the ServerAlive settings for this. They do not require any configuration on the server, and can be set on the command line if you wish.

ssh -o ServerAliveInterval=5 -o ServerAliveCountMax=1 $HOST

This will send a ssh keepalive message every 5 seconds, and if it comes time to send another keepalive, but a response to the last one wasn't received, then the connection is terminated.

The critical difference between ServerAliveInterval and TCPKeepAlive is the layer they operate at.

  • TCPKeepAlive operates on the TCP layer. It sends an empty TCP ACK packet. Firewalls can be configured to ignore these packets, so if you go through a firewall that drops idle connections, these may not keep the connection alive.
  • ServerAliveInterval operates on the ssh layer. It will actually send data through ssh, so the TCP packet has encrypted data in and a firewall can't tell if its a keepalive, or a legitimate packet, so these work better.

The TCPKeepAlive option is actually a very different method of keeping connections alive from ClientAlive-like or ServerAlive-like options.

Are per BSD SSH manual page, we can read that:

The client alive messages are sent through the encrypted channel and therefore will not be spoofable. The TCP keepalive option enabled by TCPKeepAlive is spoofable. The client alive mechanism is valuable when the client or server depend on knowing when a connection has become inactive.

The TCPKeepAlive make sure whether the system should send TCP keepalive messages to the other side. The default option is always enabled.

If you're using ClientAliveInterval, you can disable TCPKeepAlive. This option will send a message through the encrypted channel to request a response from the client (the default is 0, so no messages are sent to the client) and ClientAliveCountMax sets the number of client alive messages before sshd will disconnect the client, by terminating the session.