Conntrack: when health checks pass and traffic stops
A stateful firewall decides a connection's path once, at creation, so a long-lived flow can keep a stale decision while every fresh probe passes.
In this article 5 sections
- A stateful firewall makes the forwarding and NAT decision for a connection on its first packet and keeps it in the state table for the life of the flow. Routing and policy changes are not applied to flows that already exist.
- A continuous stream such as syslog over TCP, database replication, a metrics forwarder or a tunnel can hold one connection for weeks. If the network changes underneath it, that flow keeps the old decision while pings and new connections get the new one, so every check passes.
- Monitor what arrives, not what is up: byte counters against expected volume, state-table occupancy, and an end-to-end check that the data landed. A frozen flow is fixed by deleting its state entry, not by rebooting the firewall.
The decision is made once
Connection tracking exists so that a firewall evaluates policy once per flow instead of once per packet. The first packet is matched against the rules, the forwarding and NAT result is written to the state table, and every later packet of that flow is a hash lookup. netfilter conntrack, the pf state table in OPNsense and the connection table in Cisco Firepower all work this way. The entry lives until the flow closes or its idle timeout expires, and for an established TCP connection that timeout is measured in days.
The consequence is easy to miss: a change to routing, to a NAT rule or to an interface affects the next flow, not the flows already in the table.
Two ways this drops traffic quietly
The stale path. A route changes, an interface moves, a NAT pool is edited. New connections take the new path. A long-lived stream keeps its original egress interface or NAT binding, so its packets leave the old way, towards a next hop that no longer forwards them or without the translation the far end now expects. The sender's TCP stack retransmits patiently on a connection it still considers established, the receiver sees nothing, and no process on either side has failed.
The full table. nf_conntrack_max is reached, usually because of a scanner, a NAT'd client with thousands of short flows, or long timeouts on a busy box. The kernel logs one line and starts dropping the first packet of new connections while existing flows continue. A health check that reuses an open connection keeps passing; users opening new ones get timeouts.
Neither shows up in process monitoring. The daemon is up, the listener is bound, the rule is present.
Why the health checks pass
A ping, a TCP connect probe and a firewall's path simulator each create a new flow or evaluate the policy fresh, so each receives the current, correct decision. The green reading is true about the wrong object. The question to put to the firewall is not what it would do with a new packet, but what it is doing with the flows it already holds.
# the live flows from one source, with their state and NAT binding
conntrack -L -s <source>
# how full the table is
sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
# the kernel's own report of a full table
journalctl -k | grep -i 'nf_conntrack: table full'A flow that predates the last routing change and shows an egress or a translation that no current rule would produce is the entry to look at.
What to monitor
Arrival, not liveness. Rows per minute landing in the collector's database, messages per minute at the syslog receiver, a replication position that advances. A flat line is the alarm, whatever the process state says.
Counters against expectation. The byte counter on the rule that carries a stream grows at roughly the stream's rate. A rule that should be busy and is nearly flat means the packets are matching a different rule or not arriving at all.
State-table occupancy. Alert on nf_conntrack_count as a fraction of nf_conntrack_max well before it fills, and on the kernel's table-full message, which otherwise sits in the log unread.
Synthetic checks shaped like the real traffic. A probe that holds a long-lived connection and pushes a record through it at intervals, verified at the far end, tests the thing that breaks. A fresh ping tests the thing that does not.
After every routing or NAT change, list the flows older than the change and decide on purpose whether to flush them.
The fix is a scalpel
Delete the state for the affected source. The next packet creates a fresh entry with the current decision and the stream resumes.
conntrack -D -s <source>A reboot or a full flush also works, and takes every other live connection down with it. For a full table, the fix is to raise nf_conntrack_max together with the hash size, shorten the established timeout where days are not needed, and find the client that is creating the flows.
Straight answers
Why does the firewall say allow but no traffic gets through?+
The path simulator computes what a new packet would get. An existing flow is bound to the decision made when it was created, and the simulator does not read the state table. Dump the live flows for that source and compare their egress and translation with what the current rules would produce.
Why does ping work when the application stream does not?+
Ping creates short-lived flows that time out in seconds, so each one receives a fresh decision. The stream holds one long-lived flow and keeps whatever decision it was born with.
How do I find a connection stuck on the wrong interface?+
conntrack -L filtered by source on netfilter, the state table view on pf or Firepower. Look for flows older than the last network change whose egress or NAT binding no current rule would give them.
How do I fix it without rebooting the firewall?+
Delete the state entries for that source with conntrack -D. The next packet is evaluated fresh. Everything else in the table is untouched.
A stream went quiet while every check is green?
Talk to an engineer who reads the state table, not the simulator.
Talk to an engineerYou’ve reached the end.
Put the reading to work
What are
you running?
Tell us about the workload, traffic and recovery requirements. Start with the server builder, or talk through the details with an engineer.
Written by the engineers who build and run the infrastructure it describes.Talk to an engineer
