Skip to content

Instantly share code, notes, and snippets.

@lrhazi
Created March 19, 2014 01:00
Show Gist options
  • Save lrhazi/9633485 to your computer and use it in GitHub Desktop.
Save lrhazi/9633485 to your computer and use it in GitHub Desktop.
F5 GTM iRule: An attempt at RRL Response Rate Limiting
when RULE_INIT {
# Max queries per second, per client ip
set static::maxconn 100
# Max identical responses per second, per client ip
set static::maxresp 20
# Number of seconds to drop traffic from infringing client ip.
set static::holdtime 15
# Maximum entries in dns_drop session table.
set static::max_drop_entries 1000000
# Maximum per client, per query, table size.
set static::max_query_entries 1000000
}
when CLIENT_DATA {
set client_ip [IP::remote_addr]
set vip [IP::local_addr]
set hsl [HSL::open -proto UDP -pool splunk_pool]
if { [table lookup -subtable "dns_drop" $client_ip] != "" } {
drop
#log local0.debug "Dropped client: $client_ip"
return
}
}
when DNS_RESPONSE {
if { [table keys -subtable "dns_drop" -count] >= $static::max_drop_entries } {
HSL::send $hsl "<190>GTM|RRL disabled! Reached max_drop_entries: $static::max_drop_entries\n"
#log local0.debug "RRL disabled! Reached max_drop_entries: $static::max_drop_entries"
return
}
if { [table keys -subtable "dns_queries" -count] >= $static::max_query_entries } {
HSL::send $hsl "<190>GTM|RRL disabled! Reached max_query_entries: $static::max_query_entries\n"
#log local0.debug "RRL disabled! Reached max_query_entries: $static::max_query_entries"
return
}
set curtime [clock second]
set ckey [b64encode [md5 "c:$client_ip:$curtime"]]
set ccount [table incr -subtable "dns_queries" $ckey]
table lifetime -subtable "dns_queries" $ckey 2
if { $ccount > $static::maxconn } {
table add -subtable "dns_drop" $client_ip "c" indef $static::holdtime
table delete $ckey
HSL::send $hsl "<190>GTM|RRL Excessive queries max=$static::maxconn v=$vip c=$client_ip\n"
#log local0.debug "Excessive queries max=$static::maxconn v=$vip c=$client_ip"
drop
return
}
set answer ""
set rrs [DNS::answer]
for {set i 1} {$i<=[llength $rrs]} {incr i} {
set rr [lindex $rrs [expr {$i-1}]]
append answer [DNS::name $rr] " "
append answer [DNS::ttl $rr] " "
append answer [DNS::class $rr] " "
append answer [DNS::type $rr] " "
set rdata [DNS::rdata $rr]
append answer $rdata " "
}
set rcode [DNS::header rcode]
if { $rcode != "NOERROR" } {
set rkey [b64encode [md5 "a:$client_ip:$curtime:$rcode"]]
} else {
set rkey [b64encode [md5 "a:$client_ip:$curtime:$answer"]]
}
set rcount [table incr -subtable "dns_queries" $rkey]
table lifetime -subtable "dns_queries" $rkey 2
if { $rcount > $static::maxresp } {
table add -subtable "dns_drop" $client_ip "a" indef $static::holdtime
table delete $rkey
HSL::send $hsl "<190>GTM|RRL Excessive identical responses max=$static::maxresp v=$vip c=$client_ip n=[DNS::question name] t=[DNS::question type] r=$rcode\n"
#log local0.debug "Excessive identical responses max=$static::maxresp v=$vip c=$client_ip n=[DNS::question name] t=[DNS::question type] r=$rcode"
drop
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment