cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Announcements

AMA-CUCM Troubleshooting: Best Practices for Reading Trace Files

Modify SIP and SDP headers via LUA script for Video

5643
Views
5
Helpful
1
Comments
Beginner

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.

LUA1.png

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


1 Comment
Beginner

Hello,

I'm not familiar with lua scripting, so I would like to ask how function retrieve_endpointinfo works. From where it retrieves info of endpoint.

I need modify media descriptions, if m-line contains RTP/AVP and there is a crypto line - modify from RTP/AVP to RTP/SAVP.

My script Identify Bridge and Endpoint type. If it is a Cisco TelePresence Server 8710 and a Cisco CTS endpoint (x-cisco-tip) endpoint do not perform media conversion (AVP crypto) to (SAVP crypto).

I have script with following function:

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

This function recognizes my non CTS endpoint (Polycom Endpoint with enabled TIP option) as CTS endpoint (by

x-cisco-tip tag, this tag contains also Polycom HDX with TIP enabled) and do not perform media conversion AVP to SAVP. I would like to add another check to script and expand identify of endpoint about another value unique for Polycom and then call function.

local function avp_to_savp(md, cts, tps)

    -- returns modified media descriptions if m-line contains RTP/AVP and there is a crypto line;

    -- otherwise returns nil if the media descrpition was not modified.

    trace.format("DEBUG LUA: avp_to_savp (Initialized)")   

    if not md

    then

        -- media description was NOT modified

        return nil

    end

    if cts and tps

    then

        trace.format("DEBUG LUA: avp_to_savp: Ignore Media Modification")  

        return nil

    end

    local crypto = md:getLine("a=", "crypto")

    if md:match("RTP/AVP") and crypto

    then

        md = md:gsub("RTP/AVP", "RTP/SAVP")

        trace.format("DEBUG LUA: avp_to_savp: Media Modified RTP/AVP to RTP/SAVP")   

        return md

    end

    -- media description was NOT modified

    return nil

end

Could you help me with this?

Thanks

Marek

CreatePlease to create content
Content for Community-Ad
August's Community Spotlight Awards