Showing posts with label kamailio. Show all posts
Showing posts with label kamailio. Show all posts

Wednesday, August 21, 2013

RTPproxy Revisited [Kamailio 4.0]

Time and again I see people getting stuck on RTPproxy integration with Kamailio. I recently got another opportunity to put RTPproxy in between the User Phones and Kamailio setup as depicted in the following diagram.



That is similar to what I've posted earlier on this topic. In this post I will try be more verbose and write each and every step I did to have RTPs flowing.

I assume you've a Kamailio installed and working and configurations file from Asipto Knowledge Base by Daniel and that there are TWO NICs configured with Public IP and Private IP as shown in the diagram above.

The important thing which I'm looking for from the configuration is the WITH_NAT tag. follow the code and see how the NAT is handled. route[NATMANAGE] is called at almost all important routes.

The overall idea is;

1- Install RTPproxy
2- Start RTPproxy in Bridged mode
3- Make Kamailio aware of multiple NICs
4- Add Private IP asterisks in dispatcher
5- Create a new route RTPPROXY to engage RTP-proxy whenever needed
6- Call in the RTPPROXY route in the NATMANAGE route.
7- Important Things to take care of.

So lets start following the steps.

1- Installing RTPproxy

root@Kamailio:~# cd /usr/src/
root@Kamailio:~# wget http://b2bua.org/chrome/site/rtpproxy-1.2.1.tar.gz
root@Kamailio:~# tar zxvf rtpproxy-1.2.1.tar.gz
root@Kamailio:~# cd rtpproxy-1.2.1/
root@Kamailio:~# ./configure
root@Kamailio:~# make
root@Kamailio:~# make install

Setup LSB script for RTP-proxy
root@Kamailio:~# cp debian/rtpproxy-default.ex /etc/default/rtpproxy
edit the default file and put in the parameters.
root@Kamailio:~# vim /etc/default/rtpproxy

2- Start RTPproxy in Bridged mode


DAEMON_OPTS="-F -s udp:127.0.0.1:7722 -l 77.66.55.44/192.168.1.244 -d DBUG:LOG_LOCAL0 -u root"

Save and Exit

root@Kamailio:~# cp debian/rtpproxy.init /etc/init.d/rtpproxy
root@Kamailio:~# chmod a+x /etc/init.d/rtpproxy

Open up the file

root@Kamailio:~# vim /etc/init.d/rtpproxy

see that the DAEMON field points to the file in /usr/bin/rtpproxy

DAEMON=/usr/bin/rtpproxy

Lets copy the RTPproxy binary to that location.

root@Kamailio:~# cp rtpproxy /usr/bin/rtpproxy

Start up RTPproxy

root@Kamailio:~# /etc/init.d/rtpproxy start

verify that rtpproxy is running and listening on the specified 7722 

root@Kamailio:~# netstat -pln | grep rtpp
udp        0      0 127.0.0.1:7722          0.0.0.0:*                           6554/rtpproxy
Thats all.

3- Making Kamailio aware of multiple NICs


Lets move on to step 3 involving Kamailio configurations.

root@Kamailio:~# vim /usr/local/etc/kamailio/kamailio.cfg

Insert the following line in global parameters section, just under where we define "listen=" or "port=" 

mhomed=1

That will ensure that Kamailio uses its Private IP to communicate with Asterisks on Private subnet. Don't forget this.

We also need to put this line on the top definitions of kamailio.cfg file so kamailio use the NAT functions.

#!define WITH_NAT

4- Adding Asterisks to dispatcher


Now Add Private IP asterisks in dispatcher: Follow my post on adding dispatcher to the plain configurations from here: http://saevolgo.blogspot.com/2011/11/how-to-increasing-voip-services.html

5- Writing some Kamailio routing logic for RTPPROXY

That was easy, now the real thing the addition of RTPPROXY route which I modified a little bit from the last link mentioned.

To have the code working I have used the SQLOPS module configured to query kamailio.dispatcher table as the AVPOPS module was already busy.

NOTE: Using the DB query is a costly operation BUT it allows me to detect if Kamailio is sending call to Dispatcher listed IPs or not. I have a mix of Asterisks on Private Subnet and on Public Subnet and if the Asterisk dispatcher has chosen or the call is coming from is a Private IP then engage RTPproxy. This detection is handled by IPOPS module and its function is_ip_rfc1918()

loadmodule "ipops.so"
loadmodule "sqlops.so"
modparam("sqlops","sqlcon","ca=>mysql://openser:openserrw@localhost/kamailio")

Then declare the route:

# RTPProxy control
route[RTPPROXY] {
        if (is_method("INVITE")){
                sql_query("ca", "select destination from dispatcher where destination like '%$dd%'","ra");
                if($dbr(ra=>rows)>0){
                        $avp(duip)=$(du{s.select,-2,:});
                        if (is_ip_rfc1918("$avp(duip)")) {
                                xlog("L_INFO", "Call is going to private IPv4 Media Server Engage RTPProxy Now\n");
                                #rtpproxy_manage("crwie","192.168.1.244");
                                rtpproxy_manage("rwie");
                        }

                }
                else if(ds_is_from_list()){
                        if (is_ip_rfc1918("$si")) {
                                xlog("L_INFO", " Call is coming from a private IPv4 Media Server Engage RTPProxy Now\n");
                                #rtpproxy_manage("crwei","77.66.55.44");
                                rtpproxy_manage("rwei");

                        }
                }else if(!ds_is_from_list()){
                          rtpproxy_manage("rwie");

                }
      }
}

6- Using RTPPROXY route

Add the RTPPROXY route just where the FLT_NATS and FLB_NATB flags are tested.

        if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB))){
                return;
        }
                route(RTPPROXY);


Now Save and Exit the kamailio.cfg file.

Restart Kamailio.

root@Kamailio:~# /etc/init.d/kamailio stop
root@Kamailio:~# /etc/init.d/kamailio start

7- Helpful Things to know

Asterisk needs to have the peer declared for kamailio using its Private IP.

[Kamailio]
type=friend
host=192.168.1.244
port=5060
disallow=all
allow=gsm
allow=g729
allow=alaw
allow=ulaw
context=SBC-Incoming
canreinvite=no
insecure=port,invite
nat=force_rport,comedia
qualify=yes
directrtpsetup=no

See that I've used "directrtpsetup=no" so that Asterisk don't decide to go direct with the End caller.

Use xlog lines in kamailio.cfg file to follow the call.

The way I always setup my whole environment is that Kamailio handles the REGISTRATIONs and only INVITES are load-balanced to Asterisks or FreeSWITCHes where they receive the call from Kamailio peer and execute dialplan applications and IF call needs to dial out they dial the destination back to kamailio.
Kamailio needs to detect the call coming FROM the Media-Servers (ds_is_from_list() function)

So I know when a user calls in and when the call comes in from the media-servers.

Always try to first have an echo test working for calls. I use Asterisk application Echo() and when I dial in from user I get my own audio echoed back and I know that atleast my audio path is complete. This never tells you that your setup is 100% perfect but it is a good way to know if you're headed right direction.


Wireshark is a great Friend. Use it to examine everything in depth. No matter what I do I always need Wireshark to visually see what is going on with the SIP packets, that gives me everything I need to know to make things right.

root@Kamailio200:~# tcpdump -i any -s 0 -w rtp-calls.pcap -vvv
tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
1666 packets captured
Download this "rtp-calls.pcap" file and open it up in Wireshark. Click "Telephony"  from the menu bar and select "VoIP Calls"


Hit the Flow button and you'll see beautiful arrows showing the direction of SIP and RTP packets.


On the very Left Hand side is my Soft Phone's Public IP address, then 77.66.55.44 is kamailio's WAN side, and suddenly we see 192.168.1.244 which is Private IP of Kamailio communicating with an Asterisk on 192.168.1.36
The Bold arrows labeled RTP are flowing in both direction means all Perfect.

Thats all for one day. Hope to have some comments and questions on this soon.

Tuesday, March 6, 2012

Making RTPproxy work


Kamailio + RTPproxy  

In response to my previous post related to Kamailio as SBC for Media-Servers, I'm often asked to show how to make this whole setup work with RTPproxy. That means a successful two-way audio call. Remember that the original configuration found on Asipto-Blog is for Public-IPs only.

I had to do alot of hit-&-trial sort of testings with my configuration before making the configurations work for me, so I'll share all that I remember now.

UPDATE: Another new version of this page is available here.

STEP-I : Starting RTPproxy:
Start RTPproxy in bridging 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
STEP-II: Kamailio Configuration file:
Define RTPproxy controller port in modparams area.

#RTPproxy instance to be used: 7722 
#PU.BL.IC.IP<<=Transform=>> 10.1.1.1
modparam("rtpproxy", "rtpproxy_sock", "udp:127.0.0.1:7722") 

Then in route[RTPPROXY] we need to know if the call is coming from WAN/internet side or from  LAN/Private Asterisk's side so I had to put the following lines in modparams area as well for DB connectivity. This is required to query the dispatcher tables in RTPPROXY route.

# ------- AVP-OPS params ---------
modparam("avpops","db_url", DBURL)
modparam("avpops","avp_table","dispatcher")

So finally in the route for RTPproxy we querry the DB table dispatcher for inbound or oubound call requests from/to media-servers.

# RTPProxy control
route[RTPPROXY] {
#!ifdef WITH_NAT
 xlog("L_NOTICE","$rm from $fu (IP:$si:$sp) in Route[RTPPROXY]\n");
 if (is_method("BYE")) {
  xlog("L_NOTICE","$rm from $fu (IP:$si:$sp) in Route[RTPPROXY] unforced RTP Proxy STATS='$rtpstat'\n");
  unforce_rtp_proxy();
 } else if (is_method("INVITE")){
#INCOMING CALL 
  if(avp_db_query("select destination from dispatcher where destination like '%$dd%'")){
   xlog("L_NOTICE","$rm from $fu (IP:$si:$sp) in route[RTPPROXY] RTPproxy with IE Flags\n");
#INCOMING CALL NEEDS TO GOTO PRIVATE-IP ASTERISK USE - IE
   rtpproxy_manage("rie");
 
 }
  else if(avp_db_query("select destination from dispatcher where destination='sip:$si:$sp'")){
   xlog("L_NOTICE","$rm from $fu (IP:$si:$sp) in route[RTPPROXY] RTPproxy with EI Flags\n");
#INCOMING CALL FROM PRIVATE-IP ASTERISK USE - EI
   rtpproxy_manage("rei");

  }
  else
  {
   rtpproxy_manage("r");
    xlog("L_NOTICE","$rm from $fu (IP:$si:$sp) in Route[RTPPROXY] Forcing RTPproxy\n");
  }

 }
 if (!has_totag()){ 
  add_rr_param(";nat=yes");
 }
#!endif
 return;
}

RTPproxy Bridging Network Scenario
Please note that the order of I and E flags. The sequence of these flags depends on the rtpproxy running in the background and the order of PUBLICIP and PRIVATEIP while starting RTPproxy.
#/usr/sbin/rtpproxy -F -s udp:127.0.0.1:7722 -l PU.BL.IC.IP/10.1.1.1 -d DBUG:LOG_LOCAL0

The flags sequence can get inverted if I execut rtpproxy like this.Note the order of Public Ip and Private IP.
#/usr/sbin/rtpproxy -F -s udp:127.0.0.1:7722 -l 10.1.1.1/PU.BL.IC.IP -d DBUG:LOG_LOCAL0

Once done changes in configurations file save it and restart kamailio. Make sure Kamailio detects the RTPproxy socket and announces that support for Rtpproxy is enabled.

Make calls and see the RTPproxy stats at the end of each call. 

Troubleshooting Tips:

Use "tcpdump" on Kamailio server and trace ALL the traffic on ALL the interfaces i.e WAN+LAN interfaces using the following command.

#tcpdump -i any -s 0 -w SIP-TRACE.pcap -vvvv

Once a call is completed stop the trace by hitting CTRL+C; download the pcap file to your windows desktop; open the file in wireshark; analyse VoIP call and see the flow of packets between caller and callee.Reading and understanding SIP/SDP INVITEs packets between caller to Kamailio and then from Kamailio to Media-server is very critical in understanding why we are having no-audio or one-way-audio for  a bad call.

Thats all for now,I'm thinking of exploring Media-Proxy as well. I hope someday I'll post about it too.

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

Sunday, November 6, 2011

Increasing VoIP services capacity



This is a very advanced VoIP topic, but I'm sharing it here before I forget the hurdles which I faced while setting it up.


Introduction:
Setting up just one or two Media-Servers(Asterisk/FreeSWITCH/SEMS) isn't much of a headache but just one server is not enough once we start having more and more incoming call volume. Specially in larger deployments just one heavy duty server with multiple asterisks and freeSWITCH instances is never enough to handle everything efficiently and in fail-safe mode.

Current Media-Servers and their call capacity:
Remember a single instance of Asterisk on one regular server can handle 150~180 calls without any QoS issue. Running two or more instances on such a regular server may increase this capacity to around 300~380 concurrent calls but the fear of Single Point Of Failure is there all the time.

FreeSWITCH on the other hand claims to handle around 700~1200 concurrent calls on the same spec machines(rough figure to be on safe side, they claim more than this :P), but FreeSWITCH is a fairly complicated application to master for most of us.

SEMS claims to handle around 1800~2300 concurrent calls BUT..BUT SEMS is one of the hardest tool to configure and manage. Not to mention that it isn't promising any applications or services as Asterisk or FreeSWITCH are offering i.e Queues or Conferences, one may need to setup asterisk or FreeSWITCH behind SEMS. SEMS is an excellent choice for IVRs and announcements.

Problem at hand:
No matter how good you are in setting up Linux-HA, the biggest challenge of increasing VoIP media services capacity remains a big ? another big hurdle is assignment of Public IPs for each individual cluster of HA-Media Servers. Even if we manage somehow to handle capacity without thinking outside the Traditional-Asterisk-Solutions, reserving Public IPs still remains annoying problem.

Consider you've to buy Public IPs each time you need to increase VoIP services capacity along with headache of communicating with your services provider to add new IP into their call distribution list or provision traffic for new server.

Solution: 
So the solution lies in setting up a SIP-Proxy which acts as gateway distributor for all the incoming calls. This SIP-Proxy encapsulates a huge farm of Media-Servers. World only knows to communicate with you using just one Public IP belonging to the SIP-proxy.
All the Media-Servers are on Private LAN. Without any struggle anyone can put in 253 Media-Servers of all types in the Media-Servers zone. Simple and Easy ! isn't it.

RTP & SIP Handling on SBC
Using SIP-proxy as Load-distributor enables us to group all the media-servers in different resource types i.e 10 servers only handling IVRs, 13 serving Voicemails, 60 for Handling conferences only. 9 Servers as PSTN gateways etc.

SIP-proxy load-balancer will make sure each of these resources only get the requests for their service types only and all the load is distributed evenly(could be any other algorithm as well)

SIP-proxy will be sending Keep-Alive to all the Media-Servers and if any servers stops responding it marks it as Probing and continues sending KAs but not routing any calls to it until it becomes active again.

Whats NEXT:
In next post I'll try to show where to start from for setting up such an environment, what will be required, what problems I faced and their solution.

Special Thanks:
Following GURUs of VoIP from user's mailing list helped me alot in understanding and overcoming the problems I faced while setting this up.

Alex Balashov
davy van de moere
Klaus Darilion
Muhammad Danish Moosa
Muhammad Shahzad


Community list are definitely the best place to ask for minor help and understanding the problems.