Tag Archives: SIP

Handling multiple SIP headers with the same name in Kamailio

The SIP RFC allows for multiple SIP headers to have the same name,

For example, it’s very common to have lots of Via headers present in a request.

In Kamailio, we often may wish to add headers, view the contents of headers and perform an action or re-write headers (Disclaimer about not rewriting Vias as that goes beyond the purview of a SIP Proxy but whatever).

Let’s look at a use case where we have multiple instances of the X-NickTest: header, looking something like this:

INVITE sip:[email protected]:5061 SIP/2.0
X-NickTest: ENTRY ONE
X-NickTest: ENTRY TWO
X-NickTest: ENTRY THREE
...

Let’s look at how we’d access this inside Kamailio.

First, we could just use the psedovariable for header – $hdr()

xlog("Value of X-NickTest is: $hdr(X-NickTest)");

But this would just result in the first entry being printed out:

Value of X-NickTest is: ENTRY ONE

If we know how many instances there are of the header, we can access it by it’s id in the array, for example:

xlog("Value of first X-NickTest is: $hdr(X-NickTest)[0]");
xlog("Value of second X-NickTest is: $hdr(X-NickTest)[1]");
xlog("Value of third  X-NickTest is: $hdr(X-NickTest)[2]");

But we may not know how many to expect either, but we can find out using $hdrc(name) to get the number of headers returned.

xlog("X-NickTest has $hdrc(X-NickTest) entries");

You’re probably seeing where I’m going with this, the next logical step is to loop through them, which we can also do something like this:

$var(i) = 0;
while($var(i) < $hdrc(X-NickTest)) {
         xlog(X-NickTest entry [$var(i)] has value $hdrc(X-NickTest)[$var(i)]);
         $var(i) = $var(i) + 1;
}

Originating calls in FreeSWITCH

Through fs_cli you can orignate calls from FreeSWITCH.

At the CLI you can use the originate command to start a call, this can be used for everything from scheduled wake up calls, outbound call centers, to war dialing.

For example, what I’m using:

originate sofia/external/[email protected]:5061 61399999995 XML default
  • originate is the command on the FS_CLI
  • sofia/external/[email protected]:5061 is the call URL, with the application (I’m using mod_sofia, so sofia), the Sofia Profile (in my case external) and the SIP URI, or, if you have gateways configured, the to URI and the gateway to use.
  • 6139999995 is the Application
  • XML is the Dialplan to reference
  • default is the Context to use

But running this on the CLI is only so useful, we can use an ESL socket to use software to connect to FreeSWITCH’s API (Through the same mechanism fs_cli uses) in order to programmatically start calls.

But to do that first we need to expose the ESL API for inbound connections (Clients connecting to FreeSWITCH’s ESL API, which is different to FreeSWITCH connecting to an external ESL Server where FreeSWITCH is the client).

We’ll need to edit the event_socket.conf.xml file to define how this can be accessed:

<configuration name="event_socket.conf" description="Socket Client">
  <settings>
    <param name="nat-map" value="false"/>
    <param name="listen-ip" value="0.0.0.0"/>
    <param name="listen-port" value="8021"/>
    <param name="password" value="yoursecretpassword"/>
    <param name="apply-inbound-acl" value="lan"/>
    <param name="stop-on-bind-error" value="true"/>
  </settings>
</configuration>

Obviously you’ll need to secure this appropriately, good long password, and tight ACLs.

You may notice after applying these changes in the config, you’re no longer able to run fs_cli and access FreeSWITCH, this is because FreeSWITCH’s fs_cli tool connects to FreeSWITCH over ESL, and we’ve just changed tha parameters. You should still be able to connect by specifying the IP Address, port and the secret password we set:

fs_cli --host=10.0.1.16 --password=yoursecretpassword --port=8021

This also means we can run fs_cli from other hosts if permitted through the ACLs (kinda handy for managing larger clusters of FreeSWITCH instances).

But now we can also connect a remote ESL client to it to run commands like our Originate command to setup calls, I’m using GreenSwitch with ESL in Python:

import gevent
import greenswitch
import sys
#import Fonedex_TelephonyAPI
#sys.path.append('../WebUI/Flask/')
import uuid

import logging
logging.basicConfig(level=logging.DEBUG)


esl_server_host = "10.0.1.16"
logging.debug("Originating call to " + str(destination) + " from " + str(source))
logging.debug("Routing the call to " + str(dialplan_entry))
fs = greenswitch.InboundESL(host=str(esl_server_host), port=8021, password='yoursecretpassword')
  try:
      fs.connect()
      logging.debug("Connected to ESL server at " + str(esl_server_host))
  except:
      raise SystemError("Failed to connect to ESL Server at " + str(esl_server_host))

r = fs.send('bgapi originate {origination_caller_id_number=' + str(source) + '}sofia/external/' + str(destination) + '@10.0.1.252:5061 default XML')

And presto, a call is originated!

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…

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.

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 + 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.

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

Screenshot of packet capture of Diameter Multimedia-Auth-Request (Diameter Command Code 303) used for IMS authentication

Diameter and SIP: Multimedia-Authentication-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 Multimedia-Authentication-Request/Answer commands are used to Authenticate subscribers / UAs using a variety of mechanisms such as straight MD5 and AKAv1-MD5.

Basics:

When a SIP Server (S-CSCF) receives a SIP INVITE, SIP REGISTER or any other SIP request, it needs a way to Authenticate the Subscriber / UA who sent the request.

We’ve already looked at the Diameter User-Authorization-Request/Answer commands used to Authorize a user for access, but the Multimedia-Authentication-Request / Multimedia-Authentication-Answer it used to authenticate the user.

The SIP Server (S-CSCF) sends a Multimedia-Authentication-Request to the Diameter server, containing the Username of the user attempting to authenticate and their Public Identity.

The Diameter server generates “Authentication Vectors” – these are Precomputed cryptographic challenges to challenge the user, and the correct (“expected”) responses to the challenges. The Diameter puts these Authentication Vectors in the 3GPP-SIP-Auth-Data (612) AVP, and sends them back to the SIP server in the Multimedia-Authentication-Answer command.

The SIP server sends the Subscriber / UA a SIP 401 Unauthorized response to the initial request, containing a WWW-Authenticate header containing the challenges.

SIP 401 Response with WWW-Authenticate header populated with values from Multimedia-Auth-Answer

The Subscriber / UA sends back the initial request with the WWW-Authenticate header populated to include a response to the challenges. If the response to the challenge matches the correct (“expected”) response, then the user is authenticated.

I always find it much easier to understand what’s going on through a packet capture, so here’s a packet capture showing the two Diameter commands,

Note: There is a variant of this process allows for stateless proxies to handle this by not storing the expected authentication values sent by the Diameter server on the SIP Proxy, but instead sending the received authentication values sent by the Subscriber/UA to the Diameter server to compare against the expected / correct values.

The Cryptography

The Cryptography for IMS Authentication relies on AKAv1-MD5 which I’ve written about before,

Essentially it’s mutual network authentication, meaning the network authenticates the subscriber, but the subscriber also authenticates the network.

LTE USIM Authentication - Mutual Authentication of the Network and Subscriber

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 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!

Kamailio Bytes – Multiple Kamailio Instances on a Single Box

For whatever reason you might want to run multiple Kamailio instances on the same machine.

In my case I was working on an all-in-one IMS box, which needed the P-CSCF, I-CSCF and S-CSCF all in the one box.

init.d File

As you probably already know, all the startup scripts for each service/daemon live in the /etc/init.d directory.

We’ll start by copying the existing init.d file for kamailio:

cp /etc/init.d/kamailio /etc/init.d/kamailio1

Next up we’ll edit it to reflect the changes we want made.

You only really need to change the DEFAULTS= parameter, but you may also want to update the description, etc.

DEFAULTS=/etc/default/kamailio1

The CFGFILE parameter we can update later in the defaults file or specify here.

Defaults File

Next up we’ll need to create a defaults file where we specify how our instance will be loaded,

Again, we’ll copy an existing defaults file and then just edit it.

cp /etc/default/kamailio /etc/default/kamailio1

The file we just created from the copy will need to match the filename we specified in the init.d file for DEFAULTS=

In my case the filename is kamailio1

In here I’ll need to at minimum change the CFGFILE= parameter to point to the config file for the Kamailio instance we’re adding.

In this case the file is called kamailio1.cfg in /etc/kamailio/

For some Ubuntu systems you’re expected to reload the daemons:

systemctl daemon-reload

Starting / Running

Once you’ve done all this you can now try and start your instance using /etc/init.d/kamailio1 start

For my example startup failed as I haven’t created the config file for kamailio1.cfg

So I quickly created a config file and tried to start my service:

/etc/init.d/kamailio1 restart

And presto, my service is running,

I can verify all is running through ps aux:

ps aux | grep kamailio1

Just keep in mind if you want to run multiple instances of Kamailio, you can’t have them all bound to the same address / port.

This also extends to tools like kamcmd which communicate with Kamailio over a socket, again you’d need to specify unique ports for each instance.

Kamailio Bytes – SIP over TLS (SIPS)

It’s probably pretty evident to most why you’d want to use TLS these days,

SIP Secure – aka sips has been around for a long time and is supported by most SIP endpoints now.

Kamailio supports TLS, and it’s setup is very straightforward.

I’ve got a private key and certificate file for the domain nickvsnetworking.com so I’ll use those to secure my Kamailio instance by using TLS.

I’ll start by copying both the certificate (in my case it’s cert.pem) and the private key (privkey.pem) into the Kamailio directory. (If you’ve already got the private key and certificate on your server for another application – say a web server, you can just reference that location so long as the permissions are in place for Kamailio to access)

Next up I’ll open my Kamailio config (kamailio.cfg), I’ll be working with an existing config and just adding the TLS capabilities, so I’ll add this like to the config:

!define WITH_TLS

That’s pretty much the end of the configuration in kamailio.cfg, if we have a look at what’s in place we can see that the TLS module loads it’s parameters from a separate file;

#!ifdef WITH_TLS
# ----- tls params -----
modparam("tls", "config", "/etc/kamailio/tls.cfg")
#!endif

So let’s next jump over to the tls.cfg file and add our certificate and private key;

[server:default]
method = TLSv1
verify_certificate = yes
require_certificate = yes
certificate = fullchain.pem
private_key = privkey.pem

Boom, as simple as that,

After restarting Kamailio subscribers can now contact us via TLS using sips.

You may wish to disable TCP & UDP transport in favor of only TLS.

A note about CAs…

If you’re planning on rolling out SIP over TLS (sips) to existing IP phones it’s worth looking at what Certificate Authorities (CAs) are recognised by the IP phones.

As TLS relies on a trust model where a CA acts kind of like a guarantor to the validity of the certificate, if the IP phone doesn’t recognise the CA, it may see the certificate as Invalid.

Some IP phones for example won’t recognize Let’s Encrypt certificates as valid, while others may not recognize any of the newer CAs.

Vendor Yealink publishes a list of CAs their IP phones recognize, which could save you a lot of headaches when setting this up and buying certificates.

Automated SIP testing with sipcmd

I wrote about some tests I ran with SIPp to load test the transcoding abilities of RTPengine a while back.

While SIPp allows you to create complex & powerful scenarios, sipcmd’s simple usage makes it great for quickly testing stuff.

Installation

Install prerequisites

apt-get install libopal-dev sip-dev libpt-dev libssl1.0-dev

Next up clone the GitHub repo and compile:

git clone https://github.com/tmakkonen/sipcmd
cd sipcmd
make 

To be able to call sipcmd from anywhere, copy the binary to /usr/sbin/

cp sipcmd /usr/sbin/

Usage

Unlike SIPp, sipcmd has a much more simple syntax to allow you to follow basic call scenarios, like call a destination, wait a set time and then hangup, or answer an incoming call and send a DTMF digit and wait for the called party to hangup.

So let’s get the most basic thing we can set, SIP Registration and Authentication.

sipcmd -P sip -u "nick" -c "mypassword" -w "192.168.190.129"

Now sipcmd will register on that host (192.168.190.129) with username nick and password mypassword.

And it works!

Next we’ll add a basic call scenario, call 123 wait 2 seconds (2000 ms) and then hangup.

“c123;w2000;h”

./sipcmd -P sip -u "nick" -c "mypassword" -w "192.168.190.129" -x "c123;w2000;h"

And there you have it, simple as that, we’ve made a test call, waited a set time and then hung up.

We can even combine this with monitoring / NMS systems like Nagios to run tests against the network continually.

For more advanced scenarios I’d recommend using SIPp, but for simple testing, particularly from a command line, sipcmd is a simple easy place to start.

SIP SIMPLE – Instant Messaging with SIP

People think SIP they think VoIP & phone calls, but SIP it’s the Phone Call Initiation Protocol it’s the Session Initiation Protocol – Sure VoIP guys like me love SIP, but it’s not just about VoIP.

Have you sent an SMS on a modern mobile phone recently? Chances are you sent a SMS over SIP using SIP MESSAGE method.

So let’s look a bit at SIP SIMPLE, the catchily titled acronym translates to Session Initiation Protocol for Instant Messaging and Presence Leveraging Extensions (Admittedly less catchy in it’s full form).

There’s two way SIP SIMPLE can be used to implement Instant Messaging, Paging Mode with each message sent as a single transaction, and Session Mode where a session is setup between users and IMs exchanged with the same Call ID / transaction.

I’m going to cover the Paging Mode implementation because it’s simpler easier to understand.

Before we get too far this is another example of confusing terminology, let’s just clear this up; According to the RFC any SIP request is a SIP Message, like a SIP OPTIONS message, a SIP INVITE message. But the method of a SIP INVITE message is INVITE, the method of a SIP OPTIONS message is OPTIONS. There’s a SIP MESSAGE method, meaning you can send a SIP MESSAGE message using the MESSAGE method. Clear as mud? I’ll always refer to the SIP Method in Capitals, like MESSAGE, INVITE, UPDATE, etc.

The SIP MESSAGE Method

The basis of using SIP for instant messaging relies on the MESSAGE method, laid out in RFC 3428.

The SIP MESSAGE method looks / acts very similar to a SIP INVITE, in that it’s got all the standard SIP headers, but also a Message Body, in which our message body lives (funny about that), typically we’ll send messages using the Content-Type: text/plain to denote we’re sending a plaintext message.

Example MESSAGE Message Flow

Like a SIP OPTIONS Method, the MESSAGE method is simply answered with a 200 OK (No Ack).

Let’s have a look at how the MESSAGE message looks:

MESSAGE sip:[email protected] SIP/2.0
Via: SIP/2.0/TCP user1pc.domain.com;branch=z9hG4bK776sgdkse
Max-Forwards: 70
From: sip:[email protected];tag=49583
To: sip:[email protected]
Call-ID: [email protected]
CSeq: 1 MESSAGE
Content-Type: text/plain
Content-Length: 18

Hello world.

After receiving the SIP MESSAGE message, the recipient simply sends back a 200 OK with the same Call-ID.

Simple as that.

You can read more about the SIP MESSAGE method in RFC 3428.

I used the SIP MESSAGE method in a Kamailio Bytes example recently where I sent a MESSAGE to an IP phone when a HTTP GET was run against Kamailio, and again to send an alert when an emergency services destination was called.

Kamailio Bytes – UAC for Remote User Registration to external SIP Server (Originating SIP REGISTER)

I’ve talked about using the UAC module, but as promised, here’s how we can use the UAC module to send SIP REGISTER requests to another SIP server so we can register to another SIP proxy.

Let’s say we’re using Kamailio to talk to a SIP Trunk that requires us to register with them so they know where to send the calls. We’d need to use Kamailio UAC module to manage SIP Registration with our remote SIP Trunk.

But Kamailio’s a proxy, why are we sending requests from it? A proxy just handles messages, right?
Proxies don’t originate messages, it’s true, and Kamailio can be a proxy, but with the UAC module we can use Kamailio as a Client instead of a server. Keep in mind Kamailio is what we tell it to be.

Getting Started

Before we can go spewing registrations out all over the internet we need to start by getting a few things in place;

First of which is configuring UAC module, which is something I covered off in my last post,

We’ll also need to have a database connection in place, again I’ve covered off connecting to a MySQL database in Kamailio here.

Once we’ve got that done we’ll need to tell the UAC module our IP Address for the from address for our Contact field, and the database URL of what we’ve setup.

modparam("uac", "reg_contact_addr", "192.168.1.99:5060")
modparam("uac", "reg_db_url", "mysql://kamailio:kamailiorw@localhost/kamailio")

I haven’t used a variable like DBURL for the database information, but you could.

Finally a restart will see these changes pushed into Kamailio.

/etc/init.d/kamailio restart

This is the end of the Kamailio config side of things, which you can find on my GitHub here.

Defining the Registration parameters

Once we’ve got a database connection in place and UAC module loaded, then we can configure an entry in the uacreg table in the database, in my example I’m going to be registering to an Asterisk box on 192.168.1.205, so I’ll insert that into my database:

mysql> INSERT INTO `uacreg` VALUES (NULL,'myusername','myusername','192.168.1.205','myusername','192.168.1.205','asterisk','myusername','mypassword','','sip:192.168.1.205:5060',60,0,0);

Note: If you’re using a later version of Kamailio (5.4+) then the DB schema changes and you may want something like this:

insert into uacreg values ('', 'myusername', 'myusername', 'mydomain', 'myusername', 'mydomain', 'asteriskrealm', 'myusername', 'mypassword', '', 'sip:remoteproxy.com:5060', 60, 0, 0, 0)

Having a look at the fields in our table makes it a bit clearer as to what we’ve got in place, setting flags to 0 will see Kamailio attempt registration. Make sure the auth_proxy is a SIP URI (Starts with sip:) and leave the auth_ha1 password empty as we haven’t calculated it.

mysql> SELECT * FROM 'uacreg' \G
            id: 2
        l_uuid: myusername
    l_username: myusername
      l_domain: 192.168.1.205
    r_username: myusername
      r_domain: 192.168.1.205
         realm: asterisk
 auth_username: myusername
 auth_password: mypassword
      auth_ha1:
    auth_proxy: sip:192.168.1.205:5060
       expires: 60
         flags: 0
     reg_delay: 0

Putting it into Play

After we’ve got our database connection in place, UAC module configured and database entries added, it’s time to put it into play, we’ll use Kamcmd to check it’s status:

kamcmd> uac.reg_reload
kamcmd> uac.reg_dump

Unfortunately from Kamcmd we’re not able to see registration status, but Sngrep will show us what’s going on:

From Sngrep we can see the REGISTRATION going out, the authentication challenge and the 200 OK at the end.

Make sure you’ve got your Realm correct, otherwise you may see an error like this:

RROR: {2 10 REGISTER [email protected]} uac [uac_reg.c:946]: uac_reg_tm_callback(): realms do not match. requested realm: [localhost]

If you’re not familiar with the SIP Registration process now’s a good time to brush up on it by having a read of my post here. – “What is a SIP Registrar?”

Kamailio Bytes – SIP UAC Module to act as a UAC / SIP Client

Kamailio is a great SIP proxy, but sometimes you might want to see requests originate from Kamailio.

While this isn’t typical proxy behaviour, RFC definitions of a proxy and technical requirements are often two different things. The UAC module allows us to use Kamailio to act as a User Agent Client instead of just a UAS.

There’s one feature I won’t cover in this post, and that’s initiating and outbound SIP Registration using the UAC module, that will get a post of it’s own in the not to distant future.

You may already be sort of using Kamailio is a UAC, if you’re using Dispatcher and sending SIP Pings, then Kamailio is sending SIP OPTIONS messages to the dispatcher destinations. If you’re using the NAT module and sending Keepalives, then you’re also using Kamailio as a UAC. The only difference is the Dispatcher and NAT Helper modules do this for us, and we’re going to originate our own traffic.

There’s a bit of a catch here, when Kamailio receives a request it follows a set of logic and does something with that request. We’re going to remain constrained by this for our example, just to keep things simple.

So let’s work on an example, if a user on our network dials a call to an emergency services number, we’ll send a text message to my IP phone to let me know who’s dialed the emergency services number.

So to start with we’ll need to load the Kamailio UAC module, using LoadModule as we would with any other module:

loadmodule "uac.so"

If you’re working on the default config file that ships with Kamailio you’ll probably have to change how record routing is handled to support UAC,

modparam("rr", "append_fromtag", 1)

Now we should have UAC support added in Kamailio, I’m going to do a bare bones example of the routing logic below, but obviously if you wanted to put this into practice in real life you’d want to actually route the SIP INVITE to an emergency services destination.

First we’ll need to find if the request is an INVITE with the Request URI to an emergency services number, I’ve programmed this in with the Australian emergency services numbers:

if(is_method("INVITE") && ($rU == "000" or $tU == "112" or $tU == "116")){      
  #Matches any INVITEs with the Request URI to Address as 000, 112 or 116
  xlog("Emergency call from $fU to $rU (Emergency number) CSeq is $cs ");
}

Now calls to 000, 112 or 116 will see the alert apear in Xlog:

07:22:41 voice-dev3 /usr/sbin/kamailio[10765]: ERROR: : Emergency call from Test to 112 (Emergency number)

So next up we need to handle the sending a SIP MESSAGE request to my IP phone on the IP 10.0.1.5 – You’re probably thinking we could use the Registrar module to lookup my registered IP address, and you’re right, but to keep things simple I’m just hardcoding it in.

So to keep our routing neat we’ll send calls to the route route(“EmergencyNotify”); and so the demo works I’ll send back a 200 OK and exit – In real life you’d want to handle this request and forward it onto emergency services.

if(is_method("INVITE") && ($rU == "000" or $tU == "112" or $tU == "116")){      
#Matches any INVITEs with the Request URI to Address as 000, 112 or 116
  xlog("Emergency call from $fU to $rU (Emergency number) CSeq is $cs ");
  route("EmergencyNotify");
  #You obviously would want this to route to an emergency services destination...
  sl_reply("200", "ok");
  exit;
}

if(is_method("INVITE")){                                                                                
  #Matches everything else
  xlog("Just a regular call from $fU to $rU");
}

Obviously we need to now create a route called route[“EmergencyNotify”]{ } where we’ll put our UAC logic.

For the UAC module we need to craft the SIP Request we’re going to send; we’re going to be sending a SIP MESSAGE request,

route["EmergencyNotify"]{
  xlog("Emergency Notify Route");
  $uac_req(method)="MESSAGE";
  $uac_req(ruri)="sip:10.0.1.5:5060";
  $uac_req(furi)="sip:Emergency Alert";
  $uac_req(turi)="sip:thisphone";
  $uac_req(callid)=$(mb{s.md5});
  $uac_req(hdrs)="Subject: Emergency Alert\r\n";
  $uac_req(hdrs)=$uac_req(hdrs) + "Content-Type: text/plain\r\n";
  $uac_req(body)="Emergency call from " + $fU + " on IP Address " + $si + " to " + $rU + " (Emergency Number)";
  $uac_req(evroute)=1;
  uac_req_send();
}

So now we’ve sort of put it all together, when a call comes into an emergency destination, like 000, the route EmergencyNotify is called which sends a SIP MESSAGE request to my IP Phone to alert me.

When a caller dials 000 I can see Kamailio sends a SIP MESSAGE to my IP Phone:

Let’s have a look at how this looks on my IP Phone:

I’ve fleshed out the code a little more to handle SIP REGISTER requests etc, and put the full running code on GitHub which you can find here.

IMS / VoLTE IPsec on the Gm Interface

For most Voice / Telco engineers IPsec is a VPN technology, maybe something used when backhauling over an untrusted link, etc, but voice over IP traffic is typically secured with TLS and SRTP.

IMS / Voice over LTE handles things a bit differently, it encapsulates the SIP & RTP traffic between the UE and the P-CSCF in IPsec Encapsulating Security Payload (ESP) payloads.

In this post we’ll take a look at how it works and what it looks like.

It’s worth noting that Kamailio recently added support for IPsec encapsulation on a P-CSCF, in the IMS IPSec-Register module. I’ll cover usage of this at a later date.

The Message Exchange

The exchange starts off looking like any other SIP Registration session, in this case using TCP for transport. The UE sends a REGISTER to the Proxy-CSCF which eventually forwards the request through to a Serving-CSCF.

This is where we diverge from the standard SIP REGISTER message exchange. The Serving-CSCF generates a 401 Unauthorized response, containing an authentication challenge in the WWW-Authenticate header, and also a Ciphering Key & Integrity Key (ck= and ik=) also in the WWW-Authenticate header.

The Serving-CSCF sends the Proxy-CSCF the 401 response it created. The Proxy-CSCF assigns a SPI for the IPsec ESP to use, a server port and client port and indicates the used encryption algorithm (ealg) and algorithm to use (In this case HMAC-SHA-1-96.) and adds a new header to the 401 Unauthorized called SecurityServer header to share this information with the UE.

The Proxy-CSCF also strips the Ciphering Key (ck=) and Integrity Key (ik=) headers from the SIP authentication challenge (WWW-Auth) and uses them as the ciphering and integrity keys for the IPsec connection.

Finally after setting up the IPsec server side of things, it forwards the 401 Unauthorized response onto the UE.

Upon receipt of the 401 response, the UE looks at the authentication challenge.

Keep in mind that the 3GPP specs dictate that IMS / VoLTE authentication requires mutual network authentication meaning the UE authenticates the network as well as the network authenticating the UE. I’ve written a bit about mutual network authentication in this post for anyone not familiar with it.

If the network is considered authenticated by the UE it generates a response to the Authentication Challenge, but it doesn’t deliver it over TCP. Using the information generated in the authentication challenge the UE encapsulates everything from the network layer (IPv4) up and sends it to the P-CSCF in an IPsec ESP.

Communication between the UE and the P-CSCF is now encapsulated in IPsec.

Wireshark trace of IPsec IMS Traffic between UE and P-CSCF

If you’re leaning about VoLTE & IMS networks, or building your own, I’d suggest checking out my other posts on the topic.

Kamailio Bytes – KEMI Intro

When learning to use Kamailio you might find yourself thinking about if you really want to learn to write a Kamailio configuration file, which is another weird scripting language to learn to achieve a task.

Enter KEMI – Kamailio Embedded Interface. KEMI allows you to abstract the routing logic to another programing language. In layman’s terms this means you can write your routing blocks, like request_route{}, reply_route{}, etc, in languages you already know – like Lua, JavaScript, Ruby – and my favorite – Python!

Why would you use KEMI?

Write in a language you already know;

You don’t need to learn how to do write complex routing logic in Kamailio’s native scripting language, you can instead do it in a language you’re already familiar with, writing your Routing Blocks in another programming language.

Change Routing on the Fly;

By writing the routing logic in KEMI allows you to change your routing blocks without having to restart Kamailio, something you can’t do with the “native” scripting language – This means you can change your routing live.

Note: This isn’t yet in place for all languages – Some still require a restart.

Leverage your prefered language’s libraries;

While Kamailio’s got a huge list of modules to interface with a vast number of different things, the ~200 Kamailio modules don’t compare with the thousands of premade libraries that exist for languages like Python, Ruby, JavaScript, etc.

Prerequisites

We’ll obviously need Kamailio installed, but we’ll also need the programming language we want to leverage setup (fairly obvious).

Configuring Kamailio to talk to KEMI

KEMI only takes care of the routing of SIP messages inside our routing blocks – So we’ve still got the Kamailio cfg file (kamailio.cfg) that we use to bind and setup the service as required, load the modules we want and configure them.

Essentially we need to load the app for the language we use, in this example we’ll use app_python3.so and use that as our Config Engine.

loadmodule "app_python3.so"
modparam("app_python3", "load", "/etc/kamailio/kemi.py")
cfgengine "python"

After that we just need to remove all our routing blocks and create a basic Python3 script to handle it,

We’ll create a new python file called kemi.py

## Kamailio - equivalent of routing blocks in Python
import sys
import Router.Logger as Logger
import KSR as KSR

# global function to instantiate a kamailio class object
# -- executed when kamailio app_python module is initialized
def mod_init():
    KSR.info("===== from Python mod init\n");
    return kamailio();


# -- {start defining kamailio class}
class kamailio:
    def __init__(self):
        KSR.info('===== kamailio.__init__\n');


    # executed when kamailio child processes are initialized
    def child_init(self, rank):
        KSR.info('===== kamailio.child_init(%d)\n' % rank);
        return 0;


    # SIP request routing
    # -- equivalent of request_route{}
    def ksr_request_route(self, msg):
        KSR.info("===== request - from kamailio python script\n");
        KSR.info("===== method [%s] r-uri [%s]\n" % (KSR.pv.get("$rm"),KSR.pv.get("$ru")));

So that’s it! We’re running,

The next step is of course, putting some logic into our Python script, but that’s a topic for another day, which I’ve covered in this post.

Running code for kamailio.cfg (Kamailio config) and kemi.py (Python3 script).