Using Tcl packages in iRules
In standard Tcl scripts, you have the ability to include packages, like for working with ip addresses. For example, if I want to normalize an IPv6 address, I can do so like this:
% package require ip 1.2
% ::ip::normalize ::1
0000:0000:0000:0000:0000:0000:0000:0001
% ::ip::normalize 2001:db8::
2001:0db8:0000:0000:0000:0000:0000:0000
% ::ip::normalize 2001:0db8:85a3::8a2e:0370:7334
2001:0db8:85a3:0000:0000:8a2e:0370:7334
Which is great, but the package keyword is disabled in iRules, so we don't have access to those tools and thus have to build them ourselves. Or do we?
Making Tcl source iRules compatible
I had an inquiry from my colleague Matt Stovall on the ability to manage IPv6 within iRules. The good news was that there is an argument on the IP::addr command to parse IPv6 addresses. The bad news is that the requirement is for the full IPv6 address but the parsed address returned by IP::addr is the short version:
Rule /Common/t1 <RULE_INIT>: 2001:0db8:85a3:0000:0000:8a2e:0370:7334 Parsed w/ IP::addr: 2001:db8:85a3::8a2e:370:7334
While looking into the problem, I noticed a codeshare entry to compress/expand IPv6 addresses from MVP Kai_Wilke. That is an option for sure. But I also wanted to see what the Tcl source had to say about IP and whether it was possible to make it iRules compatible. Matt needed three functions out of this effort:
- Normalizing IPv6 addresses
- Compressing IPv6 addresses
- Getting the IPv6 network prefix given a netmask
In the Tcl ip package, these functions, respectively, are ::ip::normalize, ::ip::contract, and ::ip::prefix, and in reviewing the source, they are native Tcl. This is good!
proc ::ip::normalize {ip {Ip4inIp6 0}} {
foreach {ip mask} [SplitIp $ip] break
set version [version $ip]
set s [ToString [Normalize $ip $version] $Ip4inIp6]
if {($version == 6 && $mask != 128) || ($version == 4 && $mask != 32)} {
append s /$mask
}
return $s
}
proc ::ip::contract {ip} {
foreach {ip mask} [SplitIp $ip] break
set version [version $ip]
set s [ToString [Normalize $ip $version]]
if {$version == 6} {
set r ""
foreach o [split $s :] {
append r [format %x: 0x$o]
}
set r [string trimright $r :]
regsub {(?:^|:)0(?::0)+(?::|$)} $r {::} r
} else {
set r [string trimright $s .0]
}
return $r
}
proc ::ip::prefix {ip} {
foreach {addr mask} [SplitIp $ip] break
set version [version $addr]
set addr [Normalize $addr $version]
return [ToString [Mask$version $addr $mask]]
}
Within each command procedure, you'll notice there are many more procedures that are referenced, and thus many more that need to be made compliant. Matt made a mermaid dependency chart for each of the top-level functions he needed. Here's the one for normalize:
With that, it just came down to cleaning up the syntax. With iRules, the use of procedures requires the use of the call command. Honing in on the normalize function, here's the before and after of that single procedure:
### BEFORE ###
proc ::ip::normalize {ip {Ip4inIp6 0}} {
foreach {ip mask} [SplitIp $ip] break
set version [version $ip]
set s [ToString [Normalize $ip $version] $Ip4inIp6]
if {($version == 6 && $mask != 128) || ($version == 4 && $mask != 32)} {
append s /$mask
}
return $s
}
### AFTER ###
proc normalize {ip {Ip4inIp6 0}} {
foreach {ip mask} [call irule_library::SplitIp $ip] break
set version [call irule_library::version $ip]
set s [call irule_library::ToString [call irule_library::Normalize $ip $version] $Ip4inIp6]
if {($version == 6 && $mask != 128) || ($version == 4 && $mask != 32)} {
append s /$mask
}
return $s
}
No heavy lifting required for solid access to time-tested tools developed by the Tcl community! The choice here was to build an iRule called irule_library that will serve as a library of procedures for the necessary ip package functions. If you use partitions, you'll want to add those references to the irule library objects (like /Common/irule_libary::ToString).
You can see the finished work for all the procs and the other dependency graphs in Matt's IPv6 iRule library github repo.
What else would you like to convert from Tcl packages to an iRules-compliant procs library?