import UBI passt-0^20250512.g8ec1341-5.el10_1

This commit is contained in:
AlmaLinux RelEng Bot 2026-05-05 06:24:09 -04:00
parent 58c7214df8
commit 7e5a554ec1
6 changed files with 546 additions and 1 deletions

View File

@ -0,0 +1,49 @@
From a7d9ce6cacf8d62ca78fa98d469902c900659cb9 Mon Sep 17 00:00:00 2001
From: David Gibson <david@gibson.dropbear.id.au>
Date: Tue, 4 Nov 2025 16:40:43 +1100
Subject: [PATCH 4/8] tcp: Properly remove sockets from epoll loop when
connection is closed
Most of the handling for closing a TCP connectin is in conn_event_do() when
it receives a 'CLOSED' event. We specifically check for this case and,
correctly, remove the connection from the flow hash table. However, we
also bypass the call tp tcp_epoll_ctl() which is not correct. By skipping
tcp_epoll_ctl() we skip it's specific handling of the CLOSED event, which
includes removing the TCP socket from epoll.
If we somehow get an event on such a stale socket, we'll get a stale flow
reference. That flow slot might have been re-used, leading to to a crash
in conn_at_sidx().
Fixes: b86afe3559c0 ("tcp: Don't defer hash table removal")
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
(cherry picked from commit 60ade6cae70dc48f7f777c38e7c70fc3696784c4)
---
tcp.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/tcp.c b/tcp.c
index 0ac298a..17e99af 100644
--- a/tcp.c
+++ b/tcp.c
@@ -681,12 +681,13 @@ void conn_event_do(const struct ctx *c, struct tcp_tap_conn *conn,
flow_dbg(conn, "%s",
num == -1 ? "CLOSED" : tcp_event_str[num]);
- if (event == CLOSED)
- flow_hash_remove(c, TAP_SIDX(conn));
- else if ((event == TAP_FIN_RCVD) && !(conn->events & SOCK_FIN_RCVD))
+ if ((event == TAP_FIN_RCVD) && !(conn->events & SOCK_FIN_RCVD)) {
conn_flag(c, conn, ACTIVE_CLOSE);
- else
+ } else {
+ if (event == CLOSED)
+ flow_hash_remove(c, TAP_SIDX(conn));
tcp_epoll_ctl(c, conn);
+ }
if (CONN_HAS(conn, SOCK_FIN_SENT | TAP_FIN_ACKED))
tcp_timer_ctl(c, conn);
--
2.47.1

View File

@ -0,0 +1,80 @@
From 34a346e3eb83b33bd6d62a57e0e990c5c698fe85 Mon Sep 17 00:00:00 2001
From: David Gibson <david@gibson.dropbear.id.au>
Date: Wed, 4 Feb 2026 21:41:34 +1000
Subject: [PATCH 5/8] tcp: Remove non-working activity timeout mechanism
This mechanism was intended to remove connections which have had no
activity for two hours, even if they haven't closed or been reset
internally. It operated by setting the two hour timeout if there are
no sooner TCP timeouts to schedule.
However, when the timer fires, the way we detect the case of the activity
timeout doesn't work: it resets the timer for another two hours, then
checks if the old timeout was two hours. But the old timeout returned
by timerfd_settime() is not the original value of the timer, but the
remaining time. Since the timer has just fired it will essentially always
be 0.
For now, just remove the mechanism, disarming the timer entirely if there
isn't another upcoming event. We'll re-introduce some sort of activity
timeout by a different means later.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
(cherry picked from commit e48ce41a1ec2f05846fb66d3847c2c2b6448ca71)
---
tcp.c | 24 +++---------------------
1 file changed, 3 insertions(+), 21 deletions(-)
diff --git a/tcp.c b/tcp.c
index 17e99af..7a7ca24 100644
--- a/tcp.c
+++ b/tcp.c
@@ -197,9 +197,6 @@
* TAP_FIN_ACKED), but no socket activity is detected from the socket within
* this time, reset the connection
*
- * - ACT_TIMEOUT, in the presence of any event: if no activity is detected on
- * either side, the connection is reset
- *
* - ACK_INTERVAL elapsed after data segment received from tap without having
* sent an ACK segment, or zero-sized window advertised to tap/guest (flag
* ACK_TO_TAP_DUE): forcibly check if an ACK segment can be sent
@@ -578,7 +575,9 @@ static void tcp_timer_ctl(const struct ctx *c, struct tcp_tap_conn *conn)
} else if (CONN_HAS(conn, SOCK_FIN_SENT | TAP_FIN_ACKED)) {
it.it_value.tv_sec = FIN_TIMEOUT;
} else {
- it.it_value.tv_sec = ACT_TIMEOUT;
+ /* Disarm */
+ it.it_value.tv_sec = 0;
+ it.it_value.tv_nsec = 0;
}
flow_dbg(conn, "timer expires in %llu.%03llus",
@@ -2294,23 +2293,6 @@ void tcp_timer_handler(const struct ctx *c, union epoll_ref ref)
tcp_timer_ctl(c, conn);
}
}
- } else {
- struct itimerspec new = { { 0 }, { ACT_TIMEOUT, 0 } };
- struct itimerspec old = { { 0 }, { 0 } };
-
- /* Activity timeout: if it was already set, reset the
- * connection, otherwise, it was a left-over from ACK_TO_TAP_DUE
- * or ACK_FROM_TAP_DUE, so just set the long timeout in that
- * case. This avoids having to preemptively reset the timer on
- * ~ACK_TO_TAP_DUE or ~ACK_FROM_TAP_DUE.
- */
- if (timerfd_settime(conn->timer, 0, &new, &old))
- flow_perror(conn, "failed to set timer");
-
- if (old.it_value.tv_sec == ACT_TIMEOUT) {
- flow_dbg(conn, "activity timeout");
- tcp_rst(c, conn);
- }
}
}
--
2.47.1

View File

@ -0,0 +1,181 @@
From 479c464c767e655fad65b9bedc496570bb40c997 Mon Sep 17 00:00:00 2001
From: David Gibson <david@gibson.dropbear.id.au>
Date: Wed, 4 Feb 2026 21:41:35 +1000
Subject: [PATCH 6/8] tcp: Re-introduce inactivity timeouts based on a clock
algorithm
We previously had a mechanism to remove TCP connections which were
inactive for 2 hours. That was broken for a long time, due to poor
interactions with the timerfd handling, so we removed it.
Adding this long scale timer onto the timerfd handling, which mostly
handles much shorter timeouts is tricky to reason about. However, for the
inactivity timeouts, we don't require precision. Instead, we can use
a 1-bit page replacement / "clock" algorithm. Every INACTIVITY_INTERVAL
(2 hours), a global timer marks every TCP connection as tentatively
inactive. That flag is cleared if we get any events, either tap side or
socket side.
If the inactive flag is still set when the next INACTIVITY_INTERVAL expires
then the connection has been inactive for an extended period and we reset
and close it. In practice this means that connections will be removed
after 2-4 hours of inactivity.
This is not a true fix for bug 179, but it does mitigate the damage, by
limiting the time that inactive connections will remain around,
Link: https://bugs.passt.top/show_bug.cgi?id=179
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
(cherry picked from commit 1820103fbbf13df98257a3f5c3ba625de624b0b3)
---
tcp.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
tcp.h | 2 ++
tcp_conn.h | 3 +++
3 files changed, 53 insertions(+), 1 deletion(-)
diff --git a/tcp.c b/tcp.c
index 7a7ca24..394fc35 100644
--- a/tcp.c
+++ b/tcp.c
@@ -201,6 +201,13 @@
* sent an ACK segment, or zero-sized window advertised to tap/guest (flag
* ACK_TO_TAP_DUE): forcibly check if an ACK segment can be sent
*
+ * We also use a global interval timer for an activity timeout which doesn't
+ * require precision:
+ *
+ * - INACTIVITY_INTERVAL: if a connection has had no activity for an entire
+ * interval, close and reset it. This means that idle connections (without
+ * keepalives) will be removed between INACTIVITY_INTERVAL s and
+ * 2*INACTIVITY_INTERVAL s after the last activity.
*
* Summary of data flows (with ESTABLISHED event)
* ----------------------------------------------
@@ -330,7 +337,8 @@ enum {
#define SYN_TIMEOUT 10 /* s */
#define ACK_TIMEOUT 2
#define FIN_TIMEOUT 60
-#define ACT_TIMEOUT 7200
+
+#define INACTIVITY_INTERVAL 7200 /* s */
#define LOW_RTT_TABLE_SIZE 8
#define LOW_RTT_THRESHOLD 10 /* us */
@@ -2000,6 +2008,8 @@ int tcp_tap_handler(const struct ctx *c, uint8_t pif, sa_family_t af,
return 1;
}
+ conn->inactive = false;
+
if (th->ack && !(conn->events & ESTABLISHED))
tcp_update_seqack_from_tap(c, conn, ntohl(th->ack_seq));
@@ -2318,6 +2328,8 @@ void tcp_sock_handler(const struct ctx *c, union epoll_ref ref,
return;
}
+ conn->inactive = false;
+
if ((conn->events & TAP_FIN_SENT) && (events & EPOLLHUP)) {
conn_event(c, conn, CLOSED);
return;
@@ -2696,6 +2708,7 @@ static void tcp_port_rebind(struct ctx *c, bool outbound)
}
}
+
/**
* tcp_port_rebind_outbound() - Rebind ports in namespace
* @arg: Execution context
@@ -2714,6 +2727,38 @@ static int tcp_port_rebind_outbound(void *arg)
return 0;
}
+/**
+ * tcp_inactivity() - Scan for and close long-inactive connections
+ * @: Execution context
+ */
+static void tcp_inactivity(struct ctx *c, const struct timespec *now)
+{
+ union flow *flow;
+
+ if (now->tv_sec - c->tcp.inactivity_run < INACTIVITY_INTERVAL)
+ return;
+
+ debug("TCP inactivity scan");
+ c->tcp.inactivity_run = now->tv_sec;
+
+ flow_foreach(flow) {
+ struct tcp_tap_conn *conn = &flow->tcp;
+
+ if (flow->f.type != FLOW_TCP)
+ continue;
+
+ if (conn->inactive) {
+ /* No activity in this interval, reset */
+ flow_dbg(conn, "Inactive for at least %us, resetting",
+ INACTIVITY_INTERVAL);
+ tcp_rst(c, conn);
+ }
+
+ /* Ready to check fot next interval */
+ conn->inactive = true;
+ }
+}
+
/**
* tcp_timer() - Periodic tasks: port detection, closed connections, pool refill
* @c: Execution context
@@ -2738,6 +2783,8 @@ void tcp_timer(struct ctx *c, const struct timespec *now)
tcp_sock_refill_init(c);
if (c->mode == MODE_PASTA)
tcp_splice_refill(c);
+
+ tcp_inactivity(c, now);
}
/**
diff --git a/tcp.h b/tcp.h
index 234a803..b75e9a7 100644
--- a/tcp.h
+++ b/tcp.h
@@ -59,12 +59,14 @@ union tcp_listen_epoll_ref {
* @fwd_out: Port forwarding configuration for outbound packets
* @timer_run: Timestamp of most recent timer run
* @pipe_size: Size of pipes for spliced connections
+ * @inactivity_run: Time we last scanned for inactive connections
*/
struct tcp_ctx {
struct fwd_ports fwd_in;
struct fwd_ports fwd_out;
struct timespec timer_run;
size_t pipe_size;
+ time_t inactivity_run;
};
#endif /* TCP_H */
diff --git a/tcp_conn.h b/tcp_conn.h
index 35d813d..93f9440 100644
--- a/tcp_conn.h
+++ b/tcp_conn.h
@@ -17,6 +17,7 @@
* @ws_from_tap: Window scaling factor advertised from tap/guest
* @ws_to_tap: Window scaling factor advertised to tap/guest
* @tap_mss: MSS advertised by tap/guest, rounded to 2 ^ TCP_MSS_BITS
+ * @inactive: No activity within the current INACTIVITY_INTERVAL
* @sock: Socket descriptor number
* @events: Connection events, implying connection states
* @listening_sock: Listening socket this socket was accept()ed from, or -1
@@ -52,6 +53,8 @@ struct tcp_tap_conn {
#define MSS_SET(conn, mss) (conn->tap_mss = (mss >> (16 - TCP_MSS_BITS)))
#define MSS_GET(conn) (conn->tap_mss << (16 - TCP_MSS_BITS))
+ bool inactive :1;
+
int sock :FD_REF_BITS;
uint8_t events;
--
2.47.1

View File

@ -0,0 +1,66 @@
From d2f7c36df97e287e2c665d4caedf1137755bfd54 Mon Sep 17 00:00:00 2001
From: David Gibson <david@gibson.dropbear.id.au>
Date: Wed, 4 Feb 2026 21:41:36 +1000
Subject: [PATCH 7/8] tcp: Extend tcp_send_flag() to send TCP keepalive
segments
TCP keepalives aren't technically a flag, but they are a zero-data segment
so they can be generated with only a small modification to
tcp_{buf,vu}_send_flag(). Implement this, using a new "pseudo-flag"
value (similar to DUP_ACK), KEEPALIVE.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
[sbrivio: Fix trivial merge conflict with 812cdb802c6e]
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
(cherry picked from commit a681e44ec60179567fb10f34351d7dfdbd2e7c7e)
---
tcp_buf.c | 4 ++++
tcp_internal.h | 2 ++
tcp_vu.c | 3 +++
3 files changed, 9 insertions(+)
diff --git a/tcp_buf.c b/tcp_buf.c
index 0530563..a324eee 100644
--- a/tcp_buf.c
+++ b/tcp_buf.c
@@ -212,6 +212,10 @@ int tcp_buf_send_flag(const struct ctx *c, struct tcp_tap_conn *conn, int flags)
tcp_payload_used++;
l4len = optlen + sizeof(struct tcphdr);
iov[TCP_IOV_PAYLOAD].iov_len = l4len;
+
+ if (flags & KEEPALIVE)
+ seq--;
+
tcp_l2_buf_fill_headers(conn, iov, NULL, seq, false);
if (flags & DUP_ACK) {
diff --git a/tcp_internal.h b/tcp_internal.h
index 36c6533..371a5f5 100644
--- a/tcp_internal.h
+++ b/tcp_internal.h
@@ -30,6 +30,8 @@
/* Flags for internal usage */
#define DUP_ACK (1 << 5)
+#define KEEPALIVE (1 << 6)
+
#define OPT_EOL 0
#define OPT_NOP 1
#define OPT_MSS 2
diff --git a/tcp_vu.c b/tcp_vu.c
index 57587cc..cb3f80f 100644
--- a/tcp_vu.c
+++ b/tcp_vu.c
@@ -135,6 +135,9 @@ int tcp_vu_send_flag(const struct ctx *c, struct tcp_tap_conn *conn, int flags)
flags_elem[0].in_sg[0].iov_len = hdrlen + optlen;
payload = IOV_TAIL(flags_elem[0].in_sg, 1, hdrlen);
+ if (flags & KEEPALIVE)
+ seq--;
+
tcp_fill_headers(conn, NULL, ip4h, ip6h, th, &payload,
NULL, seq, !*c->pcap);
--
2.47.1

View File

@ -0,0 +1,161 @@
From e349a1b2416f220a8cf518c88bec8b6a7dea201d Mon Sep 17 00:00:00 2001
From: David Gibson <david@gibson.dropbear.id.au>
Date: Wed, 4 Feb 2026 21:41:37 +1000
Subject: [PATCH 8/8] tcp: Send TCP keepalive segments after a period of
tap-side inactivity
There are several circumstances in which a live, but idle TCP connection
can be forgotten by a guest, with no "on the wire" indication that this has
happened. The most obvious is if the guest abruptly reboots. A more
subtle case can happen with a half-closed connection, specifically one
in FIN_WAIT_2 state on the guest. A connection can, legitimately, remain
in this state indefinitely. If however, a socket in this state is closed
by userspace, Linux at least will remove the kernel socket after 60s
(or as configured in the net.ipv4.tcp_fin_timeout sysctl).
Because there's no on the wire indication in these cases, passt will
pointlessly retain the connection in its flow table, at least until it is
removed by the inactivity timeout after several hours.
To avoid keeping connections around for so long in this state, add
functionality to periodically send TCP keepalive segments to the guest if
we've seen no activity on the tap interface. If the guest is no longer
aware of the connection, it should respond with an RST which will let
passt remove the stale entry.
To do this we use a method similar to the inactivity timeout - a 1-bit
page replacement / clock algorithm, but with a shorter interval, and only
checking for tap side activity. Currently we use a 300s interval, meaning
we'll send a keepalive after 5-10 minutes of (tap side) inactivity.
Link: https://bugs.passt.top/show_bug.cgi?id=179
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
(cherry picked from commit d2f7c21cfb949f2b1587b9475917efdd6ac549fd)
---
tcp.c | 39 +++++++++++++++++++++++++++++++++++++++
tcp.h | 2 ++
tcp_conn.h | 2 ++
3 files changed, 43 insertions(+)
diff --git a/tcp.c b/tcp.c
index 394fc35..8ea7794 100644
--- a/tcp.c
+++ b/tcp.c
@@ -209,6 +209,12 @@
* keepalives) will be removed between INACTIVITY_INTERVAL s and
* 2*INACTIVITY_INTERVAL s after the last activity.
*
+ * - KEEPALIVE_INTERVAL: if a connection has had no tap-side activity for an
+ * entire interval, send a tap-side keepalive. If the endpoint is no longer
+ * aware of the connection (due to a reboot, or a kernel timeout in FIN_WAIT_2
+ * state) that should trigger an RST, so we won't keep track of connections
+ * that the guest endpoint no longer cares about.
+ *
* Summary of data flows (with ESTABLISHED event)
* ----------------------------------------------
*
@@ -339,6 +345,7 @@ enum {
#define FIN_TIMEOUT 60
#define INACTIVITY_INTERVAL 7200 /* s */
+#define KEEPALIVE_INTERVAL 30 /* s */
#define LOW_RTT_TABLE_SIZE 8
#define LOW_RTT_THRESHOLD 10 /* us */
@@ -2009,6 +2016,7 @@ int tcp_tap_handler(const struct ctx *c, uint8_t pif, sa_family_t af,
}
conn->inactive = false;
+ conn->tap_inactive = false;
if (th->ack && !(conn->events & ESTABLISHED))
tcp_update_seqack_from_tap(c, conn, ntohl(th->ack_seq));
@@ -2727,6 +2735,36 @@ static int tcp_port_rebind_outbound(void *arg)
return 0;
}
+/**
+ * tcp_keepalive() - Send keepalives for connections which need it
+ * @: Execution context
+ */
+static void tcp_keepalive(struct ctx *c, const struct timespec *now)
+{
+ union flow *flow;
+
+ if (now->tv_sec - c->tcp.keepalive_run < KEEPALIVE_INTERVAL)
+ return;
+
+ c->tcp.keepalive_run = now->tv_sec;
+
+ flow_foreach(flow) {
+ struct tcp_tap_conn *conn = &flow->tcp;
+
+ if (flow->f.type != FLOW_TCP)
+ continue;
+
+ if (conn->tap_inactive) {
+ flow_dbg(conn, "No tap activity for least %us, send keepalive",
+ KEEPALIVE_INTERVAL);
+ tcp_send_flag(c, conn, KEEPALIVE);
+ }
+
+ /* Ready to check fot next interval */
+ conn->tap_inactive = true;
+ }
+}
+
/**
* tcp_inactivity() - Scan for and close long-inactive connections
* @: Execution context
@@ -2784,6 +2822,7 @@ void tcp_timer(struct ctx *c, const struct timespec *now)
if (c->mode == MODE_PASTA)
tcp_splice_refill(c);
+ tcp_keepalive(c, now);
tcp_inactivity(c, now);
}
diff --git a/tcp.h b/tcp.h
index b75e9a7..7433f15 100644
--- a/tcp.h
+++ b/tcp.h
@@ -59,6 +59,7 @@ union tcp_listen_epoll_ref {
* @fwd_out: Port forwarding configuration for outbound packets
* @timer_run: Timestamp of most recent timer run
* @pipe_size: Size of pipes for spliced connections
+ * @keepalive_run: Time we last issued tap-side keepalives
* @inactivity_run: Time we last scanned for inactive connections
*/
struct tcp_ctx {
@@ -66,6 +67,7 @@ struct tcp_ctx {
struct fwd_ports fwd_out;
struct timespec timer_run;
size_t pipe_size;
+ time_t keepalive_run;
time_t inactivity_run;
};
diff --git a/tcp_conn.h b/tcp_conn.h
index 93f9440..23ef5bd 100644
--- a/tcp_conn.h
+++ b/tcp_conn.h
@@ -17,6 +17,7 @@
* @ws_from_tap: Window scaling factor advertised from tap/guest
* @ws_to_tap: Window scaling factor advertised to tap/guest
* @tap_mss: MSS advertised by tap/guest, rounded to 2 ^ TCP_MSS_BITS
+ * @tapinactive: No tao activity within the current KEEPALIVE_INTERVAL
* @inactive: No activity within the current INACTIVITY_INTERVAL
* @sock: Socket descriptor number
* @events: Connection events, implying connection states
@@ -53,6 +54,7 @@ struct tcp_tap_conn {
#define MSS_SET(conn, mss) (conn->tap_mss = (mss >> (16 - TCP_MSS_BITS)))
#define MSS_GET(conn) (conn->tap_mss << (16 - TCP_MSS_BITS))
+ bool tap_inactive :1;
bool inactive :1;
int sock :FD_REF_BITS;
--
2.47.1

View File

@ -12,7 +12,7 @@
Name: passt
Version: 0^20250512.g8ec1341
Release: 4%{?dist}
Release: 5%{?dist}
Summary: User-mode networking daemons for virtual machines and namespaces
License: GPL-2.0-or-later AND BSD-3-Clause
Group: System Environment/Daemons
@ -22,6 +22,11 @@ Source: https://passt.top/passt/snapshot/passt-%{git_hash}.tar.xz
Patch1: 0001-treewide-By-default-don-t-quit-source-after-migratio.patch
Patch2: 0002-tcp-Cast-operands-of-sequence-comparison-macros-to-u.patch
Patch3: 0003-tcp-Don-t-consider-FIN-flags-with-mismatching-sequen.patch
Patch4: 0004-tcp-Properly-remove-sockets-from-epoll-loop-when-con.patch
Patch5: 0005-tcp-Remove-non-working-activity-timeout-mechanism.patch
Patch6: 0006-tcp-Re-introduce-inactivity-timeouts-based-on-a-cloc.patch
Patch7: 0007-tcp-Extend-tcp_send_flag-to-send-TCP-keepalive-segme.patch
Patch8: 0008-tcp-Send-TCP-keepalive-segments-after-a-period-of-ta.patch
BuildRequires: gcc, make, git, checkpolicy, selinux-policy-devel
Requires: (%{name}-selinux = %{version}-%{release} if selinux-policy-%{selinuxtype})
@ -134,6 +139,9 @@ fi
%{_datadir}/selinux/packages/%{selinuxtype}/passt-repair.pp
%changelog
* Wed Apr 22 2026 Stefano Brivio <sbrivio@redhat.com> - 0^20250512.g8ec1341-5
- Resolves: RHEL-169974 RHEL-169634
* Thu Oct 23 2025 Stefano Brivio <sbrivio@redhat.com> - 0^20250512.g8ec1341-4
- Resolves: RHEL-123415 RHEL-123424