Fix FIN_WAIT_2 accumulation by draining sockets before close#600
Open
renaudallard wants to merge 1 commit intotinyproxy:masterfrom
Open
Fix FIN_WAIT_2 accumulation by draining sockets before close#600renaudallard wants to merge 1 commit intotinyproxy:masterfrom
renaudallard wants to merge 1 commit intotinyproxy:masterfrom
Conversation
After shutdown(SHUT_WR) sends a FIN to the remote, tinyproxy was calling close() without waiting for the remote FIN. The socket was orphaned in FIN_WAIT_2 state. On Linux this is masked by tcp_fin_timeout reaping orphaned sockets after 60s. On OpenBSD, without SO_KEEPALIVE, these sockets persist indefinitely and accumulate until the proxy stalls. Add close_socket() that calls shutdown(SHUT_WR), then drains the socket with a 2-second receive timeout to allow the remote FIN to arrive before calling close(). Use it in conn_destroy_contents() for both client and server file descriptors, covering all exit paths from relay_connection() including idle timeout and poll error returns. Also add the missing shutdown(server_fd, SHUT_WR) in relay_connection() after flushing remaining data to the upstream server, so the server receives a proper FIN rather than relying on the implicit close().
72c3e70 to
004da4b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On OpenBSD, proxied connections accumulate in FIN_WAIT_2 state and are
never reaped. When enough build up the proxy stalls.
After
shutdown(client_fd, SHUT_WR)sends a FIN to the client,conn_destroy_contents()callsclose()without waiting forthe remote FIN. The socket is orphaned while still in FIN_WAIT_2.
On Linux this is masked by
net.ipv4.tcp_fin_timeout(default 60 s)which aggressively reaps orphaned FIN_WAIT_2 sockets. OpenBSD has no
equivalent aggressive timeout, so without
SO_KEEPALIVEthese socketspersist indefinitely.
The problem also affects the timeout and poll-error return paths in
relay_connection(), which skipshutdown()entirely and gostraight to
close().Fix
close_socket()helper inconns.cthat performs a properTCP close handshake:
shutdown(SHUT_WR), drain with a 2-secondSO_RCVTIMEO, thenclose(). This gives the remote side timeto send its FIN before the socket is orphaned.
conn_destroy_contents()for both client and serverfile descriptors, covering all exit paths from
relay_connection().shutdown(server_fd, SHUT_WR)inrelay_connection()after flushing remaining data to the upstreamserver.
Testing
Tested on OpenBSD. After the fix, connections transition through
TIME_WAIT normally instead of accumulating in FIN_WAIT_2.