For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

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
No CommentsBe the first to comment