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.0

Was this article helpful?

No CommentsBe the first to comment