kamailio, SIP

Building IM Server using Kamailio

Instant messaging (IM) is a type of online chat which offers real-time text transmission over the Internet. Like XMPP, we can also use SIP for real-time text transmission. The Kamailio PRESENCE module helps us built the same locally. We can convert a simple Kamailio server into a full fledged IM server.

By default, all the registered endpoints will send a “PUBLISH” requests to the Presence server with their status and Presence server stores the status. Also the endpoints will send a “SUBSCRIBE” to other user’s status. For each “SUBSCRIBE” request, the Presence server will sent a “NOTIFY” request back with status of the user. This is how our UserAgent keeps track of each user’s status. Below is simple flow diagram for the same.

Alt text

Setting up Kamailio

In my previous blog, i’ve explained how to install Kamailio from source. The only difference is we need to enable two more modules ”presence” and ”presence_xml”. If Kamailio is already installed, we need to add these two modules into modules.lst at the ”include_modules” line. Once the module names are added, we need to run ”make install” to install the two new modules. Once the module is added, we need to enable the module.

Add “#!define WITH_PRESENCE” into the “kamailio.cfg” file. Then check if there is route logic defined for the “PRESENCE” module. By default there is a PRESENCE route defined in the default “kamailio.cfg” file. If it’s not there below is the route logic. Also we need to add “*route(PRESENCE)” in the ”request_route**” section.

# Presence server route
route[PRESENCE] {
        if(!is_method("PUBLISH|SUBSCRIBE"))
                return;

        if(is_method("SUBSCRIBE") && $hdr(Event)=="message-summary") {
                route(TOVOICEMAIL);
                # returns here if no voicemail server is configured
                sl_send_reply("404", "No voicemail service");
                exit;
        }

#!ifdef WITH_PRESENCE
        if (!t_newtran())
        {
                sl_reply_error();
                exit;
        }

        if(is_method("PUBLISH"))
        {
                handle_publish();
                t_release();
        } else if(is_method("SUBSCRIBE")) {
                handle_subscribe();
                t_release();
        }
        exit;
#!endif

        # if presence enabled, this part will not be executed
        if (is_method("PUBLISH") || $rU==$null)
        {
                xlog("@ 404 here 3");
                sl_send_reply("404", "Not here");
                exit;
        }
        return;
}

Now start the Kamailio server. Now we need to add some users. For that we can use ”kamctl” binary.

$ kamctl add user1@192.168.56.100 user1

$ kamctl add user2@192.168.56.100 user2

Let’s go ahead and test the settings. For testing we need some IM clients. In my testing, i’ve used Jitsi and adium IM clients. Once the accounts are configured on the clients, it’s better to start a packet capture using wireshark, so that we can see these PUBLISH, SUBSCRIBE and NOTIFY requests between the clients and the Kamailio server. If you are using Adium, go to account options and check the “Publish Status to Everyone” so that it will start sending PUBLISH request to the Kamailio server. Now add the accounts to the Contact lists on the IM clients and then we will be able to see the users status (ONline/OFFline). Now we can start the chat conversation between the user’s.

Standard
FreeSwitch, kamailio, SIP

Integrating Kamailio with FreeSwitch

Kamailio aka OpenSER is one of the most powerfull and popular Open Source SIP server. It can be used as SIP Proxy/ Registrar/ LB/ Router etc. It also provides a lot of features like WebSocket support for WebRTC, ; SIMPLE instant messaging and presence with embedded XCAP server and MSRP relay,IMS extensions,ENUM and offcourse AAA (accounting, authentication and authorization) also. Kamailio is a modular system, ie, it has lot of modules which corresponds to particular functions. These modules can be easily installed and can be used easily in Kamailio. In this blog i’m going to use Kamailio as a proxy server. All the user’s are created in the Kamailio and FreeSwitch will be acting as a relay server for outbound calls. So Kamailio performs authentication and all the outbound calls wil be relayed to FreeSwitch.

Installing Kamailio

Lets download the latest version of Kamailio, now it’s 4.1

$ wget http://www.Kamailio.org/pub/Kamailio/latest/src/Kamailio-4.1.1_src.tar.gz

$ tar xvzf Kamailio-4.1.1_src.tar.gz && cd Kamailio-4.1.1

Before we start the build we need to install the basic dependencies.

$ apt-get install gcc flex bison libmysqlclient-dev make libxml2-dev libssl-dev

Now we have the dependencies installed, we can go ahead with the build.

$ make cfg      # generates config files for build system

Now open modules.lst and add the modules to be installed in ”include_modules” section. In my case i’m going to use MySQL backend so it will be ”include_modules= db_mysql” and then we can run the ”make all”. The other way is we can mention the modules directly while running the “make” rather editing the modules.lst file.

$ make include_modules="db_mysql" cfg

Now we can install,

$ make install

The above command will install Kamailio to our system. There are four main binaries for Kamailio,

Kamailio - Kamailio SIP server
kamdbctl - script to create and manage the Databases
kamctl - script to manage and control Kamailio SIP server
kamcmd - CLI - command line tool to interface with Kamailio SIP server

There is also one configuration file called “Kamailio.cfg” which is available by default at /usr/local/etc/Kamailio/Kamailio.cfg

Let’s go ahead with setting up MySQL server for Kamailio.

$ apt-get install mysql-server

Now edit the /usr/local/etc/Kamailio/kamctlrc Locate DBENGINE variable and set it to MYSQL by making ”DBENGINE=MYSQL”. Now we can use the “kamdbctl” binary to craete the default tables and users.

$ /usr/local/sbin/kamdbctl create

The script will add two users in MySQL:

  • Kamailio – (with default password ‘Kamailiorw’) – user which has full access rights to ‘Kamailio’ database

  • Kamailioro – (with default password ‘Kamailioro’) – user which has read-only access rights to ‘Kamailio’ database

There is a sample init.d script available along with Kamailio, which we can use it. We need to copy the sample init.d file to our system’s init.d folder. And the same for the system default file.

$ cp /usr/local/src/Kamailio-4.1.1/pkg/Kamailio/deb/precise/Kamailio.init /etc/init.d/Kamailio

$ cp /usr/local/src/Kamailio-4.1.1/pkg/Kamailio/deb/precise/Kamailio.default /etc/default/Kamailio

$ chmod 755 /etc/init.d/Kamailio 

Edit the new init file and modify the $DAEMON and $CFGFILE values.

DAEMON=/usr/local/sbin/Kamailio
CFGFILE=/usr/local/etc/Kamailio/Kamailio.cfg

$ mkdir -p /var/run/Kamailio    # Directory for the pid file

Default setting is to run Kamailio as user ”Kamailio” and group ”Kamailio”. For that we need to create the user:

$ adduser --quiet --system --group --disabled-password \
      --shell /bin/false --gecos "Kamailio" \
      --home /var/run/Kamailio Kamailio

$ chown Kamailio:Kamailio /var/run/Kamailio

Setting up Kamailio

All the Kamailio configurations are mentioned in only one single file /usr/local/etc/Kamailio/Kamailio.cfg. All the logics are defined in this file, and Kamailio blindly executes this logics and perform the actions. It’s very important that the logics defined in the config should suit to our VOIP platform requirement.

First we need to enable the modules and the necessary features, so add the below lines in the Kamailio.cfg

#!define WITH_MYSQL
#!define WITH_AUTH
#!define WITH_USRLOCDB
#!define WITH_FREESWITCH

We need to define the FreeSwitch server IP and port, for that we can add the below parameters in the “Custom Parameters” section.

#!ifdef WITH_FREESWITCH
freeswitch.bindip = "192.168.56.100" desc "FreeSWITCH IP Address"
freeswitch.bindport = "5090" desc "FreeSWITCH Port"
#!endif

Now we can go ahead to the ”request_route” section which performs the routing logic. Here i’m going to add two more routing logic for the FreeSwitch relay. After the ”request_route” section, we can see the definition for each routing options. Below that we need to add our new route definitions.

route[FSDISPATCH] {
        # dial number selection
        route(FSRELAY);
        exit;
}

route[FSRELAY] {
        $du = "sip:" + $sel(cfg_get.freeswitch.bindip) + ":" + $sel(cfg_get.freeswitch.bindport);
        if($var(newbranch)==1)
        {
                append_branch();
                $var(newbranch) = 0;
        }
        route(RELAY);
        exit;
}

By default, all the routes mentioned in the “request_route” will be executed line by line. There is a default route called ”Location”, which splits the user part from the request URI and verifies if the user exists in the location table. But when we dial an outside number/user, this location check will fail, so i’m going to add a condition which checks if the user in the request URI contains a number with a length 9-15 will be relayed to the FreeSwitch. Again this is just a simple condition, we can create a more complex condition, like check the domain part, if the domain part contains a domain which doesnot belong to our Domain list, we can either decline the request, or we can relay to FreeSwitch or we can make DNS query and we can make Kamailio to process the request to that domain’s Proxy server. Like this we can define our own conditions in the config file, and Kamailio will execute it line by line.

I’m going to add my check condition on the ”LOCATION” route definition.

route[LOCATION] {

    #!ifdef WITH_SPEEDDIAL
        # search for short dialing - 2-digit extension
        if($rU=~"^[0-9][0-9]$")
                if(sd_lookup("speed_dial"))
                        route(SIPOUT);
    #!endif
        if($rU=~"^[0-9]{9,15}$")        # checking for numbers in the Request URI
                route(FSDISPATCH);
    #!ifdef WITH_ALIASDB
        # search in DB-based aliases
        if(alias_db_lookup("dbaliases"))
                route(SIPOUT);
    #!endif
        $avp(oexten) = $rU;
        if (!lookup("location")) {
                xlog("L_INFO", "CALL $rm $ci lookup here\n");
                xlog("L_INFO", "$fU@$fd - Lookup contact location for $rm\n");
                xlog("L_INFO", "rc is $var(rc)");
                switch ($rc) {
                        case -1:
                        case -3:
                                xlog("L_ERR", "$fU@$fd - No contact found\n");
                                send_reply("404", "Not Found here");
                                exit;
                        case -2:
                                send_reply("405", "Method Not Allowed");
                                exit;
                }
        }

        # when routing via usrloc, log the missed calls also
        if (is_method("INVITE"))
        {
                setflag(FLT_ACCMISSED);
        }
        xlog("L_INFO", "CALL $rm $ci relay\n");
        xlog("L_INFO", "$fU@$fd - Relaying $rm\n");
            route(RELAY);
        exit;
}

So now all the calls coming with numbers of length 9-15 in the Request URI will be relayed to the FreeSwitch, and FreeSwitch will process the call based on the DialPlan configured in the FreeSwitch. Since i’m going to use IP authentication, i need to whitelist the Kamailio ip in “acl_conf.xml” file in the FreeSwitch autload conf directory, so that FreeSwitch will accept the invites from Kamailio. Again i’m not defining any Voicemail options here. If we have a Voice mail server, then we can create another route option and when the caller doesn’t pick the call we can route the call to the Voice Mail server.
For example the below condition will route the failures to FreeSwitch Voice Mailbox.

if(is_method("INVITE"))
        {
            # in case of failure - re-route to FreeSWITCH VoiceMail
            t_on_failure("FAIL_FSVBOX");        # where FSVBOX is a route definition
        }

Kamailio has a lot of modules which really comes in handy. For example we can use LDAP module to use LDAP as a backend. There is a PRESENCE module which helps us to setup an Instant Messaging server using Kamailio. I’ll be writing a blog on how to use Kamailio as an IM server soon. One of the main advantage of Kamailio over OpenSIP is the WebSocket support. This just a basic configuration, but we can design much complex system using Kamailio. We can even remove the default route logics, and we can use our own routing logics. Kamailio doesnot depend on default routing logics, it blindly reads the route and executes it for each incoming connections.

Standard
Uncategorized

SIP Trunking With PLIVO and FreeSwitch

It’s more than a month since i joined the DevOps family at Plivo, since i’m pretty new to the Telecom Technology, i was digging more around it. This time i decided to play around with FreeSwitch, a free and open source communications software for the creation of voice and messaging products. Thanks to Anthony Minessale for designing and opensourcing such a powerfull powerfull application. FreeSwitch is well documented and there are pretty good blogs also available on how to setup a PBX using FreeSwitch. This time i’m going to explain on how to make a private Freeswitch server to use Plivo as a SIP Trunking service.

A bit about Plivo. Plivo is a cloud based API Platform for building Voice and SMS enabled Applications. Plivo provides Application Programming Interfaces (APIs) to make and receive calls, send SMS, make a conference call, and more. These APIs are used in conjunction with XML responses to control the flow of a call or a message. We can create Session Initiation Protocol (SIP) endpoints to perform the telephony operations and the APIs are platform independent and can be used in any programming environment such as PHP, Ruby, Python, etc. It also provides helper libraries for these programming languages.

First we need a valid Plivo account. Once we have the Plivo account, we can log into the Plivo Cloud service. Now go to the ”Endpoints” tab and create a SIP endpoint and attach a DirectDial app to it. Once this is done we can go ahead and start setting up the FreeSwitch instance.

Installing FreeSwitch

Clone the Official FreeSwitch Github and Repo and compile from the source.

$ git clone git://git.freeswitch.org/freeswitch.git && cd freeswitch

$ ./bootstrap.sh && ./configure --prefix=/usr/local/freeswitch

$ make && make install

$  make all cd-sounds-install cd-moh-install    # optional, run this if you want IVR and Music on Hold features

Now if we have more than one ip address on the machine, and if we want to bind to a particular ip, we need to modify two files ”/usr/local/freeswitch/conf/sip_profiles/external.xml” and ”/usr/local/freeswitch/conf/sip_profiles/internal.xml”. In both the files, change the parameters ”name=”rtp-ip”” and ”param name=”sip-ip”” with the bind ip as the values.

By default, Freeswitch will create a set of users, which includes numerical usernames ie, 1000-1019. So we can test the basic connectivity between user’s by making a call between two user accounts. We can register two of the accounts in two SoftPhones and we can make a test call and make sure that FreeSwitch is working fine. We can use the FS binary file to start FreeSwitch service in forground.

$ /usr/local/freeswitch/bin/freeswitch

Configuring Gateway

Once the FreeSwitch is working fine, we can start configuring the SIP trunking via Plivo. So first we need to create an external gateway to connect to Plivo. I’m going to use the SIP endpoint created on the Plivo Cloud to initiate the connection. The SIP domain for Plivo is ”phone.plivo.com”. We need to create a gateway config. Go to ”/usr/local/freeswitch/conf/sip_profiles/external/”, here we can create an XML gateway config file. My config file name is plivo. Below is the content for the same.

<include>
  <gateway name="plivo">
  <param name="realm" value="phone.plivo.com" />
  <param name="username" value="<Plivo_SIP_Endpoint_User_Name" />
  <param name="password" value="<Plivo_SIP_EndPoint_Password" />
  <param name="register" value="false" />
  <param name="ping" value="5" />
  <param name="ping-max" value="3" />
  <param name="retry-seconds" value="5" />
  <param name="expire-seconds" value="60" />
  <variables>
        <variable name="verbose_sdp" value="true"/>
  </variables>
  </gateway>
</include>

There are a lot of other parameters which we can add it here, like caller id etc. Replace the username and password with the Plivo endpoint credentials. If we want to keep this endpoint registered, we can set the register param as true and we can set the expiry time at expire-seconds, so that the Fs will keep on registering the Endpoint with Plivo’s Registrar server. once the gateway file is created, we can either restart the service or we can run “reload mod_sofia” on the FScli. If the FreeSwitch service si started in foreground, we will get the FScli, so we can run the reload command directly on it.

Setting up Dialplan

Now we have the Gateway added. Now we need to the setup the Dial Plan to route the outgoing calls through Plivo. Go to ”/usr/local/freeswitch/conf/dialplan/” folder and add an extension on the ”public.xml” file. Below is a sample extension config.

    <extension name="Calls to Plivo">
      <condition field="destination_number" expression="^(<ur_regex_here>)$">
        <action application="transfer" data="$1 XML default"/>
      </condition>
    </extension>

So now all calls matching to the Regex will be transferred to the default dial plan. Now on the the default dial plan, i’m creating an exntension and will use the FreeSwitch’s ”bridge” application to brdige the call with Plivo using the Plivo Gateway. So on the ”default.xml” add the below extension.

       <extension name="Dial through Plivo">
         <condition field="destination_number" expression="^(<ur_regex_here>)$">
           <action application="bridge" data="sofia/gateway/plivo/$1"/>
         </condition>
       </extension>

Now we can restart the FS service or we can reload “mod_dialplan_xml” from the FScli. Once the changes are into effect, we can test whether the call is getting routed via Plivo. Configure a soft phone with a default FS user and make an outbound call which matches the regex that we have mentioned for routing to Plivo. Now if all works we should get a call on the destination number. We can check the FS logs at ”/usr/local/freeswitch/log/freeswitch.log”.

If the Regex is matched, we can see the below lines in the log.

1f249a72-9abf-4713-ba69-c2881111a0e8 Dialplan: sofia/internal/1001@192.168.56.11 parsing [public->Calls from BoxB] continue=false
1f249a72-9abf-4713-ba69-c2881111a0e8 EXECUTE sofia/internal/1001@192.168.56.11 transfer(xxxxxxxxxxxx XML default)
1f249a72-9abf-4713-ba69-c2881111a0e8 Dialplan: sofia/internal/1001@192.168.56.11 Regex (PASS) [Dial through Plivo] destination_number(xxxxxxxxxxxx) =~ /^(xxxxxxxxxxxx)$/ break=on-false
1f249a72-9abf-4713-ba69-c2881111a0e8 Dialplan: sofia/internal/1001@192.168.56.11 Action bridge(sofia/external/plivo/xxxxxxxxxxxx@phone.plivo.com)   
1f249a72-9abf-4713-ba69-c2881111a0e8 EXECUTE sofia/internal/1001@192.168.56.11 bridge(sofia/external/plivo/xxxxxxxxxxxx@phone.plivo.com)
1f249a72-9abf-4713-ba69-c2881111a0e8 2014-02-14 06:32:48.244757 [DEBUG] mod_sofia.c:4499 [zrtp_passthru] Setting a-leg inherit_codec=true
1f249a72-9abf-4713-ba69-c2881111a0e8 2014-02-14 06:32:48.244757 [DEBUG] mod_sofia.c:4502 [zrtp_passthru] Setting b-leg absolute_codec_string=GSM@8000h@20i@13200b,PCMA@8000h@20i@64000b,PCMU@8000h@20i@64000b

We can also mention the CallerID on the Direct Dial app which we have mapped to the SIP endpoint. Now for Incoming calls, create a app that can forward the calls to one of the user’s present in the FreeSwitch, using the Plivo’s Dial XML. So the XML should look something like below. I will be writing a more detailed blog about Inbound calls once i’ve have tested it out completely.

<Response>
  <Dial>
    <User>FSuser@FSserverIP</User>
  </Dial>
</Response>

But for security, we need to allow connections from Plivo server. So we need to allow those IP’s on the FS acl. We can allow the IP’s in the ”acl.conf.xml” file at ”/usr/local/freeswitch/conf/autoload_configs”. And make sure that the FS server is accessible via a public ip atleast for the Plivo server’s which will forward the calls.

Standard
HipChat, Plivo, Redis, Robut

Make Text to Speech Calls From Hip Chat

I’ve been using HipChat for the last one month. Before that it was IRC always. But when i joined Plivo’s DevOps family, i’ve got a lot of places to integrate plivo to automate a lot of stuffs. This i was planning to do some thing with Hipchat. There are a couple of HipChat bot’s available, i decided to use ’robut’, as it is simple, written in Ruby and easy to write plugins also. Robut need only one “Chatfile” as its config file. All the Hip Chat account and room details are mentioned in this file. The Plugins are enabled in this file.

First we need to clone the repository.

$ git clone https://github.com/justinweiss/robut.git

Now i need ’redis’ and ’plivo’ gems for my new plugin. So need to add the same in the Gemfile. Once the gems are added in to the Gemfile, we need to do a ”bundle install” to install all the necessary gems. The path for the Chatfile can be mentioned in ”bin/robut” file. Now before starting robut we need to add the basic HipChat settings ie, jabberid, nick, password and room. Now wen eed to create a plugin to use with robut. The default plugins are available in ”lib/robut/plugin/” folder. Below is the plugin which i created for making Text To Speech calls.

require 'json'
require 'timeout'
require 'securerandom'
require 'plivo'
require 'redis'

class Robut::Plugin::Call
  include Robut::Plugin

  def usage
    "#{at_nick} call <number> <message> "
  end

  def handle(time, sender_nick, message)
    new_msg = message.split(' ')
    if sent_to_me?(message) && new_msg[1] == 'call'
    num = new_msg[2]
    textt = message.split(' ').drop(3).join(' ')
      begin
        reply("Calling #{num}")
        plivo_auth_id = "XXXXXXXXXXXXXXXXX"
        plivo_auth_token = "XXXXXXXXXXXXXXXXX"
        uuid = SecureRandom.hex
        r = Redis.new(:host => "redis_host", :port => redis_port, :password => "redis_port")
        temp = {
               'text' => "#{textt}"
               }
        plivo_number = "plivo_number"
        to_number = "#{num}"
        answer_url = "http://polar-everglades-1062.herokuapp.com/#{uuid}"

        call_params = {
                      'from' => plivo_number,
                      'to' => to_number,
                      'answer_url' => answer_url,
                      'answer_method' => 'GET'
                      }
        r.set "#{uuid}", temp.to_json
        r.expire "#{uuid}", 3000
        sleep 5
        puts "Initiating plivo call"
        p = Plivo::RestAPI.new(plivo_auth_id, plivo_auth_token)
        details = p.make_call(call_params)
        reply("Summary #{details}")
      rescue
        reply("Sorry Unable to Make initiate the Call.")
      end
    end
  end
end
Standard