The hashlimit module in iptables lets you rate-limit connections based on a hash of IP addresses, making it a powerful tool for preventing DoS attacks and managing resource usage.

Let’s see it in action. Imagine you want to limit incoming SSH connections to 5 per minute per IP address. Here’s how you’d set that up:

sudo iptables -A INPUT -p tcp --syn --dport 22 -m hashlimit --hashlimit-name ssh_limit --hashlimit-above 5/min --hashlimit-mode srcip --hashlimit-htable-expire 300000 -j ACCEPT
sudo iptables -A INPUT -p tcp --syn --dport 22 -j DROP

This might look a bit dense, so let’s break down what’s happening.

First, we’re adding a rule to the INPUT chain. The -p tcp specifies the TCP protocol, and --syn matches only new connection attempts (the SYN flag). --dport 22 targets the SSH port.

The core of this rule is the -m hashlimit module.

  • --hashlimit-name ssh_limit: This assigns a unique name to our hash table. This name is used internally by iptables to track the limits.
  • --hashlimit-above 5/min: This is the actual rate limit. It means "if the rate exceeds 5 per minute…"
  • --hashlimit-mode srcip: This is crucial. It tells hashlimit to hash based on the source IP address (srcip) of incoming packets. So, each IP address gets its own independent limit of 5 connections per minute. Other modes exist, like dstip, srcport, dstport, or combinations, but srcip is common for connection limiting.
  • --hashlimit-htable-expire 300000: This sets the expiration time for entries in the hash table in milliseconds. Here, 300000ms is 5 minutes. After an IP address stops making new connections for 5 minutes, its entry is removed from the hash table, allowing it to start counting again from zero.

If the conditions of the first rule are met (i.e., the source IP has not exceeded 5 new SSH connections in the last minute), the packet is ACCEPTed.

The second rule, sudo iptables -A INPUT -p tcp --syn --dport 22 -j DROP, acts as a catch-all. Any new SSH connection attempt that didn’t match the first rule (meaning it exceeded the limit) will fall through to this rule and be DROPped.

The problem this solves is simple: a single IP address or a small group of IP addresses could overwhelm your SSH server by initiating thousands of connections simultaneously, leading to resource exhaustion and denial of service. By using hashlimit with srcip, you ensure that no single client can monopolize your server’s connection resources.

The hashlimit module works by maintaining an in-memory hash table. When a packet arrives, iptables calculates a hash based on the specified hashlimit-mode (e.g., source IP). This hash value is used to look up an entry in the hash table. Each entry stores a timestamp and a counter for the number of matching packets seen within the current time window. If the counter exceeds the specified limit, the rule action (in our case, DROP) is applied. The hashlimit-htable-expire parameter dictates how long an entry remains in the table after the last matching packet.

What most people don’t realize is that the hashlimit module is stateful per hash entry. When you set --hashlimit-above 5/min, it’s not just counting packets that arrive exactly within a one-minute window. Instead, it’s maintaining a sliding window. If an IP makes 5 connections at 10:00:00, then another at 10:00:30, and another at 10:01:00, the connection at 10:01:00 might still be allowed if the first connection from 10:00:00 has fallen out of the sliding window. The exact windowing mechanism is a bit more complex than a simple fixed interval, which can sometimes lead to slightly more connections being allowed than a strict "exactly X per Y time" interpretation might suggest, but it’s generally very effective for practical rate limiting.

After successfully rate-limiting your SSH connections, you might find yourself needing to do the same for other services, like HTTP or HTTPS, or perhaps you’ll want to implement more granular limits for specific user agents or URLs.

Want structured learning?

Take the full Iptables course →