aboutgitcodebugslistschat
diff options
context:
space:
mode:
authorStefano Brivio <sbrivio@redhat.com>2025-08-29 22:11:32 +0200
committerStefano Brivio <sbrivio@redhat.com>2025-09-11 17:03:46 +0200
commitbde1847960cfecc46d8bbf203b931705f2922d31 (patch)
treed19bc6e08949084ba83ab4b86a355117c7179eec
parent660cd6907e14a41ad9bc77d317140c70ab416fce (diff)
downloadpasst-bde1847960cfecc46d8bbf203b931705f2922d31.tar
passt-bde1847960cfecc46d8bbf203b931705f2922d31.tar.gz
passt-bde1847960cfecc46d8bbf203b931705f2922d31.tar.bz2
passt-bde1847960cfecc46d8bbf203b931705f2922d31.tar.lz
passt-bde1847960cfecc46d8bbf203b931705f2922d31.tar.xz
passt-bde1847960cfecc46d8bbf203b931705f2922d31.tar.zst
passt-bde1847960cfecc46d8bbf203b931705f2922d31.zip
tcp: Fast re-transmit if half-closed, make TAP_FIN_RCVD path consistent
We currently have a number of discrepancies in the tcp_tap_handler() path between the half-closed connection path and the regular one, and they are mostly a result of code duplication, which comes in turn from the fact that tcp_data_from_tap() deals with data transfers as well as general connection bookkeeping, so we can't use it for half-closed connections. This suggests that we should probably rework it into two or more functions, in the long term, but for the moment being I'm just fixing one obvious issue, which is the lack of fast retransmissions in the TAP_FIN_RCVD path, and a potential one, which is the fact we don't handle socket flush failures. Add fast re-transmit for half-closed connections, and handle the case of socket flush (tcp_sock_consume()) flush failure in the same way as tcp_data_from_tap() handles it. Signed-off-by: Stefano Brivio <sbrivio@redhat.com> Reviewed-by: David Gibson <david@gibson.dropbear.id.au> Tested-by: Paul Holzinger <pholzing@redhat.com> Reviewed-by: Jon Maloy <jmaloy@redhat.com>
-rw-r--r--tcp.c42
1 files changed, 39 insertions, 3 deletions
diff --git a/tcp.c b/tcp.c
index a08d327..b35f220 100644
--- a/tcp.c
+++ b/tcp.c
@@ -1653,6 +1653,23 @@ static int tcp_data_from_sock(const struct ctx *c, struct tcp_tap_conn *conn)
}
/**
+ * tcp_packet_data_len() - Get data (TCP payload) length for a TCP packet
+ * @th: Pointer to TCP header
+ * @l4len: TCP packet length, including TCP header
+ *
+ * Return: data length of TCP packet, -1 on invalid value of Data Offset field
+ */
+static ssize_t tcp_packet_data_len(const struct tcphdr *th, size_t l4len)
+{
+ size_t off = th->doff * 4UL;
+
+ if (off < sizeof(*th) || off > l4len)
+ return -1;
+
+ return l4len - off;
+}
+
+/**
* tcp_data_from_tap() - tap/guest data for established connection
* @c: Execution context
* @conn: Connection pointer
@@ -2113,9 +2130,28 @@ int tcp_tap_handler(const struct ctx *c, uint8_t pif, sa_family_t af,
/* Established connections not accepting data from tap */
if (conn->events & TAP_FIN_RCVD) {
- tcp_sock_consume(conn, ntohl(th->ack_seq));
- tcp_update_seqack_from_tap(c, conn, ntohl(th->ack_seq));
- if (tcp_tap_window_update(c, conn, ntohs(th->window)))
+ bool retr;
+
+ retr = th->ack && !tcp_packet_data_len(th, l4len) && !th->fin &&
+ ntohl(th->ack_seq) == conn->seq_ack_from_tap &&
+ ntohs(th->window) == conn->wnd_from_tap;
+
+ /* On socket flush failure, pretend there was no ACK, try again
+ * later
+ */
+ if (th->ack && !tcp_sock_consume(conn, ntohl(th->ack_seq)))
+ tcp_update_seqack_from_tap(c, conn, ntohl(th->ack_seq));
+
+ if (retr) {
+ flow_trace(conn,
+ "fast re-transmit, ACK: %u, previous sequence: %u",
+ ntohl(th->ack_seq), conn->seq_to_tap);
+
+ if (tcp_rewind_seq(c, conn))
+ return -1;
+ }
+
+ if (tcp_tap_window_update(c, conn, ntohs(th->window)) || retr)
tcp_data_from_sock(c, conn);
if (conn->seq_ack_from_tap == conn->seq_to_tap) {