virtual server connection rate limit with tables

Problem this snippet solves:

Summary: Limit the rate of connections to a virtual server to prevent overloading of pool members

iRule Methodology:

For a given time interval, ensure that the rate of connections doesn't exceed a configurable threshold. Uses the table command to track number of new connections per second.

Code :

#
# Name: virtual server connection rate limiting rule
#
# Purpose: Limit the rate of connections to a virtual server to prevent overloading of pool members
#
# Methodology: For a given time interval, ensure the rate of connections doesn't exceed a configurable threshold
#
# Requirements: 
#   - LTM v10.1 or higher to use the table command
#   - IP address datagroup containing a whitelist of IPs/subnets that should not be limited/counted (vsratelimit_whitelist_class)
#   - Addition of this iRule to the virtual server.
#
# Warning: Due to a bug (fixed in 10.2.2), TMM can crash if this iRule is reconfigured or removed 
#   from the virtual server while traffic is pending.  For more info see: 
#   Changing an iRule assignment on a virtual server may cause TMM to panic. 
#   http://support.f5.com/kb/en-us/solutions/public/11000/100/sol11149.html
#
when RULE_INIT {

   # Log debug to /var/log/ltm? 1=yes, 0=no.
   set static::conn_debug 1

   # Maximum connection rate (new connections / time interval)
   # To avoid the bug described in SOL11149, you could move this configuration to a datagroup
   set static::conn_rate 10

   # Time interval (in seconds)
   # This shouldn't need to be changed.
   # We track connections opened in the last X seconds
   set static::interval 1

   log local0. "Configured to enforce a rate of [expr {$static::conn_rate / $static::interval}]\
      cps ($static::conn_rate connections / $static::interval second)"

   # IP datagroup name which contains IP addresses/subnets to not track/rate limit
   set static::whitelist_class vsratelimit_whitelist_class

   # Generate a subtable name unique to this iRule and virtual server
   #   We use the virtual server name with this prefix (vsratelimit_)
   set static::tbl "vsratelimit"
}
when CLIENT_ACCEPTED {

   # Don't process any of this iRule if the client IP is in the whitelist datagroup
   if {[class match [IP::client_addr] equals vsratelimit_whitelist_class]}{

      # Exit this event from this iRule
      return
   }

   # Track this connection in the subtable using the client IP:port as a key
   set key "[IP::client_addr]:[TCP::client_port]"

   # Save the virtual server-specific subtable name (vsratelimit_)
   set tbl ${static::tbl}_[virtual name]

   # Check if we're under the connection limit for the virtual server
   set current [table keys -subtable $tbl -count]
   if { $current >= $static::conn_rate } {

      # We're over the rate limit, so reset the connection 
      if { $static::conn_debug }{ log local0. "$key: Connection to [IP::local_addr]:[TCP::local_port]\
         ([virtual name]). At limit, rejecting (current: $current / max: $static::conn_rate)" }
      #reject
      # apachebench stops when it gets a reset, so you can test with TCP::close to send a FIN and close the connection cleanly.
      TCP::close

   } else {

      # We're under the virtual server connection rate limit, 
      # so add an entry for this client IP:port with a lifetime of X seconds
      table set -subtable $tbl $key " " indefinite $static::interval
      if { $static::conn_debug }{ log local0. "$key: Connection to [IP::local_addr]:[TCP::local_port]\
         ([virtual name]). Under limit, allowing (current: [table keys -subtable $tbl -count] / max: $static::conn_rate)" }
   }
}
Published 11 years ago
Version 1.0

4 Comments

No CommentsBe the first to comment