Linux: Firewall Tutorial, iptables, netfilter

By Xah Lee. Date: . Last updated: .

This is a tutorial of Linux's iptables command. You need to have a basic understanding of TCP/IP. If not, see: TCP/IP Tutorial for Beginner.

firewall 21570
image source

iptables is a advanced firewall for Linux. It can {drop, redirect, modify/NAT} packets based on {IP address, port, …}. For a short intro of what it can do, see: Linux: What's Netfilter, iptables, Their Differences?

you need to be root to use iptables. So, sudo su root first, or precede all your commands by sudo.

A Simple Example

first, do this to first see that your network is working:

# check network
ping -c 3

Now, do:

# add a rule to drop ALL incoming packets
sudo iptables --table filter --append INPUT --jump DROP

now, all packets coming to your computer are dropped. Now try ping again. It won't work.

you can look at the packet filtering rules you've added:

# show a list of rules
sudo iptables --list

sample output:

Chain INPUT (policy ACCEPT)
target     prot opt source               destination
DROP       all  --  anywhere             anywhere

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

now, clear the rules you've created.

# remove all filtering rules
sudo iptables --flush

now, try ping -c 3 again. It works again.

Basic Concepts and Terminology

iptables is a complex software, and the man page is badly written and hard to understand. You need to understand the following basic concepts first, then, you'll be able to read the man page as reference.

firewall works by matching packets with rules. When a rule match, a specified action is done, such as blocking the packet or letting it pass or alter some info in the packet or redirect. The rules can be checking on IP address, port number, protocol, number of packets, a particular network interface the packet is from or to, or connection state, etc.

The iptables command is used to set the rules and actions.

tables, chain, target

The rules are grouped into “table”, then “chain”. A action to be done on a packet is called “target”.

Basic Syntax


# list current rules for filter table
sudo iptables --table filter --list

here's a sample output:

# iptables --list

Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     icmp --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere
ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:ssh
REJECT     all  --  anywhere             anywhere            reject-with icmp-host-prohibited

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
REJECT     all  --  anywhere             anywhere            reject-with icmp-host-prohibited

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

On your machine, you'll probably see no rules, because, you haven't set any rules yet.

The general syntax is this:

iptables --table table --append/delete/rename/… CHAIN rule --jump TARGET

It means, work with table named table, then append/delete/rename the rule rule to the chain named CHAIN, when match, do TARGET.

here's one example:

sudo iptables --table filter --append INPUT --match conntrack --ctstate ESTABLISHED,RELATED --jump ACCEPT

here's what it means, broken down in parts:

iptables Tables

The possible table names are:

For example, you can see the list of rules in a table:

sudo iptables -table filter --list
sudo iptables -table nat --list

Builtin Chains

Each table has several builtin chains (name for a list of rules) defined by default.

“filter” Table Builtin Chains

“nat” Table Builtin Chains

“mangle” Table Builtin Chains

“raw” Table Builtin Chains

“security” Table Builtin Chains

the “filter” table is the most useful table.

Predefined Actions (buildin targets)

Remember: “target” is a name for a defined action. By default, the following are defined:

The above are the most basic ones. There are many others that comes with iptables but are considered “extension modules”. For example, there's also REJECT that is like DROP but also send back a error packet. There's also LOG, which doesn't do anything to the packet but writes to log.

Print Options

shortcommand syntaxpurpose
-L--list chain rulenumList the rules in a chain or all chains. Default table is “filter”
-S--list-rules chain rulenumPrint the rules in a chain or all chains
-n--numericnumeric output of addresses and ports
-v--verboseprint more info
--line-numbersprint line numbers when listing rules
-x--exactdisplay exact values for numerical values

Commands to Edit Chain

Here are the commands to edit “chain”. The “chain” here is a name.

Rule in chain are ordered, and can be identified by their number, starting with 1.

shortcommand syntaxpurpose
-A--append chain ruleAppend to chain
-D--delete chain ruleDelete matching rule from chain
-D--delete chain rulenumDelete rule rulenum (1 = first) from chain
-I--insert chain rulenumInsert in chain as rulenum (rulenum defaults to 1)
-R--replace chain rulenumReplace rule rulenum (1 = first) in chain
-X--delete-chain chainDelete a user-defined chain, or all user-defined chains.
-E--rename-chain old_chain new_chainChange chain name, (moving any references)
-N--new chainCreate a new user-defined chain
-C--check chain ruleCheck for the existence of a rule
shortcommand syntaxpurpose
-F--flush chain]Delete all rules in chain. If not specified, delete all chains
-Z--zero chain rulenumclear packet counters and byte counter in chain or all chains
shortcommand syntaxpurpose
-P--policy chain targetChange policy on chain to target. (chain must be a builtin one only, and target can only be one of {ACCEPT, DROP, QUEUE}.)

Syntax for Rules

Some rules can be preceded by !, meaning boolean inverse. That is, changing “matching” to “not matching”. These are marked by a star ★.

shortoption syntaxpurpose
-p--proto protoprotocol. proto is protocol number or one of {tcp, udp, udplite, icmp, esp, ah, sctp} or one in “/etc/protocols” or “all”. “all” is default.
-4--ipv4Nothing (line is ignored by ip6tables-restore)
-6--ipv6Error (line is ignored by iptables-restore)
-s--source address/masksource IP address
-d--destination address/maskdestination IP address
-m--match matchturn on extended match named match. (may load extension)
-f--fragmentmatch second or further fragmented packets only
-i--in-interface name[+]set rule to match only incoming packets on network interface name ([+] for wildcard)
-o--out-interface name[+]set rule to match only out going packets on network interface name ([+] for wildcard)
--set-counters packets bytesturn on packet counter for --insert, --append, --replace

Options for Action (target)

shortoption syntaxpurpose
-j--jump targetgoto action named target (may load target extension)
-g--goto chaingoto rule set named chain

General Options

shortoption syntaxpurpose
-t--table tableuse rules in table table (default is “filter”)
--modprobe=cmdtry to insert modules using command cmd
shortoption syntaxpurpose
-V--versionprint version number.

The above are general options. If you work with iptables a lot, you basically have to memorize all of the above. There are MANY MORE options, for specifying port, protocol, network adaptor, TCP packet detail, …. See man iptables.


when --table table is not specified, the “filter” table is used by default.

# ban a MAC address
sudo iptables --table filter --append INPUT --match mac --mac-source 00:0f:b4:ac:a0:58 --jump DROP
# redirect all HTTP traffic to a specific server
sudo iptables --table nat --append PREROUTING --protocol tcp --dport 80 --jump REDIRECT --to-port
# ban a ip address
sudo /sbin/iptables --table filter --append INPUT --source $ipAddr --jump DROP
# set default action to drop
sudo iptables --policy INPUT DROP

Clean Your Tables, Rules, Chains

When testing, you can use --flush to clear out the rules. You need to flush every table if you have added rules in them.

You can save the following as and run them in one shot sudo bash

# remove all filtering rules, remove all user created chains

iptables -t filter --flush
iptables -t filter --delete-chain

iptables -t nat --flush
iptables -t nat --delete-chain

iptables -t mangle --flush
iptables -t mangle --delete-chain

iptables -t raw --flush
iptables -t raw --delete-chain

iptables -t security --flush
iptables -t security --delete-chain

If you have created a “policy” (default action), the flush won't clean it. You'll need to set policy yourself.

# set policy to accept
sudo iptables --policy INPUT ACCEPT

# you need to set policy for every table's chain too, if you've changed them.

# reset SOME default policies
iptables -t filter --policy INPUT ACCEPT
iptables -t filter --policy FORWARD ACCEPT
iptables -t filter --policy OUTPUT ACCEPT
iptables -t nat --policy PREROUTING ACCEPT
iptables -t nat --policy POSTROUTING ACCEPT
iptables -t nat --policy OUTPUT ACCEPT
iptables -t mangle --policy PREROUTING ACCEPT
iptables -t mangle --policy OUTPUT ACCEPT

# there's also table raw and security.
# there's might be other chains too.

More Examples

# accept incoming packet that's bound to tcp and destined for ssh port
sudo iptables --append INPUT --protocol tcp --dport ssh --jump ACCEPT
# accept incoming packet that's bound to tcp and destined for port 80
sudo iptables --append INPUT --protocol tcp --dport 80 --jump ACCEPT
# block all traffic
sudo iptables --append INPUT --jump DROP
# accept rule to INPUT ruleset in filter table, for traffic bound to loopback address

# add rule to filter table,
# as 1st rule
# in the INPUT set
# for traffic bound to loopback address, accept

sudo iptables --insert INPUT 1 --in-interface lo --jump ACCEPT
# log dropped packets

sudo iptables --insert INPUT 5 --match limit --limit 5/min --jump LOG --log-prefix "dropped: " --log-level 7
# allow max of 2 telnet connections
sudo iptables --append INPUT --proto tcp --syn --dport 23 --match connlimit --connlimit-above 2 --jump REJECT
# limit the number of parallel HTTP requests to 16 per class C sized source network (24 bit netmask)
sudo iptables --proto tcp --syn --dport 80 --match connlimit --connlimit-above 16 --connlimit-mask 24 --jump REJECT
# the following allows one way traffic

# Create chain which blocks new connections, except if coming from inside.
sudo iptables --new myblock
sudo iptables --append myblock --match state --state ESTABLISHED,RELATED --jump ACCEPT
sudo iptables --append myblock --match state --state NEW --in-interface ! ppp0 --jump ACCEPT
sudo iptables --append myblock --jump DROP

# Jump to that chain from INPUT and FORWARD chains.
sudo iptables --append INPUT --jump myblock
sudo iptables --append FORWARD --jump myblock


the following is from

sudo iptables --policy INPUT ACCEPT
sudo iptables --flush
sudo iptables --accept INPUT -i lo -j ACCEPT
sudo iptables --accept INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
sudo iptables --accept INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables --policy INPUT DROP
sudo iptables --policy FORWARD DROP
sudo iptables --policy OUTPUT ACCEPT

here's the result after above. sudo iptables --list --verbose

Chain INPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     all  --  lo     any     anywhere             anywhere
    0     0 ACCEPT     all  --  any    any     anywhere             anywhere            state RELATED,ESTABLISHED
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere            tcp dpt:ssh
Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination


# Limit the number of connections to a particular host:
ip6tables --proto tcp --syn --dport 49152:65535 -d 2001:db8::1 --match connlimit --connlimit-above 100 --jump REJECT
# limit the number of parallel HTTP requests to 16 for the link local network (ipv6)

ip6tables --proto tcp --syn --dport 80 --source fe80::/64 --match connlimit --connlimit-above 16 --connlimit-mask 64 --jump REJECT

Saving and Restoring Firewall Config

sudo iptables-save → dump iptables rules to stdout

sudo iptables-restore → Restore IP Tables


Related Reference

StackOverflow ServerFault

Related Tools

Ask me question on patreon