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


12 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.