Understanding iptables Rate Limiting: Deep Dive into –limit and –limit-burst for ICMP Echo Requests


3 views

When working with iptables firewall rules, the limit module provides crucial traffic shaping capabilities. Let's dissect the two example rules from CentOS 5.x systems:

-A RH-Firewall-1-INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT
-A RH-Firewall-1-INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 3 -j ACCEPT

The --limit 1/s parameter specifies that the rule will match at an average rate of 1 packet per second. This is implemented using a token bucket algorithm where tokens are added to the bucket at the specified rate.

The --limit-burst parameter (defaulting to 5 when unspecified) determines:

  • Initial capacity of the token bucket
  • Maximum burst capacity before rate limiting engages
  • Number of packets that can arrive in quick succession

For the first rule (without explicit burst):

Time  Action        Tokens
0s    Packet 1      4 (5-1)
0.1s  Packet 2      3
0.2s  Packet 3      2
0.3s  Packet 4      1
0.4s  Packet 5      0
0.5s  Packet 6      Rejected (bucket empty)
1.0s  Token added   1 (rate replenishment)

For the second rule (burst=3):

Time  Action        Tokens
0s    Packet 1      2 (3-1)
0.1s  Packet 2      1
0.2s  Packet 3      0
0.3s  Packet 4      Rejected
1.0s  Token added   1

These rules apply globally to all ICMP echo requests, not per-host. For per-host limiting, you'd need additional matches:

-A RH-Firewall-1-INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 2 -m hashlimit --hashlimit-mode srcip --hashlimit-above 1/s -j DROP

Combining with logging for debugging:

-A RH-Firewall-1-INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 3 -j LOG --log-prefix "ICMP_ACCEPT: "
-A RH-Firewall-1-INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 3 -j ACCEPT
-A RH-Firewall-1-INPUT -p icmp --icmp-type echo-request -j LOG --log-prefix "ICMP_DROP: "
-A RH-Firewall-1-INPUT -p icmp --icmp-type echo-request -j DROP

For SSH rate limiting (more practical example):

-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 --rttl --name SSH -j DROP

The limit module adds minimal overhead as it uses kernel-level counters. However, for high-traffic systems, consider:

  • Using hashlimit for per-IP scaling
  • Combining with connlimit for connection-based throttling
  • Testing rules with -j LOG before production deployment

The Linux kernel's netfilter framework (accessed via iptables) includes a powerful limit match module that implements token bucket filtering. Here's the technical breakdown:

-m limit --limit [rate] --limit-burst [number]

The token bucket algorithm works by:

  • Filling a "bucket" with tokens at the specified rate (--limit)
  • Each packet consumes one token
  • When tokens are exhausted, packets stop matching

Your first example:

-A RH-Firewall-1-INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT

This means:

  • Allow 1 ICMP echo request per second on average
  • Uses default burst of 5 packets (when unspecified)
  • Tokens replenish at 1 per second
  • The second example with explicit burst:

    -A RH-Firewall-1-INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 3 -j ACCEPT
    

    Key differences:

    • Initial burst capacity of 3 packets
    • After burst, limits to 1 packet per second
    • Burst bucket refills at 1 token per second

    These limits apply globally to all matching traffic, not per-source IP. For per-IP limiting, combine with the hashlimit module:

    -A RH-Firewall-1-INPUT -p icmp --icmp-type echo-request \
      -m hashlimit --hashlimit-name ICMP \
      --hashlimit-mode srcip --hashlimit 1/s \
      --hashlimit-burst 3 -j ACCEPT
    

    Verify your rules with these commands:

    # Watch rule hits in real-time:
    watch -n1 'iptables -L RH-Firewall-1-INPUT -v --line-numbers'
    
    # Generate test traffic (from another host):
    ping -f [target_ip]  # Flood ping
    ping -i 0.5 [target_ip]  # 2 packets/sec
    

    Expected behavior with --limit 1/s --limit-burst 3:

    • First 3 pings succeed immediately
    • Subsequent pings limited to 1 per second
    • After 3 seconds of no traffic, burst refills to 3

    For more precise control, combine with logging:

    -A RH-Firewall-1-INPUT -p icmp --icmp-type echo-request \
      -m limit --limit 1/s --limit-burst 3 -j ACCEPT
    -A RH-Firewall-1-INPUT -p icmp --icmp-type echo-request \
      -j LOG --log-prefix "ICMP Flood: "
    -A RH-Firewall-1-INPUT -p icmp --icmp-type echo-request -j DROP
    

    This configuration will:

    1. Allow bursts of up to 3 ICMP requests
    2. Then permit 1 request per second
    3. Log and drop any excess requests

    Remember that these rules process in order, so place your rate-limited ACCEPT rule before your DROP rules.