– Kamailio SIP Server –

This tutorial is designed to help relative newbies (like myself) who wish to understand how exactly to install Kamailio (OpenSER) in a slightly unconventional way, as well as understand the basics of Kamailio (OpenSER). The goal of this exercise is to install Kamailio (OpenSER) on two PCs, each with a specific task, and have them interact with a MySQL server on another machine. The design here is academic: one of the Kamailio PC’s will act as a simple stateful proxy, and the other will only act as a registrar:

On a side-note: all the information here is inferred from the scattered documentation and my own use of Kamailio. So it may not be accurate. If I got it wrong somewhere, feel free to tell me. The version this tutorial was based on is 1.1.0, with earlier and later versions of Kamailio there may be variations.

Most of the information in this document can be found elsewhere, but you’ll need to read several documents to put it all together: the SER getting started, a few topics in the Kamailio (OpenSER) forums, the dokuwiki, the Kamailio (OpenSER) modules and finally this useful beginner’s guide

The pages on the website that explain how to download and install Kamailio (OpenSER) are quite good, so make sure to consult them to have the appropriate libraries Kamailio depends on. In fact in this tutorial I won’t give the detail on how to download, but I will explain the installation. However if you have a problem installing with the .deb or the .tar.gz, don’t hesitate to download a stable version from the SVN!

How does Kamailio (OpenSER) work?

If you want to do operations that differ from the usual, you might want to broadly understand the structure of Kamailio (OpenSER). Not the workings of the code (unless you want to do something REALLY special), but how it works once compiled. There’s basically two parts to Kamailio (OpenSER): the core, which is the working executable part, and the modules, which add functionality (especially SIP functionality) to the core.

The modules are by far the most important component that give Kamailio the ability to perform a specific task. The working part of the modules can be divided in two parts: the exported parameters, and the exported functions. These will both be important when it comes to setting up Kamailio with it’s configuration file: kamailio.cfg (normally located in /usr/local/etc/kamailio/). Basically the exported parameters will set up the modules to act as you wish, and the exported functions will allow you to program the handling of sip messages. You will also find that some modules depend on other modules to be able to work. For example the Registrar module depends both on the Stateless replier module and the User location implementation module. The information about specific modules, as well as their parameters, functions and dependencies can be found here.

The core provides the framework for Kamailio (OpenSER). Whatever else you will find in the configuration file is part of the core (which has parameters, functions, and more – see documentation). Basically, the core parameters will affect local setting, such as debug level, the port to listen on, whether to run in daemon mode, etc… The core routing blocks and routing constructs provide the logical structures for the handling of the messages, and the core functions provides both instruction logic (such as exit()) and instructions for message manipulation (such as rewritehost()).

Setting up the network

For the network we would like to build here’s what we absolutely need:

*Proxy: Kamailio (OpenSER) core, Transaction module (TM) for stateful transactions, Stateless replier module (SL), Record-Route and Route module (RR), Max-Forward processor module (MAXFWD) and the User location implementation (USRLOC), SIP Registrar implementation (REGISTRAR) and MySQL-backend for database API (MYSQL) modules for database connectivity (more on this later).

*Registrar: Kamailio (OpenSER) core, Stateless replier module (SL), User location implementation module (USRLOC), SIP Registrar implementation module (REGISTRAR) and MySQL-backend for database API module (MYSQL).

*Location service: MySQL server.

I’ll assume that you took my advice and downloaded Kamailio (OpenSER) from the SVN. If you used binary packages and managed to get them to work, never fear, it won’t be a problem. Just skip the part about the Kamailio (OpenSER) installation. If you really want to follow the rest of the tutorial, go to the directory where the modules are installed (.so files, they should be in /usr/local/lib/kamailio/) and delete or move (you may want them later) all module files that are not mentioned above.

Modular Kamailio (OpenSER) servers installation

To fully understand what is happening, we’ll only install the strict minimum. In order to do this, we will only install those modules that we need and that I mentioned earlier. There’s several ways to achieve this, and here’s what I find the simplest:

Edit the Makefile, and at line 304 change this instruction:

Install: all mk-install-dirs install-cfg install-bin install-modules \
       install-doc install-man

To

Install: kamailio mk-install-dirs install-cfg install-bin install-modules \
       install-doc install-man

Now, making sure that you have super-user privileges, execute:

> make install

Now we have only the core installed, we’ll add the modules that we want to the installation. First, however, we’ll need to build them (make sure that you have the appropriate libraries installed):

 > make include_module=”mysql” modules 

Now you simply need to copy the modules you want to the appropriate directory. If you followed my instructions up to here, it should be /usr/local/lib/kamailio/modules/. For the proxy you will be copying: tm.so, sl.so, rr.so, maxfwd.so, usrloc.so, registrar.so and db_mysql.so. For the registrar: sl.so, usrloc.so, registrar.so and db_mysql.so.

Setting up MySQL

I’ll assume that you managed to install MySQL on the PC destined to be a location server. You now need to install the necessary Kamailio (OpenSER) database using: kamdbctl create. You will be prompted to enter your MySQL root password (just press ENTER you haven’t put one) and domain. As far as I can ascertain, this information is in fact mostly useless. If you have a domain, enter it here, otherwise put the IP address of the machine. As well as adding the necessary database and tables. It will also add a read-only and read-write user:

*read-only : user: openser password: openserro *read-write: user: openser password: openserrw.

These users can be either a local or a remote user (fortunately), and I recommend for security reasons that you change their default passwords. If you don’t know how to do this, you may find a MySQL tutorial on the web.

One final thing remains to be done before you can use the MySQL location service, and that’s to make it accessible remotely. You’ll need to change a line in /etc/mysql/my.cnf from

Bind-address		= localhost

to

Bind-address		=  ''ip address you’ll be listening on''

Setting up Kamailio (OpenSER)

To set up Kamailio (OpenSER) you will need to modify two files: kamailio.cfg and kamctlrc. Modifying kamctlrc. is simple, and is the same on both on the proxy and on the registrar. You will have :

#
# openser control tool resource file
#
# here you can set variables used in the kamctl

## your SIP domain
SIP_DOMAIN= ''your domain…or ip of the pc with the location service''

## database type: MYSQL or PGSQL, by defaulte none is loaded
DBENGINE=MYSQL

## database host
DBHOST= ''ip of the machine with the location service''

## database name
DBNAME=openser

## database read/write user
DBRWUSER=openser

## database read only user
DBROUSER=openserro

## password for database read only user
DBROPW=openserro

## database super user
DBROOTUSER="root"

## type of aliases used: DB - database aliases; UL - usrloc aliases
## - default: none
ALIASES_TYPE="DB"

## control engine: FIFO or UNIXSOCK
## - default FIFO
CTLENGINE="FIFO"

## path to FIFO file
OSER_FIFO="FIFO"

## check ACL names; default on (1); off (0)
VERIFY_ACL=1

## ACL names - if VERIFY_ACL is set, only the ACL names from below list
## are accepted
ACL_GROUPS="local ld int voicemail free-pstn"

## verbose - debug purposes - default '0'
VERBOSE=1

This file is used by the kamctl utility for database manipulation. For the initial configuration this isn’t very useful, but we’ll be using it when we add security. The entries in lines from DBRWUSER to DBROOTUSER will depend on the configuration of your database (default values shown here).

A side note about kamctl : although the utility has many uses, you'll need to have the Mysql client installed to use it to modify your database. To see what commands are available with the utility you simply need to type

> kamctl

For kamailio.cfg the file will be different for the proxy and the registrar, as they will treat messages differently.

Setting up the Kamailio (OpenSER) proxy

The first part of the kamailio.cfg pertains to the local configuration, and we’ll basically skip over it. You will note that not all the default code needs to be replaced.

#
#
# simple quick-start config script
#
 
# ----------- global configuration parameters ------------------------
  
#Uncomment these lines to enter debugging mode
debug=9            # debug level
Fork=no
Log_stderror=yes
  

check_via=no    # (cmd. line: -v)
dns=no          # (cmd. line: -r)
rev_dns=no      # (cmd. line: -R)
listen= ip this pc will listen on
port=5060
children=4
fifo="/tmp/kamailio_fifo"
fifo_db_url="mysql://openser:openserrw@ /*ip of the machine with the location service*/ /openser"

It gets interesting once we load the modules and set their parameters.

# ------------------ module loading ----------------------------------

# Uncomment this if you want to use SQL database
loadmodule "/usr/local/lib/kamailio/modules/db_mysql.so"

loadmodule "/usr/local/lib/kamailio/modules/sl.so"
loadmodule "/usr/local/lib/kamailio/modules/tm.so"
loadmodule "/usr/local/lib/kamailio/modules/rr.so"
loadmodule "/usr/local/lib/kamailio/modules/maxfwd.so"
loadmodule "/usr/local/lib/kamailio/modules/usrloc.so"

loadmodule "/usr/local/lib/kamailio/modules/registrar.so"

# ----------------- setting module-specific parameters --------------- 
# -- usrloc params --

modparam("usrloc", "db_url", "mysql://openser:openserrw@ /*ip of the machine with the location service*/ /openser")
 
# Uncomment this if you want to use SQL database
# for persistent storage and comment the previous line
modparam("usrloc", "db_mode", 3)
 
# -- rr params --
# add value to ;lr param to make some broken UAs happy
modparam("rr", "enable_full_lr", 1)

You may have been wondering why we use registrar.so while we do not want the proxy to double as registrar… The reason is that the functions to retrieve the location of a user from the database are unfortunately part of this module, so any server that needs to query the location service needs to load it.

As for the parameters, we tell usrloc where the database is situated, and that we do not want to keep a local table for the addresses, only the remote database (modparam(“usrloc”, “db_mode”, 3)). The part of the file that pertains to the handling of messages will naturally reflect the use of a proxy.

# -------------------------  request routing logic -------------------
 
# main routing logic
 
route{
 
       # initial sanity checks -- messages with
       # max_forwards==0, or excessively long requests
       if (!mf_process_maxfwd_header("10")) {
               sl_send_reply("483","Too Many Hops");
               exit;
       };

       if (msg:len >=  2048 ) {
               sl_send_reply("513", "Message too big");
               exit;
       };

       # we record-route all messages -- to make sure that
       # subsequent messages will go through our proxy; that's
       # particularly good if upstream and downstream entities
       # use different transport protocol
       if (!method=="REGISTER")
               record_route();

       # subsequent messages withing a dialog should take the
       # path determined by record-routing
       if (loose_route()){
               route(1);
       };

       if (!uri==myself) {
               route(1);
       };

       # if the request is for other domain use UsrLoc
       #(in case, it does not work, use the following command
       # with proper names and addresses in it)
       if (uri==myself) {

               if (method=="REGISTER") {
                       route(2);
                       exit;
               };
 
               lookup("aliases");
               if (!uri==myself) {
                       route(1);
               };
 
 #native SIP destinations are handled using our USRLOC DB
               if (!lookup("location")) {
                       sl_send_reply("404", "Not Found");
                       exit;
               };
       };

       route(1);
}


route[1] {
       # send it out now; use stateful forwarding as it works reliably
       # even for UDP2TCP
       if (!t_relay()) {
               sl_reply_error();
       };
       exit;
}

# route[2] will route REGISTER messages towards the registrar
# No database connectivity is required here, as the registrar then sends the messages to the location server
route[2] {
       #rewrinting the host allows the registrar to know that the message is destined for it
       rewritehost("/*registrar ip address*/");
       if(!t_relay("udp: /*registrar ip address*/:5060")) {
               sl_reply_error();
       };
       exit;
}

Basically, the proxy merely routes (relays) the messages to the appropriate location, checking against the database if necessary. As you can see, the proxy also makes a few checks (and modifications) on the message and sends an error message if necessary.

Setting up the Kamailio (OpenSER) registrar

The registrar is obviously simpler then the proxy server, as the only thing that needs to be done is accept the REGISTER request and record it in the database. As the beginning of the code is essentially the same, we’ll only check it from the addition of the modules:

# ------------------ module loading ----------------------------------

# Uncomment this if you want to use SQL database
loadmodule "/usr/local/lib/kamailio/modules/db_mysql.so"

loadmodule "/usr/local/lib/kamailio/modules/sl.so"
loadmodule "/usr/local/lib/kamailio/modules/usrloc.so"
loadmodule "/usr/local/lib/kamailio/modules/registrar.so"
 
# ----------------- setting module-specific parameters ---------------

# -- usrloc params --
 
modparam("usrloc", "db_url", "mysql://openser:openserrw@ /*ip of the machine with the location service*/ /openser")
 
# Uncomment this if you want to use SQL database
# for persistent storage and comment the previous line
modparam("usrloc", "db_mode", 3)
 
# -------------------------  request routing logic -------------------
 
# main routing logic
 
route{
 
       # initial sanity checks -- too long messages
       if (msg:len >=  2048 ) {
               sl_send_reply("513", "Message too big");
               exit;
       };
 
       if (!uri==myself) {
               sl_send_reply("400", "Bad destination, only register messages destined for this server are accepted");
               exit;
       };
 
       # the only valid message for the registrar is the register message
       # other messages are answered with an error message
       if (uri==myself) {

               if (method=="REGISTER") {

                       sl_send_reply("100", "Trying");

                       if(!save("location")) {
                               sl_reply_error();
                       };
                       exit;
               } else {
                       sl_send_reply("403", "Forbidden");
                       exit;
               };
       };
}

Adding minimal security

We now have a fully functional but extremely insecure network. We will add security so that messages will not be accepted from just any machine. In order to do so, we will add a few modules to each server: Authentication Interface module (AUTH), Database-backend authentication module (AUTH_DB) and URI operation with database support module (URI_DB). The idea is that your users are pre-recorded in the database and both REGISTER and INVITE messages are tested against the database to check if it comes from a valid user. For both the proxy and the registrar, you will need to add the following lines to load and configure the modules correctly:

loadmodule "/usr/local/lib/kamailio/modules/auth.so"
loadmodule "/usr/local/lib/kamailio/modules/auth_db.so"
loadmodule "/usr/local/lib/kamailio/modules/uri_db.so"

modparam("auth_db", "calculate_ha1", yes)

#this line tells auth_db the name of the password column
#(for the ha1 calculation)
modparam("auth_db", "password_column", "password")

For the proxy we’ll want to modify the way INVITE messages are handled (this code replaces the if ( method==”REGISTER)):

if (method=="ACK") {
        route(1);
        exit;
} if (method=="INVITE") {
        route(3);
        exit;
} else if (method=="REGISTER") {
        route(2);
        exit;
 };

And add the corresponding route at the end of the code:

route[3] {
        # -----------------------------------------------------------------
        # INVITE Message Handler
        # -----------------------------------------------------------------
        if (!proxy_authorize("/*your domain name*/","subscriber")) { 
                proxy_challenge("/*your domain name*/ ","0");
                exit;
        } else if (!check_from()) {
                sl_send_reply("403", "Use From=ID");
                break;
        };
        consume_credentials();
        lookup("aliases"); 
        if (uri!=myself) {
                route(1);
                exit;
        };
        if (!lookup("location")) {
                sl_send_reply("404", "User Not Found");
                exit;
        };
        route(1);
}

And for the registrar you only need to add the following lines before the if(!save(“location”))

if (!www_authorize("/*your domain name*/ ", "subscriber")) {
        www_challenge("/*your domain name*/ ", "1");
        exit;
};

if (!check_to()) {
        sl_send_reply("401", "Unauthorized");
        exit;
};

consume_credentials();

The last step is to add your users in the database as suscribers. In order to do so you'll need to have the MySQL client (as mentioned earlier) installed on one of your Kamailio (OpenSER) servers. The instruction is :

> kamctl add ''username'' ''password'' ''email''

It would of course also be useful to add tls support for greater security, but fortunately there’s already a good guide for that in the Kamailio (OpenSER) documentation.

tutorial by: Alistair Doswald Project VaDeSe - VoIP security and video on demand - www.vadese.org Institute for Information and Communication Technologies(IICT) - www.iict.ch Haute École d'Ingénierie et de Gestion du Canton de Vaud(HEIG-VD)