Background
Cisco introduced in their main call control platforms LUA language. Lua is a non-proprietary, lightweight scripting language.
Lua was created back in 1993 at the Pontifical Catholic University of Rio de Janeiro, in Brazil. Lua has a deserved reputation for performance which makes it a great choice when processing real time traffic in the form of text.
For Cisco Unified Communications Manager introduce SIP normalization and transparency feature, where you can modify SIP headers and SDP values.
In Cisco ASR SBC modification for SDP is available via LUA. Starting Cisco SBC 3.3S version SIP editors feature was introduced and allowed.
More information:
http://www.cisco.com/en/US/docs/voice_ip_comm/cucm/sip_tn/8_5_1/3-sip_msg.html
http://www.cisco.com/en/US/docs/routers/asr1000/configuration/guide/sbcu/sbc_sip_profiles.html#wp1156962
Development
When creating LUA script please refer to the Programming guide for CUCM and SBC.
In Development guide you will have access to built-in API which will give you full functionality and access to SIP messages.
CUCM LUA
In this example we will obtain the type of endpoint information for future processing.
-- function identifyEndpoint
local function identifyEndpoint(msg)
trace.format("DEBUG LUA: identifyEndpoint(Initialized)")
local contact = msg:getHeader("Contact")
local cts = nil
trace.format("DEBUG LUA: identifyEndpoint: Contact header value is %s", contact)
cts = string.find(contact,"-tip",1)
if cts
then
trace.format("DEBUG LUA: identifyEndpoint: A Cisco-Telepresence product supporting x-cisco-tip is part of the call and is being identified")
local context = msg:getContext()
if context
then
context["Contact"] = contact
end
return true
end
trace.format("DEBUG LUA: identifyEndpoint: WARN: A Cisco-Telepresence product not supporting x-cisco-tip is part of the call!")
return false
end
-- function retrieve_endpointinfo
local function retrieve_endpointinfo(msg)
trace.format("DEBUG LUA: retrieve_endpointinfo (Initialized)")
local context = msg:getContext()
if context
then
local endpoint_history = context["Contact"]
if endpoint_history
then
trace.format("DEBUG LUA: retrieve_endpointinfo: A Cisco-Telepresence: x-cisco-tip is part of the call ")
return true
end
end
return false
end
-- Obtain SDP
local function getMediaDescriptions(sdp)
-- initialize table of media descriptions
if not sdp
then
-- there is no SDP
return
end
local mds = {}
-- there won't be more than 10 media descrpitions; loop below exits on the first index
-- that returns nil; that will be the one after the last valid index
for i = 1, 10
do
mds[i] = sdp:getMediaDescription(i)
if not mds[i]
then
-- the last one we got was the last media description in this SDP; exit the loop
break
end
end
return mds
end
-- In this function when Initial Invite is received process_outbound_SDP function is called
local function process_outbound_SDP(msg, isInvite)
local sdp = msg:getSdp()
trace.format("DEBUG LUA: process_outbound_SDP")
if isInvite
then
if msg:isInitialInviteRequest()
then
-- identify x-cisco-tip endpoint
identifyEndpoint(msg)
-- get a table (indexed by media level) of the media descriptions
local mds = getMediaDescriptions(sdp)
sdp = outbound_srtp_fallback(msg, sdp, mds)
end
end
-- remove application media description if it doesn't contain payloads
sdp = remove_malformed_media_description(sdp)
if sdp
then
msg:setSdp(sdp)
end
end
-- Obtain SDP media information
local function getMediaDescriptions(sdp)
-- initialize table of media descriptions
if not sdp
then
-- there is no SDP
return
end
local mds = {}
-- there won't be more than 10 media descrpitions; loop below exits on the first index
-- that returns nil; that will be the one after the last valid index
for i = 1, 10
do
mds[i] = sdp:getMediaDescription(i)
if not mds[i]
then
-- the last one we got was the last media description in this SDP; exit the loop
break
end
end
return mds
end
-- Remove bad media
local function remove_malformed_media_description(sdp)
if not sdp
then
-- there is no SDP
return
end
local mds = getMediaDescriptions(sdp)
local malformed_index = 0
for i, md in ipairs(mds)
do
if md:match("m=application %d+ RTP/AVP\r?\n")
then
malformed_index = i
break
end
end
if malformed_index > 0
then
return sdp:removeMediaDescription(malformed_index)
end
return sdp
end
-- Call function
local function process_outbound_request(msg, isInvite)
process_outbound_SDP(msg, isInvite)
end
-- Process initial outgoing INVITE
M.outbound_INVITE = function(msg)
process_outbound_request(msg, true)
end
return M
-- Create a new function and retrieve_endpoint information from table if you need it. This function you can call it when a response comes in, example 200 OK, this may need extra modification in the bottom rules.
local cts = retrieve_endpointinfo(msg)
-- Example
local function process_info(msg)
local sdp = msg:getSdp()
if not sdp
then
-- there is no inbound SDP
return
end
-- During Initial Invite CTS is able to be sent to a CTMS/TPS or TIP capable bridge
local context = msg:getContext()
-- get a table (indexed by media level) of the media descriptions
local cts = retrieve_endpointinfo(msg)
if cts
then
trace.format("DEBUG LUA: CTS found")
return nil
end
return nil
end
SBC LUA
We are going to add a function that works with default LUA script for B2BUA which verifies if Best Effort is used from remote end and performs internal conversion, our LUA checks for this and deletes crypto caps
MeLogger.debug("LUA initialized" .. "\n")
local avp="RTP/AVP"
local xcrypto="a=xcrypto:"
local origRTP="a=origRTP"
-- Function remove_crypto
function remove_crypto(msg)
for m in msg.sdp.media_blocks:iter() do
local i,j = m.media_lines[1]:find(avp)
if i then
-- if media stream is RTP this might be two cases:
-- 1: sdp comes from wire as RTP. (TPS has no encryption enabled) 2, sdp is changed by inbound editor to RTP (TPS has encryption enabled)
-- check inbound sdp rtp marker
local oRTP=m:select_by_prefix(origRTP)
-- No encryption in TPS: SDP was changed by inbound sdp editor to origRTP if no crypto found.
-- Encryption in TPS: Delete crypto caps from SDP. Remove xcrypto if it was modified previously
if oRTP:empty() then
for a in m:select_by_prefix(xcrypto):iter() do
a:delete()
end
else
for b in m:select_by_prefix(origRTP):iter() do
b:delete()
end
end
end
-- Delete remaining OrigRTP in BFCP
for c in msg.sdp:select_by_prefix(origRTP):iter() do
c:delete()
end
end
end
MeEditor.register("after_send","remove_crypto",remove_crypto)
In SBC
Step 1. Upload SBC script
Step 2. Add script to configuration
sbc asr-sbc
sbe
script mylua
filename bootflash:myscript.lua
load-order 300
type full
Step 3. Configure SBC adjacency
editor-list after-send
editor 1 remove_crypto
Configuration
In order to test your SBC LUA script before using it in production you need to load a sample text file which you can use to emulate to test the LUA functionality. Please follow the next steps to use the test functionality.
1. Using wireshark obtain packet capture of SIP message you need to modify.
2. Using wireshark select SIP as raw text and Hide \r\n characters option
Edit | Preferences | Protocols | SIP |
Display raw text for SIP message.
Enable "Don't show \r\n in raw SIP messages.
3. Export SIP text
File | Export packet dissections as 'Plain Text' file.
4. Edit file and remove Headers
- Clean Wireshark packet info:
No. Time Source Destination Protocol Length Info
Frame 9:
Ethernet II
Internet Protocol Version 4
Transmission Control Protocol, Src Port: 37550 (37550), Dst Port: sip (5060), Seq: 4289, Ack: 1, Len: 417
[9 Reassembled TCP Segments (4705 bytes): #1(536), #2(536), #3(536), #4(536), #5(536), #6(536), #7(536), #8(536), #9(417)]
Session Initiation Protocol
Session Initiation Protocol (SIP as raw text)
- Make sure first line in File is SIP URI: INVITE sip:gogasca-sys-83000068-51@2.2.2.2:5060 SIP/2.0
- If using a Windows machine verify at the end of each line you can see the CRLF.
- Remove the spaces at the beginning of each line
- Remove the spaces at the end of the line, the /r/n not displayed may generate some info which can cause SBC not to process info.
5. Upload you file to the SBC bootflash:
6. Add you script-set editor into SBC config.
sbc asr-sbc
sbe
secure-media
script-set 1 lua
script attlabs
filename bootflash:attlabs.lua
load-order 100
type full
complete
After entering complete, verify no errors are generated.
7. Test script
test sbc message sip filename bootflash:invite_sample.txt script-set 2 after-send editors att_remove
invite_sample.txt is the text file containing your SIP message
after_send is the edit point:
edit_point—Accepts one of the following values:
–AFTER_SEND
—Specifies that the outgoing message must be edited after it is processed by the adjacency and just before it is forwarded from the adjacency.
–BEFORE_RECEIVE
—Specifies that the incoming message must be edited just after it is received on the adjacency and before the adjacency begins processing it
att_remove is the editor name.
editor_name—Specifies the name that you want to assign to the editor.
Debugging
You can use in CUCM LUA the following function and enable Debug in SIP Trunk itself.
trace.enable()
Please take a look at:
http://www.cisco.com/en/US/docs/voice_ip_comm/cucm/sip_tn/8_5_1/8-trace.html
Once you enable LUA debugging you can collect CUCM SDL traces and see changes. Example:
08158122.000 |13:20:49.052 |SdlSig |SIPNormalizeReq |wait |SIPNormalization(1,100,71,1) |SIPHandler(1,100,72,1) |1,100,17,143.4^10.250.1.98^* |*TraceFlagOverrode
08158122.001 |13:20:49.052 |AppInfo |//SIP/SIPNormalization/trace_sip_message: After inbound (no change) SIP Normalization msg is:
For SBC please find previous post in how to enable traces to recover LUA logs, make sure your script uses
MeLogger.debug method.
MeLogger.debug("LUA initialized" .. "\n")
You will see a message like this
CORE: [LUA 1 DEBUG] LUA initialized