The lead came from someone else's comment: someone called the --state ESTABLISHED,RELATED rule a blank check of trust and dropped a number next to it — 432 000 seconds. That's how long Linux keeps an established connection record by default. Exactly five days.
The comment wasn't lying, but it didn't tell the whole story. The rule really does issue a blank check: a packet matching an already-known flow bypasses all other rules entirely. The only question is the duration. Five days is longer than most auth tokens, VPN sessions, and admin shifts live. I got curious where this number came from and who chose it in the first place.
It turned out they justified this number with a single sentence about a long weekend, and that it's not about security, but about the cost of an error in either direction.
The value lives in the kernel sources as literally a four-word constant: [TCP_CONNTRACK_ESTABLISHED] = 5 DAYS. In the modern nf_conntrack_proto_tcp.c, right next to the timeout table, hangs a comment left back in the 2.4 branch: "Need to look at ipfilter timeouts and state transitions more carefully. They are more complex. —RR". The initials belong to Rusty Russell, and the comment itself is an honest admission that the table here is not the result of design, but the result of porting.
There was something to port from. The mechanism came from two sources, both written for NAT, not for a firewall. The ip_masq module — masquerading from the kernel 2.0 era — kept a TCP record for 15 minutes (MASQUERADE_EXPIRE_TCP 15*60*HZ), because it only needed to remember the address translation. But IP Filter, where the very idea of the state table came from, kept an established record for exactly five days: the ipf.5 man page says exactly that — a connection can sit idle for five days before being evicted from the state table. The five-day timeout arrived in Linux from the world of stateful packet filtering and stayed even after IP Filter itself left that world.
One of the netfilter authors explained the meaning of this number in 2005 with a phrase that's hard to improve upon: the timeout was set this long so you could leave for a long weekend and, upon returning, continue working in the same telnet session. In the same mailing list thread, engineers discussed that with a ten-minute timeout, they would have to remember to send RSTs for unfamiliar traffic, otherwise remote clients would quietly "go mute" on live connections.
This number has a formal antipode — and it appeared seven years later. RFC 5382, written in 2008 for NAT devices, requires the "established connection idle-timeout" to be no less than 2 hours and 4 minutes. The rationale in the standard is arithmetic: TCP keep-alive is sent every two hours by default (that's RFC 1122), and the four minutes are added to allow packets to cross the device. Keep the record shorter, and NAT starts tearing down live sessions.
The Linux default exceeds this minimum by 58 times. And not by oversight: the standard sets a floor, but no ceiling, because there is no ceiling to TCP session longevity — an established connection can sit idle indefinitely without sending a single byte. The standard protected applications from too-short timeouts; nobody asked it to protect against too-long ones.
Exceptions do exist, and they are visible by the volume of discussions. In 2014, an OpenWrt ticket was opened: "value is too low and doesn't comply with RFC 5382" — when it was 3600 seconds. The ticket author asked for 7204, but project maintainer Felix Fietkau set it to 7440 in a commit — exactly two hours and four minutes. This is exactly the value OpenWrt carries to this day, making it one of the few distributions where the timeout on a router is a hundred times shorter than on a server.
The exact same number surfaced in 2026 in a completely different story. A breakdown was posted on the Turris forum: after an update, the system shipped with a value of 432000 — a patch bringing the timeout to the "correct" 7440 was accidentally lost during the build. Then a completely mundane scenario was described: a LAN machine is running BitTorrent, the router's table is sized for roughly 31 232 entries (formula based on 512 MB of memory), and it fills up in hours. After that, the kernel starts returning EPERM on any new connection — from any device on the network. The author honestly corrected himself after the fact: he first blamed a specific 2020 commit, then admitted that the commit was a refactoring, the logic came from 2018, and his own breakdown was based on an unverified guess.
Before calling five days insane, it's worth understanding what an error in either direction costs the developer. A conntrack entry takes about 304 bytes, plus slab allocator padding — on a real machine, a benchmark gave about 316 bytes per flow. An error towards "too long" costs three hundred bytes of RAM for every extra connection. An error towards "too short" tears down a live user's session: browser, SSH, database, anything that stayed silent longer than the timeout.
The asymmetry here is absolute, and the choice is made precisely in its favor. The kernel doesn't explicitly assign the table size at all: nf_conntrack_buckets is calculated by dividing total memory by 16384, capped at 65536 buckets, and nf_conntrack_max defaults to four buckets. On any machine with more than 4 GB of RAM, this yields 65536 buckets and a ceiling of 262 144 entries — a number nobody on your team chose. It was chosen by a heuristic written for a generic server, completely unaware that you planned to put several hundred guests behind a single masquerade rule.
The eviction mechanism is also not designed in favor of neatness. When space runs out, the kernel doesn't sort out which entries are more important: the early_drop function iterates through a bucket and skips anything with the ASSURED flag set, and ASSURED is set exactly when a correct ACK is seen in the established state. Meaning the first to go under the knife are entries created by mistake or oversight; the five-day ones are protected. This is exactly the logic the number was chosen for, and it works.
Then begins the nastiest scenario, and it's described in detail. In Cloudflare's research, they shrunk the table to seven entries and showed what happens during overflow: SYN packets vanish into thin air. No -j DROP rule, no switch — just the very fact of using conntrack means that with a full table, packets creating a new flow are hard-dropped, with a single line in dmesg: nf_conntrack: table full, dropping packet. For UDP there's a bonus: new outbound flows get EPERM, meaning the application receives a permissions error where something completely different ran out.
A classic story on this topic is from December 2023, about a GKE cluster. It started with DNS resolution errors and increased latency to "heavy" RPC services, looked like a platform issue, and the author had to go to Google support. The answer was boring: the nodes were spitting out the same message about a full table. You can beat this in two ways — expand the table or reduce the number of connections, and the author chose the latter because he didn't want to change the table size in production.
Large cloud NATs solve the same problem in the exact opposite way, and they do it cynically and correctly. The AWS gateway keeps an idle connection for only 350 seconds and sends an RST, not a FIN, upon expiration — meaning it doesn't pretend the connection was closed gracefully.
The ceiling for simultaneous connections to a single destination address is 55 000 per IP, cured by adding addresses. In 2023, Amazon made timeouts configurable and honestly explained why in the announcement: "some workloads inefficiently consume their connection allowance due to leaks and idle sessions". In the same text about choosing defaults, there's a wonderful admission that almost all states live from 30 to 180 seconds, "except for established, which can last up to five days". The provider saves money because it pays for the metric; the server owner saves money because it pays for user dissatisfaction.
And now for what makes this whole story worth examining. While the entry is alive, it rechecks nothing: not the password, not the certificate, not the application-level session. And it doesn't check everything it should.
In October 2024, researchers from Anvil Secure described what they found in real audits, not in theory. The conntrack module does not store which interface the connection was established on. An "allow RELATED,ESTABLISHED" rule on the external interface applies to all flows, including those established between two internal hosts.
Spoof an internal address, send a packet with the same address-port 4-tuple to the public interface — and the firewall will let it through by state. This works even with routing between interfaces disabled. The authors then used spoofing to create dynamic port forwards via NAT-PMP/PCP, send fake mDNS responses, and inject themselves into the exchange between two internal machines. Their list of targets included not only routers, but also hypervisors with virtual networks, VPN gateways, embedded systems, and drones: any multi-homed device without anti-spoofing rules is, in their words, "likely vulnerable".
The second story is set up more beautifully. In 2012, the kernel faithfully implemented RFC 5961 — the challenge ACK mechanism, designed as a defense against injecting spoofed packets into a connection. A response to a suspicious packet is sent, but requires an exact sequence number, and to prevent the mechanism itself from being weaponized, the challenge-ACK rate is limited to 100 per second for the entire machine. By a global counter.
In 2016, a team from UC Riverside showed that this counter is an ideal side channel: an off-path attacker creates load on the shared resource and, by response time, measures whether two arbitrary hosts are talking to each other and with what sequence numbers. Ten seconds to test a hypothesis, 40 to 60 seconds for a full cycle, 88–97% success rate — against SSH and Tor, including connection tearing and content injection. The vulnerability was assigned CVE-2016-5696, and the fix in kernel 4.7 (suggested by Linus) looks exactly like a weakening of defenses: the limit was raised from 100 to 1000 and randomization was added so the counter couldn't be used for calculations. Red Hat went all the way in its recommendations: suggesting setting tcp_challenge_ack_limit to 999999999, effectively disabling rate limiting, just to close the side channel.
There are plenty of good targets for "credit by state" without any exotic tricks. Samy Kamkar's NAT Slipstreaming (2020) tricked an unsuspecting user into opening a website that, via the SIP ALG, created a new entry in the state table on their own router — after which an external attacker connected directly to the internal service. And in 2001, an advisory text began with the phrase "almost 100%": the ip_conntrack_ftp module parsed the PORT command and didn't validate the address and port passed in it, inserting them into the RELATED table as-is. The firewall turned into an allowlist that the attacker populated themselves.
Three places where sources don't align, and I won't pretend they do.
First — about "five days" as a common place for stateful filters. In the IPFilter man page under NetBSD, the text states plainly: an established record can sit idle for five days. But the variable table on the same man page lists the default tcp_idle_timeout as 864000 seconds, which is ten days. One page, two numbers differing by a factor of two, and not a word about which one is live.
Second — the number is not only legitimized, but sometimes found by accident. A patch limiting the timeout for a connection "picked up" mid-stream (nf_conntrack_tcp_loose=1 — the default that allows a lone ACK without a SYN to create a new entry right in established with a full five-day timeout) appeared in 2013 after analyzing a trace on someone's working firewall. There, after a 61-second pause, a final ACK spawned a fresh entry with a 432000 timeout and the UNREPLIED flag, and the patch author writes that "after a couple of hours, the table starts filling up with this garbage". This seems to me the most underrated line in the whole story: a five-day entry can arise from a single packet that nobody answered.
Third — about me, not the kernel. The temptation to say "the firewall deceives the user" I consider a stretch. The timeout was chosen so as not to tear down someone else's work, and for its task, it's quite optimal.
That's exactly why I don't want to draw a moral about careless sysadmins from this story. The real vulnerability in all described cases lies not in the length of the credit, but in the nature of the entry: it's four numbers describing a flow, and nothing beyond them. Lengthen or shorten the timeout — this part won't change.
I also peeked into the machine at hand: the 432000 value is in place, the table ceiling is 262 144 entries — numbers nobody chose, but produced by a memory-volume heuristic when the module loaded. It's useful to keep exactly one thing in mind: this timeout was almost never configured consciously, in either direction. It was simply inherited along with the code.
nf_conntrack_tcp_timeout_established = 432000)net/netfilter/nf_conntrack_proto_tcp.c — Linux kernel source code, master branch (tcp_timeouts table, ASSURED flag, mid-stream pickup handling)net/ipv4/netfilter/ip_conntrack_proto_tcp.c — kernel 2.4 archive on kernel.org (5 DAYS and comment with initials --RR)include/net/ip_masq.h and net/ipv4/ip_masq.c — kernel 2.x source archive (MASQUERADE_EXPIRE_TCP = 15 minutes)tcp_idle_timeout = 8640008b62e7b (Felix Fietkau) — base-files: adjust the default netfilter tcp established connection timeout as per RFC 5382package/kernel/linux/files/sysctl-nf-conntrack.conf — OpenWrt repository (value 7440)75ff39c in the kernel; Red Hat advisory on challenge ACK