OpenSIPS Event Interface – Part 1

.

 

External applications can interact with OpenSIPS through Management Interface (MI) which is pull-based mechanism and Event Interface which is push-based mechanism. The event interface acts as a mediator between OpenSIPS regular modules, the configuration script and the transport modules. The events can be published by the modules and the configuration file/script. The external applications subscribes for a certain event and OpenSIPS event interface notifies the external application when that event is raised. OpenSIPS has some hardcoded events such as E_PIKE_BLOCKED which is raised by the “Pike” module), E_CORE_PKG_THRESHOLD which is raised when private memory usage exceeded a threshold,…..etc. Several transport protocols are used to send the event notification to the external subscribed application. Each protocol is provided by separate OpenSIPS module.

Events Raised from OpenSIPS Modules

If the module wants to export events, it must register them in its initialization function (init_function” member in the module interface “struct module_exports”). The function “evi_publish_event” defined in “evi/evi_modules.h” is used to register an event. The function “evi_raise_event” is used to raise an event. The function “evi_probe_event” checks if there is any subscriber for that event. This function is called before building parameters list because if no subscriber has been found, then the delay of building parameters list and other things can be skipped.

event_id_t evi_publish_event(str event_name);

int evi_raise_event(event_id_t id, evi_params_t* params);

int evi_probe_event(event_id_t id);

Events Raised from OpenSIPS Script

To raise an event from script, you have to call the core function “raise_event” from the script. The first parameter of this function is the event name and it is constant string parameter (pseudo variable cannot be used) because the event must be registered at startup and the pseudo variable would have an undefined value. The second and third parameters form together the list of the parameters and they are optional. This is how to call this function:

$avp(attr-name) = “param1”;

$avp(attr-name) = “param2”;

$avp(attr-val) = 1;

$avp(attr-val) = “2”

raise_event(“E_Example”, $avp(attr-name), $avp(attr-val));

Click here to see the full explanation.

The length of both AVPs have the same number of parameters.

Transport Module Interface

This interface is implemented by all transport modules that are used to send the event notification to the external applications. It is defined in the file “evi/evi_transport.h” as following:

typedef struct evi_export_ {
str proto;                      /* protocol name / transport module name */
raise_f *raise;         /* raise function */
parse_f *parse;         /* parse function */
match_f *match;         /* sockets match function */
free_f *free;           /* free a socket */
print_f *print;         /* prints a socket */
unsigned int flags;
} evi_export_t;

The first field is the name of the transport protocol. It is used by the MI subscription command so the event notification will be sent by the transport named in the subscription request. The second filed is the raise function which will be called by the OpenSIPS Event Interface for each subscriber, to send the notification on this transport. This function serializes the event parameters, and send them to the subscriber. The event parameters are passed to the transport module from the module that raised the event.

The third field is the parse function which parses the subscriber socket. The match function is used to update the expire period of the subscription or unsubscribe the application. The rest of fields are easy to understand.

The transport module which implement this interface must be registered to OpenSIPS event interface. This is can be done by calling the function with the an instance of the above structure as a parameter:

int register_event_mod(evi_export_t *ev);

The Availability of the Events

All exported events will be available at startup time after the parsing of the configuration file is completed. The external application can only subscribe to en event after OpenSIPS has been started up.

Event Subscription

To subscribe for events, the external application send this MI subscription command:

event_subscribe Event_Name Transport Socket Expire

Example: #opensipsctl fifo event_subscribe E_PIKE_BLOCKED udp:127.0.0.1:8888 1200

If the subscription succeeded, the application gets 200 OK message and it will start getting notifications for the subscribed event when the event is raised. The external application suppose to do something upon receiving the notification. For example: update the firewall, do some processing, or just printing or send information mail to administrator.


More Information


 

Advertisements

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


 

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


Attribute Value Pair (AVP) Variables in OpenSIPS

.

Here i want to talk about AVPs from routing script’s point of view and the database’s point of view.

AVPs IN The Routing Script

These variables are used in the routing script. They are allocated at the beginning of the transaction and unallocated when the transaction is completed. So they are called transaction-persistent variables (if stateful processing is used). If stateless processing is used, theses variables will be attached to a singular message. When the message is received or the transaction is created, it will initially have an empty list of AVPs. The AVPs will be created directly in the routing script or through functions called from the script. They will automatically attached to the message/transaction.

The AVPs are visible in the routes where the message will be processed (i.e. branch_route , failure_route, onreply_route). If we want the AVPs to be visible in the onreply_route, we need to enable the TM parameter “onreply_avp_mode”.

OpenSIPS ‘s AVPs are Read-Write variables (You can read data from AVP or write data to AVP). AVP can also deleted. When we assign a value to an AVP, the new value is appended to the AVP. So the AVP may contain multiple values. New assignment (write operation) will add a new value to the AVP. The AVP is like a stack (Last in First out).

AVP’s Synatx: The name starts with $ sign. It looks like $avp(name) or $(avp(name)[N]). The index “N”  referes to a specific value. If no index is used, the last added value will be returned. See the figure below

The assignment can be done directly in the script or by calling a function which brings values from database and add them to the AVP. To delete a specific AVP’s value, we do this  $(avp(test)[N])=NULL. To remove the last value, we do this $(avp(test))=NULL. If no values remained, the AVP will be deleted. You can also delete the AVP by avp_delete(“$avp(test)/g”).


AVPs In The Database

The AVPs in the routing script can be loaded by values taken from the database. The default table to store the AVPs is “usr_preferences”. This table is used by the “avpops” module. Each user which uniquely identified by Unique User ID (UUID) or (username and domain), may have several preferences formed in (attribute, value) pairs and stored in the usr_preferences table.  The AVP is defined by this tuple (Attribute, Type , Value). The Attribute is the name of the AVP(The name can be string or number). The type is number (0 (str:str), 1 (str:int), 2 (int:str), 3 (int:int)). For example $avp(123)=2 <=> Type = 3 (integer name and integer value). The value (as a field) is a string.

The figure below explains the structure of the usr_preferences table.

The table “usr_preferences” is the default but you can change it by adding this line in the opensips configuration file with your table’s name:

modparam(“avpops”, “avp_table”, “usr_preferences”)

When a SIP message is received or new SIP transaction is created, the user’s preferences should be taken into account otherwise it will not called preferences, right?.

The function avp_db_load(“$fu”,”$avp(test-name)”) loads the AVP which belongs to $fu and has the name “test_name”.  This function exists in the “avpops” module. So the module must be loaded when using this function or any other AVP ‘s functions. The second parameter of this function can be empty – which means all AVPs identified by the source(first parameter).

Opensipsctl and AVPs

You can use opensipsctl tool to manage AVPs (add, remove, or configure) in the database. To get help info, type #./opensipsctl avp help

For example to add AVP which has the name “trace”, type 1 (string,integer), and the value =1 (e.g. <=> true):

opensipsctl avp add  -T usr_preferences test@youripaddress trace 1 1

This correspond to $(AVP(trace)) in the routing script. The value will be 1.

Note: You can use your favourite database editor to manage  the AVPs  in the database directly (in “usr_preferences” table). For example “phpMyAdmin” (Mysql Administration on the web).


More Information