nftables’s GeoIP blocking isn’t about looking up country codes in real-time for every single packet; it’s about pre-populating your firewall rules with vast IP address ranges derived from GeoIP databases.

Let’s see nftables in action blocking traffic from Russia. First, you need a GeoIP database. MaxMind’s GeoLite2 Country database is a common choice. You’ll need to download it and process it into a format nftables can use, like an ipset.

Here’s a simplified nftables rule set that leverages ipset for blocking:

#!/usr/sbin/nft -f

flush ruleset

table ip filter {
    chain input {
        type filter hook input priority 0; policy accept;

        # Block traffic from Russia
        set russian_ips {
            type ipv4_addr
            flags interval
            auto-load
        }
        ip saddr @russian_ips reject

        # Allow established connections
        ct state established,related accept

        # Drop anything else
        drop
    }
}

The magic happens in the set russian_ips definition. The flags interval is key here; it tells nftables that this set will contain IP address ranges, not just individual IPs. The auto-load directive means nftables will automatically watch for updates to the file specified (more on that later).

Now, how do you populate that ipset? You’ll typically use a script that downloads the GeoIP database, extracts the IP ranges for your target country (e.g., Russia), and then converts them into ipset commands. A common tool for this is geoiplookup or custom scripts using Python libraries like geoip2.

Let’s assume you have a file named russia.zone containing IP ranges in CIDR notation, one per line:

1.0.0.0/24
1.1.1.0/24
...
91.100.0.0/16
91.101.0.0/16
...
217.64.0.0/14
...

You’d then use nft to load these into your set:

# Create the set initially (or if auto-load isn't configured correctly)
nft add set ip filter russian_ips '{ type ipv4_addr; flags interval; }'

# Load the ranges from the file
cat russia.zone | while read ip; do
    nft add element ip filter russian_ips '{ '"$ip"' }'
done

The flags interval allows nftables to efficiently match these CIDR blocks. Instead of having to check against every single IP in the database, nftables can use its internal interval tree to quickly determine if a source IP falls within any of the blocked ranges.

The auto-load option in the nftables rule set is designed to simplify updates. If you configure nftables to monitor a file (e.g., /etc/nftables/geoip/russia.zone), and you update that file with new IP ranges, nftables can be signaled to reload the set without restarting the firewall. This is often managed by systemd services or custom scripts that trigger nft monitor or nft reload.

The real power comes from using nftables’s interval flag on sets, which is specifically designed for efficient range lookups. When you add IP ranges (like 91.100.0.0/16) to an interval-flagged set, nftables doesn’t just store them as discrete entries. It builds an internal data structure, typically a tree, that allows it to quickly determine if an incoming IP address falls within any of the stored intervals. This is significantly more performant than trying to manage individual IP addresses for entire countries.

The next challenge is keeping these GeoIP databases and consequently your ipset up-to-date, as IP address assignments change.

Want structured learning?

Take the full Nftables course →