This article has been archived. information is available in the Advanced iRules series in the Scan article.

Introduction

An iRule is a powerful and flexible feature of BIG-IP devices based on F5's exclusive TMOS architecture.  iRules provide you with unprecedented control to directly manipulate and manage any IP application traffic.  iRules utilizes an easy to learn scripting syntax and enables you to customize how you intercept, inspect, transform, and direct inbound or outbound application traffic.  In this series of tech tips, we'll talk about the TCL language, its usage and control structures, as well as iRule extensions to the TCL language. Other articles in the series:

 

The Scan Command

Scan is used to parse out strings.  It takes a string and based on your format parameters, stores the matches in one or more variables.  It also returns the number of conversions performed, so it can be used in a conditional as well.  For all the options available in the command, the scan man page is available here at http://tmml.sourceforge.net/doc/tcl/scan.html.  I'll highlight a couple of the options I see used in iRules examples hitting the forums below.

scan string format ?varName varName ...?

Options

  • d - The input substring must be  a decimal integer.
  • s - The input substring consists of all the characters up to the next white-space character.
  • n - No input is consumed from the input string.  Instead, the total number of characters scanned from the input string so far is stored in the variable.
  • [chars] - The input substring consist of one or more characters in chars.  The matching string is stored in the variable.
  • [^chars] - The input substring consists of one or more characters not in chars.

Examples

So how do we put scan to use in iRules?  Consider this first example:

Scanning the Host

 

when HTTP_REQUEST {
  if { [scan [HTTP::host] {%[^:]:%s} host port] == 2 } {
    log local0. "Parsed \$host:\$port: $host:$port
  }
}

Here we are scanning the host contents.  The HTTP::host command only returns a port if it is not port 80 for http traffic and port 443 for ssl traffic, so if it is standard, the second conversion (%s) will not populate the port variable and the conditional will be false.  In the scan commands format section, %[^:] tells the scan command to store all the characters in string from the beginning until the first occurrence of the colon.  We then put a colon before the %s (which tells scan to store the remaining characters until the end or white space) so it is not included in the port variables contents.  Also note that the format string is wrapped in curly braces so that the brackets are not evaluated as a command.  Below is the functionality of the scan command in a tcl shell:

% set httphost "www.test.com:8080"
www.test.com:8080
% scan $httphost {%[^:]:%s} host port
2
% puts "$host $port"
www.test.com 8080

Another use case--splitting up an IP address into multiple variables--is accomplished in one easy step below.

% set ip 10.15.25.30
10.15.25.30
% scan $ip %d.%d.%d.%d ip1 ip2 ip3 ip4
4
% puts "$ip1 $ip2 $ip3 $ip4"
10 15 25 30

As with most things with iRules, there are many paths to the same result, even if they require more steps.  Here's another way to arrive at the same split IP with variables for each octet.  This method requires four sets of a nested split/lindex evaluation to achieve the same result.

% set ip 10.15.20.25
10.15.20.25
% set ip1 [lindex [split $ip "."] 0]
10
% set ip2 [lindex [split $ip "."] 1]
15
% set ip3 [lindex [split $ip "."] 2]
20
% set ip4 [lindex [split $ip "."] 3]
25
% puts "$ip1 $ip2 $ip3 $ip4"
10 15 20 25

If you're wondering why you'd split the IP like this, the use case in the forums was to extract each octet so they could then do some bit shifiting to create a unique ID for their stores based on IP subnets.  The post is here if interested. One final example before closing.  This example can be found in entirety in this forum post, but the scan specific operations are below.  The scan string is refined over a few steps to show the elimination of unwanted characters in the variables.

% set sipinfo {<sip:214365981110@10.15.20.25:3232>}
<sip:214365981110@10.15.20.25:3232>
% scan $sipinfo {%[^:]%s} garbage sessid
2
% puts "$garbage $sessid"
<sip :214365981110@10.15.20.25:3232>

You can see that at the first colon it dumped the contents up to that character into the garbage variable.  Everything else, including the colon, is dumped into the sessID variable.  Close but we don't want that colon, so we need to include it in the scan format string.

% scan $sipinfo {%[^:]:%s} garbage sessid
2
% puts "$garbage $sessid"
<sip 214365981110@10.15.20.25:3232>

Good.  Now we need to break off the host and the port as well.  We want all characters up until the @ sign for the session id, then all the characters between the @ sign and the colon for the host, and finally all the characters after the color for the port.

% scan $sipinfo {%[^:]:%[^@]@%[^:]:%s} garbage sessid host port
4
% puts "$sessid $host $port"
214365981110 10.15.20.25 3232>

OK, all looks good except the port.  We definitely don't want the > on the port.  So one final fix.

% scan $sipinfo {%[^:]:%[^@]@%[^:]:%[^>]} garbage sessid host port
4
% puts "$sessid $host $port"
214365981110 10.15.20.25 3232

Scan is a great command to have available in your iRules arsenal.  Thanks to Hoolio, cmbhatt, sre, and natty76 for some great examples.