fir3net
PPS-Firenetbanner-780.5x190-30-03-17

The iRule Cookbook

This cookbook is a collection of iRule tips, hints and solutions that I have discovered and found whilst writing and designing iRules across the years.

Contents

  1. How do I split a URL and assign them to separate variables ?
  2. How do I perform DNS Lookups ?
  3. What is the easiest way to Rewrite the uri ?
  4. Return vs Event Disable
  5. Debugging
  6. Equal HTTP Request distribution
  7. X-Forwarded-For via TCP Options

How do I split a URL and assign them to separate variables ?

There's a number of ways to perform string splitting/parsing (i.e regex, getfield etc). However in this recipe we use the 'scan' command. The has the benefit of using less over head then regex whilst also allowing you to split the string only using the first instance of '/' which is not possible via 'getfield'.

when HTTP_REQUEST {
     set url "www.bbc.com/sales/pictures/monkey.jpg"
     set uri ""
     scan $url {%[^/]%s} host uri
     log local0. "host = $host ; uri = $uri"
}

This produces the following log entry,

Nov  4 20:26:10 local/tmm info tmm[4917]: Rule IRULE-NEWTEST <HTTP_REQUEST>: host = www.bbc.com ; uri = /sales/pictures/monkey.jpg

Further information on the 'scan' command can be found at https://devcentral.f5.com/articles/irules-101-16-parsing-strings-with-the-tcl-scan-command

How do I perform DNS Lookups ?

When performing DNS lookups within your iRule (in conjunction with the node command) it is recommended that you split the response and only use the first IP (column) provided.

This is to prevent situations where multiple IP`s are provided within the response to a DNS lookup. As the node command only takes a single IP, at the point it trys to execute the node command, the iRule will throw an error and a reset is sent back to the client.

Below shows you an example of the code,

set lookup [RESOLV::lookup @8.8.8.8 -a www.domainname.com] 
set ip [getfield $lookup " " 1]                            
node $ip 80                                                

Below provides details of what each command does,

  1. perform lookup, assign answer to var $lookup
  2. split $lookup var (space as delim), assign column 1 to var $ip
  3. send traffic to node

What is the easiest way to Rewrite the uri ?

A great method for quickly and rewriting a URI without the need to issue a redirect back to the client is to reassign the new uri back to HTTP:uri.

As shown below,

when HTTP_REQUEST {
    if {[HTTP::uri] eq "/"}{
        HTTP::uri "/newuri"
    }
}

Return vs Event Disable

There may be times within your iRule that you may need to prevent further execution of your iRule(s). There are 2 key commands that allow to you achieve this. They are, event and return.

  • Event - Allows you to disable event(s) across all iRules for the duration of a connection across. The most common use of this command is 'event disable', which disables the particular event (as previously mentioned) for the lifetime of the connection. (more info)
  • Return - Exits the current event within the current iRule. (more info)

When is this useful ? One example may be if you have multiple iRules assigned to a virtal server. Each one preforming a redirect. Once the first redirect is performed you may want to prevent any further event execution. For a more advanced example please read here.

!! Note !! When using "event disable" be careful. If you are redirecting the client back to the same virtual server, you may face situations where the client uses the same connection. This in turn means no further events will be executed, because of disabling all events for that given connection.

Debugging

One of the best ways to debug an iRule is by adding log lines at various steps within your iRule. These logs are then added to the /var/log/ltm logfile.

Using the method below we can enable or disable logging (debugging) via a single variable ($DEBUG). The line if { if DEBUG } { log local0. "$LOG_PREFIX: <ACTION>" } is then placed before each action within the iRule.

In addition to this the client IP address is also added. To make log parsing easier during troubleshooting.

when HTTP_REQUEST {
    set LOG_PREFIX [IP::client_addr]
    set DEBUG 0
    
    if { if $DEBUG } { log local0. "$LOG_PREFIX: <ACTION>" }
    <ACTION>
 
    unset LOG_PREFIX DEBUG
} 

CORRECT HTTP Request Distribution

By default the F5 will balance traffic on a per connection basis. However in instances where multiple requests are sent over a single connection (i.e when using HTTP 1.1 KeepAlive) you may observe that each request is not sent to the correct pool member based on the logic of your iRule.

To ensure the correct selection is made mid-connection the default pool is defined within the 3WHS. And then a final else statement is added to the iRule.

when CLIENT_ACCEPTED {
# assign default pool to variable once 3WHS is complete.
set default_pool [LB::server pool]
}

when HTTP_REQUEST {
if { [HTTP::uri] starts_with "/abc" } {
pool pool_abc
} else {
pool $default_pool
}
}

Further details can be found at http://support.f5.com/kb/en-us/solutions/public/9000/800/sol9800.html.

Note : You can also use OneConnect instead of the method above. However as OneConnect SNATs the traffic the above method is favored.

X-Forwarded-For via TCP Options

To allow for CDN providers to add the true client IP to an encrypted (HTTPS) packet the IP address can be placed into the TCP options header. The IP can then be pulled from the header and placed into the XFF header within the HTTP request.

To do this you first need to instruct the F5 to examine the necessary TCP option via the command.

Note : 22 is the hex based number. So in this case this will examine TCP option 34 (i.e 0x22)

bigpipe db Rules.Tcpoption.settings [22,first]

 In terms of the iRule. We ensure the length of the TCP option (as a string) is 4.  We then parse the option into an IP address and pass this to the HTTP Request event to place into a X-Forwarded-For header.

when CLIENT_ACCEPTED {
    set opt34 [TCP::option get 34]  
    if { [string length $opt34] == 4 } {
        set optaddr [ IP::addr parse $opt34  ]
    }
}

when HTTP_REQUEST {
if { [info exists optaddr] } {
        HTTP::header insert "X-Forwarded-For" $optaddr
    }
}

Note : If you using a Cisco ASA in front of your F5 you will need to permit the TCP option. Details on how to configure this can be found here.

Reference

http://www.applicationdelivery.com.au/2012/09/06/the101-irules-2/

Tags: TCL, iRule, BIG-IP F5

About the Author

RDonato

R Donato

Rick Donato is the Founder and Chief Editor of Fir3net.com. He currently works as a Principal Network Security Engineer and has a keen interest in automation and the cloud.

You can find Rick on Twitter @f3lix001