Forum Discussion

Chris_15705's avatar
Chris_15705
Icon for Nimbostratus rankNimbostratus
Mar 16, 2012

iRule for Persistent SNAT to nodes on two different VLANS

Hello Everyone. I inherited an interesting problem with a customer's funky switching environment. Without boring you with details, the problem is that they had three switches and in order to create a redundant config, they setup transit routes between switches with a routing protocol to route the subnets that belong to each switch.

 

 

SW1 holds 10.100.1.0/24 VLAN-A

 

SW2 holds 10.100.2.0/24 VLAN-B

 

SW3 holds 10.100.3.0/24 (no nodes in this vlan)

 

 

 

I was able to help them trunk a VLAN for 10.100.4.0/24 for the VIPs.

 

 

 

Now this isn't an issue if i use SNAT Automap but because of the amount of connections I have to use a SNAT pool. When i set up a SNAT pool containing something like 10.100.1.201, 10.100.1.202, 10.100.2.201, 10.100.2.202 the F5 LTM isn't smart enough to know it should only pull a SNAT on VLAN-A for a node on VLAN-A.

 

 

 

I found an iRule for Persistent SNATs that is widely used for Exchange RPC (an Outlook connection is made up of 3 RPC connections). I adjusted the rule to test the subnet of the node selected and pick a proper SNAT. My concern is that because I have moved the processing from the event CLIENT_ACCEPTED to the event LB_SELECTED I will overwhelm the LTM when i add 1000's of clients. During my testing i can see that the LB_SELECTED event happens about every 6 or 7 minutes even if the TCP connection is ESTABLISHED and TIMEOUTS are 72000. I will post my adjusted rule below and link to the original rule here. Any input or thoughts would be very welcome. Alternative ideas to dealing with the SNAT issue are also welcome. Original iRule: EXCHANGE SNAT IRULE

 

 

 

MY iRULE:

 

 

 

when RULE_INIT {

 

 

 

Set your SNAT pool members.

 

 

The list should contain all the same IP addresses as your SNAT pool configuration does.

 

Note that this does not exempt you from applying a SNAT pool at the VS level; you must

 

still do that. This must be done in RULE_INIT until such time as there is a way to get

 

a list of SNAT pool members via an iRule function.

 

 

set static::snpoolA { 10.100.1.201 10.100.1.202 }

 

set static::snpoolB { 10.100.2.201 10.100.2.202 }

 

 

 

If using OPTION 2, uncomment the following to seed the FNV hash.

 

set static::fnv_hash 0x811c9dc5

 

set static::fnv_prime 0x01000193

 

 

 

log "RULE HAS INITIALIZED"

 

 

 

}

 

 

 

when LB_SELECTED {

 

 

-----------

 

OPTION 1

 

Convert the incoming IP to Hex and set the snat pool member based on the modulo of the full IP.

 

 

This option is fine and quite quick for most purposes, but depending on how your organization

 

assigns IP addresses via DHCP may not lead to a high degree of randomness. This could cause more

 

incoming connections to prefer a particular SNAT pool address. Uncomment the following lines to

 

enable it.

 

 

 

set octets [split [IP::remote_addr] .]

 

if { [llength $octets] != 4 } {

 

set octets [lrange [concat $octets 0 0 0] 0 3]

 

}

 

binary scan [binary format c4 $octets] H8 packed_address

 

set packed_address [format 0x%x [expr $packed_address & 0xffffffff]]

 

 

 

----------

 

OPTION 2

 

Generate an FNV 64-bit hash of the remote IP address on a connected client.

 

 

This option requires more CPU time at the F5 but will inject a good degree of randomness

 

in SNAT selection.

 

 

log "The selected server node for mapi is [LB::server addr]"

 

 

if {[IP::addr "[LB::server addr]/24" equals "10.200.1.0/24"]}{

 

 

 

for { set fnv_i 0 } { $fnv_i < [string length [IP::remote_addr]] } { incr fnv_i } {

 

binary scan [IP::remote_addr] @${fnv_i}H2 fnv_str_i

 

set fnv_hash [expr {$static::fnv_hash ^ "0x$fnv_str_i"}]

 

set fnv_hash [expr {$fnv_hash * $static::fnv_prime}]

 

}

 

set packed_address [format 0x%x [expr $fnv_hash & 0xffffffff]]

 

 

 

Select a SNAT based on the modulo of the FNV hash or hex-converted IP address according

 

to the size of the SNAT pool configured above.

 

 

 

snat [lindex $static::snpoolA [eval { expr $packed_address % [llength $static::snpoolA] }]]

 

 

log " Mapi...Selected [lindex $static::snpoolA [eval {expr $packed_address % [llength $static::snpoolA] }]] from pool of size [llength $static::snpoolA] due to findng modulo [eval {expr $packed_address % [llength $static::snpoolA]}] from [IP::remote_addr]"

 

}{

 

 

for { set fnv_i 0 } { $fnv_i < [string length [IP::remote_addr]] } { incr fnv_i } {

 

binary scan [IP::remote_addr] @${fnv_i}H2 fnv_str_i

 

set fnv_hash [expr {$static::fnv_hash ^ "0x$fnv_str_i"}]

 

set fnv_hash [expr {$fnv_hash * $static::fnv_prime}]

 

}

 

set packed_address [format 0x%x [expr $fnv_hash & 0xffffffff]]

 

 

 

Select a SNAT based on the modulo of the FNV hash or hex-converted IP address according

 

to the size of the SNAT pool configured above.

 

 

 

snat [lindex $static::snpoolB [eval { expr $packed_address % [llength $static::snpoolB] }]]

 

 

 

 

 

 

log "Mapi....Selected [lindex $static::snpoolB [eval {expr $packed_address % [llength $static::snpoolB] }]] from pool of size [llength $static::snpoolB] due to findng modulo [eval {expr $packed_address % [llength $static::snpoolB]}] from [IP::remote_addr]"

 

 

}

 

}

 

 

  • I wouldn't have any concerns if the iRule is only triggering every few mins per connection. If you want a lighter weight option which uses crc32 instead of FNV, you could adapt from this one:

     

     

    https://devcentral.f5.com/wiki/iRules.snat_pool_persistence.ashx

     

     

    I'd also suggest opening a case to request this type of persistence be added to LTM. You can reference a related case C1068843 from your case.

     

     

    Aaron
  • Also, if you stick with the above iRule, you can save a few CPU cycles by changing this:

     

     

    if {[IP::addr "[LB::server addr]/24" equals "10.200.1.0/24"]}{

     

     

    to this:

     

     

    if {[IP::addr [LB::server addr] equals 10.200.1.0/24]}{

     

     

    See Jason's article on IP::addr for details:

     

     

    http://devcentral.f5.com/Tutorials/TechTips/tabid/63/articleType/ArticleView/articleId/1086449/iRules-IP-Comparison-Considerations-with-IPaddr-Command.aspx

     

     

    Aaron
  • Thank you Hoolio. This is great feedback and exactly what i was hoping to get. I will test out the suggestions before it goes into production. Sorry about not replying sooner. I thought DevCentral would send me a notice.

     

     

    Chris

     

  • Hi Chris,

     

     

    Good to hear. If you want to add to the requests for a stock option for snat pool persistence, you can open a case with F5 Support and reference BZ384117

     

    .

     

     

    Thanks, Aaron