All posts by Nick

About Nick

Dialtone.

VoLTE Logo on Samsung Galaxy Handset

Things I wish I knew about setting up private VoLTE Networks

I’ve been working for some time on open source mobile network cores, and one feature that has been a real struggle for a lot of people (Myself included) is getting VoLTE / IMS working.

Here’s some of the issues I’ve faced, and the lessons I learned along the way,

Sadly on most UEs / handsets, there’s no “Make VoLTE work now” switch, you’ve got a satisfy a bunch of dependencies in the OS before the baseband will start sending SIP anywhere.

Get the right Hardware

Your eNB must support additional bearers (dedicated bearers I’ve managed to get away without in my testing) so the device can setup an APN for the IMS traffic.

Sadly at the moment this rules our Software Defined eNodeBs, like srsENB.

In the end I opted for a commercial eNB which has support for dedicated bearers.

ISIM – When you thought you understood USIMs – Guess again

According to the 3GPP IMS docs, an ISIM (IMS SIM) is not a requirement for IMS to work.

However in my testing I found Android didn’t have the option to enable VoLTE unless an ISIM was present the first time.

In a weird quirk I found once I’d inserted an ISIM and connected to the VoLTE network, I could put a USIM in the UE and also connect to the VoLTE network.

Obviously the parameters you can set on the USIM, such as Domain, IMPU, IMPI & AD, are kind of “guessed” but the AKAv1-MD5 algorithm does run.

Getting the APN Config Right

There’s a lot of things you’ll need to have correct on your UE before it’ll even start to think about sending SIP messaging.

I was using commercial UE (Samsung handsets) without engineering firmware so I had very limited info on what’s going on “under the hood”. There’s no “Make VoLTE do” tickbox, there’s VoLTE enable, but that won’t do anything by default.

In the end I found adding a new APN called ims with type ims and enabling VoLTE in the settings finally saw the UE setup an IMS dedicated bearer, and request the P-CSCF address in the Protocol Configuration Options.

Also keep in mind on Android at least, what you specify as your APN might be ignored if your UE thinks it knows best – Thanks to the Android Master APN Config – which guesses the best APN for you to use, which is a useful feature to almost any Android user, except the very small number who see fit to setup their own network.

Get the P-GW your P-CSCF Address

If your P-GW doesn’t know the IP of your P-CSCF, it’s not going to be able to respond to it in the Protocol Configuration Options (PCO) request sent by the UE with that nice new bearer for IMS we just setup.

There’s no way around Mutual Authentication

Coming from a voice background, and pretty much having RFC 3261 tattooed on my brain, when I finally got the SIP REGISTER request sent to the Proxy CSCF I knocked something up in Kamailio to send back a 200 OK, thinking that’d be the end of it.

For any other SIP endpoint this would have been fine, but IMS Clients, nope.

Reading the specs drove home the same lesson anyone attempting to setup their own LTE network quickly learns – Mutual authentication means both the network and the UE need to verify each other, while I (as the network) can say the UE is OK, the UE needs to check I’m on the level.

For anyone not familiar with the intricacies of 3GPP USIM Network Authentication, I’ve written about Mutual Network Authentication in this post.

In the end I added Multimedia Authentication support to PyHSS, and responded with a Crypto challenge using the AKAv1-MD5 auth,

For anyone curious about what goes on under the hood with this, I wrote about how the AKAv1-MD5 Authentication algorithm works in this post,

I saw my 401 response go back to the UE and then no response. Nada.

This led to my next lesson…

There’s no way around IPsec

According to the 3GPP docs, support for IPsec is optional, but I found this not to be the case on the handsets I’ve tested.

After sending back my 401 response the UE looks for the IPsec info in the 401 response, then tries to setup an IPsec SA and sends ESP packets back to the P-CSCF address.

Even with my valid AKAv1-MD5 auth, I found my UE wasn’t responding until I added IPsec support on the P-CSCF, hence why I couldn’t see the second REGISTER with the Authentication Info.

After setting up IPsec support, I finally saw the UE’s REGISTER with the AKAv1-MD5 authentication, and was able to send a 200 OK.

For some more info on ESP, IPsec SAs and how it works between the UE and the P-CSCF there’s a post on that too.

Get Good at Mind Reading (Or an Engineering Firmware)

To learn all these lessons took a long time,

One thing I worked out a bit late but would have been invaluable was cracking into the Engineering Debug options on the UEs I was testing with.

Samsung UEs feature a Sysdump utility that has an IMS Debugging tool, sadly it’s only their for carriers doing IMS interop testing.

After a bit of work I detailed in this post – Reverse Engineering Samsung Sysdump Utils to Unlock IMS Debug & TCPdump on Samsung Phones – I managed to create a One-Time-Password generator for this to generate valid Samsung OTP keys to unlock the IMS Debugging feature on these handsets.

I outlined turning on these features in this post.

This means without engineering firmware you’re able to pull a bunch of debugging info off the UE.

If you’ve recently gone through this, are going through this or thinking about it, I’d love to hear your experiences.

I’ll be continuing to share my adventures here and elsewhere to help others get their own VoLTE networks happening.

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

BaiCells Neutrino eNB Setup

For my LTE lab I got myself a BaiCells Neutrino, it operates on Band 3 (FDD ~1800Mhz) with only 24dBm of output power max and PoE powered it works well in a lab environment without needing -48vDC supply, BBUs, DUs feeders and antennas.

Setup can be done via TR-069 or via BaiCells management server, for smaller setups the web UI makes setup pretty easy,

Logging in with admin/admin to the web interface:

We’ll select Quick Settings, and load in our MME IP address, PLMN (MCC & MNC), Tracking Area Code, Cell ID and Absolute Radio Frequency No.

Once that’s done we’ll set our Sync settings to use GPS / GNSS (I’ve attached an external GPS Antenna purchased cheaply online).

Finally we’ll set the power levels, my RF blocking setup is quite small so I don’t want excess power messing around with it, so I’ve dialed the power right back:

And that’s it, it’ll now connect to my MME on 10.0.1.133 port 36412 on SCTP.

VoLTE / IMS – P-CSCF Assignment

The Proxy-Call Session Control Function is the first network element a UE sends it’s SIP REGISTER message to, but how does it get there?

To begin with our UE connects as it would normally, getting a default bearer, an IP address and connectivity.

Overview

If the USIM has an ISIM application on it (or IMS is enabled on the UE using USIM for auth) and an IMS APN exists on the UE for IMS, the UE will set up another bearer in addition to the default bearer.

This bearer will carry our IMS traffic and allow QoS to be managed through the QCI values set on the bearer.

While setting up the bearer the UE requests certain parameters from the network in the Protocol Configuration Options element, including the P-CSCF address.

When setting up the bearer the network responds with this information, which if supported includes the P-CSCF IPv4 &/or IPv6 addresses.

The Message Exchange

We’ll start assuming the default bearer is in place & our UE is configured with the APN for IMS and supports IMS functionality.

The first step is to begin the establishment of an additional bearer for the IMS traffic.

This is kicked off through the Uplink NAS Transport, PDN Connectivity Request from the UE to the network. This includes the IMS APN information, and the UE’s NAS Payload includes the Protocol Configuration Options element (PCO), with a series of fields the UE requires responses from the network. including DNS Server, MTU, etc.

In the PCO the UE also includes the P-CSCF address request, so the network can tell the UE the IP of the P-CSCF to use.

If this is missing it’s because either your APN settings for IMS are not valid, or your device doesn’t have IMS support or isn’t enabling it.(that could be for a few reasons).

Protocol Configuration Options (Unpopulated) used to request information from the Network by the UE

The MME gets this information from the P-GW, and the network responds in the E-RAB Setup Request, Activate default EPS bearer Context Request and includes the Protocol Configuration Options again, this time the fields are populated with their respective values, including the P-CSCF Address;

Once the UE has this setup, the eNB confirms it’s setup the radio resources through the E-RAB Setup Response.

One the eNB has put the radio side of things in place, the UE confirms the bearer assignment has completed successfully through the Uplink NAS Transport, Activate default EPS Bearer Accept, denoting the bearer is now in place.

Now the UE has the IP address(s) of the P-CSCF and a bearer to send it over, the UE establishes a TCP socket with the address specified in the P-CSCF IPv4 or IPv6 address, to start communicating with the P-CSCF.

The SIP REGISTER request can now be sent and the REGISTRATION procedure can begin.

I’ve attached a PCAP of the full exchange here.

I’ve written a bit about the Gm REGISTER procedure and how IPsec is implemented between the UE and the P-CSCF in this post.

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

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.

SRS LTE – Software Defined LTE Stack with BladeRF x40

The team at Software Radio Systems in Ireland have been working on an open source LTE stack for some time, to be used with software defined radio (SDR) hardware like the USRP, BladeRF and LimeSDR.

They’ve released SRSUE and SRSENB their open source EUTRAN UE and eNodeB, which allow your SDR hardware to function as a LTE UE and connect to a commercial eNB like a standard UE while getting all the juicy logs and debug info, or as a LTE eNB and have commercial UEs connect to a network you’re running, all on COTS hardware.

The eNB supports S1AP to connect to a 3GPP compliant EPC, like Open5Gs, but also comes bundled with a barebones EPC for testing.

The UE allows you to do performance testing and gather packet captures on the MAC & PHY layers, something you can’t do on a commericial UE. It also supports software-USIMs (IMSI / K / OP variables stored in a text file) or physical USIMs using a card reader.

I’ve got a draw full of SDR hardware, from the first RTL-SDR dongle I got years ago, to a few HackRFs, a LimeSDR up to the BladeRF x40.

Really cool software to have a play with, I’ve been using SRSUE to get a better understanding of the lower layers of the Uu interface.

Installation

After mucking around trying to satisfy all the dependencies from source I found everything I needed could be found in Debian packages from the repos of the maintainers.

To begin with we need to install the BladeRF drivers and SopySDR modules to abstract it to UHD:

sudo add-apt-repository -y ppa:myriadrf/drivers
sudo add-apt-repository -y ppa:bladerf/bladerf
apt-get install *bladerf*
apt-get install libgnuradio-uhd3.7.11 libuhd-dev soapysdr-module-uhd uhd-soapysdr

Next up installing Software Radio System’s repo:

sudo add-apt-repository -y ppa:srslte/releases
sudo apt-get update
sudo apt-get install srslte -y 

And that’s it!

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.

PyHSS Update – IMS Cx Support!

As I’ve been doing more and more work with IMS / VoLTE, the requirements / features on PyHSS has grown.

Some key features I’ve added recently:

IMS HSS Features

IMS Cx Server Assignment Request / Answer

IMS Cx Multimedia Authentication Request / Answer

IMS Cx User Authentication Request / Answer

IMS Cx Location Information Request / Answer

General HSS Features

Better logging (IPs instead of Diameter hostnames)

Better Resync Support (For USIMs with different sync windows)

ToDo

There’s still some functions in the 3GPP Cx interface description I need to implement:

IMS Cx Registration-Termination Request / Answer

IMS Cx Push-Profile-Request / Answer

Support for Resync in IMS Cx Multimedia Authentication Answer

Keep an eye on the GitLab repo where I’m pushing the changes.

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 – Python + SIP with KEMI

In my last post I talked about using KEMI in Kamailio and how you can integrate in a different programming language to handle your SIP request handling in a language you already know – Like Python!

So in this post I’ll cover the basics of how we can manage requests and responses from Kamailio in Python, if you haven’t already read it, go back to last weeks post and get that running, it’s where we’ll start off.

The Framework

Before we get too excited there’s some boilerplate we’ve got to add to our Python script, we need to create a class called kamailio and populate the class by defining some functions, we’ll define an __init__ to handle loading of the class, define a child_init for handling child processes, define ksr_request_route to handle the initial requests. We’ll also need to define a mod_init – outside of the Kamailio class to initialize the class.

import sys
import Router.Logger as Logger
import KSR as KSR

import requests

# 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.dbg("method " + KSR.pv.get("$rm") + " r-uri " + KSR.pv.get("$ru"))

Most of these should be pretty self explanatory for anyone who’s done a bit more in-depth Python programming, but it’s no big deal if you don’t understand all this, the only part you need to understand is the ksr_request_route function.

ksr_request_route: translates to our request_route{} in the Kamailio native scripting language, all requests that come in will start off in this part.

Python Kamailio Routing

So let’s start to build upon this, so we’ll blindly accept all SIP registrations;

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


        if KSR.is_method("REGISTER"):
                KSR.sl.send_reply(200, "Sure")

Here you’ll see we’ve added an if statement, as if we were doing any other If statement in Python, in this case we’re asking if the KSR.is_method(“REGISTER”), and if it is, we’ll send back a 200 OK response.

Let’s pause and talk about KSR

All the Kamailio bits we’ll use in Python will have the KSR. prefix, so let’s take a quick break here to talk about KSR. The KSR. functions are the KEMI functions we’ve exposed to Python.

Without them, we’re just writing Python, and we’d have to do all the functions provided by Kamailio nativeley in Python, which would be crazy.

So we leverage the Kamailio modules you know and love from Python using Python’s logic / programming syntax, as well as opening up the ability to pull in other libraries from Python.

There’s a full (ish) list of the KEMI functions here, but let’s talk about the basics.

Let’s look at how we might send a stateless reply,

There’s a module function to send a stateless reply;

 KSR.sl.send_reply(200, "OK")

The vast majority of functions are abstracted as module functions, like the example above, but not all of them.

So every function doesn’t need to be wrapped up as a module, there’s also a way to call any function that you’d call from the native scripting language, wrapped up, kind of like an Exec command:

KSR.x.modf("sl_send_reply", "200", "OK");

So thanks to this we can call any Kamailio function from Python, even if it’s not explicitly in the KEMI abstraction.

Python Kamailio Routing (Continued)

So earlier we managed REGISTER requests and sent back a 200 OK response.

What about forwarding a SIP Request to another proxy? Let’s follow on with an elif statement to test if the method is an INVITE and statelessly forward it.

        elif KSR.is_method("INVITE"):
                #Lookup our public IP address
                try:
                    ip = requests.get('https://api.ipify.org').text
                except:
                    ip = "Failed to resolve"

                #Add that as a header
                KSR.hdr.append("X-KEMI: I came from KEMI at " + str(ip) + "\r\n");

                #Set host IP to 10.1.1.1
                KSR.sethost("10.1.1.1");

                #Forward the request on
                KSR.forward()

Now an incoming SIP invite will be proxied / forwarded to 10.1.1.1, all from Python.

But so far we’ve only done things in KEMI / Python that we could do in our native Kamailio scripting language, so let’s use some Python in our Python!

I utterly love the Python Requests library, so let’s use that to look up our public IP address and add it as a header to our forwarded SIP INVITE;

        elif KSR.is_method("INVITE"):
                #Lookup our public IP address
                try:
                    ip = requests.get('https://api.ipify.org').text
                except:
                    ip = "Failed to resolve"

                #Add that as a header
                KSR.hdr.append("X-KEMI: I came from KEMI at " + str(ip) + "\r\n");

                #Set host IP to 10.1.1.1
                KSR.sethost("10.1.1.1");

                #Forward the request on
                KSR.forward()

(For anyone pedantic out there, Kamailio does have an HTTP client module that could do this too, but Requests is awesome)

So let’s have a look at our forwarded request:

Bottom header is the X-KEMI custom header we included with our public IP

So let’s wrap this up a bit and handle any other request that’s not an INVITE or a REGISTER, with a 500 error code.

    # SIP request routing
    # -- equivalent of request_route{}
    def ksr_request_route(self, msg):

        KSR.dbg("method " + KSR.pv.get("$rm") + " r-uri " + KSR.pv.get("$ru"))


        if KSR.is_method("REGISTER"):
            KSR.sl.send_reply(200, "OK")

        elif KSR.is_method("INVITE"):
                #Lookup our public IP address
                try:
                    ip = requests.get('https://api.ipify.org').text
                except:
                    ip = "Failed to resolve"

                #Add that as a header
                KSR.hdr.append("X-KEMI: I came from KEMI at " + str(ip) + "\r\n");

                #Set host IP to 10.1.1.1
                KSR.sethost("10.1.1.1");

                #Forward the request on
                KSR.forward()
        else:
               KSR.sl.send_reply(500, "Got no idea...")

I’ve put my full code on GitHub which you can find here.

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

Using Wireshark to peer inside IPsec ESP VoLTE data from the P-CSCF

IPsec ESP can be used in 3 different ways on the Gm interface between the Ue and the P-CSCF:

  • Integrity Protection – To prevent tampering
  • Ciphering – To prevent inception / eavesdropping
  • Integrity Protection & Ciphering

On Wireshark, you’ll see the ESP, but you won’t see the payload contents, just the fact it’s an Encapsulated Security Payload, it’s SPI and Sequence number.

By default, Kamailio’s P-CSCF only acts in Integrity Protection mode, meaning the ESP payloads aren’t actually encrypted, with a few clicks we can get Wireshark to decode this data;

Just open up Wireshark Preferences, expand Protocols and jump to ESP

Now we can set the decoding preferences for our ESP payloads,

In our case we’ll tick the “Attempt to detect/decode NULL encrypted ESP payloads” box and close the box by clicking OK button.

Now Wireshark will scan through all the frames again, anything that’s an ESP payload it will attempt to parse.

Now if we go back to the ESP payload with SQN 1 I showed a screenshot of earlier, we can see the contents are a TCP SYN.

Now we can see what’s going on inside this ESP data between the P-CSCF and the UE!

As a matter of interest if you can see the IK and CK values in the 401 response before they’re stripped you can decode encrypted ESP payloads from Wireshark, from the same Protocol -> ESP section you can load the Ciphering and Integrity keys used in that session to decrypt them.

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

SIP Supported & Require

On top of plain vanilla RFC3261, there’s a series of “Extension” methods added to SIP to expand it’s functionality, common extension methods are INFO, MESSAGE, NOTIFY, PRACK and UPDATE. Although now commonplace, of these is not defined in RFC3261 so is considered an “extension” to SIP.

It’s worth just pausing here to reiterate we’re not talking extensions like in a PBX context, like extra phones, we’re talking extensions like you’d add to a house, like extra functionality.

A SIP client can request functionality from a server (UAC to a UAS), if the server does not have support for that functionality, it can reject the session on those grounds and send back a response indicating it doesn’t know how to handle that extension, like a 420 Bad ExtensionBad SIP Protocol Extension used, not understood by the server. Response.

So clients can determine what functionality a server doesn’t support if it rejects the request, but there was no way to see what functionality the server does support, and what functionality the client requires.

Enter the Supported header, initially drafted by Rosenberg & Schulzrinne in 2000, it made it into the SIP we know today (SIP v2 / RFC3261).

If a UAC or UAS requires support for an extension – For example a Media Gateway has to understand PRACK, it can use the Require header to specify the request should be rejected if support for the listed extensions is not provided.

These headers are most commonly seen in SIP OPTIONS requests.

Kamailio Bytes – Configuring Diameter Peers with CDP

I’ve talked a little about my adventures with Diameter in the past, the basics of Diameter, the packet structure and the Python HSS I put together.

Kamailio is generally thought of as a SIP router, but it can in fact handle Diameter signaling as well.

Everything to do with Diameter in Kamailio relies on the C Diameter Peer and CDP_AVP modules which abstract the handling of Diameter messages, and allow us to handle them sort of like SIP messages.

CDP on it’s own doesn’t actually allow us to send Diameter messages, but it’s relied upon by other modules, like CDP_AVP and many of the Kamailio IMS modules, to handle Diameter signaling.

Before we can start shooting Diameter messages all over the place we’ve first got to configure our Kamailio instance, to bring up other Diameter peers, and learn about their capabilities.

C Diameter Peer (Aka CDP) manages the Diameter connections, the Device Watchdog Request/Answers etc, all in the background.

We’ll need to define our Diameter peers for CDP to use so Kamailio can talk to them. This is done in an XML file which lays out our Diameter peers and all the connection information.

In our Kamailio config we’ll add the following lines:

loadmodule "cdp.so"
modparam("cdp", "config_file", "/etc/kamailio/diametercfg.xml")
loadmodule "cdp_avp.so"

This will load the CDP modules and instruct Kamailio to pull it’s CDP info from an XML config file at /etc/kamailio/diametercfg.xml

Let’s look at the basic example given when installed:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 

 DiameterPeer Parameters 
  - FQDN - FQDN of this peer, as it should apper in the Origin-Host AVP
  - Realm - Realm of this peer, as it should apper in the Origin-Realm AVP
  - Vendor_Id - Default Vendor-Id to appear in the Capabilities Exchange
  - Product_Name - Product Name to appear in the Capabilities Exchange 
  - AcceptUnknownPeers - Whether to accept (1) or deny (0) connections from peers with FQDN 
    not configured below
  - DropUnknownOnDisconnect - Whether to drop (1) or keep (0) and retry connections (until restart)
    unknown peers in the list of peers after a disconnection.
  - Tc - Value for the RFC3588 Tc timer - default 30 seconds
  - Workers - Number of incoming messages processing workers forked processes.
  - Queue - Length of queue of tasks for the workers:
     - too small and the incoming messages will be blocked too often;
     - too large and the senders of incoming messages will have a longer feedback loop to notice that
     this Diameter peer is overloaded in processing incoming requests;
     - a good choice is to have it about 2 times the number of workers. This will mean that each worker
     will have about 2 tasks in the queue to process before new incoming messages will start to block.
  - ConnectTimeout - time in seconds to wait for an outbound TCP connection to be established.
  - TransactionTimeout - time in seconds after which the transaction timeout callback will be fired,
    when using transactional processing.
  - SessionsHashSize - size of the hash-table to use for the Diameter sessions. When searching for a 
    session, the time required for this operation will be that of sequential searching in a list of 
    NumberOfActiveSessions/SessionsHashSize. So higher the better, yet each hashslot will consume an
    extra 2xsizeof(void*) bytes (typically 8 or 16 bytes extra).
  - DefaultAuthSessionTimeout - default value to use when there is no Authorization Session Timeout 
  AVP present.
  - MaxAuthSessionTimeout - maximum Authorization Session Timeout as a cut-out measure meant to
  enforce session refreshes.
      
 -->
<DiameterPeer 
        FQDN="pcscf.ims.smilecoms.com"
        Realm="ims.smilecoms.com"
        Vendor_Id="10415"
        Product_Name="CDiameterPeer"
        AcceptUnknownPeers="0"
        DropUnknownOnDisconnect="1"
        Tc="30"
        Workers="4"
        QueueLength="32"
        ConnectTimeout="5"
        TransactionTimeout="5"
        SessionsHashSize="128"
        DefaultAuthSessionTimeout="60"
        MaxAuthSessionTimeout="300"
>

        <!--
                Definition of peers to connect to and accept connections from. For each peer found in here
                a dedicated receiver process will be forked. All other unkwnown peers will share a single
                receiver. NB: You must have a peer definition for each peer listed in the realm routing section
        -->
        <Peer FQDN="pcrf1.ims.smilecoms.com" Realm="ims.smilecoms.com" port="3868"/>
        <Peer FQDN="pcrf2.ims.smilecoms.com" Realm="ims.smilecoms.com" port="3868"/>
        <Peer FQDN="pcrf3.ims.smilecoms.com" Realm="ims.smilecoms.com" port="3868"/>
        <Peer FQDN="pcrf4.ims.smilecoms.com" Realm="ims.smilecoms.com" port="3868"/>
        <Peer FQDN="pcrf5.ims.smilecoms.com" Realm="ims.smilecoms.com" port="3868"/>
        <Peer FQDN="pcrf6.ims.smilecoms.com" Realm="ims.smilecoms.com" port="3868"/>

        <!--
                Definition of incoming connection acceptors. If no bind is specified, the acceptor will bind
                on all available interfaces.
        -->
        <Acceptor port="3868"  />
        <Acceptor port="3869" bind="127.0.0.1" />
        <Acceptor port="3870" bind="192.168.1.1" />

        <!--
                Definition of Auth (authorization) and Acct (accounting) supported applications. This
                information is sent as part of the Capabilities Exchange procedures on connecting to
                peers. If no common application is found, the peers will disconnect. Messages will only
                be sent to a peer if that peer actually has declared support for the application id of 
                the message.
        -->
        <Acct id="16777216" vendor="10415" />
        <Acct id="16777216" vendor="0" />
        <Auth id="16777216" vendor="10415"/>
        <Auth id="16777216" vendor="0" />

        <!-- 
                Supported Vendor IDs - list of values which will be sent in the CER/CEA in the
                Supported-Vendor-ID AVPs
        -->
        <SupportedVendor vendor="10415" />

        <!--
                Realm routing definition.
                Each Realm can have a different table of peers to route towards. In case the Destination
                Realm AVP contains a Realm not defined here, the DefaultRoute entries will be used.

                Note: In case a message already contains a Destination-Host AVP, Realm Routeing will not be
                applied.
                Note: Routing will only happen towards connected and application id supporting peers.
                
                The metric is used to order the list of prefered peers, while looking for a connected and
                application id supporting peer. In the end, of course, just one peer will be selected.
        -->
        <Realm name="ims.smilecoms.com">
                <Route FQDN="pcrf1.ims.smilecoms.com" metric="3"/>
                <Route FQDN="pcrf2.ims.smilecoms.com" metric="5"/>
        </Realm>

        <Realm name="temp.ims.smilecoms.com">
                <Route FQDN="pcrf3.ims.smilecoms.com" metric="7"/>
                <Route FQDN="pcrf4.ims.smilecoms.com" metric="11"/>
        </Realm>
        <DefaultRoute FQDN="pcrf5.ims.smilecoms.com" metric="15"/>
        <DefaultRoute FQDN="pcrf6.ims.smilecoms.com" metric="13"/>


</DiameterPeer>

First we need to start by telling CDP about the Diameter peer it’s going to be – we do this in the <DiameterPeer section where we define the FQDN and Diameter Realm we’re going to use, as well as some general configuration parameters.

<Peers are of course, Diameter peers. Defining them here will mean a connection is established to each one, Capabilities exchanged and Watchdog request/responses managed. We define the usage of each Peer further on in the config.

The Acceptor section – fairly obviously – sets the bindings for the addresses and ports we’ll listen on.

Next up we need to define the Diameter applications we support in the <Acct id=” /> and <SupportedVendor> parameters, this can be a little unintuitive as we could list support for every Diameter application here, but unless you’ve got a module that can handle those applications, it’s of no use.

Instead of using Dispatcher to manage sending Diameter requests, CDP handles this for us. CDP keeps track of the Peers status and it’s capabilities, but we can group like Peers together, for example we may have a pool of PCRF NEs, so we can group them together into a <Realm >. Instead of calling a peer directly we can call the realm and CDP will dispatch the request to an up peer inside the realm, similar to Dispatcher Groups.

Finally we can configure a <DefaultRoute> which will be used if we don’t specify the peer or realm the request needs to be sent to. Multiple default routes can exist, differentiated based on preference.

We can check the status of peers using Kamcmd’s cdp.list_peers command which lists the peers, their states and capabilities.

Kamailio Bytes – Dispatcher States

You may already be familiar with Kamailio’s Disptacher module, if you’re not, you can learn all about it in my Kamailio Bytes – Dispatcher Module post.

One question that’s not as obvious as it perhaps should be is the different states shown with kamcmd dispatcher.list command;

So what do the flags for state mean?

The first letter in the flag means is the current state, Active (A), Inactive (I) or Disabled (D).

The second letter in the flag means monitor status, Probing (P) meaning actively checked with SIP Options pings, or Not Set (X) denoting the device isn’t actively checked with SIP Options pings.

AP Actively Probing – SIP OPTIONS are getting a response, routing to this destination is possible, and it’s “Up” for all intents and purposes.

IPInactively Probing – Destination is not meeting the threshold of SIP OPTIONS request responses it needs to be considered active. The destination is either down or not responding to all SIP OPTIONS pings. Often this is due to needing X number of positive responses before considering the destination as “Up”.

DX Disabled & Not Probing – This device is disabled, no SIP OPTIONS are sent.

AX Active & Not Probing– No SIP OPTIONS are sent to check state, but is is effectively “Up” even though the remote end may not be reachable.

Kamailio Bytes – Rewriting SIP Headers (Caller ID Example)

Back to basics today,

In the third part of the Kamailio 101 series I briefly touched upon pseudovariables, but let’s look into what exactly they are and how we can manipulate them to change headers.

The term “pseudo-variable” is used for special tokens that can be given as parameters to different script functions and they will be replaced with a value before the execution of the function.

https://www.kamailio.org/wiki/cookbooks/devel/pseudovariables

You’ve probably seen in any number of the previous Kamailio Bytes posts me use pseudovariables, often in xlog or in if statements, they’re generally short strings prefixed with a $ sign like $fU, $tU, $ua, etc.

When Kamailio gets a SIP message it explodes it into a pile of variables, getting the To URI and putting it into a psudovariable called $tU, etc.

We can update the value of say $tU and then forward the SIP message on, but the To URI will now use our updated value.

When it comes to rewriting caller ID, changing domains, manipulating specific headers etc, pseudovariables is where it mostly happens.

Kamailio allows us to read these variables and for most of them rewrite them – But there’s a catch. We can mess with the headers which could result in our traffic being considered invalid by the next SIP proxy / device in the chain, or we could mess with the routing headers like Route, Via, etc, and find that our responses never get where they need to go.

So be careful! Headers exist for a reason, some are informational for end users, others are functional so other SIP proxies and UACs can know what’s going on.

Rewriting SIP From Username Header (Caller ID)

When Kamailio’s SIP parser receives a SIP request/response it decodes the vast majority of the SIP headers into a variety of pseudovariables, we can then reference these variables we can then reference from our routing logic.

Let’s pause here and go back to the Stateless SIP Proxy Example, as we’ll build directly on that.

Follow the instructions in that post to get your stateless SIP proxy up and running, and we’ll make this simple change:

####### Routing Logic ########


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

        xlog("Received $rm to $ru - Forwarding");
        $fU = "Nick Blog Example";   #Set From Username to this value
        #Forward to new IP
        forward("192.168.1.110");

}

Now when our traffic is proxied the From Username will show “Nick Blog Example” instead of what it previously showed.

Pretty simple, but very powerful.

As you’ve made it this far might be worth familiarising yourself with the different types of SIP proxy – Stateless, Transaction Stateful and Dialog Stateful.

Final Selectors in a ATE50 Step by Step Telephone Exchange

1938 ATE PAX50 Step By Step Exchange

Steel Switchboard & Switchgear

That’s all the description said.

The blurry photo didn’t make anything that much clearer, but they looked like two motion switches, and being a big fan of really old telco hardware, I found myself driving to an auction selling things very much unrelated to telephone exchanges to bid on what I thought might have been a step-by-step exchange.

Photo from listing

$50 later I am now the proud owner of an Automatic Telephone & Electric Co (ATE) Liverpool works 50 line PAX (Private Automatic Exchange).

The switch

My office now has less room, a big burly battery eliminator and ring machine take up the space on my desk, but I couldn’t be happier with it.

Uniselectors

Of the 5 final selectors I’ve got two somehow worked “out of the box”, while the other 3 all need some serious adjustment, but she clicks and she’s mostly complete, so should be a good summer holiday project!

I’ll post some video up when she’s fully functional.

OTP Authentication required to unlock IMS Debugging and TCPDUMP on Samsung Sysdump tool

Reverse Engineering Samsung Sysdump Utils to Unlock IMS Debug & TCPdump on Samsung Phones

Note: This post is just about the how I reverse engineered the tool, for info on how to use it, you want this post.

While poking around the development and debugging features on Samsung handsets I found the ability to run IMS Debugging directly from the handset.

Alas, the option is only available in the commercial version, it’s just there for carriers, and requires a One Time Password to unlock.

OTP Authentication required to unlock IMS Debugging and TCPDUMP on Samsung Sysdump tool "This menu is not allowed for commercial version. You can activate this menu after OTP Authentication enabled"

When tapping on the option a challenge is generated with a key.

Interestingly I noticed that the key changes each time and can reject you even in aeroplane mode, suggesting the authentication happens client side.

This left me thinking – If the authentication happens client side, then the App has to know what the valid password for the key shown is…

Some research revealed you can pull APKs off an Android phone, so I downloaded a utility called “APK Extractor” from the Play store, and used it to extract the Samsung Sysdump utility.

So now I was armed with the APK on my local machine, the next step was to see if I could decompile the APK back into source code.

Some Googling found me an online APK decompiler, which I fed the compiled APK file and got back the source code.

I did some poking around inside the source code, and then I found an interesting directory:

Here’s a screenshot of the vanilla code that came out of the app.

Samsung OTPSecurty Source Code

I’m not a Java expert, but even I could see the “CheckOTP” function and understand that that’s what validates the One Time Passwords.

The while loop threw me a little – until I read through the rest of the code; the “key” in the popup box is actually a text string representing the current UNIX timestamp down to the minute level. The correct password is an operation done on the “key”, however the CheckOTP function doesn’t know the challenge key, but has the current time, so generates a challenge key for each timestamp back a few minutes and a few minutes into the future.

I modified the code slightly to allow me to enter the presented “key” and get the correct password back. It’s worth noting you need to act quickly, enter the “key” and enter the response within a minute or so.

In the end I’ve posted the code on an online Java compiler,

Generate OTP Response from Key (Challenge)

Replace yy182 with your challenge. I suggest you try the 0 offset and type it in quickly.

I did a write up on how to use the features this unlocks in this post.

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

Samsung-Sysdump-IMS-Debug-DM-View_Cropped

VoLTE/IMS Debugging on Samsung Handsets using Sysdump & Samsung IMS Logger

Samsung handsets have a feature built in to allow debugging from the handset, called Sysdump.

Entering *#9900# from the Dialing Screen will bring up the Sysdump App, from here you can dump logs from the device, and run a variety of debugging procedures.

Samsung share information about this app publicly on their website,

Sysdump App in Samsung handsets used for debugging the device

But for private LTE operators, the two most interesting options are by far the TCPDUMP START option and IMS Logger, but both are grayed out.

Tapping on them asks for a one-time password and has a challenge key.

OTP Authentication required to unlock IMS Debugging and TCPDUMP on Samsung Sysdump tool

These options are not available in the commercial version of the OS and need to be unlocked with a one time key generated by a tool Samsung for unlocking engineering firmware on handsets.

Luckily this authentication happens client side, which means we can work out the password it’s expecting.

For those interested I’ve done a write up of how I reversed the password validation algorithm to take the key given in the OTP challenge and generate a valid response.

For those who just want to unlock these features you can click here to run the tool that generates the response.

Once you’ve entered the code and successfully unlocked the IMS Debugging tool there’s a few really cool features in the hamburger menu in the top right.

DM View

This shows the SIP / IMS Messaging and the current signal strength parameters (used to determine which RAN type to use (Ie falling back from VoLTE to UMTS / Circuit Switched when the LTE signal strength drops).

Screenshot of Samsung Sysdump tool in the IMS Debug - DM View section

Tapping on the SIP messages expands them and allows you to see the contents of the SIP messages.

Viewing SIP Messaging directly from the handset

Interesting the actual nitty-gritty parameters in the SIP headers are missing, replaced with X for anything “private” or identifiable.

Luckily all this info can be found in the Pcap.

The DM View is great for getting a quick look at what’s going on, on the mobile device itself, without needing a PC.

Logging

The real power comes in the logging functions,

There’s a lot of logging options, including screen recording, TCPdump (as in Packet Captures) and Syslog logging.

From the hamburger menu we can select the logging parameters we want to change.

Settings for Samsung IMS Logger

From the Filter Options menu we can set what info we’re going to log,

Filter options used in Dump output of Samsung IMS Logger application

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