Securing the Association of the DTLS Certificate With the User’s SIP-URI

.

imagesThe SIP protocol can be used to establish SRTP security using DTLS protocol. The DTLS extension ([RFC 5764]) is used. It describes a mechanism to transport a fingerprint attribute in SDP. So the fingerprint of the self-signed certificate can be inserted by the user agent (UA) in the SDP and sent over SIP to the proxy over an integrity protected channel (carried over TLS transport protocol). The fingerprint in the SDP looks like this:

a=fingerprint:sha-1 99:41:49:83:4a:97:0e:1f:ef:6d:f7:c9:c7:70:9d:1f:66:79:a8:07

Then after the user has been authenticated, the proxy generates a hash where the certificate’s fingerprint and SIP user ID are among others included in the calculation. The proxy signs the hash using its private key and inserts the signature in a new header field in the SIP message (the Identity header field). This secure the association between the DTLS certificate and the user’s SIP URI. The Identity-Info header field helps the verifier (the receiver of the SIP/SDP message) in the verification of the signature included in the Identity header field.

The certificates are being used as a carriers for the public keys and used to authenticate the counterpart and negotiate the session keys (symmetric keys). Then the session keys are used by SRTP to encrypt/decrypt the media. The offerer sends its fingerprint in the request and the answerer sends its fingerprint in the corresponding response after accepting the offer.

Using SIP Identity and Identity-Info

The solution as i mentioned above is using the SIP Identity ([RFC 4474]) to sign the binding of the fingerprint to the user. This is done by the proxy responsible for that user. The proxy is the holder of the private key of its domain. After the user is successfully authenticated, it is authorized to claim the identity (AOR of the user). The proxy creates the signature of the hash using its private key and inserts it in Identity header field. The proxy also inserts the place where the verifier can acquire the proxy’s certificate (public key) using the Identity-Info header field.

Example:

Identity: CyI4+nAkHrH3ntmaxgr01TMxTmtjP7MASwliNRdupRI1vpkXRvZXx1ja9k
3W+v1PDsy32MaqZi0M5WfEkXxbgTnPYW0jIoK8HMyY1VT7egt0kk4XrKFC
HYWGCl0nB2sNsM9CG4hq+YJZTMaSROoMUBhikVIjnQ8ykeD6UXNOyfI=
Identity-Info: https://example.com/cert

Note the part “/cert” in the Identity-Info URL which addresses a certificate.

The Hash Generation

The signature of the hash is added as an Identity header field in the SIP message. The calculation of the hash must contain mainly the AOR of the user and the fingerprint included in the SDP in the body of the message.  According to RFC [4474], the signature/hash is generated from certain components of SIP message, among others:

  • The AoR of the UA sending the message (or addr-spec of the From header field)
  •  The addr-spec component of the Contact header field value.
  • The whole SDP body (the fingerprint is here)
  • …….

Fingerprint Verification

Using the header Identity-Info, the user agent verifies that the fingerprint of the certificate received over the DTLS handshake matches the fingerprint received in the SDP of SIP request/response.


 References


Advertisements

Distributed Hash Table (DHT) In Structured Peer-to-Peer SIP Overlay

.

peersDistributed Hash Table (DHT) is used in structured Peer-to-Peer overlay for resource location and discovery. In SIP world, the resource can be the contact list (physical addresses) associated with an AOR (SIP/SIPS URI that points to a user on domain -virtual address of the user). The mappings between the AORs and the contact lists will be distributed amongst the peers in the SIP overlay. In this way we get a distributed registrar functionality for SIP.

There should be an interface for the DHT so it can be used without caring about the implementation and it can easily integrate with the SIP server so the server can work in P2P mode. Basically the functions that need to be implemented are: Join (join the overlay), Leave (leave the overlay), Lookup (search for a resource using the AOR as a key), and Store (store the resource). For example the lookup function works as following:

  • Using the AOR as a key to find the appropriate node/peer. The hash value of the key is used to find the node/peer ID.
  • Then the selected node/peer will do normal hash table (HT) lookup to get the value/resource/contact-list

The mappings {AOR, Contact List} can be stored persistently on disks in a key-value databases like Berkeley DB.

The IETF standards body is working on P2P-SIP. There is a working group called p2psip where you can find the internet drafts (working documents) related to P2P-SIP. For example the RELOAD (RFC [6940]) is a signaling protocol for resource location and discovery. It specifies “chord-reload” as a mandatory DHT algorithm to be implemented. The purpose of this work is distributed SIP registrar. The RELOAD works with SIP to enable the distributed SIP solution. The RELOAD can be used by other protocols and not only SIP (e.g. A Constrained Application Protocol (CoAP) usage for RELOAD: Internet Draft: draft-jimenez-p2psip-coap-reload-10).

But why P2P SIP mode. This is because in P2P:

  • There is no one point of failure.
  • Less cost: avoid having service provider (Paying) + delete nodes/peers on low demand.
  • Capacity: Adding new nodes/peers on high demand.

The peer to peer SIP overlay is very suitable to be built over Openstack cloud where the automated creation and deletion of nodes/peers is based on predefined policies. The peers are defined in an autoscaling group where they are scaled up and down based on the autoscaling policies. When a node is determined to leave the overlay based on the cloud policies, the node starts the leave process where the configuration is delivered to it from the cloud (DELETE lifecycle hook). The trigger to add (<=> join the overlay) or delete server (<=> leave the overlay) are controlled by the cloud orchestration service (the scaling policies). The policies itself are based on CPU utilization, Load,…etc.


Creative Commons License
The content of this blog is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.


WebSocket as a Transport for SIP

.

Here I just want to write a few words that summarize the RFC 7118:

globe

  • The WebSocket protocol enables message exchange between clients and servers on top of a persistent TCP connection

  • The connection is optionally secured with TLS

  • The WebSocket handshake is based on HTTP and uses the HTTP GET method with an Upgrade request → HTTP 101 response on success .

  • During the connection handshake, the client and server agree on the application-level protocol on top of Websocket transport (Websocket subprotocol) -In this document it is SIP

  • Websocket messages can be sent in either UTF-8 text frames or binary frames. SIP allows both. UTF-8 is recommended for Javascript and WebSocket API

  • Each SIP message must be carried in a single WebSocket message and the WebSocket message contains only one SIP message → simplifies the SIP message parsing – no need for message boundaries (Content-Length header)

  • “ws” is used for plain websocket connections and “wss” for secure Websocket connections. These are for “via” transport parameter and SIP URI transport parameter

  • The SIP WebSocket server may decide not to add the parameter “received” in the top via header. The Client must accept the responses without this parameter

  • The SIP webSocket client is not manadated to implement support of UDP and TCP.

  • “SIP+D2W” DNS NAPTR service value for plain Websocket connections and “SIPS+D2W” for secure websocket connections.

  • The authentication can be on SIP level or Web level (token/cookie is used) – Appendix A

  • Using GRUU is valuable here (must be implemented on the client and server)

  • The Contact URI provided by SIP UAs is not used for routing requests to those UAs.

  • When SIP WebSocket server behaves as edge outbound proxy (Outbound + Path support), the proxy performs loose routing and remains in the path.


More Information


OpenSIPS and SIP PATH Extension

.

Introduction

RFC 3327 (SIP Path Extension) specifies a way to discover and record the sequence of proxies between the UA and the registrar. The constructed routing path will be stored in the database of the registrar along with the corresponding contact address. Each intermediate proxy (NOT terminating routing point) that wants to stay in the routing path, inserts its URI as a new “Path” header field (or as value in the “Path” header field). This mechanism is applied only to requests that are transiting or originating in the home domain. The constructed routing path will be carried as pre-loaded Route set in the upcoming initial requests. This mechanism is similar to the operation of Record-Route mechanism in dialog-initiating requests (In-dialog/Sequential requests) and it can be combined with it (Example – Invite Transaction).

In this article i will talk about SIP Path extension in OpenSIPS context.

Example

Assume we have this scenario:

UA1 – – – – P1 (Outbound Proxy) – – – – P2 – – – – Registrar/Home-Proxy (OpenSIPS) – – – – UA2

The outgoing proxy is the first hop after client’s NAT . The home proxy is a terminal point for SIP routing. The requested path starts by the outgoing proxy and ends at the registrar/home proxy. It is not common that the UA adds a “Path” header field in the request.

OpenSIPS As Registrar/Home-Proxy

OpenSIPS “Registrar” module has “Path” support. When OpenSIPS receives a SIP Register request, it calls the function save(..), among others. Upon calling this function, the values of the Path header(s) along with the contact address will be stored in the database (User Location Table). The contact address will be stored in the column “contact” and the values of Path header(s) will be stored in the column “path”.

In our scenario, if the proxies P1 and P2 receive a Register request and want to be in the path vector, each one adds its own SIP address as a top most value in the “Path” header field and forward the request. When the request reached the registrar, the path vector will be: Path: <sip:P2.testdomain.com;lr>,<sip:P1.testdomain.com;lr>.

The function save(…) takes some flags as a single string parameter. Among others, we have these flags that are related to the “Path” support:

  • p0‘ (OFF Mode): The path vector will be saved in the user location table but it will not be included in the reply of the Register request.
  • p1‘ (Lazy Mode): The path vector will be saved in the user location table and it will be included in the reply only if the UA indicated path support in the request (add the tag “path” as a value in the “Supported” header field of the request: “Supported: path”).
  • p2‘ (Strict Mode): The path vector will be saved in the user location table only if the the UA indicated path support in the registration request (add the tag “path” as a value in the “Supported” header field of the request). If it is not indicated, the request is rejected with “420 – Bad Extension” and the header “Unsupported: path” will be included in the reply along with the received path vector.

The flags above enable “path” support in “registrar” module and specify the mode of this support as well.

According to all above and if the path vector {p2,p1} is sent in the response, the UA can use the path (as preloaded Route set) in the future requests if that is allowed by the policy. In addition to this, any request reached the home proxy can be routed based on that path {p2,p1} which is stored in the database of the registrar.

According to RFC 3327:

The intermediate proxies (not terminating points) should not add a “Path” header field to a request unless the UA add the tag “path” as a value in the “Supported” header field in that request. Here we say the UA indicated support for PATH extension. If the UA indicated path support and the intermediate proxy requires path support in the registrar, then the proxy adds the tag “path” as a value in the “Requires” header field in that request. If the UA has not indicated support for path and the proxy requires support for it in the registrar, the proxy should reject the request with 421 response. This nearly corresponds to using the flag ‘p2’ in the “save” function: save(“location”,”p2″).

The “save(…)” function has also flag ‘v‘ (path received). If this flag is set, the “received” parameter of the first Path URI of a registration {p1} is set as received-uri and the NAT branch flag is set for this contact. This will be explained further down.

To use multiple flags in the “save(…)” function, mix them as one string. Example: save(“location”,”p2v”).

The routing script will have these lines, among others:

….

if (method == “REGISTER”) {

save(“location”, “p2v”);
}

Registrar’s Lookup Operation

When for example UA2 wants to call UA1 (Send Invite request). When the request reached OpenSIPS that operates as a registrar/home proxy, it looks up (Call the function lookup(..)) the AOR of UA1 (UA1@testdomain.com) and returns:

  • Contact = <sip:UA1@192.18.8.10>

  • Path vector= <sip:P2.testdomain.com;lr>,<sip:P1.testdomain.com;lr>

The Request-URI is overwritten by the registered contact address and the Path vector {p2,p1} is inserted as Route set in the same order {p2,p1} in the outgoing Invite request. The topmost Route header field (proxy: p2) is used for routing decision (next hop) in conjunction with the local policy. Calling this function, also sets the destination URI to the first Path URI (outbound proxy: p1).

OpenSIPS As Outbound Proxy with NAT Detection

OpenSIPS has a module named “path” which is designed to be used at intermediate SIP proxies (e.g. outgoing proxy, load balancer,..etc.). Assume we have this scenario:

UA1 – – – NAT – – – P1 (Outbound Proxy -OpenSIPS) – – – P2 – – – Registrar/Home-Proxy

P1 is OpenSIPS that operates as outgoing proxy with NAT detection. To use “path” module, it must be configured in the routing script to be loaded at startup (loadmodule “path.so”). The module “rr” must be loaded before so it must be configured to be loaded before. To insert a path header into Register message, two functions are exported by this module:

  • The function “add_path(…)” which adds a path header in this form “Path: <sip:1.2.3.4;lr>” .The IP address is the IP address of the proxy’s outgoing interface. A port is only added if it is not the default (5060). If it is called with username as parameter, then the username will be added to the URI “Path: <sip:username@1.2.3.4;lr>“.
  • The function “add_path_received(…)” which adds a path header in this form:

Path: <sip:1.2.3.4;received=sip:2.3.4.5:1234;lr>

In our scenario, the outbound proxy p1 will call the function “add_path_received” upon receiving a Register message. The received parameter of its Path URI contains the address where the request has been received (NAT’ed address). This is used to pass the NAT’ed address to the registrar through the next proxy if exists. If it is called with username then the username is added to the URI: “Path: <sip:username@1.2.3.4;received=sip:2.3.4.5:1234;lr>“. Then the proxy will forward the message further to next hop on a way to the registrar.

If the parameter “use_received” exported from “path” module is set to 1 by the outgoing proxy (modparam(“path”, “use_received”, 1), then when the proxy receives a request, the “received” parameter of the its Route URI  (first Route URI ) is evaluated and used as destination URI(route the request to this address).

This behaviour of outbound proxy is considered as complement to registrar behaviour (SIP Path Extension-aware behaviour of Save(…) and Lookup(…)). So Keep the outbound proxy in the constructed path.

The routing script will have these lines, among others:

loadmodule “path.so”

modparam(“path”, “use_received”, 1)

if (method == “REGISTER”) {
if (!add_path_received(“Outbound”))
sl_send_reply(“503”, “Internal Path Error”);

}


Notes

  • SIP “Path” extension is very useful for SIP loadbalancers that are in front of registrars and proxies. As it helps in drawing SIP routing paths.
  • If you like to manipulate the “received” parameter of the Path URI by yourself in the script (not recommended), you can use the script core variables: $Ri which is the IP address of the interface where the request has been received and $Rp which is the port number. Informations like these are detected by the transport of OpenSIPS (under the SIP layer).
  • If you do not understand the structure of the SIP URI. Go Here.

 More Information


Regular Expressions For SIP Trunk

.

Introduction

The regular expression is a sequence of characters that form a search pattern. See the definition in Wikipedia. It is a huge topic and takes a lot of time to explain. If you didn’t use it, you will get stuck in a nested “If” statements situation. Using regular expressions means professionalism. I am assuming you know about it and i will just apply it to SIP.

.

Regular Expressions For a SIP Trunk

Usually when a SIP router receives a SIP request which is addressed to a PSTN gateway (i.e. Its Request-URI contains a telephone number), it checks which group the requested user (called party) belongs to. The router also must check if the caller has permission to make this call. So the check can be done on the caller and calle. Any check done by the router is translated into this simple thinking logic:

If (Routing Condition){

  Do Specific Work

}

This can be in the router’s routing script or internally integrated in the router’s application.

Example

If (The Call Is Addressed To A Local Number){

Do Specific Work

}

In OpenSIPS SIP router, the previous condition can be treated in the routing scripts in two ways:

  • Using the Script Variables (e.g. the reference to the request’s uri ($ru)) with the regular expression directly:

If ($ru=~sip:[2-9][0-9]{6}@){

}

The previous regular expression is for any 7 numbers starting by one digit from the range [2-9].

  • Using a function like “pcre_match” in “Regex” module which matches the given string against a given regular expression. The return value is TRUE if it is matched, FALSE otherwise:

If (pcre_match(“$ru”,sip:[2-9][0-9]{6}@){

}

The variable “$ru” is read/write variable so be aware where in the script you are checking its value. You can use “$ou” which is a reference to the request’s original URI. The module “Regex” is based on the library “PCRE” which is an implementation of regular expression pattern matching where the regular expression parameter will be compiled in PCRE object. So the development libraries of “PCRE” must be installed (“libpcre-dev” or “pcre-devel”).

Note: Use “&&” if you want to concatenate multiple conditions in one “if” statement.

If ((Routing Condition-1) && (Routing Condition-2)){

  Do Specific Work

}

Examples of Regular Expressions For SIP Trunk

You should test your regular expression before using it to know if you have constructed it correctly. There are many online free services that you can use to do your tests. Use search engines to search for syntax symbols that you can use to construct the regular expression. The following are examples. Note that each one defines a group:

  • Any user on any domain/IP: sip:(.*)@(.*)
  • Any user on certain domain: sip:(.*)@mydomain.com
  • Any user on certain IP (198.18.250.10):  sip:(.*)@198\.18\.250\.10 In regular expression, the “.” is interpreted as “any character” symbol whereas “\.” is just a period (dot).
  • Any user on IP Range (198.18.250.0 – 198.18.250.255): sip:(.*)@198\.18\.250.* The symbol “*” is quantifier which means the preceding character is found 0 or more times. Here i left the last part of IP to be anything but you can restrict it.
  • To group a set of SIP URIs that are within a certain domain or certain subdomain, use .*mydomain\.com.* For example these URIs will be matched: sip:mydomain.com:5060;transport=tcp, and sips:test.mydomain.com:5061 SIP/2.0
  • To group a set of SIP URIs that have certain string in the username part of URI and the ports 5060 and 5061 are accepted, use the regular expression .*group1@198\.18\.250.\10:506<01>.* For example these two URIs will be matched sip:serv1group1@198.18.250.10:5060;transport=tcp and sips:serv2group1@198.18.250.10:5061;tranport=tcp
  • 8-digit number on any domain: sip:[0-9]{8}@(.*) The [0-9] is a range for one number between 0 and 9 and {8} means repeat the preceding number 8 times.
  • 8-digit number starting optionally by 8 on any domain: sip:8?[0-9]{7}@(.*)  The symbol “?” means the repetition is 0 or 1 to the preceding. For example sip:80986853@mydomain.com, sip:0986853@mydomain.com, 1234567@mydomain.com, and so on.
  • 4-digit number (could be an extension number) starting by 6 on a certain domain: sip:6[0-9]{3}@(.*)
  • 4-digit number which is not starting by 55 on any domain: sip:(?!55)[0-9]{4}@(.*)

 More Information


OpenSIPS Management Interface (C Development)

.

Introduction

OpenSIPS Management Interface (MI)  is a mechanism which enables the external application (e.g. command line :opensipsctl , web: OpenSIPS-CP) to send commands (MI commands) to OpenSIPS. It is a pull based mechanism which means when you need information you need to query OpenSIPS (i.e. need to do something at a certain time). The MI command allows you to fetch/push data from/to OpenSIPS or trigger some actions there. The core has its own exported MI functions and the module has its own as well. Here you can find some examples of sending mi commands by the external applications “opensipsctl” and “OpenSIPS-CP”

Several transport protocols are used to carry MI commands and their replies between the external application and OpenSIPS. Each protocol is provided by separate OpenSIPS module. The current protocols are mi_fifo, mi_datagram, mi_xmlrpc, mi_http, mi_json, and mi_xmlrpc_ng. These modules require extra processes to avoid disturbing OpenSIPS main processes that are working with SIP.

The module must be loaded (i.e. configured to be loaded in the routing script) so its exported MI functions (exported by the module interface “exports”) are populated and can be called from the external application otherwise you will get an error message “500 command ‘Module_Name’ not available”. To be able to send MI command from the external application, the transport module also must be configured in the routing script to be loaded. For example if you want to connect to MI interface via FIFO file stream, the module MI_FIFO must be right configured and loaded. The same for the rest of transport protocols.

So two modules are needed to be able to call a specific MI function:

  • The module which exports the MI function.
  • The transport protocol which will transport the command to OpenSIPS.

The extra processes that are required for transport issues will be listening on different ports than the SIP ports. OpenSIPS can use multiple transport protocols at the same time (In the routing script, configure them to be loaded).

MI Command Syntax

If you are willing to write an external management application, you have to implement the transport you want to use. Your application will behave like a client which sends MI command in a specific format to OpenSIPS. These are the current formats:

MI Internal Structure

The following is the module_exports structure (Module Interface) defined in the file “sr_module.h” with the parts related to MI.

struct module_exports {.

……… /* Many Fields are Missed For Simplicity */

mi_export_t*     mi_cmds;           /* Array of the exported MI functions */

….

proc_export_t*  procs;     /* Array of the additional processes required by the module */

……

}

procs

Sometime the module needs extra processes like the transport protocol. So it exports

  • The number of required processes to be forked (“no” number).
  • The helper functions (pre_fork_function and post_fork_function) which help the attendant process to create the extra processes.
  • The function which will be executed by the extra processes. What will be done by these extra processes will not interfere with the rest of processes that handle the SIP.

The structure is defined in the file “sr_module.h” in OpenSIPS source directory:

typedef struct cmd_export_ cmd_export_t;

struct proc_export_ {
char *name;
mod_proc_wrapper pre_fork_function;
mod_proc_wrapper post_fork_function;
mod_proc function;
unsigned int no;
unsigned int flags;
};

typedef void (*mod_proc)(int no);

typedef int (*mod_proc_wrapper)();

The flags can be 0 or PROC_FLAG_INITCHILD. If PROC_FLAG_INITCHILD is provided, the function “child_init” from all modules will be run by the new extra processes.

Example of procs NULL terminated array:

static proc_export_t mi_procs[] = {
{“MI Datagram”,  pre_datagram_process,  post_datagram_process,
datagram_process, MI_CHILD_NO, PROC_FLAG_INITCHILD },
{0,0,0,0,0,0}
};
static param_export_t mi_params[] = {
{“children_count”,      INT_PARAM,    &mi_procs[0].no},…….}

mi_cmds

mi_cmds is an array of exported MI function (Type: mi_export_t*). The definition of the type “mi_export_t” is included in the file “mi/mi.h” as following:

typedef struct mi_export_ {
char *name;
char *help;
mi_cmd_f *cmd;
unsigned int flags;
void *param;
mi_child_init_f *init_f;
} mi_export_t;

name is the actual name of the MI function which will be called. help is the information about what this function is doing. cmd is a pointer to the actual mi function:

typedef struct mi_root* (mi_cmd_f)(struct mi_root*, void *param)

The return value is mi_root * (pointer to the root of the MI reply tree).  The first parameter is the MI root parameter(type: mi_root *) and the second parameter is the actual command parameter (void *). the param is the actual parameter of the MI function.

init_f is the child init function for the exported MI function. In some parts, some extra stuffs are needed to be done by the MI workers (processes that are handling the MI requests  like MI_FIFO proces, MI_XMLRPC process,….. This function will be called at the startup after OpenSIPS has been forked (will be called one time by each core process individually).

Example of MI functions NULL terminated array:

static mi_export_t mi_cmds[] = {
{ “mi_get_function“,”This Function is doing bla bla”,mi_get_function,MI_NO_INPUT_FLAG,0,0},
{0,0,0,0,0,0}
}

The string “mi_get_function” is the actual name of the mi command which will be named by when MI command is received and needed to be verified (lookup_mi_cmd(MethodName,..)). The actual function will be like this:

struct mi_root* mi_get_function(struct mi_root*  root, void * param){

……..

}

MI Function Reply Tree

The return value of MI function is a pointer to a tree (reply tree) . The root of this tree is pointer to struct mi_root. This structure is defined in the file “mi/tree.h”:

struct mi_root {
unsigned int  code;
str   reason;
struct mi_handler  * async_hdl;
struct mi_node node;
}

The code is the root code of the response (200 for sucess,500 for error, …) and the node is the starting node (type: mi_node defined in the same file “mi/tree.h”):

struct mi_node {
str value;
str name;
unsigned int flags;
struct mi_node *kids;
struct mi_node *next;
struct mi_node *last;
struct mi_attr *attributes;
}

To initialize MI reply tree in the previous MI function (mi_get_function), the function “init_mi_tree” can be called. To add child node, the function “add_mi_node_child“. To add attribute, the function “add_mi_attr” is called  and so on. Here you can find an example code where a reply tree is built when a MI function is called.

struct mi_root* mi_get_function(struct mi_root*  root, void * param){

………

struct mi_root * rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));    /* 200 OK Reply */

……..

}

The transport process like datagram_process, xmlrpc_process,..etc  behaves as a server which accepts connections (UDP connections in case datagram transport , TCP connections in case xmlrpc connections and so on). To serve the MI request, the transport process calls functions from OpenSIPS core (Management Interface: “mi/mi.h”) in addition to some module-defined functions.

When the message is received, the transport process checks if the requested MI function is available (looks up the MI command):

struct mi_cmd*  fu =lookup_mi_cmd((char*)methodName, strlen(methodName))

The “lookup_mi_cmd” is defined in “mi/mi.h”. If “f==0”, the MI command is not available. Otherwise it parses the parameters of the requested MI function into a MI tree. For example in xmlrpc, the function “xr_parse_tree” is defined in the file “modules/mi_xmlrpc/xr_parser.h” and called in “modules/mi_xmlrpc/xr_server.c”. It returns a pointer to the reply tree (mi_root * t).

After this, the actual mi function will be called through the function “run_mi_cmd”. As i explained above the return value of the MI function (reply tree) has the type mi_root*. This is how it is called in xmlrpc transport module “”.

struct mi_root*  mi_rpl=run_mi_cmd( fu , t , (mi_flush_f*)xr_flush_response,env))

The function “run_mi_cmd” is defined in the file “mi/mi.h” and in its body you call the actual MI function as following:

static inline struct mi_root* run_mi_cmd(struct mi_cmd *cmd, struct mi_root * t, mi_flush_f *f, void *param){

struct mi_root *ret;

…..

ret = cmd->f( t, cmd->param);

…..

}

struct mi_cmd {
int id;
str module;
str name;
str help;
mi_child_init_f *init_f;
mi_cmd_f *f;
unsigned int flags;
void *param;
};

Then the reply tree mi_rpl  (Type: mi_root *) will be formatted corresponded to the transport process (transport protocol). For example if the protocol is xmlrpc, then the reply tree will be formatted in xml either as string that contains the name and attributes of each node OR as an array where each element contains a node information (node name and its attributes). Each attribute has name and value.

Finally, the response will be written in fifo file, UDP socket (datagram transport), TCP socket (xmlrpc transport) so it will be sent to the external application.

Test MI Function of a New Module

You can test the MI command by using opensipsctl as following:

# scripts/opensipsctl fifo mi_get_function


 Note

  • The OpenSIPS core MI exported functions (mi_core_cmds) are defined in the file “mi_core.c”.
  • The OpenSIPS Module MI exported functions (mi_cmds) are defind in the file modules/Module_Name/Module_Name.c
  • OpenSIPS has many transport modules. Go to “modules/mi_Transport“, Where Transport can be datagram, xmlrpc, json,…etc.

 More Information


OpenSIPS Memory Debugging

.

Introduction

If you have problem with the memory on your SIP router, do these:

  • Check things related to the memory management in your operating system. This is explained in the article “Linux Tuning For SIP Routers – Part 3“.
  • Check the memory management in the application of your SIP router.

Here i will give you just an example and you will take this as a way of thinking.

OpenSIPS Memory Debugging

If you got an error message related to OpenSIPS memory (e.g out of memory message), you have to debug it. The OpenSIPS Memory manager (NOT standard) supports debugging so what you can do is enabling the debugging:

  • From the OpenSIPS ‘s src  folder (“/usr/local/src/opensips_1_11″), run: “make menuconfig”. You will get this text based interface:

1

  • Press “ENTER” key to enter in “Configure Compile Flags” item:

2

  • Use the arrow keys to go to the sub-item “F_MALLOC“. Press the SPACE key to uncheck it. Then move to DBG_QM_MALLOC and check it (press SPACE key). Use the option “DBG_QM_MALLOC” in development and the option “F_MALLOC” in deployment because “F_MALLOC” is much faster.
  • Press ‘q’ to go back to the previous screen.
  • Press on “Save Changes”.
  • Press ‘q’ again and then move to the item “Compile and Install OpenSIPS” to compile and install OpenSIPS.
  • Open the configuration file and set memlog=1
  • Restart OpenSIPS and wait OpenSIPS to be completely loaded.

Now everything is ready to get the memory dumps (at shutdown and at runtime).

If you want to get the dumps at runtime:

  • Run “ps aux |grep opensips” to get OpenSIPS processes with their PIDs.
  • Run “kill -SIGUSR1 PID”. Where PID is the ID of the process and SIGUSR1 is the signal name which triggers the memory dump in OpenSIPS. The signals handlers (file: “main.c”) are loaded at the first stage of OpenSIPS startup.
  • Wait some time so the messages for private and shared memory will be dumped.

At shutdown: Stop OpenSIPS and go to the log and get the memory messages.


More Information


 

OpenSIPS Module Interface – Part 1 (C Development)

.

Introduction

OpenSIPS consists of two parts: the core and the modules. OpenSIPS module is a shared library (.so file extension) which is configured in the routing script and loaded at OpenSIPS startup time (the loader: /usr/bin/ld). The name of the module is the name of the shared library.

To open and edit the source files, I will use the text editor vim. You can install it on Fedora Red Hat: # yum install vim Or use your own favourite text editor to edit the source files. Then go to the OpenSIPS src folder (It can be “/usr/local/src/opensips_1_11”) and explore the files (# vim File-Name). To enter in write mode, press i. To paste a copied code into an opened file do it like this: press ESC and then write :set paste. To save a file press ESC and them write :wq

If you are reading in a source file and you want to open a file (i.e. the file where its name comes after the “#include” statement), put the curser on the file name and press either “gf” to go to the file or “ctrl-w f” to open the file in a new window.

In this part i will talk about the definition of the module interface, some fields of its structure, and how to export function from the module to the routing script. So you can call this function from the routing script. Also i will talk about exporting a module parameter.

OpenSIPS Module Interface

OpenSIPS module interface is called module_exports and it is used by OpenSIPS core to serve the module. Each OpenSIPS module creates a variable of this type and call it by “exports”.  When OpenSIPS tries to load the module, it takes the module path from the configuration file (loadmodule statement), open the shared object file (.so file), and try to find the “exports” which populates everything about the module (e.g. script functions, parameters, mi functions, ..etc.).

ModuleInterface

The structure of the module interface (module_exports) is defined in the header file “/usr/local/src/opensips_1_11/sr_module.h”. It is a struct C data type which has the declared name module_exports. It defines a grouped list of fields that are populated by the module. I will explain some of these fields and leave the rest to the next article.

struct module_exports {
.          char* name;                                     /*  Module Name */
.          char* version;                                  /*  Module Version */
.          char* compile_flags;                        /*  Compile flags used on the module */
.          unsigned int dlflags;                          /*  Flags for dlopen */

.          cmd_export_t* cmds;                      /* Array of the exported commands */
.          param_export_t* params;                /* Array of the exported module parameters */

.          …  /*Rest of Fields*/
}

The fields are:

name: It is the module name. If the module will be used, its name must be passed to the function “loadmodule” in the configuration file. The corresponding shared library (shared object) will be “new_module.so” and it will be loaded at the startup time. version: It is the module version. It is used to detect the conflict situations where you have a module with version different from the core version. In the same file “sr_module.h”, we have the defined variable “MODULE_VERSION” which is OPENSIPS full version concatenated with the OpenSIPS compilation flags.

#define MODULE_VERSION  OPENSIPS_FULL_VERSION, OPENSIPS_COMPILE_FLAGS

This variable can be set to the field version in the variable “exports” which has the type module_exports and represents the actual module interface. This is shown further below.

compile_flags: You can skip this if you have used the MODULE_VERSION which already has the compile flags after the comma “,“.  Otherwise use them independently. Remember when you define a variable of struct in c language, the “,”  is used to separate between the fields.

dlflags: In the same file “sr_module.h”, we have  the defined variable DEFAULT_DLFLAGS which has the value 0 which means instructs the module loader to use OpenSIPS default dlopen flags.

#define DEFAULT_DLFLAGS  0

This variable can be set to the field dlflags in the actual interface “exports”.

cmds which has the OpenSIPS-defined C-type cmd_export_t*. It is an array of functions that are exported to the routing script. It is defined in the same file “sr_module.h”

typedef  struct cmd_export_   cmd_export_t;

struct cmd_export_ {
char* name;                                    /* null terminated command name */
cmd_function function;                    /* pointer to the corresponding function */
int param_no;                                  /* number of parameters used by the function */
fixup_function fixup;                      /* pointer to the function called to “fix” the parameters */
free_fixup_function free_fixup;     /* pointer to the function called to free the “fixed” parameters */
int flags;                                         /* Function flags */
}

The structure above defines a function in a dynamic and generic way. The name is the function actual name which will be used from the script when calling the function. The function is a pointer to the C code of the function which will be actually called. This pointer is defined as following:

typedef  int (*cmd_function)(struct sip_msg*, char*, char*, char*, char*, char*, char*);

The return value of the function is integer (int). “cmd_function” is the pointer to the function. The rest are the parameters of the function (First parameter is the actual SIP message which triggers the processing and the rest are multiple char pointers. You can have till sex arguments). In the module C-code, you will have something like this:

int new_function(struct sip_msg *msg,char *param) {

   ……… /* Actual Exported Function Code*/

    /* You can manipulate the SIP message Here:

    /* Remove/Add header Fields …….*/

}

This function has one “char *” parameter but as i said you can have up to 6 “char *” parameters. The return value of the exported function can be either a positive number (higher than +1) on success or lower than -1 on failure. The return value must not be zero because this will exit the script.

The param_no is the number of parameters passed to the function. If this number is equal to zero, this means there are no parameters for the function. But keep in mind that the first parameter (SIP message) will always be received as a first parameter of the C code function even if you call the function with no parameters from the routing script (Example new_function() in the script will be something like new_function(msg) in the C code).

These parameters must be evaluated (get the length of each “char *” parameter) and probably processed. To avoid CPU penalty of doing this evaluation and processing many times (For each function call), the evaluation and processing can be done only once at the OpenSIPS startup via fixup function. This reduces the runtime evaluations.

fixup is a pointer to the function which will be called to fix the parameters of the exported function. The structure of fixup pointer is “fixup_function” which is defined in the file “sr_module.h” as following:

typedef  int (*fixup_function)(void** param, int param_no);

The fixup function will receive the current parameter to be processed (param) and its index (param_no). It is important to check the parameter index so the function knows which parameter it is currently evaluating.  The actual code of the fixup function looks like this:

int do_fixup (void** param, int param_no){

/* Parameter Evaluation and probably Processing */ check the parameter you are currentlu evaluating

char * value= (char *)(*param);

str* ret_value;

 ………

if (param_no==1){

ret_value=pkg_malloc(sizeof(str));

 ……..

ret_value->s=value;

ret_value->len=strlen(value);

*param=(void* )ret_value;

return 1;

}

       …….

return 0;

}

OpenSIPS 1.X has its own internal memory manager (NOT standard) and the function pkg_malloc allocates a memory of a given size (here sizeof(str)) and return a pointer to the allocated memory (here ret_value is a pointer to str).  Note that the received parameter (param) is replaced with the evaluated and processed value (ret_value). After the fixup function has been called, the exported function will have the param fixed (type = str not char*) but casting is still required ((str *)param).

Using fixup function is not restricted to constants. It can be also used with variables.

free_fixup is a pointer to the function which is used to free up the fixed parameters (free up the resources used for the fixed parameters).

flags represents the places in the routing script where the function is available and can be called. For example it can be called from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE, or LOCAL_ROUTE (these routes are defined in the header file “route.h”). If the function is called in forbidden place in the routing script, you will get an error message and OpenSIPS will not start.

The figure below is a graphical representation of the module interface:

The Actual Module Interface

When you create a new module, you need to create a variable of the type struct module_exports which will be the actual interface of the new module. Assume you are writing the module “new_module”, you will add its interface (named “exports”) in its C code. It could be like this:

struct module_exports exports= {
.               “new_module”,                                  /* module name */
.                MODULE_VERSION,                   /* Module Version
.                DEFAULT_DLFLAGS,                  /*  dlopen flags */
.                cmds,                                              /*  exported functions */
.                params,                                           /*  exported parameters */

        …. rest of values

}

How to Export a Function to the Routing Script ?

For example if the module has two functions and you want to export these functions to the routing script, add them to an array of the type cmd_export_t and make the array null terminated ({0,0,0,0,0,0}). As an example assume the first function with one argument and the second function without any argument (same name will be used in the routing script but different names in the C code).

static cmd_export_t cmds[]={

{new_function“,(cmd_function)new_function,1,do_fixup,0, REQUEST_ROUTE|ONREPLY_ROUTE},
{new_function“,(cmd_function)new_function0,0,0,0, REQUEST_ROUTE|ONREPLY_ROUTE},
{0,0,0,0,0,0}
}

In the routing script, these functions can be called only within REQUEST_ROUTE or ONREPLY_ROUTE. Otherwise as i mentioned above, you will get an error messages and OpenSIPS will not start.

The fixup function “do_fixup” is populated here so it will fix up the only one parameter of the exported function “new_function”.

Somewhere in the module C code, You will write the bodies of the actual exported functions: The first function “new_function” which has only one parameter to be passed from the script will look like this:

int new_function(struct sip_msg *msg,char *param){

…….

/* The do_fixup is the fixup function of this function, so we can cast the param to str * as it is evaluated there */

str * param_value = (str *)param; 

……..

}

The second function is new_function0 which has no parameters when it is called from the routing script.

int new_function0(struct sip_msg *msg){

…….

}

How to Export a Parameter to the Routing Script ?

The field “params” in the definition of the module interface is an array of parameters which are exported from the module to the routing script so they can be used in the script and populated by script parser. The exported parameter can be set from the routing script. The OpenSIPS-defined C-Type of the exported parameter is “param_export_t” and it is defined in the file “sr_module.h” as following:

typedef struct param_export_ param_export_t;

struct param_export_ {
char* name;                  /*!< null terminated param. name */
modparam_t type;        /*!< param. type */
void* param_pointer;    /*!< pointer to the param. memory location */
}
The field name is the name of the parameter which will be used in the script. The type can be:

  • A regular string (Flag: STR_PARAM) or regular integer (Flag: INT_PARAM)
  • Or one of the above types (STR_PARAM or  INT_PARAM) combined with the flag USE_FUNC_PARAM (STR_PARAM|USE_FUNC_PARAM) or (INT_PARAM|USE_FUNC_PARAM). This means there is a function will be called every time the parameter is set.

The field param_pointer which is a pointer to either the memory location of the parameter or a pointer to the function which will be called on the value given from the routing script.

All exported parameters will be grouped in “params” in “exports” (actual interface). Remember the OpenSIPS-defined C-Type of the params array is param_export_t*. For example it can be:

static param_export_t params[]={
{ “new_param”, STR_PARAM|USE_FUNC_PARAM, (void *)set_function},
{ “db_url”,     STR_PARAM, &db_url.s},
{0,0,0}
}

The name of the first exported parameter is the string “new_param”. The type of this parameter is “STR_PARAM|USE_FUNC_PARAM” (i.e. “|” is a bit mask).  The function “set_function” will be called each time the parameter “new_param” is set. It look like this:

int set_function(unsigned int type, void *val){

…….

}

In the routing script this parameter can be set as following: modparam(“new_module”,”new_param”,”Test Value”) and the function “set_function” will get the value of “new_param” parameter (“Test Value”) as its argument.


Note

Start playing with OpenSIPS Source code (read in the code, make some changes, write log statements (e.g. LM_INFO(….)), recompile&start OpenSIPS, and read the log and see the results). If you want to build your own module, I suggest you to take as start step the OpenSIPS Example which is available on the OpenSIPS web site and make any OpenSIPS module like dialog module (Files: dialog.c and dialog.h) as a reference to your code (follow the same strategy).


Next

In the Next Part, i will continue explaining the rest of the module interface fields.


 More Information


Strict and Loose Routing of SIP Sequential Requests

.

Strict Routing of SIP Sequential Requests

Sequential requests within a dialog (like ACK, BYE, reINVITE) must take the path determined by record-routing and represented by Route set. In other words strict routing exactly follows the path represented by the Route set. Each strict router on the routing path, will route the SIP message as following: Rewrite the Request-URI with the topmost Route header field value and forward the message to the new Request URI. The Request-URI always contains the URI of the next hop. The next hop can be another SIP router or destination user agent. Strict Routing is legacy approach.

Loose Routing of SIP Sequential Requests

Sequential requests within a dialog should take the path determined by record-routing and represented by Route set. Loose routing of the sequential requests leaves the Request-URI and does not change it.

  • The sequential request has “To-tag” (i.e. belongs to a dialog) whereas the initial request does not have “To-tag” (does not belong to any dialog). The initial request (Method: INVITE, or SUBSCRIBE) creates the dialog.
  • The Request-URI of the sequential request contains the URI of the destination user agent (username@FQDN/IP) whereas the initial request has AOR (username@proxyDomain).
  • Taking a simple case where all routers on the routing path are loose routers and the user agents support loose routing (i.e. The user agent will not insert its URI (user@FQDN) in a Record-Route). According to this, we can say:

The Request-URI of the sequential request will not be changed by any proxy at all because the one who is responsible for the resource associated with this Request-URI is the destination user agent. Whereas the Request-URI of the initial request will be changed once by the proxy which is responsible for the domain in the Request-URI.

The last Route header field value will be for the last proxy in the routing path.

Upon this, the routing procedure will look like following:

The Pre-loaded Route header field values (Route Set in an initial SIP Request) should not be allowed for security reasons.

Note: Transition from strict routing to loose routing would break the compatibility with the older user agents. Avoiding this problem adds lot of overhead and causes problems.

Routing Path (Route Set) in SIP Message

Assume a set of loose proxies {P1 -> P2 -> P3}.

The Route set which will be included in the SIP sequential requests , will look like this:

Route: <sip:p1.domain.com;lr>,<sip:p2.domain.com;lr>,<sip:p3.domain.com;lr>

lr parameter is added to indicate that the router is a loose router. If the SIP router is strict, then the URI will not have the lr parameter.

In the strict routing,the Request-URI is overwritten so we keep the destination user agent (the contact address) in an additional Route header field value.

OpenSIPS Route Processing

In OpenSIPS, I would like to mention two important functions in “rr” module:

  • The function loose_route() which used in routing SIP requests that contain Route Set. For example to route the sequential requests we call this function as following:

if (has_totag()){

loose_route()

 # route it out to whatever destination was set by loose_route()
# in $du (destination URI).
route(relay);

}

If Route set is not empty the loose_route() returns TRUE and the destination uri ($du) is set to the first Route header field value.

  • The function record_route() which used for building the routing path by adding Record-Route header field value upon passing each proxy in a way the initial request follows to reach the destination user agent.

More Information


Homer as a SIP Capturing System – Part 1

.

Definition

homerHomer is a system designed to serve packet capturing purpose.The playing actors in this system are the capturing agent, the capturing server, and the web interface. The capturing agent is a daemon process (i.e. background process) which is running on the system of the routing node (SIP router). When a SIP message reaches the router, the capturing agent on this router takes a copy of this message, encapsulate it in HEP/IPIP packet (add an addition header to the message), and send it to the capturing server. The capturing agent will be configured with the address of the capturing server so it knows exactly where to send the packets. The capturing agent can be an external agent likeCaptagent“.

When the capturing server receives a packet, it decapsulates it (remove the added header), extracts,validate, and parses the SIP message and insert it in the database.The capturing server can also capture the SIP messages directly from the Ethernet (network monitoring connection). This is when a network switch send a copy of networks packets seen on one switch port to a network monitoring connection on another switch port.

The web interface which is connected to the database of the capturing server, loads the captured data  from the database and displays it  in a proper way. The figure below  shows how Homer system looks like:

OpenSIPS as a Homer Capturing Server

OpenSIPS can operate as a Homer capturing server. This is done with SipCapture module which is contributed to OpenSIPS in 2011. To achieve that, load and configure the SipCapture module in the routing script. In the routing script will be no routing for SIP messages but only capturing, processing and storing in the database. So another module must be loaded here which is the database module “db_mysql.so”.

Choosing OpenSIPS to operate as a capturing server is a good choice because of these:

  • Good core performance of OpenSIPS (e.g. Receive Message Handling, SIP Validator, SIP Parser).
  • By using OpenSIPS database interface, the capturing server will support many database servers (e.g. Mysql, Oracle,..).
  • Portability (Can be compiled on different systems).
  • Open source and free.

OpenSIPS as a SIP Router with Integrated Capturing Agent

Here OpenSIPS will operate as a SIP router with an integrated tracing capability (integrated capturing agent). To achieve this, the SipTrace module will be loaded to take a copy of each SIP message (or some of SIP messages) before routing it, encapsulate the message and send it to Homer capturing server. The address of Homer capturing server is configured as a parameter of SipTrace module.

modparam(“siptrace”, “duplicate_uri”, “sip:10.0.0.1:9060”) # The address to send a duplicate of traced message to it.

modparam(“siptrace”, “duplicate_with_hep”, 1)  # Enable HEP

modparam(“siptrace”, “trace_to_database”, 0) # Disable tracing to the local database

I will back to this later.

Note: OpenSIPS can not operate as capturing agent (siptrace module, HEP mode) and capturing server (sipcapture module) at the same time.

Homer Web Interface

The captured SIP messages are stored in the database and they should be displayed in proper way to the user who wants to analyse this data to be able to find any problem and solve it. This can be done through Homer multi-user web interface where you can see the messages displayed in table.You can search , filter, and sort the data. You can export the data as pcap files.  You can visualize the call flows. You can also get statistics. So the user login to the Homer web interface from anywhere and start working with SIP data.


Next:

The Next Part of this article will be about the compilation and installation of SipCapture Module, Setup of Homer Database, Installation of Homer Web interface.


More Information