Forum Discussion
David_Remington
Employee
Feb 28, 2008Interesting Class Behavior
So I am writing an iRule that essentially needs to parse an order list and exit processing on the first match.
However I have discovered that the order of the individual elements of the class memory structure is not identical to the order the elements are listed in the class file.
Is this the expected behavior? I've confirmed on 9.3.1 and 9.4.3.
Here is an example class:
class cl_snatacl {
type string
filename "/config/snatacl.class"
mode read
}
classfile contents:
"1 tcp 216.17.29.0/24 any 172.20.0.0/16 80",
"2 tcp 12.214.195.170/32 any any any",
"3 tcp any any any any",
Here is a the meat of the iRule that does the parsing:
rule selective_lb.v0.1-tcp {
This irule reads a class file in the format of
"rulenum proto srcip srcport dstip dstport",
In the acl, 'any' may be used to match any value.
Port support is only included for tcp and udp,
however other protocols should list 'any' in the
port fields.
The rule also requires a class file listing the
assigned IP protocol numbers.
This rule does not support non-IP traffic.
Connections which match the acl are load-balanced
and snat'd using snat automap and persisted.
when CLIENT_ACCEPTED {
debug levels: 0 - silent, 1 - success/fail, 2 - verbose
set debug 1
if {$debug > 1} {log local0. "IP protocol - [IP::protocol]"}
if { [IP::protocol] < 140 } {
set protoname [string tolower [findclass [IP::protocol] $::cl_ipproto " "]]
if { $debug > 1} {log local0.notice "IP protocol name - $protoname"}
} else {
set protoname unassigned
}
foreach {rule proto srcip srcport dstip dstport} [split $::cl_snatacl " "] {
set rule [string trimleft $rule "{"]
set dstport [string trimright $dstport "}"]
set acl "$rule $proto $srcip:$srcport->$dstip:$dstport"
if { $debug > 1} { log local0.notice "ACL : $rule $proto $srcip $srcport $dstip $dstport" }
if { $proto == $protoname or $proto == "any"} {
switch $protoname {
tcp {
set thisconn "$protoname [IP::client_addr]:[TCP::client_port]->[IP::local_addr]:[TCP::local_port]"
if { $debug > 1} {log local0.notice "Comparing ACL: $acl to Connection: $thisconn" }
if { $srcip == "any"} { set srcip [IP::client_addr] }
if { $srcport == "any"} { set srcport [TCP::client_port] }
if { $dstip == "any"} { set dstip [IP::local_addr] }
if { $dstport == "any"} { set dstport [TCP::local_port] }
if { $debug > 1} {log local0. "ACL after any substitutions: $rule $proto $srcip:$srcport->$dstip:$dstp
if { [IP::addr [IP::client_addr] equals $srcip] and $srcport == [TCP::client_port] and [IP::addr [IP::local_addr] equals $dstip] and $dstport == [TCP::local_port] } {
if { $debug > 0 } {log local0.info "Connection $thisconn matched ACL $acl."}
persist uie "$proto.$srcip.$dstip" 300
snat automap
pool lb_links
break
} else {
if { $debug > 0} {log local0.notice "Connection $thisconn did not match ACL $acl"}
}
}
default {
set thisconn "$protoname [IP::client_addr]->[IP::local_addr]"
if { $debug > 1} {log local0.notice "Comparing ACL: $acl to Connection: $thisconn" }
if { $srcip == "any"} { set srcip [IP::client_addr] }
if { $dstip == "any"} { set dstip [IP::local_addr] }
if { $debug > 1} {log local0. "ACL after any substitutions: $rule $proto $srcip->$dstip"}
if { [IP::addr [IP::client_addr] equals $srcip] and [IP::addr [IP::local_addr] equals $dstip] } {
if { $debug > 0 } {log local0.info "Connection $thisconn matched ACL $acl."}
persist uie "$proto.$srcip.$dstip" 300
snat automap
pool lb_links
break
} else {
if { $debug > 0} {log local0.notice "Connection $thisconn did not match ACL $acl"}
}
}
}
}
}
}
}
Here is the logging output of a connection attempt:
Feb 28 13:17:08 tmm tmm[19965]: Rule selective_lb.v0.1-tcp : Connection tcp 12.214.195.170:33618->172.20.1.111:80 did not match ACL 1 tcp 216.17.29.0/24:any->172.20.0.0/16:80
Feb 28 13:17:08 tmm tmm[19965]: Rule selective_lb.v0.1-tcp : Connection tcp 12.214.195.170:33618->172.20.1.111:80 matched ACL 3 tcp any:any->any:any.
I caused this behavior by putting in the three elements and doing a 'b load', then going back and editing element '2' and loading again. That apparently causes the order in memory to be 1 3 2, which is bad.
As you can see, the second element in the class should have been the one matched and element 3 should not have even been processed.
Do I need to load the class into a list ordered by rule number in RULE_INIT or is there some option on the classfile I can specify that tells the LTM to use the class in the exact order in the file?
(note this rule is not pretty atm, nor complete, for those like me who troll devcentral for code snippets.. I'm in the "make it work" phase of the project!)
Thanks!
- David_Remington
Employee
Added info. I added a log statement to dump the output of [split $::cl_snatacl " "] and this is what it shows: - David_Remington
Employee
No ideas? Is this a bug, feature or is there an option I am missing? - David_Remington
Employee
Thanks for the comment. I assume that lsort is going to want my ACL numbers to be 001 002 003 .... 999, right? - hoolio
Cirrostratus
You can specify how you want the sort done, so you wouldn't necessarily need to pad the numbers:% set my_list {"1 aa" "2 bb" "10 jj"} "1 aa" "2 bb" "10 jj" % lsort $my_list {1 aa} {10 jj} {2 bb} % lsort -dictionary $my_list {1 aa} {2 bb} {10 jj}
Recent Discussions
Related Content
DevCentral Quicklinks
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com
Discover DevCentral Connects