iptables is a firewall utility that allows you to filter network traffic. The filter table, which is the default, has three built-in chains: INPUT (for packets destined for the local machine), OUTPUT (for packets originating from the local machine), and FORWARD (for packets being routed through the machine). Each chain has a default policy, usually ACCEPT or DROP. Rules are added to these chains to control traffic.

When you set up a firewall, you often want to allow traffic that is part of an existing connection, and also traffic that is related to an existing connection. This is crucial for protocols that use multiple connections, like FTP, or for allowing responses to your own outgoing requests. Without these rules, even if you allow incoming traffic on a specific port, the replies might be blocked, making the connection unusable.

Let’s see this in action. Imagine you have a web server running on your machine, and you want to allow incoming HTTP traffic on port 80. A basic rule to allow this might look like:

iptables -A INPUT -p tcp --dport 80 -j ACCEPT

However, this rule only allows new incoming connections to port 80. The server’s responses, which are outgoing packets from port 80 to your client’s ephemeral port, would be blocked if your OUTPUT chain policy is DROP or if there’s a restrictive rule there. Similarly, if you’re making an outgoing SSH connection to a remote server, and your INPUT chain policy is DROP, the incoming packets from the SSH server’s reply would be blocked.

This is where the state module in iptables comes in. It allows you to match packets based on their connection state. The common states are:

  • NEW: A packet that is the first in a connection.
  • ESTABLISHED: A packet that is part of an existing connection. This means it’s either a request from the server to the client, or a reply from the client to the server, and iptables has seen packets from both directions.
  • RELATED: A packet that is related to an existing connection but not directly part of it. This is common for protocols like FTP, where a control connection might spawn a separate data connection.
  • INVALID: A packet that doesn’t match any known connection state. This could be a malformed packet or a packet that arrived out of order.

To allow established and related connections, you add rules using the -m state --state option. A very common and effective pattern is to place these rules at the beginning of your INPUT and FORWARD chains.

Here’s how you’d typically set up rules to allow established and related traffic:

# Allow all incoming traffic that is part of an established connection
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow all outgoing traffic that is part of an established connection
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

If your default INPUT policy is DROP, these rules ensure that your server can properly respond to outgoing requests and that legitimate incoming connections, once established, can continue to communicate. The ESTABLISHED,RELATED state covers not just TCP but also UDP, where connection state is inferred.

For FORWARD chains (if your machine acts as a router), you’d do the same:

# Allow forwarded traffic that is part of an established connection
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

The INVALID state is also important. Packets marked as INVALID are often malicious or malformed. It’s good practice to drop them to enhance security. You would typically add this rule after your ESTABLISHED,RELATED rules, so you don’t accidentally drop legitimate traffic.

# Drop invalid packets
iptables -A INPUT -m state --state INVALID -j DROP
iptables -A FORWARD -m state --state INVALID -j DROP

By placing the ESTABLISHED,RELATED rules at the top of your chains, you create a "allow list" for traffic that your system already recognizes as legitimate. Any traffic that doesn’t match these initial ESTABLISHED,RELATED rules then falls through to subsequent rules, where you can be more granular about allowing NEW connections (like the HTTP example earlier) or dropping everything else.

The most surprising thing is that iptables maintains connection tracking information not just for TCP and UDP, but also for protocols like ICMP. When an ICMP message arrives (like a ping reply), iptables can often determine if it’s RELATED to an outgoing ICMP request, allowing it through even if you have a strict INPUT policy.

After allowing established and related connections, the next common hurdle is managing new incoming connections for specific services, like allowing SSH on port 22 while blocking everything else on the INPUT chain.

Want structured learning?

Take the full Iptables course →