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 microsoft.com
The install process is pretty standard for windows, if you're
accustomed to Microsoft products, there are no surprises here.
Compiling the code
- Start the "Visual Studio Command Prompt". By default, that's
in "Start/Programs/Microsoft Visual Studio 2010 Express".
- Navigate to the directory you unpacked
the sample
code in. Remove any old .exe files with 'del *.exe'.
- Run 'nmake /f NMakefile'. After a couple of seconds, you have
six brand-new .exe files. Here's what this step looks like:
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.
Comments
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.
(website)
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 openss7.org (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.
Matt
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
Embza
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 http://www.itu.int) 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.
Thanks
Meskerem
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]) - 2the complete code is at http://www.corelatus.com/gth/api/gth_python_examples.zip
(the site also has exactly the same decoding routines in Perl and Erlang)