lunes, 20 de agosto de 2012

Configuracion de Kamailio 3.3 con NAT Traversal y XCAP.

En esta entrada les ofrecere un ejemplo de configuracion de kamailio 3.3 con NAT traversal y XCAP server.
Si aun no sabes como funciona una red basada en SIP o un servidor SIP como Kamailio, primero deberias leer la anterior entrada para comprender mejor el funcionamiento de Kamailio.

Para seguir este tutorial es necesario tener un servidor conectado a internet con IP publica y con SO Linux.
En kamailio.org/wiki pueden encontrar diferentes guias de instalacion para muchos SO, aunque yo les explicare aqui como instalar kamailio en Ubuntu 12.04.

Instalacion de Kamailio en Ubuntu 12.04 

  • Abrimos un terminal.
  • Importamos la llave del repositorio
wget http://deb.kamailio.org/kamailiodebkey.gpg
apt-key add kamailiodebkey.gpg                                                             

  • Editamos /etc/apt/sources.list y añadimos al final
deb http://deb.kamailio.org/kamailio precise main
deb-src http://deb.kamailio.org/kamailio precise main

  • Ejecutamos sudo apt-get update y para buscar todos los paquetes de kamailio ejecutamos sudo apt-cache search kamailio
  • En este tutorial utilizaremos kamailio y MySQL como base de datos para el registrar y el servidor XCAP. Ejecutamos sudo apt-get install kamailio kamailio-mysql-modules kamailio-tls-modules
  • Instalamos mysql-server 
sudo apt-get install mysql-server
  • Para atravesar un NAT simetrico, los mas comunes,  utilizaremos rtpproxy.
sudo apt-get install rtpproxy

  • Editamos el archivo /etc/default/rtpproxy y lo dejamos de esta forma:
# Defaults for rtpproxy

# The control socket.
CONTROL_SOCK="udp:127.0.0.1:7722"

# Additional options that are passed to the daemon.
EXTRA_OPTS="-l your_public_ip -m 35000 -M 65000 -d DBUG:LOG_LOCAL5 -F"
Con estos parametros le estamos indicando que utilizara los puertos del 35000 al 65000, y tendremos que abrir ese rango de puertos si estan bloqueados.
  • Ejecutamos el siguiente comando para iniciar el rtpproxy.
sudo /etc/init.d/rtproxy restart
Configuracion de Kamailio 3.3

  • Editamos /etc/default/kamailio:
RUN_KAMAILIO=yes

  • Editamos /etc/kamailio/kamctlrc
SIP_DOMAIN=_your_server_ip_or_hostname_ puede ser localhost
DBENGINE=MYSQL
y quitamos los comentarios a DBHOST, DBNAME, DBRWUSER y DBRWPW.


  • Creamos la base de datos que necesita Kamailio, para ello ejecutamos el siguiente script, introducimos la contraseña del root de MySQL y le respondemos "yes" a todo. Este script crea los usuarios DBRWUSER y DBROUSER en MySQL, crea la base de datos openser y todas las tablas necesarias para el funcionamiento de los diferentes modulos.
kamdbctl create

  • Creamos dos usuarios diferentes de la siguiente forma. 
kamctl add userid password
kamctl add bob bob123
kamctl add alice alice123

  • Ahora vamos a editar el archivo de configuracion de Kamailio, el verdadero "cerebro" de nuestro servidor SIP. Este archivo se ejecuta en cada peticion que hagamos al servidor. Primero vamos a hacer una copia de seguridad de nuestro arhivo de configuracion. 
sudo mv /etc/kamailio/kamailio.cfg /etc/kamailio/kamailio.cfg.save

  • Despues de hacer la copia de seguridad borramos el archivo de configuracion
sudo rm -f /etc/kamailio/kamailio.cfg

  • Editamos un nuevo archivo de configuracion, podemos hacerlo con cualquier programa de edicion de texto o en el terminal podemos utilizar nano.
sudo nano /etc/kamailio/kamailio.cfg
y copiamos lo siguiente con CTRL+ MAYUS+V si estamos en la terminal. OJO!!! Antes de copiarlo debemos asegurarnos de cambiar las lineas escritas en rojo y fijarnos en las lineas escritas en verde.


#!KAMAILIO
#!define WITH_AUTH
#!define WITH_MYSQL
#!define WITH_USRLOCDB
##!define WITH_TLS /*Deshabilitado./
##!define WITH_DEBUG /*Deshabilitado para activarlo quita un simbolo #*/
#!define WITH_NAT
#!define WITH_PRESENCE
#!define WITH_XCAPSRV
# Kamailio (OpenSER) SIP Server v3.3 - default configuration script
#     - web: http://www.kamailio.org
#     - git: http://sip-router.org
#
# Direct your questions about this file to: <sr-users@lists.sip-router.org>
#
# Refer to the Core CookBook at http://www.kamailio.org/dokuwiki/doku.php
# for an explanation of possible statements, functions and parameters.
#
# Several features can be enabled using '#!define WITH_FEATURE' directives:
#
# *** To run in debug mode: 
#     - define WITH_DEBUG
#
# *** To enable mysql: 
#     - define WITH_MYSQL
#
# *** To enable authentication execute:
#     - enable mysql
#     - define WITH_AUTH
#     - add users using 'kamctl'
#
# *** To enable IP authentication execute:
#     - enable mysql
#     - enable authentication
#     - define WITH_IPAUTH
#     - add IP addresses with group id '1' to 'address' table
#
# *** To enable persistent user location execute:
#     - enable mysql
#     - define WITH_USRLOCDB
#
# *** To enable presence server execute:
#     - enable mysql
#     - define WITH_PRESENCE
#
# *** 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
#
# *** To enable PSTN gateway routing execute:
#     - define WITH_PSTN
#     - set the value of pstn.gw_ip
#     - check route[PSTN] for regexp routing condition
#
# *** To enable database aliases lookup execute:
#     - enable mysql
#     - define WITH_ALIASDB
#
# *** To enable speed dial lookup execute:
#     - enable mysql
#     - define WITH_SPEEDDIAL
#
# *** To enable multi-domain support execute:
#     - enable mysql
#     - define WITH_MULTIDOMAIN
#
# *** To enable TLS support execute:
#     - adjust CFGDIR/tls.cfg as needed
#     - define WITH_TLS
#
# *** To enable XMLRPC support execute:
#     - define WITH_XMLRPC
#     - adjust route[XMLRPC] for access policy
#
# *** To enable anti-flood detection execute:
#     - adjust pike and htable=>ipban settings as needed (default is
#       block if more than 16 requests in 2 seconds and ban for 300 seconds)
#     - define WITH_ANTIFLOOD
#
# *** To block 3XX redirect replies execute:
#     - define WITH_BLOCK3XX
#
# *** To enable VoiceMail routing execute:
#     - define WITH_VOICEMAIL
#     - set the value of voicemail.srv_ip
#     - adjust the value of voicemail.srv_port
#
# *** To enhance accounting execute:
#     - enable mysql
#     - define WITH_ACCDB
#     - add following columns to database
#!ifdef ACCDB_COMMENT
  ALTER TABLE acc ADD COLUMN src_user VARCHAR(64) NOT NULL DEFAULT '';
  ALTER TABLE acc ADD COLUMN src_domain VARCHAR(128) NOT NULL DEFAULT '';
  ALTER TABLE acc ADD COLUMN src_ip varchar(64) NOT NULL default '';
  ALTER TABLE acc ADD COLUMN dst_ouser VARCHAR(64) NOT NULL DEFAULT '';
  ALTER TABLE acc ADD COLUMN dst_user VARCHAR(64) NOT NULL DEFAULT '';
  ALTER TABLE acc ADD COLUMN dst_domain VARCHAR(128) NOT NULL DEFAULT '';
  ALTER TABLE missed_calls ADD COLUMN src_user VARCHAR(64) NOT NULL DEFAULT '';
  ALTER TABLE missed_calls ADD COLUMN src_domain VARCHAR(128) NOT NULL DEFAULT '';
  ALTER TABLE missed_calls ADD COLUMN src_ip varchar(64) NOT NULL default '';
  ALTER TABLE missed_calls ADD COLUMN dst_ouser VARCHAR(64) NOT NULL DEFAULT '';
  ALTER TABLE missed_calls ADD COLUMN dst_user VARCHAR(64) NOT NULL DEFAULT '';
  ALTER TABLE missed_calls ADD COLUMN dst_domain VARCHAR(128) NOT NULL DEFAULT '';
#!endif

####### Defined Values #########

# *** Value defines - IDs used later in config
#!ifdef WITH_MYSQL
# - database URL - used to connect to database server by modules such
#       as: auth_db, acc, usrloc, a.s.o.
#!define DBURL "mysql://openser:openserrw@localhost/openser"
#!endif
#!ifdef WITH_MULTIDOMAIN
# - the value for 'use_domain' parameters
#!define MULTIDOMAIN 1
#!else
#!define MULTIDOMAIN 0
#!endif

# - 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

####### Global Parameters #########

#!ifdef WITH_DEBUG
debug=3
log_stderror=yes
#!else
debug=2
log_stderror=no
#!endif

memdbg=5
memlog=5

log_facility=LOG_LOCAL0

fork=yes
children=4

/* uncomment the next line to disable TCP (default on) */
#disable_tcp=yes

/* uncomment the next line to disable the auto discovery of local aliases
   based on reverse DNS on IPs (default on) */
#auto_aliases=no

/* add local domain aliases */ s

alias="your_public_ip or your domain"

/*alias es muy importante, si vas a utilizar varios dominios agrega varias lineas alias="domain1" ... se utiliza en if(uri==myself), remplazandose myself por alias*/ /* uncomment and configure the following line if you want Kamailio to bind on a specific interface/port/proto (default bind on all available) */ /*Si estas detras de un NAT estatico tal vez necesites utilizar advertise, para atravesar ese NAT, sino tal vez no hace falta. */

listen=private_ip advertise public_ip:port

/* port to listen to * - can be specified more than once if needed to listen on many ports */

port=5060

#!ifdef WITH_TLS enable_tls=yes #!endif # life time of TCP connection when there is no traffic # - a bit higher than registration expires to cope with UA behind NAT tcp_connection_lifetime=3605 ####### Custom Parameters ######### # These parameters can be modified runtime via RPC interface # - see the documentation of 'cfg_rpc' module. # # Format: group.id = value 'desc' description # Access: $sel(cfg_get.group.id) or @cfg_get.group.id # #!ifdef WITH_PSTN # PSTN GW Routing # # - pstn.gw_ip: valid IP or hostname as string value, example: # pstn.gw_ip = "10.0.0.101" desc "My PSTN GW Address" # # - by default is empty to avoid misrouting pstn.gw_ip = "" desc "PSTN GW Address" #!endif #!ifdef WITH_VOICEMAIL # VoiceMail Routing on offline, busy or no answer # # - by default Voicemail server IP is empty to avoid misrouting voicemail.srv_ip = "" desc "VoiceMail IP Address" voicemail.srv_port = "5060" desc "VoiceMail Port" #!endif #!ifdef WITH_XCAPSRV tcp_accept_no_cl=yes #!endif ####### Modules Section ######## # set paths to location of modules (to sources or installation folders) #!ifdef WITH_SRCPATH mpath="modules_k:modules" #!else

/*Asegurate de que esta ruta sea valida, en este caso es un SO de 64 bits, si el tuyo es de 32 bits la ruta es diferente.*/

mpath="/usr/lib64/kamailio/modules_k/:/usr/lib64/kamailio/modules/"

#!endif #!ifdef WITH_MYSQL loadmodule "db_mysql.so" #!endif loadmodule "mi_fifo.so" loadmodule "kex.so" loadmodule "tm.so" loadmodule "tmx.so" loadmodule "sl.so" loadmodule "rr.so" loadmodule "pv.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "siputils.so" loadmodule "xlog.so" loadmodule "sanity.so" loadmodule "ctl.so" loadmodule "cfg_rpc.so" loadmodule "mi_rpc.so" loadmodule "acc.so" #!ifdef WITH_AUTH loadmodule "auth.so" loadmodule "auth_db.so" #!ifdef WITH_IPAUTH loadmodule "permissions.so" #!endif #!endif #!ifdef WITH_ALIASDB loadmodule "alias_db.so" #!endif #!ifdef WITH_SPEEDDIAL loadmodule "speeddial.so" #!endif #!ifdef WITH_MULTIDOMAIN loadmodule "domain.so" #!endif #!ifdef WITH_PRESENCE loadmodule "presence.so" loadmodule "presence_xml.so" #!endif #!ifdef WITH_NAT loadmodule "nathelper.so" loadmodule "rtpproxy.so" #!endif #!ifdef WITH_TLS loadmodule "tls.so" #!endif #!ifdef WITH_ANTIFLOOD loadmodule "htable.so" loadmodule "pike.so" #!endif #!ifdef WITH_XMLRPC loadmodule "xmlrpc.so" #!endif #!ifdef WITH_DEBUG loadmodule "debugger.so" #!endif #!ifdef WITH_XCAPSRV loadmodule "xhttp.so" loadmodule "xcap_server.so" #!endif #!ifdef WITH_MSILO loadmodule "msilo.so" #!endif # ----------------- setting module-specific parameters --------------- # ----- mi_fifo params ----- modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") # ----- tm params ----- # auto-discard branches from previous serial forking leg modparam("tm", "failure_reply_mode", 3) # default retransmission timeout: 30sec modparam("tm", "fr_timer", 30000) # default invite retransmission timeout after 1xx: 120sec modparam("tm", "fr_inv_timer", 120000) # ----- rr params ----- # add value to ;lr param to cope with most of the UAs modparam("rr", "enable_full_lr", 1) # do not append from tag to the RR (no need for this script) modparam("rr", "append_fromtag", 0) # ----- registrar params ----- modparam("registrar", "method_filtering", 1) /* uncomment the next line to disable parallel forking via location */ # modparam("registrar", "append_branches", 0) /* uncomment the next line not to allow more than 10 contacts per AOR */ #modparam("registrar", "max_contacts", 10) # max value for expires of registrations modparam("registrar", "max_expires", 3600) # set it to 1 to enable GRUU modparam("registrar", "gruu_enabled", 0) # ----- acc params ----- /* what special events should be accounted ? */ modparam("acc", "early_media", 0) modparam("acc", "report_ack", 0) modparam("acc", "report_cancels", 0) /* by default ww do not adjust the direct of the sequential requests. if you enable this parameter, be sure the enable "append_fromtag" in "rr" module */ modparam("acc", "detect_direction", 0) /* account triggers (flags) */ modparam("acc", "log_flag", FLT_ACC) modparam("acc", "log_missed_flag", FLT_ACCMISSED) modparam("acc", "log_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_ouser=$tU;dst_user=$rU;dst_domain=$rd") modparam("acc", "failed_transaction_flag", FLT_ACCFAILED) /* enhanced DB accounting */ #!ifdef WITH_ACCDB modparam("acc", "db_flag", FLT_ACC) modparam("acc", "db_missed_flag", FLT_ACCMISSED) modparam("acc", "db_url", DBURL) modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_ouser=$tU;dst_user=$rU;dst_domain=$rd") #!endif # ----- usrloc params ----- /* enable DB persistency for location entries */ #!ifdef WITH_USRLOCDB modparam("usrloc", "db_url", DBURL) modparam("usrloc", "db_mode", 2) modparam("usrloc", "use_domain", MULTIDOMAIN) #!endif # ----- auth_db params ----- #!ifdef WITH_AUTH modparam("auth_db", "db_url", DBURL) modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "password_column", "password") modparam("auth_db", "load_credentials", "") modparam("auth_db", "use_domain", MULTIDOMAIN) # ----- permissions params ----- #!ifdef WITH_IPAUTH modparam("permissions", "db_url", DBURL) modparam("permissions", "db_mode", 1) #!endif #!endif # ----- alias_db params ----- #!ifdef WITH_ALIASDB modparam("alias_db", "db_url", DBURL) modparam("alias_db", "use_domain", MULTIDOMAIN) #!endif # ----- speedial params ----- #!ifdef WITH_SPEEDDIAL modparam("speeddial", "db_url", DBURL) modparam("speeddial", "use_domain", MULTIDOMAIN) #!endif # ----- domain params ----- #!ifdef WITH_MULTIDOMAIN modparam("domain", "db_url", DBURL) # use caching modparam("domain", "db_mode", 1) # register callback to match myself condition with domains list modparam("domain", "register_myself", 1) #!endif #!ifdef WITH_PRESENCE # ----- presence params ----- modparam("presence", "db_url", DBURL) # ----- presence_xml params ----- modparam("presence_xml", "db_url", DBURL) modparam("presence_xml", "force_active", 1) #!endif #!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@your_public_ip") # params needed for NAT traversal in other modules modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)") modparam("usrloc", "nat_bflag", FLB_NATB) #!endif #!ifdef WITH_TLS # ----- tls params ----- modparam("tls", "config", "/etc/kamailio/tls.cfg") #!endif #!ifdef WITH_ANTIFLOOD # ----- pike params ----- modparam("pike", "sampling_time_unit", 2) modparam("pike", "reqs_density_per_unit", 16) modparam("pike", "remove_latency", 4) # ----- htable params ----- # ip ban htable with autoexpire after 5 minutes modparam("htable", "htable", "ipban=>size=8;autoexpire=300;") #!endif #!ifdef WITH_XMLRPC # ----- xmlrpc params ----- modparam("xmlrpc", "route", "XMLRPC"); modparam("xmlrpc", "url_match", "^/RPC") #!endif #!ifdef WITH_DEBUG # ----- debugger params ----- modparam("debugger", "cfgtrace", 1) #!endif #!ifdef WITH_XCAPSRV # ----- xcap_server params ----- modparam("xcap_server", "db_url", DBURL) #!endif /*Este modulo es opcional, se utiliza para guardar mensajes en la base de datos si el usuario esta offline, y se lo envia cuando vuelva a estar en linea, para activarlo debes escribir al principio #!define WITH_MSILO y cambiar las siguientes lineas*/ #!ifdef WITH_MSILO # ------ msilo params ------ modparam("msilo","db_url",DBURL) modparam("msilo","from_address","sip:registrar@your_public_ip") #modparam("msilo","contact_hdr","Contact: <sip:registrar@your_public_ip>\r\n") modparam("msilo","content_type_hdr","Content-Type: text/plain\r\n") modparam("msilo","offline_message","*** User $rU is offline!") #modparam("msilo", "check_time", 10) #!endif ####### Routing Logic ######## # Main SIP request routing logic # - processing of any incoming SIP request starts with this route # - note: this is the same as route { ... } request_route { # per request initial checks route(REQINIT); # NAT detection route(NATDETECT); # handle requests within SIP dialogs route(WITHINDLG); ### only initial requests (no To tag) # CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); # authentication route(AUTH); # record routing for dialog forming requests (in case they are routed) # - remove preloaded route headers remove_hf("Route"); if (is_method("INVITE|SUBSCRIBE")) { record_route(); } # account only INVITEs if (is_method("INVITE")) { setflag(FLT_ACC); # do accounting } # dispatch requests to foreign domains route(SIPOUT); ### requests for my local domains # handle presence related requests route(PRESENCE); # handle registrations route(REGISTRAR); if ($rU==$null) { # request with no Username in RURI sl_send_reply("484","Address Incomplete"); exit; } # dispatch destinations to PSTN route(PSTN); # user location service route(LOCATION); route(RELAY); } route[RELAY] { # enable additional event routes for forwarded requests # - serial forking, RTP relaying handling, a.s.o. if (is_method("INVITE|SUBSCRIBE")) { t_on_branch("MANAGE_BRANCH"); t_on_reply("MANAGE_REPLY"); } if (is_method("INVITE")) { t_on_failure("MANAGE_FAILURE"); } if (!t_relay()) { sl_reply_error(); } exit; } # Per SIP request initial checks route[REQINIT] { #!ifdef WITH_ANTIFLOOD # flood dection from same IP and traffic ban for a while # be sure you exclude checking trusted peers, such as pstn gateways # - local host excluded (e.g., loop to self) if(src_ip!=myself) { if($sht(ipban=>$si)!=$null) { # ip is already blocked xdbg("request from blocked IP - $rm from $fu (IP:$si:$sp)\n"); exit; } if (!pike_check_req()) { xlog("L_ALERT","ALERT: pike blocking $rm from $fu (IP:$si:$sp)\n"); $sht(ipban=>$si) = 1; exit; } } #!endif if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if(!sanity_check("1511", "7")) { xlog("Malformed SIP message from $si:$sp\n"); exit; } } # Handle requests within SIP dialogs route[WITHINDLG] { if (has_totag()) { # sequential request withing a dialog should # take the path determined by record-routing if (loose_route()) { if (is_method("BYE")) { setflag(FLT_ACC); # do accounting ... setflag(FLT_ACCFAILED); # ... even if the transaction fails } if ( is_method("ACK") ) { # ACK is forwarded statelessy route(NATMANAGE); } route(RELAY); } else { if (is_method("SUBSCRIBE") && uri == myself) { # in-dialog subscribe requests route(PRESENCE); exit; } if ( is_method("ACK") ) { if ( t_check_trans() ) { # no loose-route, but stateful ACK; # must be an ACK after a 487 # or e.g. 404 from upstream server t_relay(); exit; } else { # ACK without matching transaction ... ignore and discard exit; } } sl_send_reply("404","Not here"); } exit; } } # 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(); #!ifdef WITH_MSILO m_dump(); #!endif exit; } } # USER location service route[LOCATION] { #!ifdef WITH_SPEEDIAL # search for short dialing - 2-digit extension if($rU=~"^[0-9][0-9]$") if(sd_lookup("speed_dial")) route(SIPOUT); #!endif #!ifdef WITH_ALIASDB # search in DB-based aliases if(alias_db_lookup("dbaliases")) route(SIPOUT); #!endif #!ifdef WITH_MSILO if(is_method("MESSAGE")){ route(MSILO); exit; } #!endif $avp(oexten) = $rU; if (!lookup("location")) { $var(rc) = $rc; route(TOVOICEMAIL); t_newtran(); switch ($var(rc)) { case -1: case -3: send_reply("404", "Not Found"); exit; case -2: send_reply("405", "Method Not Allowed"); exit; } } # when routing via usrloc, log the missed calls also if (is_method("INVITE")) { setflag(FLT_ACCMISSED); } } # Presence server route route[PRESENCE] { if(!is_method("PUBLISH|SUBSCRIBE")) return; #!ifdef WITH_PRESENCE if (!t_newtran()) { sl_reply_error(); exit; }; if(is_method("PUBLISH")) { handle_publish(); t_release(); } else if( is_method("SUBSCRIBE")) { handle_subscribe(); t_release(); } exit; #!endif # if presence enabled, this part will not be executed if (is_method("PUBLISH") || $rU==$null) { sl_send_reply("404", "Not here"); exit; } return; } # Authentication route route[AUTH] { #!ifdef WITH_AUTH #!ifdef WITH_IPAUTH if((!is_method("REGISTER")) && allow_source_address()) { # source IP allowed return; } #!endif #!ifdef WITH_MSILO if(is_method("MESSAGE")){ return; } #!endif if ((is_method("REGISTER") || from_uri==myself)) { # authenticate requests if (!auth_check("$fd", "subscriber","1")) { auth_challenge("$fd", "0"); exit; } # user authenticated - remove auth header if(!is_method("REGISTER|PUBLISH")) consume_credentials(); } # if caller is not local subscriber, then check if it calls # a local destination, otherwise deny, not an open relay here if (from_uri!=myself && uri!=myself) { sl_send_reply("403","Not relaying"); exit; } #!endif return; } # Caller NAT detection route route[NATDETECT] { #!ifdef WITH_NAT force_rport(); setflag(FLT_NATS); if (nat_uac_test("19")) { if (is_method("REGISTER")) { fix_nated_register(); } else { fix_nated_contact(); } setflag(FLT_NATS); } #!endif return; } # RTPProxy control route[NATMANAGE] { #!ifdef WITH_NAT if (is_request()) { if(has_totag()) { if(check_route_param("nat=yes")) { setbflag(FLB_NATB); } } } if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB))) return; rtpproxy_manage(); if (is_request()) { if (!has_totag()) { add_rr_param(";nat=yes"); } } if (is_reply()) { if(isbflagset(FLB_NATB)) { fix_nated_contact(); } } #!endif return; } # Routing to foreign domains route[SIPOUT] { if (!uri==myself) { append_hf("P-hint: outbound\r\n"); route(RELAY); } } # PSTN GW routing route[PSTN] { #!ifdef WITH_PSTN # check if PSTN GW IP is defined if (strempty($sel(cfg_get.pstn.gw_ip))) { xlog("SCRIPT: PSTN rotuing enabled but pstn.gw_ip not defined\n"); return; } # route to PSTN dialed numbers starting with '+' or '00' # (international format) # - update the condition to match your dialing rules for PSTN routing if(!($rU=~"^(\+|00)[1-9][0-9]{3,20}$")) return; # only local users allowed to call if(from_uri!=myself) { sl_send_reply("403", "Not Allowed"); exit; } $ru = "sip:" + $rU + "@" + $sel(cfg_get.pstn.gw_ip); route(RELAY); exit; #!endif return; } # XMLRPC routing #!ifdef WITH_XMLRPC route[XMLRPC] { # allow XMLRPC from localhost if ((method=="POST" || method=="GET") && (src_ip==127.0.0.1)) { # close connection only for xmlrpclib user agents (there is a bug in # xmlrpclib: it waits for EOF before interpreting the response). if ($hdr(User-Agent) =~ "xmlrpclib") set_reply_close(); set_reply_no_connect(); dispatch_rpc(); exit; } send_reply("403", "Forbidden"); exit; } #!endif # route to voicemail server route[TOVOICEMAIL] { #!ifdef WITH_VOICEMAIL if(!is_method("INVITE")) return; # check if VoiceMail server IP is defined if (strempty($sel(cfg_get.voicemail.srv_ip))) { xlog("SCRIPT: VoiceMail rotuing enabled but IP not defined\n"); return; } if($avp(oexten)==$null) return; $ru = "sip:" + $avp(oexten) + "@" + $sel(cfg_get.voicemail.srv_ip) + ":" + $sel(cfg_get.voicemail.srv_port); route(RELAY); exit; #!endif return; } # manage outgoing branches branch_route[MANAGE_BRANCH] { xdbg("new branch [$T_branch_idx] to $ru\n"); route(NATMANAGE); } # manage incoming replies onreply_route[MANAGE_REPLY] { xdbg("incoming reply\n"); if(status=~"[12][0-9][0-9]") route(NATMANAGE); } #!ifdef WITH_MSILO #manage failure message failure_route[MANAGE_FAILURE_MESSAGE] { if (!method=="MESSAGE") { exit; } log(1,"MSILO:the downstream UA doesn't support MESSAGEs\n"); # we have changed the R-URI with the contact address, ignore it now if (m_store("$ou")) { t_reply("202", "Accepted"); }else{ t_reply("503", "Service Unavailable"); } } #!endif # manage failure routing cases failure_route[MANAGE_FAILURE] { route(NATMANAGE); if (t_is_canceled()) { exit; } #!ifdef WITH_BLOCK3XX # block call redirect based on 3xx replies. if (t_check_status("3[0-9][0-9]")) { t_reply("404","Not found"); exit; } #!endif #!ifdef WITH_VOICEMAIL # serial forking # - route to voicemail on busy or no answer (timeout) if (t_check_status("486|408")) { route(TOVOICEMAIL); exit; } #!endif } #!ifdef WITH_XCAPSRV #!define WITH_XHTTPAUTH event_route[xhttp:request] { xdbg("===== xhttp: request [$rv] $rm => $hu\n"); if (!www_authorize("xcap", "subscriber")) { www_challenge("xcap", "0"); exit; } if($hu=~"^/xcap-root/") { set_reply_close(); set_reply_no_connect(); # xcap ops - break down http uri to get xcap user id $xcapuri(u=>data) = $hu; if($xcapuri(u=>xuid)=~"^sip:.+@.+") $var(uri) = $xcapuri(u=>xuid); else $var(uri) = "sip:"+ $xcapuri(u=>xuid) + "@" + $Ri; xlog("===== xhttp: $xcapuri(u=>auid) : $xcapuri(u=>xuid)\n"); # handle XCAP capability request if($rm=="GET" && $xcapuri(u=>auid)=="xcap-caps") { $var(xbody) = "<?xml version='1.0' encoding='UTF-8'?> <xcap-caps xmlns='urn:ietf:params:xml:ns:xcap-caps'> <auids> <auid>rls-services</auid> <auid>pidf-manipulation</auid> <auid>xcap-caps</auid> <auid>resource-lists</auid> <auid>pres-rules</auid> <auid>org.openmobilealliance.pres-rules</auid> </auids> <extensions> </extensions> <namespaces> <namespace>urn:ietf:params:xml:ns:rls-services</namespace> <namespace>urn:ietf:params:xml:ns:pidf</namespace> <namespace>urn:ietf:params:xml:ns:xcap-caps</namespace> <namespace>urn:ietf:params:xml:ns:resource-lists</namespace> <namespace>urn:ietf:params:xml:ns:pres-rules</namespace> </namespaces> </xcap-caps>"; xhttp_reply("200", "ok", "application/xcap-caps+xml", "$var(xbody)"); exit; } # be sure auth user access only its documents if ($au!=$(var(uri){uri.user})) { xhttp_reply("403", "Forbidden", "text/html", "operation not allowed"); exit; } xdbg("SCRIPT: xcap service $xcapuri(u=>auid) for $xcapuri(u=>xuid)\n"); switch($rm) { case "PUT": xcaps_put("$var(uri)", "$hu", "$rb"); if($xcapuri(u=>auid)=~"pres-rules") { xlog("===== xhttp put: refreshing watchers for $var(uri)\n"); pres_update_watchers("$var(uri)", "presence"); pres_refresh_watchers("$var(uri)", "presence", 1); } exit; break; case "GET": xlog("===== xhttp: get $var(uri) => $hu\n"); xcaps_get("$var(uri)", "$hu"); exit; break; case "DELETE": xcaps_del("$var(uri)", "$hu"); if($xcapuri(u=>auid)=~"pres-rules") { xlog("===== xhttp del: refreshing watchers for $var(uri)\n"); pres_update_watchers("$var(uri)", "presence"); pres_refresh_watchers("$var(uri)", "presence", 1); } exit; break; } } # other http requests xhttp_reply("200", "OK", "text/html", "<html><body>OK: $si:$sp</body></html>"); exit; } #!endif route[MSILO]{ #!ifdef WITH_MSILO if(!is_method("MESSAGE")) return; if(!lookup("location")) { $var(rc)=$rc; if (! t_newtran()) { sl_reply_error(); exit; }; switch ($var(rc)) { case -1: case -3: if (m_store("$ru")) { if (!t_reply("202", "Accepted")) { sl_reply_error(); }; }else{ if (!t_reply("503", "Service Unavailable")) { sl_reply_error(); }; }; exit; case -2: t_on_failure("MANAGE_FAILURE_MESSAGE"); t_relay(); exit; } }; t_on_failure("MANAGE_FAILURE_MESSAGE"); t_relay(); exit; #!endif return; }
  • Ahora ejecutamos 
sudo /etc/init.d/kamailio restart 
y deberia funcionar, si ocurre algun error fijate en la salida y en la linea que te da el error. Si quieres depurar escribe al principio del archivo de configuracion #!define WITH_DEBUG.


  • Si queremos ver los dialogos SIP debemos ejecutar netstat -anp|grep 5060.
Podemos ver que todo funcione correctamente, descargando y configurando un Cliente SIP como JITSY para LINUX/WINDOWS, SIPDROID para Android. En Blackberry tenemos el VMOBILE.

Configurar un cliente sip es muy sencillo. Recordemos que tenemos 2 usuarios registrados bob y alice,

En username escribimos bob por ejemplo
Username: bob
PASSWORD: bob123
Domain: tu_ip_publica o dominio
Registrar: tu_ip_publica:5060
PROXY: Lo mismo que en domain o le damos a configurar proxy automaticamente si existe la opcion.
Puerto: 5060. 
Servidor XCAP: yourpublic_ip:5060/xcap-root (Si existe la opcion)

Registramos los 2 clientes, uno en el PC y otro en un Movil o en otro PC y ya podremos hacer llamadas y videollamadas, ademas de escribirnos mensajes.


Espero les haya sido de ayuda. 

Funcionamiento de redes basadas en SIP y Kamailio.

En esta entrada  les explicare el funcionamiento de las  redes basadas en SIP y de un servidor SIP de codigo abierto, Kamailio.



Características del protocolo SIP


SIP es un protocolo genérico de establecimiento de sesiones multimedia. El inicio de
la sesión, cambio o término de la misma, son independientes del tipo de medio o aplicación
que establece la llamada; una sesión puede incluir varios tipos de datos, incluyendo audio,
video y muchos otros formatos.

SIP es un protocolo de capa de aplicación, cuyo diseño permite una fácil
implementación y una buena escalabilidad y flexibilidad. Se encuentra disponible en su
versión 2.0 y está documentado a través del RFC 3261; el cual reemplaza a la versión
anterior (RFC2543).

SIP se complementa con otros protocolos tales como SDP (Sesion Description
Protocol) y RTP/RTCP (Real Time Protocol) para completar la comunicación. RTP/RTCP se
emplea para transportar los datos multimedia en tiempo real mientras que SDP se utiliza
para describir las características de los participantes de la sesión multimedia. Es un protocolo
orientado a conexiones End-to-End. Toda la lógica se encuentra almacenada en los
dispositivos finales (salvo el ruteo de mensajes).


Las funciones principales de SIP son:


  • Establecer, modificar y finalizar las sesiones entre dos o más participantes.
  • Registro y localización de participantes (Movilidad)
  • Gestión del conjunto de participantes y de los componentes del sistema.
  • Describir las características de las sesiones y negociar las capacidades de los participantes.

Algunas de sus características son:


  • Basado en Texto. (Similar a HTTP)
  • Uso de Identificador Universal de Recursos (URIs con esquemas sip, sips y tel)
  • Métodos básicos: INVITE, ACK, BYE, CANCEL, REGISTER, OPTIONS.
  • Los mensajes se agrupan en transacciones y llamadas (diálogos).
  • Las descripciones de sesiones multimedia (SDP) se encuentran en el cuerpo de los mensajes. 
  • Localización basada en DNS.

Protocolo SDP (Session Description Protocol)
Este protocolo fue diseñado para transportar información de la sesión/medios hacia los
destinatarios. Permite asociar más de un flujo de medios a una misma sesión (Audio y Video). Para
ello se establece una descripción y negociación de los parámetros de sesión a través de mensajes
SDP codificados como texto plano (ISO 10646 UTF-8) cuyos nombres de campo y atributos usan US-
ASCII pero los demás emplean 10646. Se eligió este formato para aumentar la “portabilidad” hacia
sistemas Web.

Protocolo RTP / RTCP (Real Time Protocol / Real Time Control Protocol)
El RFC 1889 se refiere a este protocolo como “Transporte y Monitoreo de Flujos en Tiempo
Real”. (Real Time Media Streaming).
Sus funciones principales son:

  • Identificación del tipo de carga útil transportada (Codecs de Audio/Video)
  • Verificación de la entrega de los paquetes en orden (Marca de tiempo) y si resulta necesario reordenar los bloques fuera de orden.
  • Transporte de información de sincronismo para la codificación y decodificación
  • Monitoreo de la entrega de la información.

Entidades que componen una red basada en SIP

A continuación se definen las diferentes entidades que componen una red basada en
protocolo SIP. Esto es importante para definir conceptos tenidos en cuenta en este proyecto.

  • Agentes de Usuario (User Agent)
  • Servidor Registrar
  • Servidor Redirect
  • Servidor Proxy
Agentes de Usuario (UA)
Se denominan Agentes de Usuario (UA: User Agent) a los terminales que utilizan SIP
para encontrar y negociar con otros terminales las características de la sesión.
Cada Agente de Usuario (UA) se compone lógicamente de dos entidades:
  •  Agente de Usuario Cliente (UAC)
  • Agente de Usuario Servidor (UAS)
El UAC es la parte del agente de usuario que genera peticiones y recibe respuestas a
esas peticiones. El UAS es la parte del agente de usuario que recibe peticiones y genera
respuestas.
Un agente de usuario se comporta como UAC o UAS dependiendo de la situación.
Por ejemplo un agente de usuario que realiza una llamada se comporta como UAC cuando
envía mensaje de INVITE y recibe las respuestas a ese pedido. Un agente de usuario llamado
se comporta como UAS cuando recibe el mensaje de INVITE y envía las respuestas. Pero esta
situación cambia cuando la parte llamada decide enviar un mensaje BYE para terminar la
sesión. En este caso el agente de usuario llamado se comporta como UAC (enviando un BYE)
y el agente de usuario llamante se comporta como UAS.

Servidor “Registrar”
Este elemento de Red posee la función de autenticar y validar la cuenta de un usuario contra
una base de datos interna o externa y “registrar” la localización actual del mismo. Este tipo de
servicio, la mayoría de las veces se implementa en forma conjunta con el servidor Proxy SIP.


Servidor “Proxy”
Son aplicaciones que reciben los pedidos de los clientes SIP e inician nuevas
peticiones hacia los agentes de usuario de destino o hacia otros servidores Proxy. Es decir,
actúan enrutando los mensajes SIP, en base a reglas predefinidas.
Los Proxy SIP tienen la habilidad de conocer varias rutas alternativas para localizar a
un agente de usuario y pueden intentar el proceso de bifurcación en forma secuencial o en
paralelo dependiendo de cómo este configurado el Proxy.

Servidor “Redirect”
Esta entidad que integra una red SIP escucha peticiones y regresa respuestas que contienen
la localización actual de un usuario en particular. Estas respuestas son mensajes SIP de clase 3XX.
El usuario o Proxy que realizó la petición original extrae la información de las respuestas y
envía otra petición “redirigida” en base al resultado de la búsqueda.

Mensajes, Transacciones y Diálogos SIP

Mensajes SIP
SIP es un protocolo textual que usa una semántica semejante a la del protocolo HTTP.
Como se vio anteriormente, los UAC realizan las peticiones y los UAS retornan respuestas a
las peticiones de los clientes. SIP define la comunicación a través de dos tipos de mensajes.
Las solicitudes (métodos) y las respuestas (códigos de estado) emplean el formato de
mensaje genérico establecido en el RFC 2822 , que consiste en una línea inicial seguida de un
o más campos de cabecera (headers), una línea vacía que indica el final de las cabeceras, y
por último, el cuerpo del mensaje que es opcional.
Peticiones, Solicitudes o Métodos SIP
Las peticiones SIP son caracterizadas por la línea inicial del mensaje, llamada
Request-Line, que contiene el nombre del método, el identificador del destinatario de la
petición (Request-URI) y la versión del protocolo SIP. Existen seis métodos básicos SIP que
describen las peticiones de los clientes:

 -INVITE: Permite invitar un usuario o servicio para participar en una sesión o para
modificar parámetros en una sesión ya existente.
- ACK: Confirma el establecimiento de una sesión.
- OPTION: Solicita información sobre las capacidades de un servidor.
- BYE: Indica la terminación de una sesión.
- CANCEL: Cancela una petición pendiente.
- REGISTER: Registrar al User Agent.
Sin embargo, existen otros métodos adicionales que pueden ser utilizados,
publicados en otros RFCs como los métodos INFO, SUBSCRIBER,etc.

Respuestas (Códigos de estado) SIP
Después de la recepción e interpretación del mensaje de solicitud SIP, el receptor del
mismo responde con un mensaje. Este mensaje, es similar al anterior, difiriendo en la línea
inicial, llamada Status-Line, que contiene la versión de SIP, el código de la respuesta (Status–
Code) y una pequeña descripción (Reason-Phrase). El código de la respuesta está compuesto
por tres dígitos que permiten clasificar los diferentes tipos existentes. El primer dígito define
la clase de la respuesta.
Código - Clases

1xx - Mensajes provisionales.
2xx - Respuestas de éxito.
3xx - Respuestas de redirección.
4xx - Respuestas de fallo de método.
5xx - Respuestas de fallos de servidor.
6xx - Respuestas de fallos globales.

Transacciones y Diálogos SIP
Una Transacción SIP es una secuencia de mensajes entre dos elementos de Red. Corresponde
a una petición y todas sus respuestas. Una transacción puede incluir cero o más respuestas
provisionales y una o más respuestas finales (INVITE puede ser dividido por un Proxy, por lo tanto
tendrá múltiples respuestas finales).
Un Diálogo SIP es una conversación peer-to-peer entre dos UA (Agentes de Usuario). Los
diálogos son identificados usando los campos Call-ID (Id. de llamada), From (De) y To (Para). Los
mensajes con estos campos iguales pertenecerán al mismo diálogo. Un diálogo es una secuencia de
transacciones.

Tipos de Servidores Proxy SIP

Existen dos tipos de servidores Proxy: Stateful y Stateless.

• Stateful Proxy (Dialog & Transaction)
Los servidores Proxy Stateful retienen cierta información (estado) durante la llamada.
Respecto del tipo de información que pueden retener los servidores Proxy Stateful cabría
realizar una nueva clasificación. Es muy común encontrarse con aplicaciones como por
ejemplo aquellas basadas en SER (Sip Express Router) que solamente actúan manteniendo el
estado para una simple transacción SIP (minimal state). En esta caso se dice que estamos en
presencia de un servidor tipo “Transaction Stateful Proxy”. En el caso que se almacene el
estado durante todo el tiempo que dure el establecimiento de una llamada estaremos en
presencia de un “Dialog Stateful Proxy”.

• Stateless Proxy
Los servidores Proxy Stateless, procesan un mensaje SIP y olvidan todo lo referente a
la llamada hasta que vuelven a recibir otro mensaje SIP. La implementación Stateless provee
buena escalabilidad, pues los servidores no requieren mantener información referente al
estado de la llamada una vez que la transacción ha sido procesada. Además, esta solución es
muy robusta dado que el servidor no necesita “recordar” nada en relación con una llamada.
Sin embargo, no todas las funcionalidades pueden ser implementadas en un servidor proxy
stateless, por ejemplo, las funcionalidades relativas al accounting y facturación de las
llamadas puede requerir funcionalidades proxy stateful, de mane


Escenarios de Registro, Inicio y Finalización de Sesión

Escenario de Registro SIP
Para que un usuario pueda ser llamado por otro, este debe registrarse primero ante el
Registrar (o Proxy). El registro consiste en el envío de un mensaje REGISTER seguido de su
correspondiente respuesta 200 OK. Si el usuario no da credenciales válidas recibirá por respuesta un
mensaje 407, con lo cual tendrá que reenviar el mensaje de Registro hasta que tenga éxito.

Escenario de Inicio de Sesión SIP
La invitación a realizar un inicio de sesión SIP comienza con el mensaje INVITE dirigido
comúnmente al Servidor Proxy. Éste responde con un mensaje TRYING (100) para evitar posibles
retransmisiones y reenvía las peticiones hacia el usuario llamado. Todas las respuestas provisionales
generadas por el usuario llamado son regresadas al usuario origen, por ejemplo RINGING (180). Una
respuesta 200 (Ok) es generada en cuanto el usuario llamado contesta.

Escenario de Finalización de Sesión SIP
Una sesión finaliza cuando uno de los usuarios envía el mensaje BYE al otro extremo. El otro
usuario confirma el final de la conversación enviando por respuesta un mensaje 200 (Ok). La
transacción para terminar la sesión se realizará de un extremo a otro sin pasar por el Proxy (sólo si no
existe un registro de ruta).

Registro de Ruta: Hay situaciones en las que el Proxy requiere estar presente en la ruta de todos los
mensajes con fines de control del tráfico, o por ejemplo, cuando existe un router NAT como veremos
más adelante. Esto se logra por medio de la inserción del campo RECORD ROUTE en los encabezados de los mensajes SIP.

Soluciones NAT del lado del cliente


En esta sección se comentan brevemente algunas soluciones para NAT que existen en
el lado del cliente, es decir, o bien en su dispositivo SIP o bien en su router/firewall NAT:

Un dispositivo que usa STUN, antes de registrarse y/o hacer llamadas, se pone en
contacto con un servidor STUN que previamente se indicó en su configuración (ej:
stun.ekiga.net) y a través de él, se entera de si está detrás de NAT y qué tipo de NAT.
A continuación se verá con más detalle lo que ocurre:
Se inicia el dispositivo SIP. Este tiene configurado usar STUN contra un servidor STUN
predeterminado. En su configuración SIP y RTP tienen puertos asignados, supongamos SIP
5060 y RTP 8000. Inicia el test de STUN contra el servidor de STUN. Dicho test realiza una
serie de consultas:
Hola servidor STUN, ¿con qué IP y puerto me ves?
Entonces el dispositivo se entera de si está detrás de NAT. En caso afirmativo continúa el test
con la siguiente consulta:
Te envío un paquete desde mi puerto local 5060 y luego otro desde el 8000, ¿tú qué IP y
puertos mapeados en mi router NAT recibes?
Esto le permite al dispositivo conocer con qué IP y puertos saldría vía IP pública desde su
router NAT (hay que tener en cuenta que un router NAT puede manejar más de una IP
pública).

¿Y si hago la misma consulta pero a tu segunda IP pública? ¿ves la misma IP y puertos?
Esto es crucial y sirve para detectar el NAT simétrico, el único que impide el
funcionamiento de STUN. Si el servidor STUN ve la misma IP y puertos origen entonces el
dispositivo ya sabe qué IP pública y puertos debe indicar en sus mensajes SIP corrigiendo los
que aparecían en negrita.
Supongamos que el puerto SIP 5060 se mapea en el router NAT al 15060 y el RTP
8000 al 1800, entonces el dispositivo SIP indicaría esos valores en su mensaje INVITE en vez
de los indicados en negrita. Con esto sencillamente se consigue que nuestro dispositivo SIP
aparente tener IP pública.


Limitaciones de STUN

STUN no funciona con NAT simétrico (bastante implementado en routers NAT típicos
ofrecidos por las operadoras para ADSL). Esto es debido a que este tipo de NAT no garantiza
que si se contacta con distintas IP's externas éstas lo vean con el mismo puerto público. Es
decir, el test de STUN no serviría puesto que no se sabe con qué puertos lo verá el destino
SIP.
STUN necesita mantener el keepalive de SIP para que el router NAT no cierre la
entrada de nuevos mensajes SIP transcurrido un cierto tiempo (por ejemplo: 20 segundos).
Esto no es problema puesto que el propio cliente podría enviar mensajes SIP periódicos al
destino (como un OPTIONS).
STUN sólo sirve para UDP y no para TCP. Esto es debido a la forma en la que un
router NAT detecta las conexiones establecidas para permitir nuevo tráfico entrante hacia la
LAN. En UDP se considera "misma comunicación" si anteriormente un paquete UDP ha salido
en sentido contrario hacia la IP y puerto público desde el que se reciben ahora nuevos
paquetes UDP. Pero en TCP (protocolo orientado a conexión) el router/firewall NAT sólo
considera una conexión TCP establecida si se ha seguido el protocolo para ello existente en
el propio TCP, es decir: SYN, SYN/ACK y ACK.

TURN (Traversal Using Relay NAT)

TURN se emplea para complementar los casos donde STUN no puede actuar (NAT
simétrico y TCP) pero con el gran costo añadido de requerir de un servidor TURN por donde
se enviará el tráfico. Es decir, un servidor STUN sólo interviene en la consulta inicial de los
clientes SIP, en cambio, un servidor TURN se emplea para enviar a través suyo el tráfico final.
Obviamente en términos de RTP el uso de TURN debe ser la última opción a considerar.
Además, puesto que el servidor TURN debe reunir requisitos de ancho de banda apropiados,
la opción de TURN debe considerarse como una solución del lado del cliente y del servidor.

ICE (Interactive Connectivity Establishment)
ICE no es en realidad un nuevo protocolo, sino una metodología que permite a los
clientes investigar qué solución de lado cliente (por ejemplo STUN ó TURN) es la más
adecuada según el escenario.

ALG (Application Layer Gateway)
AGL es un sistema implementado en el router/firewall que examina los paquetes SIP
que lo atraviesan y modifican las IP's y puertos para adecuarlos a su situación de NAT. Es,
digamos, una solución transparente para el cliente. No obstante, por implementarse en su
red local se considera solución de lado cliente.

Problemas con ALG
Al parecer muchas implementaciones ALG para SIP que vienen en los routers ADSL
son nefastas y reescriben los puertos con valores incorrectos (fuera del rango de 2^16), lo
que impide la transmisión del audio. Debido a este problema, en algunos routers es
necesario desactivar ALG de SIP vía telnet.


Soluciones NAT del lado del Servidor

En esta sección comento algunas soluciones para el NAT que existen en el lado del
servidor, lo que conlleva a la total transparencia desde el punto de vista del cliente. En este
caso será el servidor/proxy SIP el que tome medidas.

Proxy RTP

Un proxy RTP sirve como "pasarela" del tráfico RTP, de tal forma que el llamante y el
llamado envían su tráfico RTP a través de él. Dicho proxy RTP es manejado desde un servidor
SIP y debe disponer de IP pública para que sea accesible a los usuarios.
Supongamos que un usuario tras NAT llama a través de su proxy SIP a un usuario SIP
con IP pública:
Un usuario tras NAT y sin ninguna solución del lado del cliente hace una llamada a
través de su proxy SIP.
El proxy detecta que el INVITE proviene de una IP privada (mirando las cabeceras) así
que modifica las IP's y puertos del contacto SIP reemplazándolos por los valores de la IP y
puertos públicos del router.
Además reescribe la IP y puerto del SDP poniendo la IP del proxy RTP y un puerto que
en una consulta previa dicho proxy RTP le haya ofrecido.

Entonces se rutea el INVITE al destino, el cuál verá como punto de contacto SIP la IP
pública del NAT llamante y como contacto RTP la IP pública del proxy RTP.
Así pues la comunicación SIP pasa por el proxy SIP (como debe ser) mientras que el
tráfico de audio pasa por el proxy RTP que se encarga de puentear los flujos RTP de llamante
y llamado.
Obsérvese también, que si el proxy SIP ha añadido cabeceras "Record-Route" para
permanecer toda la llamada en el path, y como el proxy SIP ha puesto la IP pública NAT del
llamante en la cabecera "Contact" del INVITE que luego ha rutado al llamado, entonces si el
llamado cuelga la llamada enviará algo como:

BYE sip:841101@170.210.68.101:15060 SIP/2.0

Así que el proxy SIP ruteará ese mensaje a la IP pública del llamante. Si esta cabecera
no se hubiese corregido entonces sería:

BYE sip:841101@192.168.128.101:5060 SIP/2.0

Lo que provocaría que el proxy SIP no podría rutearla ya que intentaría rutearla a una
IP privada.

Otras consideraciones
También hay que tener en cuenta el caso en el que el llamado esté tras NAT. En ese
caso el proxy SIP debe detectarlo en la respuesta del llamado ("Trying", "Ringing", "OK"...).

Proxy's RTP disponibles

Las opciones más extendidas (al menos en el mundo del software libre) son RTP
Proxy y Media Proxy, ambos compatibles con OpenSer y con SER, los dos proxy SIP más
populares.

Kamailio

Kamailio es un completo servidor SIP de codigo abierto que implementa las funciones de registrar, proxy, XCAP server, MSRP relay.... y soporta muchas bases de datos como MYSQL, Cassandra ....


Archivo de Configuración: Kamailio.cfg


Kamailio basa su funcionamiento en un núcleo que recibe y procesa mensajes SIP. La
mayor parte de su funcionalidad se brinda a través de módulos extra. Al poseer una
arquitectura modular, Kamailio posee un núcleo muy pequeño, rápido y estable. Las
funcionalidades de los módulos se emplean a través del archivo de configuración de
Kamailio (kamailio.cfg). Este archivo de configuración controla que módulos serán cargados
y define como se comportaran a través de las variables de módulo.


Kamailio.cfg consta de siete secciones lógicas:

1. Sección de Definiciones Globales: Esta parte del archivo .cfg normalmente contiene el
IP y puerto donde el servicio se encuentra escuchando peticiones, el nivel de
depuración empleado (debug level), etc. Las definiciones de esta sección afectan
directamente al servicio (SER daemon).

2. Sección de Módulos: Esta sección contiene una lista de las librerías externas que se
necesesitan para brindar funcionalidad no provista por el núcleo. Estos módulos son
archivos de objetos compartidos “.so” y se cargan con la directiva “loadmodule”;

3. Sección de Configuración de Módulos: Muchas de las librerías especificadas en la
“Sección de Módulos” necesitan del seteo de algunos parámetros para su
funcionamiento. Estos parámetros se setean a través del comando “modparam” bajo
la siguiente forma: modparam (“nombre_modulo”, ”parámetro_modulo”,
valor_modulo).

4. Bloque Principal de Ruteo: Este bloque se asemeja a la función principal (main) de un
programa escrito en C. Este es el punto de entrada del procesamiento de un mensaje
SIP y controla como maneja cada mensaje recibido.(route[1])


5. Bloques Secundarios de Ruteo: Además del bloque principal de ruteo, el archivo .cfg
puede contener otros bloques adicionales de ruteo llamados desde el bloque
principal o desde otro bloque secundario. Un bloque secundario de ruteo es análogo
a una subrutina. (route[x])

6. Bloque de Ruteo de Respuesta: Se pueden utilizar bloques opcionales destinados a
manejar respuestas a mensajes SIP la mayoría de los cuales son mensajes “OK”.
(on_reply_route[x])

7. Bloques de Ruteo de Falla: Se pueden utilizar bloques opcionales cuando se necesita
procesar o manejar diferentes condiciones de falla como por ejemplo un mensaje
“BUSY” o un “timeout”. (failure_route*x+)

SIP: Transacciones, Diálogos y Sesiones

A fines de entender apropiadamente el funcionamiento del archivo de configuración
.cfg, se necesita comprender tres conceptos básicos:


  • Transacción SIP: Esta compuesta por un mensaje SIP (y cualquier reenvío) y su respuesta directa (y casi siempre inmediata). Por ejemplo: Un Agente de Usuario (UA) envía un mensaje “REGISTER” a OpenSER y recibe de su parte un mensaje “OK”;
  •  Diálogo SIP: Se trata de una relación entre dos o más transacciones que existen por un cierto tiempo. Por ejemplo: Se establece un diálogo entre un mensaje “INVITE” finalizado por un “BYE”);
  • Sesión: Se corresponde con el flujo o stream de audio de una conversación entre dos teléfonos SIP.
Procesamiento del archivo de configuración .cfg

Se puede entender al archivo kamailio.cfg como un script que se ejecuta cada vez que
se recibe un mensaje SIP. Por ejemplo, un agente de usuario (UA) envía un mensaje INVITE a
otro UA para establecer una conversación. En este caso al recibir el mensaje INVITE,
OpenSER comienza a procesarlo a través de los comandos encontrados en el bloque de
ruteo principal (main route []).

Luego, el proceso continua hasta que se alcanza un punto en el cual se toma la
decisión acerca de donde enviar el INVITE (usando el comando t_relay()), devolver al origen
una respuesta con error (usando sl_send_reply()), o simplemente descartar el INVITE (ya sea
llegando al final de la ruta principal o por encontrarse con un comando break). Por supuesto,
esto último no se recomienda.

El destinatario responderá al INVITE con un mensaje OK. El mensaje OK es una
respuesta directa al mensaje inicial INVITE por lo tanto es controlado por la sección del
archivo de configuración denominada on_reply_route[x]. Si el destinatario no responde, o
responde con un error (busy, etc), se llama a la rutina failure_route[x].
Finalmente, si en la respuesta no existe error, el origen enviará un mensaje “ACK” para
indicar al destinatario que todo fue recibido y aceptado.

Cuando se emplea t_relay(), el comportamiento se manifiesta de acuerdo a lo
expuesto anteriormente, entonces se dice que OpenSER trabaja como “trasaction stateful
proxy”.
Cabe aclarar que un diálogo como el descripto en el ejemplo anterior incluye otras
respuestas intermedias antes del OK, pero las mismas no fueron contempladas por
simplificación.
Todos los mensajes que encabezan una nueva transacción SIP entran al tope de la
ruta principal (main route[]). En OpenSER existe una total libertad en la manera que se
procesan los mensajes SIP. A continuación se presentan algunos ejemplos de funciones:

  • save(“location”): Se emplea para registrar cuando un usuario esta disponible o en línea. 
  • lookup(“location”): Esta función retorna la dirección IP del usuario destino de la llamada.
  • setflag(x): Permite el almacenamiento de cierta información limitada acerca de los usuaros en forma de banderas o flags.

Entendiendo SIP y RTP


SIP es un protocolo de señalización que permite manejar o controlar una llamada:
invitando a que la misma se realice (INVITE), cancelando durante el timbrado (CANCEL),
cortando una vez establecida (BYE), etc.
Los mensajes SIP pueden ser intercambiados directamente entre agentes de usuario
(UA), aunque a menudo se emplean SIP servers a los fines de ubicar los destinatarios.
Cuando un servidor SIP como OpenSER recibe un mensaje, puede decidir si permanece en el
medio del loop o no. Si no lo hace, OpenSER proveerá al UA la información necesaria para
contactar al otro UA y luego los mensajes SIP se intercambiaran directamente entre los dos
UAs.
Si OpenSER permanece en el loop, por ejemplo para asegurar que un mensaje BYE
sea recibido a los fines de tarifar la comunicación, OpenSER debe insertar un encabezado de
ruta (Route header) en el mensaje SIP usando la función record_route() para indicarle a los
demás que quiere participar. A los fines de que esto funcione, OpenSER y el resto de los
servidores SIP que intervienen deben realizar lo que se denomina “liberar la ruta” (“loose
routing”). Esto significa que los mensajes SIP no deben enviarse directamente al UA sino a
través de aquellos que pusieron un encabezado de ruta (route header) en el mensaje SIP.
Para chequear esto se emplea la función “loose_route()”.

Un mensaje SIP puede contener información adicional, por ejemplo relacionada
sobre como configurar una llamda con audio y video stream. (llamado SDP, Session
Description Protocol).

La información SDP resultará en uno o más sesiones RTP a ser configuradas,
normalmente entre los dos UA. Kamailio no participa de esto. RTP son por naturaleza de
procesamiento de ancho de banda intensivo. Sin embargo, como se describe después,
OpenSER puede asegurar que otra aplicación como un B2BUA (“Back to Back User Agent”) o
RTP Proxy puede convertirse en intermediarios. (“middle man”).
Finalmente, de Real-Time Control Protocol (RTCP) transmite información acerca de
los streams RTP entre UA. RTCP puede emplear el puerto RTP + 1 o un puerto indicado en el
propio mensaje SDP.

Aplicaciones Back-End

Está claro que OpenSER no mantiene información de estado sobre un diálogo SIP,
solo transfiere mensajes SIP (como parte de transacciones) entre dos agentes. La
consecuencia de esto es que OpenSER no puede ser un “peer” en el diálogo.
Si se necesita proveer capacidades de correo de voz o simplemente reproducir un
aviso indicando que el usuario no está disponible se necesitará algo que actúe de
intermediario como UA. Para ello existen módulos extra que proveen dicha funcionalidad
denominadas “back-end user applications”. Estas aplicaciones pueden actúar como “middle
man” para los mensajes SIP, para el audio va RTP, o ambos a la vez. Cada UA entonces
dialogará con este punto intermedio (“middle man”) y nada conocerá del UA remoto. Los
servidores que corren este tipo de aplicaciones reciben el nombre de “B2BUA”.

URI, R-URI, y Ramas (Branches)

Para identificar un usuario de manera unívoca se emplea el identificador URI
(Uniform Resource Identifier). La denominación URI se emplea como una dirección del
contacto destino. La forma típica de un identificador SIP (URI) es la siguiente:
sip:nombreusuario@dominio.com. El identificador URI original ubicado en la primera línea
de un mensaje SIP se llama request-URI (R-URI). Este URI es referenciado dentro del archivo
de configuración .cfg usando la expresión “uri”. Ejemplo: if (uri=~sip:username@.*)

Un URI puede ser manipulado a través del archivo de configuración a través de
diversas funciones desde diferentes módulos. Por ejemplo: lookup(“location”) tomará la
expresión uri modificada a lo largo del proceso, buscará en la base de datos “location” y
reescribirá el URI usando la información de contacto correcta (incluyendo @ipaddress:port).
Al ejectuarse t_relay() el mismo empleará como URI destino la forma final luego de las
transformaciones realizadas.

Algunas funciones, tales como lookup(“location”) pueden agregar ramas al conjunto
de URIs destino. Esto significa que cuando se ejecuta t_relay(), el mensaje INVITE se duplica
hacia todas las (potenciales) formas transformadas. El comando a nivel de núcleo
denominado “revert_uri()” reemplazara el actual URI destino con el valor original dado por
el campo “request-URI”.