Linux iptables Reference Guide with Examples

December 11, 2023

Getting Started

In this post, you will find the essential information about the Iptables with examples.

Traversing Tables & Chains

iptables traversing tables chains linux
The figure is redrawn based on this and this sources.

  • raw [PREROUTING]: Tpically to mark packets that should not be handled by the connection tracking system (i.e. don’t keep note of their state; don’t act as a stateful firewall on these packets). This is done using the NOTRACK target.

  • nat [PREROUTING]: Only the first packet in a stream of packets hits this table, and al subsequent packets of that stream follow whatever is determined with the first packet.

  • filter chain: The firewall. ACCEPT, DROP, or REJECT packets them depending on filters on the INPUT chain (packets destined for this host), OUTPUT chain (packets generated by this host), or FORWARD chain.

Connection Tracking System

Connection tracking is done to let the Netfilter framework know the state of a specific connection. Firewalls that implement this are generally called stateful firewalls. A stateful firewall is generally much more secure than non-stateful
firewalls since it allows us to write much tighter rule-sets.

Within iptables, packets can be related to tracked connections in four different so called states. These are known as NEW, ESTABLISHED, RELATED and INVALID.

All of the connection tracking is done by special framework within the kernel called conntrack. There are also more specific parts of conntrack that handles the TCP, UDP or ICMP protocols among others. These modules grab specific, unique, information from the packets, so that they may keep track of each stream of data. The information that conntrack gathers is then used to tell conntrack in which state the stream is currently in. For example, UDP streams are, generally, uniquely identified by their destination IP address, source IP address, destination port and source port.

All connection tracking is handled in the PREROUTING chain, except locally generated packets which are handled in the OUTPUT chain. If we send the initial packet in a stream, the state gets set to NEW within the OUTPUT chain, and when we receive a return packet, the state gets changed in the PREROUTING chain to ESTABLISHED, and so on source.


Make Rules Persistent

# Installation
sudo apt install iptables-persistent netfilter-persistent

# To save changes
sudo netfilter-persistent save

List Rules

# List Rules in filter table
sudo iptables -L
sudo iptables -L --line-numbers -n
sudo iptables -S

# Other tables
sudo iptables -L -t nat
sudo iptables -L -t mangle
sudo iptables -L -t raw

Essential Rules

# Allow loopback
sudo iptables -A INPUT -i lo -j ACCEPT
# Instead of add, you can insert directly into a place within the chain:
sudo iptables -I INPUT 1 -i lo -j ACCEPT

# Allow Established and Related Incoming Connections
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Drop Invalid Packets
sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

# Allow Incoming SSH (22/2224)
sudo iptables -A INPUT -p tcp --dport 2224 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Allow Incoming HTTP (80) and HTTPS (443)
sudo iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
3 -m conntrack --ctstate ESTABLISHED -j ACCEPT

# Allow Incoming MYSQL Request (3306)
sudo iptables -A INPUT -p tcp --dport 3306 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT


Using MASQUERADE any outgoing packet from the interface will have whatever IP address that is assigned to the interface.

iptables nat table snat prerouting linux
# assigne to eth1 interface IP to outgoing packets
sudo iptables -t nat -A POSTROUTING -s \
  -o eth1 -j MASQUERADE

# Any outgoing packet from eth0 will have the source IP of 
sudo iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to


iptables nat table dnat prerouting linux
# Any incoming packet destined to will be forwarded 
# to
sudo iptables -t nat -A PREROUTING -d \
  -p tcp -m tcp --dport 9991 -j DNAT --to-destination

Default Action

# Typically, you would want the default target for the INPUT 
# chain to be DROP. However, before setting that, make sure 
# you have rules in place to accept incoming traffic; 
# otherwise, you may lose your access
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT

Delete Rules

# Delete a rule be replacing -A to -D:
sudo iptables -t nat -D POSTROUTING -o eth0 -j SNAT --to

# Or list rules and identify the chain number of a rule
sudo iptables -L --line-numbers -v
# And then delete rule 16 from the chain INPUT
sudo iptables -D INPUT 16

Flush All Chain

# Remove rules of chain INPUT
sudo iptables -F INPUT

# Remove chain INPUT
sudo iptables -X INPUT


You can mark a packet using TRACE and see the chain of rules that are applied on it:

sudo iptables -traw -A PREROUTING -i eth0 -p udp -m udp --dport 3000 -j TRACE

# Then look at the logs:
sudo dmesg -wH

Linux Networking