How to limit DHCP Discover

kirirokiriro Member
Posted by kiriro

Hi

I'd like to limit frequent dhcp discover from a specific
client PC's mac in thermal runaway.
This dhcp client send huge DHCP discover.

I tried to apply "DHCP discover packet" to following rule,
It's famous irule sample used as traditional ddos protection,
and I changed $srcip to $mac, for this reason, I want to catch
"dchp client mac" inside dhcp relay agent(Source-ip is always
same because of same dhcp relay agent).

Then, array as global variable cannot work.

I can catch client mac on dchp discover packet
by using this aflex command.

binary scan [UDP::payload 28 6] H12H mac dammy

Could you please advice to this rule?


DDos protection irule
===========================================
when RULE_INIT {
set ::maxquery 100
set ::holdtime 600
set ::maxarraysize 400
array set ::usertable { }
array set ::blacklist { }
}

when CLIENT_ACCEPTED {
set srcip [IP::remote_addr]
set currtime [clock seconds]
if { [info exists ::blacklist($srcip)] } {
if { $::holdtime > [expr {$currtime - $::blacklist($srcip)}] } {
#set ::blacklist($srcip) $currtime
#log local0. "BL now [array get ::blacklist]"
drop
#log local0. "Drop $srcip"
return
} else {
unset ::blacklist($srcip)
#log local0. "remove $srcip from blacklist"
}
}
if { [info exists ::usertable(time,$srcip)] and $currtime == $::usertable(time,$srcip) } {
incr ::usertable(freq,$srcip)
if { $::usertable(freq,$srcip) > $::maxquery } {
#log local0. "New blacklist member <$srcip> with $::usertable(freq,$srcip) times"
set ::blacklist($srcip) $currtime
unset ::usertable(freq,$srcip)
unset ::usertable(time,$srcip)
drop
return
}
} else {
set ::usertable(freq,$srcip) 1
set ::usertable(time,$srcip) $currtime
#log local0. "New member <$srcip><$currtime>"
}
}

when CLIENT_CLOSED {
if { [array size ::usertable] > $::maxarraysize } {
set usertablelist [array get ::usertable]
foreach { x y } $usertablelist {
if { $x contains "time," and $currtime ne $y } {
set recip [string trimleft $x "time,"]
unset ::usertable(time,$recip)
unset ::usertable(freq,$recip)
}
}
}
#log local0. "Usertable is now [array get ::usertable]"
}
=============================================

Best Regards,
Kiriro


--
A10 NETWORKS CONFIDENTIAL: DO NOT DISTRIBUTE INTERNALLY OR EXTERNALLY

Comments

  • edited February 2014
    Posted by ddesmidt

    I understand your issue is to get the "array" working and not to get the client mac on dchp discover packet.

    So here is an aFleX (using arrays) that drops the clients making more than 100 connections in 1 second.
    Note: For your test, you'll simply need to change the test currently done on "client_ip@" with the client mac on dchp discover packet.

    There are a lot of comments and "logs" to help you understand the aFleX.
    Note: Of course remove the "logs" in production.
    Come back to me if you have any question.

    Code:

    when RULE_INIT { set ::maxquery 100 set ::holdtime 600 set ::maxarraysize 400 # the user_freq table contains "client_ip" + "how many req in the last second" array set ::user_freq { } # the user_time table contains "client_ip" + "when the req has been done" array set ::user_time { } # the blacklist table contains "client_ip" + "time when client entered in the blacklist" array set ::blacklist { } } when CLIENT_ACCEPTED { set srcip [IP::remote_addr] set currtime [clock seconds] # Clear up user_time + user_freq table older than 1 second when table is bigger than ::maxarraysize if { [array size ::user_time] > $::maxarraysize } { log "Clear up user_time + user_freq table" foreach {client time} [array get ::user_time] { if {$time < $currtime} { unset ::user_time($client) unset ::user_freq($client) log "Client $client removed from the user_time + user_freq" } } } # Check if the client_ip is in the blacklist if { [info exists ::blacklist($srcip)] } { # Check if the client_ip has been in the blacklist for less than $::holdtime if { [expr {$currtime - $::blacklist($srcip)}] < $::holdtime } { # Drop the client_ip that is the the blacklist for less than $::holdtime log "Client $srcip in the backlist for less than $::holdtime => drop" drop } else { # Remove the client_ip from the blacklist since he was there for more than $::holdtime log "Client $srcip removed from the blacklist" unset ::blacklist($srcip) } } # Test if the client_ip already sent queries and if so in the same second if { [info exists ::user_time($srcip)] and $currtime == $::user_time($srcip) } { # Increament the number of requests the client_ip did in the same second incr ::user_freq($srcip) log "Client $srcip sent multiple request in the same second. Now = $::user_freq($srcip)" # Test if the client_ip sent more than the max authorized queries (::maxquery) if { $::user_freq($srcip) > $::maxquery } { # Create a blacklist entry for the client_ip, drop its request and remove the client_ip from the user_time + user_freq set ::blacklist($srcip) $currtime unset ::user_freq($srcip) unset ::user_time($srcip) log "Add client $srcip in blacklist because it sent $::maxquery that second + drop" drop } # The client_ip never sent a query recently (not in usertable) or not in the same second } else { # Create/Update entry in usertable for client_ip set ::user_freq($srcip) 1 set ::user_time($srcip) $currtime log "Creation/Update user_freq + user_time for the client $srcip at $currtime" } }
  • kirirokiriro Member
    edited February 2014
    Posted by kiriro

    Hi Dimitri,

    I have 2 problem, 1 is that aflex can be adapted to only first packet
    in udp session, subsequent packets in same session can not.

    DHCP Discover is sent from always same dhcp relay agent and destination
    address is also same (unicast relay host).So, continuous subsequent
    packets are same udp session.

    2 is that I can not insert and extract data to "array". Please see
    following very simple aflex result.


    ax27#sh aflex dhcp14
    Name: dhcp14
    Syntax: Check
    Virtual port: Bind
    s1: 67
    Statistics:
    Event RULE_INIT execute 1 times (0 failures, 0 aborts)
    Event CLIENT_ACCEPTED execute 1 times (0 failures, 0 aborts)
    Content:
    when RULE_INIT {
    set ::maxquery 10
    set ::holdtime 60
    set ::maxarraysize 400
    array set ::user_freq { }
    array set ::user_time { }
    array set ::blacklist { }
    }

    when CLIENT_ACCEPTED {
    binary scan [UDP::payload 28 6] H12H mac dammy
    set currtime [clock seconds]
    log "$mac $currtime"

    set ::user_time($mac) $currtime
    log "time is [array get ::user_time($mac)]"

    return
    }



    ax27#Oct 25 10:22:22 ax27 a10logd: [AFLEX]<6> 000c292b6e85 1319505742
    Oct 25 10:22:22 ax27 a10logd: [AFLEX]<6> time is



    Thank you,
    -Kiriro
  • edited February 2014
    Posted by ddesmidt

    Let's go over your 2 problems:

    1. aFleX running only on the first packet of the UDP session
    The aFleX event you selected is: "when CLIENT_ACCEPTED".
    This event triggers only once the session has been established. On TCP that's after the SYN-SYN/ACK-ACK. On UDP that's after the first UDP packet received.
    The event that triggers after each UDP packet is "When CLIENT_DATA".


    2. Cannot insert an entry in an array
    Actually you inserted the entry in your array.
    The issue was with the display of the entry.
    Use instead: log "time is ::user_time($mac)"


    Anyway, here is the whole aFleX you need for your need (this time working on DHCP discover packets):

    Code:

    when RULE_INIT { set ::maxquery 100 set ::holdtime 600 set ::maxarraysize 400 # the user_freq table contains "client_mac" + "how many req in the last second" array set ::user_freq { } # the user_time table contains "client_mac" + "when the req has been done" array set ::user_time { } # the blacklist table contains "client_mac" + "time when client entered in the blacklist" array set ::blacklist { } } when CLIENT_DATA { # Get the mac@ of the user sending the DHCP request and the time of the request binary scan [UDP::payload 28 6] H12H mac dammy set currtime [clock seconds] log "mac = $mac at time = $currtime" # Clear up user_time + user_freq table older than 1 second when table is bigger than ::maxarraysize if { [array size ::user_time] > $::maxarraysize } { log "Clear up user_time + user_freq table" foreach {client time} [array get ::user_time] { if {$time < $currtime} { unset ::user_time($client) unset ::user_freq($client) log "Client $client removed from the user_time + user_freq" } } } # Check if the client_mac is in the blacklist if { [info exists ::blacklist($mac)] } { # Check if the client_mac has been in the blacklist for less than $::holdtime if { [expr {$currtime - $::blacklist($mac)}] < $::holdtime } { # Drop the client_mac that is the the blacklist for less than $::holdtime log "Client $mac in the backlist for less than $::holdtime => drop" drop } else { # Remove the client_mac from the blacklist since he was there for more than $::holdtime log "Client $mac removed from the blacklist" unset ::blacklist($mac) } } # Test if the client_mac already sent queries and if so in the same second if { [info exists ::user_time($mac)] and $currtime == $::user_time($mac) } { # Increament the number of requests the client_mac did in the same second incr ::user_freq($mac) log "Client $mac sent multiple request in the same second. Now = $::user_freq($mac)" # Test if the client_mac sent more than the max authorized queries (::maxquery) if { $::user_freq($mac) > $::maxquery } { # Create a blacklist entry for the client_mac, drop its request and remove the client_mac from the user_time + user_freq set ::blacklist($mac) $currtime unset ::user_freq($mac) unset ::user_time($mac) log "Add client $mac in blacklist because it sent $::maxquery that second + drop" drop } # The client_mac never sent a query recently (not in usertable) or not in the same second } else { # Create/Update entry in usertable for client_mac set ::user_freq($mac) 1 set ::user_time($mac) $currtime log "Creation/Update user_freq + user_time for the client $mac at $currtime" } }
  • kirirokiriro Member
    edited February 2014
    Posted by kiriro

    Hi Dimitri,

    It works great.
    I appreciate your help.

    -Kiriro
Sign In or Register to comment.