Connection Tracking: The Hidden State Machine

In the previous chapter, we learned that netfilter is the framework responsible for packet filtering, NAT, and packet manipulation inside the Linux kernel.

One important feature of netfilter is that it is stateful. Instead of treating every packet as an independent event, the kernel remembers ongoing network conversations. This memory is provided by a subsystem called connection tracking, often abbreviated as conntrack.

Connection tracking is one of the most important—and least visible—parts of Linux networking. It enables stateful firewalls, makes NAT work correctly, and allows Linux to understand the relationship between packets.

In this chapter, we'll explore how connection tracking works, how to inspect it, and why understanding it is essential when troubleshooting Linux networks.


What Is Connection Tracking?

Despite its name, connection tracking does not only track TCP connections.

Instead, it tracks network flows.

A flow is identified by information such as:

  • Source IP address
  • Destination IP address
  • Transport protocol (TCP, UDP, ICMP, ...)
  • Source port (if applicable)
  • Destination port (if applicable)

This combination is often called the 5-tuple.

For example:

SourceDestinationProtocol
192.168.1.10:5300010.0.0.5:443TCP

Once Linux sees the first packet belonging to this flow, it creates an entry in the connection tracking table.

Future packets matching the same flow can be recognized immediately.


Why Does Linux Need Connection Tracking?

Imagine a firewall without memory.

Every packet would have to be evaluated independently.

Consider a TCP connection:

Client                     Server

SYN -------------------->

      <---------------- SYN-ACK

ACK -------------------->

HTTP Request ----------->

      <---------------- HTTP Response

Without remembering previous packets, Linux cannot easily determine whether an incoming packet belongs to an existing connection or is an unexpected packet.

Connection tracking solves this problem.

It allows Linux to recognize that:

  • this packet belongs to an already established flow,
  • this packet is a reply,
  • this packet is part of an existing NAT mapping,
  • this packet is invalid.

The Hidden State Machine

Connection tracking internally maintains a state machine for every tracked flow.

The most commonly encountered states are:

  • NEW
  • ESTABLISHED
  • RELATED
  • INVALID

These are connection tracking states, not TCP states.

That distinction is extremely important.


NEW

NEW means the packet is attempting to start a new flow.

For TCP, this is typically the initial SYN packet.

Example:

Client ---- SYN ----> Server

The flow has never been seen before.


ESTABLISHED

Once traffic is flowing in both directions, the connection becomes established.

Client <=====> Server

Packets belonging to this conversation match the existing conntrack entry.

Most firewall rules simply allow:

  • established
  • related

traffic while applying stricter rules only to new connections.


Some protocols create additional connections that logically belong to an existing one.

Examples include:

  • FTP data channels
  • Some ICMP error messages

Although these packets belong to a different flow, they are related to an existing tracked connection.


INVALID

Packets may be marked invalid for many reasons:

  • malformed packets
  • packets that cannot be associated with any flow
  • corrupted headers
  • unexpected sequence of packets

Many firewall configurations simply drop invalid packets.


Conntrack State vs TCP State

A very common misunderstanding is confusing conntrack states with TCP states.

TCP itself has many protocol states:

  • LISTEN
  • SYN_SENT
  • SYN_RECEIVED
  • ESTABLISHED
  • FIN_WAIT
  • TIME_WAIT
  • CLOSE_WAIT

Connection tracking does not replace TCP.

Instead, it provides a higher-level view used by the networking stack.

Think of it like this:

Application
      │
 TCP State Machine
      │
Connection Tracking
      │
Netfilter
      │
IP Layer

TCP handles reliable communication.

Connection tracking remembers flows for packet processing.


Following a New Connection

Let's follow an HTTPS connection.

Client                     Linux Server

SYN ------------------------>

          conntrack creates entry

SYN-ACK <--------------------

ACK ------------------------->

HTTPS Request --------------->

HTTPS Response <--------------

After the first packet, Linux already knows:

  • who initiated the flow,
  • both endpoints,
  • transport protocol,
  • ports,
  • current tracking state.

Every later packet can immediately match this entry.


Where Are Connections Stored?

The Linux kernel stores tracked flows inside the connection tracking table.

This table exists entirely inside the kernel.

Each entry contains information such as:

  • source address
  • destination address
  • ports
  • protocol
  • timeout
  • current state
  • NAT information (if applicable)

The table is constantly updated as packets arrive.

When a flow becomes inactive, its entry eventually expires.


Inspecting the Connection Tracking Table

The easiest way to inspect tracked connections is with the conntrack utility.

On Debian-based systems:

sudo apt install conntrack

List current entries:

sudo conntrack -L

Example output:

tcp      6 431999 ESTABLISHED src=192.168.1.10 dst=10.0.0.5 sport=53000 dport=443 \
src=10.0.0.5 dst=192.168.1.10 sport=443 dport=53000

Although the output looks intimidating, notice that it simply describes:

  • original direction
  • reply direction
  • protocol
  • state
  • timeout

Watch Connections Appear

Open one terminal:

sudo conntrack -E

This command listens for connection tracking events.

Open another terminal:

curl https://example.com

You should see events similar to:

NEW
UPDATE
DESTROY

This demonstrates that connection tracking is constantly updating its internal state.


Compare ss and conntrack

Run:

ss -tn

You may see:

ESTAB 192.168.1.10:53000 10.0.0.5:443

Now compare:

sudo conntrack -L

Although both commands show active communication, they answer different questions.

ss shows:

  • sockets
  • processes
  • transport-layer information

conntrack shows:

  • packet flows
  • flow state
  • NAT information
  • kernel tracking entries

They complement each other.


Observe UDP Tracking

Many people think only TCP is tracked.

Let's prove otherwise.

Start a DNS lookup:

dig example.com

Immediately inspect:

sudo conntrack -L | grep udp

You should see a UDP flow.

Although UDP has no handshake, Linux still tracks the exchange for a limited amount of time.

Connection tracking is therefore about flows, not protocol reliability.


Connection Tracking and NAT

Connection tracking and NAT are tightly coupled.

Consider a home router.

Laptop
192.168.1.20
      │
      ▼
Linux Router
Public IP
      │
      ▼
Internet

When the laptop sends a packet:

192.168.1.20:53000
        ↓
203.0.113.15:41000

The router changes the source address.

But when the reply returns:

203.0.113.15:41000

How does Linux know which internal machine should receive it?

Connection tracking remembers the translation.

Without conntrack, NAT would fail after the first packet.

This is why NAT depends heavily on connection tracking.

We'll examine NAT in detail in the next chapter.


Timeouts

Entries cannot remain in memory forever.

Each protocol has its own timeout values.

Examples:

  • TCP established connections
  • UDP flows
  • ICMP exchanges

Once the timeout expires, the kernel removes the entry.

You can inspect some timeout-related settings:

sysctl net.netfilter.nf_conntrack_tcp_timeout_established

Example:

net.netfilter.nf_conntrack_tcp_timeout_established = 432000

This value represents the timeout (in seconds) for established TCP connections.


Connection Tracking Capacity

Since every active flow consumes kernel memory, the tracking table has a maximum size.

Check the current limit:

cat /proc/sys/net/netfilter/nf_conntrack_max

Check current usage:

cat /proc/sys/net/netfilter/nf_conntrack_count

Example:

Current entries : 1854
Maximum entries : 262144

If the table becomes full, Linux cannot track additional flows.

This can cause:

  • connection failures
  • dropped packets
  • NAT failures
  • firewall issues

Monitoring conntrack usage is therefore an important operational task on busy systems.


When Connection Tracking Causes Problems

Although conntrack is extremely useful, it can also become a bottleneck.

Common issues include:

  • exhausted conntrack table
  • incorrect timeout values
  • asymmetric routing
  • hardware offloading interactions
  • extremely high connection rates

Large Kubernetes clusters, load balancers, and busy NAT gateways often require conntrack tuning.


When Is Connection Tracking Not Used?

Not every packet must be tracked.

Examples include:

  • some high-performance routers
  • specialized packet forwarding
  • certain DDoS mitigation scenarios

Skipping connection tracking reduces memory usage and CPU overhead, but you lose stateful firewalling and NAT support.

For most Linux servers, leaving connection tracking enabled is the correct choice.


Debugging Checklist

When packets do not behave as expected, ask:

  1. Does the flow appear in conntrack -L?
  2. Is the state what you expect?
  3. Is the conntrack table full?
  4. Are timeout values appropriate?
  5. Is NAT depending on this connection?

Answering these questions often narrows the problem quickly.


Summary

  • Connection tracking is a kernel subsystem that remembers network flows.
  • It is implemented as part of the netfilter framework.
  • It tracks flows, not just TCP connections.
  • Common conntrack states are NEW, ESTABLISHED, RELATED, and INVALID.
  • Conntrack states are different from TCP protocol states.
  • NAT relies heavily on connection tracking to map reply traffic back to the correct flow.
  • The conntrack utility allows you to inspect, monitor, and troubleshoot tracked flows.
  • Every tracked flow consumes kernel memory, making table sizing and monitoring important on busy systems.

In the next chapter, we'll build on this foundation by exploring Network Address Translation (NAT) and see how connection tracking enables address and port rewriting while preserving end-to-end communication.