Monday, September 27, 2010

Dynamic Routing with Opensips

Opensips is a Sip Express Router software. It lets you route phone calls to different media servers. The solutions discussed in this tutorial uses a MySQL database to route phone calls based on rules loaded in a DB table.

Opensips is the example here, but Kamalio, another Sip Express Router software solution can be substituted with only a very few modifications.

The most important features of this solutions are:
1) Easily modified routes.
2) Fail over in case a route goes down.

This solution doesn't cover load balancing. This guide also assumes that you have Opensips and MySQL already installed and working.
Setting up either is outside the scope of this article. Its best to add dynamic routing to your setup after you have tested that the routes work, otherwise you may be fighting unresolved NAT or other issues and not know it.

Since you already have opensips setup, you now need to modify the config file. It should be called opensips.cfg.

The first thing that needs to be done is to load the MySQL and dynamic routing modules.
Modules are loaded with the "loadmodule" key word and should go in the Modules section of opensips.cfg.

####### Modules Section ########

loadmodule ""
loadmodule ""

The newly loaded modules then need to be configured. The next section of your config file should be for module parameters. A module parameter is a global setting for a specific module.
These options need to be tailored to your specific system requirements. The "modparam" keyword is used to define parameters for a module.

It accepts three parameters, the module name and the parameter to be modified and then the value to set it to. Passing in the module name allows the same function to operate on any module without having to worry about its order within the config file.

# ------- drouting params -------
modparam("drouting", "db_url", "mysql://username:password@localhost/opensips")
modparam("drouting", "use_domain", 1)
modparam("drouting", "probing_interval", 60)
modparam("drouting", "probing_from", "sip:pinger@")
modparam("drouting", "probing_method", "OPTIONS")
modparam("drouting", "probing_reply_codes", "501, 403, 404")

The first parameter set is the db_url. This is the location of your mysql database. Its possible to use a Postgres database as well. The format for the DB url string is:
dbtype://username:password@hostname/db_name, making it easy to just fill in the blanks with your actual values.

The next important parameter is the probing interval. This is for failover support. Opensips will periodically ping your media servers to make sure they are up. If it doesn't get a reply, it will mark them as down and skip them in the routing. The proving interval sets how often opensips will ping each server. Setting this to a low value will increase the sensitivity to down servers, but will cause a lot more traffic to your media servers. Setting this to too low a value could result in dropped calls.

The probing from address should be set to the address of your opensips server using the form sip:username@hostname. I haven't seen anywhere that the username matters and you can insert your own values for it.

The probing method is important to your media servers. Asterisk servers will only accept "OPTIONS", while other servers may only accept "INFO".

The standard probing reply codes were set at "501" and "403", I added "404" to the list because my asterisk servers responded with "404" and I didn't want to adjust their settings.

This list can be adjusted to accept whatever reply codes come back from your media servers that you will accept as "this server is working".

From here in the config file, we move on to the routing section. The main route starts with "route{" and is pretty easy to spot. We are going to add a subroute within the main route.

route[4] {
  if (!do_routing("0")) {
    send_reply("503", "No Rules matching the URI");
  if (is_method("INVITE")) {

We have labeled this "route[4]" or subroute "4". This will allow us to call the route from within the main routing logic.

The first thing we do is call the do_routing() function and check to see if it worked.
If it doesn't work, we send a reply back saying we couldn't handle the call and we exit this part of the script.

If it does work, we continue on and set flag "10" and then arm a failure route using t_on_failure.
Setting that value to "4" tells us to use a failure route called "4" in case we fail to send the call. So it doesn't just turn it on, but tells us which one to go to.

This will be very important for fail over routing. The next thing we have to do is call our route from the main routing logic.

You will need to find a good place to call it from within your existing script. It could be something like this:

####### Routing Logic ########
# main request routing logic

  if (is_method("INVITE")) {
    setflag(1); # do accounting
    route(4); # Call our dynamic route.

Notice how route(4) has parenthesis instead of square brackets? We use parenthesis when we call the route, but square brackets when we define the route.

They both use the same label of "4" though to designate the route name. Now that we are calling our new route, we need to deal with the fail over. If you remember, we have already called our failure route in the previous section, so now we need to create the failure route for it to go to. It needs to be named the same. I decided to call it "4" as well, though the naming is arbitrary. The failure_route[] and t_on_failure() just both have to match.

failure_route[4] {
    if (t_was_cancelled()) {
    if (t_check_status("[34][0-9][0-9]")) {

    if (use_next_gw()) {
    } else {
        t_reply ("503", "Service not available");

First we let any call that was cancelled die in peace. Then we filter out any errors that are not actually a gateway failure. Then we try to use the next gateway. The use_next_gw() is a function from the drouting module that gets the next gateway ready to be used. If it succeeded, we relay the call and exit this part of the script. If it fails it will reply that the service is not available.

This should be enough to get Opensips launched. However, still need to setup the database.
The table structures are located in the scripts directory in the opensips source code. There should be a directory for mysql and postgres.

At a minimum, you will want to use the standard-create.sql and drouting-create.sql files. I've included the contents here for completeness, but the formats change occasionally.

CREATE TABLE version (
table_name CHAR(32) NOT NULL,
CONSTRAINT t_name_idx UNIQUE (table_name)

INSERT INTO version (table_name, table_version) values ('dr_gateways','4');
CREATE TABLE dr_gateways (
address CHAR(128) NOT NULL,
pri_prefix CHAR(16) DEFAULT NULL,
description CHAR(128) DEFAULT '' NOT NULL

INSERT INTO version (table_name, table_version) values ('dr_rules','3');
CREATE TABLE dr_rules (
groupid CHAR(255) NOT NULL,
prefix CHAR(64) NOT NULL,
timerec CHAR(255) NOT NULL,
priority INT(11) DEFAULT 0 NOT NULL,
routeid CHAR(255) NOT NULL,
gwlist CHAR(255) NOT NULL,
description CHAR(128) DEFAULT '' NOT NULL

INSERT INTO version (table_name, table_version) values ('dr_gw_lists','1');
CREATE TABLE dr_gw_lists (
gwlist CHAR(255) NOT NULL,
description CHAR(128) DEFAULT '' NOT NULL

INSERT INTO version (table_name, table_version) values ('dr_groups','2');
CREATE TABLE dr_groups (
username CHAR(64) NOT NULL,
domain CHAR(128) DEFAULT '' NOT NULL,
description CHAR(128) DEFAULT '' NOT NULL

Now you need to put some data in the DB.

First create some gateways. This is where the call gets sent. You can set these using opensips-cp or by hand using an insert statement like these:

insert into dr_gateways (type,address,strip,probe_mode,description) VALUES ("1","","0","2","Asterisk Server #1");
insert into dr_gateways (type,address,strip,probe_mode,description) VALUES ("1","","0","2","Asterisk Server #2");

The type corresponds with the types that are set in /tools/system/drouting/gw_types.txt if you are using opensips-cp. But generally they just refer to a grouping that you set.

The address is the IP address of the media server. Strip is used if you need to remove some digits from the phone number that is being passed in before matching it to the rules.

The probe mode is very important. The default setting it "0" which means don't probe it. If this is set, the dynamic routing fail over will be turned off for that media server.

The code we have written requires that we set it to "2", which is automatic detection of problems. The description is so you can remember what this gateway does.

The next thing we need to do is setup some gateway groups.

insert into dr_gw_lists (gwlist,description) VALUES ("1,2","My Media Servers");

The value for gwlist is the GWIDs from the dr_gateways table for the media servers you want to send the calls to, in the order you want them to fail over in. So, in this example, it would send the calls to asterisk server 1 and then to asterisk server 2 in case it couldn't send it to the first server.

Next we need to create at least one group. The do_routing() function pulls the gateways from a group associated with the customer. This allows you to put people in different call groups based on geography or some other criteria and do routing differently just for them. For our purposes, we only need one group and we are putting everyone in it.

insert into dr_groups (username,domain,groupid,description) VALUES (".*",".*","0","Everyone");

This creates group "0", which we already referenced when we created the config script, think do_routing("0").

Now the server knows how to route the calls to the media servers, but it doesn't know which numbers to route to which servers. We need to set up some rules.

The dr_rules table takes a phone number prefix and tells opensips which gateway to use to route the call.

insert into dr_rules (groupid, prefix,routeid,gwlist,description) VALUES ( "0","011","4","#1","International Calls");

The group ID is the same group ID as we just created. This is where you would put in multiple rules that routed to a different gateway depending on the group they belonged to.

The Prefix is the phone number you are routing. In this example, I used 011, the prefix for making international calls from the US. However, I could put a single phone number in there.

insert into dr_rules (groupid, prefix,routeid,gwlist,description) VALUES ( "0","12025551234","4","#1","Freds number");

It selects the route based on the most numbers matched. So, an exact match would be the preferred route. To set a default route, you just create a rule with a blank prefix.

insert into dr_rules (groupid, prefix,routeid,gwlist,description) VALUES ( "0","","4","#1","Default Route");

The gwlist is the ID for the row in the dr_gw_lists table prefixed by a number sign (#), or its a standard number, which corresponds to a single gateway ID from the dr_gateways table.

So, #1 uses the gateway list for failover and "2" would just be "Asterisk Server #2" and have no failover at all.

The route ID is the subroute we created in the config file. We created route[4], so that is what we set the value to.

That should be everything you need to make this work with dynamic routing, server monitoring, and fail over.


  1. Hello,

    any parameters from drounting module not exist??¿¿

    In description modules no have the parameters: probing_interval, probing_from, probing_method, probing_reply_codes.

    This, of course, returns error.

    Thanks, regards.

  2. Ups! version problem, i have 1.5 version.


  3. Hello,
    i want to configure the opensips on wan through internet
    i have my static ip
    but i don't know what i need from modules , tools and what i have to add in the cfg file

    your reply is highly appreciated
    thanks in advance

  4. would be great to see a complete opensips.cfg with drouting above and the authorizations

  5. Hi kotis,

    Here is my setup:
    Polycom on asterisk ---> opensips ---> service provider
    The call goes through to the service provider but asterisk gives this error and drops the call:
    SIP/2.0 500 Server error occurred (6/SL)

    Opensips on the other hand has this output in the log:
    ERROR:tm:add_uac: maximum number of branches exceeded
    Oct 11 17:06:42 cust18 /usr/sbin/opensips[22254]: ERROR:tm:t_forward_nonack: failure to add branches
    Oct 11 17:06:42 cust18 /usr/sbin/opensips[22254]: ERROR:tm:w_t_relay: t_forward_nonack failed

    The error is repeated many times.

    Any help is appreciated.

    Thank you


  6. How to integrate load balancing and failover together?

  7. This tutorial is very good, but if you allow me, i have some corrections and suggestions to make.

    The search on table dr_groups for username and domain does not allow regular expressions, so it is a lot easier to manage dynamic routing for user groups using avpops, through the exported function avp_db_load to check which is the dynamic routing tree to apply for that user.
    You can, as I did, specify a default routing and deal with exceptions through avpops.
    I've used a $var(dr_group_id) just to define a hard-coded value on beginning of the script, for readability.
    Here is my code:

    route {

    # Dynamic routing default table
    $var(dr_group_id) = 0;


    route[4] {
    avp_db_load("$ruri/username", "$avp(dr_group_id)");
    if (!is_avp_set("$avp(dr_group_id)")) {
    $avp(dr_group_id) = $var(dr_group_id);
    if ( !do_routing( "$avp(dr_group_id)") )


  8. I have used this to redirect a calls, but when I add the data to the mysql database I need to restart the opensips to load the data. There is any way to reload datas in database without restart the opensips server.

  9. I have used this tutorial to redirect calls to an Asterisk server, but when I add new data to the mysql database I need to restart the opensips to load them. There is any way to reload datas in database without restart the opensips server?