Rewriting iRules...LIVE!

We host four shows on Tuesdays at 9:30am pacific every month. This November, we had a bonus Tuesday, so Buu and decided to try something new: live coding with the audience through Visual Studio Code's live sharing extension! Now, I've written iRules for many years, but in the past few that work has given way to more iControl and other automation tools, so whereas I am a little rusty, I still enjoy the experience. In the show, which you can watch in replay below, we walked through the progression of four versions of the DNS Flood Rate Limiting iRule out in the codeshare before settling in to rewrite this iRule from the iRules Toolbox on Github.

Original State

The iRule evaluates the HTTP host and uri, and if there's a match, rewrites them both. Here's the original form:

when HTTP_REQUEST {
  #find a match using host+uri against the class and
  #returns the whole string (field1 field2 field3)then
  #set it as newURI variable.
  set newURI "[findclass [HTTP::host][HTTP::uri] $::redlist]"
  if { $newURI ne "" } {
    # Parse the three fields in the matched datagroup line
    scan $newURI {%s %s %s} unused host uri
    #change host and uri
    if {$host ne ""}{
      HTTP::header replace Host $host
    }
    if {$uri ne ""}{
      HTTP::uri $uri
    }
  }
}

We talked during the show about the findclass command and that it's been deprecated for quite some time, and that the $::data-group-name format utilized is also deprecated and even if it wasn't, would not be a good idea as it would demote the virtual server from CMP. Another thing that jumped out at me was the independent checks on the host and uri, as I'm not sure the original intent would be to rewrite one without the other, but without the context of the original problem it's hard to tell. Finally, the scan, which is an awesome command, takes the input from the data-group and stores the values in three separate variables, one of which, aptly named, is unused.

Final State

I simply LOVE the interaction that the live stream coupled with the VS Code live sharing provided. Daniel Wolf and Josh Becigneul, in their engagements, added a couple insights to what is the final state that I would not have included in my version if coding this solo. First, the data group and the iRule, and then some comments.

The original data group seemed to have a format without key/value pair, but it’s hard to tell since it wasn’t provided. That said, It makes more sense to me, and Daniel Wolf also alluded to this, to have the record name be the string we’re matching and for the data to be the new host/uri, separated by a single whitespace.

    # ltm data-group internal redlist {
    #   records {
    #     testvip.test.local/testpath/img.png {
    #       data "newtestvip.test.local /newtestpath/img.png"
    #     }
    #   }
    # }

when HTTP_REQUEST priority 501 {
  if { [HTTP::has_responded] } {
    return
  } else {
    scan [class match -value -- [HTTP::host][string tolower [HTTP::uri]] starts_with redlist] {%s %s} host uri
    if {($host ne "") and ($uri ne "") } {
        HTTP::header replace Host $host
        HTTP::uri $uri
    }
  }
}

The rewrite kicks off with a mention in the comments from Josh about making sure the BIG-IP, either by policy or other iRule, hasn’t already responded. These soft errors used to drive me batty trying to locate them when I was operationally responsible for DevCentral and adopted 10+ iRules running on a single virtual doing a myriad of things. This is a newer command to handle the problem, and I’m grateful for it. Nice callout! Next, the sweet sauce is all consolidated into the scan command. From the inside out:

  • Added a string tolower for the HTTP::uri so we don’t have to manage all those cases in a static data-group
  • Replaced findclass with class match
  • Since using HTTP::uri instead of HTTP::path, used starts_with operator instead of equals
  • Utilized -value to return the data within the record instead of just a boolean
  • Utilized -- to prevent additional arguments
  • With scan, since we optimized the data-group, we only need to store the host and uri and don’t have to handled the record name at all. 

Finally, before doing a header replace or rewriting the URI, I check to make sure neither is an empty string, so they’re coupled together. This may or may not have been the original intent, but it’s the logic choice I’m making here in the update.

Not necessary for the logic that we’re rewriting, but I added this event below to test the changes above since I didn’t want to test actual server URI stuff.

# This lower priority request event is strictly for testing the iRule, it is not intended for rollout
when HTTP_REQUEST priority 502 {
    HTTP::respond 200 content "New Host: [HTTP::header Host], New URI: [HTTP::uri]"
}

And with that, we can fire up one of my Today I Learned segments in VS Code, the Thunder Client, to run a quick test.

You can see in the client that I am inserting a host header since I am testing the URL to an IP address of my test box, and that I am using the URI I intend to match from the data-group above. And finally, in steps 3 and 4, the results we expect to see as a result of the iRule logic. Huzzah!

Conclusion

This was a super fun show to do, and we loved the engagement. Rewriting things is also a great way to skill build. Sometimes the most daunting task is an empty screen. It's like BASF used to say in their commercials, "We don't make the products you buy, we make the products you buy better." In that vein, if you want to skill build, go out to the iRules Toolbox on Github and check over all the 20 Lines or Less iRules and start making them better. It'll make YOU better in the process. Until next time...

Published Dec 01, 2021
Version 1.0
No CommentsBe the first to comment