Advanced iRules: Getting Started with iRules Procedures
As Colin so eloquently puts it in the procs overview on Clouddocs:
"Ladies and gentlemen, procs are now supported in iRules!"
Yes, the rumors are true. As of BIG-IP version 11.4, you can move all that repetitive code into functional blocks the Tcl language defines as procedures, or procs for short. If you program in any other languages, function support is not new. If you don't, maybe you've at some point built a macro in a Microsoft Office product to perform a repetitive task, such as substituting diapers for Dwight every time it was typed as Jim Halpert did in NBC's The Office. Anywho...now that we have them, what can you do with them? The biggest benefit is code re-use. If you have a particular set of commands that you use multiple times in one or more rules, it makes sense to code that once and just call that code block when necessary. Another benefit of using a procedure is having one source to document/troubleshoot/update when necessary.
Where Do They Go, and How Do I Use Them?
With few exceptions, code blocks must be place within the context of an event. Procedures join the short list of exceptions of code blocks or commands that live outside an event. There are two options for placing procs, in the iRule where it will be called, or in another iRule altogether. In this first example, the proc is local.
rule encodeHTML {
proc html_encode { str } {
set encoded ""
foreach char [split $str ""] {
switch $char {
"<" { append encoded "<" }
">" { append encoded ">" }
"'" { append encoded "'" }
{"} { append encoded """ }
"&" { append encoded "&" }
default { append encoded $char }
}
}
return $encoded
}
when RULE_INIT {
# iRule that calls the html_encode proc:
set raw {some xss: < script > and sqli: ' or 1==1# "}
log local0. "HTML encoded: [call html_encode $raw]"
}
}
The proc definition is pretty basic, it receives a string and splits it up so it can encode characters matched in the switch statement, and otherwise leaves the characters alone. Notice, however, in the INIT event the new call command: [call html_encode $raw]. This is where the proc is invoked. Now, let's look at a remote proc.
rule library {
proc html_encode { str } {
set encoded ""
foreach char [split $str ""] {
switch $char {
"<" { append encoded "<" }
">" { append encoded ">" }
"'" { append encoded "'" }
{"} { append encoded """ }
"&" { append encoded "&" }
default { append encoded $char }
}
}
return $encoded
}
}
rule encodeHTML {
when RULE_INIT {
# iRule that calls the html_encode proc:
set raw {some xss: < script > and sqli: ' or 1==1# "}
log local0. "HTML encoded: [call library::html_encode $raw]"
}
}
Notice the subtle change to the call command? Because the proc is in a remote irule, it is necessary to set that rule's name as the namespace of the proc being called. A couple of notes:
- I'd recommend starting a library of procedures and store that library in the common partition so all application owners from different partitions can use it. This works from partition x: [call library::proc_a], but I'd include the partition to be safe: [call /Common/library::proc_a].
- If creating a library of procs, and one proc calls another proc in that library, make sure to explicitly define the namespace in that call. Example below.
proc sequence {from to} {
for {set lst {}} {$from <= $to} {incr from} {
lappend lst $from
}
return $lst
}
proc knuth_shuffle lst {
set j [llength $lst]
for {set i 0} {$j > 1} {incr i;incr j -1} {
set r [expr {$i+int(rand()*$j)}]
set t [lindex $lst $i]
lset lst $i [lindex $lst $r]
lset lst $r $t
}
return $lst
}
proc shuffleIntSequence {x y} {
return [call procs::knuth_shuffle [call procs::sequence $x $y]]
}
The shuffleIntSequence proc, which is called from another iRule, makes calls to two more procs, knuth_shuffle_lst and sequence. Without explicitly setting the namespace, the Tcl interpreter is expecting the call to sequence to be local to the iRule calling it, and it will fail.
Use Cases
There are already a couple procs out in the codeshare, and are referenced as well on a page on Clouddocs specifically for procedures. Logging, math functions, canned response pages, and more have been tossed around as ideas for procs. What else would you like to see?
- Algebraic_MirrorCirrostratus
What kind of performance impact is there from calling a proc, vs putting the functionality directly in the irule without using a proc? For stuff that is getting called on every HTTP request, would we noticed any extra overhead from using the procs?