Friday, November 11, 2011

How To: Increasing VoIP Services Capacity


This is the second part on increasing voip services capacity. In the previous post I had a high level overview of what an SBC is and how to radically increase the call-capacity. In this post we'll proceed with the architecture setup and configurational steps required.

The very first thing anyone requires is servers, either on VMware or physical. One server is required for kamailio and RTPproxy while at least two servers for asterisk (media-servers). We require at-least two servers to test our Load-Balancer and Fail-over scenarios else one server for media is enough to verify the call-media related tests.

So Install a Ubuntu Server and then follow this blog post to install Kamailio and integrate with Asterisk servers.

Make sure we've two interfaces on the SBC server for a setup like below:



Since we have a NAT environment for SIP Servers so don't forget to define NAT settings in kamailio configuration file. 
This is how we can invoke NAT settings in the configuration file.
# *** To enable nat traversal execute:
#     - define WITH_NAT
So, Just write this line on the very top of the configuration file:

#!define WITH_NAT 


So, the top few lines of kamailio.cfg look like this.

#!KAMAILIO

#!define WITH_MYSQL
#!define WITH_AUTH
#!define WITH_USRLOCDB
#!define WITH_ASTERISK
#!define WITH_NAT


As soon as we've more than one interface on our Kamailio SBC we need to explicitly tell kamailio that we are now multi-homed.
Add this in kamailio.cfg as well 

mhomed=1

Above blog post is about setting up kamailio/RTPproxy and integration with asterisk realtime sipusers table for authentication purposes. For load-balancing and Fail-over  we require to load "dispatcher module" in Kamailio.


Following line needs to be added in the kamailio configuration file.

loadmodule "dispatcher.so"

Following parameters need to be in place for the above module.


# ------- Load-balancer params ------
modparam("dispatcher", "db_url","mysql://openser:openserrw@localhost/openser")
modparam("dispatcher", "table_name", "dispatcher")
modparam("dispatcher", "setid_col", "setid")
modparam("dispatcher", "destination_col", "destination")
modparam("dispatcher", "force_dst", 1)
modparam("dispatcher", "flags", 3)
modparam("dispatcher", "dst_avp", "$avp(i:271)")
modparam("dispatcher", "cnt_avp", "$avp(i:273)")
modparam("dispatcher", "ds_ping_from", "sip:proxy@10.1.1.1")
modparam("dispatcher", "ds_ping_interval",15)
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "ds_ping_reply_codes", "class=2;code=403;code=404;code=484;class=3")

I've highlighted code=403;code=404 above. these are important since asterisks' reply with SIP 404 Not Found or SIP 484 if no peer information is provided in sip.conf. Adding the lines mentioned below in asterisk's sip.conf will allow the Kamailio SBC


[Kam-SBC]
type=friend
host=10.1.1.1
port=5060
disallow=all
allow=gsm
allow=g729
allow=alaw
allow=ulaw
context=SBC-Incoming
canreinvite=no
insecure=port,invite
dtmfmode=rfc2833
nat=yes
qualify=yes

Doing a "sip reload" in asterisk CLI should result ina notice like below:

chan_sip.c:19842 handle_response_peerpoke: Peer 'Kam-SBC' is now Reachable. (2ms / 2000ms)



Up-till now one should've SIP users successfully REGISTER on SBC using asterisk-sip realtime table. Also we've load-balancer module setup in SBC.Now add Media-Servers in the dispatcher module in the openser DB.

log into mysql,
# mysql -uopenser -popenserrw openser
INSERT INTO dispatcher (setid,destination,flags,priority,attrs,description) VALUES (1,"sip:10.1.1.3:5060",0,0,"weight=50","Asteriskl-I"),(1,"sip:10.1.1.4:5060",0,0,"weight=50","Asteriskl-II");

Next we need to restart kamailio to activate all the configuration changes. Changes in Dispatcher table can be made effective using the mi-fifo command.

#kamctl dispatcher reload

Status of the Media-Servers can be viewed in realtime using mi-fifo command on linux shell.
#kamctl dispatcher dump

[UPDATE] Forwarding  Calls to Asterisk Servers

Now, in our kamailio.cfg file we need to send the calls to the Asterisk server IPs which we just loaded in dispatcher table. 

Find this code in configuration:
# Send to Asterisk
route[TOASTERISK] {
 $du = "sip:" + $sel(cfg_get.asterisk.bindip) + ":"
   + $sel(cfg_get.asterisk.bindport);
 route(RELAY);
 exit;
}
 
Now, we see that we're hard-coding the $du, destination URI to use just one IP which we need to change to select some Load-balanced available Asterisk server's IP:PORT and route to.

Update the above code to something like this:


# Send to Asterisk
route[TOASTERISK] {
        ds_mark_dst("P");
        if(!ds_select_dst("1", "4")) {
                sl_send_reply("500", "Service Unavailable");
                xlog("L_INFO","[$fU@$si:$sp]{$rm} No destinations available for $rd \n");
                exit;
        }

        xlog("L_INFO","[$fU@$si:$sp]{$rm} From Outside World to Asterisk Box $du\n");
        rtpproxy_manage("cawei");

 route(RELAY);
 exit;
}
 

Similarly we need to detect if calls are coming in FROM Asterisk Boxes so in route FROMASTERISK put in some modifications to detect if call is coming from our own Asterisk servers.

Find the following code:

# Test if coming from Asterisk
route[FROMASTERISK] {
 if($si==$sel(cfg_get.asterisk.bindip)
   && $sp==$sel(cfg_get.asterisk.bindport))
  return 1;
 return -1;
}

And Modify it to something like this:

# Test if coming from Asterisk
route[FROMASTERISK] {
   if(ds_is_from_list()){
         rtpproxy_manage("cawie"); 
  xlog("L_INFO","[$fU@$si:$sp]{$rm} Call from Media-Server Cluster\n");
        return 1;
   }
 return -1;
}


With those above changes I believe a complete call-in , call-out scenario should be covered.

P.S: Do come back with your issues while following this tutorial and I will update it with fixes or your suggestions to help other people trying to go through this stage.

Special Considerations for RTPproxy

We need to engage RTPproxy for all inbound and outbound calls in Bridged mode. To start rtpproxy in bridged mode

#/usr/sbin/rtpproxy -F -s udp:127.0.0.1:7722 -l PU.BL.IC.IP/10.1.1.1 -d DBUG:LOG_LOCAL0

For all the incoming calls from Public Interface and terminating at Private-IP media-server  we need to rtpproxy_manage()  with "IE" flags like,rtpproxy_manage("ie")

For all outbound calls originating from Private IP media-server to some external destination should be using "EI" flags like, rtpproxy_manage("ei")

All of this will be done in RTPPROXY route. Just figure out the direction of the call and use the above mentioned flags and all the calls will have both-way media just like this diagram.
Ideal Signalling & Media flow.

If we don't use the IE/EI flags appropriately then we may end up in a call flow something like this.

Invalid Destinations for RTP/Media 
Tips:
1 -Use xlog() alot, This was my very first attempt on kamailio and I traced the whole configuration flow using the xlog() command and syslogs i.e.

xlog("L_NOTICE","$rm from $fu (IP:$si:$sp) Main Route before  ---NAT---\n");

xlog("L_NOTICE","$rm from $fu (IP:$si:$sp) in Route[NAT] fix_nat-register\n");
xlog("L_NOTICE","$rm from $fu (IP:$si:$sp) in route[RTPPROXY] RTPproxy with EI Flags\n");


2 -Get help from User's mailing list, don't expect an email with fully functional error-free configuration from there rather just hints and directions to look for problem solution.

3 -Read the module documentations.

4 -Use Wireshark as much as possible to look for packet flow. This will help you understand what's going on with the packets.

Physical-Dev Environment
I couldn't wait for physical servers and all the networking hassle so I used Oracle Virtual Box and created as many virtual machines as I required and setup a basic networking environment there. Once all the pings started to flow I build the above setup and tested calls - everything worked perfectly as expected.

My Dev Environment

Thats all for now, I hope this post be of some help for anyone interested in VoIP learning.

References:
http://nil.uniza.sk/sip/nat-fw/configuring-nat-traversal-using-kamailio-31-and-rtpproxy-server
http://kb.asipto.com/asterisk:realtime:kamailio-3.1.x-asterisk-1.6.2-astdb
http://www.kamailio.org/docs/modules/3.1.x/modules_k/rtpproxy.html#id2994642
http://www.kamailio.org/docs/modules/3.1.x/modules_k/nathelper.html
http://www.kamailio.org/dokuwiki/doku.php/pseudovariables:3.1.x#destination_uri
http://lists.sip-router.org/pipermail/sr-users/2011-September/070029.html
http://www.mail-archive.com/sr-users@lists.sip-router.org/msg07166.html

49 comments:

  1. So are you saying that you have to install Kamailio + Asterisk on box A and then the calls can be load balanced between box B1 and box B2?

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
  2. No, Not at all. kamailio alone can load-balance between Asterisk boxes.. Did I say anything as u wrote anywhere? do mention !

    ReplyDelete
  3. I'm trying to do similar design besides that each asterisk is bounded to different customer. I'm trying to find some tutorial how to direct traffic (SIP+RTP) for specific domain (cust1.mydomain.com, cust2.mydomain.com ext) to specific asterisk - could you help?

    Thanks
    Alex

    ReplyDelete
    Replies
    1. Hey Alex,
      Its easy - if you've domain table with you the each defined domain will have its ID in that table, just enter the asterisk instances in dispatcher for each specific customer with the same "groupid" as of its domain's id.
      So for any incoming domain you've to just query its ID from db and set that id for load-balancer's group of asterisk instance.
      getting the idea !?

      Delete
  4. Hello,

    First, thanks for this howto and for extras links!
    I need to dispatch call and registring request on the differents asterisk servers... But i don't know how to use the function of dispacher module...

    I'm using the config file gived by asipto.com but ... in my routing logic, i dont understand where use distpaching functionnality.

    Could you help me please ?

    Thanks in advance,
    Loic.

    ReplyDelete
    Replies
    1. Hey Chabert,
      I'm sorry for being late here. If you need my help in the topic do let me know again.

      I think I need to update this tutorial and tell explicitly where to place the new dispatcher functionality.

      Delete
  5. I think I'm having the same confusion as the first comment. Here's why. The referenced article on asterisk-kamailio realtime integration has you setting up asterisk and kamailio on the same box with each listening on different ports.

    If the scenario here is that the Kamailio is on a box by itself (no asterisk) and 2+ asterisk servers are on separate boxes, then are we still to configure the asterisk.bindip in kamailio and use WITH_ASTERISK? Is the asterisk.bindip set to 10.1.1.1 interface of the Kamailio box and the kamailio.bindip set to the public ip of the Kamailio box?

    Thanks so much!

    ReplyDelete
    Replies
    1. Hello,
      yes you are correct in understanding that this tutorial is about having Kamailio and Asterisk on different boxes.

      We definitely don't need to change the 'asterisk.bindip' param and limit ourselves to just one Asterisk.


      So, we do use the 'WITH_ASTERISK' flag and the above variables are populated but we don;t use those variables anymore in our route[TOASTERISK] instead we call in the dispatcher load-balancing function in that route.

      Please see the UPDATE.

      Delete
  6. Hello Sammy,

    Thanks for your update, is very usefull... Now i have an example, i will use loadbalance module in place of dispatch module. Loadbalance is more intelligent.

    I will use also heartbeat and kamailio for HA, and OpenStack for virtual infrastructure ...

    Many thanks,
    Loic.

    ReplyDelete
    Replies
    1. Dear Loic,
      I'm thankful to you and Stephen for making me update this post. Please note that Kamailio has only Dispatcher module whereas OpenSIPS has both Dispatcher as well as Load-Balancer modules.

      There is Difference in Dispatcher and LoadBalancer module in OpenSIPS, one is stateless while LB maintains state of the load delivered to asterisks/Media-Servers.

      I'm happy to know that you'll use OpensStack, do share your experience with openstack and how I can quickly get started with openstack.

      Thanks,
      Gohar

      Delete
    2. Hello Gohar,

      Oh yes, i have check and Dispatcher module do the same things that Load-Balancer module in Opensips ... Thanks.

      For openstack, i think you should use "RDO" scripts (RedHat). This script is very usefull for a basic installation of Openstack nodes and components...

      Regards,
      Loic.

      Delete
  7. HI,

    I am trying to setup Separate sip server and Asterisk PBX.

    In this URL : http://kb.asipto.com/asterisk:realtime:kamailio-4.0.x-asterisk-11.3.0-astdb

    They have mentioned that to make SIP server and PBX in same server.

    If i want to make a Seterate server for SIP and Asterisk what are the modification require for it ?

    Regards,

    Nishar Hamsa

    ReplyDelete
  8. Can this be implemented into Freeswitch?

    ReplyDelete
  9. Good blog congratulations

    i just have a question in this case , how can i balance register request across several asterisk boxes like invite request that you did in this example?.

    thanks

    ReplyDelete
  10. You might need some memcache of the Registrar server once the LB gives output and forward Register there...I havent done that myself for a while so let me know once you do it.

    ReplyDelete
  11. Hello, I trying to integrate this scheme on my deploy. My problem is sending valid ACK from called party to Asterisk through Kamailio. When I recieve OK message Contact header contains private IP of asterisk server, so ACK try sends back to private ip of asterisk server. Offcourse it is wrong, because private ip is reacheble only through Kamailio.

    I've rtied this:
    onreply_route[MANAGE_REPLY] {
    xdbg("incoming reply\n");

    if(status=~"[12][0-9][0-9]")
    {


    if(ds_is_from_list("1"))
    {
    xlog("L_INFO", "Trying to change CONTACT FIELD\n");
    remove_hf("Contact");
    $avp(contact)="sip:"+$tU+"@PRO.XY.PUB.IP:5060";
    insert_hf("Contact:<$avp(contact)>\r\n","Call-ID");

    }
    }
    }

    But it has no shanges with Contact (so strange, but I've read - it bug)

    Did you have this problem& And if you have- how do you resolve it? Thanks.

    ReplyDelete
  12. Thank you for the AWESOME tutorials!

    I have a question.

    We would like to use Kamailio as a routing server to route calls and registrations to a few FreePBX boxes.

    Example:

    Client 1 (client1.mydomain.com) > Kamailio > FreePBX Server 1
    Client 2 (client.2.mydomain.com) > Kamailio > FreePBX Server 2

    The FreePBX boxes will handle authentication and RTP (unless there is a way to make Kamailio aware of all the extensions in the FreePBX servers their databases, which would totally be AWESOME!)

    The reason behind this is so we can have one public IP address facing the clients while Kamailio takes care of all the internal routing.

    Is this possible?

    ReplyDelete
  13. Shouldn't forwarded registrations (in route[REGFWD]) be balanced as well?

    ReplyDelete
    Replies
    1. yes we can, but we don't want Registrations to be tracked and all related OPTIONS messages to be relayed to the Registrar MediaServer.
      So its easy to make Kamailio the Registrar and LoadBalance the Calls to Media-Servers (Asterisk in this case)

      Delete
  14. Thank you very much for your quick response. To be clear; you suggest removing the route REGFWD completely?

    ReplyDelete
    Replies
    1. Well read it first if its doing anything in sequence to other routes then leave it, else you can remove/comment it. Read and understand it first.

      Delete
  15. I want to create a Kamailio server who have dispatcher module, to loadbalance some asterisk servers who have my billing system.

    But, if I call from internet to my Public IP on Kamailio server, and then call goes to internal IP asterisk, who execute Echo App, I cant hear myself.

    If I call to my Public IP on Kamailio server, and call goes to EXTERNAL IP on Asterisk, everything works nice.
    I want this : Sip Client > Public IP Kamailio > Internal IP Kamailio > Internal IP Asterisk Server > Echo()

    I have rtpproxy running on same machine that Kamailio, and Im running with this command:


    rtpproxy -F -s udp:127.0.0.1:7722 -l PU.BL.IC.IP/172.16.1.1 -d DBUG:LOG_LOCAL0

    My asterisk have 172.16.1.2 as internal IP.

    https://www.cloudshark.org/captures/15724d2d7e51

    ReplyDelete
    Replies
    1. If you take a look at the call flow, your SDP going to the Asterisk is perfectly modified to contain the Private IP of RTPproxy, but when Asterisk send 200OK the SDP reaches Kamailio and rtpproxy don't get engaged.

      Hence the 200OK gets back to the caller with a RTP IP in SDP c= field as 172.16.1.2 [Asterisk IP]

      1- So, you need to only process the RTPproxy engagement in the NATMANAGE route of kamailio.
      2- You might need to engage the NATMANAGE route' rtpproxy handling all the time so you can set the FLT_NATB flag for all incoming calls.

      Delete
    2. Gohar, I made this scenario work with this:

      #!define ASTERISK_LAN1 172.16.1.0/24

      On NATMANAGE route, I did this:

      if(src_ip==ASTERISK_LAN1)
      {
      rtpproxy_manage("cwei");
      } else {
      rtpproxy_manage("cwie");
      }

      On TOASTERISK route I commented out the line that calls RTPPROXY ROUTE like this

      xlog("L_INFO","[$fU@$si:$sp]{$rm} From Outside World to Asterisk Box $du\n");
      #route(RTPPROXY);
      route(RELAY);

      Now, my OUTBOUND scenario is working, but I cant call SIP > SIP clients who are registered into Kamailio, they send the call to Asterisk, but in Asterisk my sip peers are UNREACHABLE.

      Another question I have is about DID, I have some trunks registered on Asterisk, and then I receive a call, I forward the call to a customer. What is the best, keep registry in kamailio and then asterisk send calls to kamailio, or forward the register to asterisk, who will have the REAL REGISTRY IP of customer, and send the call to it? Thanks for your time.

      Delete
  16. nice image, which diagram tool do you use?

    ReplyDelete
  17. Thank you for this tutorial,
    i am trying to create an environment which will include
    1 Kamailio - for load balance
    3 asterisk machines

    my questions is, do i need to mirror the asterisk machines for them to work in a load balance environment?

    thanks

    ReplyDelete
    Replies
    1. Not really, depending on your services design possibly make different pools of servers and send calls as needed...i.e "Voicemail servers defined with gid=2" whereas IVR servers with gid=1.

      Delete
    2. thanks for the fast responce
      so baisclly if a calls come in Kamailio will rout it to the server i.e ivr server , vm server
      but how will ivr server will rout it to the designated extension or q?

      Delete
    3. https://www.dropbox.com/s/ds4mfo9q90dkdn1/reply.jpg?dl=0

      Delete
    4. WOW that's great
      and all this can be done using freepbx or PIAF as the asterisk servers?

      Delete
    5. Yes anything, Asterisk, FreeSwitch, SEMS, Yate, anything as media servers..however way you want...Like I said it all depends on what you want to achieve..

      Now, mirror configuration has its own usage, and using different groups is your design...we can have same group//pool of servers with same configurations and different group of servers facilitating different roles.

      Delete
  18. This comment has been removed by the author.

    ReplyDelete
  19. I'm trying to do your tuto for 1 kamailio server and 2 asterisk server. The load balancing works but the call routing is failed. This is the notice from asterisk servers:

    handle_request_invite: Call from 'Kam-SBC' (x.x.x.x:5060) to extension 'y' rejected because extension not found in context 'SBC-Incoming'.

    Please help.

    ReplyDelete
    Replies
    1. That is related to asterisk dialplan, it clearly states that the extension.destination number you're trying to dial is not defined in the dialplan context "SBC-Incoming"

      Delete
  20. Thanks for you tutorial, it's very explanatory.
    I'm trying to use Kamailio as simple dispatcher between several Sip-Proxy of an IMS system.
    I've noticed that Kamailio does not forward "180 ringing" message, so I guess that there's something different I have to do in order to dispatch also the call.

    So, I would like to know if I can adapt your tutorial in ordered to resolve my problem. I means can I substitute the Asterisk servers with my Sip-Pproxy servers without any specific modifications to the configuration file?
    If not, what are the modifications I should apply?

    Thanks in advance

    ReplyDelete
  21. How can I use kamailio to balance the traffic towards several IMS proxy? In such a case I must send the Register and all the subsequent packets on the same IMS proxy. I've tried the dispatcher algorithm with the hash function on the call-I'd but without success. Is there any specific configuration that I should follow to do this?

    ReplyDelete
    Replies
    1. Strange, I just did the same sort of setup last week- If I don't do hash based on Call-ID then same Registrar never gets the Register with creds in it. As soon as I did this algo all problems were solved.

      The way I did was :

      route[REGFWD]{
      1- if not Register method then exit.
      2- dispatch with call-id based hash. Makes sure Register Handshake is done only on one Registrar
      3- In case of failure to route to destination invoke failure route.
      }

      I wonder how you're doing it, share code snippet as well as Register packet captures on Kamailio Proxy.

      Delete
    2. I'm using this configuration:

      # Handle SIP registrations
      route[REGISTRAR] {
      if (is_method("REGISTER"))
      {
      if (!save("location"))
      sl_reply_error();

      route(REGFWD);

      exit;
      }
      }


      route[RELAY] {
      if (!t_relay()) {
      sl_reply_error();
      }
      exit;
      }



      # Forward REGISTER to Asterisk
      route[REGFWD] {
      if(!is_method("REGISTER"))
      {
      return;
      }

      route(DISPATCH);
      }


      # Dispatch requests
      route[DISPATCH] {
      # round robin dispatching on gateways group '1'
      if(!ds_select_dst("1", "0"))
      {
      send_reply("404", "No destination");
      exit;
      }
      xlog("L_DBG", "--- SCRIPT: going to <$ru> via <$du>\n");
      route(RELAY);
      exit;
      }

      My experimental infrastructure is this: UserA -> Kamailio -> Sip proxy IMS.
      In particular the UserA first simulates the registration and after the call of two users, so this means that all the packets have this node as starting point and ending point. Then, the packers go from the UA to kamailio, it balances them towards the sip proxy, the sip proxy answer kamailio (in a stateful mode) that finally returns the packets to the UA. Vice versa, in a stateless mode kamailio should forward the packets related to the call directly to the UA.

      Delete
    3. Well for REGISTER packets all things seem to be in place. What seems to be the issue then ? SIP Dialog with same Call-ID with this algo should goto same IMS proxy, In this case REGISTER handshake.

      Delete
    4. This comment has been removed by the author.

      Delete
    5. Yes ofcourse I've an idea, but if there is a timeout...that means something is not able to connect. Most probably check Kamailio IP and port and if requests are reaching out to kamailio and further if IMS proxies are Active and responding to dispatcher ping requests.
      Send me a google hangout request and will review things for you quickly.

      Delete
  22. Hi, good tutorial man, i have been following some of your tutorials for a while and i see that in many of them you followed the popular guide "kamailio asterisk realtime integration" which i also followed and tweak them quite a bit to do authentication, load balancing with algorithm 10 and manage nat traversal issues.

    Now i managed all of that using the mechanism of forwarding register packets to Asterisk, i would like to make Kamailio to manage the whole SIP user registration process. I quite managed to do it, but i am not satisfy with the result because the behaviour in which i configured asterisk is quite weird and i dont get it.

    So if someone have a configuration example with a bit more detail of how to integrate asterisk pool and kamailio in terms of registration that could send me out that would be awesome. For what i see, you integrate them with the KAM-SBC sip peer configured but i am not following the idea, probably is quite simple

    Thank you very much, Keep the good work! :)

    ReplyDelete
    Replies
    1. Thanks for kind words. I hope to re write in detail again soon. Keep following me at blog pages on www.saevolgo.ca

      You can connect with me and I'll be happy to help you if its nothing too big.

      Seems like you're already there just need a small hint/guidance and you can do it. So let me know if you wanna take some directions.

      Delete
    2. yeah, as you can see, is nothing too difficult, and i believe, is nothing out of what you already did in your well done tutorials, if there is any other way you prefer to contact to you you tell me and i m gonna do it for sure :)

      Delete
    3. Follow me on twitter and send me a PM. I'll share skype contact. I'll be happy to help.

      Delete
    4. Sadly i dont have twitter, is it a problem if I keep registration using sipusers (sip user table, which is in asterisk database) for kamailio while i am using asterisk realtime? (both, Kamailio and asterisk farm, are reaching the same database)

      Delete