Forum Discussion

2funky_105078's avatar
Apr 09, 2014

data group iRule

Hello iExperts,

I just wanted to double check with you the following irule where i want to double check that the client IP is in a determinate data group range of IPs (ecmvpn_ip_dg) and if so, return a HTTP 302 redirect to such client. Is it the correct and "optimized" way to do it?

when RULE_INIT {
    set static::ecmvpn_flag 0
}

when CLIENT_ACCEPTED {
if { [class match [IP::client_addr] equals ecmvpn_ip_dg] } {
set static::ecmvpn_flag 1
}}

when HTTP_REQUEST {

if {$static::ecmvpn_flag==1}{
            HTTP::redirect http://ecmvpn.[HTTP::uri]
            return
        }

switch -glob [string tolower [HTTP::host]] {
"ecm.domain"
          {
                 if { ([HTTP::uri] starts_with "/APPLICATION")} {   
                    HTTP::header insert USER-IP  [IP::remote_addr]
                    persist cookie insert
                    pool DCTM_ECM_APPLICATION _APPLICATION PORT_PROD  (or PREP)
                    return
                  }
          }
  default { return }
}
}
  • Hi you don't need to use static variables - just use a TCP connection scoped variable set in CLIENT_ACCEPTED. Not too sure what you want from the switch ...anyway updated slightly

    when CLIENT_ACCEPTED {
        set ecmvpn_flag 0
        if { [class match [IP::client_addr] equals ecmvpn_ip_dg] } {
            set ecmvpn_flag 1
        }
    }
    
    when HTTP_REQUEST {
        if {$ecmvpn_flag}{
             You need an FQDN here so I've added ".com.au"
            HTTP::redirect http://ecmvpn.com.au[HTTP::uri]
            return
        }
    
        switch -glob [string tolower [HTTP::host]] {
            "ecm.*"
                {
                    if {[string tolower [HTTP::uri]] starts_with "/application"} {   
                            HTTP::header insert USER-IP  [IP::remote_addr]
                             You could just use an HTTP persistence profile attached to the virtual for this
                            persist cookie insert
                            pool pl_DCTM_ECM_APPLICATION 
                            return
                }
            }
            default { return }
        }
    
    }
    
  • Hi you don't need to use static variables - just use a TCP connection scoped variable set in CLIENT_ACCEPTED. Not too sure what you want from the switch ...anyway updated slightly

    when CLIENT_ACCEPTED {
        set ecmvpn_flag 0
        if { [class match [IP::client_addr] equals ecmvpn_ip_dg] } {
            set ecmvpn_flag 1
        }
    }
    
    when HTTP_REQUEST {
        if {$ecmvpn_flag}{
             You need an FQDN here so I've added ".com.au"
            HTTP::redirect http://ecmvpn.com.au[HTTP::uri]
            return
        }
    
        switch -glob [string tolower [HTTP::host]] {
            "ecm.*"
                {
                    if {[string tolower [HTTP::uri]] starts_with "/application"} {   
                            HTTP::header insert USER-IP  [IP::remote_addr]
                             You could just use an HTTP persistence profile attached to the virtual for this
                            persist cookie insert
                            pool pl_DCTM_ECM_APPLICATION 
                            return
                }
            }
            default { return }
        }
    
    }
    
  • Vernon_97235's avatar
    Vernon_97235
    Historic F5 Account

    Just to be clear, "static" in this context isn't really the opposite of "dynamic", but rather the opposite of "automatic". A variable declared in the

    static::
    namespace is stored in a TMM scoped memory location and exists for the entire run of that TMM instance. Automatic variables exist for the duration of a connection. As a side-effect of this, the value of a
    static::
    namespace variable is visible in an iRule executing for any connection serviced by the TMM in which the value is set. That's why
    static:
    : namespace variables are usually declared in
    RULE_INIT
    . Each TMM instance independently executes the
    RULE_INIT
    event. Thus, if, in an iRule -- I'll call it ruleA --, you have the following:

    RULE_INIT {
        set static::somevar  10
    }
    

    then an instance of

    static::somevar
    will be created in each TMM's memory space (independently), and the value will be set to 10. This value is globally visible across connections executing on each TMM. That is, if there is another iRule -- I'll call it ruleB --, then that iRule could access
    $static::somevar
    , as well.

    In every regard, these are normal variables. Most importantly, their value can be altered after being set, and they can be created or altered outside of

    RULE_INIT
    . This is, however, almost always a bad idea, and may produce an unexpected and undesirable result. Let us say that you had the following:

    when CLIENT_ACCEPTED {
       set static::ip [IP::client_addr]
       ...
    }
    

    then, on each connection, you are setting the value of a variable that is globally visible to the TMM serving that connection. Particularly before 11.4, because of TMM's thread model, I assume you won't really run into problems until you do something like this:

    when CLIENT_ACCEPTED {
       set static::ip [IP::client_addr]
    
       after 1000
       log local0. "client is $static::ip"
    }
    

    The '

    after
    ' command (as well as a few others) suspends the execution of a connection and allow another connection to proceed. That other connection will potentially set
    static::ip
    before the first connection is un-suspended.

    The general rule is, only set static:: variables once, and only in the

    RULE_INIT
    event. That is, treat them (in Java-speak) like static final variables. Also, beware of using generic identifiers. For example, let's say in your various iRules, you create a
    static::
    namespace variable that is set to 0 if you don't want debug logging, and 1 if you do, as in:

    when RULE_INIT {
       set static::debug 1
    }
    
    when CLIENT_ACCEPTED {
       if { $static::debug } { log local0. "Connect from client [IP::client_addr]"
    }
    

    Let's say you do that in both ruleA and ruleB. Now let's say you change

    static::debug
    in ruleA to 1 and
    static::debug
    in ruleB to 0.
    static::debug
    is global to the TMM instance, so it can only have one of those two values. It will ultimately have the value of whichever of the two iRules loaded last. In this way, you could accidentally turn on debugging for an iRule that is not being debugged, and adversely affect system performance. It would be better to do this:

    when RULE_INIT {
       set static::ruleA_debug 1
    }
    
    when CLIENT_ACCEPTED {
       if { $static::ruleA_debug } { log ... }
    }
    
    • JG's avatar
      JG
      Icon for Cumulonimbus rankCumulonimbus
      "Particularly before 11.4, because of TMM's thread model" Will you please expand on this? I thought it was 11.3.0 when a multi-threaded architecture started to be used.
    • Vernon_97235's avatar
      Vernon_97235
      Historic F5 Account
      Quite right. And to clarify, I don't know whether threading affects the behavior of the static namespace.
  • Just to be clear, "static" in this context isn't really the opposite of "dynamic", but rather the opposite of "automatic". A variable declared in the

    static::
    namespace is stored in a TMM scoped memory location and exists for the entire run of that TMM instance. Automatic variables exist for the duration of a connection. As a side-effect of this, the value of a
    static::
    namespace variable is visible in an iRule executing for any connection serviced by the TMM in which the value is set. That's why
    static:
    : namespace variables are usually declared in
    RULE_INIT
    . Each TMM instance independently executes the
    RULE_INIT
    event. Thus, if, in an iRule -- I'll call it ruleA --, you have the following:

    RULE_INIT {
        set static::somevar  10
    }
    

    then an instance of

    static::somevar
    will be created in each TMM's memory space (independently), and the value will be set to 10. This value is globally visible across connections executing on each TMM. That is, if there is another iRule -- I'll call it ruleB --, then that iRule could access
    $static::somevar
    , as well.

    In every regard, these are normal variables. Most importantly, their value can be altered after being set, and they can be created or altered outside of

    RULE_INIT
    . This is, however, almost always a bad idea, and may produce an unexpected and undesirable result. Let us say that you had the following:

    when CLIENT_ACCEPTED {
       set static::ip [IP::client_addr]
       ...
    }
    

    then, on each connection, you are setting the value of a variable that is globally visible to the TMM serving that connection. Particularly before 11.4, because of TMM's thread model, I assume you won't really run into problems until you do something like this:

    when CLIENT_ACCEPTED {
       set static::ip [IP::client_addr]
    
       after 1000
       log local0. "client is $static::ip"
    }
    

    The '

    after
    ' command (as well as a few others) suspends the execution of a connection and allow another connection to proceed. That other connection will potentially set
    static::ip
    before the first connection is un-suspended.

    The general rule is, only set static:: variables once, and only in the

    RULE_INIT
    event. That is, treat them (in Java-speak) like static final variables. Also, beware of using generic identifiers. For example, let's say in your various iRules, you create a
    static::
    namespace variable that is set to 0 if you don't want debug logging, and 1 if you do, as in:

    when RULE_INIT {
       set static::debug 1
    }
    
    when CLIENT_ACCEPTED {
       if { $static::debug } { log local0. "Connect from client [IP::client_addr]"
    }
    

    Let's say you do that in both ruleA and ruleB. Now let's say you change

    static::debug
    in ruleA to 1 and
    static::debug
    in ruleB to 0.
    static::debug
    is global to the TMM instance, so it can only have one of those two values. It will ultimately have the value of whichever of the two iRules loaded last. In this way, you could accidentally turn on debugging for an iRule that is not being debugged, and adversely affect system performance. It would be better to do this:

    when RULE_INIT {
       set static::ruleA_debug 1
    }
    
    when CLIENT_ACCEPTED {
       if { $static::ruleA_debug } { log ... }
    }
    
    • JG's avatar
      JG
      Icon for Cumulonimbus rankCumulonimbus
      "Particularly before 11.4, because of TMM's thread model" Will you please expand on this? I thought it was 11.3.0 when a multi-threaded architecture started to be used.
    • VernonWells's avatar
      VernonWells
      Icon for Employee rankEmployee
      Quite right. And to clarify, I don't know whether threading affects the behavior of the static namespace.