Re: [regression] Network failure beyond first connection after 69894e5b4c5e ("netfilter: nft_connlimit: update the count if add was skipped")
From: Fernando Fernandez Mancera <hidden>
Date: 2026-03-14 19:00:19
Also in:
lkml, netfilter-devel, regressions, stable
Subsystem:
netfilter, networking [general], the rest · Maintainers:
Pablo Neira Ayuso, Florian Westphal, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
On 3/14/26 5:13 PM, Fernando Fernandez Mancera wrote:
Hi, On 3/14/26 3:03 PM, Salvatore Bonaccorso wrote:quoted
Control: forwarded -1 https://lore.kernel.org/ regressions/177349610461.3071718.4083978280323144323@eldamar.lan Control: tags -1 + upstream Hi In Debian, in https://bugs.debian.org/1130336, Alejandro reported that after updates including 69894e5b4c5e ("netfilter: nft_connlimit: update the count if add was skipped"), when the following rule is set iptables -A INPUT -p tcp -m connlimit --connlimit-above 111 -j REJECT --reject-with tcp-reset connections get stuck accordingly, it can be easily reproduced by: # iptables -A INPUT -p tcp -m connlimit --connlimit-above 111 -j REJECT --reject-with tcp-reset # nft list ruleset # Warning: table ip filter is managed by iptables-nft, do not touch! table ip filter { chain INPUT { type filter hook input priority filter; policy accept; ip protocol tcp xt match "connlimit" counter packets 0 bytes 0 reject with tcp reset } } # wget -O /dev/null https://git.kernel.org/torvalds/t/linux-7.0- rc3.tar.gz --2026-03-14 14:53:51-- https://git.kernel.org/torvalds/t/linux-7.0- rc3.tar.gz Resolving git.kernel.org (git.kernel.org)... 172.105.64.184, 2a01:7e01:e001:937:0:1991:8:25 Connecting to git.kernel.org (git.kernel.org)|172.105.64.184|:443... connected. HTTP request sent, awaiting response... 301 Moved Permanently Location: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/ linux.git/snapshot/linux-7.0-rc3.tar.gz [following] --2026-03-14 14:53:51-- https://git.kernel.org/pub/scm/linux/kernel/ git/torvalds/linux.git/snapshot/linux-7.0-rc3.tar.gz Reusing existing connection to git.kernel.org:443. HTTP request sent, awaiting response... 200 OK Length: unspecified [application/x-gzip] Saving to: ‘/dev/null’ /dev/null [ <=> ] 248.03M 51.9MB/s in 5.0s 2026-03-14 14:53:56 (49.3 MB/s) - ‘/dev/null’ saved [260080129] # wget -O /dev/null https://git.kernel.org/torvalds/t/linux-7.0- rc3.tar.gz --2026-03-14 14:53:58-- https://git.kernel.org/torvalds/t/linux-7.0- rc3.tar.gz Resolving git.kernel.org (git.kernel.org)... 172.105.64.184, 2a01:7e01:e001:937:0:1991:8:25 Connecting to git.kernel.org (git.kernel.org)|172.105.64.184|:443... failed: Connection timed out. Connecting to git.kernel.org (git.kernel.org)| 2a01:7e01:e001:937:0:1991:8:25|:443... failed: Network is unreachable. Before the 69894e5b4c5e ("netfilter: nft_connlimit: update the count if add was skipped") commit this worked.Thanks for the report. I have reproduced this on upstream kernel. I am working on it.
This is what is happening: 1. The first connection is established and tracked, all good. When it finishes, it goes to TIME_WAIT state 2. The second connection is established, ct is confirmed since the beginning, skipping the tracking and calling a GC. 3. The previously tracked connection is cleaned up during GC as TIME_WAIT is considered closed. 4. count is therefore 0 and xt performs a drop. There are two different approaches to fix this IMHO. The first one would be to stop considering TIME_WAIT as closed. But that would artificially solve the issue. The second one is to check what is the TCP status inside the nf_ct_is_confirmed() check and if it is SENT or RECV but confirmed there are two options - ore it is a retransmission or the ct was confirmed even before we tracked it. In both situations, perform an insert with a GC. Then we make sure no duplicate tracking is happening and the connection is tracked properly. The following diff fixes it, what do you think? I can send a formal patch if this solution is considered acceptable.
diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c
index 00eed5b4d1b1..ae94e5d7e00b 100644
--- a/net/netfilter/nf_conncount.c
+++ b/net/netfilter/nf_conncount.c@@ -78,6 +78,15 @@ static inline bool already_closed(const struct nf_conn *conn)
return false;
}
+static inline bool tcp_syn_sent_or_recv(const struct nf_conn *conn)
+{
+ if (nf_ct_protonum(conn) == IPPROTO_TCP)
+ return conn->proto.tcp.state == TCP_CONNTRACK_SYN_SENT ||
+ conn->proto.tcp.state == TCP_CONNTRACK_SYN_RECV;
+ else
+ return false;
+}
+
static int key_diff(const u32 *a, const u32 *b, unsigned int klen)
{
return memcmp(a, b, klen * sizeof(u32));@@ -183,6 +192,9 @@ static int __nf_conncount_add(struct net *net, * might have happened before hitting connlimit */ if (skb->skb_iif != LOOPBACK_IFINDEX) { + if (tcp_syn_sent_or_recv(ct)) + goto check_connections; + err = -EEXIST; goto out_put; }
Thanks, Fernando.