Forum Discussion

mark_06_140158's avatar
mark_06_140158
Icon for Nimbostratus rankNimbostratus
Dec 19, 2013

Simple irule for novice

Hi I am a novice. Could you tell me if the following irule would work to load balance to specified pools based on source ip of dns request?

 

When dns request {ip::address [ip::client_addr] equals 10.10.10.5/24}{pool pool1}

 

When dns request {ip::address [ip::client_addr] equals 10.10.20.5/24}{pool pool2}

 

When dns request {ip::address [ip::client_addr] equals 10.10.30.5/24}{pool pool3}

 

Thanks

 

1 Reply

  • Unfortunately not. I'll start by providing an iRule that will work, then provide some commentary.

    I assume that you want to match on individual IP addresses, rather than all IP addresses in netblocks. That is, a query from 10.10.10.5 should go to pool1, but it is not necessarily the case that all IPs in 10.10.10.0/24 should. If that is so, this iRule achieves that:

    when DNS_REQUEST {
    switch [IP::client_addr] {
    "10.10.10.5" {
    pool pool1
    }
    "10.10.20.5" {
    pool pool2
    }
    "10.10.30.5" {
    pool pool3
    }
    }
    }
    

    Generally speaking, a when clause for a specific event (DNS_REQUEST in this example) occurs only once in a particular iRule. As with most general purpose programming languages, iRules (a Tcl dialect) supports conditionals (both if/elseif/else and switch, as above), which is where the decision making happens.

    iRule commands broadly come in two forms: inquisitive and imperative. An inquisitive command returns a value, while an imperative command instructs the system to do something. Inquisitive commands should always be wrapped in the square brackets ([]), while imperative commands usually are not. In Tcl, the square brackets force inline evaluation of the statement contained within the brackets. Inquisitive commands are expected to evaluate to a value which you subsequently use (e.g., as the conditional for the switch above); thus, the reason why square brackets are needed. Notice that the pool command -- which is imperative -- is not wrapped in square brackets. We don't expect it to evaluate to a value, and even if it did, we don't need the return value.

    I mention this because there is a form of IP::addr which can be used for netblock comparison, as in:

        if { [IP::addr [IP::client_addr] equals "10.10.10.0/24"] } { pool1 }
    

    Notice that both IP::client_addr and IP::addr are wrapped in square brackets. We want them both to evaluate to something (an IP address and a boolean, respectively).

    For completeness, you can use a slight variant of the IP::addr statements you have above in an if conditional:

    when DNS_REQUEST {
    if { [IP::addr [IP::client_addr] equals "10.10.10.5"] } {
    pool pool1
    }
    elseif { [IP::addr [IP::client_addr] equals "10.10.20.5"] } {
    pool pool2
    }
    elseif { [IP::addr [IP::client_addr] equals "10.10.30.5"] } {
    pool pool3
    }
    
    }
    

    but I consider that to be less readable. And, strictly speaking, the IP::addr is not needed. The following would suffice:

    when DNS_REQUEST {
    if { [IP::client_addr] equals "10.10.10.5" } {
    pool pool1
    }
             ... etc ...
    }
    

    Since, in this context, IP::client_addr produces a string.

    A few things to keep in mind:

    1. Usually, with this type of rule, it's a good idea to have a default pool defined. You could do this via the iRule, or better yet, attach a pool to the virtual server on which this iRule is defined;
    2. This will only work on the LTM module if GTM is licensed as well, or with a DNS Services add-on license. If neither of those are true, the DNS_REQUEST event is not defined and will never fire.