Forum Discussion

RoutingLoop_179's avatar
Jan 29, 2013

my DNS irule - how can i optimise it further or is there a better way?

Hi - i've created an irule losely based on DNS blackhole irules i've seend to rewrite DNS responses.

 

 

But my rule basically has to check a client source address against a list of subnets in a datagroup and if it matches it then needs to check the DNS name is contained in say a whitelist datagroup and then return the DNS response based on the datagroup value. However, i have to repeat this for different source address datagroups and whitelists.

 

 

The issue i have because i'm checking against datagroups i've ended up with multiple if - elseif and embedded if/else. Which optimisation 101 says avoid if you can. I cna't use switch because datagroups aren't supported. I've tried ot look at the timings with "timings on" but haven't got my head around the numbers yet.

 

 

Anyway speed and performance is most important - but is there a better way of doing this?

 

 

Any help or hints are appreciated

 

 

Adrian

 

 

my DNS irule:

 

 

timing on

 

when RULE_INIT {

 

set static::whitelist_ttl "300"

 

}

 

 

when DNS_REQUEST {

 

set fqdn [DNS::question name]

 

 

debugging statement see all questions and request details

 

log -noname local0. "Client: [IP::client_addr] Question:[DNS::question name] Type:[DNS::question type] Class:[DNS::question class] Origin:[DNS::origin]"

 

 

Whitelist_Match is used to track when a Query matches the whitelist

 

Ensure it is always set to 0 or false at beginning of the DNS request

 

 

set Whitelist_Match 0

 

 

does the client source address exist in site address external data-group

 

 

if { [class match [IP::client_addr] equals site1_address_datagroup] } {

 

 

does FQDN exist in our whitelist datagroup for that site.

 

if { [class match $fqdn equals site1_whitelist] } {

 

 

Client made a DNS request for a Whitelist site.

 

set Whitelist_Match 1

 

set FakeIPv4 [class match -value $fqdn equals site1_whitelist]

 

DNS::return

 

 

} elseif { [class match $fqdn ends_with site1_wildcard] } {

 

set Whitelist_Match 1

 

set FakeIPv4 "198.18.255.254"

 

DNS::return

 

}

 

 

} elseif { [class match [IP::client_addr] equals site2_address_datagroup] } {

 

 

does FQDN exist in our whitelist string:value datagroup for that site.

 

if { [class match $fqdn equals site1_whitelist] } {

 

 

Client made a DNS request for a Whitelist site.

 

set Whitelist_Match 1

 

set FakeIPv4 [class match -value $fqdn equals site2_whitelist]

 

DNS::return

 

 

} elseif { [class match $fqdn ends_with site2_wildcard] } {

 

set Whitelist_Match 1

 

set FakeIPv4 "198.18.255.254"

 

DNS::return

 

}

 

} elseif { [class match [IP::client_addr] equals site3_address_datagroup] } {

 

 

does FQDN exist in our whitelist string:value datagroup for that site.

 

if { [class match $fqdn equals site3_whitelist] } {

 

 

Client made a DNS request for a Whitelist site.

 

set Whitelist_Match 1

 

set FakeIPv4 [class match -value $fqdn equals site3_whitelist]

 

DNS::return

 

 

} elseif { [class match $fqdn ends_with site3_wildcard] } {

 

set Whitelist_Match 1

 

set FakeIPv4 "198.18.255.254"

 

DNS::return

 

}

 

}

 

 

add further elseif's for more sites.... e.g site3, site4 site_n....

 

 

 

If Whitelist match is still 0 check common whitelist datagroup.

 

if { !$Whitelist_Match } {

 

if { [class match $fqdn equals CM_HTTPS_WL] } {

 

 

set Whitelist_Match 1

 

 

set FakeIPv4 [class match -value $fqdn equals CM_HTTPS_WL]

 

DNS::return

 

 

} elseif { [class match $fqdn ends_with CM_WC_WL] } {

 

set Whitelist_Match 1

 

set FakeIPv4 "198.18.255.254"

 

DNS::return

 

}

 

}

 

}

 

 

when DNS_RESPONSE {

 

 

debugging statement to see all questions and request details

 

log -noname local0. "Request: $fqdn_name Answer: [DNS::answer] Origin:[DNS::origin] Status: [DNS::header rcode] Flags: RD [DNS::header rd] RA [DNS::header ra]"

 

 

if { $Whitelist_Match } {

 

This DNS request was for a Whitelist FQDN. Take different actions based on the request type.

 

 

switch [DNS::question type] {

 

 

"A" {

 

Clear out any DNS responses and insert the custom response. RA header = recursive answer

 

DNS::answer clear

 

 

DNS::answer insert "$fqdn. $static::whitelist_ttl [DNS::question class] [DNS::question type] $FakeIPv4"

 

 

DNS::header ra "1"

 

 

whitelist: 10.1.1.1484902 requested foo.com query type: A class IN A-response: 10.1.1.60

 

log -noname local0. "whitelist: [IP::client_addr][UDP::client_port] requested [DNS::question name] query type: [DNS::question type] class [DNS::question class] A-response: $FakeIPv4"

 

}

 

"AAAA" {

 

 

DNS::last_act reject

 

}

 

default {

 

 

For other record types, e.g. MX, NS, TXT, etc, provide a blank NOERROR response

 

DNS::last_act reject

 

rejected: 10.1.1.1484902 requested foo.com query type: A class IN unable to respond

 

 

log -noname local0. "rejected onwl1: [IP::client_addr][UDP::client_port] requested $fqdn query type: [DNS::question type] class [DNS::question class] non whitelisted DNSRR"

 

}

 

}

 

}

 

}

 

 

5 Replies

  • Hi RL,

     

     

    Here are a few thoughts on this:

     

     

    - Combine the data group entries so you're reducing the number of class searches. Instead of having separate data groups for site1_address_datagroup, site2_address_datagroup, etc, you could combine them into one siteX_address_datagroup. If you need to differentiate between different addresses per site, you could set a value per address key showing which site an address is for:

     

     

    1.1.1.1 := site1

     

    2.1.1.1 := site2

     

    3.1.1.1 := site3

     

     

    When you check if the FQDN is in the whitelist, save the return value so you can avoid a second lookup:

     

     

    current:

     

    if { [class match $fqdn equals site1_whitelist] } {

     

     

    suggested:

     

    if { [set FakeIPv4 [class match -value -- $fqdn equals site1_whitelist]] ne ""} {

     

     

    Aaron
  • Awesome thanks Aaron.

     

    I've tried your suggestion of

     

    if { [set FakeIPv4 [class match -value -- $fqdn equals site1_whitelist]] ne ""} {

     

    but i get response is not well formed XML, i have tried altering it a bit but i honestly can't see whats wrong. interestingly even if I comment the line out it still displays the error until i remove it completely.

     

     

    I had the same thoughts about a single site subnet list, but couldn't work out how I'd link both the comparison of address and site name in one if condition. but i suppose would you do something like this (in sudo code)?

     

    if { [class match [IP::client_addr] equals site_subnet_list_all && [class match -value [IP::client_addr] equals site_subnet_list_all] eq "site1" ] }

     

     

    many thanks for your response btw.

     

    Regards

     

    Adrian

     

  • Can you make sure the braces are all matching? The syntax looks good to me and a simplified version saves for me:

    
    when HTTP_REQUEST {
    if { [set FakeIPv4 [class match -value -- $fqdn equals site1_whitelist]] ne ""} {
    }
    }
    

    Aaron

  • If you had each IP or subnet as a key in one data group and had the sitename as the value for each key, you could do something like:

    
    switch [class match -value [IP::client_addr] equals site_subnet_list_all] {
       "site1" {
            site1 match so do something
       }
       "site2" {
            site2 match so do something else
       }
       default {
            no match so take a default action
       }
    }
    

    Aaron
  • Yet another thank you - When i was researching/reading about optimising rules, a few posts I came across said you couldn't use switch with datagroups as i was trying to avoid if/elseif/elseif/elseif/elseif logic as suggested, especially as the number of sites grow. But couldn't seem to find a way, this is exactly what I was trying to do and it means i don't have to create loads of datagroups for each different site. Looks like F5 could sell me a place on the irules course next :-).

    Regarding the not well formed XML, it seems that comments don't cancel out curly braces - when i removed the commented line it works as expected e.g. this is what didn't work.

    
              if { [set FakeIPv4 [class match -value -- $fqdn equals site1_whitelist]] ne ""} {
              if { [class match $fqdn equals site1_whitelist] } {
                      Client made a DNS request for a Whitelist site.
                     set Whitelist_Match 1
                     set FakeIPv4 [class match -value $fqdn equals site1_whitelist]
                     DNS::return
              }
     

    Regards

    Adrian