Category Archives: VoIP

Adding support for AMR Codec in FreeSWITCH

If you’re building IMS Networks, the AMR config is a must, but FreeSWITCH does not ship with AMR due to licencing constraints, but has all the hard work done, you just need to add the headers for AMR support and compile.

LibOpenCore has support for AMR which we build, and then with a few minor tweaks to copy the C++ header files over to the FreeSWITCH source directory, and enable support in modules.conf.

Then when building FreeSWITCH you’ve got the AMR Codec to enable you to manage IMS / VoLTE media streams from mobile devices.

Instead of copying and pasting a list of commands to do this, I’ve published a Dockerfile here you can use to build a Docker image, or on a straight Debian Buster machine if you’re working on VMs or Bare Metal if you run the commands from the Dockerfile on the VM / bare metal.

You can find the Dockerfile on my Github here,

A very unstable Diameter Routing Agent (DRA) with Kamailio

I’d been trying for some time to get Kamailio acting as a Diameter Routing Agent with mixed success, and eventually got it working, after a few changes to the codebase of the ims_diameter_server module.

It is rather unstable, in that if it fails to dispatch to a Diameter peer, the whole thing comes crumbling down, but incoming Diameter traffic is proxied off to another Diameter peer, and Kamailio even adds an extra AVP.

Having used Kamailio for so long I was really hoping I could work with Kamailio as a DRA as easily as I do for SIP traffic, but it seems the Diameter module still needs a lot more love before it’ll be stable enough and simple enough for everyone to use.

I created a branch containing the fixes I made to make it work, and with an example config for use, but use with caution. It’s a long way from being production-ready, but hopefully in time will evolve.

https://github.com/nickvsnetworking/kamailio/tree/Diameter_Fix

VoIP is an only child – ‘Gotchas’ on running VoIP applications inside Containers

It’s 2021, and everyone loves Containers; Docker & Kubernetes are changing how software is developed, deployed and scaled.

And yet so much of the Telco world still uses bare metal servers and dedicated hardware for processing.

So why not use Containers or VMs more for VoIP applications?

Disclaimer – When I’m talking VoIP about VoIP I mean the actual Voice over IP, that’s the Media Stream, RTP, the Audio, etc, not the Signaling (SIP). SIP is fine with Containers, it’s the media that has a bad time and that this post focuses on,

Virtualization Fundamentals

Once upon a time in Development land every application ran on it’s own server running in a DC / Central Office.

This was expensive to deploy (buying servers), operate (lots of power used) and maintain (lots of hardware to keep online).

Each server was actually sitting idle for a large part of the time, with the application running on it only using a some of the available resources some of the time.

One day Virtualization came and suddenly 10 physical servers could be virtualized into 10 VMs.

These VMs still need to run on servers but as each VM isn’t using 100% of it’s allocated resources all the time, instead of needing 10 servers to run it on you could run it on say 3 servers, and even do clever things like migrate VMs between servers if one were to fail.

VMs share the resources of the server it’s running on.

A server running VMs (Hypervisor) is able to run multiple VMs by splitting the resources between VMs.

If a VM A wants to run an operation at the same time a VM B & VM C, the operations can’t be run on each VM at the same time* so the hypervisor will queue up the requests and schedule them in, typically based on first-in-first out or based on a resource priority policy on the Hypervisor.

This is fine for a if VM A, B & C were all Web Servers.
A request coming into each of them at the same time would see the VM the Hypervisor schedules the resources to respond to the request slightly faster, with the other VMs responding to the request when the hypervisor has scheduled the resources to the respective VM.

VoIP is an only child

VoIP has grown up on dedicated hardware. It’s an only child that does not know how to share, because it’s never had to.

Having to wait for resources to be scheduled by the Hypervisor to to VM in order for it to execute an operation is fine and almost unnoticeable for web servers, it can have some pretty big impacts on call quality.

If we’re running RTPproxy or RTPengine in order to relay media, scheduling delays can mean that the media stream ends up “bursty”.

RTP packets needing relaying are queued in the buffer on the VM and only relayed when the hypervisor is able to schedule resources, this means there can be a lot of packet-delay-variation (PDV) and increased latency for services running on VMs.

VMs and Containers both have this same fate, DPDK and SR-IOV assist in throughput, but they don’t stop interrupt headaches.

VMs that deprive other VMs on the same host of resources are known as “Noisy neighbors”.

The simple fix for all these problems? There isn’t one.

Each of these issues can be overcome, dedicating resources, to a specific VM or container, cleverly distributing load, but it is costly in terms of resources and time to tweak and implement, and some of these options undermine the value of virtualization or containerization.

As technology marches forward we have scenarios where Kubernetes can expose FPGA resources to pass them through to Pods, but right now, if you need to transcode more than ~100 calls efficiently, you’re going to need a hardware device.

And while it can be done by throwing more x86 / ARM compute resources at the problem, hardware still wins out as cheaper in most instances.

Sorry, no easy answers here…

Using Docker to develop SIP solutions with Kamailio

VoIP networks are often large beasts, with lots of moving parts; carrier links, SBCs, application servers, etc.

While writing the other Kamailio tutorials on this blog I often find I’m spinning up several VMs to act as different parts of the network, for example in the posts on the Dispatcher module I needed to have 3 VMs to show it in action.

Instead of going to all the effort of creating VMs (or running Ansible playbooks) we can use Docker and docker-compose to create a test environment with multiple Asterisk instances to dispatch traffic to from Kamailio.

I covered the basics of using Kamailio with Docker in this post, which runs a single Kamailio instance inside Docker with a provided config file, but in this post we’ll use docker-compose to run multiple Asterisk instances and setup Kamailio to dispatch traffic to them.

I am a big Kubernetes fan, and yes, all this can be done in Kubernetes, and would be a better fit for a production environment, but for a development environment it’s probably overkill.

Like in the first post that covered Kamailio and Docker we’ll start with a Dockerfile to create the config we want.
The Dockerfile is largely unchanged from my original post on Docker and Kamailio, for the Kamailio config in this example I’m using Dispatcher with a flat text file of the targets to dispatch to (dispatcher.list), so I’ll include a command to copy the two config files into the Container when the image is created in the Dockerfile:

#Copy the config file onto the Filesystem of the Docker instance
COPY dispatcher.list /etc/kamailio/
COPY kamailio.cfg /etc/kamailio/

The Kamailio config we’re using is very similar to the Dispatcher example but with a few minor changes to the timers and setting it to use the Dispatcher data from a text file instead of a database. If you have a look at the contents of dispatcher.list you’ll see three entries; dispatcher_w_docker_asterisk_1, dispatcher_w_docker_asterisk_2 & dispatcher_w_docker_asterisk_3. These will be the hostnames of the 3 Asterisk instances we’ll create.

Next up we’ll take a look at the docker-compose file, which defines how our environment will be composed, and defines which containers will be run

The docker-compose file contains definitions about the Containers we want to run, for this example we’ll run several Asterisk instances and a single Kamailio instance.

I’m using a Docker image for Asterisk from Andrius Kairiukstis to deploy Asterisk, running a default config, so we’ll start our docker-compose file by defining a service called “asterisk” using Andrius’ image:

services:
   asterisk:
     image: andrius/asterisk
     deploy:
       mode: replicated
       replicas: 6

The replicas: 6 parameter is ignored by standard docker-compose up command, but will be used if you’re using Docker swarm, otherwise we’ll manually set the number of replicas when we run the command.

So with that defined let’s define our Kamailio service;

services:
   asterisk:
     image: andrius/asterisk
     deploy:
       mode: replicated
       replicas: 6
   kamailio_dispatcher:
     depends_on:
       - asterisk
     build:
      context: .
      dockerfile: Dockerfile
     ports:
       - "5060:5060/udp"
     restart: always

That will build Kamailio from our Dockerfile and expose port 5060,

We can run the docker-compose file with 3 replicas by running:

nick@oldfaithful:kamailio-101-tutorial/Docker_examples/Dispatcher_w_Docker$ docker-compose up --force-recreate --build --scale asterisk=3

Next if we run docker-ps we can see the running instances, the single Kamailio and the 3 Asterisk instances:

nick@oldfaithful:kamailio-101-tutorial/Docker_examples/Dispatcher_w_Docker$ docker ps

We can also query Kamailio to see how it’s Dispatcher table is looking and confirm it can reach each of our 3 instances:

nick@oldfaithful:kamailio-101-tutorial/Docker_examples/Dispatcher_w_Docker$ docker exec -it dispatcher_w_docker_kamailio_dispatcher_1 kamcmd dispatcher.list

Kamailio Bytes – OnReply Route

So far with most of our discussions about Kamailio we’ve talked about routing the initial SIP request (INVITE, REGISTER, SUBSCRIBE, etc), but SIP is not a one-message protocol, there’s a whole series of SIP messages that go into a SIP Dialog.

Sure the call may start with an INVITE, but there’s the 180 RINGING, the 200 OK and the ACK that go into getting the call actually established, and routing these in-dialog messages is just as important as routing the first INVITE.

When we’ve talked about SIP routing it’s all happened in the request_route {} block:

request_route {
        xlog("Received $rm to $ru - Forwarding");
        append_hf("X-Proxied: You betcha\r\n");
        #Forward to new IP
        forward("192.168.1.110");
}

In the example above we statelessly forward any initial requests to the IP 192.168.1.110.

All the routing from that point on happens using the standard RFC3261 in-dialog routing using the Route headers.

We can add an onreply_route{} block to handle any replies from 192.168.1.110 back to the originator.

But why would we want to?

Some simple answers would be to do some kind of manipulation to the message – say to strip a Caller ID if CLIP is turned off, or to add a custom SIP header containing important information, etc.

onreply_route{
        xlog("Got a reply $rs");
        append_hf("X-Proxied: For the reply\r\n");
}

Let’s imagine a scenario where the destination our SIP proxy is relaying traffic to (192.168.1.110) starts responding with 404 error.

We could detect this in our onreply_route{} and do something about it.

onreply_route{
        xlog("Got a reply $rs");
        if($rs == 404) {
                #If remote destination returns 404
                xlog("Got a 404 for $rU");
                #Do something about it
        }
}

In the 404 example if we were using Dispatcher it’s got easily accessed logic to handle these scenarios a bit better than us writing it out here, but you get the idea.

There are a few other special routes like onreply_route{}, failure routes and event routes, etc.

Hopefully now you’ll have a better idea of how and when to use onreply_route{} in Kamailio.

I like big Butts and I cannot lie.

In other parts of the world it’s known as a telephone test set, lineman’s handset, test phone, etc, but to me it’s a butt / butt set / buttinski.

They’re essentially ruggedized, portable telephones, often with an ability to monitor a line without looping it / going off hook, and used by techs and lineys throughout phone networks everywhere.

I carry Fluke TS52 Pro in my toolbox, it’s got a built in voltmeter, waterproof, backlit display and lots of memory storage options.

It’s a really beautiful bit of kit, (only thing missing is a TDR which the next model up has) but I very rarely use it.

The butt set is in my mind the quintessential piece of test gear, and I’ve got a few from various time periods.

The Telecom Ruggabut was launched in 1994/1995 and was standard issue prior to privatization, and was designed in Australia for the Australian market,

As such it features some uniquely Australian features such as detection for 12kHz Subscriber Pulse Metering (used in Payphones), while the “TLB” Button is tone-loop-break, a 100ms pause in dialling,

Prior to the Ruggabutt there was the Versadial and Versadial Mk2. Lightweight, tough and with a handy RJ12 jack for testing subscriber handpieces, these were made in huge numbers, by PMG and then Telecom.

And as far back as my “collection” goes is the Australian Post Office (APO) Telephone Test Handset No. 4, which lives on the Step exchange in my office, and is a simple rotary dial plus speaker, mic, switch and Neon light to denote ringing.

SIP Hold – With RFC6337

I had a discussion with a friend the other day about if hold is signified with a=sendonly or a=recvonly, which led me to revisiting the RFC to confirm, so here’s an overview of how “Call Hold” works in SIP.

By the Book

According to RFC 6337 a user can hold calls by sending a new SDP offer in an established session (Re-INVITE on active call), with an SDP payload of a=sendonly for each media stream the user want’s to hold.

The SIP Switch / PBX / UAS replies with an updated SDP where each media stream’s SDP contains a=recvonly.

So it’s both, depending on which leg you’re looking at.

In Common Practice

When a UAC puts a call in hold, it does so by sending a SIP re-INVITE, updating the SDP to include the attribute line “sendonly”

See the bottom line of the SDP is a=sendonly ? That’s denoting the call is to be put on hold,

If the call hold was sucesful the UAS sends back a 200 Ok, with the SDP attribute set to recvonly

The a=recvonly denotes the call has been held.

To retrieve the call another SIP re-invite is sent by the UAC, this time setting the media attribute back to sendrecv

If sucesful a 200 OK is sent by the UAS with the a=sendrecv also set.

FreeSWITCH WebRTC with sipML5

Most people think of SIP when it comes to FreeSWITCH, Asterisk and Kamailio, but all three support WebRTC.

FreeSWITCH makes WebRTC fairly easy to use and treats it much the same way as any SIP endpoint, in terms of registration and diaplan.

Setting up the SIP Profile

On the SIP profile we’ll need to activate WebRTC you’ll need to ensure a few lines of config are present:

    <!-- for sip over secure websocket support -->
    <!-- You need wss.pem in $${certs_dir} for wss or one will be created for you -->
    <param name="wss-binding" value=":7443"/>

Next you’ll need to restart FreeSWITCH and a self-signed certificate should get loaded,

Once you’ve restarted FreeSWITCH will fail to detect any WebSocket certificate and generate a self signed certificate for you. This means that you can verify FreeSWITCH is listening as expected using Curl:

curl https://yourhostname:7443 -vvv

You should see an error regarding the connection failing due to an invalid certificate, if so, great! Let’s put in a valid certificate.

If not double check the firewall on your server allow traffic to port TCP 7443,

Loading your TLS Certificate

WebRTC & websocket are recent standards – this means a valid TLS certificate is mandatory. So to get this to work you’ll need a valid SSL certificate.

Let’sEncrypt should work fine, if you’ve got your own CA that’s in the trusted CA list on your machine that’ll do, or I’m using a cert I generated with Mkcert.

When we restarted FreeSWITCH after adding the wss-binding config a certificate was automatically generated in the $${certs_dir} of FreeSWITCH,

You can verify where the certs_dir is by echoing out the variable in FreeSWITCH:

fs_cli -x 'eval $${certs_dir}'

Unless you’ve changed it you’ll probably find your certs in /etc/freeswitch/tls/

The certificate and private key are stored in a single file, with the Certificate and the Private Key appended to the end,

In my case the certificate is called “webrtc.pem” and the private key file is “webrtc-key.pem”,

I’ll need to start by replacing the contents of the current certificate/ key file wss.pem with the certificate I’ve got webrtc.pem, and then appending the private key – webrtc-key.pem to the end of wss.pem,

cat /home/nick/webrtc.pem > /etc/freeswitch/tls/wss.pem
cat /home/nick/webrtc-key.pem >> /etc/freeswitch/tls/wss.pem

Next up I’ll restart FreeSWITCH, and run Curl again to verify this time the certificate is valid:

curl https://yourhostname:7443 -vvv

All going well no certificate error will be reported and we can setup our WebRTC client.

Configuring sipML5

Dubango Telecom’s sipML5 is a BSD licenced HTML5 SIP client,

I’ll use the demo version on their website to connect to my FreeSWITCH WebRTC server, which you can run in your browser from here,

We’ll start by clicking the “Export Mode” button to set our wss:// URL;

Change the WebSocket Server URL to the URL of your FreeSWITCH instance (you must use a domain, not an IP Address)

If you’re running behind a NAT adding ICE servers is probably a good idea, although this will slow down connection times, you can use Google’s public STUN server by pasting in the below value:

[{ url: 'stun:stun.l.google.com:19302'}]

Finally we’ll save those settings and return back to the main tab,

You’ll need to register with a username and password that’s valid on the FreeSWITCH box, in my case I’m using 1000 with the password 1000 (exists by default),

Replace webrtc with the domain name of your FreeSWITCH instance,

Finally you should be able to click Login and see Connected above,

Then we can make calls to endpoints on FreeSWITCH using the dial box;

The Debug console in your browser will provide all the info you need to debug any issues, and you can trace WebSocket traffic using Sofia like any other SIP traffic.

Hopefully this was useful to you – I’ll cover more of WebRTC on Asterisk and also Kamailio in later posts!

Kamailio Bytes – Gotchas with Kamailio as an Asterisk Load Balancer

How do I make Kamailio work with Asterisk?

It’s a seemingly simple question, the answer to which is – however you want, sorry if that’s not a simple answer.

I’ve talked about the strengths and weaknesses of Kamailio and Asterisk in my post Kamailio vs Asterisk, so how about we use them to work together?

The State of Play

So before we go into the nitty gritty, let’s imagine we’ve got an Asterisk box with a call queue with Alice and Bob in it, set to ring those users if they’re not already on a call.

Each time a call comes in, Asterisk looks at who in the queue is not already on a call, and rings their phone.

Now let’s imagine we’re facing a scenario where the single Asterisk box we’ve got is struggling, and we want to add a second to share the load.

We do it, and now we’ve got two Asterisk boxes and a Kamailio load balancer to split the traffic between the two boxes.

Now each time a call comes in, Kamailio sends the SIP INVITE to one of the two Asterisk boxes, and when it does, that Asterisk box looks at who is in the queue and not already on a call, and then rings their phone.

Let’s imagine a scenario where a Alice & Bob are both on calls on Asterisk box A, and another call comes in this call is routed to Asterisk box B. Asterisk box B looks at who is in the queue and who is already on a call, the problem is Alice and Bob are on calls on Asterisk box A, so Asterisk box B doesn’t know they’re both on a call and tries to ring them.

We have a problem.

Scaling stateful apps is a real headache,

So have a good long hard think about how to handle these issues before going down this path!

FreeSWITCH + ESL = Programmable Voice

No great secret, I’m a big Python fan.

Recently I’ve been working on a few projects with FreeSWITCH, and looking at options for programmatically generating dialplans, instead of static XML files.

Why not Static XML?

So let’s say I define a static XML dialplan.

It works great, but if I want to change the way a call routes I need to do it from the dialplan,

That’s not ideal if you’re using a distributed cluster of FreeSWITCH instances, or if you want to update on the fly.

Static XML means we have to define our dialplan when setting up the server, and would have to reconfigure the server to change it.

So what about mod_xml_curl?

When I’ve done this in the past I’ve relied on the mod_xml_curl module.

mod_xml_curl gets the XML dialplan using Curl from a web server, and so you setup a web server using Flask/PHP/etc, and dynamically generate the dialplan when the call comes in.

This sounds good, except you can’t update it on the fly.

mod_xml_curl means call routing decisions are made at the start of the call, and can’t be changed midway through the call.

So what’s ESL?

ESL is the Event Socket Library, essentially a call comes in, an ESL request is made to an external server.

For each step in the dialplan, an ESL request will be sent to the external server which tells it to do,

ESL allows us to use all FreeSWITCH’s fantastic modules, without being limited as to having to perform the call routing logic in FreeSWITCH.

So how do I use ESL?

You’ll need two create an ESL server,

Luckily there’s premade examples for popular languages;

Once you’ve got a basic server defined we’ll need to put some config in our XML Dialplan to say “transfer all your thinking to ESL!”;

<!-- Send everything that's numbers to ESL for Processing --> 
    <extension name="esl_route">
      <condition field="destination_number" expression="^\d*$">
        <action application="socket" data="10.0.1.252:5000"/>
        <action application="log" data="ERR Made it past ESL - Play error."/>
        <action application="playback" data="ivr/ivr-call_cannot_be_completed_as_dialed"/>
      </condition>
    </extension>

In the above example my ESL server is on 10.0.1.252 / port 5000.

Now any calls coming in will be transfered to ESL and where it goes from there, is something you define in your prefered programming language.

In the next post I’ll cover how I’ve been addressing this using Python and Greenswitch.

Kamailio Bytes – UAC – Authenticate Outbound Calls

Sometimes you need Kamailio to serve as a User Agent Client, we covered using UAC to send SIP REGISTER messages and respond with the authentication info, but if you find you’re getting 401 or 407 responses back when sending an INVITE, you’ll need to use the UAC module, specifically the uac_auth() to authenticate the INVITE,

When Kamailio relays an INVITE to a destination, typically any replies / responses that are part of that dialog will go back to the originator using the Via headers.

This would be fine except if the originator doesn’t know the user name and password requested by the carrier, but Kamailio does,

Instead what we need Kamailio to do is if the response to the INVITE is a 401 Unauthorised Response, or a 407 Proxy Authentication required, intercept the request, generate the response to the authentication challenge, and send it to the carrier.

To do this we’ll need to use the UAC module in Kamailio and set some basic params:

loadmodule "uac.so"
modparam("uac", "reg_contact_addr", "10.0.1.252:5060")
modparam("uac", "reg_db_url", DBURL)
modparam("uac","auth_username_avp","$avp(auser)")
modparam("uac","auth_password_avp","$avp(apass)")
modparam("uac","auth_realm_avp","$avp(arealm)")

Next up when we relay the INVITE (using the Transaction module because we need the response to be transaction stateful).

Before we can call the t_relay() command, we need to specify a failure route, to be called if a negative response code comes back, we’ll use one called TRUNKAUTH and tell the transaction module that’s the one we’ll use by adding t_on_failure(“TRUNKAUTH”);

	$du = "sip:sip.nickvsnetworking.com:5060";
	if(is_method("INVITE")) {
		t_on_failure("TRUNKAUTH");
		t_relay();
		exit;
	   }

What we’ve done is specified to rewrite the destination URI to sip.nickvsnetworking.com, if the request type is an INVITE, it’ll load a failure route called TRUNKAUTH and proxy the request with the transaction module to sip.nickvsnetworking.com.

What we get is a 401 response back from our imaginary carrier, and included in it is a www-auth header for authentication.

To catch this we’ll create an on failure route named “TRUNKAUTH”

failure_route[TRUNKAUTH] {
    xlog("trunk auth");
    }

We’ll make sure the transaction hasn’t been cancelled, and if it has bail out (no point processing subsequent requests on a cancelled dialog).

failure_route[TRUNKAUTH] {
    xlog("trunk auth");
    if (t_is_canceled()) {
        exit;
    }

And determine if the response code is a 401 Unauthorised Response, or a 407 Proxy Authentication required (Authentication requests from our upstream carrier):

failure_route[TRUNKAUTH] {
    xlog("trunk auth");
    if (t_is_canceled()) {
        exit;
    }
	xlog("Checking status code");
    if(t_check_status("401|407")) {
	xlog("status code is valid auth challenge");
    }
}

Next we’ll define the username and password we want to call upon for this challenge, and generate an authentication response based on these values using the uac_auth() command,

failure_route[TRUNKAUTH] {
    xlog("trunk auth");
    if (t_is_canceled()) {
        exit;
    }
	xlog("Checking status code");
    if(t_check_status("401|407")) {
	xlog("status code is valid auth challenge");
        $avp(auser) = "test";
        $avp(apass) = "test";
        uac_auth();

    }
}

Then finally we’ll relay that back to the carrier with our www-auth header populated with the challenge response;

failure_route[TRUNKAUTH] {
    xlog("trunk auth");
    if (t_is_canceled()) {
        exit;
    }
	xlog("Checking status code");
    if(t_check_status("401|407")) {
	xlog("status code is valid auth challenge");
        $avp(auser) = "test";
        $avp(apass) = "test";
        uac_auth();
	xlog("after uac_auth");
        t_relay();
        exit;
    }
}

And done!

We can get this data from the UAC database so we don’t need to load these values directly into our config file too using the SQLops module.

As always I’ve put the running code example on my GitHub.

Diameter and SIP: Registration-Termination-Request / Answer

These posts focus on the use of Diameter and SIP in an IMS / VoLTE context, however these practices can be equally applied to other networks.

The Registration-Termination Request / Answer allow a Diameter Client (S-CSCF) to indicate to the HSS (Diameter Server) that it is no longer serving that user and the registration has been terminated.

Basics:

The RFC’s definition is actually pretty succinct as to the function of the Server-Assignment Request/Answer:

The Registration-Termination-Request is sent by a Diameter Multimedia server to a Diameter Multimedia client in order to request the de-registration of a user.

Reference: TS 29.229

The Registration-Termination-Request commands are sent by a S-CSCF to indicate to the Diameter server that it is no longer serving a specific subscriber, and therefore this subscriber is now unregistered.

There are a variety of reasons for this, such as PERMANENT_TERMINATION, NEW_SIP_SERVER_ASSIGNED and SIP_SERVER_CHANGE.

The Diameter Server (HSS) will typically send the Diameter Client (S-CSCF) a Registration-Termination-Answer in response to indicate it has updated it’s internal database and will no longer consider the user to be registered at that S-CSCF.

Packet Capture

I’ve included a packet capture of these Diameter Commands from my lab network which you can find below.

Other Diameter Cx (IMS) Calls

User-Authorization-Request / User-Authorization-Answer
Server-Assignment-Request / Server-Assignment-Answer
Location-Info-Request / Location-Info-Answer
Multimedia-Auth-Request / Multimedia-Auth-Answer
Registration-Termination-Request / Registration-Termination-Answer
Push-Profile-Request / Push-Profile-Answer

References:

3GPP Specification #: 29.229

RFC 4740 – Diameter Session Initiation Protocol (SIP) Application

Diameter-User-Authorization-Request-Command-Code-300-Packet-Capture

Diameter and SIP: User-Authorization-Request/Answer

These posts focus on the use of Diameter and SIP in an IMS / VoLTE context, however these practices can be equally applied to other networks.

The Diameter User-Authorization-Request and User-Authorization-Answer commands are used as the first line of authorization of a user and to determine which Serving-CSCF to forward a request to.

Basics

When a SIP Proxy (I-CSCF) receives an incoming SIP REGISTER request, it sends a User-Authorization-Request to a Diameter server to confirm if the user exists on the network, and which S-CSCF to forward the request to.

When the Diameter server receives the User-Authorization-Request it looks at the User-Name (1) AVP to determine if the Domain / Realm is served by the Diameter server and the User specified exists.

Assuming the user & domain are valid, the Diameter server sends back a User-Authorization-Answer, containing a Server-Capabilities (603) AVP with the Server-Name of the S-CSCF the user will be served by.

I always find looking at the packets puts everything in context, so here’s a packet capture of both the User-Authorization-Request and the User-Authorization-Answer.

First Registration

If this is the first time this Username / Domain combination (Referred to in the RFC as an AOR – Address of Record) is seen by the Diameter server in the User-Authorization-Request it will allocate a S-CSCF address for the subscriber to use from it’s pool / internal logic.

The Diameter server will store the S-CSCF it allocated to that Username / Domain combination (AoR) for subsequent requests to ensure they’re routed to the same S-CSCF.

The Diameter server indicates this is the first time it’s seen it by adding the DIAMETER_FIRST_REGISTRATION (2001) AVP to the User-Authorization-Answer.

Subsequent Registration

If the Diameter server receives another User-Authorization-Request for the same Username / Domain (AoR) it has served before, the Diameter server returns the same S-CSCF address as it did in the first User-Authorization-Answer.

It indicates this is a subsequent registration in much the same way the first registration is indicated, by adding an DIAMETER_SUBSEQUENT_REGISTRATION (2002) AVP to the User-Authorization-Answer.

User-Authorization-Type (623) AVP

An optional User-Authorization-Type (623) AVP is available to indicate the reason for the User-Authorization-Request. The possible values / reasons are:

  • Creating / Updating / Renewing a SIP Registration (REGISTRATION (0))
  • Establishing Server Capabilities & Registering (CAPABILITIES (2))
  • Terminating a SIP Registration (DEREGISTRATION (1))

If the User-Authorization-Type is set to DEREGISTRATION (1) then the Diameter server returns the S-CSCF address in the User-Authorization-Answer and then removes the S-SCSF address it had associated with the AoR from it’s own records.

Other Diameter Cx (IMS) Calls

User-Authorization-Request / User-Authorization-Answer
Server-Assignment-Request / Server-Assignment-Answer
Location-Info-Request / Location-Info-Answer
Multimedia-Auth-Request / Multimedia-Auth-Answer
Registration-Termination-Request / Registration-Termination-Answer
Push-Profile-Request / Push-Profile-Answer

References:

3GPP Specification #: 29.229

RFC 4740 – Diameter Session Initiation Protocol (SIP) Application

Diameter - Server Assignment Answer - All

Diameter and SIP: Server-Assignment-Request/Answer

These posts focus on the use of Diameter and SIP in an IMS / VoLTE context, however these practices can be equally applied to other networks.

The Server-Assignment-Request/Answer commands are used so a SIP Server can indicate to a Diameter server that it is serving a subscriber and pull the profile information of the subscriber.

Basics:

The RFC’s definition is actually pretty succinct as to the function of the Server-Assignment Request/Answer:

The main functions of the Diameter SAR command are to inform the Diameter server of the URI of the SIP server allocated to the user, and to store or clear it from the Diameter server.

Additionally, the Diameter client can request to download the user profile or part of it.

RFC 4740 – 8.3

The Server-Assignment-Request/Answer commands are sent by a S-CSCF to indicate to the Diameter server that it is now serving a specific subscriber, (This information can then be queried using the Location-Info-Request commands) and get the subscriber’s profile, which contains the details and identities of the subscriber.

Typically upon completion of a successful SIP REGISTER dialog (Multimedia-Authentication Request), the SIP Server (S-CSCF) sends the Diameter server a Server-Assignment-Request containing the SIP Username / Domain (referred to as an Address on Record (SIP-AOR) in the RFC) and the SIP Server (S-CSCF)’s SIP-Server-URI.

The Diameter server looks at the SIP-AOR and ensures there are not currently any active SIP-Server-URIs associated with that AoR. If there are not any currently active it then stores the SIP-AOR and the SIP-Server-URI of the SIP Server (S-CSCF) serving that user & sends back a Server-Assignment-Answer.

For most request the Subscriber’s profile is also transfered to the S-SCSF in the Server-Assignment-Answer command.

SIP-Server-Assignment-Type AVP

The same Server-Assignment-Request command can be used to register, re-register, remove registration bindings and pull the user profile, through the information in the SIP-Server-Assignment-Type AVP (375),

Common values are:

  • NO_ASSIGNMENT (0) – Used to pull just the user profile
  • REGISTRATION (1) – Used for first registration
  • RE_REGISTRATION (2) – Updating / renewing registration
  • USER_DEREGISTRATION (5) – User has deregistered

Complete list of values available here.

Cx-User-Data AVP (User Profile)

The Cx-User-Data profile contains the subscriber’s profile from the Diameter server in an XML formatted dataset, that is contained as part of the Server-Assignment-Answer in the Cx-User-Data AVP (606).

The profile his tells the S-CSCF what services are offered to the subscriber, such as the allowed SIP Methods (ie INVITE, MESSAGE, etc), and how to handle calls to the user when the user is not registered (ie send calls to voicemail if the user is not there).

There’s a lot to cover on the user profile which we’ll touch on in a later post.

Other Diameter Cx (IMS) Calls

User-Authorization-Request / User-Authorization-Answer
Server-Assignment-Request / Server-Assignment-Answer
Location-Info-Request / Location-Info-Answer
Multimedia-Auth-Request / Multimedia-Auth-Answer
Registration-Termination-Request / Registration-Termination-Answer
Push-Profile-Request / Push-Profile-Answer

References:

3GPP Specification #: 29.229

RFC 4740 – Diameter Session Initiation Protocol (SIP) Application

Diameter and SIP: Location-Info-Request / Answer

These posts focus on the use of Diameter and SIP in an IMS / VoLTE context, however these practices can be equally applied to other networks.

The Location-Information-Request/Answer commands are used so a SIP Server query a Diameter to find which P-CSCF a Subscriber is being served by

Basics:

The RFC’s definition is actually pretty succinct as to the function of the Server-Assignment Request/Answer:

The Location-Info-Request is sent by a Diameter Multimedia client to a Diameter Multimedia server in order to request name of the server that is currently serving the user.Reference: 29.229-

The Location-Info-Request is sent by a Diameter Multimedia client to a Diameter Multimedia server in order to request name of the server that is currently serving the user.

Reference: TS 29.229

The Location-Info-Request commands is sent by an I-CSCF to the HSS to find out from the Diameter server the FQDN of the S-CSCF serving that user.

The Public-Identity AVP (601) contains the Public Identity of the user being sought.

Here you can see the I-CSCF querying the HSS via Diameter to find the S-CSCF for public identity 12722123

The Diameter server sends back the Location-Info-Response containing the Server-Name AVP (602) with the FQDN of the S-CSCF.

Packet Capture

I’ve included a packet capture of these Diameter Commands from my lab network which you can find below.

Other Diameter Cx (IMS) Calls

User-Authorization-Request / User-Authorization-Answer
Server-Assignment-Request / Server-Assignment-Answer
Location-Info-Request / Location-Info-Answer
Multimedia-Auth-Request / Multimedia-Auth-Answer
Registration-Termination-Request / Registration-Termination-Answer
Push-Profile-Request / Push-Profile-Answer

References:

3GPP Specification #: 29.229

RFC 4740 – Diameter Session Initiation Protocol (SIP) Application

SIP INVITE from Osmo-SIP-Connector used for Mobile Network Call Control

GSM with Osmocom: Call routing in GSM

So we’ve got a functional network, but let’s dive deeper into what we can do to connect it with other networks and how things work in “the real world”.

Media Handling – OsmoMGW

The Audio/Voice (media stream) data on a call between subscribers does not go directly between the subscribers and instead needs to be proxed relayed. The reason for this is because there’s no direct link from one BTS to another BTS (even if they are served by the same BSC) and as our subscribers can move from cell to cell while on a call – which may mean moving from one BSC to another depending on where they’re heading – we need to have a single point for the audio to remain.

To handle this a Media Gateway is used, a single point for call audio to be “anchored” – meaning regardless of which BTS/BSC is serving the subscribers on either end of the call, the media will be sent by both parties to a single destination (The Media Gateway), and that destination will send the audio to the other party.

The Media gateway relays / proxies the Media Stream – the RTP packets containing the call audio. OsmoMSC provides the SDP payload containing the codecs and RTP details for the session via MGCP (Media Gateway Control Protocol) to the OsmoMGW which relays the audio.

In terms of running osmo-mgw we installed it earlier,

The only parameter you really need to change is the rtp bind-ip,

On the MGW you can also limit and restrict the codecs supported and also allow or disallow transcoding.

MNCC-SAP & Call Routing

In it’s default mode, the OsmoMSC will look at the destination a call is being sent to, and if the destination is a subscriber on the network (in it’s VLR), will route the call to that subscriber

This on-net only mode is great but it puts our network on an island – cut off from the outside world.

Calls between MSCs, to the PSTN and users everywhere else are not possible in this scenario.

3GPP defined “MNCC-SAP” (Mobile Network Call Control – Service Access Point) a protocol for handling calls to/from destinations outside of the local MSC.

When in MNCC-SAP mode all calls (even on-net calls between subscribers on the same MSC) are routed to the external MNCC-SAP, and left up to it to determine how to route the call.

Configuring Osmo-MSC to talk MNCC

As we just covered by default Osmo-MSC only switches calls internally between subscribers, so we’ll need to turn off this behaviour and isntead reconfigure it to talk MNCC-SAP.

To do this we’ll telnet / VTY into Osmo-MSC;

root@gsm-bts:/etc/osmocom# telnet localhost 4254
Welcome to the OsmoMSC VTY interface
OsmoMSC - Osmocom Circuit-Switched Core Network implementation
OsmoMSC> enable
OsmoMSC# configure terminal
OsmoMSC(config)# msc
OsmoMSC(config-msc)# mncc external /tmp/msc_mncc
OsmoMSC(config-msc)# end
OsmoMSC# cop run st
Configuration saved to /etc/osmocom/osmo-msc.cfg

After making this change we have to restart OsmoMSC;

systemctl restart osmo-msc

Now OsmoMSC will not switch calls locally, but instead when a mobile originated call comes to the MSC, it will signal to the external MNCC via the file sock at /tmp/msc_mncc,

MNCC-SAP sounds great but platform X only speaks SIP

Enter the Osmo-SIP-Connector, which takes the MNCC-SAP messages and converts them to SIP.

From here you can tie the call control functions of the MNC into any SIP software such as Kamailio, FreeSwitch, Asterisk, etc, to handle call routing, number translation, application services like voicemail and conferencing, etc, etc.

On my to-do list is to make a call between one subscriber on GSM and one on VoLTE, I’ll cover that in a subsequent post.

So anywho, let’s get Osmo-SIP-Connector setup,
I’m running it on the same box as the MSC on 10.0.1.201,
My softphone client is running on 10.0.1.252

root@gsm-bts:/etc/osmocom# apt-get install osmo-sip-connector
root@gsm-bts:/etc/osmocom# telnet localhost 4256
Welcome to the OsmoSIPcon VTY interface
OsmoSIPcon> enable
OsmoSIPcon# configure t
OsmoSIPcon(config)# mncc
OsmoSIPcon(config-mncc)# socket-path /tmp/msc_mncc
OsmoSIPcon(config-mncc)# exit
OsmoSIPcon(config)# sip
OsmoSIPcon(config-sip)# local 10.0.1.201 5060
OsmoSIPcon(config-sip)# remote 10.0.1.252 5060
OsmoSIPcon(config-sip)# end
OsmoSIPcon# cop run st
Configuration saved to /etc/osmocom/osmo-sip-connector.cfg

Now any Mobile Originated calls will result in a SIP INVITE being sent to 10.0.1.252 port 5060 (using UDP).

Any SIP INVITES where the request URI is a valid MSISDN @ 10.0.1.201 from 10.0.1.252 will be routed to the correct subscriber for that MSISDN.

A small note – The GSM codec is (unsurprisingly) used as the codec for GSM calls by default.

Some handsets support different codecs, but many off-the-shelf IP phones don’t include GSM support, so you may find you’re required to transcode between codecs if there is no support for the other codecs.

So now we’re able to define our call routing logic in something that speaks SIP and connect calls between multiple MSCs, VoLTE / IMS networks and fixed networks, all based on what we do with the SIP.

Local Call, Local Switch

If two subscribers on the same BSC call each other, the RTP / call audio will route to the MGW where it’s anchored.

This makes sense from a mobility standpoint, but adds load to the MGW and relies on a quality A interface connection, which may be an issue depending on what backhaul options you have.

Local Call, Local Switch is a 3GPP spec to allow the RTP / call audio to act as the RTP proxy instead of the MGW.

There’s a good talk on how this works from OsmoDevCOn 2018 you can find here.

3GPP TS 23.284 version 15.0.0 Release 15 / ETSI TS 123 284 V15.0.0

Kamailio Bytes – Docker and Containers

I wrote about using Ansible to automate Kamailio config management, Ansible is great at managing VMs or bare metal deployments, but for Containers using Docker to build and manage the deployments is where it’s at.

I’m going to assume you’ve got Docker in place, if not there’s heaps of info online about getting started with Docker.

The Dockerfile

The Kamailio team publish a Docker image for use, there’s no master branch at the moment, so you’ve got to specify the version; in this case kamailio:5.3.3-stretch.

Once we’ve got that we can start on the Dockerfile,

For this example I’m going to include

#Kamailio Test Stuff
FROM kamailio/kamailio:5.3.3-stretch

#Copy the config file onto the Filesystem of the Docker instance
COPY kamailio.cfg /etc/kamailio/

#Print out the current IP Address info
RUN ip add

#Expose port 5060 (SIP) for TCP and UDP
EXPOSE 5060
EXPOSE 5060/udp

Once the dockerfile is created we can build an image,

docker image build -t kamtest:0.1 .

And then run it,

docker run kamtest:0.1

Boom, now Kamailio is running, with the config file I pushed to it from my Dockerfile directory,

Now I can setup a Softphone on my local machine and point it to the IP of the Docker instance and away we go,

Where the real power here comes in is that I can run that docker run command another 10 times, and have another 10 Kamailio instannces running.

Tie this in with Kubernetes or a similar platform and you’ve got a way to scale and manage upgrades unlike anything you’d get on Bare Metal or VMs.

I’ve uploaded a copy of my Dockerfile for reference, you can find it on my GitHub.

Kamailio Proxy-CSCF Pull

I had a few headaches getting the example P-CSCF example configs from the Kamailio team to run, recent improvements with the IPsec support and code evolution meant that the example config just didn’t run.

So, after finally working out the changes I needed to make to get Kamailio to function as a P-CSCF, I took the plunge and made my first pull request on the Kamailio project.

And here it is!

https://github.com/kamailio/kamailio/pull/2203

It’s now in the master branch, so if you want to setup a P-CSCF using Kamailio, give it a shot, as the example config finally works!

SIP Register – Lesser Known Features

In the past we’ve covered what a SIP Registrar does, how to build one, and covered some misconceptions about what being Registered means, but there’s a few little-utilized features of SIP Registration that are quite useful.

A lot of people think there’s a one-to-one relationship between a registration Address on Record, and a username.

That doesn’t have to be the case, there are some platforms that only allow a single registration for a single username, but the RFC itself allows multiple registrations for a single username.

REGISTER requests add, remove, and query bindings.

A REGISTER request can add a new binding between an address-of-record and one or more contact addresses.

Registration on behalf of a particular address-of-record can be performed by a suitably authorized third party.

A client can also remove previous bindings or query to determine which bindings are currently in place for an address-of-record.

RFC 3261 – 10.2

Let’s say you’ve got a SIP phone on your desk at the office and at home.

What we could do is create a different username and password for home & work, and then setup some time based forward rules to ring the office from 9-5 and home outside of that.

You could register both with the same username and password, and then unplug the one at home before you leave to work, get to work, plug in your office phone, unplug it before you leave to go home, and when you get home plug back in your home phone, or if multi-device registration is supported, register both and have incoming calls ring on both.

Admittedly, platforms that support this are the exception, not the rule, but the RFC does allow it.

The other little known feature in SIP Registration is that you can query the SIP Registrar to get the list of Addresses on Record.

So there you go, factoids about SIP REGISTER method!