Monthly Archives: January 2019

SigMa Cookbook (for Avaya SBCE / Sipera SBCs)

Hello Everyone! I’m excited to say I got my hands on an unofficial “SigMa Cookbook” from a buddy of mine at Avaya. This was put together by some engineers at Sipera. This was passed around within Avaya and maybe some customers, but I don’t think it was officially released.

These recipes were put together in 2010 and 2011 for “generic release 4.0.4 Q102 or later”. I do not think there’s official support for this. It was an internal document and was always sent with various warnings about “your mileage will vary”. So you have been warned! If you find any errors and clean up some of these, please let me know and I’ll fix them here.

I have formatted it as HTML for easy searching. I do not know if the engineers want me to include their names. If any of you are from Avaya or Sipera and want attribution for these recipes, please let me know and I’m happy to do it!

And also let me know if you have any good examples we should add. There’s not much of a user community for these SBCs so let’s help each other!

Example 1: P-Asserted-Identity (PAI) Header Manipulation

Use Case:

The P-Asserted-Identity header field can be used to present the identity of the originator of a request within a trusted network. Since the “From” header field is populated by the originating User-Agent it may not contain the actual identity. It usually is established by means of authentication between the originating User-Agent and its outgoing proxy. The outgoing proxy then adds a P-Asserted-Identity header field to assert the identity of the originator to other proxies.

  1. If there is no P-Asserted-Identity header field present, a proxy MAY add one containing at most one SIP or SIPS URI, and at most one tel URL.
  2. If the proxy received the message from an element that it does NOT trust and if there is a P-Asserted-Identity header present, the proxy MUST replace the SIP URI or remove it.

Script:

within session "ALL"  {
    /* 
        Looks into all the messages 
        Message should be a request (act on request) and the messages coming towards the Uc-Sec should be considered,
        i.e. the destination of the message should be Uc-Sec (%DIRECTION="INBOUND").
        The actions are invoked as soon as the message comes from the wire (%ENTRY_POINT="AFTER_NETWORK") 
    */

    act on request where %DIRECTION="INBOUND" and %ENTRY_POINT="AFTER_NETWORK" {
        /*
            Checks if the first P-Asserted-Identity header is present/exists in the message. 
            Each header is represented as %HEADERS["<Header-name>"][<Header position>].
            For headers such as From and Contact, the Header Position is always 1.
            For headers like Via and P-Asserted-Identity, the positions can range from 1 to n
        */

        if(exists(%HEADERS["p-asserted-identity"][1])) then {
            remove(%HEADERS["p-asserted-identity"][1]); //Remove the header
        } else {
            /*
                If the P-Asserted-Identity header is not found in the message
                Add a SIP and a telephone URI.
            */
            %HEADERS["p-asserted-identity"][1] = "12345<sip:12345@192.168.150.150>";
            %HEADERS["p-asserted-identity"][2] = "tel:+14085554000";
        }
    }
}

Additional Description:

The script looks into each message that comes in (since the script acts on all sessions) and checks if

  1. It is a request message
  2. The message is inbound towards the Uc-Sec

If both the above conditions are successful, as soon as the message comes from the wire, (and after the basic sanity checks and DoS checks are performed on the message), the action is performed.  The script checks to see if a “P-Asserted-Identity” header exists.  If it does, it removes it, else it adds the header.

Limitations: 

If you would like to remove all the P-Asserted-Identity headers, it is assumed that you know the maximum number of headers that would be present in the messages.  You do not need to know the exact number of headers that come in because if you perform an operation on a header that does not exist, it is simply ignored.

Note:

If %HEADERS[“<Header-Name>”][<Header Position>] is already present, then the operation

%HEADERS["<Header-Name>"][<Header Position>] = <Val>

will modify the header. If it is not present in the message,

%HEADERS["<Header-Name>"][<Header Position>] = <Val>

will add the header to the message.

Example 2: Addition of a media attribute in SDP

Use Case:

It might be required to add/modify SDP attributes or connection parameters for interoperability.

Script:

within session "INVITE" {
    /*
        Looks into messages in the INVITE session only (It includes all messages in the INVITE dialog)
    */
    
    act on request where %DIRECTION="INBOUND" and %ENTRY_POINT="AFTER_NETWORK" {
        /*
            The "m=" field in SDP contains information about the type of media session. 
            It includes the format-list parameter for specifying the codecs.
            Assuming that the message comes in with 2 codecs, we add a third codec as 101
        */

        %SDP[1]["s"]["m"][1].FORMATS[3]="101";    

        /*
            The "a=" field contains attributes to provide more information on the codecs.
            Assuming that the message does not have any fmtp attribute, we add the first one as 101 0-16
        */

        %SDP[1]["s"]["m"][1].ATTRIBUTES["fmtp"][1]="101 0-16";    
    }
}

Additional Description:

The script looks into all the messages of the “INVITE” session.  A session is defined as a SIP dialog and has the same lifetime as that of a dialog.  A new format-type and an attribute is added corresponding to fmtp.

Limitations:

We would need to know the number of codecs (number of formats in format-list parameter  and attributes), or we would end up replacing an existing format-type.

Example 3: Changing Privacy Header Value

Use Case:

Restricting Calling Party Presentation.

Script:

within session "ALL" {
    act on message where %DIRECTION="INBOUND" and %ENTRY_POINT="AFTER_NETWORK" {
        /*
            Checks if the privacy header value matches with the regular expression given (e.g. "none").
            If it matches, then the privacy header value is changed to "id"
        */

        if(%HEADERS["Privacy"][1] = "none") then {
            %HEADERS["Privacy"][1] = "id";
        }
    }
}

Example 4 – Replace “From” header for a set of users

Use Case:

In an organization, there could be several phones used by the employees and each of them might have a unique “From URI” associated with them.  It may be required that all calls going out from the organization have the same “From URI”.  For this purpose, the following script can be used.

Script:

within session "INVITE" {     
    /* 
        For users whose Uri begins with the prefix 10, when the message comes towards the Uc-Sec, 
        the Uri is changed to “9000”<sip:9000@domain>. So, when the receiver answers the call, 
        the From is 9000.
    */

    act on request where %DIRECTION="INBOUND" and %ENTRY_POINT="AFTER_NETWORK" {
        /*
            A Uri can be represented as "<diplay_name>"<scheme>:<user>@<host>:<port>, 
            eg: "roger"<sip:roger@rogerthephoneguy.com:5060>. 
            URI.USER extracts the user portion of the URI.  
            regex_match tries to match the string against the regular expression. 
            It is of the form <string>.regex_match("<regular expression>"). In this example,
            it is checked if the USER portion in the "From" Header starts with the prefix 10 
        */

        if(%HEADERS["From"][1].URI.USER.regex_match("^10")) then {
            /*
                The uri and display name of the actual user is stored in temporary variables
            */

            %OriginalFromUri = %HEADERS["From"][1].URI.USER;
            %OriginalFromName = %HEADERS["From"][1].DISPLAY_NAME;        
    
            /* 
                The display name and uri is changed to the new values.
            */

            %HEADERS["From"][1].DISPLAY_NAME = "9000";
            %HEADERS["From"][1].URI.USER = "9000";          
        }
    }

    /*
        When the response comes back, we need to change the URI USER and DISPLAY NAME to the actual user. 
        So,before the message is sent out to the wire from the Uc-Sec, it is checked if the URI.USER is 9000.
        If yes, then change it back to the original user's details. 

        Message should be a response (act on response) and the messages going out 
        from the Uc-Sec should be considered (%DIRECTION="INBOUND"). The actions are 
        invoked before the message goes out (%ENTRY_POINT="BEFORE_NETWORK") 
    */

    act on response where %DIRECTION="OUTBOUND" and %ENTRY_POINT="BEFORE_NETWORK" {
        /*
            Check if the user portion of the From URI is 9000
        */

        if(%HEADERS["From"][1].URI.USER = "9000") then {
            /*
                Change the URI.USER and display name to the original user’s details, 
                which are saved in the temporary variables
            */

            %HEADERS["From"][1].URI.USER = %OriginalFromUri;
            %HEADERS["From"][1].DISPLAY_NAME = %OriginalFromName;
        }
    }
}

Additional Description:

The above example shows how to modify a message (request) on its way out and also modify a message (response) when it comes in.

Limitations:

The example illustrates the use of regex_match.  The regular expression provided within the parentheses, i.e. regex_match(<regular expression>), can be any valid Perl regular expression.  However, the “$” symbol (which typically anchors the match to the end of the pattern) cannot be used in the regular expression.

Example 5: Editing the “Allow” Header

Use Case:

The Allow header is used to indicate the methods supported by the user agent, e.g. “Allow: INVITE, ACK, BYE, INFO, OPTIONS, CANCEL”.  The OPTIONS method is used to query a user agent or server about its capabilities and discover its current availability. The response to the request lists the capabilities of the user agent or server.  This may not be desired (probably due to security reasons).  In this case, the Uc-Sec can strip the OPTIONS method from the Allow header before sending out the message.

Script:

within session "INVITE" {

    /*
        Look for INVITE messages only. This is specified with the extra condition 
        %METHOD="INVITE" in the where clause
    */

    act on request where %DIRECTION="INBOUND" and %ENTRY_POINT="AFTER_NETWORK" and %METHOD="INVITE" { 

        /*
            There could be (1) multiple methods in Allow or (2) OPTIONS could be the only method in Allow. 
            If there are multiple methods in Allow, OPTIONS could be (1) in 
            the beginning (2) in the middle, or (3) the end 
        */

        /*
            If OPTIONS is in the middle/end in Allow, it would be of the form 
            Allow:<Methods>,OPTIONS,<More methods> or Allow:<Methods>,OPTIONS. 
            So, we try to match Allow against the regex ",OPTIONS"
        */

        /*
            Note from RogerThePhoneGuy - this example had ,OPTIONS in the comment
            but comma [space] OPTIONS in the regex below. You might want to check
            traces to see if there's a space between the comma and the word OPTIONS
            and make sure you match it in this script!
        */

        if(%HEADERS["Allow"][1].regex_match(",OPTIONS")) then {
            
            /*
                <string1>regex_replace("<regex1>","<string2>") looks for 
                regex1(regular expression) in string1 and replaces it with 
                string2(plain string). Here we replace ",OPTIONS" with
                an empty string, indirectly removing ",OPTIONS"
            */

            %HEADERS["Allow"][1].regex_replace(",OPTIONS",""); 
            /* 
                Note from Roger: The original example had a space between the comma and the word OPTIONS
                but I think that is incorrect so I removed it for this example.
                I do not have an SBC at the moment though - please check your traces!
            /*
        } else {
            /*
                Nested if-else
            */

            /*
                If OPTIONS is in the beginning in Allow, it would be of the form 
                Allow: OPTIONS,<More methods>. So, we try to match Allow 
                against the regex "OPTIONS,"
            */

            if(%HEADERS["Allow"][1].regex_match(" OPTIONS,")) then {
                /*
                    We replace OPTIONS, with an empty string, indirectly removing " OPTIONS,"
                */

                %HEADERS["Allow"][1].regex_replace(" OPTIONS,", "");
            } else {
                /*
                    If OPTIONS is the only method in Allow, it would be of the form 
                    Allow: OPTIONS. So, we try to match Allow against the regex " OPTIONS"
                */

                if(%HEADERS["Allow"][1].regex_match(" OPTIONS")) then {

                    /*
                        Since OPTIONS is the only method in Allow, we remove the entire header
                    */

                    /*
                        remove(%HEADERS["<Header-name>"][<Posn>] removes the header specified in 
                        <Header-name> in Position <Posn>. Here we remove the Allow header
                    */

                    remove(%HEADERS["Allow"][1]);
                }
            }
        }
    } 
}

Additional Description:

The above script can come in handy while operating on headers such as Allow, Supported, Content-Type etc, whose values can’t be extracted individually as compared to headers like “From”, “To”, or “Contact”.

Limitations:

The regular expression in regex_replace can’t have the $ symbol.

Example 6: Prefix Stripping

Use Case:

Phone numbers may come in with a prefix. Sometimes this needs to be stripped off before the call is routed. This is useful in scenarios where a call transfer is made and the number to which the call has to be transferred is entered with a prefix.

Script:

within session "INVITE" {
    /*
        Look for REFER messages only. This is specified with the extra 
        condition %METHOD="REFER" in the where clause
    */

    act on request where %DIRECTION="INBOUND" and %ENTRY_POINT="AFTER_NETWORK" and %METHOD="REFER" {

        /*
            The User portion of the URI in the Refer-To header is checked to 
            see if it starts with the prefix 011. If it does, then it is 
            replaced with an empty string. If URI.USER does not 
            match the regex, then the action is ignored and the message is left intact.
        */

        %HEADERS["Refer-To"][1].URI.USER.regex_replace("^011","");
    }
}

Additional Description:

The messages which have the “Refer-To” method are checked if the URI contains a prefix.  If so, it is stripped before sending it out.

Limitations:

The regular expression in regex_replace can’t have the $ symbol.

Example 7: Diversion Header Addition and phone context removal

Use Case:

The user may want the option to add a diversion header or manipulate the extension field or change the URI parameters in the header. This example also illustrates combining two functions such as addition of a diversion header only to the INVITE and the removal of phone-context from all outbound messages.

Script:

within session "ALL" //Looks into all the messages {
	act on message where %DIRECTION="OUTBOUND" and %ENTRY_POINT="POST_ROUTING" {

		/* 
			Any message that is outbound from the UC-Sec is checked 
		*/

		/*
			Remove phone-context from Request Line
		*/

		remove(%HEADERS["Request_Line"][1].PARAMS["phone-context"]);

		/*
			Remove phone-context from the From header
		*/

		remove(%HEADERS["From"][1].PARAMS["phone-context"]);

		/*
			Remove phone-context from the To header
		*/

		remove(%HEADERS["To"][1].PARAMS["phone-context"]); 

		if (%INITIAL_REQUEST = "true") then {

			/*
				Add a diversion header
			*/

			%HEADERS["Diversion"][1] = "sip:3145552375@rogerthephoneguy.com";

		}
	}
}

Example 8: Redirection

Use Case:

The user may require to populate the  From header in the outbound INVITE message with the contents of the Diversion header in the incoming INVITE.

Script:

within session "ALL" {
	act on message where %DIRECTION="OUTBOUND" and %ENTRY_POINT="POST_ROUTING" and %METHOD="INVITE" {

		/*
			Store the contents of diversion header URI
		*/

		%Div = %HEADERS["Diversion"][1].URI;

		/*
			Replace the contents of From header with stored value
		*/

		%HEADERS["From"][1].URI  = %Div;
	}
}

Example 9: Invite XML Body Manipulation

Use Case:

The user may require manipulation of the contents of the xml body present in the INVITE message.

Script:

within session "INVITE" {     
    act on request where %DIRECTION="OUTBOUND" and %ENTRY_POINT="POST_ROUTING" {

    	/*
    		Check the body for the regex value 
    	*/

        if(%BODY[1].regex_match("sip:2911@192.168.3.150")) then {
        	/* 
        		Replace the matching string with sip:anonymous@anonymous.com 
        	*/   
            %BODY[1].regex_replace("sip:2911@192.168.3.150","sip:anonymous@anonymous.com");   
        }
    }
}

Example 10: Switching From and To tags

Use Case:

The user may require to switch tags on the From and To in a particular message. The following example illustrates tag switching in the 200 OK message.

Script:

within session "REGISTER" {
    act on response where %DIRECTION="INBOUND" and %ENTRY_POINT="AFTER_NETWORK" and %RESP_CODE="200" {
        %from = %HEADERS["From"][1].PARAMS["tag"];
        %HEADERS["From"][1].PARAMS["tag"] = %HEADERS["To"][1].PARAMS["tag"];
        %HEADERS["To"][1].PARAMS["tag"] = %from;
    }
}

Note from Roger: Does anyone know why you’d want to swap the from and to? This was the 10th example in the doc but I cannot think of why this would be useful.