using the kernel routing table instead of iptables for effective IP blacklist management

iptables is great, but not if you want to ban thousands of IPs from your box.  By design, firewalls aren’t really meant for that sort of thing, as they typically do ruleset iteration on each traffic flow (such as a sctp or tcp connection).

Instead, to do this efficiently, we use the kernel routing table.  The reason why we use the kernel routing table is because, typically, it is a radix trie which uses IP masks as keys.  This is a lot faster than iterating through thousands of iptables rules and has the same effect – since an outbound route is blocked, replies can’t be received by the hostile IP, which means that connections can’t be set up, as they require a three-way handshake.

Linux defines RTN_BLACKHOLE as the flag used to define nullroutes.  This flag is supported by the IPv4, IPv6 and DECnet routing tables.

To add an outbound nullroute, simply do the same thing as you would an inbound nullroute on your firewalling box:

# ip route add blackhole 192.168.1.1/32

Deleting works like this:

# ip route del blackhole 192.168.1.1/32

If you use FreeBSD or NetBSD or MacOS, it’s something like that. On FreeBSD, it’s:

# route add -host 192.168.1.1 127.0.0.1 -blackhole
# route del -host 192.168.1.1 127.0.0.1 -blackhole

I don’t know what it is on NetBSD, but I would guess that it’s the same.

The cool thing about this is that if you can aggregate your banlists to network boundaries, you can actually set them as nullroutes in CIDR format and it makes things even faster.  Mind, ruleset aggregation also improves netfilter’s performance, but this is considerably more scalable than using netfilter.  Netfilter (iptables) should be used when you actually need to do filtering, the routing table should be used when you want to ban IPs outright – that’s why the networking stack has blackhole functionality.