on 17-Mar-2015 17:22
Problem this snippet solves:
An alternative HTTP throttle iRule
2 modes of operations: strict and window (burst) mode
Code :
# Configuration steps * configure stats profile * create iRule * configure http virtual and apply http profile 1) Stats profile Create stats profile containing policy name follow by request and blocked as follow profile stats http_throttle { defaults from stats field1 total request field2 total blocked field3 default_policy request field4 default_policy blocked field5 request_post request field6 request_post blocked } 2) Create iRule Copy the following iRule to bigip.conf or configuration GUI when RULE_INIT { # set default policy or set to "" if no default policy is required # set static::default_policy "" set static::default_policy "default_policy" # Customize per-policy parameter here # mode of operation # 0 - strict mode: rate is strictly enforced per time slot # 1 - window mode: rate is enforced per time window array set static::Mode { "default_policy" 1 "request_post" 1 } # Request per time slot array set static::Rs { "default_policy" 10 "request_post" 1 } # Time slot in millisecond array set static::Ts { "default_policy" 10 "request_post" 3000 } # Number of slot array set static::Slot { "default_policy" 100 "request_post" 5 } # Tw - Time window = Time slot x Slot foreach p [array name static::Mode] { if {![info exists static::Ts($p)]}{ set static::Ts($p) $static::Ts("default_policy") } if {![info exists static::Slot($p)]}{ set static::Slot($p) $static::Slot("default_policy") } set static::Tw($p) [expr $static::Ts($p) * $static::Slot($p)] } # Rw - Request per time window foreach p [array name static::Mode] { if { ![info exists static::Rs($p)] } { set static::Rs($p) $static::Rs("default_policy") } set static::Rw($p) [expr $static::Rs($p) * $static::Slot($p)] } # clean up after 1000 -periodic { foreach p [array name static::Mode] { foreach k [array name ::rate_${p}_key] { set delete 1 foreach t [array name ::rate_${p}_${k}] { set delete 0 set ctime [clock clicks -milliseconds] if { $ctime < 0 } { set ctime [expr 4294967296 + $ctime] } set ltime [expr ($ctime / $static::Ts(${p})) - $static::Slot(${p})] if { $t < $ltime } { unset rate_${p}_${k}(${t}) } } foreach t [array name ::sum_${p}_${k}] { set delete 0 set ctime [clock clicks -milliseconds] if { $ctime < 0 } { set ctime [expr 4294967296 + $ctime] } set ltime [expr ($ctime / $static::Ts(${p})) - $static::Slot(${p})] if { $t < $ltime } { unset sum_${p}_${k}(${t}) } } if { $delete } { unset ::rate_${p}_key($k) } } } } } when HTTP_REQUEST { # Policy assignment # - default policy set p $static::default_policy # - custom routine to assign policy # (customize policy assignment here) if { [HTTP::method] eq "POST" } { set p "request_post" } STATS::incr http_throttle "total request" # Policy enforcement if { $p eq "" } { # request does not match any policy # and there is no default policy configured return } STATS::incr http_throttle "$p request" set block 0 # current time (millisecond) set ctime [clock clicks -milliseconds] if { $ctime < 0 } { set ctime [expr 4294967296 + $ctime] } set ctime [expr $ctime / $static::Ts($p)] # Customize unique key here set k [IP::client_addr] if { ![info exists ::rate_${p}_key(${k})] } { set ::rate_${p}_key(${k}) 1 } set static::Mode($p) 1 if { $static::Mode($p) == 0 } { # strict mode if { ![info exist ::rate_${p}_${k}(${ctime}) ] } { set ::rate_${p}_${k}(${ctime}) 1 } elseif { [subst \$\{::rate_${p}_${k}(${ctime})\}] < $static::Rs(${p}) } { # in strict mode, don't allow rate in each time slot to go beyond Rs incr ::rate_${p}_${k}(${ctime}) } else { set block 1 } } else { # window mode if { ![info exist ::sum_${p}_${k}(${ctime}) ] } { # calculate sum of requests for last ($static::Slot - 1) slots set ptime [expr $ctime - 1] set ltime [expr $ctime - $static::Slot($p)] if { [info exist ::sum_${p}_${k}(${ptime}) ] } { set ::sum_${p}_${k}(${ctime}) [subst \$\{::sum_${p}_${k}(${ptime})\}] if { [info exist ::rate_${p}_${k}(${ltime}) ] } { incr ::sum_${p}_${k}(${ctime}) -[subst \$\{::rate_${p}_${k}(${ltime})\}] } } else { set ::sum_${p}_${k}(${ctime}) 0 for { set xtime [expr $ctime - 1] } { $xtime > $ltime } { incr xtime -1 } { if { [info exist ::rate_${p}_${k}(${xtime}) ] } { incr ::sum_${p}_${k}(${ctime}) [subst \$\{::rate_${p}_${k}(${xtime})\}] } } } # in window mode, allow rate to go beyond Rs but not Rw if { [subst \$\{::sum_${p}_${k}(${ctime})\}] < $static::Rw(${p}) } { # update statistics set ::rate_${p}_${k}(${ctime}) 1 incr ::sum_${p}_${k}(${ctime}) } else { set block 1 } } else { # sum of requests exists if { [subst \$\{::sum_${p}_${k}(${ctime})\}] < $static::Rw($p) } { incr ::rate_${p}_${k}(${ctime}) incr ::sum_${p}_${k}(${ctime}) } else { set block 1 } } } # Customize throttle action here # default is responding with 500 status code (no content) if { $block } { HTTP::respond 500 STATS::incr http_throttle "$p blocked" STATS::incr http_throttle "total blocked" } } 3) HTTP virtual server Configure http virtual and apply http profile to it. This version of iRule does not support CMP so CMP should be disabled. virtual http { cmp disable pool http destination 10.10.71.200:http ip protocol tcp rules http_throttle_test profiles { http {} http_throttle {} tcp {} } } # Modifying Parameters # 1) Default Policy set static::default_policy variable to desired value or set to "" if no default policy is required. For example: set static::default_policy "default_policy" # 2) Set mode of operation (0=strict mode, 1=window mode) Add policy name follow by selected mode of operation to array static::Mode. For example: array set static::Mode { "default_policy" 1 "request_post" 1 } # 3) Customize routine to assign policy - The following example use switch statement to select policy based on HTTP::method and HTTP::uri. Set variable p to selected policy name. if { [HTTP::method] eq "POST" } { set p "request_post" } # 4) Set request limit Strict mode - In Strict mode, 2 parameters are needed to be configured: Ts and Rs. - Ts (or Time Slot) is a period of time (in millisecond). - Rs is a number of requests allowed per Time Slot interval (Ts). - Ts and Rs for each policy have to be put together in array. For example, # Request per time slot array set static::Rs { "default_policy" 10 "request_post" 1 } # Time slot in millisecond array set static::Ts { "default_policy" 10 "request_post" 3000 } - In this example, the “request_post” policy allows 1 request per every 3000 milliseconds. Window mode - In Window mode, 3 parameters are needed to be configured: Ts, Rs and Slot. - Ts (or Time Slot) is a period of time (in millisecond). - Rs is a number of requests allowed per Time Slot interval (Ts). - In window mode, number of requests allowed is not enforced per Time slot but per Sliding Window - Slot is a number of Time Slot (Ts) to be combined as a Sliding Window. Maximum requests per Sliding Window equal to Slot x Rs. - Rs, Ts and Slot should be configured per policy in an array. Here is an example: # Request per time slot array set static::Rs { "default_policy" 10 "request_post" 1 } # Time slot in millisecond array set static::Ts { "default_policy" 10 "request_post" 3000 } # Number of slot array set static::Slot { "default_policy" 100 "request_post" 5 } - For the request_post policy -- Rs=1, Ts=3,000 and Slot=5. -- Sliding window = Slot x Ts = 5 x 3,000 = 15,000 milliseconds -- Maximum requests allowed per sliding window = Slot x Rs = 5 x 1 = 5 requests -- Request statistics will be updated every 3000 milliseconds.