lundi 20 décembre 2010

Boghe - IMS/RCS Client for Windows

We are proud to announce the availability of the first alpha release of Boghe, our IMS/RCS client for Windows XP, Vista and 7.



For more information: http://code.google.com/p/boghe/

mercredi 28 juillet 2010

IMSDroid - SIP/IMS Client for Android (Beta)

We are proud to announce the availability of the first fully featured open source IMS Client for Android devices (1.5 and later).
The main purpose of the project (codename IMSDroid) is to exhibit doubango's features and to offer an IMS client to the open source community.

The current version of IMSDroid partially implements GSMA Rich Communication Suite release 3 and The One Voice profile V1.0.0 (LTE/4G, also known as GSMA VoLTE) specifications. Missing features will be implemented in the next releases. Stay tuned.
Highlights
  • SIP(RFC 3261, 3GPP TS 24.229 Rel-9)
  • Signaling Compression, SigComp (RFC 3320, 3485, 4077, 4464, 4465, 4896, 5049, 5112 and 1951)

  • Enhanced Address Book (XCAP storage, authorizations, presence, ...)
  • Partial supports for GSMA Rich Communication Suite release 3
  • Partial supports for One Voice Profile V1.0.0 (GSMA VoLTE)
  • Partial supports for MMTel UNI (used by GSMA RCS and GSMA VoLTE)

  • IMS-AKA registration (both AKA-v1 and AKA-v2), Digest MD5, Basic
  • 3GPP Early IMS Security (3GPP TS 33.978)
  • Proxy-CSCF discovery using DNS NAPTR+SRV
  • Private extension headers for 3GPP
  • Service Route discovery
  • Subscription to reg event package (Honoring network initiated (re/de/un)-registration events)

  • 3GPP SMS Over IP (3GPP TS 23.038, 24.040, 24.011, 24.341 and 24.451)
  • Voice Call (AMR-NB, GSM, PCMA, PCMU, Speex-NB, iLBC)
  • Video Call (H264, Theora, H.263, H.263-1998, H.261)
  • DTMF (RFC 4733)
  • QoS negotiation using Preconditions (RFC 3312, 4032 and 5027)
  • SIP Session Timers (RFC 4028)
  • Provisional Response Acknowledgments (PRACK)
  • Communication Hold (3GPP TS 24.610)
  • Message Waiting Indication (3GPP TS 24.606)
  • Calling E.164 numbers by using ENUM protocol (RFC 3761)
  • NAT Traversal using STUN2 (RFC 5389) with possibilities to automatically discover the server by using DNS SRV (TURN already implemented and ICE is under tests)

Many other features are supported by the underlying framework but not exposed to the user interface (in progress). For more information please refer to doubango website.
These features include: IPv6, OMA Large IM Message (MSRP), File Transfer (MSRP), Image Sharing (IR.79), Video Sharing (IR.74), TLS and IPSec Security Agreement (RFC 3329), Proxy-CSCF discovery using DHCPv4/v6, TURN, ...

For more information: http://code.google.com/p/imsdroid

jeudi 18 mars 2010

SDP Offer/Answer (SOA)

SDP stands for Session Description Protocol and has been specified by the IETF in RFC 4566. It was previously defined in RFC 2327, which is now obsolete.

In order to understand this post, the reader MUST have a basic knowledge of how SIP (Session Initiation Protocol) works and how sessions are setted up and teared down.

The goal of this post is to explain (Highlights) how to implement a robust SOA (SDP Offer/Answer) machine. This can apply to both basic SIP equipment and 3GPP NGN/IMS nodes with MMTel extensions.


Related RFCs (Highlights): 3261, 3262 (100rel; PRACK), 3264 (Offer/Answer model), 3311 (UPDATE), 3312 (Preconditions/QoS), 3960 (Early Media and Ringing Tone Generation) and draft-ietf-sipping-sip-offeranswer-12.



Alice

Bob





SDP Offer

SDP Answer

RFC

Callflow

Note







1- Basic Call

[[Alice ==> Bob]]

INVITE

2xx INVITE

3261

  • Alice sends the initial offer in the INVITE (or reINVITE if session is already established)

  • Bob's answer is sent in the 2xx INVITE response

  • If a reINVITE fails, the session parameters in effect prior to the reINVITE MUST remain unchanged

  • To reject the offer, Bob should send a 488 INVITE response with a warning header field indicating the reason. If this was a reINVITE, a failure response should not be sent if media has already been exchanged.

2- Bodiless INVITE (without 100rel extension)

[[Bob ==> Alice]]

ACK

2xx INVITE

3261

  1. Alice sends initial INVITE without offer

  2. Bob's offer is sent in the 2xx INVITE response

  3. Alice's answer is sent in the ACK request

  • The initial INVITE has no SDP offer

  • As there is no offer in the initial offer, the first reliable non-failure message MUST have an Offer. As we suppose that 100rel extension is not supported then the offer MUST be in the 2xx response.

  • To reject /update the offer, Alice should first send her answer in the ACK response. Once the ACK is sent, Alice can send another offer or hangup the call.

3- Provisional response

[[Alice ==> Bob]]

INVITE

1xx-rel INVITE

3262

  1. Alice sends the initial offer in the INVITE

  2. Bob's answer is sent in the first reliable provisional response, in this case it's a 1xx response

  • Both clients MUST support 100rel extension

  • As the first realiable provisional response contains a SDP, and is the first to do so, then that SDP is the answer to the offer and cannot be updated in subsequent reliable responses (200 to 600).

  • If an unreliable response precedes a reliable response it's considered as a "preview" of the answer that will be coming, and hence may be treated like an answer until the actual one arrives.


  • To reject the offer, Bob should send a 488 INVITE response with a warning header field indicating the reason.

4- Bodiless INVITE (with 100rel extension)

[[Bob ==> ALice]]

PRACK

1xx-rel INVITE

3262

  1. Alice sends an initial INVITE without offer

  2. Bob's answer is sent in the first reliable provisional response, in this case it's a 1xx INVITE response

  3. Alice's answer is sent in the PRACK response

  • Both clients MUST support 100rel extension

  • As there is no offer in the initial INVITE, the first reliable non-failure message MUST have an offer. As we suppose that 100rel extension is supported by both endpoints then the offer MUST be in the first 1xx-rel response.


  • To reject/update the offer, Alice should first send her answer in the PRACK request. Once the 2xx PRACK is received, Alice can send another offer (reINVITE or UPDATE) or hangup the session.

5- Two rounds

[[Alice ==> Bob]]

PRACK

200 PRACK

3262

  1. Alice sends an initial INVITE with or without offer (case 3 or 4 respectively)

  2. Bob's answer or offer (case 3 or 4 respectively) is sent in the first reliable provisional response

  3. As the first provisional response contains a SDP then, Alice can send another offer in the PRACK request

  4. Bob sends his second answer in the 200 PRACK

  • To reject/update the offer, Bob should first send his answer in the 200 PRACK response. Once the PRACK is sent, Bob can send another offer (reINVITE or UPDATE) or hangup the call.

6 – UPDATE

[[Alice ==> Bob]]

UPDATE

2xx UPDATE

3311

  1. The initial offer/answer could be made by using any of the above use cases

  2. Alice sends an UPDATE request with an offer to update the previous negociated SDP session

  3. Bob's answer is included in the 2xx UPDATE

  • Both clients MUST support UPDATE

  • To reject the offer, Bob should send a 488 UPDATE response with a warning header field indicating the reason.In this case the session parameters in effect prior to the UPDATE MUST remain unchanged and the session is not teared down.





SDP

== SDP offer can be in:

  • Any reliable non-failure response (1xx-rel or 2xx),

  • INVITE, PRACK and ACK requests. PRACK request contains an offer only if the reliable response which it acknoledges contains an answer to the previous offer/answer exchange (draft-ietf-sipping-sip-offeranswer-12 subclause 2.1)

== In a SDP pmessage, some lines in each description are REQUIRED and some are OPTIONAL, but all MUST appear in exactly the order given in RFC 4566 subclause 5.

Generating an Answer

== SDP message in the unreliable responses to the INVITE request must be identical to the answer which is included in the reliable response. A session description in an unreliable response that precedes a reliable response can be considered a "preview" of the answer that will be coming, and hence may be treated like an answer until the actual one arrives (draft-ietf-sipping-sip-offeranswer-12 subclause 3.1).

== An offered stream MAY be rejected in the answer, for any reason. If a stream is rejected, the offerer and answerer MUST NOT generate media (or RTCP packets) for that stream. To reject an offered stream, the port number in the corresponding stream in the answer MUST be set to zero (RFC 3264 subclause 6).

== For each "m=" line in the offer, there MUST be a corresponding "m=" line in the answer. The answer MUST contain exactly the same number of "m=" lines as the offer. This allows for streams to be matched up based on their order. This implies that if the offer contained zero "m=" lines, the answer MUST contain zero "m=" lines (RFC 3264 subclause 6).

== UAS should send an SDP answer reliably (if possible) before it starts sending media. And, if neither the UAC nor the UAS support 100rel, the UAS should send a preview of the answer before it starts sending media (draft-ietf-sipping-sip-offeranswer-12 subclause 3.1).

== When a UAS has received an initial INVITE without an offer, it must include an offer in the first reliable response to the INVITE (draft-ietf-sipping-sip-offeranswer-12 subclause 5.2.2).

== The answer to an offered session description is based on the offered session description. If the answer is different from the offer in any way (different IP addresses, ports, etc.), the origin line MUST be different in the answer, since the answer is generated by a different entity. In that case, the version number in the "o=" line of the answer is unrelated to the version number in the o line of the offer (RFC 3264 subclause 6).

== The "t=" line in the answer MUST be equal that of the offer. The time of the session cannot be negotiated (RFC 3264 subcaluse 6).

== When a UAC does not include an SDP body in the INVITE request, it expects the offer to be received with the first reliable response.

== The UAC will send the answer in the request to acknowledge the response, i.e. PRACK or ACK request of the reliable response (draft-ietf-sipping-sip-offeranswer-12 subclause 3.1.2)

Modifying the Session

== Both the re-INVITE and UPDATE methods can be used in an established dialog to update the session (draft-ietf-sipping-sip-offeranswer-12 subclause 3.3).

== When both UAs support the 100rel extension, they can UPDATE the session in the early dialog once the first offer/answer exchange has been completed (draft-ietf-sipping-sip-offeranswer-12 subclause 3.2).

== When issuing an offer that modifies the session, the "o=" line of the new SDP MUST be identical to that in the previous SDP, except that the version in the origin field MUST increment by one from the previous SDP. If the version in the origin line does not increment, the SDP MUST be identical to the SDP with that version number. The answerer MUST be prepared to receive an offer that contains SDP with a version that has not changed; this is effectively a no-op. If the previous SDP had N "m=" lines, the new SDP (in the UPDATE or reINVITE) MUST have at least N "m=" lines. The i-th media stream in the previous SDP, counting from the top, matches the i-th media stream in the new SDP, counting from the top. (RFC 3264 subcaluse 8).


== New media streams are created by new additional media descriptions below the existing ones, or by reusing the "slot" used by an old media stream which had been disabled by setting its port to zero. Reusing its slot means that the new media description replaces the old one, but retains its positioning relative to other media descriptions in the SDP. New media descriptions MUST appear below any existing media sections (RFC 3264 subcaluse 8.1).

== Existing media streams are removed by creating a new SDP with the port number for that stream set to zero. The stream description MAY omit all attributes present previously, and MAY list just a single media format (RFC 3264 subclause 8.2).

== The list of media formats used in the session MAY be changed (RFC 3264 subcaluse 8.3.2).

== The Address, Port or Transport for a stream MAY be changed (RFC 3264 subclause 8.3.1).

== The media type (audio, video, etc.) for a stream MAY be changed (RFC 3264 subclause 8.3.3).

== Any other attributes in a media description MAY be updated in an offer or answer (RFC 3264 subclause 8.3.4).

== If a reINVITE fails, the session parameters in effect prior to the reINVITE MUST remain unchanged, as if no re-INVITE had been issued (draft-ietf-sipping-sip-offeranswer-12 subclause 3.4 which reference RFC 3261 section 14.1).

== At any time, either agent MAY generate a new offer that updates the session. However, it MUST NOT generate a new offer if it has received an offer which it has not yet answered or rejected. It MUST NOT generate a new offer if it has generated a prior offer for which it has not yet received an answer or a rejection (draft-ietf-sipping-sip-offeranswer-12 subclause 4).

Holding media

== "HOLD" should be indicated in an established session by sending a new offer containing "a=sendonly" for each media stream to be held. An answerer is then to respond with "a=recvonly" to acknowledge that the hold request has been understood (RFC 3264 subclause 5.1). That is, the directionality of the media stream has no impact on the RTCP usage.

If UA2 has previously been "placed on hold" by UA1, via receipt of "a=sendonly", then it may initiate its own hold by sending a new offer containing "a=sendonly" to UA1. Upon receipt of that, UA1 will answer with "a=inactive" because that is the only valid answer that reflects its desire not to receive media (draft-ietf-sipping-sip-offeranswer-12 subclause 5.3).

== An agent MUST be capable of receiving SDP with a connection address of 0.0.0.0, in which case it means that neither RTP nor RTCP should be sent to the peer (RFC 3264 subclause 8.4).

Indicating Capabilities

== An SDP constructed to indicate media capabilities is structured as follows (RFC 3264 subclause 9):

  • It MUST be a valid SDP, except that it MAY omit both "e=" and "p=" lines. The "t=" line MUST be equal to "0 0"
  • For each media type supported by the agent, there MUST be a corresponding media description of that type
  • The session ID in the origin field MUST be unique for each SDP constructed to indicate media capabilities

jeudi 28 janvier 2010

Building/Debugging android native C applications

In this post I will explain how to compile, install and debug an Android native "C" application.
If you are reading this post just because you have googled the magic keywords ("android" + "native code") then you should know that there is an easier way to build native applications using android makefiles ("Android.mk" and "Application.mk").
The method I'm describing here is only useful if you want to understand how things work in order to create more complex standard GNU makefiles. This is also useful if you would like to create your own GNU autotools wrappers to compile projects using GNU configure.
I'm using Windows Vista as host machine but any other supported platforms (e.g. linux-x86 or darwin-x86) should work.

I have tested both the NDK (1.6) and SDK (2.1) on:
  • Windows XP (32-bit) and Vista (64-bit)
  • Mac OS X Snow Leopard
  • Ubuntu Intrepid
Installing Android SDK

To download the latest Android SDK, visit this address http://developer.android.com/sdk/index.html.
If you need information on how to install the SDK, visit this address http://developer.android.com/sdk/installing.html.
If the "SDK setup" fail to update the installed packages you can change the remote site URL from https://dl-ssl.google.com/android/repository/repository.xml to http://dl-ssl.google.com/android/repository/repository.xml (change the URL scheme from HTTPS to HTTP) or try to disable your anti-virus or firewall.

I have installed the SDK version 2.1 under c:/android-sdk (a.r.a /cygdrive/c/android-sdk).
Add an environment variable named ANDROID_SDK_ROOT pointing to the SDK root directory.

Important: You should add "$ANDROID_SDK_ROOT/tools" directory to the $PATH environment variable.
Under *nix:

export PATH=$ANDROID_SDK_ROOT/tools:$PATH
Under Cygwin: Open C:\Cygwin\Cygwin.bat and add:

set PATH=%ANDROID_SDK_TOOLS%;%PATH%
Installing Cygwin

If you are using Windows XP or Vista as host machine then you MUST install Cygwin Devel package with GNU Make (3.81 or later) before installing the NDK.
It should also work with MinGW.

Installing the Android NDK

To download the latest Android NDK, visit this address http://developer.android.com/sdk/ndk/1.6_r1/index.html.
I have uncompressed the NDK version 1.6 under c:/android-ndk (a.r.a /cygdrive/c/android-ndk).
Add an environment variable named ANDROID_NDK_ROOT pointing to the NDK root directory.
To install the NDK:

cd $ANDROID_NDK_ROOT
build/host-setup.sh
If all is OK then the console will print Host setup complete.
To test that the toolchain has been correctly installed you can try to build the hello-jni sample which comes with the NDK by doing this:

cd $ANDROID_NDK_ROOT
make -APP=hello-jni
If all is OK then the console will print:

Android NDK: Building for application 'hello-jni'
Compile thumb : hello-jni <= sources/samples/hello-jni/hello-jni.c SharedLibrary : libhello-jni.so Install : libhello-jni.so => apps/hello-jni/project/libs/armeabi
This mean that your native shared library (libhello-jni.so) have been successfully generated under $ANDROID_NDK_ROOT/apps/hello-jni/project/libs/armeabi folder.

Creating an AVD

AVD stands for Android Virtual Device and can be seen as a device profile (keyboard, dialing pad, skin, screen dimensions, appearance ...) to load into your emulator. You can create as many AVDs as you need.
To create an AVD named "avdtest" targeting platform 2.1 (targetID=android-7):

android create avd -n avdtest -t android-7
If all is OK the console will print:

Created AVD 'avdtest' based on Android 2.1, with the following hardware config: hw.lcd.density=160
Create test.c

Here I will create a basic test.c file under C:\tmp with the following content:

#include <stdio.h>// printf

int main(int argc, char **argv)
{
int i = 1;
i+=2;

printf("Hello, world (i=%d)!\n", i);

return 0;
}
Create makefile

Just create an empty file named makefile (without any extension) under C:\tmp (which is the same directory as test.c).
Now We will fill the makefile step by step.

Add application name, $ROOT directory, install directory and the NDK platform version:

APP := test
ROOT:=/cygdrive/c
NDK_PLATFORM_VER := 1.5
INSTALL_DIR := /data/tmp
Add useful environment vars:

ANDROID_NDK_ROOT:=$(ROOT)/android-ndk
ANDROID_NDK_HOST:=windows
ANDROID_SDK_ROOT:=$(ROOT)/android-sdk
PREBUILD:=$(ANDROID_NDK_ROOT)/build/prebuilt/$(ANDROID_NDK_HOST)/arm-eabi-4.2.1
BIN := $(PREBUILD)/bin
You MUST change ANDROID_NDK_HOST value from windows to linux-x86 if you are under *nix or darwin-x86 on MAC OS X.

Add GCC options:

CPP := $(BIN)/arm-eabi-g++
CC := $(BIN)/arm-eabi-gcc
CFLAGS :=
LDFLAGS := -Wl
Add targets

all: $(APP)

OBJS += $(APP).o

$(APP): $(OBJS)
$(CPP) $(LDFLAGS) -o $@ $^

%.o: %.c
$(CC) -c $(INCLUDE) $(CFLAGS) $< -o $@
install: $(APP)
$(ANDROID_SDK_ROOT)/tools/adb push $(APP) $(INSTALL_DIR)/$(APP)
$(ANDROID_SDK_ROOT)/tools/adb shell chmod 777 $(INSTALL_DIR)/$(APP)

shell:
$(ANDROID_SDK_ROOT)/tools/adb shell

run:
$(ANDROID_SDK_ROOT)/tools/adb shell $(INSTALL_DIR)/$(APP)

clean:
@rm -f $(APP).o $(APP)
Building the application

To build the application, switch to the directory where you have created both files and then:

make
At the output of the console you will get many errors saying that it's impossible to find stdlib.h, stdio.h etc etc.
To resolve this issue, add the Bionic header files to $CFLAGS variable like this:

CFLAGS := -I$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/include
If you retry (make) you will now get this link error:

crt0.o: No such file: No such file or directory
To avoid directly linking against the "C runtime" you must add "-nostdlib" flag to the link options like this:

LDFLAGS := -Wl -nostdlib
If you retry (make) you will now get these link errors:

test.c:(.text+0x34): undefined reference to `printf'
test.c:(.text+0x3c): undefined reference to `exit'
You get these errors because Bionic libc is missing. To add libc you MUST change $LDFLAGS like this:

LDFLAGS := -Wl -L$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/lib
LDFLAGS += -nostdlib -lc

If you retry (make) you will now get this link error:

/cygdrive/c/android-ndk/build/platforms/android-1.5/arch-arm/usr/lib/libc.so: undefined reference to `dl_unwind_find_exidx'
To resolve this issue you MUST specify the first set of directories into which to search the system shared libraries (*.so) . This is done by adding the "-rpath-link" option to the link options like this:

LDFLAGS := -Wl,-rpath-link=$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/lib -L$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/lib
If you retry (make) you will now get this warning:

/cygdrive/c/android-ndk/build/prebuilt/windows/arm-eabi-4.2.1/bin/../lib/gcc/arm
-eabi/4.2.1/../../../../arm-eabi/bin/ld: warning: cannot find entry symbol _start; defaulting to 000082c8
This is an Android known issue. You have this warning because the linker search "_start" as entry point. You can resolve this issue by renaming your main function. But the elegant way to resolve this issue is to specify the entry point in the link options like this:

LDFLAGS := -Wl,--entry=main,-rpath-link=$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/lib -L$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/lib
LDFLAGS += -nostdlib -lc
Now When you retry (make) your application will successfully build without any errors or warnings.

Testing your application

Before testing your application you MUST run the emulator like this:

emulator -avd avdtest
where "avdtest" is the name of the previously created avd (see "creating an avd" section).
To install the application on the emulator, open a new console and go to to directory where you have created test.c and makefile. Install your application on the emulator like this:

make install
If all is OK the console will print:

/cygdrive/c/android-sdk/tools/adb push test /data/tmp/test
304 KB/s (2493 bytes in 0.008s)
/cygdrive/c/android-sdk/tools/adb shell chmod 777 /data/tmp/test
To run the application type:

make run
You will probably get an error message saying:

/cygdrive/c/android-sdk/tools/adb shell /data/tmp/test
/data/tmp/test: not found
This error message is a bit confusing because if you browse the /data/tmp directory you will notice that the executable is here. The question is why?
I spent hours searching and I found that this error happens because the loader fails to load the application because it cannot found a proper linker.
To specify a search directory for the dynamic linker (at run time) you MUST change the link options like this:

LDFLAGS := -Wl,--entry=main,-rpath-link=$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/lib,-dynamic-linker=/system/bin/linker -L$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/lib
LDFLAGS += -nostdlib -lc
Now rebuild and install your application (make clean && make && make install) then run it again (make run).
The console will print the expected result ("hello, world (i=3)!") but just after we have an segmentation fault error ("[1] Segmentation fault /data/tmp/test").
To resolve this issue you can exit the program (exit(0);) just before the main function returns (return 0;). You should also include <stdlib.h>.
If you retry the build&&run process (make clean && make && make install && make run) then you should have:

/cygdrive/c/android-sdk/tools/adb shell /data/tmp/test
Hello, world (i=3)!
which is the expected result.

Debugging your application
Before doing anything you MUST copy the gdbserver file to the emultor.
This file is under $BIN ($ANDROID_NDK_ROOT/build/prebuilt/$ANDROID_NDK_HOST/arm-eabi-4.2.1/bin).
Copy gdbserver to the emulator like this:

adb push gdbserver $INSTALL_DIR/gdbserver
adb shell chmod 777 $INSTALL_DIR/gdbserver
where $INSTALL_DIR is the directory where you have installed your application (it's not mandatory to copy it in this directory).
Before running the server on port 1234 you MUST redirect all tcp connection to this port like this:

adb forward tcp:1234: tcp:1234
it's not mandatory to forward connections to the same port number.
Now it's time to run the server:

adb shell $INSTALL_DIR/gdbserver :1234 $INSTALL_DIR/$APP
note that only the server port is specified (no host).
If all is OK the the server will print something like this:

Process /data/tmp/test created; pid = 246
Listening on port 1234
Now to debug our application we will change the makefile by adding a new debug target like this.

GDB_CLIENT := $(BIN)/arm-eabi-gdb

debug:
$(GDB_CLIENT) $(APP)

To launch the application in debug mode type "make debug" (after make clean && make && make install of course). If you do this, you will see a warning message saying that "no debugging symbols found". No symbols ==> no debug.
To generate debug symbols you MUST change the makefile like this (should not be hard coded like this):

DEBUG = -g
CFLAGS := $(DEBUG) -I$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/include
Now rebuild and install your application (make clean && make && make install) then run it again (make debug). This (make debug) should open gdb invite command((gdb)) on the same console.
Connect to the server (from the same console) like this:

target remote :1234
Set a breakpoint on the main function and execute step by step (commands above are informational and you can use any gdb commands):

b main
c
n
p i
#$1 = 1
n
#9 printf("Hello, world (i=%d)!\n", i);
p i
#$2 = 3
c
#Program exited normally.

The final makefile and test.c files are shown below:

makefile

APP := test
ROOT:=/cygdrive/c
INSTALL_DIR := /data/tmp
NDK_PLATFORM_VER := 1.5

ANDROID_NDK_ROOT:=$(ROOT)/android-ndk
ANDROID_NDK_HOST:=windows
ANDROID_SDK_ROOT:=$(ROOT)/android-sdk
PREBUILD:=$(ANDROID_NDK_ROOT)/build/prebuilt/$(ANDROID_NDK_HOST)/arm-eabi-4.2.1
BIN := $(PREBUILD)/bin
GDB_CLIENT := $(BIN)/arm-eabi-gdb

DEBUG = -g

CPP := $(BIN)/arm-eabi-g++
CC := $(BIN)/arm-eabi-gcc
CFLAGS := $(DEBUG) -I$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/include
LDFLAGS := -Wl,--entry=main,-rpath-link=$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/lib,-dynamic-linker=/system/bin/linker -L$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/lib
LDFLAGS += -nostdlib -lc

all: $(APP)

OBJS += $(APP).o
$(APP): $(OBJS)
$(CPP) $(LDFLAGS) -o $@ $^
%.o: %.c
$(CC) -c $(INCLUDE) $(CFLAGS) $< -o $@
install: $(APP)
$(ANDROID_SDK_ROOT)/tools/adb push $(APP) $(INSTALL_DIR)/$(APP)
$(ANDROID_SDK_ROOT)/tools/adb shell chmod 777 $(INSTALL_DIR)/$(APP)
shell:
$(ANDROID_SDK_ROOT)/tools/adb shell
run:
$(ANDROID_SDK_ROOT)/tools/adb shell $(INSTALL_DIR)/$(APP)
debug:
$(GDB_CLIENT) $(APP)
clean:
@rm -f $(APP).o $(APP)


test.c

#include <stdio.h> // printf
#include <stdlib.h> //exit

int main(int argc, char **argv)
{
int i = 1;
i+=2;

printf("Hello, world (i=%d)!\n", i);

exit(0);
return 0;
}

mardi 19 janvier 2010

SIP/IMS and NAT traversal (Part 1)

This is the first part of a three-part series about NAT traversal solutions for SIP/IMS. I will begin from simple solutions (e.g. "rport" extension) and end with some more complex one (e.g ICE).

Today most Internet users are in a private network behind one or several NATs. This is not a problem for your browser (HTTP, FTP...) or email client (SMTP, POP...) because they use protocols that can operate behind NATs. The problem comes when you try to use your SIP phone to REGISTER or place a call to another softphone in different private network through Internet (Public network).
The problem with SIP is that IP addresses and Ports where to contact/respond an agent (e.g. PC, Box or mobile phone) are embedded in the SIP message itself.

For example, when you REGISTER to a registrar (e.g Serving-CSCF through Proxy-CSCF) you add a contact header (IP address and Port - Contact: <sip:alice@192.168.16.108:27208;transport=udp>) where the registrar should send all your incoming calls(INVITEs). This address is called Address-Of-Record (a.k.a AOR).
If you are in a private network you will add your private IP address and port in this header (Contact) and the problem is that this AOR is not visible to the elements outside your private network.
This mean that INVITE requests will never reach your agent.
You will have the same problem when you are the caller, as you will add in your SDP the IP addresses and ports where RTP packets shall be sent to you.

So, NAT problem concern both signaling (SIP/SDP) and media (RTP/RTCP) plans.

rport

This is the simplest way to deal with NATs problem at signalling (SIP only) plan for connectionless protocols (e.g. UDP). rport has been defined in RFC 3581 and apply to both connectionless (e.g. UDP) and connection-oriented (e.g. TCP, TLS, SCTP) protocols.

Because connection-oriented protocols such as TCP are bidirectional it is easier to deal with NAT traversal (all responses will be sent back using the same connection from which the request has been received from).

The philosophy (of rport) is that when you add this attribute (rport) in your requests (Via: SIP/2.0/UDP 192.168.16.108:27208;branch=z9hG4bK1234;rport) then all responses will be sent back to the ip:port from which the request has been received from. This parameter MUST be added in the top most Via header.

The response will contain a new parameter (« received ») containing the IP address from which the request has been received and the rport value will be filled with the mapped (public?) source port. In this way the client/caller (the sender of the request) can learn it's public/reflexive IP address and port (Via: SIP/2.0/UDP 192.168.16.108:27208;branch=z9hG4bK1234;rport=1234;received=10.1.1.1).

By examining the response the client can know if it's behind a NAT or not.

The problem with this solution is that it only deals with SIP messages and cannot be used for RTP/RTCP packets as the SDP will contain wrong IP addresses and ports. A solution to this issue could be using Symmetric RTP/RTCP (see above).

Symmetric RTP / RTP Control Protocol (RTCP)

As we have seen above (rport), we cannot use « rport » to deal with RTP/RTCP packets which are almost always transported using UDP. A solution to this problem could be using « Symetric RTP/RTCP » as per RFC 4961. This solution is a bit like using « rport ».

In this case the caller (INVITE originator) will create the request as per RFC 3261 as usual. When the 2xx (with SDP) or 1xx (with SDP) is received, the caller will ignore the IP addresses and ports defined in the response and send RTP/RTCP packets to IPs/Ports from which the RTP/RTCP packets have been sent (like rport).

To summarize:

  1. Bob sends an INVITE to Alice
  2. Alice sends back a 2xx (with SDP) or 1xx (with SDP) to Bob
  3. Bob waits for first RTP/RTCP packets to come from Alice
  4. Alice send first RTP/RTCP packest from IP-a and Port-a to IP-b and Port-b (Bob address and port defined in the SDP).
  5. Instead of sending RTP/RTCP packets to IP-asdp and Port-asdp as sepecified in the Alice's SDP, Bob will begin sending media stream to IP-a and Port-a.

As you can imagine this solution only work for only some NATs.

Session border controller

As its name says, it controls (both media and SIP packets) sessions (SIP calls) and is in the border (between two networks) of the networks.

In our case (NAT traversal) the role of SBC will be to inspect/control all outgoing and incoming SIP/RTP/RTCP packets. All SIP packets will be inspected and IP addresses and ports within the packet will be rewritten (e.g from private IP to public IP).

This solution has several problems:

  • Very expensive (€€$$££)
  • As the SBC operates on the SIP packets then it MUST be aware of all headers (or functions) in order to know what should be changed and what should not be changed. This mean that the SBC MUST always be up to date in order to efficiently handle SIP packets

  • Most SBCs can only handle well-know protocols such as SIP, RTP or RTCP and will drop all unknown protocols

  • There is also many problems when End-to-End encryption (e.g. TLS, SRTP or IPSec) is used unless the SBC has the key (which is a bad idea)

  • As the SBC will be used to relay all RTP/RTCP packets then this will introduce additional delay (dad sound quality)

  • When unreliable transport is used this (relaying packets) could also increase packet loss (QoS problem)

In the 3GPP IMS context the SBC is in most case bundled with the Proxy-CSCF (P-CSCF plus IMS-ALG) and this could resolve the security issue (SIP-IPSec).

STUN

STUN was previously defined in RFC 3489 and updated by RFC 5389. STUN stands for « Session Traversal Utilities for NAT » and is a client-server protocol (request <-> response) as SIP.

There is also « indication requests » that don't generate responses like « binding requests ».

Both reliable (e.g. UDP) and unreliable (e.g TCP, TLS or SCTP) are supported.

Like SIP, when unreliable transoport is used there is the notion of transctions and retransmissions.

When STUN is used the client learn its public IP address and port (a.k.a reflexive transport address) by sending a binding request to the STUN server (default UDP/TCP port:3478 and default TLS port: 5349). The server will in some case challenge (401) the client which should resend its request with all credentials (HTTP digest authentication).

If the request is suceessfuly autheticated by the server, then a success binding response is sent back to the client. This response contains a STUN attribute (XOR-MAPPED-ADDRESS)

with the client's public IP address, family and port. This is also called the « reflexive transport address ».

Once this reflexive transport address is know then the client can for example begin REGISTERing using this address and port as AOR.

====== Step 1: Sending binding request ======

In this request I send my STUN request from "192.168.16.108:1115" to the server in order to get the public IP address and port associated to this local socket/Private address (file descriptor).


====== Step 2 Success binding response ======

In step 2 the STUN client receive an response from the server with its public IP address and port. To match the response with the request we compare the transaction IDs.
As some routers rewrite the content of the packets the IP address and port are not sent "as is" but in XOR format (into the XOR-MAPPED-ADDRESS attribute).
To retrieve the port:
uint16_t port = ntohs(*((uint16_t*)payloadPtr));
port ^= 0x2112; /* First two bytes of the STUN2 magic cookie. */
To retrive the IPv4 address:
uint32_t addr = ntohl(*((uint32_t*)payloadPtr));
addr ^= 0x2112A442; /* The STUN2 magic cookie */

====== Step 3 Sending first SIP request ======


From step 1 and 2 the sip agent can assert that [192.168.16.108:1159] is mapped to [89.127.73.39:1115] (take care to the port mapping).
In step 3 when sending it's first REGISTER request it will use this pulic IP address and port to build its AOR. As rport option is used then you could keep the Via IP address and port inchanged (or not).

The major problem with STUN is that it could not be used behind bi-directional NATs. In the next parts I will explain how to overcome this problem by using TURN and ICE.