Limit Connections From Client

Problem this snippet solves:

Limit the number of TCP connections to a virtual server from client IP addresses.

This example should only be used on v9. For v10 or higher, use the session table to track the client connections. See the first example in the table wiki page for one way to do this.

This iRule illustrates how to use an internal array to track the number of concurrent connections to a virtual server based on the source IP address, and reject any connection attempts above a configurable limit.

The example adds logging for rejected connections including client IP address and connection counts, and supports a whitelist of IP addresses to exclude from the limit check.

Care should be taken when using this iRule, as clients may access the virtual server through a reverse proxy which performs address translation. In such a case, there might be legitimate reasons for a large number of TCP connections from a single source IP address.

Code :

when RULE_INIT {

   # The maximum number of TCP connections to the virtual server from a single client IP address
   set ::max_connections_per_ip 10

   # Clear the array of clients with open connections to the VIP
   array set ::active_clients { }

   # Replace this array with a datagroup of type 'address' once done testing!
   array set white_client {
     10.41.0.610
     10.0.0.2
   }
}

when CLIENT_ACCEPTED {

   log local0. "\$:

   # Check if the client is already in the active clients array
   if { ([info exists ::active_clients([IP::client_addr])]) } {

      # Regardless of whether we reject this client, we've already accepted the TCP connection.
      # so increment the counter for this client.  The count will be decremented when the connection is closed.
      incr ::active_clients([IP::client_addr])
      log local0. "Incremented \$::active_clients([IP::client_addr]) to: $::active_clients([IP::client_addr])"

      # Check if client is already over the maximum
      if {$::active_clients([IP::client_addr]) > $::max_connections_per_ip} {

         # Send TCP reset to client
         reject

         log local0. "Rejected IP [IP::client_addr], count ($::active_clients([IP::client_addr]))"

      }
      # Don't need an else clause here.  The default action will be to allow the connection to continue.

   } elseif { ![info exists ::white_client([IP::client_addr])] }{

      # Client wasn't already in the array and isn't in the white list, so add them to the array with a count of 1.
      set ::active_clients([IP::client_addr]) 1
      log local0. "Initialised \$::active_clients([IP::client_addr]) to: 1"
   }
}
when CLIENT_CLOSED {

   # Check if the client has a count in the array
   if { [info exists ::active_clients([IP::client_addr])]} {

      # Decrement the count by 1
      incr ::active_clients([IP::client_addr]) -1

      # Check if the count is 0 or negative
      if { $::active_clients([IP::client_addr]) <= 0 } {

         # Clear the array element
         unset ::active_clients([IP::client_addr])
      }
   }
}
Published Mar 18, 2015
Version 1.0

Was this article helpful?

No CommentsBe the first to comment