Forum Discussion
F5 irule - Failing to include the key from subtable in the generated log message
I disagree just slightly with @Stanislas here. While your conditional structure is perhaps more unwieldy than the proposed alternative, it isn't functionally incorrect to do it your way, nor is computationally more (or less) expensive.
For posterity, I'm re-including your iRule, formatted for improved readability:
when RULE_INIT {
set static::maxRate 5
set static::timeout 60
}
when HTTP_REQUEST {
set key [HTTP::header "MSISDN"]
set getCount [table lookup -notouch -subtable requests $key]
if { $getCount equals "" } {
log local0. "New one: getCount=$getCount $key [clock seconds]"
table set -subtable requests $key "1" $static::timeout $static::timeout
} else {
if { $getCount < $static::maxRate } {
table incr -notouch -subtable requests $key]
} else {
if { $getCount == $static::maxRate } {
log local0. "User @ $key [clock seconds] has reached $getCount requests in $static::timeout seconds." table incr -notouch -subtable requests $key
}
}
}
}
Far more importantly, and just as @Stanislas says, it's most likely that the MSISDN header is simply missing. You will, in that case, still write a table entry. The key will be the empty string. Here is an alternative (not tested!):
when RULE_INIT {
set static::maxRate 5
set static::timeout 60
}
when HTTP_REQUEST {
if { [HTTP::header exists MSISDN] } {
set key "count-msisdn-[HTTP::header MSISDN]"
set count [table incr $key]
if { $count == 1 } {
do this to set the timeout and lifetime, overriding 'table incr' defaults
table set $key 1 $static::timeout $static::timeout
} elseif { $count == $static::maxRate } {
log local0. "User @ $key has reached $count requests in $static::timeout seconds."
reject
} elseif { $count > $static::maxRate } {
reject
}
}
}
I'm just assuming you wish to reject when the threshold is met. Naturally, you can do whatever you wish. Notice I perform a table incr then see if it is 1 (which it will be if it didn't previously exist). In your code, you perform two table commands for each request message. My code executes two table commands only for the first request with a given MSISDN in a single interval period. For subsequent requests with that same MSISDN in the same period, only a single table command is executed. table commands are somewhat expensive, so reducing the count is useful. Notice, further, that I dropped the -notouch. Since your lifetime is the same as your timeout, the -notouch (which only relates to the timeout) shouldn't be needed. Finally, I moved this from a sub-table to the main table. In general, sub-tables should only be used if you need to enumerate the keys.
I cut the clock seconds because clock operations are also fairly expensive, and the syslog entry will be timestamped automatically. For production, I'd recommend using High-Speed Logging rather than local logging.
Finally, i have this working. It might not be the optimal code in terms of resources utilization and structure but it is giving the desired results. Initially, i was searching for the "MSISDN" in the header fields but after analyzing the request, i realized that the "MSISDN" is in the payload and it is identified in various ways. Below is the working code,
Note: The code is not meant to reject requests after a certain number but to generate logs which are then sent to security team to do further analysis
when RULE_INIT {
This is the max requests allowed during "interval" specified below.
set static::maxRate 5; set static::timeout 60; }
when HTTP_REQUEST {
HTTP::collect [HTTP::header Content-Length] }
when HTTP_REQUEST_DATA { set first_search [findstr [HTTP::payload] "" 9 "/"] if {$first_search ne "" } { set MSISDN $first_search } else { set second_search [findstr [HTTP::payload] "" 12 "/"] if {$second_search ne "" } { set MSISDN $second_search } else { set MSISDN [findstr [HTTP::payload] "" 16 "/"] } } set getCount [table lookup -notouch -subtable requests $MSISDN] if { $getCount equals "" } { log local0. "New one: $MSISDN [clock seconds]" table set -subtable requests $MSISDN "1" $static::timeout $static::timeout } else { if { $getCount < $static::maxRate } { table incr -notouch -subtable requests $MSISDN } else { if {$getCount == $static::maxRate } { log local0. "User @ MSISDN $MSISDN has reached $getCount requests in $static::timeout seconds." table incr -notouch -subtable requests $MSISDN }
} } }
Help guide the future of your DevCentral Community!
What tools do you use to collaborate? (1min - anonymous)Recent Discussions
Related Content
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com