SUPL ILP Message Based Load Balancing with Persistence
Problem this snippet solves:
This rule provides TCP message-based load-balancing for Secure User Plane Location (SUPL) Internal Location Protocol messages. It uses the Session-ID2.SLC_Session_ID as a key for persistence, and assumes that, for all messages, the SLC_Session_ID.SLC_Session part is an IPv4 address.
Code :
########
#
# $VERSION = '1.3'
#
# ILP Message load-balancing. ILP Messages (part of the SUPL suite)
# are described in a PDF document which can be downloaded here:
# http://goo.gl/AR2NIZ
# Each message begins as follows:
# - [2 bytes] Message length in octets
# - [3 bytes] ILP Message version (must be 0x02,0x00,0x00)
# - [variable] Session ID2
#
# The Session ID2 has one, two or three sub-parts, including:
# - SLC Session ID
# - SET Session ID
# - SPC Session ID
#
# The SLC Session ID is mandatory. SET Session ID is optional. The
# SPC Session ID is mandatory except for messages of type PREQ. This
# iRule uses the SLC Session ID only as a persistence key. The SLC
# Session ID is:
# - [4 bytes] Session ID
# - [variable] SLC ID
# The SLC ID sub-part is either an IPv4 address, an IPv6 address or a
# variable length FQDN. This iRule assumes the SLC ID is a four-byte
# IPv4 address, but the iRule can be easily extended to support all three
# types.
#
# ILP is encoded using canonical PER rules, so the actual bitwise encoding is:
#
# 16 bits- message length
# 24 bits- version
# 1 bit- is setSessionID present
# 1 bit- is spcSessionID present
# 32 bits- slcSessionID.sessionID
# 1 bit- is slcSessionID.slcID IP(0) or FQDN(1)
# [ if slcSessionID.slcID is IP: ]
# 1 bit- is slcSessionID.slcID IPv4(0) or IPv6(1)
# [ if slcSessionID.slcID is ipv4: ]
# 32 bits- slcSessionID.ipv4Addr
#
# Since this code assumes slcSessionID.slcID is IPv4, the bit selectors
# are passed over without inspection
#
# Over a single TCP connection, multiple ILP messages may arrive, and
# the messages may have differing Session ID2 values (including differing
# SLC Session IDs). This iRule uses TCP message-based load-balancing
# (mblb) to direct messages to multiple back-end servers (presumably
# SPCs). It ensures that messages with the same SLC Session ID are
# directed to the same SPC. It utilizes the persistence table, so if
# persistence mirroring is enabled, the table entries will be copied
# to a standby LTM and will survive a failover. Messages may span
# multiple TCP segments, and a single TCP segment may contain the
# end of one message and the start of another.
#
# To make this work properly, the mblb profile must be activated on the
# Virtual Server to which this iRule is attached. This must be done
# using tmsh, as follows (in this example, vs-ilp is the name of the
# Virtual Server to which SLCs are sending the ILP messages):
# tmsh modify ltm virtual vs-ilp add profiles { mblb { } }
#
# Because of the message delivery mechanism (TCP::notify eom), this code
# only works on 11.5.0 and beyond.
#
# Potentially useful debugging log statements are commented out. On an
# 11.5.0 VE, this rule averages ~350K cycles for CLIENT_DATA, which
# is where most of the work happens.
#
# For TCP mblb here is a breakdown of what's happening:
# 1. when a new TCP connection completes (SYN/SYN-ACK/ACK on the LTM
# client-side), some variables for the connection are set. The
# iRule is instructed to collect TCP data into the payload buffer,
# then raise CLIENT_DATA when the buffer is ready for processing;
# 2. when CLIENT_DATA is raised, the assumption is that the TCP::payload
# buffer is at the start of an ILP message. The first time this event
# is raised, that is naturally true. Subsequently, this is guaranteed
# because TCP::release is not called until the payload buffer contains
# at least one full message. When TCP::release is called, it only
# releases one message-worth of data at a time;
# 3. initially in CLIENT_DATA, the code verifies that there are at least 14
# bytes of data in the payload buffer. This ensures that there is enough
# data to extract the message length and the slcSessionID. If there is
# not yet 14 bytes, it continues to collect until there is. Once the message
# is extracted, the message persistence entry is added to the persistence table.
# Once the entire message length is collected, the message is delivered,
# which happens when 'TCP::notify eom' is invoked;
# 4. On the serverside, 'TCP::notify eom' is executed when an entire message is
# found. The normal BIG-IP mechanism is to send the response back to the
# matching client-side TCP socket, which is the desired outcome here, so nothing
# special needs to occur.
#
# (2013-Nov-27) Initial Version; Vernon Wells, F5 Networks
# (2014-Jan-17) Version 1.1; Properly accounts for bit selectors in slcSessionID
# (2014-Feb-05) Version 1.2; Use 'TCP::notify eom' rather than 'TCP::notify request'
# and use a while loop to more sensibly extract multiple
# messages from the TCP::payload buffer
# (2014-Feb-08) Version 1.3; Notify of messages from server side
#
# supl_ilp_mblb Copyright (c) 2013, F5 Networks, Inc. All rights reserved.
#
#####
when RULE_INIT {
#set static::im_run 03
}
when CLIENT_ACCEPTED {
TCP::collect
}
when CLIENT_DATA {
#log local0. "{$static::im_run} Entering CLIENT_DATA. payload length = [TCP::payload length]"
while { [TCP::payload length] > 14 } {
#log local0. "{$static::im_run} ... more than 14 bytes remain in stream"
binary scan [TCP::payload] "S1c3Wc" message_length ilp_version ssa ssb
set message_length [expr { $message_length & 0xffff }]
#log local0. "{$static::im_run} ... ... extracted message_length = $message_length"
if { [TCP::payload length] < $message_length } {
#log local0. "{$static::im_run} ... ... more data must be collected to reach message_length"
TCP::collect
return
}
set sessionID [expr { (($ssa << 2) >> 32) & 0xffffffff }]
set ssc [expr { (($ssa << 8) | $ssb) << 26 }]
set slcIP [expr { (($ssc << 3) >> 32) & 0xffffffff }]
set slcSessionID "$sessionID:$slcIP"
#log local0. "{$static::im_run} ... ... slc_session = $slcSessionID ($sessionID, $slcIP)"
persist uie $slcSessionID
TCP::release $message_length
TCP::notify eom
}
TCP::collect
}
when SERVER_CONNECTED {
TCP::collect
}
when SERVER_DATA {
while { [TCP::payload lengt] > 2 } {
binary scan [TCP::payload] "S1" message_length
set message_length [expr { $message_length & 0xffff }]
#log local0. "{$static::im_run} Server side, message_length = $message_length"
if { [TCP::payload length] < $message_length } {
#log local0. "{$static::im_run} ... Server side, more data must be collected to reach message_length"
TCP::collect
return
}
else {
#log local0. "{$static::im_run} ... Server side, message delivered"
TCP::release $message_length
TCP::notify eom
}
}
#log local0. "{$static::im_run} ... Server side, completing iteration of SERVER_DATA"
TCP::collect
}Published Mar 18, 2015
Version 1.0VernonWells
Employee
I am an F5 Solutions Architect for Service Providers in the Americas, focusing on Virtualization, SP protocols (Diameter, GTP, DNS) and programmability.
Prior to joining F5, I worked as a Wi-Fi Network Engineer for a large U.S. public University, as an Operations Engineer, a network management Software Engineer, and as a Technical Trainer.VernonWells
Employee
I am an F5 Solutions Architect for Service Providers in the Americas, focusing on Virtualization, SP protocols (Diameter, GTP, DNS) and programmability.
Prior to joining F5, I worked as a Wi-Fi Network Engineer for a large U.S. public University, as an Operations Engineer, a network management Software Engineer, and as a Technical Trainer.No CommentsBe the first to comment