The Corelatus Blog
E1/T1 and SDH/SONET telecommunications
About Matt Lang

I'm one of the founders at Corelatus. Our niche is telecom hardware for the E1/T1 and SDH/SONET interfaces. I work with layer 2 signalling, i.e SS7 MTP-2, ATM and frame relay, and the Erlang control system for our embedded hardware.

You can reach me at

Getting hex dumps of E1/T1 timeslots
Posted 7th May 2014

This post looks at a few simple tricks for understanding the WWW browser-based hex dump from a typical E1/T1 and then moves on to some more complicated things you can do with timeslot data by piping it to the standard (from BSD, but now on most unix-like systems) hexdump tool.

Browser-based hex dumps

The HTTP server built into GTH (and STH 3.0) lets you click your way to a timeslot-by-timeslot hexdump of an E1/T1. It shows 8ms of data, which gives you a rough idea of what's happening on an E1/T1:

timeslot-by-timeslot hex dump, from L1/PCM1A/hexdump

The HTTP server is on port 8888. To get to the hex dump, click 'L1' (at the top), then the E1/T1 you're interested in, for instance pcm4A, then 'hex dump' (at the bottom).

In the screenshot, timeslot 0 has a repeating two-octet pattern typical of an E1 link using doubleframe.

Timeslots 1 and 2 have the default idle pattern for E1 links: hex 54. That's silence, so those timeslots are most likely unused for the moment.

Timeslot 3 has nonstop 3f 3f 3f 3f. Writing that out in binary, 00111111001111110011111100111111, lets you see the pattern of six ones with a zero on either side. That's a flag. ISDN LAPD and Frame Relay links transmit flags between packets. Timeslot 3 probably contains LAPD signalling. There are eight possible bit rotations of the flag: 7e, e7, fc, cf, 9f, f9, 3f, f3.

Timeslot 4 has a repeating six-octet pattern. That's an MTP-2 FISU. Timeslot 4 is almost certainly running MTP-2.

Command-line hex dumps

This section assumes you're comfortable compiling C programs and using a Unix-like operating system, probably one of the BSDs or Linux. (You can do the same thing using Python, if you prefer, there's sample code for that too.) has some C sample code. It's also on github. Using that code, you can record a timeslot to a file:

./record 4A 1 /tmp/recording.raw

Let it run for 10 or 20 seconds, then stop it with ^C. You can now look at the data using the standard BSD tool 'hexdump' (on Debian, it's in the 'bsdmainutils' package):

tmp >hexdump -C recording.raw  | head -2
00000000  72 f9 d8 76 e5 df d6 fd  dc 5d ff f5 f0 94 57 da
00000010  eb 6e fa e4 7d 90 e4 e0  91 e2 eb ea ed e4 e3 e4

There's no limit to how large such recordings can be, so if you're debugging something, you can leave the recorder running for hours.

Live hex dumps

The 'record.c' sample code can also write to standard output. You can use that to get a live view of a timeslot via hexdump:

c >./record 3A 3 - 2>/dev/null | hexdump -C
00000000  3f 3f 3f 3f 3f 3f 3f 3f  3f 3f 3f 3f 3f 3f 3f 3f

The example above demonstrates a neat feature in 'hexdump': 'hexdump' suppresses repeated data. So you can leave it running in a window and it'll only produce output when something changes.

Permalink | c, gth, telecom-signalling.
save_to_pcap now supports PCap-NG for Wireshark
Posted 15th December 2013

Problem: you want to sniff packets from many SS7 signalling timeslots on many E1/T1s at the same time and analyse them with Wireshark.

Until now, the only way to know which packet came from where was to look at the DPC/OPC. Why? Because the PCap file format, used by Wireshark, tcpdump and many other tools, doesn't have any way to keep track of which interface a packet came from.

Solution: PCap-NG is a completely new file format which lets you keep track of which interface a packet came from. Wireshark understands PCap-NG (and, of course, classic PCap).


The C sample code from Corelatus includes a program called 'save_to_pcap' which takes SS7 packets from Corelatus E1/T1 and SDH/SONET hardware and translates them to PCap so that Wireshark can read them.

'save_to_pcap' now saves to PCap-NG by default. Wireshark 10.8 (released in June 2012) reads and writes PCap-NG by default. Here's how to capture packets from 8 signalling channels at the same time:

  $ ./save_to_pcap -n 8 1A 1B 2A 2B 16 2 load_generator.pcapng
  monitoring 1A:16
  monitoring 1B:16
  monitoring 2A:16
  monitoring 2B:16
  monitoring 1A:2
  monitoring 1B:2
  monitoring 2A:2
  monitoring 2B:2
  capturing packets, press ^C to abort
  saving to file load_generator.pcapng.1
  saving to file load_generator.pcapng.2

I used '-n 8' to force the capture file to rotate after 8 packets. That gives us clean, closed file to look at. Here's what it looks like in wireshark:

screen capture of the wireshark main application window

I've drawn yellow ellipses around the new parts. To get the "Interface ID" column:

  1. right-click on one of the existing column headings
  2. select "Column Preferences"
  3. select "Field type: Custom" and "Field name: frame.interface_id"
  4. Add
  5. OK

There's actually more information in the PCap-NG file. Wireshark shows it in the Statistics/Summary menu. This shows you the full interface names, i.e. "interface 1" is actually E1/T1 port 1B, timeslot 16 and you can also see exactly which GTH the capture came from.

screen capture of the wireshark statistics dialog

You can also use 'frame.interface_id' in filter expressions.

PCap-NG is a nice format

The PCap-NG format is nicely designed, much better than the original PCap format. Had PCap-NG been around 13 years ago, we probably would have made GTH output traces directly in this format. It's flexible enough to let us include all sorts of information, e.g. we could even add layer 1 status changes on a separate "interface".

Wireshark limitations

PCap-NG is still relatively new in Wireshark, so there are few things that will probably improve with time. The ones I noticed are:

  • The 'if_name' field, i.e. the descriptive name for an interface, isn't available in the packet detail pane in the Wireshark gui. That means you just have to know that "interface 3" corresponds to, say, 4A:16. This was briefly discussed on the Wireshark mailing list, it looks like it'll eventually be added.
  • It looks like live captures in Pcap-NG format don't work. Wireshark just says "unknown format". You can work around that by reverting to the classic format, using the -c switch on save_to_pcap.

Getting the code

The C sample code is here and also on github.

Permalink | gth, telecom-signalling, wireshark.
Dimensioning SDH/SONET monitoring
Posted 27th February 2013

This post is an example of how to figure out how much hardware you need to monitor all the Abis signalling at a GSM site which uses STM-1 to transport E1 lines.

The site

The site we're looking at, a BSC, has STM-1 connections to three BTSs: S1, S2 and S3. They have 51, 18 and 33 E1s on them. Here's how we set it up:

connecting three STM-1 optical links to four STH sub-modules

The yellow lines at the top are the original 155Mbit/s STM-1 links going from the BSC to each BTS. There's one fiber for each direction.

The green lines are the output of the fiber taps. They're an optical copy (typically taking 10% of the light) of the original STM-1. Each link has two taps, one for each direction. For this example, we'll assume that the application needs the signalling from both directions.

The blue lines are fibers going from one SDH/SONET probe's sub-module to another. That lets us shift processing load from one sub-module to another.

Ports and modules on Corelatus' SDH/SONET probe

There are two models of the SDH/SONET probe.

The lower one in the diagram is the low-end model: it has one sub-module. A sub-module has two SFP sockets and two ethernet ports. The SFP sockets accept SFP modules which connect to the optical lines (green).

The upper one in the diagram is the full model: it has three sub-modules.

Layer 2 (LAPD) signalling

For this example, I've assumed that we're interested in 10 timeslots of LAPD signalling on each E1. That's the worst-case in normal GSM installations. In practice, there can be fewer signalling timeslots, either because BTSs are daisy-chained or because the BTS doesn't have the maximum number of radio transceivers.

Each submodule can decode 640 LAPD channels. Link S1 has 51 x 2 x 10 = 1020 LAPD channels, so one submodule can't process them all. We handle that by running a fiber to the next sub-module. That way, 51 + 13 E1s are handled on the first submodule and the remaining 38 on the next sub-module.


On 3G networks, the signalling is often carried as ATM, either directly on STM-1 (or OC-3) or inside E1 lines carried on STM-1. The principles for dimensioning are the same, but the numbers are different.

Permalink | gth, sdh and sonet, telecom-signalling.
Configuring SDH/SONET
Posted 30th January 2013

Today, I'm going to walk through setting up a Corelatus SDH/SONET probe to look at E1/T1 lines carried on 155 Mbit/s SDH/SONET.

A quick look at SDH/SONET

Wikipedia has a good article about SDH/SONET. SONET is the standard mostly used in North America, SDH is the one mostly used in the rest of the world. The differences between the two are minor, for Corelatus' hardware it's just a setting in software.

SDH/SONET can both be used to carry various types of data. This time around, we'll just look at the E1/T1 lines. Here's a diagram of the scheme SONET uses to pack many E1/T1 lines into one 155 Mbit/s line, usually an optical fiber:

SONET multiplexing trees

SONET calls the 155 Mbit/s line an "OC-3" (on the left of the diagram). SONET then has three layers of packing lines together (STS-1, VT, VT2), and that allows it to fit 3 x 7 x 3 = 63 E1 lines or 3 x 7 x 4 = 84 T1 lines.

SDH uses different names for similar ideas. The end result is the same: it carries 63 E1s or 84 T1s:

SDH multiplexing trees

To work with E1/T1 lines on a Corelatus SDH/SONETs Monitoring Probe, we need to do three things:

  1. Turn on (enable) SDH/SONET at the top level
  2. Obtain (map) a name for a particular E1/T1 line
  3. Turn on (enable) layer 1 processing on the E1/T1 line

Setting up SDH/SONET using the API

Same as all other Corelatus probes, the SDH/SONET probe responds to text commands sent over TCP port 2089. To support SDH/SONET, we added two new comands enable and map.

First, enable SDH/SONET at the top level, in SONET mode for this example:

  C: <enable name='sdh1'><attribute name='SONET' value='true'/></enable>
  G: <ok/>

Once sdh1 is enabled, you can walk through the containers carried on it using the query command. But we'll skip right to mapping one of the E1 links:

    C: <map target_type='pcm_source'>
          <sdh_source name='sdh1:hop2:lop7_4'/></map>
    G: <pcm_source name='pcm60'/>

Now that we know what name the E1 has (pcm60 in this example), we can enable L1 using the same command you'd use on a probe with an electrical E1:

    C: <enable name='pcm60'><attribute name='mode' value='T1'/></enable>
    G: <ok/>

After these three commands, you have an T1 which is ready to use just like a T1 on other Corelatus hardware, i.e. you can start layer 2 decoding on it, or copy out the data or...

The API manual goes into detail, with more examples, including how to use disable and unmap.

Setting up SDH/SONET using the CLI

The SDH/SONET probe has a command-line interface. It's useful for experimenting and exploring. Here's how to set up things the same as above:

    $ ssh cli@
    cli@'s password: (mail
    GTH CLI, press Enter to start (^D to exit)

    GTH CLI started. 'help' lists commands
    gth> enable sdh1 SONET true
    gth> map sdh1:hop2:lop7_4
    Mapped to: pcm60
    gth> enable pcm60 mode T1

Setting up SDH/SONET using Python

Corelatus has sample code in a few languages, including Python, which shows how to use the API to set up an SDH link just like above. Here's the python demo program doing the same thing:

    $ ./ enable sdh1 SONET true
    $ ./ map sdh1:hop2:lop7_4
    $ ./ enable pcm60 mode T1

The C and Erlang sample code (on the same page) provides the same functionality, but with slightly different syntax.

Permalink | gth, python, sdh and sonet.
Retroactive debugging: recording signalling for later analysis
Posted 6th December 2012

This post is about saving signalling data for later analysis.

Problem: a subscriber reports that it wasn't possible to call a particular handset two hours ago. Connecting a signalling analyzer now won't help, so the best you can try to do is to reproduce the problem with test calls.

An alternative approach is to save all signalling data for a few days. Then, when you get a trouble report, examine the signalling affecting the subscriber's handset around the time the problem actually happened.

Disk space

Signalling on a single timeslot of an E1 cannot possibly be more than 8 kByte per second in each direction. There are 86400 seconds in a day and you usually save both directions of the link, so there's never going to be more than 140 MByte of data per day per ordinary link, uncompressed.

Real-world links aren't going to be anywhere near that busy. It's more likely that they're only carrying a few megabytes of signalling per day.

Even on high-speed-signalling-links at 1980 kbit/s, it's never going to be more than 5 GByte per day.

Sample code

The C sample code has an example which saves signalling from as many links as you want to a file in PCAP format.

 ./save_to_pcap -m -n 1000 1A 2A 16 captured_packets.pcap

'-m' tells the GTH that the incoming signal is attenuated by 20dB
'-n 1000' means that you want a new '.pcap' file after every 1000 packets; the files automatically get a .1, .2, .3, ... suffix.
'1A 2A' tells the GTH that you want packets captured on the E1/T1 interfaces called 1A and 2A. '16' tells the GTH that you want timeslot 16

The PCAP files can then be opened by wireshark, here's an example of doing that.

Permalink | gth, telecom-signalling.
Upgrading Firmware on GTH
Posted 31st May 2012

This note is about two ways to upgrade the firmware on Corelatus hardware. For one-offs, you can use a browser-based upgrade. For large installations, it's easier and much faster to use a program which talks to the GTH's API on port 2089.

A Few Principles

All GTH hardware Corelatus has ever shipped (at the time of writing: GTH 1.x, GTH 2.x and the RAN probe) uses the same principles and methods for upgrading the firmware.

GTH has two completely separate firmware images. That way, things like a power failure in the middle of an upgrade, or a corrupt firmware image won't leave you with a 'bricked' system.

The two firmware images on the GTH are called system and failsafe. During everyday operation, the system image is used, it supports the full set of GTH commands. In exceptional circumstances, for instance while upgrading, the failsafe image is used. The failsafe image provides just enough functionality to support upgrading and troubleshooting.

The In-Browser Approach

  1. Get a firmware image from (mail to get a password)
  2. point your browser at the on-board serivce page:
  3. Select the 'Install firmware' link and follow instructions. The whole upgrade process will take two or three minutes.

catch-22: You can't use the browser-based upgrade systems running firmware from before May 2010, you need at least gth2_failsafe_10 and gth2_system_34a.

The API/Program Approach

For installations with tens or hundreds of modules, upgrading via a webbrowser is a waste of time. It's better to use the GTH's API on port 2089.

The quickest way to get started with that is to use Corelatus' sample code. We have working examples in

C: install_release.c, included in the C sample code.

Java: gth_upgrade.jar, included in the Java sample code.

What the API/Program Upgrade is Doing

The API-driven upgrade checks what firmware version is running, reboots the system to the 'failsafe' firmware, installs the release and checks that the installation succeeded. Breaking that down into steps:

Step 1: Check which image is running

Do that by issuing a query command

    <resource name="system_image"/>

The GTH replies with something like:

    <resource name="system_image">
    <attribute name="version" value="development_release_9"/>
    <attribute name="locked" value="false"/>
    <attribute name="busy" value="true"/>

You can tell that the GTH is running the system_image because busy=true. There are three circumstances under which the failsafe image may be booted:

  • The system image is damaged, for instance as result of a failed upgrade.
  • The GTH has attempted more than 5 consecutive boots without successfully booting. "Successfully booting" is defined as completing the boot process, starting the API and staying up for four minutes.
  • The boot mode has been manually set to failsafe before the most recent boot.

Step 2: Boot the failsafe image

Images are not upgraded in-place, so if we want to upgrade the system image, we need to boot failsafe first. Do that by setting the failsafe boot mode

    <set name="os">
    <attribute name="boot mode" value="failsafe"/>

and then sending a reset command:

    <resource name="cpu"/>

Step 3: Unlock the System Image

To make it a bit harder to accidentally upgrade a system, you have to 'unlock' an image before upgrading it:

  <set name="system_image">
  <attribute name="locked" value="false"/>

Step 4: Install the new release

Upgrades to the image are standard compressed 'tar' archives.

Install them using the install command:

    <install name="system_image"/>

The actual archive is sent immediately following the install command, in a block with content type 'binary/filesystem'.

The GTH sends an ok response after the filesystem has finished transferring. The actual install process continues after the transfer, an event is sent when it completes:

    <event><info reason="install_done"/></event>

If you reboot without waiting for the install_done message, you'll probably get a corrupted installation. Start again from the top.

Step 5: Verify that the install completed

A query of the system image, as shown above, reveals the version number of the installed image. An install which has not completed shows up as empty.

Finally, we reboot again. If the system fails to boot after five attempts, the failsafe system starts.

Upgrading the Failsafe System

Upgrading the failsafe image is analogous to upgrading the system image. A special failsafe firmware release file is used.

Permalink | gth.
Decoding signalling on the Abis interface with Wireshark
Posted 25th February 2012

Wireshark can decode the packets on GSM Abis links (and, probably also UMTS Iub). To do it, you need to first capture the data using a GTH, which is basically the same process as described in this entry about capturing data from the Gb interface, except for one difference: you want to capture LAPD, not Frame Relay:

  save_to_pcap:lapd("", "3A", 1, "abis.pcap").

Wireshark can open the file 'as is', but it only decodes up to L2. To get wireshark to decode everything, go to Edit/Preferences/Protocols/LAPD and then tick the "Use GSM SAPI values" checkbox. Presto, you get the RSL (Radio Signalling Link) protocol decoded for you. Nice.


The signalling protocol on GSM Abis links is specified in ETSI TS 100 595. ETSI specs are freely available from ETSI, you just have to register with an email address.

Permalink | gth, telecom-signalling.
Controlling a GTH from Amazon EC2
Posted 1st October 2011

This post is about what I did to get the GTH example code (the C version) to run on Amazon EC2, controlling a GTH on the public internet.

Amazon EC2 is a service where you rent a virtual server by the second. The virtual server can run Windows or Linux. Our in-house environment is all-linux, so we use EC2 to give us an on-demand Windows environment to check example code on.


  1. Connect a GTH to the Internet
  2. Create and set up an Amazon EC2 Instance running Windows Server
  3. Install the example code
  4. Open up holes in the firewall

Taking each step in detail:

Connect a GTH to the Internet

First off: spend a moment thinking about security. You probably don't want SS7 traffic from a live network going over the internet. The GTH is designed to be installed in a secure network, not directly facing the internet. Take care.

Most likely, you've got the GTH behind a firewall. The firewall needs to be configured so that TCP port 2089 gets to the GTH.

The GTH needs to be told where the firewall is. In my case, it's at

query_set eth1 "default gateway"

Create an Amazon EC2 Instance

I'm assuming you're familiar with Amazon EC2. It's your choice which OS you want to run at Amazon. In my case, I wanted Windows because our primary development environment is linux, so I like the convenience of starting up an Amazon machine when I need to do something with Windows.

Install the example code

An easy way to do that is to use a WWW browser on EC2 to download the example code from

On Amazon windows instances, an alternative is to use a remote share via terminal server. Still another is to install 'putty' and use 'psftp' to get the code from your own server.

Now you can try out the install:

> query_set board
LED mode=normal
PCM LED assignment=universal
voice coding=alaw
ROM ID=0x26cbce800000005f
power consumption=6.4
power source=A
POE source=

'query_set' only uses the port 2089 API socket. But some of the other examples do more complicated things with TCP. 'playback_file', 'record' and 'save_to_pcap' all open TCP connections back to EC2 to send data.

Relaxing firewall rules

There are three places which are likely to block TCP connections originating at the GTH. You need to make sure each of those lets them through.

  1. The firewall between the GTH and the internet needs to allow the GTH to make outbound TCP connections. On most small firewall setups, that's the default. Some firewalls block outbound connections entirely, in which case you're out of luck (unless you can tunnel TCP through something else, such as SSH or an HTTP/1.1 proxy, but even that will require modifying the example code to use known port numbers).
  2. The firewall in EC2 Windows "security groups" needs to allow inbound TCP connections on all ports, as long as the originate from the GTH. Use the "Security Group" screen of the AWS console, in the "inbound" tab create a new rule. Use the internet-facing IP address the GTH has as the "source" IP. That way only the GTH can connect to your EC2 instance.
  3. Windows' own firewall needs to allow inbound TCP connections. Either make a program-specific exception, or disable the windows firewall entirely (and just rely on EC2's firewall).
           netsh firewall add allowedprogram c:\users\mml\downloads\save_to_pcap.exe mode=ENABLE

All done

Now you can run use pretty much anything in the GTH API from a machine in Amazon's cloud.

Permalink | c, gth, windows.
Live Wireshark Captures on Windows
Posted 15th June 2011

Update 23. January 2014: the -c switch is needed to force save_to_pcap to emit data in classic Pcap format, instead of Pcap-NG. Wireshark 1.10.5 doesn't allow Pcap-NG in pipes.

I've written about using Wireshark to look at signalling captured from an E1/T1 before. As of a few days ago, it's possible to do live captures on Windows, like this:

 save_to_pcap -c 1A 2A 16 \\.\pipe\ss7.1
 wireshark -k -i \\.\pipe\ss7.1

When you're capturing live, the SS7 packets appear in Wireshark in real-time.

The new version of 'save_to_pcap' program is part of the C examples, the .zip file contains both source (compiles on Unix and Windows) and .exe files.

It's always been possible to do live captures on Unix, you just pipe stdout:

./save_to_pcap -c gth21 1A 2A 16 - | wireshark -k -i -
Permalink | c, gth, telecom-signalling, windows.
Porting C Socket Code to Windows
Posted 16th June 2010

I just ported the C example code for controlling Corelatus E1/T1 hardware to Visual Studio 2010. Doing that was easier than expected. This first part of this post is about how to use that code. The second part is a bit of history about what I had to do to port the code.

Obtaining Visual Studio

Microsoft have their C/C++ compiler available for free download on

The install process is pretty standard for windows, if you're accustomed to Microsoft products, there are no surprises here.

Compiling the code

  1. Start the "Visual Studio Command Prompt". By default, that's in "Start/Programs/Microsoft Visual Studio 2010 Express".
  2. Navigate to the directory you unpacked the sample code in. Remove any old .exe files with 'del *.exe'.
  3. Run 'nmake /f NMakefile'. After a couple of seconds, you have six brand-new .exe files. Here's what this step looks like:

screenshot of a VC compile If you just wanted to see how to compile the code, you can stop reading now. The rest of the post is just about what I had to do to make this work with Microsoft's compiler.

Hurdle #1: MS nmake vs gnumake vs Visual Studio projects

Conclusion: I ended up using Microsoft's nmake and wrote a separate NMakefile for it.

My code gets built by 'make', through a Makefile. Visual Studio doesn't include a make program which can understand normal Makefiles, leaving me a choice of either using the Visual Studio IDE or using Microsoft's nmake.

I spent half an hour trying the IDE route. When you make a new project, you have to choose what sort it is. "CLR Console Application" seemed closest to what I wanted to do, so I chose that. Maybe "Makefile Project" would have worked better. Things got increasingly confusing from there. What's a "solution?" Why are "precompiled headers" enabled? I just want to compile a few hundred lines of C. At that point, I gave up, the IDE is clearly not the easiest way for me to get started.

Next, I tried Microsoft's replacement for make, called nmake. That didn't start flawlessly either, nmake uses its own syntax. Initially, I thought I might be able to write one Makefile which both gnumake and nmake understand. But Microsoft's syntax seems to be too different to allow that. Oh well. The NMakefile is just a dozen lines or so.

Hurdle #2: Missing header files

Conclusion: I used #ifdefs to include different header files when compiling in a win32 environment.

Visual Studio doesn't provide some of the header files the code uses, e.g. there's no 'unistd.h' and no 'sys/socket.h'. Odd. Providing most of unistd.h shouldn't be too hard.

The point of this exercise is to get the code to compile using a Microsoft toolchain, so I didn't investigate using cygwin or mingw.

In the end, it turns out that I wasn't using anything from 'unistd.h' which didn't get provided by something else in win32. 'socket.h' (and friends) was a bit harder, but it turns out that's provided by 'winsock2.h'.

Hurdle #3: 'Mostly compatible' socket API

Conclusion: winsock2 is different to the BSD socket API, but the differences can be worked around with just a few changes.

Sockets were invented on unix, so I assumed that other OSes would just copy the interface. But no, not on win32. There seem to be at least two attempts to make a socket API for windows, called 'winsock' and 'winsock2'. The first seems to be deprecated. The main problems I hit were:

  • You can't use close() on a socket, you have to use closesocket()
  • You can't use read() on a socket, you have to use recv()
  • errno doesn't report socket errors, you have to call a special Windows Socket error reporting function.

None of those problems are hard to get around.

Hurdle #4: GNU and Microsoft can't agree on function names

Both GNU and Microsoft have replacements for some functions which are prone to security problems. If you use plain strcat() or strcpy, visual studio warns about security problems.

Disabling the warnings would be quick but feels like cheating. So I looked at the 'less insecure' alternatives. GNU call them strncat and strncpy whereas Microsoft call them strcat_s and strcpy_s. And the arguments are the same way around. It would have been nice if GNU and MS had solved this the same way, but no. Minor annoyance.

Hurdle #5: Structure packing pragmas

Conclusion: Both MS and GNU understand the #pragma pack(N) syntax, but only GNU understands the __attribute__((__packed__)) syntax.

A couple of the examples (save_to_pcap.c and record.c) use structures to encode or decode data defined by an external format. For instance, here's what the wire format of a 'pcap' protocol capture file looks like:

      typedef struct {
        unsigned int magic;
        unsigned short major_version;
        unsigned short minor_version;
        unsigned int GMT_to_localtime;
        unsigned int sigfigs;
        unsigned int snaplen;
        unsigned int network;
      } PCAP_global_header;


We have to tell the compiler that spacing out the fields to get a certain alignment isn't allowed. The recommended GNU way to do that is to add __attribute__((__packed__)) after the structure. The recommended Microsoft way is to add #pragma pack(1) somewhere before the structure, and then remember to change the packing back again before hitting any code which is sensitive to performance.

Since this is just example code, we don't care about that last bit of performance, so we can just leave the packing at 1.

Aside: the structure above is a bit sloppy because just quietly assumes that an int is 32 bits and a short 16.

Hurdle #6: Windows firewall

Some of the example code opens TCP sockets for listening. By default, the windows firewall doesn't allow that. Just click "fine, ok, let me do this".

Unfortunately, that looks ugly for the user---the program will fail the first time you run it, and the pop-up box says "publisher unknown". Does anyone know how to improve on this? It'd be nice if the user could approve the publisher, i.e. me, once, and then every program works.

How long did the porting take?

A couple of days, including getting Visual Studio installed. That's for a few thousand lines of code with a bit of socket IO and file IO.

If I had to do it again, it'd take an afternoon or so.

Permalink | c, gth, windows.
Decoding UMTS (3G) Interfaces with Wireshark
Posted 24th May 2010

Many 3G networks use ATM on their internal interfaces, e.g. on the Iub and Iu-PS interfaces. Those interfaces carry both control information (radio environment information, attach/detach messages, location updates) and also subscriber data, for instance IP traffic.

Wireshark understands how to decode those ATM interfaces. Here's an example of an interface sniffed by a GTH. The interface was carrying IP traffic over ATM on an E1 line.

wireshark screenshot

How to tell the GTH to capture an ATM link

To look at a 3G network like this, you need to:

  1. Connect one of the GTH's E1 interfaces to the E1 (or T1) interface carrying the ATM interface. You typically do that at a cross connect panel, using a G.772 monitor point.
  2. Enable the E1 interface you connected.
  3. Tell the GTH to start decoding ATM AAL5 (and/or AAL2) on that interface
  4. Convert the captured data to the file format which wireshark understands, libpcap.
  5. Open the captured file in wireshark. (It's also possible to pipe the captured data into wireshark live, both on Windows and Unix-like OSs).

Taking those steps one at a time, starting with #2:

Enable the E1 interface

  <set name='pcm3A'><attribute name='monitoring' value='true'/></set>

Tell the GTH to start decoding ATM AAL5

IP traffic on ATM is always carried in AAL5. The timeslot arrangement is usually 1--15 + 17--31. A few sites share the E1 with other protocols, this is called fractional ATM. The GTH can handle either scheme.

    <atm_aal5_monitor ip_addr='' ip_port='1234' vpi='0' vci='5'>
      <pcm_source span='3A' timeslot='1'/>
      <pcm_source span='3A' timeslot='2'/>
      <pcm_source span='3A' timeslot='3'/>
      <pcm_source span='3A' timeslot='15'/>
      <pcm_source span='3A' timeslot='17'/>
      <pcm_source span='3A' timeslot='31'/>

In this example, the VPI/VCI is 0/5. If you know the VPI/VCI in advance, great. If you don't, the GTH can sniff traffic at the AAL0 interface and show you which VPI/VCI are active on the link.

Convert the captured data

GTH sends out data in a format described in the API manual. Wireshark wants the data to be in libpcap format. save_to_pcap.erl, in the sample Erlang code for GTH can do the conversion, like this:

  save_to_pcap:from_file("/tmp/captured.raw", "/tmp/captured.pcap").

A lazier approach is to let save_to_pcap.erl configure the GTH and start the capture:

  save_to_pcap:aal5("", "3A", lists:seq(1,15) ++ lists:seq(17,31),
  {0,5}, "aal5.pcap").

The C version of save_to_pcap can currently only convert MTP-2, not AAL5. If you want it extended, send mail (address at top right).

Start up wireshark

Recent versions of Wireshark, e.g. 1.2.7, can decode such capture files out of the box, without any configuration. Finished.

Permalink | gth, telecom-signalling.
Decoding the Gb Interface with Wireshark
Posted 31st March 2010

The Gb interface is part of the packet radio data network (GPRS) in GSM, it sits between the BSC and the SGSN and carries subscriber data headed to and from the internet.

Wireshark understands how to decode the Gb interface, so you can use wireshark to look through data sniffed from a Gb interface by a Corelatus GTH. Here's what it looks like:

wireshark screenshot

How to tell the GTH to capture Gb

To look at a GPRS network like this, you need to do a few things:

  1. Connect one of the GTH's E1 interfaces to the E1 (or T1) interface carrying the Gb interfaces. You typically do that at a cross connect panel, using a G.772 monitor point.
  2. Enable the E1 interface you connected.
  3. Tell the GTH to start decoding frame relay on that interface
  4. Convert the captured data to the file format which wireshark understands, libpcap.
  5. Open the captured file in wireshark. (On unix-like operating systems, including OSX, you can also pipe into wireshark to get a live view of the capture.)

Taking those steps one at a time, starting with #2:

Enable the E1 interface

  <set name='pcm3A'><attribute name='monitoring' value='true'/></set>

Tell the GTH to start decoding frame relay

The Gb interface uses frame relay on E1. Different sites use different configurations of timeslots. One common setup is to use timeslots 1--15. Another common setup is to 1--15 + 17--31. The GTH can handle any setup.

    <fr_monitor ip_addr='' ip_port='1234'>
      <pcm_source span='3A' timeslot='1'/>
      <pcm_source span='3A' timeslot='2'/>
      <pcm_source span='3A' timeslot='3'/>
      <pcm_source span='3A' timeslot='15'/>

Convert the captured data

GTH sends out data in a format described in the API manual. Wireshark wants the data to be in libpcap format. save_to_pcap.erl, in the sample Erlang code for GTH can do the conversion, like this:

  save_to_pcap:from_file("/tmp/captured.raw", "/tmp/captured.pcap").

A lazier approach is to let save_to_pcap.erl configure the GTH and start the capture:

  save_to_pcap:frame_relay("", "3A", lists:seq(1,15), "gprs.pcap").

The C version of save_to_pcap can currently only convert MTP-2, not frame relay. If you want it extended, send mail.

Start up wireshark

By default, wireshark decodes frame relay as 'FRF 3.2/CISCO HDLC'. That's not quite what we want. Go to Edit/Preferences/Protocols/FR and change the encapsulation to 'GPRS Network Service'. Now you get full decoding.

Permalink | gth, telecom-signalling.
Posted 20th January 2010

This post is for people who want to use C to control a GTH. Other languages (e.g. Erlang, Java, Python and Perl) are easier to work with, but in some applications you want the complete control that C gives you.

Corelatus provides a C API for GTH. The C API lets you control a GTH using plain C function calls---all of the XML wire format is taken care of.

Here's an example of how to use it to record speech on an E1/T1 timeslot, something you'd typically do in a voicemail system:

#include "gth_apilib.h"

  GTH_api api;                     // GTH_api represents one GTH API connection
  int result;
  int data_socket;
  char buffer[2000];
  char job_id[MAX_JOB_ID];
  int octet_count;

  result = gth_connect(&api, "");    // Assuming the default GTH IP
  assert(result == 0);

  // We want to record audio on the E1/T1 called "1A", on timeslot 3.
  data_socket = gth_new_recorder(api, "1A", 3, job_id);

  while ( (octet_count = recv(data_socket, buffer, sizeof buffer, 0)) ) {
    // do whatever you want with the received data


The recording above happens in the 'while' loop. It continues forever (i.e. until you abort it). The audio data is bit-for-bit identical to what was on the E1/T1 timeslot, so this can be used for recording both voice and signalling.

Further examples

The C API code includes further examples to:

  • Record audio or signalling from a timeslot to a .wav file
  • Play back previously recorded files
  • Install (upgrade) the firmware on a GTH
  • Monitor (sniff) SS7 MTP-2 and save the signalling to a wireshark (PCAP) compatible file.
  • Monitor CAS R2 signalling.

Standalone Parser

Aside: the C API code includes a standalone parser for the XML responses the GTH emits. You can use the parser without using the rest of the API library, if you want to.

Permalink | c, gth.
New blog software
Posted 19th January 2010

This blog now uses chronicle to compile the blog.

Before, I used Wordpress. Wordpress is OK and mostly 'just works'. But, after a year, I wanted something which followed my normal workflow, i.e. edit-compile-test-checkin-publish. It's easier for me to write posts in emacs, build the blog with a Makefile, version-control with git and publish via scp.

I've made all comments go via mail, comment spam was getting overwhelming.

Audio power levels on E1/T1 timeslots: the digital milliwatt
Posted 10th October 2009

Sometimes, you want to know when the audio on an E1/T1 timeslot has gotten louder than some limit. In a voice mail application, that's useful for catching mistakes such as a subscriber leaving a message but then not hanging up the phone properly---you don't want to record hours and hours of silence. In an IVR application, you might want to keep an eye on the audio level so that a frustrated (shouting!) subscriber can be forwarded to a human operator.

GTH provides a "level detector" to do that sort of thing. You start a level detector on a timeslot, give it a loudness threshold, and it'll notify you whenever the audio on the timeslot goes over that threshold. Here's an example command which notifies you if the power on timeslot 13 of an E1/T1 is louder than -20dBm0:

  <new><level_detector threshold='-20'>
     <pcm_source span='2A' timeslot='13'/>

The algorithm is:

  1. Take a 100ms block of audio (800 samples)
  2. Square all the samples
  3. Sum the squares
  4. If the sum exceeds the loudness threshold, send an XML event

There are some details to worry about.

The digital milliwatt

The threshold, e.g. -20 in the example above, has to be relative to something. The standard reference power level in telecommunications is the milliwatt. ITU-T G.711, table 5 and 6 defines the sequences which represent a milliwatt:

A-law: 34 21 21 34 b4 a1 a1 b4
μ-law: 1e 0b 0b 1e 9e 8b 8b 9e

Here's what a few periods of the digital milliwatt look like:


In this post, the unit 'dBm0' means power, in dB relative to the digital milliwatt, as defined by the sequences above. If you have no idea what dB means, wikipedia has a decent article.

What's the loudest possible sound on a timeslot?

170 is the highest value possible in A-law encoding. It corresponds to linear 4032. That's about 6dB louder than the digital milliwatt.

85 is the smallest value possible in A-law encoding. It corresponds to linear -1. That's about 66dB softer than the digital milliwatt.

The range -66dBm0...+6dBm0 sets an upper bound on the range of power on a timeslot. Then there are other things which further limit the practical range, so you're unlikely to actually use a +6dBm0 threshold in practice, but it's there if you want it.

Is the G.711 definition the best one?

The sequence given in G.711 is a 1kHz sine wave. The sampling rate on E1/T1 is 8kHz, so the reference sequence can be expressed in just eight values. That's nice, but that also leads to small errors, about 0.13dB, because of quantisation.

ITU-T O.133 discusses that problem in detail and proposes a test signal which specfically is not 1kHz (i.e. not a submultiple of the sampling rate). For most practical purposes, 0.13dB doesn't matter and so the simple and robust thing to do is to use the well-defined and well-known G.711 sequence as a reference.

Here's what a few periods of a 1020Hz signal look like. Notice that the samples, i.e. the red crosses, don't appear in the same spot one period later---that way we don't get the same errors over and over again.

1020Hz signal sampled at 125us intervals
1020Hz signal sampled at 125us intervals

Testing pitfall: the .wav header

GTH players plays raw A-law or μ-law data. If you feed a player a .wav file in 8kHz A-law, or μ-law if your network uses μ-law, there will be a very short bit of noise at the start of the playback because the .wav header gets treated as though it were audio.

When testing level detection, especially at quiet levels, that header noise is enough to trigger a detector. Here's a .wav of a 1000Hz sine wave at about -30dBm0:

00000000  52 49 46 46 42 27 00 00  57 41 56 45 66 6d 74 20  |RIFFB'..WAVEfmt |
00000010  12 00 00 00 06 00 01 00  40 1f 00 00 40 1f 00 00  |........@...@...|
00000020  01 00 08 00 00 00 66 61  63 74 04 00 00 00 10 27  |......fact.....'|
00000030  00 00 64 61 74 61 10 27  00 00 d5 c4 f5 f1 f3 f1  |'........|
00000040  f5 c4 d5 44 75 71 73 71  75 44 d5 c4 f5 f1 f3 f1  |...DuqsquD......|
00002740  f5 c4 d5 44 75 71 73 71  75 44                    |...DuqsquD|

The first 58 octets (bytes) are the header. If we turn that header into a periodic signal, it's at about -8dBm0, which is fairly loud. With the default period parameter of 100ms in the level detector, that'll cause a false level of about -11dBm0.

The period parameter

The level_detector has an optional parameter, the period. The period sets the size of the audio block the GTH considers when measuring the power. A short period makes the GTH responsive to sudden changes in power on the timeslot, which would be useful in an application such as figuring out which of the people in a conference call are currently talking. A long period averages out the power over a longer time, which is useful in deciding whether a voicemail recording has finished.

The default is 100ms.

Sample files

This .zip file contains sample recordings with 1kHz sine waves at 0, -10, -20, -30, -40 and -50 dBm0. The .wav versions are useful for listening to or importing into an audio editing program to calibrate the level meter. The .raw versions are just plain A-law samples---you can use them with a GTH player.

Permalink | gth, telecom-signalling.
How does TCP behave on an interrupted network?
Posted 27th August 2009

GTH E1/T1 modules are always controlled by a general-purpose server, usually some sort of unix machine. The server and GTH are connected by ethernet and communicate using TCP sockets. Normally, that ethernet connection is chosen to be simple and reliable, for instance by putting the server and the GTH in the same rack, connected to the same ethernet switch.

I experimented a bit to see what happens when that network gets interrupted. I interrupted the network in a reproduceable way by disabling and re-enabling the server's ethernet port for a known length of time while running a <recorder>. (A <recorder> sends all the data, typically someone talking, from an E1 timeslot to the server over a TCP socket, 8000 octets per second.)

Capturing the ethernet packets

Here's what I did to capture traffic and interrupt the ethernet:

  tcpdump -w /tmp/capture.pcap -s 0 not port 22
  sudo ifconfig eth0 down; sleep 5; sudo ifconfig eth0 up

A trace where traffic recovers in time to prevent an overrun

The GTH buffers about two seconds of timeslot traffic. So a 'sleep' of about a second won't result in an overrun. Here's what it looks like in wireshark:

PacketTimeDirectionFlagsSeq. #

133 7.596 GTH -> server [PSH, ACK] 59393
134 7.633 server -> GTH [ACK] 1
135 7.724 GTH -> server [PSH, ACK] 60417
136 7.761 server -> GTH [ACK] 1
137 7.852 GTH -> server [PSH, ACK] 61441
138 7.889 server -> GTH [ACK] 1
139 7.980 GTH -> server [PSH, ACK] 62465
140 8.017 server -> GTH [ACK] 1
141 8.108 GTH -> server [PSH, ACK] 63489
142 8.145 server -> GTH [ACK] 1
143 8.236 GTH -> server [PSH, ACK] 64513
144 8.273 server -> GTH [ACK] 1
145 8.364 GTH -> server [PSH, ACK] 65537
146 8.401 server -> GTH [ACK] 1
147 10.151 GTH -> server [PSH, ACK] 66561
148 10.151 server -> GTH [ACK] 1
149 10.151 GTH -> server [ACK] 67585
150 10.151 server -> GTH [ACK] 1

Everything up to packet 146 is normal: the GTH ( sends 8000 octets every second and the server ( acks them. It happens to be in chunks of 1024 octets about eight times per second. After packet 146, about 8.4 seconds after the capture started, the ethernet interface went down and stayed down for 1s. The TCP stream started up again after about 1.5s and then 'caught up' by sending many packets in quick succession.

A trace where traffic didn't recover

I took a second trace similar to the first one, except this time, I disabled ethernet for about five seconds:

Packet Time     Source IP     Dest IP    SPort   DPort
 28   1.040083 -> 54271 > 45195 [PSH, ACK] Seq=7169
 29   1.040095 -> 45195 > 54271 [ACK] Seq=1
 30   1.168065 -> 54271 > 45195 [PSH, ACK] Seq=8193
 31   1.168078 -> 45195 > 54271 [ACK] Seq=1 
 32   1.296067 -> 54271 > 45195 [PSH, ACK] Seq=9217
 33   1.296079 -> 45195 > 54271 [ACK] Seq=1 
 34   1.424068 -> 54271 > 45195 [PSH, ACK] Seq=10241
 35   1.424081 -> 45195 > 54271 [ACK] Seq=1
 36   7.782851 -> 54271 > 45195 [PSH, ACK] Seq=11265
 37   7.782863 -> 45195 > 54271 [ACK] Seq=1
 38   7.783406 -> 54271 > 45195 [ACK] Seq=12289
 39   7.783413 -> 45195 > 54271 [ACK] Seq=1
 40   7.783569 -> 54271 > 45195 [ACK] Seq=13737
 50   7.784962 -> 54271 > 45195 [FIN, PSH, ACK] Seq=23873
 51   7.784972 -> 45195 > 54271 [ACK] Seq=1
 52   7.785026 -> 45195 > 54271 [FIN, ACK] Seq=1
 53   7.785348 -> 54271 > 45195 [ACK] Seq=25322

Everything is normal up to packet 35. Then, ethernet is suspended for five seconds and TCP takes a further second to recover, which causes a buffer overrun on the GTH ( The GTH closes the socket at packet 50 and also sends an overrun event to the application so that it knows why the socket was closed.

Bottom line

GTH uses IP for control and traffic. It is important that the IP link between the GTH and the server is simple and reliable. Ideally the GTH and server should be in the same rack and be connected by an ethernet switch.

It's possible for a system to survive a short interruption (less than a second) to the ethernet traffic without pre-recorded calls getting interrupted. For longer interruptions, all bets are off.

(Interruptions aren't the only type of network problem, e.g. radio networks such as 802.11 can suffer significant packet loss, which can trigger TCP congestion avoidance. But that's another topic.)

Permalink | gth, questions-from-customers.
Capturing SS7 with wireshark or tshark
Posted 10th August 2009

Update 23. January 2014: save_to_pcap now emits Pcap-NG by default, but current versions (1.10.5)of Wireshark don't allow that through pipes. Use the '-c' switch to force classic Pcap.

I often use wireshark to look at SS7 signalling on E1 links. Up until today, I've always done that by capturing the signalling (from a GTH), then converting the captured data to libpcap format and finally loading the file into wireshark.

Someone showed me a better way today: wireshark can read from a pipe or from standard input. That lets me see and filter the packets in wireshark in real time. Here's how to do it, using the save_to_pcap demo program (included in gth_c_examples):

> ./save_to_pcap -c gth21 1A 2A 16 - | wireshark -k -i -
capturing packets, press ^C to abort
saving capture to stdout

The same thing works for tshark:

 >./save_to_pcap -c gth21 1A 2A 16 - | tshark -V -i -
capturing packets, press ^C to abort
saving capture to stdout
Capturing on -
Frame 1 (15 bytes on wire, 15 bytes captured)
    Arrival Time: Aug 10, 2009 20:38:29.388000000
   Message Transfer Part Level 2
    .000 1101 = Backward sequence number: 13
    1... .... = Backward indicator bit: 1
    .011 1000 = Forward sequence number: 56
    1... .... = Forward indicator bit: 1
    ..00 0000 = Length Indicator: 0
    00.. .... = Spare: 0

Works on *nix and Windows

Piping standard output to wireshark/tshark works on all the *nixes, i.e. linux, BSD, OSX, Solaris. On Windows, things are a bit different, you have to use 'named pipes' instead, like this:

 save_to_pcap -c 1A 2A 16 \\.\pipe\ss7.1
 wireshark -k -i \\.\pipe\ss7.1

On some older (as of August 2009) versions of wireshark, possibly in combination with older libraries, the "-i -" switch doesn't work, at least according to google, even though the tshark version works.

Permalink | questions-from-customers, telecom-signalling.
Generating DTMF using a 'player' on GTH
Posted 9th June 2009

The GTH can transmit in-band signalling tones on a timeslot. That's useful for testing and for building active in-band signalling systems.


The tones transmitted when the subscriber presses a number key on fixed or mobile handset are called DTMF. Wikipedia has an article about it. To generate DTMF, all we really need to know is that there are 16 possible DTMF signals, that each signal is made up of two sine waves of particular frequencies and that sending the signal for 100ms is a reasonable thing to do.

Here's a .zip file with DTMF tones in it. Each file is raw ALAW data, i.e. it's ready for the GTH to play (transmit) on a timeslot.

The GTH has two ways of playing tones. One way is to stream the audio data in over a TCP socket each time we want to play it. I wrote a post about that earlier. The other way is to store the sample data on the GTH and command its playback whenever it's needed. Since there's a small number of different tones (12, or 16 if you want to use the A/B/C/D tones as well) and the tones are short, storing them on the GTH makes sense. To store the tone:

<new><clip name='dtmf5'></new>
(and now send the 800 byte file)

to play the tone later on:

  <clip id='clip dtmf5'/>
  <pcm_sink span='3A' timeslot='19'/>

Sequences of tones

Sometimes you want to transmit a sequence of DTMF tones, for instance to simulate a subscriber dialling a number. The GTH lets you start a player with a sequence of tones like this:

   <clip id='clip dtmf5'/><clip id='clip dtmf6'/><clip id='clip dtmf8'/>
   <pcm_sink span='3A' timeslot='19'/>

But that isn't a valid sequence of DTMF tones. Why not? Because DTMF expects a gap between tones. The cleanest way to handle that is to define another clip consisting of just silence and putting it between each tone. A good 'silence' value on E1 lines is 0x54. 60ms (480 samples) is a reasonable length.

Other in-band tones

DTMF in-band signalling is used in pretty much all handsets (telephones), mostly for dialling, but also to navigate menus in IVR systems. But before SS7 became popular, in-band signalling in the form of CAS and SS5 was even used to communicate call setup information between exchanges. GTH can also generate those tones, but that can be the subject of another post.

Permalink | gth, questions-from-customers, telecom-signalling.
Perl example code for GTH: SS7 ISUP decoding and playback/record
Posted 28th May 2009

To help people get started, has some example code for doing useful things with GTH units.

Now it also has Perl example code. It does the same thing as the python examples:

  • Enable an E1/T1 port
  • Start MTP-2 monitoring on a timeslot and decode SS7 ISUP (to print out when calls start and stop). I wrote a post a while back about how to decode ISUP.
  • Dump the contents of a timeslot to a file (for later analysis)
  • Feed a file into a timeslot (for playback of previously captured files)

It's built on top of a Perl module which provides a Perl API for a subset of the GTH API.

A quick example

Here's a quick example of how it's used. We want to enable (turn on) the first E1/T1 interface on a GTH module:

my $api = gth_control->new($host);
$api->send("<set name='pcm$span'><attribute name='mode' value='E1'/></set>");
defined $api->next_non_event()->{ok} || die("error from GTH (bogus PCM?)");

It's good for experimenting.

The Perl module the examples are based on,, is at a level which makes it useful for experiments and prototypes. To build a full-fledged product on top of it, more work is needed.

For a start, you'd probably want to move the XML generation (like the '<set name=...' code above) out of the application code and into the module, thus making it a pure Perl interface.

Next, you need to come up with a strategy to deal with concurrency, because being limited to recording one timeslot at a time is fine for lab work, but not fine for (say) a voicemail system.


The zipfile of the code is linked from the bottom of the API page.

Permalink | gth.
Decoding MTP-3 and ISUP
Posted 25th March 2009

Sometimes, you want to look at the signalling on an E1 and use it to figure out when telephone calls start and stop. In SS7 networks, call setup and tear-down is done by the ISUP layer, which fits in to the SS7 stack like this:

Layer 4: ISUP
Layer 3: MTP-3
Layer 2: MTP-2
Layer 1: MTP-1 (typically an 2Mbit/s E1 or a 1.5Mbit/s/T1)

If you have a GTH connected to the E1 you're interested in, either via a DXC or a monitor point, the GTH takes care of layers one and two. That leaves MTP-3 and ISUP to you.

The easiest way to decode MTP-3 and ISUP is to let wireshark do it for you. There's a note about how to do that on Corelatus' official site. But this blog entry is about how to decode MTP-3 and ISUP yourself.

A signal unit (packet)

In SS7, packets are usually called "signal units". Here's what an SS7 signal unit looks like 'on the wire', octet by octet, with MTP-2 and MTP-1 already decoded:

  8d c8 1f 85 02 40 00 00  35 00 01 00 21 00 0a 02
  02 08 06 01 10 12 52 55  21 0a 06 07 01 11 13 53
  55 00 6e 00


The start of the packet is the MTP-2 (ITU-T Q.703) and MTP-3 (ITU-T Q.704) headers. These headers are easy to decode because they are always fixed-length:

Octet(s) Value Purpose
00--01 8d c8 MTP-2 sequence numbers, safe to ignore
02 1f MTP-2 length indicator. Anything less than 3 is reserved for MTP-2 itself and should be discarded.
03 85 MTP-2 SIO. The SIO tells us which 'service' the signal unit is intended for. Q.704 sections14.2.1 and 14.2.2 tell us that anything ending in hex 5 is for ISUP.
04--07 02 40 00 00 MTP-3 Routing label. The routing label is just a "from" and "to" address in the SS7 network. For most applications we can ignore it. Q.704 figure 3 shows what's in the routing label.

Upshot: to see calls start and stop, all we have to do for MTP-3 is:

  1. Look at the length indicator (offset 2) and discard any signal unit where it's less than 3.
  2. Look at the SIO (offset 3). Discard if (SIO & 0x0f != 5)


The rest of the signal unit is ISUP. Annex C in ITU-T Q.767 tells us how to decode ISUP. ISUP is fiddly because there are several types of ISUP packets, because several of those types have optional fields and because some of those fields are variable length. Here are the octets we have left after removing MTP-2 and MTP-3:

  35 00 01 00 21 00 0a 02
  02 08 06 01 10 12 52 55
  21 0a 06 07 01 11 13 53
  55 00 6e 00

The first two octets are the CIC. The third octet is the Message type.

The CIC (Q.767 C.1.2) tells us which circuit this call uses. All the signalling for one call has the same CIC. In ITU networks, it's a 12-bit value packed into the field in little-endian byte order. In this case CIC=0x0035. We're sniffing an E1 line, so C.1.2.a tells us that the lower five bits correspond to the timeslot (timeslot 5) and the rest identifies the E1 itself.

The Message Type (Q.767 Table C-3) field tells us what sort of ISUP message this signal unit is. 0x01 is an IAM. 0x10 is RLC. For a minimal "show me what calls are going through the system" hack, we only need to look at the IAM (comes at the start of the call, contains the A and B numbers) and the RLC (sent when the call is finished) messages.

Now we know that the CIC=0x35, that the message is an IAM and we still have about a dozen octets to decode. Q.767 table C-16 tells us how to decode an IAM. There are some uninteresting fixed-length fields followed by the B number and then the A number. Look at the code (or Q.767, section C.3.7) if you're interested in the details. All we really care about is that these octets

  06 01 10 12 52 55 21

represent the B number: 21255512. You can see the number in the raw data if you skip the first three octets and swap every second digit.

Turning those ISUP steps into an algorithm to decode one signal unit:

  1. Save the CIC
  2. Is the message an IAM? Decode it as an IAM, which is fiddly.
  3. Is the message an RLC? Just print the CIC.

That's all you need to do to make a simple system which prints the start and end of each call. To do something useful, you need to maintain a table of in-progress calls and match up the IAM and RLC messages with the same CIC. You also need to handle things like systems restarting.

Further reading

The ITU now have most of their standards freely available at So one way to learn more about MTP-3 and ISUP is to read the standards, e.g. all the Q-series standards about signalling are here.

Erlang code

Everything discussed above is implemented in the ss7_sniffer.erl example on It makes good use of Erlang's binary syntax, e.g. here's the MTP-3 decoder:

    mtp3(<<_Sub:4, Service_indicator:4>>, <<DPC:14, OPC:14, SLS:4,
						    Rest/binary>>) ->
	case Service_indicator of
	0 -> % Management
	1 -> % Test/maintenance
	3 -> % SCCP
	5 ->
        isup(DPC, OPC, SLS, Rest);
	9 -> % B-ISUP; similar to ISUP, but not compatible.
	X ->
        io:fwrite("ignoring SU with unexpected service indicator=~p\n", [X])

It looks a lot like one of the examples in the original paper about the binary syntax.

Python Code

The same thing done in Python is fairly straightforward once you discover the Python 'struct' library, which is basically the same thing as PERL's pack/unpack. The code is in, inside the GTH python examples zip.

It feels like I haven't discovered whatever it is python people use to unpack bitfields, e.g. something neater than:

    is_even = ((ord(num[1]) &amp; 0x80) == 0)

or, better still, a clean way to decode the MTP-3 routing label.


Bartosz, 28. January 2010

Hey. Just stumbled on this page while I was googling after Mtp3 procedures. I'm wondering if you have any materials that you could share regarding link setup - I'm working on open source stacks for ss7. Im able to setup mtp2,3 and layer 4(signle link), now I'm working on getting it work with more than one and frankly Qs for mtp3 are not very helpful.


Matt, 28. January 2010

@Bartosz: I only really work with MTP2. My customers use MTP3, so I know quite a bit about it, but you probably know more. For MTP2, the standard is difficult to read, but comprehensive. MTP3 seems harder. I have a copy of "Signalling in Telecommunications Networks" by van Bosse et al, it's useful for getting started, but I always end up trudging through the standards.

Interesting to see someone working on an open SS7 stack. Up until now, I've only been aware of (written in C, some parts seem complete, the project seems to be a one-man effort).

Or: I don't think I can help much, but what you're doing looks interesting.


Emza, September 15, 2009

I found it very important but still not enough for me. I was looking for ISUP MTP-3 and ISUP decoding in C or in C#(not only start and end singnal unit but general which includes all message types). I would like to thank you but can help in my case please.

Thanks again

Matt, September 15, 2009

@Emza, decoding the rest of the message types is 'just' a matter of working your way through ITU-T Q.767 (ITU standards are now available for free at and writing code to handle each and every section in the standard. Same basic idea as the messages I did. Doing it all is a few days of drudge work, which is why I haven't done it here.

About doing it in C or C#. I know nothing about C#. I work with C most days, though. Doing this sort of protocol decoding in C is fairly straightforward, but it's inevitably going to be more tedious than doing it in something like python, perl or erlang. In some applications the performance gain might be worth the extra effort.

Meskerem David, September 15, 2009

It is a great job. Keep working...

Can u please me one brief algorithm of decoding ISUP. Especially I am not clear how to decode the variable length parameters and the optional ones.


Matt, September 15, 2009

@Meskerem, In an IAM, the A-number and B-number parameters are variable length. The algorithm for finding the start is trivial, it's just a pointer offset. Here's what it looks like in the python example code:


      def isup_iam(_, CIC, sif):
      # First 5 octets can be ignored
      bnum_pointer = ord(sif[5])
      anum_pointer = ord(sif[6])
      bnum = sif[5 + bnum_pointer:]
      anum = sif[7 + anum_pointer:]
      print "IAM called party: %s calling party: %s CIC=%d" \
      % (isup_number(bnum), isup_number(anum), CIC)

      # And here's how the example code figures out the length:

      # Decode an ISUP number, as per C 3.7
      def isup_number(num):
      length = ord(num[0]) - 2


the complete code is at

(the site also has exactly the same decoding routines in Perl and Erlang)

Permalink | erlang, gth, python, telecom-signalling.

Blog generation by Chronicle v4.1