OpenSIPS Dynamic Routing

By Tait Clarridge, Thu 09 February 2012, Category Linux

opensips, voip

I've been playing with OpenSIPS and Freeswitch recently and was intrigued by the Dynamic Routing [drouting] module for call routing.

Although I don't have a full grip on all the features that are available in this module, basic functionality is still pretty useful.

I basically wanted to be able to route specific DIDs to certain Freeswitch servers and have a list of carriers with failover for outbound calls.

So lets get started, I'm assuming you already have a mysql database setup for opensips and have granted access to it to a user.

Lets setup the database:

There are 4 tables involved in dynamic routing:

To setup the dr_groups I chose to create two separate groups for inbound and outbound calling to help split up the dial plans. Connect to your opensips mysql instance and change to your opensips database.

mysql> use opensips;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> INSERT INTO dr_groups(username,domain,groupid,description) VALUES(".*",".*","0","INBOUND");

Since it didn't really matter about the domains and usernames, I set them as wildcard values. The important part is the groupid, it is how we are going to call our dynamic routes. I set the description to INBOUND because that is what I am going to do for my inbound calling routes.

Into the dr_gateways table we can put the gateways that we will use.

mysql> INSERT INTO dr_gateways(type,address,strip,probe_mode,description) VALUES("1","GW_IP:PORT","0","2","Freeswitch");

I don't think the type matters right now. And the GW_IP:GW_PORT is the IP of your endpoint, in my case it was the freeswitch. The "strip" column is how many digits you want to strip before passing it on. The "probe_mode" is important if you want failover when using dr_gw_lists. Setting it to 2 enables probing so it will keep a list of which gateways are still active.

You can decide to setup dr_gw_lists if you would like to.

mysql> INSERT INTO dr_gw_lists(gwlist,description) VALUES("1","DESCRIPTION");

For the "gwlist" column you put in the gwid from the gateways you created in the dr_gateways table, in this example I'm using the gwid of 1. You can also reference multiple gateways by comma separating them like VALUES("4,3","DESCRIPTION). That would use gateways with gwid 3 and 4.

Last we can setup the rules we are going to use.

mysql> INSERT INTO dr_rules(groupid,prefix,priority,gwlist,description) VALUES("0","4165555555","0","1","My Number");

So we can see here that the groupid is 0 like we added to dr_groups. The prefix is the number to match, in this case it is a full DID, you can put less or more specific prefixes in there to your liking. For example I could add a default route by leaving the prefix empty or match all 416 numbers by putting the prefix as "416". It will match the most specific prefix and route accordingly. The "gwlist" column references either the gateway from dr_gateways or the gateway list from dr_gw_lists. My example above uses the gateway with a gwid of 1, to use a gateway list just add a "#" before the id number of the entry from dr_gw_lists.

Now we can look at changing the opensips configuration files.

You need to add the following to your /etc/opensips/opensips.cfg file (if they don't already exist):

loadmodule "drouting.so"
loadmodule "db_mysql.so"

Now add the drouting module options:

modparam("drouting", "db_url", "mysql://DBUSER:DBPASSWD@localhost/DBNAME")
modparam("drouting", "probing_interval", 60)
modparam("drouting", "probing_from", "sip:probe@URI")
modparam("drouting", "probing_method", "OPTIONS")
modparam("drouting", "probing_reply_codes", "501, 403, 404")
modparam("drouting", "use_domain", 1)

For DBUSER put in the username you gave access to the opensips database, DBPASSWD is the password of that user and DBNAME is the database name. The next items just setup the probing configuration where probing_interval is how often it polls in seconds, probing_from is the URI you send from, probing_method is the call that you will make (different for some types of gateways), and probing_reply_codes are the codes other than a 200 that will be considered valid.

Since the database is taken care of and populated, it's time to add the dynamic routing entries to your routing config.

In your main routing block you need to add something similar to the following:

if (method == "INVITE") {
                setflag(1);
        record_route();
                xlog("INBOUND CALL,$dd,$ru,$ci,$fn,$fu");
        route(10);
        exit;
}

This will call the subroute of 10 when an INVITE is called, so we have to add that too.

route[10] {
    if (!do_routing("0")) {
        xlog("do_routing: No rules matching the URI\n");
        send_reply("503","No rules matching the URI");
        exit;
    }

    if (is_method("INVITE")) {
        t_on_failure("10");
    }
    route(1);
}

This is where your dr_groups table entry is important. The do_routing("0") is calling the group with the groupid of zero and will lookup any subsequent routes that have 0 as the groupid.

It also primes a failure route incase it fails, we will need to add this as well.

failure_route[10] {
    xlog("DEBUG: DROUTING failure route active\n");
    if (t_was_cancelled()) {
        exit;
    }
    if (t_check_status("[34][0-9][0-9]")) {
        exit;
    }

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

On failure it will lookup the next gateway in the gw_list (if active) and try and fail the call to that secondary gateway.

This should be all you need to get started. You can add something very similar to route outbound calls. Where you setup another dr_groups entry, along with all the necessary gateways and rules. Then to differentiate between inbound and outbound calls you can add another entry to your main routing block that contains:

if (method == "INVITE" && src_ip =~ "xxx\.xxx\.xxx.*" ) {

Where xxx.xxx.xxx is a local subnet that your freeswitch would be sending invites from.

Finally just add another routing block, something like route[20] and set up do_routing to reference your new outbound group from dr_groups.

Leave a comment if you run into problems. If you've followed other tutorials and it is failing, make sure that in your dr_rules table the routeid isn't set to anything.

You can check out the dynamic routing module man page here: http://www.opensips.org/html/docs/modules/devel/drouting.html