Monday, March 28, 2016

FreeSWITCH mod_xml_curl With mod_callcenter

FreeSWITCH Loading Queues from Database

This has been a long due post waiting in my drafts for above two years now. This was done right after when I finished loading SIP users from Database in this blog post.

I always found myself at trouble when FreeSWITCH's MOD_XML_CURL was named together with loading configurations but as soon as I gave it a shot it became very easy and interesting. 

Here is how to get started with loading queue from Database in just few minutes. 

Here is what we need to get started:

1 - Database table containing Queue's parameters.
2 - A WebServer containing our DB-to-XML converter code
3 - FreeSWITCH with mod_xml_curl installed and configured.

Creating Table for Queue Parameters 

So, first things first lets create a simple Database table:

root@DBSERVER:# su - postgres
postgres@DBSERVER:~$ psql
psql (9.4.6)
Type "help" for help.

postgres=# \c freeswitch_configs
You are now connected to database "freeswitch_configs" as user "postgres".

queue_id  serial primary key,
name VARCHAR(255) not null,
strategy VARCHAR(100) default 'longest-idle-agent',
moh_sound VARCHAR(255) default 'local_stream://moh',
announce_sound VARCHAR(255) default NULL,
announce_frequency VARCHAR(5) default NULL,
time_base_score VARCHAR(255) DEFAULT 'queue' ,
tier_rules_apply VARCHAR(25) DEFAULT 'false',
tier_rule_wait_second VARCHAR(4) DEFAULT '300',
tier_rule_wait_multiply_level VARCHAR(5) DEFAULT 'true',
tier_rule_no_agent_no_wait VARCHAR(255) DEFAULT 'false',
discard_abandoned_after VARCHAR(5) DEFAULT '60',
abandoned_resume_allowed VARCHAR(5) DEFAULT 'false',
max_wait_time VARCHAR(25) DEFAULT '0',
max_wait_time_with_no_agent VARCHAR(10) DEFAULT '0',
max_wait_time_with_no_agent_time_reached VARCHAR(10) DEFAULT '0',
record_template VARCHAR(255) DEFAULT NULL

Once Table is created you can go ahead and create some queues in it via INSERT statements.

Setting up WebServer

Alright, we've some queues defined into our database table. Now, is the time to create a web service which will query the Database and pull required configurations. Once configurations are found for a queue we will put them nicely into the required XML format and reply back.

Take the PHP sample code from my github repo:

Configure the Database IP, Port, User, and Password as required and make sure there is no error in it.
Once this is ready and running we can query to this web service and see that it returns an Error XML if no queue name is passed to it.

Test it:

If everything is configured properly and Queue: MyQueueName exists then it should print a fine XML - if that doesn't happen then check your PHP code, PHP PDO extension for PGSQL and verify that port is accessible and webserver is running. 

Configuring MOD_XML_CURL

Assuming mod_xml_curl is installed on your FreeSwitch server now its time to configure its module xml_curl to pull configurations from webserver instead of local file.

root@FREESWITCH1:# cd /usr/local/freeswitch/conf
root@FREESWITCH1:conf# cd autoload_configs/
root@FREESWITCH1:autoload_configs# vim xml_curl.conf.xml

<configuration name="xml_curl.conf" description="cURL XML Gateway">
    <binding name="configuration">         
      <param name="gateway-url" value="http://WEBSERVER/freeswitch/getconfig.php" bindings="configuration"/>

Ensure that there are no other configuration binding to conflict here.

Save and exit. Go inside freeswitch console and reload mod_xml_curl

root@FREESWITCH1:# fs_cli
fs_cli>reload mod_xml_curl 
+OK Reloading XML
+OK module unloaded
+OK module loaded

All set, now we are ready to load Queues from Database directly. To test things out issue this command

freeswitch@internal> callcenter_config queue load MynewQueue
freeswitch@internal> callcenter_config queue list

To debug XML Curl

freeswitch@internal> xml_curl debug_on

freeswitch@internal> callcenter_config queue load NOTMYQUEUE
-ERR Invalid Queue not found!

2016-03-28 19:32:21.128783 [CONSOLE] mod_xml_curl.c:323 XML response is in /tmp/3337e053-077c-4f39-9c3f-0805c4896851.tmp.xml

root@FREESWITCH1:# cat /tmp/3337e053-077c-4f39-9c3f-0805c4896851.tmp.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="freeswitch/xml">
<section name="result"><result status="not found" />


So, now we see how simple it is to pull configurations from Database - for a practice try this all with Conferences

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
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: -l -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


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*                           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=" 


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:

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 ""
loadmodule ""

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 (is_ip_rfc1918("$avp(duip)")) {
                                xlog("L_INFO", "Call is going to private IPv4 Media Server Engage RTPProxy Now\n");

                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");

                }else if(!ds_is_from_list()){


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))){

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.


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 is kamailio's WAN side, and suddenly we see which is Private IP of Kamailio communicating with an Asterisk on
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.

Sunday, July 28, 2013

Linux IPSec VPN-2: Amazon Cloud Sever & Linksys Router

This is a post in response to a comment made earlier on my previous blog post on Linux IPSec Setup asking for assistance. So here's what I could possibly do to help the needy.

This is a setup which I assisted one of my friend in creating a VPN between a Static IP Linksys Router and an Amazon cloud based server. Since we all know that Amazon cloud servers don't actually have a static public IP assigned to them instead they've a One-to-One NAT mechanism at the best so this becomes a bit trickier for anyone new to the OpenSWAN or IPSec in Linuxes.

Regardless of the Operating System the openswan package needs to be installed on the server properly. Please refer to other blogs or Google in order to install ipsec service. See this references in this link:

The topology we'll be working on is defined in the diagram below.

Now get to the configurations.

The ipsec.conf file contains these:

config setup
conn Linksys

And ipsec.secrets contains this: : PSK  "y0ur_S3cret_PSK_k3y"

Lets quickly get to the Linksys router and adjust the router according to the following settings.

Move to the VPN tab after logging in to the Linksys router.

Save the settings and restart vpn on both ends. Your VPN should start rocking by now. Ping from the LAN to the Amazon IPSec Server's Private IP and it should be replying.

Please always read logs on both the router and the linux server very carefully and figure out what they are trying to communicate. Without any logs I probably would never had created this VPN.

I hope it be of some help to someone. Have a great day.