There are so many things that you can do with iRules that it can be pretty staggering to narrow down what the "most useful" commands are, but if I were given that task and absolutely had to, I would say that DNS resolution ranks up there pretty high on the most powerful list. Perhaps not as widely used as the HTTP or string commands, but the times that it does get used it solves problems that simply couldn't be solved any other way, often times. Whether it's querying an address before routing traffic, securing access of an application to only particular segments, or simply using DNS as a high-speed DB to store whatever other information you want, the possibilities are broad and deep.
For those of you that have been familiar with DNS queries via iRules for a while now, that should come as no surprise. What was a nice surprise, however, was when the name resolution game got changed as of BIG-IP v10.1. Sure, that was a while ago, but I still run into this from time to time where people don't know the differences between the old and new methods, so I figured it was high time to put them down here. There are two ways of performing DNS queries via a native iRule command: NAME::lookup, and RESOLV::lookup. We'll take a look at both, but I'll clearly say it up front, RESOLV::lookup is the preferred method of resolution these days. I'm simply documenting both and comparing them to clear up questions and issues that might be faced by those migrating or that don't understand the differences and pros/cons.
First the old way, NAME::lookup:
This command was introduced in v9, so it's been around for years now. It still works, and many people, myself included, have used it to great success in the past. The command is simple, and takes either an IP or Hostname as an argument, performing the appropriate lookup on that string as necessary. The major issues to note here that have historically made this a bit cumbersome are:
1. This command does not directly return a value
You might think that's a bit awkward, that the command being executed to do a lookup doesn't actually return the value of the lookup. I wouldn't necessarily disagree anymore. At the time it was implemented this was required as there was no way to have the command suspend the connection as necessary, execute the query and return with the value retrieved in an efficient, reliable manner. Thus, there are really two commands to deal with when looking at NAME::lookup. But the lookup command, and the response command (NAME::response). To perform the lookup, you fire NAME::lookup, to retrieve the data, you call NAME::resolved.
2. It is non blocking
What does non blocking mean? Well, I'll cover this in more depth soon, but basically it means that it does not pause the connection in progress. That sounds fantastic, meaning that the current connection gets to fire through without being slowed down, but think of the situations in which you're likely going to be using these commands. The chances are if you're performing a lookup it's for some sort of decision, and you want to know the result of that lookup before allowing the connection to finish processing anyway. That means that in most cases, we'd see people throwing in arbitrary collect statements to suspend the connection until the results of the lookup were returned anyway. This means you then have to manage the collect & release of the connection manually, etc. This isn't necessarily a show stopper, but it's definitely more to think about.
3. It requires a second event to retrieve the data being returned
Given that this is a non blocking command, and as such doesn't return the value directly, there was an event implemented that fires when the information from the lookup returns, so that the results may be processed. The NAME_RESOLVED event is where any processing of the DNS response is performed. It's here that you're able to call the NAME::resolved command, do comparisons as needed on the result, any routing or redirection desired, etc. And don't forget to release the connection (TCP::release, HTTP::release, etc.) if you suspended it with a collect.
That's a lot of talking. Here's a snippet from the Wiki of what it looks like in practice:
2:# Hold HTTP data until hostname is resolved
5:# Start a name resolution on the hostname
10:log local0. "client name = >[NAME::response]<"
12:# Release HTTP data once hostname is resolved
As you can see, while functional and powerful, that is not exactly the most straight-forward method of doing a lookup. As such, there was motivation to change things, and change they did! In version 10.1 the RESOLV::lookup command was introduced, which changed the way in which we're able to perform lookups in iRules considerably. Not only ease of use, but flexibility as well. Let's take a look at that command:
While the syntax for the command might look a little bit more complex (it is), there's good reason for that, and I assure you that this newer, fancier command is much easier to use. First of all, this is now a blocking command, which means it will automatically handle the connection suspension for you. That's a good thing, 95% of the time, and makes for less code to write and manage, no connection management to have to build yourself, etc. All headache removing bits. But this command didn't just get easier to use in 10.1, it got far more functionality as well. Given the extra flags you can provide, you are now able to specify a resolver of your choosing, multiple types of lookup records to retrieve, and even offers IPv6 support. Now...to be fair those command options have been back-ported into the NAME::lookup command now, but they originated here, so this is where I'm giving credit.
What this leaves you with is a command that is not only easier to use, faster to code, and more feature rich...but less confusing to approach. I've heard people shying away from the NAME::lookup command because of the added event and the required suspension code, but hopefully this will show how easy it can be to use the powerful DNS features within iRules thanks to the RESOLV::lookup command. Oh...and did I mention it's faster? The execution is lighter within the iRule, so it takes less cycles to execute than its predecessor. As if there weren't enough reasons already to lean this direction. Let's take a look at doing the same thing as above in the code, but with this newer, lighter command:
2:log local0. "client name = > [RESOLV::lookup -ptr "[IP::client_addr]"]"
Well there you have it, a look at the old and the new. I know which one I'd rather (and currently do) use. The RESOLV::lookup command makes DNS queries approachable and lighter, as well as much more flexible. If you got scared off the first time around by the NAME::lookup limitations or requirements, take a look again and see how things have changed. Even though it's not a brand new command, it's one that seems to get overlooked more than it should, so here's to many happy DNS queries in your iRuling future.