All posts by Nick

About Nick

Dialtone.

GSM base Station

GSM with Osmocom Part 2: BTS Basics

By far the most visable part of any mobile network (apart from your phone!) is the Base (Transciver) Stations.

Dotted around the countryside, on masts, towers and monopoles, whether you notice them or not, base stations are everywhere.

The Architecture

The RF side of LTE has an eNodeB, which is a smart device. – You connect it to a TCP/IP network, it establishes a connection with your MME(s) and away you go.

A GSM BTS (Base Transceiver Station) isn’t all that clever…

The BTS is a similar to the WiFi access points that talk to a centralised controller for all their thinking.
A BTS gets most of its brains from elsewhere and essentially just handles the TX/RX of baseband data.

That elsewhere is the BSC – Base Station Controller. Each BTS connects to a BSC, and a BSC would typically control a number of BTS.

We’ll explore the BSC and it’s connections in depth, but I’ve put together a basic diagram of how everything fits together below.

Basic GSM Access Architecture

Um Interface

The Um interface is the Air Interface of GSM. It’s what takes the data and sends it out “over the air”.

There’s a lot to know about air interfaces, and I know very little. What I do know is I need to set the Um interface to use a frequency band my mobile phone supports (so I can see and connect to the network).

The Abis Interface

The fact that GSM was first deployed in 1991, explains why the Abis interface used ISDN E1/T1 TDM links to connect the Base Transceiver Stations (BTSs) to the Base Station Controller (BSC).

While now looking back you may ask why TCP/IP wasn’t used for the Abis interface, keep in mind that Windows 95 was the first version to include TCP/IP support, and that gives you an idea of the state of play. ISDN is very reliable and was well known in the telco space at that time.

I no longer have any ISDN hardware, so for me this is all going to be built using packet switched networks working as circuit switched.

Osmocom does have support for E1/T1 interfaces, so if you’ve got BTS hardware that only communicates over TDM links, that’s an option too.

GSMA never wrote a standard for taking Abis over IP, so as such each vendor has implemented it differently.

Osmocom have a flavour of Abis over IP protocol they’ve developed based on traces from a commercial implementation which we’ll be using. You can find the full protocol spec for Osmocom’s Abis over IP interface here.

OML Interface

With all the brains for the BTS residing in the BSC, there’s a need to control the BTS from the BSC. The Operation and Maintenance Link (OML) is a protocol for changing certain parameters of the BTS from the BSC.

A prime example of use of the OML would be the BSC turning the BTS off/on.

We’ll see a tiny bit of OML usage in the next post, just for turning the BTS off and on.

So let’s put this into practice and setup a virtual BTS with Osmocom.

Osmocom Logo

GSM with Osmocom Part 1: Intro

Meta

Like most people at the moment because of the lockdown I’ve got a bit more time at home than normal.

Because of this I thought I’d finally dive into GSM/UMTS and all that circuit switched tech you skip out on when getting started with LTE.

Please excuse the loving tone I use when describing some older tech, it’s a result of being a telephony tragic who gets all reminiscent thinking about the first phones they interacted with & wondered about how it all worked…

So why learn GSM?

My best friend is a translator of technical documents. In University, what’s the first language they study? Latin. Because it’s the root of so many languages.

While a lot of carriers have already switched off their GSM networks (There are no public GSM networks in Australia), the core of GSM is essentially shared with that of UMTS / 3G, which is still going to be around for the foreseeable future.

Circuit Switched Fallback (CSFB) is still common today for voice calls for a great many LTE handsets without VoLTE support. GSM powers GSM-R, the rail specific standard of GSM used across Europe. The uplink power of GSM can be up to 8 Watts (while in LTE it’s 20 dBm – 0.1W) which means it’s effective service area could be larger than 3G and 4G air interfaces.

GSM isn’t as dead as it might seem, so let’s have a Weekend at Bernie’s!

Disclaimer: Please let me know if I’ve got anything wrong! These posts will focus on the Omsocom network elements which do handle a few things the “non standard” way.

Platforms

I wrote last week about using YateBTS to get a functional GSM / GPRS network online,

It’s great that it works from a Um interface perspective; your UE / terminal can see and connect to the network. But it’s sort of an all-in-one solution; there’s no Mobile Switching Center, Base Station Controller, Sigtran, HLR or Media Gateway; Yate ties all this up into a single easy to use package.

This gets you on the air in no time, but unfortunately you don’t get exposed to how GSM / UMTS works in the real world – real networks don’t look like YateBTS NITPC.

Enter Osmocom

Osmocom (and the Sysmocom team driving many of the projects) have done a phenomenal job of building each of the network elements I just talked about pretty much “by the book”, meaning most should interop with commercial equipment and comply to the standards.

Over the next few weeks I’ll cover setting up each of the network elements, talking about what they do and how they work, and use them to create a functional GSM network (2G / Circuit Switched) using the software from Osmocom.

Once we’ve got our network functional we’ll be adding SMS, Data (GPRS / EDGE), USSD codes and even inter-RAT handover with LTE.

For this series of posts I’ll be using a mix of hardware. At the start I’ll be using a Software Defined Radio (LimeSDR) to do the RF side of the network (BTS). Osmocom has support for a lot of common SDR hardware, so hopefully for anyone wanting to follow along at home you’ll have access to a LimeSDR or USRP.

Osmocom supports many commercial BTS vendors products, as well as Sysmocom’s hardware, hardware from Range Networks. Osmocom also supports the NanoBTS range from ip.access – which are available second hand quite cheaply, which I’ll be adding to our network as well.

So buy some cheap programmable SIM cards, grab your SDR hardware or order cheap second hand GSM BTS online, and let’s get building a GSM network!

Topics Covered

I’ll update the links in here as I publish these posts, I may forget so if there’s something missing search the site or drop me a line.

My Osmocom Config Files on GitHub

Radio Access Network

BTS Basics

BTS In Practice with LimeSDR & osmo-bts-trx

The Base Station Controller (BSC)

Integrating our LimeSDR BTS with OsmoBSC

Integrating an ipaccess NanoBTS with OsmoBSC

Channel Types

Switching & Signaling

Home Location Register (HLR) (with EIR & AuC)

The SS7 Signaling Transfer Point

The Mobile Switching Center

Basic calls & SMS

Call Routing

SMS Routing

Distributed GSM

Cell Broadcast Center

External USSD Interfaces

Data Services

GPRS & Packet Data Basics

Serving Gateway Support Node

Gateway GPRS Support Node

Call Flows & Handovers

Basic GSM Calls

Basic GSM SMS

Basic GSM Packet Bearers

Inter BSC

Inter MSC

Inter-RAT

Inter RAT between LTE and GSM

GSM CSFB with OmsmoMSC and Open5GS LTE

SGs Interface for SMS over LTE

Decoding MAC LTE Frames in Wireshark

Working with LTE MAC traces in Wireshark

I recently pulled MAC layer traces off an eNB and wanted to view them,

In Wireshark this shows up as raw data, and there’s no option to decode as LTE MAC from the Decode As menu.

Instead you’ve got to go to Preferences -> Protocols and select DLT_USER and then edit the encapsulation table.

For DLT_147 enter:

mac-lte-framed

Now you’ll have your MAC frames decoded:

On top of this there’s also now the option to run analysis on these traces,

By selecting Telephony -> LTE -> MAC Statistics you’re able to view stats for each RNTI connected to the eNB.

I’ve attached a copy of my trace for reference.

Magic SIM Card Art

16 in 1 Magic SIM Card Revisited

I found a “16-in-1 Super SIM X-SIM” in my SIM card drawer, I think I ordered these when I was first playing with GSM and never used it.

I was kind of curious about how these actually worked, so after some online sleuthing I found a very suspicious looking rar file, which I ended up running in a VM and mapping the Card Reader to the VM.

What a treat I was in for in terms of UI.

The concept is quite simple, you program a series of IMSI and K key values onto the SIM card, and then using a SIM Toolkit application, you’re able to select which IMSI / K key combination you want to use.

A neat trick, I’d love a LTE version of this for changing values on the fly, but it’d be a pretty niche item considering no operator is going to give our their K and OPc keys,

But come to think of it, no GSM operator would give out K keys, so how do you get the K key from your commercial operator?

I noticed the grayed out “Crack” icon on the menu.

After rifling through my SIM drawer I found a few really old 2G SIMs, stuck one in, reconnected and clicked “Crack” and then start.

I left it running in the background after the manual suggested it could take up to 24 hours to run through all the codes.

To my surprise after 2 minutes the software was requesting I save the exported data, which I did.

Then I put the 16 in 1 back in, selected Magic and then imported the cracked SIM data (IMSI, ICCID, Ki & SMSp).

By the looks of it the software is just running a brute force attack on the SIM card, and the keyspace is only so large meaning it can be reversed in.

I did a bit of research to find out if this is exploiting any clever vulnerabilities in UCCID cards, but after running some USB Pcap traces it looks like it’s just plain old brute force, which could be easily defended against by putting a pause between auth attempts on the SIM.

I’ve no idea if that’s the actual K value I extracted from the SIM – The operator that issued the SIM doesn’t even exist anymore, but I’ll add the details to the HLR of my Osmocom GSM lab and see if it matches up.

Out of curiosity I also connected some of my development USIM/ISIM/SIM cards that I can program, the software is amazing in it’s response:

Configuring YateBTS for Software Defined GSM/GPRS

I did a post yesterday on setting up YateBTS, I thought I’d cover the basic setup I had to do to get everything humming;

Subscribers

In order to actually accept subscribers on the network you’ll need to set a Regex pattern to match the prefix of the IMSI of the subscribers you want to connect to the network,

In my case I’m using programmable SIMs with MCC / MNC 00101 so I’ve put the regex pattern matching starting with 00101.

BTS Configuration

Next up you need to set the operating frequency (radio band), MNC and MCC of the network. I’m using GSM850,

Next up we’ll need to set the device we’re going to use for the TX/RX, I’m using a BladeRF Software Defined Radio, so I’ve selected that from the path.

Optional Steps

I’ve connected Yate to a SIP trunk so I can make and receive calls,

I’ve also put a tap on the GSM signaling, so I can see what’s going on, to access it just spin up Wireshark and filter for GSMMAP

Compiling YateBTS NIPC for Software Defined GSM / GPRS

A lot of the Yate tutorials are a few years old, so I thought I’d put together the steps I used on Ubuntu 18.04:

Installing Yate

apt-get install subversion autoconf build-essential
cd /usr/src svn checkout http://voip.null.ro/svn/yate/trunk yate 
cd yate
./autogen.sh
./configure
make
make install-noapi

Installing YateBTS

cd /usr/src 
svn checkout http://voip.null.ro/svn/yatebts/trunk yatebts
cd yatebts/ 
./autogen.sh 
./configure
make install

Defining location of libyate

On Ubuntu I found I had to add the library location in ldconf:

echo "include /usr/local/lib/" > /etc/ld.so.conf
ldconfig

Installing Web Interface for NIB / NIPC

apt-get install apache2 php
cd /usr/src/yatebts/nipc
make install
cd /var/www/html
ln -s /usr/local/share/yate/nipc_web nipc
chmod a+rw /usr/local/etc/yate/

Kamailio Bytes – Docker and Containers

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

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

The Dockerfile

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

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

For this example I’m going to include

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

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

#Print out the current IP Address info
RUN ip add

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

Once the dockerfile is created we can build an image,

docker image build -t kamtest:0.1 .

And then run it,

docker run kamtest:0.1

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

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

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

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

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

Kamailio Proxy-CSCF Pull

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

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

And here it is!

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

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

Multi Operator Core-Networks (MOCN) for RAN Sharing

MOCN is one of those great concepts I’d not really come across,

Multi-tenancy on the RAN side of the network, allowing an eNB to broadcast multiple PLMN IDs (MCC/MNC) in the System Information Block (SIB).

It allows site sharing not just on the tower itself, but site sharing on the RAN side, allowing customers of MNO A to see themselves connected to MNO A, and customers from MNO B see themselves as connected to MNO B, but they’re both connected to the same RAN hardware.

Setup in my lab was a breeze; your RAN hardware will probably be different.

In terms of signaling it’s a standard S1AP Setup Request except with multiple broadcast PLMN keys:

Now when I run a manual cell selection on my UE I can see the PLMN 460/11 as well as the Open5gs 00101 PLMN:

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 – http_client

I’ve touched on the http_client module in Kamailio in the past, and I’ve talked about using Kamailio as an HTTP server.

Today I thought I’d cover a simple use case – running an HTTP get from Kamailio and doing something with the output.

The http_client does what it sounds – Acts as an HTTP client to send HTTP GET and POST requests.

The use cases for this become clear quite quickly, you could use http_client to request credit from an accounting server via it’s API, get the latest rate to a destination from a supplier, pull weather data, etc, etc.

Let’s take a very simple example, we’ll load http_client by adding a loadmodule line:

...
loadmodule "http_client.so"
...

Next I’ve put together a very simple request_route block that requests a HTTP file from a web server and sends the response to a SIP client:

####### 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("Got request");
        http_client_query("https://nickvsnetworking.com/utils/curl.html", "", "$var(result)");
        xlog("Result is $var(result)");
        sl_reply("200", "Result from HTTP server was  $var(result)");
}

Using the http_client_query() function we’re able to query a HTTP server,

We’ll query the URL https://nickvsnetworking.com/utils/curl.html and store the output to a variable called result.

If you visit the URL you’ll just get a message that says “Hello there“, that’s what Kamailio will get when it runs the http_client function.

Next we print the result using an xlog() function so we can see the result in syslog,

Finally we send a stateless reply with the result code 200 and the body set to the result we got back from the server.

We can make this a bit more advanced, using HTTP Post we can send user variables and get back responses.

The http_client module is based on the ubiquitous cURL tool, that many users will already be familiar with.

You can find a full copy of my running code on GitHub.

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.

Ansible for Scaling and Deployment of Evolved Packet Core NEs

There’s always lots of talk of Network Function Virtualization (NFV) in the Telco space, but replacing custom hardware with computing resources is only going to get you so far, if every machine has to be configured manually.

Ansible is a topic I’ve written a little bit about in terms of network automation / orchestration.

I wanted to test limits of Open5gs EPC, which led me to creating a lot of Packet Gateways, so I thought I’d share a little Ansible Playbook I wrote for deploying P-GWs.

It dynamically sets the binding address and DHCP servers, and points to each PCRF in the defined pool.

You can obviously build upon this too, creating another playbook to deploy PCRFs, MMEs and S-GWs will allow you to reference the hosts in each group to populate the references.

The Playbook

---
- name: Install & configure Open5GS EPC (P-GW)

  hosts: epc

  become: yes
  become_method: sudo

  vars:
    dns_servers: ['8.8.8.8','1.1.1.1']
    diameter_realm: 'nickvsnetworking.com'
    pcrf_hosts: ['pcrf1.nickvsnetworking.com', 'pcrf2.nickvsnetworking.com']
  tasks:
    - name: Set Hostname to {{inventory_hostname}}
      hostname:
        name: "{{inventory_hostname}}"
      register: rebootrequired

    - name: Updating hotnamectl
      command: "hostnamectl set-hostname {{inventory_hostname}}"
      become: True
      become: True
      when: rebootrequired.changed

    - name: Reboot VM due to Hostname Change
      reboot:
        reboot_timeout: 180
      when: rebootrequired.changed

    - name: Add Software common
      apt:
        name: software-properties-common

    - name: Add Repository
      apt_repository:
        repo: ppa:open5gs/latest

    - name: Install Open5gs P-GW
      apt:
        update_cache: true
        name: open5gs-pgw

    - name: Fill in GTP Addresses in Config
      template:
        src: pgw.yaml.j2
        dest: /etc/open5gs/pgw.yaml
        backup: true
      register: config_changed

    - name: Fill in P-GW Diameter Config
      template:
        src: pgw.conf.j2
        dest: /etc/freeDiameter/pgw.conf
        backup: true
      register: config_changed


    - name: Restart P-GW Service if Config Changed
      service:
        name: open5gs-pgwd
        state: restarted
      when: config_changed.changed

Jinja2 Template

P-GW Config (pgw.yaml.jn2)

logger:
    file: /var/log/open5gs/pgw.log

parameter:

pgw:
    freeDiameter: /etc/freeDiameter/pgw.conf
    gtpc:
      - addr: {{hostvars[inventory_hostname]['ansible_default_ipv4']['address']}}

    gtpu:
      - addr: {{hostvars[inventory_hostname]['ansible_default_ipv4']['address']}}

    ue_pool:
      - addr: 45.45.0.1/16
      - addr: cafe::1/64
    dns:
{% for dns in dns_servers %}
      - {{ dns }}
{% endfor %}

Diameter Config (pgw.conf.j2)

# This is a sample configuration file for freeDiameter daemon.

# Most of the options can be omitted, as they default to reasonable values.
# Only TLS-related options must be configured properly in usual setups.

# It is possible to use "include" keyword to import additional files
# e.g.: include "/etc/freeDiameter.d/*.conf"
# This is exactly equivalent as copy & paste the content of the included file(s)
# where the "include" keyword is found.


##############################################################
##  Peer identity and realm

# The Diameter Identity of this daemon.
# This must be a valid FQDN that resolves to the local host.
# Default: hostname's FQDN
#Identity = "aaa.koganei.freediameter.net";
Identity = "{{ inventory_hostname  }}.{{ diameter_realm }}";

# The Diameter Realm of this daemon.
# Default: the domain part of Identity (after the first dot).
#Realm = "koganei.freediameter.net";
Realm = "{{ diameter_realm }}";
##############################################################
##  Transport protocol configuration

# The port this peer is listening on for incoming connections (TCP and SCTP).
# Default: 3868. Use 0 to disable.
#Port = 3868;

# The port this peer is listening on for incoming TLS-protected connections (TCP and SCTP).
# See TLS_old_method for more information about TLS flavours.
# Note: we use TLS/SCTP instead of DTLS/SCTP at the moment. This will change in future version of freeDiameter.
# Default: 5868. Use 0 to disable.
#SecPort = 5868;

# Use RFC3588 method for TLS protection, where TLS is negociated after CER/CEA exchange is completed
# on the unsecure connection. The alternative is RFC6733 mechanism, where TLS protects also the
# CER/CEA exchange on a dedicated secure port.
# This parameter only affects outgoing connections.
# The setting can be also defined per-peer (see Peers configuration section).
# Default: use RFC6733 method with separate port for TLS.
#TLS_old_method;

# Disable use of TCP protocol (only listen and connect over SCTP)
# Default : TCP enabled
#No_TCP;

# Disable use of SCTP protocol (only listen and connect over TCP)
# Default : SCTP enabled
#No_SCTP;
# This option is ignored if freeDiameter is compiled with DISABLE_SCTP option.

# Prefer TCP instead of SCTP for establishing new connections.
# This setting may be overwritten per peer in peer configuration blocs.
# Default : SCTP is attempted first.
#Prefer_TCP;

# Default number of streams per SCTP associations.
# This setting may be overwritten per peer basis.
# Default : 30 streams
#SCTP_streams = 30;

##############################################################
##  Endpoint configuration

# Disable use of IP addresses (only IPv6)
# Default : IP enabled
#No_IP;

# Disable use of IPv6 addresses (only IP)
# Default : IPv6 enabled
#No_IPv6;

# Specify local addresses the server must bind to
# Default : listen on all addresses available.
#ListenOn = "202.249.37.5";
#ListenOn = "2001:200:903:2::202:1";
#ListenOn = "fe80::21c:5ff:fe98:7d62%eth0";
ListenOn = "{{hostvars[inventory_hostname]['ansible_default_ipv4']['address']}}";


##############################################################
##  Server configuration

# How many Diameter peers are allowed to be connecting at the same time ?
# This parameter limits the number of incoming connections from the time
# the connection is accepted until the first CER is received.
# Default: 5 unidentified clients in paralel.
#ThreadsPerServer = 5;

##############################################################
##  TLS Configuration

# TLS is managed by the GNUTLS library in the freeDiameter daemon.
# You may find more information about parameters and special behaviors
# in the relevant documentation.
# http://www.gnu.org/software/gnutls/manual/

# Credentials of the local peer
# The X509 certificate and private key file to use for the local peer.
# The files must contain PKCS-1 encoded RSA key, in PEM format.
# (These parameters are passed to gnutls_certificate_set_x509_key_file function)
# Default : NO DEFAULT
#TLS_Cred = "<x509 certif file.PEM>" , "<x509 private key file.PEM>";
#TLS_Cred = "/etc/ssl/certs/freeDiameter.pem", "/etc/ssl/private/freeDiameter.key";
TLS_Cred = "/etc/freeDiameter/pgw.cert.pem", "/etc/freeDiameter/pgw.key.pem";

# Certificate authority / trust anchors
# The file containing the list of trusted Certificate Authorities (PEM list)
# (This parameter is passed to gnutls_certificate_set_x509_trust_file function)
# The directive can appear several times to specify several files.
# Default : GNUTLS default behavior
#TLS_CA = "<file.PEM>";
TLS_CA = "/etc/freeDiameter/cacert.pem";

# Certificate Revocation List file
# The information about revoked certificates.
# The file contains a list of trusted CRLs in PEM format. They should have been verified before.
# (This parameter is passed to gnutls_certificate_set_x509_crl_file function)
# Note: openssl CRL format might have interoperability issue with GNUTLS format.
# Default : GNUTLS default behavior
#TLS_CRL = "<file.PEM>";

# GNU TLS Priority string
# This string allows to configure the behavior of GNUTLS key exchanges
# algorithms. See gnutls_priority_init function documentation for information.
# You should also refer to the Diameter required TLS support here:
#   http://tools.ietf.org/html/rfc6733#section-13.1
# Default : "NORMAL"
# Example: TLS_Prio = "NONE:+VERS-TLS1.1:+AES-128-CBC:+RSA:+SHA1:+COMP-NULL";
#TLS_Prio = "NORMAL";

# Diffie-Hellman parameters size
# Set the number of bits for generated DH parameters
# Valid value should be 768, 1024, 2048, 3072 or 4096.
# (This parameter is passed to gnutls_dh_params_generate2 function,
# it usually should match RSA key size)
# Default : 1024
#TLS_DH_Bits = 1024;

# Alternatively, you can specify a file to load the PKCS#3 encoded
# DH parameters directly from. This accelerates the daemon start
# but is slightly less secure. If this file is provided, the
# TLS_DH_Bits parameters has no effect.
# Default : no default.
#TLS_DH_File = "<file.PEM>";


##############################################################
##  Timers configuration

# The Tc timer of this peer.
# It is the delay before a new attempt is made to reconnect a disconnected peer.
# The value is expressed in seconds. The recommended value is 30 seconds.
# Default: 30
#TcTimer = 30;

# The Tw timer of this peer.
# It is the delay before a watchdog message is sent, as described in RFC 3539.
# The value is expressed in seconds. The default value is 30 seconds. Value must
# be greater or equal to 6 seconds. See details in the RFC.
# Default: 30
#TwTimer = 30;

##############################################################
##  Applications configuration

# Disable the relaying of Diameter messages?
# For messages not handled locally, the default behavior is to forward the
# message to another peer if any is available, according to the routing
# algorithms. In addition the "0xffffff" application is advertised in CER/CEA
# exchanges.
# Default: Relaying is enabled.
#NoRelay;

# Number of server threads that can handle incoming messages at the same time.
# Default: 4
#AppServThreads = 4;

# Other applications are configured by loaded extensions.

##############################################################
##  Extensions configuration

#  The freeDiameter framework merely provides support for
# Diameter Base Protocol. The specific application behaviors,
# as well as advanced functions, are provided
# by loadable extensions (plug-ins).
#  These extensions may in addition receive the name of a
# configuration file, the format of which is extension-specific.
#
# Format:
#LoadExtension = "/path/to/extension" [ : "/optional/configuration/file" ] ;
#
# Examples:
#LoadExtension = "extensions/sample.fdx";
#LoadExtension = "extensions/sample.fdx":"conf/sample.conf";

# Extensions are named as follow:
# dict_* for extensions that add content to the dictionary definitions.
# dbg_*  for extensions useful only to retrieve more information on the framework execution.
# acl_*  : Access control list, to control which peers are allowed to connect.
# rt_*   : routing extensions that impact how messages are forwarded to other peers.
# app_*  : applications, these extensions usually register callbacks to handle specific messages.
# test_* : dummy extensions that are useful only in testing environments.


# The dbg_msg_dump.fdx extension allows you to tweak the way freeDiameter displays some
# information about some events. This extension does not actually use a configuration file
# but receives directly a parameter in the string passed to the extension. Here are some examples:
## LoadExtension = "dbg_msg_dumps.fdx" : "0x1111"; # Removes all default hooks, very quiet even in case of errors.
## LoadExtension = "dbg_msg_dumps.fdx" : "0x2222"; # Display all events with few details.
## LoadExtension = "dbg_msg_dumps.fdx" : "0x0080"; # Dump complete information about sent and received messages.
# The four digits respectively control: connections, routing decisions, sent/received messages, errors.
# The values for each digit are:
#  0 - default - keep the default behavior
#  1 - quiet   - remove any specific log
#  2 - compact - display only a summary of the information
#  4 - full    - display the complete information on a single long line
#  8 - tree    - display the complete information in an easier to read format spanning several lines.

LoadExtension = "/usr/lib/x86_64-linux-gnu/freeDiameter/dbg_msg_dumps.fdx" : "0x8888";
LoadExtension = "/usr/lib/x86_64-linux-gnu/freeDiameter/dict_rfc5777.fdx";
LoadExtension = "/usr/lib/x86_64-linux-gnu/freeDiameter/dict_mip6i.fdx";
LoadExtension = "/usr/lib/x86_64-linux-gnu/freeDiameter/dict_nasreq.fdx";
LoadExtension = "/usr/lib/x86_64-linux-gnu/freeDiameter/dict_nas_mipv6.fdx";
LoadExtension = "/usr/lib/x86_64-linux-gnu/freeDiameter/dict_dcca.fdx";
LoadExtension = "/usr/lib/x86_64-linux-gnu/freeDiameter/dict_dcca_3gpp.fdx";


##############################################################
##  Peers configuration

#  The local server listens for incoming connections. By default,
# all unknown connecting peers are rejected. Extensions can override this behavior (e.g., acl_wl).
#
#  In addition to incoming connections, the local peer can
# be configured to establish and maintain connections to some
# Diameter nodes and allow connections from these nodes.
#  This is achieved with the ConnectPeer directive described below.
#
# Note that the configured Diameter Identity MUST match
# the information received inside CEA, or the connection will be aborted.
#
# Format:
#ConnectPeer = "diameterid" [ { parameter1; parameter2; ...} ] ;
# Parameters that can be specified in the peer's parameter list:
#  No_TCP; No_SCTP; No_IP; No_IPv6; Prefer_TCP; TLS_old_method;
#  No_TLS;       # assume transparent security instead of TLS. DTLS is not supported yet (will change in future versions).
#  Port = 5868;  # The port to connect to
#  TcTimer = 30;
#  TwTimer = 30;
#  ConnectTo = "202.249.37.5";
#  ConnectTo = "2001:200:903:2::202:1";
#  TLS_Prio = "NORMAL";
#  Realm = "realm.net"; # Reject the peer if it does not advertise this realm.
# Examples:
#ConnectPeer = "aaa.wide.ad.jp";
#ConnectPeer = "old.diameter.serv" { TcTimer = 60; TLS_old_method; No_SCTP; Port=3868; } ;
{% for pcrf in pcrf_hosts %}
ConnectPeer = "{{ pcrf }}" { ConnectTo = "{{ pcrf }}"; No_TLS; };
{% endfor %}



##############################################################

GTPv2 – F-TEID Interface Types

I’ve been working on a ePDG for VoWiFi access to my IMS core.

This has led to a bit of a deep dive into GTP (easy enough) and GTPv2 (Bit harder).

The Fully Qualified Tunnel Endpoint Identifier includes an information element for the Interface Type, identified by a two digit number.

Here we see S2b is 32

In the end I found the answer in 3GPP TS 29.274, but thought I’d share it here.

0S1-U eNodeB GTP-U interface
1S1-U SGW GTP-U interface
2S12 RNC GTP-U interface
3S12 SGW GTP-U interface
4S5/S8 SGW GTP-U interface
5S5/S8 PGW GTP-U interface
6S5/S8 SGW GTP-C interface
7S5/S8 PGW GTP-C interface
8S5/S8 SGW PMIPv6 interface (the 32 bit GRE key is encoded in 32 bit TEID field and since alternate CoA is
not used the control plane and user plane addresses are the same for PMIPv6)
9S5/S8 PGW PMIPv6 interface (the 32 bit GRE key is encoded in 32 bit TEID field and the control plane and
user plane addresses are the same for PMIPv6)
10S11 MME GTP-C interface
11S11/S4 SGW GTP-C interface
12S10 MME GTP-C interface
13S3 MME GTP-C interface
14S3 SGSN GTP-C interface
15S4 SGSN GTP-U interface
16S4 SGW GTP-U interface
17S4 SGSN GTP-C interface
18S16 SGSN GTP-C interface
19eNodeB GTP-U interface for DL data forwarding
20eNodeB GTP-U interface for UL data forwarding
21RNC GTP-U interface for data forwarding
22SGSN GTP-U interface for data forwarding
23SGW GTP-U interface for DL data forwarding
24Sm MBMS GW GTP-C interface
25Sn MBMS GW GTP-C interface
26Sm MME GTP-C interface
27Sn SGSN GTP-C interface
28SGW GTP-U interface for UL data forwarding
29Sn SGSN GTP-U interface
30S2b ePDG GTP-C interface
31S2b-U ePDG GTP-U interface
32S2b PGW GTP-C interface
33S2b-U PGW GTP-U interface

I also found how this data is encoded on the wire is a bit strange,

In the example above the Interface Type is 7,

This is encoded in binary which give us 111.

This is then padded to 6 bits to give us 000111.

This is prefixed by two additional bits the first denotes if IPv4 address is present, the second bit is for if IPv6 address is present.

Bit 1Bit 2Bit 3-6
IPv4 Address Present IPv4 Address PresentInterface Type
11 000111

This is then encoded to hex to give us 87

Here’s my Python example;

interface_type = int(7)
interface_type = "{0:b}".format(interface_type).zfill(6)   #Produce binary bits
ipv4ipv6 = "10" #IPv4 only
interface_type = ipv4ipv6 + interface_type #concatenate the two
interface_type  = format(int(str(interface_type), 2),"x") #convert to hex

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.

Kamailio Bytes – Nightly Releases the Lazy Way

Installing from source can be a headache,

If you’re running a Debian system, the Kamailio team provide nightly development builds as Debian packages that can be installed on Debian or Ubuntu systems using the apt package manager.

Installing is a breeze, first we just add the GPG key for the repo:

 wget -O- http://deb.kamailio.org/kamailiodebkey.gpg | sudo apt-key add - 

Then it’s just a matter of adding the release to your /etc/apt/sources.list file.

I’m running Bionic, so I’ll add:

deb     http://deb.kamailio.org/kamailiodev-nightly bionic main
deb-src http://deb.kamailio.org/kamailiodev-nightly bionic main 

Then just update and install the packages you require:

apt-get update
apt-get install kamailio*

For a full list of the Debian packages published check out the Debian package list:

https://deb.kamailio.org/

Where you can find the nightly builds and stable builds for each of the releases.

Enjoy!

Kamailio Bytes – Ansible for Automating Deployments

Despite the fact it’s 2020 there’s still a lot of folks in the world manually configuring boxes,

Ansible is a topic I could talk all day about, but in essence it’s kind of an automation framework, tell Ansible what to do one and it can spin you up two boxes, or two thousand boxes and manage the config on them.

I talked about DMQ, the Distributed Message Queue in a Kamailio Bytes post some time ago, and so as an example I’ll share an example playbook to Install Kamailio the lazy way from the Repos, and load the DMQ config with the IP Address and DMQ Address pulled from variables based on the host itself.

There’s a huge number of posts on installing and the basics of Ansible online, if you’re not familiar with Ansible already I’d suggest starting by learning the basics and then rejoining us.

The Hosts

Depending on if your hosts are running on bare metal, VMWare VMs or cloud based, I’m going to assume you’re working with a Debian system.

I’ve already got 3 servers ready to go, they’ve got sequential IP Addresses so I’ve added the range to my /etc/ansible/hosts file:

I’ve created the group kamailio and put the IP Address range 10.0.1.193 to 10.0.1.195 in there.

You will probably need to add the authentication info, such as passwords, private keys and privilege escalation details, but I’m going to assume you’ve already done that and you can run the ping module on each one:

ansible kamailio -m ping

Assuming that comes back OK and you can get into each one let’s move onto the Playbook.

The Playbook

There’s a few tasks we’ll get out of the way before we configure Kamailio,

The first of which is adding the Debian repo and the keys,

Next we’ll load a Kamailio config from a template that fills in our IP Address and Kamailio version, then we’ll install Kamailio,

Rather than talk you through each of the plays here’s a copy of my playbook:

---
- name: Configure Kamailio


  hosts: kamailio
  become: yes

  vars:
    kamailio_version: "53"
    debian_sources_dir: "/etc/apt/sources.list.d"

  tasks:



    - name: Add keys for Kamailio repo
      apt_key:
        url: http://deb.kamailio.org/kamailiodebkey.gpg
        state: present


    - name: Add repo to sources.list
      apt_repository:
        repo: deb http://deb.kamailio.org/kamailio{{kamailio_version}} {{hostvars[inventory_hostname]['ansible_lsb']['codename']}} main
                #The full list of Debian repos can be found at http://deb.kamailio.org/
                #The version is based off the versions listed there and the release is based on the codename of the Debian / Ubuntu release.
        state: present


    - name: Copy Config Template
                #Copies config from the template, fills in variables and uplaods to the server
      template:
        src: kamailio.cfg.j2
        dest: /etc/kamailio/kamailio.cfg
        owner: root
        group: root
        backup: yes
      register: config_changed

    - name: Install Kamailio
                #Updates cache (apt-get update) and then installs Kamailio
      apt:
        name: kamailio
        update_cache: yes
        state: present
      register: kamailio_installed_firstrun

    - name: Restart Kamailio if config changed
      service:
       name: kamailio
       state: restarted
      when: config_changed.changed

    - name: Start Kamailio if installed for the first time
      service:
       name: kamailio
       state: started
      when: kamailio_installed_firstrun.changed

Should be pretty straight forward to anyone who’s used Ansible before, but the real magic happens in the template module. Let’s take a look;

Kamailio config in Jinja2 template

Pusing out static config is one thing, but things like IP Addresses, FQDNs and SSL certs may differ from machine to machine, so instead of just pushing one config, I’ve created a config and added some variables in Jinja2 format to the config, that will be filled with the value on the target when pushed out.

In the template module of the playbook you can see I’ve specified the file kamailio.cfg.j2 this is just a regular Kamailio config file but I’ve added some variables, let’s look at how that’s done.

On the machine 10.0.1.194 we want it to listen on 10.0.1.194, we could put list 0.0.0.0 but this can lead to security concerns, so instead let’s specify the IP in the Jinja config,

listen=udp:{{ ansible_default_ipv4.address }}:5060
listen=tcp:{{ ansible_default_ipv4.address }}:5060
listen=udp:{{ ansible_default_ipv4.address }}:5090

By putting ansible_default_ipv4.address in two sets of curly brackets, this tells Ansible to fill in thes values from the template with the Ansible IPv4 Address of the target machine.

Let’s take a look on the 10.0.1.194’s actual kamailio.cfg file:

Let’s take another example,

To keep DMQ humming it makes sense to have different DMQ domains for different versions of Kamailio, so in the Kamailio config file template I’ve called the variable kamailio_version in the DMQ address,

This means on a Kamailio 5.2 version this URL look like this on the boxes’ config:

# ---- dmq params ----
modparam("dmq", "server_address", "sip:10.0.1.194:5090")
modparam("dmq", "notification_address", "sip:dmq-53.nickvsnetworking.com:5090")

Running It

Running it is just a simple matter of calling ansible-playbook and pointing it at the playbook we created, here’s how it looks setting up the 3 hosts from their vanilla state:

The great thing about Kamailio is it’s omnipotent – This means it will detect if it needs to do each of the tasks specified in the playbook.

So if we run this again it won’t try and add the repo, GPG keys, install Kamailio and load the template, it’ll look and see each of those steps have already been done and skip each of them.

But what if someone makes some local changes on one of the boxes, let’s look at what happens:

Likewise now if we decide to change our config we only need to update the template file and Ansible will push it out to all our machines, I’ve added a comment into the Kamailio template, so let’s run it again and see the config pushed out to all the Kamailio instances and them restarting.

Hopefully this gives you a bit more of an idea of how to manage a large number of Kamailio instances at scale, as always I’ve put my running code on GitHub, Ansible Playbook (configure_kamailio.yml) and Kamailio Jinja config template (kamailio.cfg.j2)

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.