For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

Forum Discussion

alex100's avatar
alex100
Icon for Cirrostratus rankCirrostratus
Nov 09, 2023
Solved

iRule operator evaluation order OR/AND

Hi all, 

This must be some simple syntax problem but I have been bashing my head for a while. Here is the irule:

 

 

when HTTP_REQUEST {
    if {(![class match [getfield [IP::client_addr] "%" 1] eq tcp_whitelist]) or (![class match [getfield [HTTP::header X-Forwarded-For] "%" 1] equals xff_whitelist]) and ([class match [string tolower [HTTP::uri]] contains uripath_a])}
        {
            
            log local0. "## Batman Client IP [IP::client_addr], Client XXF IP [HTTP::header X-Forwarded-For],  [HTTP::method], URL: [HTTP::host][HTTP::uri]"
        } else {
          
         pool pl-something-WEB-443
        #log local0. "## Robin Client IP [IP::client_addr], Client XXF IP [HTTP::header X-Forwarded-For],  [HTTP::method], URL: [HTTP::host][HTTP::uri]"
        }
        
    }

 

 

Here is some additional info. I have two data groups containing XFF src IPs and another group with TCP IP  as well as a datagroup with list of URI paths. So the idea is that we evaluated where http request is NOT comming from the IP address neither on xff_whitelist nor tcp_whitelist dg and also that URI path is matching one in uripath_a dg. I am having problems figuring out proper syntax to evaluate or operator before evaluating and operator. 

I have tried wrapping or statemnts in additional clause but that does not seem to work. I think I am doing it wrong. 

 

 

{((![class match [getfield [IP::client_addr] "%" 1] eq tcp_whitelist]) or (![class match [getfield [HTTP::header X-Forwarded-For] "%" 1] equals xff_whitelist])) and ([class match [string tolower [HTTP::uri]] contains uripath_a])}

 

 

Any help and advice is greatly appreceated. 

  • Hi alex100,

    It might be better to think in parts.

    when HTTP_REQUEST {
        #if uri matches with uripath_a:
        if { [class match [string tolower [HTTP::uri]] contains /Common/uripath_a] } {
            #log local0. "Log1 - Uri matched [HTTP::host][HTTP::uri]"
            
            #if Client IP doesn't match with tcp_whitelist
            if { !([class match [getfield [IP::client_addr] "%" 1] eq /Common/tcp_whitelist]) }
                #log local0. "Log2 - Uri matched [HTTP::host][HTTP::uri] | Client IP did not match [IP::client_addr]"
                
                #if XFF doesn't match with xff_whitelist
                if { !([class match [getfield [HTTP::header X-Forwarded-For] "%" 1] eq /Common/xff_whitelist]) }
                    #log local0. "Log3 - Uri matched [HTTP::host][HTTP::uri] | Client IP did not match [IP::client_addr] | XFF did not match [HTTP::header X-Forwarded-For]"
                }
                #if Client IP doesn't match with tcp_whitelist and XFF matches with xff_whitelist
                else {
                    #log local0. "Log4 - Uri matched [HTTP::host][HTTP::uri] | Client IP did not match [IP::client_addr] | XFF matched [HTTP::header X-Forwarded-For]"
                }
            #if Client IP matches with tcp_whitelist
            else {
                #log local0. "Log5 - Uri matched [HTTP::host][HTTP::uri] | Client IP matched [IP::client_addr]"
                
                #if XFF doesn't match with xff_whitelist
                if { !([class match [getfield [HTTP::header X-Forwarded-For] "%" 1] eq /Common/xff_whitelist]) }
                    #log local0. "Log6 - Uri matched [HTTP::host][HTTP::uri] | Client IP matched [IP::client_addr] | XFF did not match [HTTP::header X-Forwarded-For]"
                }
                #if Client IP matches with tcp_whitelist and XFF matches with xff_whitelist
                else {
                    #log local0. "Log7 - Uri matched [HTTP::host][HTTP::uri] | Client IP matched [IP::client_addr] | XFF matched [HTTP::header X-Forwarded-For]"
                }
            }
        }
        #if uri doesn't match with uripath_a
        else {
            #log local0. "Log8 - Uri did not match [HTTP::host][HTTP::uri]"
        }
    }

    I think you want this condition:

    when HTTP_REQUEST {
        if { !([class match [getfield [IP::client_addr] "%" 1] eq /Common/tcp_whitelist]) and !([class match [getfield [HTTP::header X-Forwarded-For] "%" 1] eq /Common/xff_whitelist]) and ([class match [string tolower [HTTP::uri]] contains /Common/uripath_a]) }
            # log local0. "Batman Client IP [IP::client_addr], Client XXF IP [HTTP::header X-Forwarded-For], [HTTP::method], URL: [HTTP::host][HTTP::uri]"
        }
        else {
            pool pl-something-WEB-443
        }
    }

    some function with different operators:

    when HTTP_REQUEST {
        if { !(([class match [getfield [IP::client_addr] "%" 1] eq /Common/tcp_whitelist]) or ([class match [getfield [HTTP::header X-Forwarded-For] "%" 1] eq /Common/xff_whitelist])) and ([class match [string tolower [HTTP::uri]] contains /Common/uripath_a]) }
            # log local0. "Batman Client IP [IP::client_addr], Client XXF IP [HTTP::header X-Forwarded-For], [HTTP::method], URL: [HTTP::host][HTTP::uri]"
        }
        else {
            pool pl-something-WEB-443
        }
    }

2 Replies

  • Hi alex100,

    It might be better to think in parts.

    when HTTP_REQUEST {
        #if uri matches with uripath_a:
        if { [class match [string tolower [HTTP::uri]] contains /Common/uripath_a] } {
            #log local0. "Log1 - Uri matched [HTTP::host][HTTP::uri]"
            
            #if Client IP doesn't match with tcp_whitelist
            if { !([class match [getfield [IP::client_addr] "%" 1] eq /Common/tcp_whitelist]) }
                #log local0. "Log2 - Uri matched [HTTP::host][HTTP::uri] | Client IP did not match [IP::client_addr]"
                
                #if XFF doesn't match with xff_whitelist
                if { !([class match [getfield [HTTP::header X-Forwarded-For] "%" 1] eq /Common/xff_whitelist]) }
                    #log local0. "Log3 - Uri matched [HTTP::host][HTTP::uri] | Client IP did not match [IP::client_addr] | XFF did not match [HTTP::header X-Forwarded-For]"
                }
                #if Client IP doesn't match with tcp_whitelist and XFF matches with xff_whitelist
                else {
                    #log local0. "Log4 - Uri matched [HTTP::host][HTTP::uri] | Client IP did not match [IP::client_addr] | XFF matched [HTTP::header X-Forwarded-For]"
                }
            #if Client IP matches with tcp_whitelist
            else {
                #log local0. "Log5 - Uri matched [HTTP::host][HTTP::uri] | Client IP matched [IP::client_addr]"
                
                #if XFF doesn't match with xff_whitelist
                if { !([class match [getfield [HTTP::header X-Forwarded-For] "%" 1] eq /Common/xff_whitelist]) }
                    #log local0. "Log6 - Uri matched [HTTP::host][HTTP::uri] | Client IP matched [IP::client_addr] | XFF did not match [HTTP::header X-Forwarded-For]"
                }
                #if Client IP matches with tcp_whitelist and XFF matches with xff_whitelist
                else {
                    #log local0. "Log7 - Uri matched [HTTP::host][HTTP::uri] | Client IP matched [IP::client_addr] | XFF matched [HTTP::header X-Forwarded-For]"
                }
            }
        }
        #if uri doesn't match with uripath_a
        else {
            #log local0. "Log8 - Uri did not match [HTTP::host][HTTP::uri]"
        }
    }

    I think you want this condition:

    when HTTP_REQUEST {
        if { !([class match [getfield [IP::client_addr] "%" 1] eq /Common/tcp_whitelist]) and !([class match [getfield [HTTP::header X-Forwarded-For] "%" 1] eq /Common/xff_whitelist]) and ([class match [string tolower [HTTP::uri]] contains /Common/uripath_a]) }
            # log local0. "Batman Client IP [IP::client_addr], Client XXF IP [HTTP::header X-Forwarded-For], [HTTP::method], URL: [HTTP::host][HTTP::uri]"
        }
        else {
            pool pl-something-WEB-443
        }
    }

    some function with different operators:

    when HTTP_REQUEST {
        if { !(([class match [getfield [IP::client_addr] "%" 1] eq /Common/tcp_whitelist]) or ([class match [getfield [HTTP::header X-Forwarded-For] "%" 1] eq /Common/xff_whitelist])) and ([class match [string tolower [HTTP::uri]] contains /Common/uripath_a]) }
            # log local0. "Batman Client IP [IP::client_addr], Client XXF IP [HTTP::header X-Forwarded-For], [HTTP::method], URL: [HTTP::host][HTTP::uri]"
        }
        else {
            pool pl-something-WEB-443
        }
    }
    • alex100's avatar
      alex100
      Icon for Cirrostratus rankCirrostratus

      Thank you for your feedback, time and refactored rule. Looks like I was on the right path but the culprit turned out to be about negation sign ["!"] placement. 

      Original if statement 

      {((![class match [getfield [IP::client_addr] "%" 1] eq tcp_whitelist]) or (![class match [getfield [HTTP::header X-Forwarded-For] "%" 1] equals xff_whitelist])) and ([class match [string tolower [HTTP::uri]] contains uripath_a])}

      Re-factored if statement

      { ([class match [string tolower [HTTP::uri]] starts_with uripath_a]) and !(([class match [getfield [IP::client_addr] "%" 1] eq /Common/tcp_whitelist]) or ([class match [HTTP::header X-Forwarded-For] contains /Common/xff_whitelist])) }

      Looks like with multiple class mach commands , "!" has to be moved outside of ().