You can block IP address ranges with iptables by creating rules that match packets originating from those ranges and then dropping them. This is a fundamental way to control network traffic at the host level.

Let’s say you want to block the entire private IP range 192.168.0.0/16.

sudo iptables -A INPUT -s 192.168.0.0/16 -j DROP
sudo iptables -A FORWARD -s 192.168.0.0/16 -j DROP

The -A INPUT appends a rule to the INPUT chain, which handles traffic destined for the local machine. The -A FORWARD appends a rule to the FORWARD chain, which handles traffic passing through the machine (acting as a router). The -s 192.168.0.0/16 specifies the source IP address range using CIDR notation. -j DROP tells iptables to silently discard any matching packets.

You can also block specific ports within a range. For example, to block all SSH traffic (port 22) from the 10.0.0.0/8 range:

sudo iptables -A INPUT -s 10.0.0.0/8 -p tcp --dport 22 -j DROP

Here, -p tcp specifies the protocol, and --dport 22 specifies the destination port.

To block traffic to a specific range, you’d use the -d (destination) flag. For instance, blocking access to a server at 172.16.1.100 from anywhere:

sudo iptables -A OUTPUT -d 172.16.1.100 -j DROP

This rule would be added to the OUTPUT chain to prevent local processes from initiating connections to that destination.

If you need to block a list of individual IPs that don’t form a contiguous range, you can use multiple rules, or, for efficiency, the ipset utility. ipset allows you to create sets of IP addresses and then use a single iptables rule to reference that set.

First, create an IP set (e.g., for a list of malicious IPs):

sudo ipset create bad_ips hash:ip

Then, add individual IPs to the set:

sudo ipset add bad_ips 1.1.1.1
sudo ipset add bad_ips 2.2.2.2

Finally, create an iptables rule to drop traffic from IPs in that set:

sudo iptables -A INPUT -m set --match-set bad_ips src -j DROP

This is much more performant than having hundreds of individual iptables rules for each IP.

The order of your iptables rules is critical. Rules are evaluated from top to bottom. The first rule that matches a packet determines its fate. Therefore, blocking rules should generally appear before rules that permit traffic.

To view your current iptables rules, use:

sudo iptables -L -v -n

The -L lists rules, -v provides verbose output (including packet/byte counts), and -n shows IP addresses and port numbers numerically, which is faster and clearer.

To delete a specific rule, you can use -D followed by the chain and the rule specification, or use -D with the rule number displayed by iptables -L --line-numbers. For example, to delete the first rule in the INPUT chain:

sudo iptables -D INPUT 1

If you want to block a range of ports, you can use the multiport module. To block TCP traffic to ports 80 and 443 from a specific subnet 192.168.1.0/24:

sudo iptables -A INPUT -s 192.168.1.0/24 -p tcp -m multiport --dports 80,443 -j DROP

When you make changes with iptables, they are often temporary and will be lost on reboot. To make them persistent, you need to save them. The method varies by distribution. On Debian/Ubuntu, you can use iptables-persistent:

sudo apt-get install iptables-persistent
sudo netfilter-persistent save

On RHEL/CentOS/Fedora, you might use iptables-services:

sudo yum install iptables-services
sudo systemctl enable iptables
sudo service iptables save

If you block a critical IP range or port without a preceding allow rule for essential services (like SSH), you can lock yourself out of your server. Always have a way to access the server (e.g., out-of-band console, or a temporary allow rule for your current IP) before applying restrictive blocking rules.

The next challenge you’ll encounter is managing the state of connections, which leads to the conntrack module and stateful firewalling.

Want structured learning?

Take the full Iptables course →