Getting Started with iRules: Control Structures & Operators

So far in the first several installments of this series we’ve talked about some solid introduction topics, from programming basics to F5 terminology to covering an introduction of what iRules is as a technology, how it works, and how to make use of it. These introductions are a solid stepping-stone to get people on the same page so that we can delve into iRules without losing people along the way, or so goes the theory.

In the last couple of installments we moved past introductions and started talking about iRules proper, discussing events, which are a foundational piece of the iRules framework, and priorities. It’s important to understand events and the way iRules work within an event driven framework, so I highly suggest giving at least that installment, as well as the article on variables a read if you are new to the series.

Building from those ideas we’re now going to cover control structures and operators, two more fundamental and important pieces of iRules that are integral to just about any iRules script that you may write. Combining these along with events will give you the basic framework for a logical flow within almost any iRule, with varying degrees of complexity, of course.

Control Structures

What is a control structure?

A control structure is a logical statement that will allow you to make a comparison and, based on the result of that comparison, perform some set of actions. If you’ve done any form of scripting whatsoever you’ve used them, if, else, switch…etc. Whether this is checking to see if a value is equal to zero and then only executing a block of code if that is true, or determining whether or not an inbound IP address is in the allowed list before granting access to a restricted portion of your application, any decision you’re making within your iRule is made using control structures.

Just like without events, you’d be unable to execute code at the proper moment in time, based on what’s happening with network flow; without control structures you wouldn’t be able to make logical separations in which code gets executed under certain circumstances which, obviously, is paramount to building a functional script.

What different control structures are there?

In iRules the main control structures you’ll be working with are “if”, “else”, “elseif”, “switch”, and “class”, which is a bit of a special case, but we’ll discuss how that works. Let’s go through a simple look at each of these available options:

if

The if statement is by far the commonly used and widely understood form of a logical control structure out there. It’s in every language I’ve ever seen, and makes sense to just about anyone without any form of explanation whatsoever. Quite simply, this statement says “only execute the below code if the statement contained results as true”. A quick example would be:

if {[info exists $auth]} {
    pool auth_pool
}
else

Else, in and of itself, isn’t really a control structure, but is rather an optional addition to the if statement. It allows you to make a logical separation between the two outcomes of an if statement. So logically the above example reads as “if the auth variable is set, send traffic to the pool named auth_pool”. But…what happens in this case if the auth variable isn’t set? Nothing, as it stands right now. If we want to change that, we can use the else statement to describe what we want to happen. For example:

if {[info exists $auth]} {
    pool auth_pool
} else {
    pool other_pool
}
elseif

So now we can make a pretty solid logical statement based on a single comparison (does the auth variable exist, in the above example). What if, however, we want to be able to make multiple comparisons and act differently based on which outcome proves to be true? We need to create a multi-choice logical statement, which can be done a couple of ways. The first, likely most common way is with the elseif statement, which is also associated with the if structure. With an if/else statement you are saying “if x, do y else do z”. With an elseif clause you can effectively say, “if x do y, if not x but a do b”, etc. It sounds much more complex trying to spell it out than it actually is, an example is probably a simpler way of depicting how elseif fits in with if and else:

if {[info exists $auth]} {
    pool auth_pool
} elseif {[info exists $secondary]} {
    pool secondary_pool
} else {
    default_pool
}
switch

Having covered if, else, and elseif we’ve covered the most common control structures used in most cases. That does not, however, mean that they are necessarily the best choice in every case. They are widely used because they are widely known, but there’s another option that, in many cases, can prove superior in not only readability but also in all important performance. A switch statement can be used to replace an if, an if/else, an if/elseif chain, and more. It’s a very versatile, compact statement and it would behoove any would be iRuler to learn the ins and outs of bending switch statements to their will. The one big caveat with a switch statement is that while it supports multiple match options, it does so against a single comparison string.

Taking a look at a basic switch statement that inspects the URI and acts based on the contents looks like this:

switch [HTTP::uri] {
    “/app1” {
        pool http_pool
    }
}

This effectively says “Inspect the HTTP::uri variable, and if it is /app1, send traffic to the http_pool”. This is a pretty basic if statement. That, however, isn’t anywhere near all of what switch can do. What if you wanted to do multiple possible matches against the URI. I.E. “If the HTTP::uri is /app1, send to http_pool, if it is /app2, send to http_pool2, if it is /app3, send to http_pool3”. That would be simple with switch, and would look like:

switch [HTTP::uri] {
    “/app1” {
        pool http_pool
    }
    “/app2” {
        pool http_pool2
    }
    “/app3” {
        pool http_pool3
    }
}

Note the lack of need for else or elseif statements, and the simple, clean logical flow. Last let’s look at a logical or statement, a particularly useful trick that you can perform with a switch statement. If your logic statement looks like “If the URI is /app1, /app2, or /app3, send to the http_pool”. The switch statement to satisfy that requirement would be as simple as:

switch [HTTP::uri] {
    “/app1” –
    “/app2” –
    “/app3” {
        pool http_pool
    }
}

The dash after a switch statement allows it to fall through in a logical “or” fashion, making it easy to chain multiple match cases to go along with a single action, as above.

Finally, with the switch statement, you can use glob-style pattern matching.

switch -glob [HTTP::uri] {
    "*\\?*ghtml*" {
        #do work here
    }
    {*\?*ghtml*} {
        #do work here
    }
}

Where the pattern is something like

*\?*ghtml*

and you are using double quotes, note that string substitution occurs before processing the glob pattern argument, so two back slashes are required to escape the question mark. By using the curly braces, you avoid the string substitution and the glob pattern can be matched as expected.

 

Which control structure is right for me?

There is much more to learn about the ins and outs of the different control structures, but that should be enough to get you started. Now the tough choice – which to use? While there is not a hard and fast set of rules saying “this is when you must use x vs y”, there are some general guidelines to live by:

  1. If is the most commonly used for a reason. If you are doing 1-3 comparisons, if or if/else(if) are probably the most appropriate, and easiest to use and understand.
  2. If you are making 3-10 comparisons, you should likely be using a switch statement. They are higher performance, easier to read, and much simpler to debug, generally speaking.
  3. Past 10-15 entries, you should be using a data group and the class command, but we’ll cover more on that in another article.

What should I keep in mind when setting up my structure?

When building out your control structures it’s important, regardless of whether it’s a series of if/else comparisons or a switch statement, to do your best to order them for efficiency. By putting the most frequently matched items towards the top of the list you can improve efficiency dramatically. The deeper the match depth in the if/elseif chain or the switch statement the more comparisons need to be made before arriving at the match.

Comparison Operators

What is a comparison operator?

A comparison operator (just operator for our purposes) is how you define what type of comparison you’re going to use when comparing two items. Every if or switch requires some kind of true/false result, and most of these will make use of a comparison of some sort. Deciding “if {$x eq $y}” or “if {$z > 5}” requires the use of such operators, as does just about any logical decision being made. As such, they’re exceedingly common and frequently used.

What different comparison operators are there?

The main comparison operators are:

Operator Example
equal “$x eq $y” or “$x == 5”
not equal “$x ne $y” or “$x != 5”
contains [HTTP::uri] contains “abc”
starts_with [HTTP::uri] starts_with “/app1”
ends_with [HTTP::path] ends_with “.jpg”

There are obviously more (>, %lt;, >=, <=, etc) but the above are the most commonly used, generally speaking. Comparison operators aren’t iRules specific, so I won’t belabor all of them here. Suffice to say most commonly accepted comparison operators will work in Tcl, and if they work in Tcl they’ll work in iRules.

What are the pros and cons of the different operators?

The most important thing to remember when dealing with operators is “the more specific the better”. Always use equals when you can, use starts_with or ends_with over contains when you can, etc. The more specific the operator you’re using the less work that needs to be done to identify whether or not the comparison will return true or not. This is directly visible in a savings in CPU cycles, and as such is worth some thought when writing your iRules.

There is also the notion of polymorphism to take into account but that has already been covered, and tends to be a more advanced concept, so if you’d like to read more please do, but we don’t need to cover it in any great detail here.

That wraps up control structures and operators. Next week we’ll dig into delimiters, covering the different types available within iRules, the pros and cons, the situations in which you’d use each, and more.

Updated Oct 02, 2023
Version 3.0

Was this article helpful?

4 Comments

  • I think

     

    "A comparison operator (just operator for our purposes) is how you define what time of comparison you’re going to use when comparing two items"

     

    should be

     

    "A comparison operator (just operator for our purposes) is how you define what type of comparison you’re going to use when comparing two items."

     

  • @mattcarlson...that only took 2 years for someone to find! Glaring and yet well hidden. Thanks for the catch!