David_Remington
Feb 28, 2008Employee
Interesting 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!