Forum Discussion

waleed_osama_23's avatar
waleed_osama_23
Icon for Nimbostratus rankNimbostratus
Jul 30, 2016

iRule for Outbound Pool ISP based Load Balancing

Dears, thanks in advance for your help.

We have a customer who has several subnet and each subnet needs to connect to the internet on 2 ISPs.

I am going to do load balancing based on ISPs (ISP1 AND ISP2) and if the user is load-balanced to ISP1 then he should be source natted with IP 1 and if ISP2 then IP 2. And if the source IP of users do not match any IPs, please be source natted with IP in ISP1 Range and go to that ISP.

This is the iRule I made, but I'm facing troubles matching and pool members do not accept the connections,

I think I'm making a serious mistake like doing 2 server side connections per 1 session which cause the page to reset and maybe I have mistake with the iRule that doesn't even let it match well Below is the iRule, I'm open to suggestions to make this easier or improve the mistakes in it, thanks a lot.

Dears, thanks in advance for your help.

We have a customer who has several subnet and each subnet needs to connect to the internet on 2 ISPs.

I am going to do load balancing based on ISPs (ISP1 AND ISP2) and if the user is load-balanced to ISP1 then he should be source natted with IP 1 and if ISP2 then IP 2. And if the source IP of users do not match any IPs, please be source natted with IP in ISP1 Range and go to that ISP (192.168.5.5)

This is the iRule I made, but I'm facing troubles matching and pool members do not accept the connections,

I think I'm making a serious mistake like doing 2 server side connections per 1 session which cause the page to reset and maybe I have mistake with the iRule that doesn't even let it match well Below is the iRule, I'm open to suggestions to make this easier or improve the mistakes in it, thanks a lot.

 

when LB_SELECTED {

        if  {(not([class match [IP::client_addr] equals Group2 ]) &&
(not ([class match [IP::client_addr] equals wirlessEmp2Outside ])) &&
(not ([class match [IP::client_addr] equals wirlessEmp_br2Outside ])) &&
(not ([class match [IP::client_addr] equals Mail2Outside ])) &&
(not ([class match [IP::client_addr] equals wirlessGuest2Outside ])) &&
(not ([class match [IP::client_addr] equals wirlessGuest_br2Outside ])) &&
    (not ([class match [IP::client_addr] equals wirlessTest2Outside ]))     )
}{
            log local0. "IP Not Found in Data Groups for [IP::client_addr] - sending to default ISP1 pool member"
    LB::detach
    pool irule_pool member 192.168.5.5
    snat 6.6.6.6

}
switch [LB::server addr]    { 
192.168.5.5 {
      LB pool was ISP1 , So  select the corresponding SNAT Pool from ISP1 Groups    
     if {[class match [IP::client_addr] equals User2Outside ] || [class match  [IP::client_addr] equals DMZ2Outside ]}
     {
     log local0. "ISP1 source IP 1.1.1.1 was chosen for [IP::client_addr] within User2Outside or  DMZ2Outside" 
     snat 1.1.1.1 
     }
     elseif { [class match [IP::client_addr] equals Group2]}
     {l
     og local0. "ISP1  source IP 2.2.2.2 was chosen for [IP::client_addr] for Group2 "
     snat 2.2.2.2
     }
           }       

192.168.5.6 {
     LB pool was ISP2 , So  select the corresponding SNAT IP from ISP2
    if {[class match [IP::client_addr] equals User2Outside ] || [class match  [IP::client_addr] equals DMZ2Outside ]}
     {
     log local0. "ISP2 source IP 3.3.3.3 was chosen for [IP::client_addr] within User2Outside or  DMZ2Outside" 
     snat 3.3.3.3
     }
     elseif {[class match [IP::client_addr] equals Group2]}
     {log local0. "ISP2 source IP 4.4.4.4 was chosen for [IP::client_addr] for Group2 "
     snat 4.4.4.4
     }
            }
                               }
                }

 

  • don't have time to fully look through, but is there a reason to do this is LB_SELECTED and not earlier, for example CLIENT_ACCEPTED?

     

  • I have thought of that but the action I want to take happens after the pool member has been chosen.

     

    So I don't know how to do that during the CLIENT_ACCEPTED event.

     

  • Hi Waleed,

    to support dynamic outbound SNAT (aka. IP-Mascarading) for your internal Clients, nothing more than a "IP Forwarding" Virtual Server bound to 0.0.0.0/0 and an iRule with certain [snat] commands is required.

    To support multiple ISPs at the same time, you have to mix the [snat] command with the [nexthop] command to forward the traffic either to one or the other ISP (aka. dynamically changing the default gateway).

    In addition to that I would recommend to not chain multiple [class match] commands in a row (each of them will add CPU overhead). Try to use a single datagroup containing the a.) internal subnets, b.) the SNAT IP and c.) the Def-GW of the ISP and then perform a single [class lookup] command to extract and parse this data.

     

    ltm data-group internal DG_MultiISP_SNAT {
        records {
            192.168.0.0/24 {
                    data "1.1.1.11 1.1.1.1"
                }
            192.168.1.0/24 {
                    data "1.1.1.12 1.1.1.1"
                }
            192.168.2.0/24 {
                    data "1.1.1.13 1.1.1.1"
                }
            192.168.3.0/24 {
                    data "1.1.1.14 1.1.1.1"
                }
            192.168.4.0/24 {
                    data "2.2.2.11 2.2.2.1"
                }
            192.168.5.0/24  {
                    data "2.2.2.12 2.2.2.1"
                }
            192.168.6.0/24  {
                    data "2.2.2.13 2.2.2.1"
                }
            192.168.7.0/24  {
                    data "2.2.2.14 2.2.2.1" 
                } 
        }
        type ip
    }
    

     

    Note: The data format of the above datagroup is "snat_ip(space)nexthop_ip"

    Then use the rather simple iRule below, to dynamically set the [snat] and [nexthop] values based on the extracted data group settings.

     

    when CLIENT_ACCEPTED {
        if { not [set DG_MultiISP_SNAT_DATA [class lookup [IP::client_addr] equals DG_MultiISP_SNAT]] eq "" } then {
            log local0. "Source IP \"[lindex $DG_MultiISP_SNAT_DATA 0]\" using Gateway IP \"[lindex $DG_MultiISP_SNAT_DATA 1]\" for Client IP \"[IP::client_addr]\""
            snat [lindex $DG_MultiISP_SNAT_DATA 0]
            netxhop [lindex $DG_MultiISP_SNAT_DATA 1]
        } else {
             Client subnet not found in the datagroup. Using virtual server and route domain default settings to forward the packet.
        }
    }
    

     

    Note: Didn't verified the provided example code. But the chances should be above average, that the code will work for you without any changes. If not, then feel free to correct the left over typos... 😉

    Cheers, Kai

  • I would not use an iRule at all. It seems unnecessary. Try to take advantage of Source IP field in the Virtual Server configuration settings.

     

    1. First, create a working configuration for one ISP, refrain from using any iRules and stick to Virtual Server settings (SNAT pool, Default Pool) . Observing that you have a large number of data groups to compare against (ISP2), it might be best to create that default configuration for ISP2 users. (IP Source setting: 0.0.0.0/0)
    2. Create a second Virtual Server with the same Destination IP but different Source IP to cover for ISP1 users. Create more Virtual Servers as needed if you have more than one Source IP subnets to compare against. (IP Source setting: yourSubnet1, yourSubnet2...)

    Incoming connections will be matched based on the closest-match logic. So if there's a better match to client's IP address than 0.0.0.0/0, a dedicated VS for ISP1 user will get the connection.

     

    • Kai_Wilke's avatar
      Kai_Wilke
      Icon for MVP rankMVP

      Hi Hannes, (long time no reading 😉

      yeah, it would be possible to support this scenario by deploying multiple IP-Forwarding virtual servers (e.g. Mask:192.168.0.0/24 and Mask:192.168.1.0/24) without any iRules. But doing so will most likely also require you to setup different route domains to be able to forward the traffic to the independent ISPs...

      https://support.f5.com/kb/en-us/products/big-ip_ltm/manuals/product/tmos_management_guide_10_1/tmos_route_domains.html

      I guess using a unified Virtual Server (aka. 0.0.0.0/0) with a single iRule attached to control [snat] and [nexthop] is far less complicated then...

       

      Cheers, Kai

       

    • Hannes_Rapp's avatar
      Hannes_Rapp
      Icon for Nimbostratus rankNimbostratus

      I think he probably already has a direct connection to both ISP uplinks covered by SelfIPs. If that's the case, there's no extra work with route domains. It might indeed be better to use one Virtual Server with default settings derived for ISP2 users and a single iRule attached to it which covers the fewer exceptions that apply to ISP1 users. It really depends on how many of those 0.0.0.0/0 Virtuals are needed to get away without iRules. Nevertheless, covering all user-cases as exceptions in an iRule just because it's possible is a no-go!

       

    • Kai_Wilke's avatar
      Kai_Wilke
      Icon for MVP rankMVP

      Using SelfIPs for every ISP wouldn't be sufficient to dynamically flip the nexthop's (aka. the different ISPs Gateways) accordingly to which Virtual Server or SNAT IP was choosen. This would require either certains PBRs (Policy Based Routings) in front of the F5, independent Route-Domains with unique Routing-Tables at the F5 level or an rather simple iRule using the [nexthop] command to dynamically flip beween the ISPs Gateways...

       

      Cheers, Kai