diff options
author | Stefano Brivio <sbrivio@redhat.com> | 2025-09-30 22:26:02 +0200 |
---|---|---|
committer | Stefano Brivio <sbrivio@redhat.com> | 2025-10-07 22:22:27 +0200 |
commit | b3217aa5aec10704774c7142ef07daec5e698415 (patch) | |
tree | 8dfd7706c1a2ce3d9a69be343c7c11b843a327b7 | |
parent | 2a16cdf3e605a1c72647ccd520106a1e59ec8d50 (diff) | |
download | passt-b3217aa5aec10704774c7142ef07daec5e698415.tar passt-b3217aa5aec10704774c7142ef07daec5e698415.tar.gz passt-b3217aa5aec10704774c7142ef07daec5e698415.tar.bz2 passt-b3217aa5aec10704774c7142ef07daec5e698415.tar.lz passt-b3217aa5aec10704774c7142ef07daec5e698415.tar.xz passt-b3217aa5aec10704774c7142ef07daec5e698415.tar.zst passt-b3217aa5aec10704774c7142ef07daec5e698415.zip |
tcp: Fix ACK sequence on FIN to tap
If we reach end-of-file on a socket (or get EPOLLRDHUP / EPOLLHUP) and
send a FIN segment to the guest / container acknowledging a sequence
number that's behind what we received so far, we won't have any
further trigger to send an updated ACK segment, as we are now
switching the epoll socket monitoring to edge-triggered mode.
To avoid this situation, in tcp_update_seqack_wnd(), we set the next
acknowledgement sequence to the current observed sequence, regardless
of what was acknowledged socket-side.
However, we don't necessarily call tcp_update_seqack_wnd() before
sending the FIN segment, which might potentially lead to a situation,
not observed in practice, where we unnecessarily cause a
retransmission at some point after our FIN segment.
Avoid that by setting the ACK sequence to whatever we received from
the container / guest, before sending a FIN segment and switching to
EPOLLET.
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
-rw-r--r-- | tcp_buf.c | 14 | ||||
-rw-r--r-- | tcp_vu.c | 7 |
2 files changed, 19 insertions, 2 deletions
@@ -368,7 +368,19 @@ int tcp_buf_data_from_sock(const struct ctx *c, struct tcp_tap_conn *conn) conn_flag(c, conn, STALLED); } else if ((conn->events & (SOCK_FIN_RCVD | TAP_FIN_SENT)) == SOCK_FIN_RCVD) { - int ret = tcp_buf_send_flag(c, conn, FIN | ACK); + int ret; + + /* On TAP_FIN_SENT, we won't get further data events + * from the socket, and this might be the last ACK + * segment we send to the tap, so update its sequence to + * include everything we received until now. + * + * See also the special handling on CONN_IS_CLOSING() in + * tcp_update_seqack_wnd(). + */ + conn->seq_ack_to_tap = conn->seq_from_tap; + + ret = tcp_buf_send_flag(c, conn, FIN | ACK); if (ret) { tcp_rst(c, conn); return ret; @@ -410,7 +410,12 @@ int tcp_vu_data_from_sock(const struct ctx *c, struct tcp_tap_conn *conn) conn_flag(c, conn, STALLED); } else if ((conn->events & (SOCK_FIN_RCVD | TAP_FIN_SENT)) == SOCK_FIN_RCVD) { - int ret = tcp_vu_send_flag(c, conn, FIN | ACK); + int ret; + + /* See tcp_buf_data_from_sock() */ + conn->seq_ack_to_tap = conn->seq_from_tap; + + ret = tcp_vu_send_flag(c, conn, FIN | ACK); if (ret) { tcp_rst(c, conn); return ret; |