Žilinská univerzita > Fakulta riadenia a informatiky > Katedra informačných sietí

Configuring NAT traversal using Kamailio 3.1 and the Rtpproxy server

This article continues on series of articles about the Kamailio 3.1.x SIP proxy server deployed on the debian lenny and its features. In previous articles we have focused on:

1) installing clear Kamailio 3.1.x server 

2) adding of the Mysql support for persistance location storage

3) installing of the SIREMIS web management interface for our Kamailio server.

4) configuring the IM and presence service on Kamailio 3.1 - Howto

5) configuring the XCAP support for SIMPLE.

6) configuring TLS support

Now we will take a closer look on the NAT traversal solution with the usage of the Rtpproxy server.

Prerequisities

1) Installed and working Kamailio (OpenSER) 3.1.0 server.

2) Installed and running rtpproxy server. For this guide (as you see at the end, rtpproxy 1.2.1 does not work, 1.1.2 does)

3) usrloc (user location) module of the kamailio loaded

 Configuration

Kamailio 3.1 has for the NAT traversal preconfigured zone block prepared for the the rtpproxy server usage. So open /etc/kamailio/kamailio.cfg and find lines starting with:

# *** To enable nat traversal execute:
#     - define WITH_NAT
#     - install RTPProxy: http://www.rtpproxy.org
#     - start RTPProxy:
#        rtpproxy -l _your_public_ip_ -s udp:localhost:7722

They recommends to define "WITH_NAT" directive and to install rtpproxy with recommended parameters. We have rtpproxy already installed, thus we need to define the zone block directive only

#!define WITH_NAT

Going next, in module loading part of the config file, we may find zone block:

#!ifdef WITH_NAT
loadmodule "nathelper.so"
loadmodule "rtpproxy.so"
#!endif

which will load modules (nathelper and rtpproxy) required for the NAT traversal,  conditional of the WITH_NAT directive defined. We did it in previous step, so following modules will be loaded during the next restart of the Kamailio server.

Going next, in module param section of the kamailio.cfg we will find zone block:

#!ifdef WITH_NAT
# ----- rtpproxy params -----
modparam("rtpproxy", "rtpproxy_sock", "udp:127.0.0.1:7722")

# ----- nathelper params -----
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping_bflag", FLB_NATSIPPING)
modparam("nathelper", "sipping_from", "sip:pinger@kamailio.org")

# params needed for NAT traversal in other modules
modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)")
modparam("usrloc", "nat_bflag", FLB_NATB)
#!endif

This part of the Kamailio config file we have to modify slightly. Our topology is using the Kamamilio and the Rtpproxy running in the same machine, and they will communicate between each other using the localhost IP address. So, if we are using the Rtpproxy server with default configuration, we have to open /etc/default/rtpproxy file and uncomment following line regarding of udp socket, that will be sued for interconnection:

CONTROL_SOCK=udp:127.0.0.1:22222

and than we have to modify first modparam line from above kamailio.cfg block to reflect the actual rtpproxy configuration:

modparam("rtpproxy", "rtpproxy_sock", "udp:127.0.0.1:22222")

Next line of the configuration, modparam("nathelper", "natping_interval", 30) define in seconds how often NAT ping will be send to keep NAT binding opened, here 30second. We do not need to modify it.

Line modparam("nathelper", "ping_nated_only", 1) define, that only SIP UAs which are behind NAT will be pinged. We do not need to modify it.

Line modparam("nathelper", "sipping_bflag", FLB_NATSIPPING) define which branch flag of nat-ed client will be used for nat pinging. We do not need to modify it. By default the 7th flag is set and is named FLB_NATSIPPING, we may find it in  ####### Defined Values ######### part of the kamailo.cfg

# - flags
#   FLT_ - per transaction (message) flags
#       FLB_ - per branch flags
#!define FLT_ACC 1
#!define FLT_ACCMISSED 2
#!define FLT_ACCFAILED 3
#!define FLT_NATS 5
#!define FLB_NATB 6
#!define FLB_NATSIPPING 7

Tutorial about Kamailio flags is available at the link. Currently there are 31 tags defined, which can be named (like here).

 

Next line modparam("nathelper", "sipping_from", "sip:pinger@kamailio.org")  I will change to reflect my server DNS name:

modparam("nathelper", "sipping_from", "sip:pinger@ps.sip.uniza.sk")

Line modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)") define, I cited module documentation:

name of the Attribute-Value-Pair (AVP) used to store the URI containing the received IP, port, and protocol. The URI is created by fix_nated_register function of nathelper module and the attribute is then used by the registrar to store the received parameters. Do not forget to change the value of corresponding parameter in registrar module if you change the value of this parameter.

Line modparam("usrloc", "nat_bflag", FLB_NATB)  tell to the usrloc module to save branch parameter, that will be used as the NAT marker (decide if the contact is or not natted). This is a branch flag and it will be imported and used by nathelper module.

Route logic

route[NAT]

The first routing logic which is working with NAT traversal is the route [NAT] called from the main route logic. route [NAT] detects if a caller is behind a NAT. It is used for all methods which are generated from an UAC.

# Caller NAT detection route
route[NAT] {
#!ifdef WITH_NAT
        force_rport();
        if (nat_uac_test("19")) {
                if (method=="REGISTER") {
                        fix_nated_register();
                } else {
                        fix_nated_contact();
                }
                setflag(FLT_NATS);
        }
#!endif
        return;
}

Function force_rport() adds the rport parameter to the first Via header of the received message. Rport parameters cause that proxy will send subsequence responses back to this source port (this is the "public" port after NAT-ing).

Then inside of the logic test if UAC is behind NAT is done (nat_uac_test("19")), from the docu 19 = 1 + 2 + 16 (i.e.

  • 1 - Contact header field is searched for occurrence of RFC1918 addresses.

  • 2 - the "received" test is used: address in Via is compared against source IP address of signaling

  • 4 - Top Most VIA is searched for occurrence of RFC1918 addresses

  • 8 - SDP is searched for occurrence of RFC1918 addresses

  • 16 - test if the source port is different from the port in Via

  • 32 - test if the source IP address of signaling is a RFC1918 address

)

and if the SIP method is REGISTER, the function fix_nated_register() creates a URI consisting of the source IP, port, and protocol and stores the URI in an Attribute-Value-Pair ($avp(RECEIVED) in our case). The registrar will store the URI in the received column inside of the location table.

Otherwise the Contact header field of the SIP message will be rewritten to contain request's source address:port (fix_nated_contact()).

As the last step, the flag FLT_NATS is setted up to one as a mark that NATing is used.

Registration handling - route [REGISTRAR]

During the UA registration the registrar have to adjust flags indicating that a client is behind a NAT and to save branch param. Registrar has to setup B flag also, which is used by the nathelper module.

Route logic executed during a registation is by default:

# Handle SIP registrations
route[REGISTRAR] {
        if (is_method("REGISTER"))
        {
                if(isflagset(FLT_NATS))
                {
                        setbflag(FLB_NATB);
                        # uncomment next line to do SIP NAT pinging
                        ## setbflag(FLB_NATSIPPING);
                }
                if (!save("location"))
                        sl_reply_error();

                exit;
        }
}

and we have to uncomment the line:

setbflag(FLB_NATSIPPING);

to start pinging the NAT performed by the nathelper module (performed if FLB_NATSIPPING is setted up).

The server during the registration phase check if the 5th flag is set (line isflagset(FLT_NATS)), and since it is (setted up in route[NAT]), as a next step the Branch flag named FLB_NATB is set (the 5th flag), together with the flag FLB_NATSIPPING (7th flag) (nat pinging required). Branch param will be saved to (usrloc) and logic execution is stopped.

The module responsible for working with Kamailio flags is KEx module.

Dialog initialization - route [RELAY]

This route logic is called few times:

  • for methods which does not contain "to_tag",
    • it is usually during dialog initialization (it is from the second route[RELAY] of the main route logic),
    • or for methods that are statefully relayed to the destination (interdomain call),
    • or for methods for local domain users
  • and then it is called again for messages exchanged within dialog.

From the NAT traversal point of view the logic setup the branch flag and if prerequsities for detecting NAT is fulfilled it call the rtpproxy logic

route[RELAY] {
#!ifdef WITH_NAT
        if (check_route_param("nat=yes")) {
                setbflag(FLB_NATB);
        }
        if (isflagset(FLT_NATS) || isbflagset(FLB_NATB)) {
                route(RTPPROXY);
        }
#!endif
        /* example how to enable some additional event routes */         if (is_method("INVITE")) {                 #t_on_branch("BRANCH_ONE");                 t_on_reply("REPLY_ONE");                 t_on_failure("FAIL_ONE");         }         if (!t_relay()) {                 sl_reply_error();         }         exit; } 

The rtpproxy logic

The logic is called from the RELAY route and it is used to rewrite private addresses inside of the SDP body (force_rtp_proxy()) of the message or at the end of a call to close down a RTPproxy session.

# RTPProxy control
route[RTPPROXY] {
#!ifdef WITH_NAT
        if (is_method("BYE")) {
                unforce_rtp_proxy();
        } else if (is_method("INVITE")){
                force_rtp_proxy();
        }
        if (!has_totag()) add_rr_param(";nat=yes");
#!endif
        return;
}

 Testing

This testing was done for the Kamailio 3.1 and the Rtpproxy 1.2.1

UAC behind nat without NAT traversal support on Kamailio

Usrloc record made for registration of the Client behind nat looks like:

AOR:: jan
                Contact:: sip:jan@10.0.2.15 Q=
                        Expires:: 3441
                        Callid:: zbkhwyxuwuthbgv@bell.netlab.kis.fri.uniza.sk
                        Cseq:: 224
                        User-agent:: Twinkle/1.4.2
                        State:: CS_SYNC
                        Flags:: 0
                        Cflag:: 0
                        Socket:: udp:158.193.139.51:5060
                        Methods:: 6111

 the REGISTER method received from the UAC behind NAT contain private addresses. The registration process was sucessfull

REGISTER sip:ps.sip.uniza.sk SIP/2.0.
Via: SIP/2.0/UDP 10.0.2.15;rport;branch=z9hG4bKwyecvlbs.
Max-Forwards: 70.
To: "jan" <sip:jan@ps.sip.uniza.sk>.
From: "jan" <sip:jan@ps.sip.uniza.sk>;tag=hxwkx.
Call-ID: zbkhwyxuwuthbgv@bell.netlab.kis.fri.uniza.sk.
CSeq: 224 REGISTER.
Contact: <sip:jan@10.0.2.15>;expires=3600.
Authorization: Digest username="jan",realm="ps.sip.uniza.sk",nonce="TPiqzUz4qaGMhfAopOhr80LB7LZ22p1r",uri="sip:ps.sip.uniza.sk",response="b18c53a210525abb334cff1f28cc0fe7",algorithm=MD5.
Allow: INVITE,ACK,BYE,CANCEL,OPTIONS,PRACK,REFER,NOTIFY,SUBSCRIBE,INFO,MESSAGE.
User-Agent: Twinkle/1.4.2.
Content-Length: 0.
.


U 158.193.139.51:5060 -> 158.193.139.235:60361
SIP/2.0 200 OK.
Via: SIP/2.0/UDP 10.0.2.15;rport=60361;branch=z9hG4bKwyecvlbs;received=158.193.139.235.
To: "jan" <sip:jan@ps.sip.uniza.sk>;tag=f11c829fa10fd0f1cba4621773c131eb.1e7c.
From: "jan" <sip:jan@ps.sip.uniza.sk>;tag=hxwkx.
Call-ID: zbkhwyxuwuthbgv@bell.netlab.kis.fri.uniza.sk.
CSeq: 224 REGISTER.
Contact: <sip:jan@10.0.2.15>;expires=3600.
Server: kamailio (3.1.0 (x86_64/linux)).
Content-Length: 0. 

UAC behind nat with NAT traversal support on Kamailio

Now we will start the kamailio with NAT support and we will observe the changes.

The Usrloc is changed a  little be, Received parameter was added and the Cflag is changed:

AOR:: jan
                Contact:: sip:jan@10.0.2.15 Q=
                        Expires:: 3586
                        Callid:: zbkhwyxuwuthbgv@bell.netlab.kis.fri.uniza.sk
                        Cseq:: 234
                        User-agent:: Twinkle/1.4.2
                        Received:: sip:158.193.139.235:62055                         State:: CS_NEW
                        Flags:: 0
                        Cflag:: 192
                        Socket:: udp:158.193.139.51:5060
                        Methods:: 6111

During of the register message exchange, inside of the 200 Ok message the received parameter is inserted

REGISTER sip:ps.sip.uniza.sk SIP/2.0.
Via: SIP/2.0/UDP 10.0.2.15;rport;branch=z9hG4bKskrtfzbd.
Max-Forwards: 70.
To: "jan" <sip:jan@ps.sip.uniza.sk>.
From: "jan" <sip:jan@ps.sip.uniza.sk>;tag=dohaq.
Call-ID: zbkhwyxuwuthbgv@bell.netlab.kis.fri.uniza.sk.
CSeq: 234 REGISTER.
Contact: <sip:jan@10.0.2.15>;expires=3600.
Authorization: Digest username="jan",realm="ps.sip.uniza.sk",nonce="TPitRkz4rBoh5k2qVS7QRyL9FjFXuI1Y",uri="sip:ps.sip.uniza.sk",response="d807146af45f4ce248738f6b7fe4d411",algorithm=MD5.
Allow: INVITE,ACK,BYE,CANCEL,OPTIONS,PRACK,REFER,NOTIFY,SUBSCRIBE,INFO,MESSAGE.
User-Agent: Twinkle/1.4.2.
Content-Length: 0.
.


U 158.193.139.51:5060 -> 158.193.139.235:62055
SIP/2.0 200 OK.
Via: SIP/2.0/UDP 10.0.2.15;rport=62055;branch=z9hG4bKskrtfzbd;received=158.193.139.235.
To: "jan" <sip:jan@ps.sip.uniza.sk>;tag=f11c829fa10fd0f1cba4621773c131eb.36d7.
From: "jan" <sip:jan@ps.sip.uniza.sk>;tag=dohaq.
Call-ID: zbkhwyxuwuthbgv@bell.netlab.kis.fri.uniza.sk.
CSeq: 234 REGISTER.
Contact: <sip:jan@10.0.2.15>;expires=3600;received="sip:158.193.139.235:62055".
Server: kamailio (3.1.0 (x86_64/linux)).
Content-Length: 0.

Registration was sucesfull (as usually).

Call from public UA

The INVITE message arrived to the server, signalling works, media does't

T 158.193.139.192:49260 -> 158.193.139.51:5060 [AP]
INVITE sip:jan@ps.sip.uniza.sk SIP/2.0.
Via: SIP/2.0/TCP 158.193.139.192:49918;branch=z9hG4bK-d8754z-975d22393e6c4702-1---d8754z-;rport.
Max-Forwards: 70.
Contact: <sip:jojo@158.193.139.192:49260;transport=TCP>.
To: <sip:jan@ps.sip.uniza.sk>.
From: "jojo"<sip:jojo@ps.sip.uniza.sk>;tag=73203c66.
Call-ID: N2M1MTc4MzAyNjI4ZTViZTA1NDE5MjAzZDc4ZWU3NjA..
CSeq: 2 INVITE.
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO.
Content-Type: application/sdp.
Proxy-Authorization: Digest username="jojo",realm="ps.sip.uniza.sk",nonce="TPizQUz4shXsqLjd3BfmYyVrrljigrVn",uri="sip:jan@ps.sip.uniza.sk",response="60829a1aaccdd15f8d183c3f98f32277",algorithm=MD5.
Supported: replaces.
User-Agent: Bria Professional release 2.4 stamp 49381.
Content-Length: 451.
.
v=0.
o=- 5 2 IN IP4 158.193.139.192.
s=CounterPath Bria Professional.
c=IN IP4 158.193.139.192.
t=0 0.
m=audio 27460 RTP/AVP 107 119 100 106 0 98 8 18 101.
a=fmtp:18 annexb=yes.
a=fmtp:101 0-15.
a=rtpmap:107 BV32/16000.
a=rtpmap:119 BV32-FEC/16000.
a=rtpmap:100 SPEEX/16000.
a=rtpmap:106 SPEEX-FEC/16000.
a=rtpmap:98 iLBC/8000.
a=rtpmap:18 G729/8000.
a=rtpmap:101 telephone-event/8000.
a=sendrecv.
a=x-rtp-session-id:8767743075B64036B830760C208977F5.

INVITE is redirected to the UA behind a NAT and is sent to 158.193.139.235:62055

U 158.193.139.51:5060 -> 158.193.139.235:62055
INVITE sip:jan@10.0.2.15 SIP/2.0.
Record-Route: <sip:158.193.139.51;r2=on;lr=on;nat=yes>.
Record-Route: <sip:158.193.139.51;transport=tcp;r2=on;lr=on;nat=yes>.
Via: SIP/2.0/UDP 158.193.139.51;branch=z9hG4bK45bf.d8da62b.0;i=1.
Via: SIP/2.0/TCP 158.193.139.192:49918;branch=z9hG4bK-d8754z-975d22393e6c4702-1---d8754z-;rport=49260.
Max-Forwards: 69.
Contact: <sip:jojo@158.193.139.192:49260;transport=TCP>.
To: <sip:jan@ps.sip.uniza.sk>.
From: "jojo"<sip:jojo@ps.sip.uniza.sk>;tag=73203c66.
Call-ID: N2M1MTc4MzAyNjI4ZTViZTA1NDE5MjAzZDc4ZWU3NjA..
CSeq: 2 INVITE.
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO.
Content-Type: application/sdp.
Supported: replaces.
User-Agent: Bria Professional release 2.4 stamp 49381.
Content-Length: 451.
.
v=0.
o=- 5 2 IN IP4 158.193.139.192.
s=CounterPath Bria Professional.
c=IN IP4 158.193.139.192.
t=0 0.
m=audio 27460 RTP/AVP 107 119 100 106 0 98 8 18 101.
a=fmtp:18 annexb=yes.
a=fmtp:101 0-15.
a=rtpmap:107 BV32/16000.
a=rtpmap:119 BV32-FEC/16000.
a=rtpmap:100 SPEEX/16000.
a=rtpmap:106 SPEEX-FEC/16000.
a=rtpmap:98 iLBC/8000.
a=rtpmap:18 G729/8000.
a=rtpmap:101 telephone-event/8000.
a=sendrecv.
a=x-rtp-session-id:8767743075B64036B830760C208977F5.

200 Ok came to the server from the client behind NAT

U 158.193.139.235:62055 -> 158.193.139.51:5060
SIP/2.0 200 OK.
Via: SIP/2.0/UDP 158.193.139.51;branch=z9hG4bK45bf.d8da62b.0;i=1,SIP/2.0/TCP 158.193.139.192:49918;rport=49260;branch=z9hG4bK-d8754z-975d22393e6c4702-1---d8754z-.
Record-Route: <sip:158.193.139.51;r2=on;lr=on;nat=yes>,<sip:158.193.139.51;transport=tcp;r2=on;lr=on;nat=yes>.
To: <sip:jan@ps.sip.uniza.sk>;tag=helfk.
From: "jojo" <sip:jojo@ps.sip.uniza.sk>;tag=73203c66.
Call-ID: N2M1MTc4MzAyNjI4ZTViZTA1NDE5MjAzZDc4ZWU3NjA..
CSeq: 2 INVITE.
Contact: <sip:jan@10.0.2.15>.
Content-Type: application/sdp.
Allow: INVITE,ACK,BYE,CANCEL,OPTIONS,PRACK,REFER,NOTIFY,SUBSCRIBE,INFO,MESSAGE.
Server: Twinkle/1.4.2.
Supported: replaces,norefersub.
Content-Length: 192.
.
v=0.
o=twinkle 63212600 546185878 IN IP4 10.0.2.15.
s=-.
c=IN IP4 10.0.2.15.
t=0 0.
m=audio 8000 RTP/AVP 100 101.
a=rtpmap:100 speex/16000.
a=rtpmap:101 telephone-event/8000.
a=fmtp:101 0-15. 

200 OK is redirected to the calling public UA, contact is rewrited, SDP is not

T 158.193.139.51:5060 -> 158.193.139.192:49260 [AP]
SIP/2.0 200 OK.
Via: SIP/2.0/TCP 158.193.139.192:49918;rport=49260;branch=z9hG4bK-d8754z-975d22393e6c4702-1---d8754z-.
Record-Route: <sip:158.193.139.51;r2=on;lr=on;nat=yes>,<sip:158.193.139.51;transport=tcp;r2=on;lr=on;nat=yes>.
To: <sip:jan@ps.sip.uniza.sk>;tag=helfk.
From: "jojo" <sip:jojo@ps.sip.uniza.sk>;tag=73203c66.
Call-ID: N2M1MTc4MzAyNjI4ZTViZTA1NDE5MjAzZDc4ZWU3NjA..
CSeq: 2 INVITE.
Contact: <sip:jan@158.193.139.235:62055>.
Content-Type: application/sdp.
Allow: INVITE,ACK,BYE,CANCEL,OPTIONS,PRACK,REFER,NOTIFY,SUBSCRIBE,INFO,MESSAGE.
Server: Twinkle/1.4.2.
Supported: replaces,norefersub.
Content-Length: 192.
.
v=0.
o=twinkle 63212600 546185878 IN IP4 10.0.2.15.
s=-.
c=IN IP4 10.0.2.15.
t=0 0.
m=audio 8000 RTP/AVP 100 101.
a=rtpmap:100 speex/16000.
a=rtpmap:101 telephone-event/8000.
a=fmtp:101 0-15.

Troubleshooting

The usage of the rtpproxy 1.2.1 leads to some problems, described next. I reccommend, as the working solution, to install rtpproxy 1.1.2 from the main debian repository.

rtpproxy 1.2.1 built from the git is unusable
  1. First of all, rtpproxy 1.2.1 built from the git will not start as the service, Caused that /etc/init.d/rtpproxy script is pointing to other folder where rtpproxy binary is not. Script is pointing to the /usr/sbin, but after compilation rtpproxy is in /usr/local/bin.
  2. If we change it, the init.d script/rtpproxy will find and start the proxy server, but the rtpproxy server when is contacting by the kamailio fell with segmentation fault error
Dec  3 11:29:23 pstest /usr/sbin/kamailio[5567]: ERROR: <core> [tcp_read.c:882]: ERROR: tcp_read_req: error reading
Dec  3 11:29:34 pstest kernel: [1285797.388217] rtpproxy[5529]: segfault at 0 ip 409468 sp 7fffe8208d70 error 4 in rtpproxy[400000+f000]

Solution

Go back to the standard rtpproxy debian package version 1.1.2, installed with

apt-get install rtpproxy

and change /etc/default/rtpproxy to

# Defaults for rtpproxy

# The control socket.
#CONTROL_SOCK="unix:/var/run/rtpproxy/rtpproxy.sock"
# To listen on an UDP socket, uncomment this line:

LISTEN_ADDR=158.193.139.51
CONTROL_SOCK="udp:localhost:22222"

# Additional options that are passed to the daemon.
EXTRA_OPTS="-l ${LISTEN_ADDR}"

Setup correctly and restart kamailio...it work now!!!!

 

log tutorial http://www.kamailio.org/dokuwiki/doku.php/tutorials:debug-syslog-messages

 

 

Groups: