aboutgitcodebugslistschat
diff options
context:
space:
mode:
authorDavid Gibson <david@gibson.dropbear.id.au>2026-01-27 19:39:53 +1100
committerStefano Brivio <sbrivio@redhat.com>2026-01-27 12:40:29 +0100
commite3f70c05bad90368a1a89bf31a9015125232b9ae (patch)
tree2c0f465980c1450702a02e5b1108ef453164962e
parentcce94e92fb3d2a90730c125f2bad32c9ed51da3f (diff)
downloadpasst-e3f70c05bad90368a1a89bf31a9015125232b9ae.tar
passt-e3f70c05bad90368a1a89bf31a9015125232b9ae.tar.gz
passt-e3f70c05bad90368a1a89bf31a9015125232b9ae.tar.bz2
passt-e3f70c05bad90368a1a89bf31a9015125232b9ae.tar.lz
passt-e3f70c05bad90368a1a89bf31a9015125232b9ae.tar.xz
passt-e3f70c05bad90368a1a89bf31a9015125232b9ae.tar.zst
passt-e3f70c05bad90368a1a89bf31a9015125232b9ae.zip
tcp_splice: Force TCP RST on abnormal close conditionsHEADmaster
When we need to prematurely close a spliced connection, we use: conn_flag(conn, CLOSING); This does destroy the flow, but does so in the same way as a clean close from both ends. That's not what we want in error conditions, or when one side of the flow has signalled an abnormal exit with an EPOLLHUP event. Replace all places where we close the connection - except for the happy path close - with calls to a new tcp_splice_rst() function, which forces the sockets to emit a TCP RST on each side. Link: https://bugs.passt.top/show_bug.cgi?id=193 Signed-off-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
-rw-r--r--tcp_splice.c58
1 files changed, 46 insertions, 12 deletions
diff --git a/tcp_splice.c b/tcp_splice.c
index cb81e01..8806523 100644
--- a/tcp_splice.c
+++ b/tcp_splice.c
@@ -234,6 +234,37 @@ static void conn_event_do(struct tcp_splice_conn *conn, unsigned long event)
conn_event_do(conn, event); \
} while (0)
+/**
+ * tcp_splice_rst() - Close spliced connection forcing RST on each side
+ * @conn: Connection pointer
+ */
+static void tcp_splice_rst(struct tcp_splice_conn *conn)
+{
+ const struct linger linger0 = {
+ .l_onoff = 1,
+ .l_linger = 0,
+ };
+ unsigned sidei;
+
+ if (conn->flags & CLOSING)
+ return; /* Nothing to do */
+
+ /* Force RST on sockets to inform the peer
+ *
+ * We do this by setting SO_LINGER with 0 timeout, which means that
+ * close() will send an RST (unless the connection is already closed in
+ * both directions).
+ */
+ flow_foreach_sidei(sidei) {
+ if (setsockopt(conn->s[sidei], SOL_SOCKET,
+ SO_LINGER, &linger0, sizeof(linger0)) < 0) {
+ flow_dbg_perror(conn,
+"SO_LINGER failed, may not send RST to peer");
+ }
+ }
+
+ conn_flag(conn, CLOSING);
+}
/**
* tcp_splice_flow_defer() - Deferred per-flow handling (clean up closed)
@@ -299,7 +330,7 @@ static int tcp_splice_connect_finish(const struct ctx *c,
if (pipe2(conn->pipe[sidei], O_NONBLOCK | O_CLOEXEC)) {
flow_perror(conn, "cannot create %d->%d pipe",
sidei, !sidei);
- conn_flag(conn, CLOSING);
+ tcp_splice_rst(conn);
return -EIO;
}
@@ -437,7 +468,7 @@ void tcp_splice_conn_from_sock(const struct ctx *c, union flow *flow, int s0)
flow_trace(conn, "failed to set TCP_QUICKACK on %i", s0);
if (tcp_splice_connect(c, conn))
- conn_flag(conn, CLOSING);
+ tcp_splice_rst(conn);
FLOW_ACTIVATE(conn);
}
@@ -474,14 +505,14 @@ void tcp_splice_sock_handler(struct ctx *c, union epoll_ref ref,
flow_trace(conn, "Error event on socket: %s",
strerror_(err));
- goto close;
+ goto reset;
}
if (conn->events == SPLICE_CONNECT) {
if (!(events & EPOLLOUT))
- goto close;
+ goto reset;
if (tcp_splice_connect_finish(c, conn))
- goto close;
+ goto reset;
}
if (events & EPOLLOUT) {
@@ -515,7 +546,7 @@ retry:
while (readlen < 0 && errno == EINTR);
if (readlen < 0 && errno != EAGAIN)
- goto close;
+ goto reset;
flow_trace(conn, "%zi from read-side call", readlen);
@@ -539,7 +570,7 @@ retry:
while (written < 0 && errno == EINTR);
if (written < 0 && errno != EAGAIN)
- goto close;
+ goto reset;
flow_trace(conn, "%zi from write-side call (passed %zi)",
written, c->tcp.pipe_size);
@@ -602,8 +633,11 @@ retry:
}
}
- if (CONN_HAS(conn, FIN_SENT(0) | FIN_SENT(1)))
- goto close;
+ if (CONN_HAS(conn, FIN_SENT(0) | FIN_SENT(1))) {
+ /* Clean close, no reset */
+ conn_flag(conn, CLOSING);
+ return;
+ }
if ((events & (EPOLLIN | EPOLLOUT)) == (EPOLLIN | EPOLLOUT)) {
events = EPOLLIN;
@@ -613,12 +647,12 @@ retry:
}
if (events & EPOLLHUP)
- goto close;
+ goto reset;
return;
-close:
- conn_flag(conn, CLOSING);
+reset:
+ tcp_splice_rst(conn);
}
/**