Tag Archives: VoIP

Kamailio 101 – Part 3 – Routing Blocks & Structure

I know when I look back on code I’ve written in the past I cringe a little. As you go along you learn more efficient ways to do things, so before we hop to far into Kamailio we’re going to talk about how we’ll write our kamailio.cfg file.

General Rules

Some rules of thumb to get you started with writing Kamailio configs:

  • All lines that do things (instructions) have to end with a semicolon
  • Comments start with # if single line or /* blah */ for multi-line
  • Changes to the config only take effect when you restart Kamailio
  • With what we have setup restarting Kamailio causes it to forget all about your active Registrations (Even though your devices might say they’re still registered) (For now, although we can change this behaviour)

Now we’ve got that out of the way let’s continue on from our last tutorial and start routing requests!

(We’ll skip all the config above the request_route{} block that configures the modules and their setup, it’s all very important, but we’ll touch upon that in a later tutorial I promise)

As I touched upon in the Introduction post, you define what Kamailio is and does in terms of routing SIP requests, so let’s jump straight in and get started on the blocks that take care of this.

The request_route{} Block

The request_route{} block is where all our incoming SIP requests start off. Replies / responses are handled a bit differently (more on that later), but essentially every new SIP request / message / dialog starts off here.

We can create new blocks other than request_route{} to help keep our code clean, in the same way we might define functions while programming.

Let’s take a look:

request_route {
        route(RESPOND_501);    #Jump to the RESPOND_501 block
}

route[RESPOND_501]{
        sl_reply("501", "Not Implemented");   #Send 501 reply
}

We can see in the above example, any SIP Request coming in will enter our request_route{},

In our request_route{} block we only have one instruction, which is route().

The route() command allows us to then specify another block of the config file / code to continue on from. Think of it kind of like calling a function or god forbid – a GoTo. So in this example the call comes in and then routed off to a new routing block called route[RESPOND_501],

This allows us to reuse bits of code multiple times and generally keep everything a lot cleaner.

It’s worth noting that unless you tell it to, routing to another block won’t stop Kamailio from continuing to execute it’s way through the code, it’ll do what it’s told in the other routing block and then finish what it started.

Let’s look at an example:

request_route {
        xlog("Hello, I am in the request_route");
        route(RESPOND_501);    #Jump to the RESPOND_501 block
        xlog("Back in request_route");
}

route[RESPOND_501]{
        xlog("Now I am in the respond 501 route");
        sl_reply("501", "Not Implemented");   #Send 501 reply
}

If we’re to run this code and send a SIP Request to it, we’d see each of these xlog entries in your syslog,

  • As the message firsts hits the xlog(“Hello, I am in the request_route”);, then is routed off to route[RESPOND_501]
  • In route[RESPOND_501] we’ll see the xlog(Now I am in the respond 501 route”); – Kamailio has now executed the route[RESPOND_501] block and resumes from where it was in request_route{}
  • When we get back to request_route we get the final xlog(“Back in request_route”);.

We can stop routing in a specific block using the exit; command which stops processing that request once hit. For example:

request_route {
        xlog("Hello, I am in the request_route");
        route(RESPOND_501);    #Jump to the RESPOND_501 block
        xlog("Back in request_route");
}

route[RESPOND_501]{
        xlog(Now I am in the respond 501 route");
        sl_reply("501", "Not Implemented");   #Send 501 reply
        exit;
}

Because of the exit we added in the route[RESPOND_501] Kamailio stops executing at that point, so we don’t continue passing through the config file and this time we won’t get the final xlog(“Back in request_route”); call.

Boilerplate Routes

Kamailio’s example config by default comes with a lot of preconfigured routes that can be reused over and over again, so you don’t have to create everything from scratch if you don’t want to. We’ll talk about using these in an upcoming tutorial, but for now just keep in mind there’s pre-written routing blocks for things like managing NAT, sanity checking traffic, etc.

Basic Message Routing

Let’s take a very basic use case, and write some code in the request_route{} block.

We’ll set it up so if we receive an INVITE request, we’re going to respond with a 480 Temporarily Unavailable message, and for everything else, we’ll respond with 501 “Not Implemented”.

/* Main SIP request routing logic
 * - processing of any incoming SIP request starts with this route
 * - note: this is the same as route { ... } */
request_route {
        if(method=="INVITE"){
                sl_reply("480", "Temporarily Unavailable");
                exit;
        }
        sl_reply("501", "Not Implemented");
}

So let’s break this down,

First we have a an if statement – If the method of the SIP message is an INVITE request, then execute the code inside the curly brackets.

Inside the curly brackets we’ll respond with 480 Temporarily Unavailable, using sl_reply() (You may remember from the last post that sl_reply sends a reply back to the sender of the message with the response code and text specified) and then exit, meaning we won’t continue executing what’s next in the config.

Finally outside of our if statement (so catching any SIP requests who’s method isn’t INVITE) we’ll respond with 501 “Not Implemented”.

We’ll restart Kamailio & fire up a packet capture as we did in Part 2 of the series. Let’s take a look:

REGISTER - Not Implemented - INVITE - Temporarily Unavailable

Here we can see our REGISTER message got back a 501 Not Implemented response, while our INVITE got a 480 Temporarily Unavailable response.

We’ve now expanded upon our useless SIP server, in the next tutorial we’ll cover doing something useful!

Link to Kamailio 101 – Tutorial 4 | Kamailio 101 – Tutorial 2 | Kamailio 101 – Tutorial 1

Other posts in the Kamailio 101 Series:
Kamailio 101 – Tutorial 1 – Introduction

Kamailio 101 – Tutorial 2 – Installation & First Run

Kamailio 101 – Tutorial 3 – Routing Blocks & Structure

Kamailio 101 – Tutorial 4 – Taking Registrations

Kamailio 101 – Tutorial 5 – First Call

Kamailio 101 – Tutorial 6 – Reusing Code

Kamailio 101 – Tutorial 7 – Security in Theory

Kamailio 101 – Tutorial 8 – Security in Practice

Kamailio 101 – Tutorial 9 – Adding Carrier Links

Kamailio 101 – Tutorial 10 – Recap

Kamailio 101 – Part 2 – Installation & First Run


This tutorial will use Kamailio on Ubuntu 18.04, installed from the default repos using apt-get, but these concepts will apply to any version 4.x Kamailio instance, though some of your directories & file names may differ.

After setting up our Ubuntu box we’ll update our repos and install Kamailio

apt-get update 
apt-get install kamailio*

Now we’ve installed Kamailio with all the extra modules and plugins.

In production you’d only install what you need, but this would mean our development environment won’t be complaining about not having modules, so we’re installing the lot.

So now Kamailio is installed we can get to work making it do something.

Kamailio is driven by a text based config file that defines the routing rules and how we’ll handle SIP messages.

You need to specify how to handle the different types of SIP messages (aka SIP methods / requests), in a way the other devices communicating with it will understand and that generally follows the standards.

But for our example, we’ll create a really basic config that replies to any SIP message sent to it, saying it doesn’t know how to handle that message.

We’ll open kamailio.cfg – the text file that contains all our routing info, and get to work.

vi /etc/kamailio/kamailio.cfg

You’ll see the config starts by defining what modules to load, and the config for each of these modules. For us, the defaults will work for now, let’s get to the juicy bits. Keep moving down the config file until you hit this section:

/* Main SIP request routing logic
 * - processing of any incoming SIP request starts with this route
 * - note: this is the same as route { ... } */
request_route {

The request_route section is what handles the initial SIP message that comes through.

We’ll remove all the text after request_route { leaving us with a blank canvas in terms of how we handle messages. We’ll then put in a single line to log to syslog when we get a SIP message:

/* Main SIP request routing logic
 * - processing of any incoming SIP request starts with this route
 * - note: this is the same as route { ... } */
request_route {
        xlog("I got a message");
}

If we were to stop here and restart Kamailio, when a SIP message comes into our SIP server, we’d just write an entry to syslog saying “I got a message” each time we get a SIP message, but Kamailio won’t respond to the received SIP message, it’ll just enter a log entry each time it gets a SIP message. So let’s respond to any SIP message we get with a 501 “Not Implemented” message.

/* Main SIP request routing logic
 * - processing of any incoming SIP request starts with this route
 * - note: this is the same as route { ... } */
request_route {
        xlog("I got a message");
        sl_reply("501", "Not Implemented");
}

So let’s break down the two functions we just used.

xlog(“”);

xlog is your friend.
Put simply xlog prints whatever you put inside the parenthesis to syslog.
You can use it to check to see if your messages are getting to the part of the config you want, check what pseudovariables are doing and many other things, but I’m getting ahead of myself. For now you just need to know xlog is like NoOp() in Asterisk, print() in Python, etc.

sl_reply();

sl_reply is stateless reply.
It takes two parameters, the SIP Response Code and the text to go with it, when called it sends the SIP response code and text back to the requester.
In this case we’re using SIP Response Code 501, which translates to Not Implemented, meaning the server does not support the functionality required to fulfill the request aka:

Essentially what a 501 response translates to

Putting it into Play

Now let’s restart Kamailio to make our config live, point a SIP endpoint at it and see what happens!

First we’ll restart Kamailio:

/etc/init.d/kamailio restart

All going well you’ll restart Kamailio and be good to go:

After restarting via init.d you should see [ok] if it restarted sucesfully

Now let’s fire some traffic at it and see what happens.
I’m using tcpdump to capture traffic on port 5060, and I’ve setup a SIP endpoint that’s going to try and register to our IP to see what we get:

Presto – REGISTER message received and 501 response

So this is exciting, we now have a SIP server that can do nothing except let people know it can do nothing.

Let’s dig a bit deeper into the fundamentals and then we’ll have a good foundation to start building from, and get to work on stitching everything together to create something useful.

Link to Part 3 | Back to Part 1

Other posts in the Kamailio 101 Series:
Kamailio 101 – Tutorial 1 – Introduction

Kamailio 101 – Tutorial 2 – Installation & First Run

Kamailio 101 – Tutorial 3 – Routing Blocks & Structure

Kamailio 101 – Tutorial 4 – Taking Registrations

Kamailio 101 – Tutorial 5 – First Call

Kamailio 101 – Tutorial 6 – Reusing Code

Kamailio 101 – Tutorial 7 – Security in Theory

Kamailio 101 – Tutorial 8 – Security in Practice

Kamailio 101 – Tutorial 9 – Adding Carrier Links

Kamailio 101 – Tutorial 10 – Recap

Kamailio 101 – Part 1 – Introduction

Kamailio (formerly OpenSER) is an open source SIP server, but Kamailio is a bit difficult to grasp what “it is“, but once you understand it’s all very logical.

Over this series I’ll attempt to explain what Kamailio is (and isn’t), and through a series of examples, show you how to use Kamailio to build cool stuff.

I’ll try and make it accessible for people with a background / understanding of VoIP, specifically with an understanding of SIP.

There’s a lot of meticulous documentation out there on specific Kamailio modules, but not much I could find that gives an overview of how the platform works, so over this series of Tutorials, I’ll attempt to cover the basics of using Kamailio to solve problems, as together we build a basic PBX with Kamailio, touching upon some of the common modules and core concepts of Kamailio.


So what is Kamailio ?

Kamailio is a SIP Server.

It’s a bit confusing at the start, because Kamailio isn’t like FreeSWITCH, Asterisk, YATE, an SBC, a PBX or any of other telephony platforms you may have encountered before, because out of the box, Kamailio doesn’t really do anything.

You’ve got to tell Kamailio how to do everything.

Let’s take a SIP INVITE message, used to start a call (aka session) that we might send to a PBX with the domain name biloxi.example.com and a SIP endpoint registered as ‘101’:

INVITE sip:[email protected] SIP/2.0

If we sent this message to a generic PBX, the PBX would have the logic to know that it has an extension 101 and the PBX would ring extension 101.

Our generic PBX looks at the Request URI in the INVITE message it received and has the logic predefined to know that 101 is a device it has registered and that we want to connect to that device, so sends the call to the matching device.

If we sent the same INVITE to an Asterisk box, Asterisk would take a look at our SIP INVITE message, and see if there’s an entry in the dialplan under the current context for 101. Asterisk doesn’t assume if you have a user registered on SIP/101 and receive a SIP INVITE to 101 that you want to get to SIP/101, it’d need to be told this through the dialplan.

Kamailio takes this example even further.

If we want to dial 101 on Kamailio and have it ring the device registered on 101, you have to tell Kamailio what to do when it receives an INVITE message in the first place, lookup if that destination is in our AOR (we’ll get to that) table, and then forward the INVITE to the destination if it exists, and forward the provisional responses (1xx) and finally the final response (200 OK) from the remote end back to the originator. Plus we’ve got to think about how we handle a scenario where the destination doesn’t exist, or isn’t registered, of if the destination returns a 4xx response to the INVITE, how we handle provisional responses and CANCEL messages and finally the BYE message (if we’re record routing).

Phew. This seems like a lot to handle.

It all seems pretty daunting at first, calling from one SIP Endpoint to another seems like a pretty rudimentary thing in a telephony product, but by putting how the system thinks, routes and manipulates messages up to you, we open the doors to all the possibilities.

What if you want something to sit in front of your servers and only allow certain SIP User Agents? Or load balance between several soft-switches? Or route least-cost between connected carriers and seamlessly failover if one is lost? Rate limit dodgy traffic before it hits your environment? Manage hundreds of thousands of registrations?

Kamailio can do all of that.

Kamailio can do anything you can think of (to do with signaling).

And that’s the awesome part of Kamailio. It is, what you define it to be.

So let’s get started!

Other posts in the Kamailio 101 Series:
Kamailio 101 – Tutorial 1 – Introduction

Kamailio 101 – Tutorial 2 – Installation & First Run

Kamailio 101 – Tutorial 3 – Routing Blocks & Structure

Kamailio 101 – Tutorial 4 – Taking Registrations

Kamailio 101 – Tutorial 5 – First Call

Kamailio 101 – Tutorial 6 – Reusing Code

Kamailio 101 – Tutorial 7 – Security in Theory

Kamailio 101 – Tutorial 8 – Security in Practice

Kamailio 101 – Tutorial 9 – Adding Carrier Links

Kamailio 101 – Tutorial 10 – Recap

What is a SIP Registrar?

If we want to send a SIP message to Bob’s phone, we needs to know the IP Address of Bob’s phone. There are 4,294,967,296 IPv4 addresses, so finding Bob may take a while.

Bob could let us know his IP address, but what if Bob’s IP changes? If he’s using a Softphone while he’s out to lunch and a desk phone once he gets back to the office. How do we find Bob?

SIP manages this using a SIP Registrar, essentially, when Bob goes out to lunch and starts his softphone app, the softphone checks in with the Registrar and lets the Registrar know what IP to find Bob on now (the softphone’s IP).

When he gets back to the office he closes the softphone app, as it shuts down it checks in with the Registrar again to let it know Bob’s not using the softphone any more.

So our Registrar keeps track of the IP address you can find a SIP endpoint on.

It does this using an Address on Record (AoR). It’s a record of a contact – Like Bob, and the IP Addresses to contact Bob, kind of like DNS is a record of the domain name and the IP it translates to.

A simplified example AoR might look like:

 Bob | 192.168.1.2 | Expires 1800

So if we want to send a SIP message to Bob, we look up Bob’s IP in our Address on Record list, and send it to that IP.

A Registrar takes the info received in a SIP REGISTER messages and stores the IP Address and contact info in the form of an Address on Record (AoR).

Registrars also manage expiry, if Bob’s softphone sends a SIP REGISTER message letting us know we’re on one IP address, and then his phone runs out of battery or drops out of coverage, we don’t want to keep sending SIP messages that are going to be lost, so in this case Bob has 1800 seconds left, after which his Address on Record will be discarded if he doesn’t send another REGISTER message before then.

Different SIP Registrars use different ways to store this information, and some store more info, like User-Agent, NAT information and multiple contact IP addresses. Most implementations of a SIP Registrar use some form of database back end or another to store this information. In my Kamailio Registrar example we store it in memory, but you could store it in some form of SQL database, text files, post it notes or punch card, so long a you have a quick way to look it up when needed.

So that’s a SIP Registrar in a nutshell, we’ll talk more about the REGISTER process and flow, including what the www-auth header does, the Contact header and multi endpoint registration in future posts.

SDP – Session Description Protocol – Overview

Content-Type application/sdp is something you’ll see a whole lot when using SIP for Voice over IP, especially in INVITEs and 200 OK responses.

This is because SIP uses SDP to negotiate the media setup.

While Voice over IP uses RTP for media, and SIP for signalling, the meat in this sandwich is SDP, used to negotiate the RTP parameters and payloads before going ahead.

Without SDP you’d just have random unidentified RTP streams going everywhere and no easy way to correlate them back to a Session (SIP) or guarantee both endpoints support the same codec (RTP payload).

Enter SDP, the Session Description Protocol, before any RTP is sent, SDP advertises capabilities (which codecs to use), contact information, port information (which port to send the RTP stream to) and attempts to negotiate a media session both endpoints can support.

SDP is designed to be lightweight, while SIP uses human readable headers like To and From, SDP does away with this in favour of single letters representing what that header contains.

As an interesting aside, SIP at one stage also offered one-letter headers to make it smaller on the wire, but this never really took off.

Here we can see what an SDP header looks like, showing the Session ID, Session Name, Connection Information and Media Descriptions.

SDP from an INVITE

Let’s dig a little deeper and have a look at what this SDP header actually shows that’s useful to us.

The SDP Offer

Session Identifiers

Session information

The Owner / Creator & Session ID header (abbreviated to o=) contains the SDP session ID and the session owner / creator information. This contains the SDP Session ID and the IP Address / FQDN of the owner or creator of this session. In this case the SDP Session ID is 777830 and the Session owner / creator is 195.135.145.201.

Connection Information

Receiving / listening information

Next up we’ve got the connection information header (abbreviated to c=) which contains the IP Address we want the incoming RTP stream sent to. In this example it’s coming IN on IPv4 address 192.135.154.201.

The Media Description header (m=) also contains the port we want to receive the audio on, 15246.

So in summary we’re telling the called party that we’ll be listening on IP Address 192.135.154.201 on port 15246, so they should send their RTP audio stream to that address & port.

Media Attributes

Media attributes

The Media Description header (abbreviated to m=) contains a name and address, in this case it’s audio, and sent to address (port) 15246.

After that we’ve got the RTP Audio / Video profile numbers. Because SDP is designed to be lightweight instead of saying PCMA, PCMU here each codec is assigned a number by IANA that translates to a codec. The full list is here, but 8 is equal to PCMA and 0 is equal to PCMU.

So from the Media Description header we can learn that it’s an Audio session, with media to be sent to port 15246, via RTP using PCMA or PCMU.

Different codecs can have different bitrates, so by using the Media Attribute header (Abbreviated to a=) we can set the bitrates for each. In this case both PCMA and PCMU are using a bitrate of 8000.

Summary

So to summarise we’ve told the party we’re calling our session ID is 777830 and it’s owned / created by 195.135.145.201. We support PCMA and PCMU at 8000Hz, and we’ll be listening on IPv4 on 195.135.145.201 on port 15246 for them to send their audio stream to.

The SDP Answer

Next we’ll take a look at the SDP from a 200 OK response, and work out what our session will look like.

Codec Selection

We can see this device only supports PCMA, which makes codec selection pretty easy, it’s going to be PCMA as that was also supported in the SDP offer contained in the initial INVITE.

In the scenario where both devices support the same codecs, the order in which the codecs are listed defines what codec is selected.

Connection Information

Like in the SDP offer we can see that we’re requesting incoming RTP / media to be sent to, in this case we’re asking for the RTP / media on 195.135.145.195 port 25328

Final Steps

Generally after the 200 OK is received an ACK is sent and media starts flowing in both directions between endpoints.

In this example 195.135.145.195 will send their audio (aka media / RTP) to 195.135.145.201 on port 15246 (called party to the caller) and 195.135.145.201 will send their audio to 195.135.145.195 on port 25328 (calling party to the called party).

It’s always worth keeping in mind that SIP doesn’t have to be used for Voice, nor does it have to use SDP, nor does SDP have to be used with SIP, it can be used with other protocols (IAX, H.323), and doesn’t have to negotiate RTP sessions, but could negotiate anything.

That said, the SIP – SDP – RTP sandwich is pretty ubiquitous for good reason, and while it’s true that none of these protocols require each other, the truth is, most of their usage is with one-another and it’s easier to just say “SIP uses SDP” and “SDP uses RTP” than continually saying “SIP can use SDP” and “SDP can use RTP” etc.

RTPengine – Installation & Configuration (Debian 11 / Ubuntu 19.04 and below)

This post was originally published in September 2019.
It was updated December 2021 and updated for Debian 11
An updated version has been posted here for 20.04 and above.
If you’re reading this unless you are installing this on an old release, you probably want the post linked above…

Note before using

There’s a few RTP Proxies out there (rtpproxy/mediaproxy) but rtpengine from Sipwise has simplicity and flexibility that makes you wonder how you ever used the others.

Some of it’s more impressive features:

  • Bridge Encrypted (SRTP) & Plaintext RTP Sessions
  • ICE Bridge
  • Farmable loads (Can have a pool of RTPengine instances)
  • Recording of Media Streams (In a not stupid – accidentally-fill-up-the-disk way)
  • Transcoding
  • Media repacketization

Installation

This package isn’t in the default Ubuntu/Debian repos, so we’ll get it from the git repo:

git clone https://github.com/sipwise/rtpengine.git
cd rtpengine 

Next we’ll need to install some dependencies:

apt-get install debhelper default-libmysqlclient-dev gperf iptables-dev libavcodec-dev libavfilter-dev libavformat-dev libavutil-dev libbencode-perl libcrypt-openssl-rsa-perl libcrypt-rijndael-perl libhiredis-dev libio-multiplex-perl libio-socket-inet6-perl libjson-glib-dev libdigest-crc-perl libdigest-hmac-perl libnet-interface-perl libnet-interface-perl libssl-dev libsystemd-dev libxmlrpc-core-c3-dev libcurl4-openssl-dev libevent-dev libpcap0.8-dev markdown unzip nfs-common dkms libspandsp-dev libiptc-dev libmosquitto-dev python3-websockets

The dependency you’ll get stuck on will be the G.729 library, which we have to manually compile.

VER=1.0.4

curl https://codeload.github.com/BelledonneCommunications/bcg729/tar.gz/$VER >bcg729_$VER.orig.tar.gz

tar zxf bcg729_$VER.orig.tar.gz 

cd bcg729-$VER 

git clone https://github.com/ossobv/bcg729-deb.git debian 

dpkg-buildpackage -us -uc -sa -b -rfakeroot

cd ../

dpkg -i libbcg729-*.deb

Now let’s check the RTPengine dependencies again:

dpkg-checkbuilddeps

If you get an empty output you’re good to start building the packages:

dpkg-buildpackage  --no-sign

If that completed sucessfully in the directory above you should have a bunch of .deb files:

cd ../

dpkg -i ngcp-rtpengine-daemon_*.deb ngcp-rtpengine-iptables_*.deb ngcp-rtpengine-kernel-dkms_*.deb

Getting it Running

Now we’ve got RTPengine installed let’s setup the basics,

There’s an example config file we’ll copy and edit:

mv /etc/rtpengine/rtpengine.sample.conf /etc/rtpengine/rtpengine.conf

vi /etc/rtpengine/rtpengine.conf

We’ll uncomment the interface line and set the IP to the IP we’ll be listening on:

Once we’ve set this to our IP we can start the service:

/etc/init.d/ngcp-rtpengine-daemon start

All going well it’ll start and rtpengine will be running.

You can learn about all the startup parameters and what everything in the config means in the readme.

Want more RTP info?

If you want to integrate RTPengine with Kamailio take a look at my post on how to set up RTPengine with Kamailio.

For more in-depth info on the workings of RTP check out my post RTP – More than you wanted to Know

Kamailio Bytes – MySQL Database Backend for Module Config

Many Kamailio modules require, or have additional functionality, when you’re using a database backend.

There’s a few options, but for this tutorial we’ll use a MySQL database backend.

To begin with we’ll install MySQL & Kamailio,

apt-get install kamailio* mysql-server

Next we’ll want to configure the file called kamctlrc in which we’ll add our database information so command line tools like kamcmd and kamctl can read and write from the database Kamailio is using.

vi /etc/kamailio/kamctlrc

We’ll need to set a few values, the SIP_DOMAIN, DBENGINE, DBHOST, DBNAME and DBPORT.

Next we’ll use the kamdbctl tool to create the database and tables required for Kamailio’s database driven modules.

kamdbctl create

Assuming you haven’t set a root password for MySQL you can just hit enter to leave it blank.

Next we’ll define a variable (AVP) in our Kamailio config file containing our database information. This means we only have to define it once and for each module we load we can just call this variable instead of defining our MySQL database information over and over again in the config.

In the default config we’ll define WITH_MYSQL to use MySQL database config:

#!define WITH_MYSQL

That’ll automatically put the DBURL line into play:

And we’re done, now we can call different modules that have database functionality and start using it, some examples:

modparam("usrloc", "db_url", DBURL)
...
modparam("auth_db", "db_url", DBURL)
...
modparam("permissions", "db_url", DBURL)

Implementation varies from module to module but you’ll have created the database tables and should be good to go implementing modules with database functionality.


RFC2976 / RFC6086 – SIP INFO

SIP INFO was designed to carry session related information during a session.

SIP was designed to setup and tear down sessions, with little thought given to what happens after the setup, but before the teardown.

SIP INFO (RFC2976) was designed to fill this gap. (Obsoleted by RFC6086)

It’s predominantly used now to carry DTMF info during a call.

The message body in this case contains the signal (DTMF digit 3) and the Duration (180ms).

The SIP Info standard doesn’t actually stipulate how the message body should be structured, but the above shows a defacto standard that’s now common, although not set in stone.

See also:

DTMF over IP – SIP INFO, Inband & RTP Events

RFC2833 – RTP Events

Reverse MD5 on SIP Auth

MD5 isn’t a particularly well regarded hashing function these days, but it’s still pretty ubiquitous.

SIP authentication, for the most part, still uses MD5 in the form of Message Digest Authentication,

If we were to take the password password and hash it using an online tool to generate MD5 Hashes we’d get “5f4dcc3b5aa765d61d8327deb882cf99”


If we hash password again with MD5 we’d get the same output – “5f4dcc3b5aa765d61d8327deb882cf99”,


The catch with this is if you put “5f4dcc3b5aa765d61d8327deb882cf99” into a search engine, Google immediately tells you it’s plain text value. That’s because the MD5 of password is always 5f4dcc3b5aa765d61d8327deb882cf99, hashing the same input phase “password” always results in the same output MD5 hash aka “response”.

By using Message Digest Authentication we introduce a “nonce” value and mix it (“salt”) with the SIP realm, username, password and request URI, to ensure that the response is different every time.

Let’s look at this example REGISTER flow:

We can see a REGISTER message has been sent by Bob to the SIP Server.

REGISTER sips:ss2.biloxi.example.com SIP/2.0    
Via: SIP/2.0/TLS client.biloxi.example.com:5061;branch=z9hG4bKnashds7
Max-Forwards: 70
From: Bob <sips:[email protected]>;tag=a73kszlfl
To: Bob <sips:[email protected]>
Call-ID: [email protected]
CSeq: 1 REGISTER
Contact: <sips:[email protected]>
Content-Length: 0

The SIP Server has sent back a 401 Unauthorised message, but includes the WWW-Authenticate header field, from this, we can grab a Realm value, and a Nonce, which we’ll use to generate our response that we’ll send back.

 SIP/2.0 401 Unauthorized    
Via: SIP/2.0/TLS client.biloxi.example.com:5061;branch=z9hG4bKnashds7 ;received=192.0.2.201
From: Bob <sips:[email protected]>;tag=a73kszlfl
To: Bob <sips:[email protected]>;tag=1410948204
Call-ID: [email protected]
CSeq: 1 REGISTER
WWW-Authenticate: Digest realm="atlanta.example.com", qop="auth",nonce="ea9c8e88df84f1cec4341ae6cbe5a359", opaque="", stale=FALSE, algorithm=MD5
Content-Length: 0

The formula for generating the response looks rather complex but really isn’t that bad.

HA1=MD5(username:realm:password)
HA2=MD5(method:digestURI)
response=MD5(HA1:nonce:HA2)

Let’s say in this case Bob’s password is “bobspassword”, let’s generate a response back to the server.

We know the username which is bob, the realm which is atlanta.example.com, digest URI is sips:biloxi.example.com, method is REGISTER and the password which is bobspassword. This seems like a lot to go through but all of these values, with the exception of the password, we just get from the 401 headers above.

So let’s generate the first part called HA1 using the formula HA1=MD5(username:realm:password), so let’s substitute this with our real values:
HA1 = MD5(bob:atlanta.example.com:bobspassword)
So if we drop bob:atlanta.example.com:bobspassword into our MD5 hasher and we get our HA1 hash and it it looks like 2da91700e1ef4f38df91500c8729d35f, so HA1 = 2da91700e1ef4f38df91500c8729d35f

Now onto the second part, we know the Method is REGISTER, and our digestURI is sips:biloxi.example.com
HA2=MD5(method:digestURI)
HA2=MD5(REGISTER:sips:biloxi.example.com)
Again, drop REGISTER:sips:biloxi.example.com into our MD5 hasher, and grab the output – 8f2d44a2696b3b3ed781d2f44375b3df
This means HA2 = 8f2d44a2696b3b3ed781d2f44375b3df

Finally we join HA1, the nonce and HA2 in one string and hash it:
Response = MD5(2da91700e1ef4f38df91500c8729d35f:ea9c8e88df84f1cec4341ae6cbe5a359:8f2d44a2696b3b3ed781d2f44375b3df)

Which gives us our final response of “bc2f51f99c2add3e9dfce04d43df0c6a”, so let’s see what happens when Bob sends this to the SIP Server.

REGISTER sips:ss2.biloxi.example.com SIP/2.0 
Via: SIP/2.0/TLS client.biloxi.example.com:5061;branch=z9hG4bKnashd92
Max-Forwards: 70
From: Bob <sips:[email protected]>;tag=ja743ks76zlflH
To: Bob <sips:[email protected]>
Call-ID: [email protected]
CSeq: 2 REGISTER
Contact: <sips:[email protected]>
Authorization: Digest username="bob", realm="atlanta.example.com", nonce="ea9c8e88df84f1cec4341ae6cbe5a359", opaque="", uri="sips:ss2.biloxi.example.com", response="bc2f51f99c2add3e9dfce04d43df0c6a"
Content-Length: 0
SIP/2.0 200 OK
Via: SIP/2.0/TLS client.biloxi.example.com:5061;branch=z9hG4bKnashd92;received=192.0.2.201
From: Bob <sips:[email protected]>;tag=ja743ks76zlflH
To: Bob <sips:[email protected]>;tag=37GkEhwl6
Call-ID: [email protected]
CSeq: 2 REGISTER
Contact: <sips:[email protected]>;expires=3600
Content-Length: 0

There you have it, a 200 OK response and Bob is registered on biloxi.example.com.

Update 2021: Jason Murley has contributed a much more robust version of the code below, which is way better than what I’d made!

You can find his code here.

I’ve written a little tool in Python to generate the correct response based on the nonce and values associated with it:

import hashlib

nonce = 'ea9c8e88df84f1cec4341ae6cbe5a359'
realm = 'sips:biloxi.example.com'
password = 'bobspassword'
username    =   str("bob")
requesturi  =   str(s"ips:biloxi.example.com")
print("username: " + username)
print("nonce: " + nonce)
print("realm: " + realm)
print("password: " + password)
print("\n")

HA1str = username + ":" + realm + ":" + password
HA1enc = (hashlib.md5(HA1str.encode()).hexdigest())
print ("HA1 String: " + HA1str)
print ("HA1 Encrypted: " + HA1enc)
HA2str = "REGISTER:" + requesturi
HA2enc = (hashlib.md5(HA2str.encode()).hexdigest())

print ("HA2 String: " + HA2str)
print ("HA2 Encrypted: " + HA2enc)

responsestr = HA1enc + ":" + nonce + ":" + HA2enc
print("Response String: " + responsestr)
responseenc = str((hashlib.md5(responsestr.encode()).hexdigest()))
print("Response Encrypted" + responseenc)