[PATCH 1/1] ipvs: preserve conn hash flags when late-binding dest
Ren Wei <[email protected]> Sun, 28 Jun 2026 00:05:02 +0800
| Newsgroups | org.kernel.vger.lvs-devel,org.kernel.vger.netfilter-devel |
|---|---|
| Message-ID | <1b914f41d725bc064c9ba9830dc8169329737270.1782540466.git.roxy520tt@gmail.com> |
From: Zhiling Zou <[email protected]> Synced connections can be created before their destination exists. When the destination is later added, ip_vs_try_bind_dest() binds it to the existing connection through ip_vs_bind_dest(). ip_vs_bind_dest() copies destination connection flags into cp->flags. For an already hashed connection, changing flags that define conn_tab membership breaks the hash table invariants. In particular, adding IP_VS_CONN_F_ONE_PACKET after the connection has been hashed can make expiry skip unlinking it from conn_tab. Changing the forwarding method can also make unlink use a different single or double hash-node layout than the one used at insertion time. Preserve the flags that define conn_tab hashing when binding a destination to an already hashed connection. Fixes: 26ec037f9841 ("IPVS: one-packet scheduling") Cc: [email protected] Reported-by: Yuan Tan <[email protected]> Reported-by: Yifan Wu <[email protected]> Reported-by: Juefei Pu <[email protected]> Reported-by: Xin Liu <[email protected]> Assisted-by: Codex:gpt-5.4 Signed-off-by: Zhiling Zou <[email protected]> Signed-off-by: Ren Wei <[email protected]> --- net/netfilter/ipvs/ip_vs_conn.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index cb36641f8d1c..016273906aac 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -998,7 +998,11 @@ static inline int ip_vs_dest_totalconns(struct ip_vs_dest *dest) static inline void ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) { + const unsigned int hash_flags = IP_VS_CONN_F_FWD_MASK | + IP_VS_CONN_F_NOOUTPUT | + IP_VS_CONN_F_ONE_PACKET; unsigned int conn_flags; + __u32 old_flags; __u32 flags; /* if dest is NULL, then return directly */ @@ -1011,7 +1015,8 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) conn_flags = atomic_read(&dest->conn_flags); if (cp->protocol != IPPROTO_UDP) conn_flags &= ~IP_VS_CONN_F_ONE_PACKET; - flags = cp->flags; + old_flags = cp->flags; + flags = old_flags; /* Bind with the destination and its corresponding transmitter */ if (flags & IP_VS_CONN_F_SYNC) { /* if the connection is not template and is created @@ -1023,6 +1028,13 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) flags &= ~(IP_VS_CONN_F_FWD_MASK | IP_VS_CONN_F_NOOUTPUT); } flags |= conn_flags; + + /* Preserve conn_tab hashing invariants after late binding. */ + if (old_flags & IP_VS_CONN_F_HASHED) { + flags &= ~hash_flags; + flags |= old_flags & hash_flags; + } + cp->flags = flags; cp->dest = dest; -- 2.43.0