Hostname based Load balancing (prior to 11.6.0)

Problem this snippet solves:

This code is initially developed to provide a forward proxy to Apple Push Notification Services. This code can easily be customized to load balance requests to any resolved FQDN.

Several issues are solved here :

  • Forward proxy for several Apple APNs services like gateway.push.apple.com and feedback.push.apple.com
  • Hostname based loadbalancing for BIG-IP under version 11.6.0
  • Source address persistence using tables (because when using the node command, persistence settings doesn't works)

How to use this snippet:

Installation

This irule can be installed on multiple VS at the same time or on a wilcard VS.

During several tests, we configured the following Virtual Server settings :

ltm virtual vs_wildcard_apns {
    description *.push.apple.com
    destination 192.168.20.200:any
    ip-protocol tcp
    mask 255.255.255.255
    profiles {
        fastL4 { }
    }
    rules {
        irule_apns
    }
    source 0.0.0.0/0
    source-address-translation {
        type automap
    }
    translate-address enabled
    translate-port disabled
    vs-index 9
}

Code :

when RULE_INIT {
    set static::nameserver "8.8.8.8"
    set static::max_age 1800
}
 
when CLIENT_ACCEPTED {
  switch [TCP::local_port] {
    "2195" {
        set host "gateway.push.apple.com"
    }
    "2196" {
        set host "feedback.push.apple.com"
    }
    default {
        log local0. "virtual=[virtual], src=[IP::client_addr], dst=$dest:[TCP::local_port], action=reject, reason=\"Request not allowed\""
        reject
        return
    }
  }
  
  # resolve the hostname using table or dns lookup
  if { [table lookup "$host.[IP::client_addr]"] ne "" } {
    set dest [table lookup "$host.[IP::client_addr]"]
    log local0. "virtual=[virtual], src=[IP::client_addr], dst=$dest:[TCP::local_port], action=resolve, reason=\"from table\""
    node $dest [TCP::local_port]
  
  } else {
    set dest [lindex [RESOLV::lookup @$static::nameserver -a $host] 0]
    log local0. "virtual=[virtual], src=[IP::client_addr], dst=$dest:[TCP::local_port], action=resolve, reason=\"from dns\""
    
    if { $dest ne "" } {
        table set "$host.[IP::client_addr]" "$dest" indefinite $static::max_age
        node $dest [TCP::local_port]
        log local0. "virtual=[virtual], src=[IP::client_addr], dst=$dest:[TCP::local_port], action=forward"
  
    } else {
        log local0. "virtual=[virtual], src=[IP::client_addr], dst=$dest:[TCP::local_port], action=reject, reason=\"DNS resolution failed\""
        reject
        return
    }
  }
}
 
when CLIENT_CLOSED {
   table delete conn:[IP::client_addr]:[TCP::client_port]
}

Tested this on version:

11.3
Updated Jun 06, 2023
Version 2.0
No CommentsBe the first to comment