Mi Brain-Training Personal

Para que no se me olviden las cosas…

Blink Qt beta para GNU/Linux disponible!

Tras muchos meses de trabajo por fin tenemos Blink Qt (beta) para GNU/Linux disponible para su descarga.

Tanto la versión de Mac, Blink-Cocoa como la versión para GNU/Linux (y próximamente Windows), Blink-Qt comparten el mismo core: SIPSIMPLE SDK. De momento todas las funcionalidades no se encuentran implementadas (beta), pero vamos paso a paso, intentando construir el mejor cliente SIP para todas las plataformas.

Ésta es la lista de features de la versión beta de Blink-Qt: http://icanblink.com/features.phtml

Hay paquetes Debian disponibles para Debian testing y unstable y Ubuntu Lucid (10.04) y Karmic (9.10), tenéis todas las instrucciones de instalación así como una breve guía de uso aquí: http://icanblink.com/blink-qt-beta.phtml

Hemos puesto mucho esfuerzo en ésta versión, pero como humanos que somos puede haber fallos, todo feedback es bienvenido. ;-) La lista de correo de Blink donde reportar errores, sugerencias, etc. está aquí: http://lists.ag-projects.com/mailman/listinfo/blink

blink-qt

Happy Blinking! :-)

Probando el soporte IPv6 de Asterisk 1.8

Como ya habréis leído por ahí la próxima versión de Asterisk, la 1.8, traerá soporte para IPv6. Gracias a mi buen amigo Mikel “packet tracer” Jimenez dispongo de plena conectividad IPv6 en mi casa, así que hacer una llamada SIP por IPv6 era la siguiente prueba tras ping6. ;-)

No quería ensuciar mucho el servidor de SIPdoc (donde realicé las pruebas) así que opté por instalar Asterisk 1.8 con el script live_ast que ya comenté en su día. Resumiendo:

svn co http://svn.asterisk.org/svn/asterisk/branches/1.8 asterisk18
cd asterisk18
cp contrib/scripts/live_ast .
./live_ast configure
./live_ast install
./live_ast samples
./live_ast run -vvvvvvvvvvvvvvc

Tras instalar asterisk modificamos el fichero sip.conf e indicamos que Asterisk escuche en IPv6:

udpbindaddr=::

Si tenemos una IP concreta a la que queremos bindear haríamos lo siguiente:

udpbindaddr=[2001:470:1f12:X:X::1]:5060

¡Ya tenemos el servidor configurado! Para hacer nuestra primera llamada SIP con IPv6 utilizaremos la herramienta pjsua del proyecto PJSIP.

Por defecto PJSIP no se compila con soporte IPv6, así que haremos lo siguiente para descargar y compilar PJSIP:

svn co http://svn.pjsip.org/repos/pjproject/trunk pjsip
cd pjsip
echo "#define PJ_HAS_IPV6 1" > pjlib/include/pj/config_site.h
./configure && make dep && make

La herramienta pjsua se habrá compilado en el directorio pjsip-apps/bin. Nos cambiamos a ese directorio y ya podemos arrancarla:

./pjsua-x86_64-unknown-linux-gnu --ipv6 --no-tcp

pjsua-ipv6

Probemos ha hacer una llamada: pulsamos ‘m’ e introducimos la URI SIP a la que queremos llamar: ‘sip:1000@[2001:470:1f12:286::2]‘.

Si hemos dejado el dialplan por defecto deberíamos estar escuchando a Allison Smith y su demo-congrats. ¡A través de IPv6!

asterisk-ipv6

¡Parece que funciona!

INVITE sip:1000@[2001:470:1f12:286::2]:5060 SIP/2.0
Via: SIP/2.0/UDP [2001:470:c846:1:225:ff:feac:6aca]:5060;rport;branch=z9hG4bKPjBcy323TSZVZa86u8GMIV01Zv8wNs1DAE
Max-Forwards: 70
From: ;tag=-3p-SYm.MD6bVaH-8WHegCIArkOYu.9R
To: sip:1000@[2001:470:1f12:286::2];tag=as349aa7db
Contact: 
Call-ID: cWEqE795OfqpixU.l1IavBkTPpgVmX3F
CSeq: 21549 INVITE
Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, SUBSCRIBE, NOTIFY,
REFER, MESSAGE, OPTIONS
Supported: replaces, 100rel, timer, norefersub
Session-Expires: 1800;refresher=uas
Min-SE: 90
Content-Type: application/sdp
Content-Length:   310

v=0
o=- 3490026667 3490026668 IN IP6 2001:470:c846:1:225:ff:feac:6aca
s=pjmedia
c=IN IP6 2001:470:c846:1:225:ff:feac:6aca
t=0 0
a=X-nat:0
m=audio 4030 RTP/AVP 3 101
a=rtcp:4031 IN IP6 2001:470:c846:1:225:ff:feac:6aca
a=rtpmap:3 GSM/8000
a=sendrecv
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15

--end msg--
 21:51:09.178   pjsua_core.c  RX 659 bytes Response msg
100/INVITE/cseq=21549 (rdata0x1f61238) from UDP
2001:470:1f12:286::2:5060:
SIP/2.0 100 Trying
Via: SIP/2.0/UDP [2001:470:c846:1:225:ff:feac:6aca]:5060;branch=z9hG4bKPjBcy323TSZVZa86u8GMIV01Zv8wNs1DAE;received=2001:470:c846:1:225:ff:feac:6aca;rport=5060
From: ;tag=-3p-SYm.MD6bVaH-8WHegCIArkOYu.9R
To: sip:1000@[2001:470:1f12:286::2];tag=as349aa7db
Call-ID: cWEqE795OfqpixU.l1IavBkTPpgVmX3F
CSeq: 21549 INVITE
Server: Asterisk PBX SVN-branch-1.8-r281052
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY,
INFO, PUBLISH
Supported: replaces, timer
Require: timer
Session-Expires: 1800;refresher=uas
Contact: 
Content-Length: 0

--end msg--
 21:51:09.180   pjsua_core.c  RX 981 bytes Response msg
200/INVITE/cseq=21549 (rdata0x1f61238) from UDP
2001:470:1f12:286::2:5060:
SIP/2.0 200 OK
Via: SIP/2.0/UDP [2001:470:c846:1:225:ff:feac:6aca]:5060;branch=z9hG4bKPjBcy323TSZVZa86u8GMIV01Zv8wNs1DAE;received=2001:470:c846:1:225:ff:feac:6aca;rport=5060
From: ;tag=-3p-SYm.MD6bVaH-8WHegCIArkOYu.9R
To: sip:1000@[2001:470:1f12:286::2];tag=as349aa7db
Call-ID: cWEqE795OfqpixU.l1IavBkTPpgVmX3F
CSeq: 21549 INVITE
Server: Asterisk PBX SVN-branch-1.8-r281052
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY,
INFO, PUBLISH
Supported: replaces, timer
Require: timer
Session-Expires: 1800;refresher=uas
Contact: 
Content-Type: application/sdp
Content-Length: 293

v=0
o=root 1289948586 1289948587 IN IP6 2001:470:1f12:286::2
s=Asterisk PBX SVN-branch-1.8-r281052
c=IN IP6 2001:470:1f12:286::2
t=0 0
m=audio 11994 RTP/AVP 3 101
a=rtpmap:3 GSM/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=silenceSupp:off - - - -
a=ptime:20
a=sendrecv

Dejo pendiente para otro día el montaje de IPv6 que realicé con Mikel en el servidor de SIPdoc y mi casa.

Happy IPv6 calling!

svcrash: ¿el comienzo del anti-hacking en SIP?

Hace unas semanas fue presentada (el autor también fue invitado al VUC) la utilidad svrash.py, de la suite de seguridad SIPVicious. Con esta utilidad es posible hacer que las utilidades svwar.py y svcrack.py se dentengan, ya que les provoca una excepción. ¿WTF?

Así es, svcrash es capaz de detener los ataques causados desde SIPVicious. Su autor no programó las utilidades para que la gente lanzara ataques contra otros, sino para que cualquiera pudiera comprobar el nivel de seguridad de sus propios sistemas, pero como hay malos sueltos por ahí que utilizan las herramientas de un modo bastante dudoso, programó svcrack.

Svcrash aprovecha un bug (tal vez dejado intencionadamente) para convertirlo en una feature: todas las versiones de SIPVicious tienen un bug que provoca la parada del programa debido a una excepción no controlada si se le envía un To tag con determinada forma. El funcionamiento de svcrash es sencillo: responde a los requests enviados por svcrack o svwar con un 200 OK con el To tag adecuado para provocar una excepción. El autor incluso ha añadido un modo automático, en el cual svcrash funciona en modo demonio leyendo los logs de Asterisk, y responde con éstos 200 OK (con el To tag adecuado) en caso de que Asterisk indique un fallo de autenticación en los logs.

Podéis ver un vídeo de su funcionamiento aquí.

Además de ser una manera elegante de terminar un ataque, el concepto de svcrash me hizo pensar un rato… ¿y si en lugar de bloquear o evitar un ataque simplemente contraatacamos, llegado el momento?

Supongamos el siguiente escenario: estamos sufriendo un ataque con el objetivo de adivinar el password de alguno de nuestros usuarios SIP. Lo habitual al detectar el tráfico generado por éste tipo de ataques suele ser bloquear el acceso de la IP en cuestión. Pero eso no quiere decir que el atacante haya dejado de enviar paquetes. El atacante podría enviarnos tráfico desde distintas IPs de manera dinámica, lo que haría más complicado bloquearlo, aunque tampoco imposible.

Si “la mejor defensa es un buen ataque” aquí va una idea: extender el concepto de svcrash y crear una suite de anti-hacking para SIP. Las herramientas de ataque para SIP no suelen implementar un completo cliente SIP, básicamente se quieren enviar paquetes SIP por un socket rápido o con ciertas credenciales. Por lo tanto, podríamos suponer que la herramienta que nos ataque haya dejado algún caso sin contemplar en su parser y podríamos provocar una excepción en la aplicación con una respuesta especialmente preparada (tal y como hace svcrash). Aquí van unas cuantas ideas que se me ocurren a botepronto:

  • Que la respuesta empiece con “SIP/1.0 200 OK”.
  • Utilizar unicode en las cabeceras From y To. (el soporte unicode es algo en lo que casi nunca se piensa desde el principio…)
  • Eliminar cabeceras de manera arbitraria: responder sin Contact, sin From ni To, …

Es solo una idea, pero sería interesante ver alguna herramienta que empiece a responder con alguna de las respuestas malvadas cuando detecte X intentos de autenticación fallidos, ¿no creéis?

Happy anti-hacking! :)

¿Desvelado el secreto de Skype?

Ayer por la noche la noticia recorría Internet como la pólvora: Skype puede haber sido hackeado haciendo ingeniería inversa su protocolo.

Skype lleva muchos años en el mercado y se conocen muchos detalles sobre su infraestructura: sigue un modelo P2P (al igual que el sistema de almacenamiento de Google o la tecnología Dynamo de Amazon). Los nodos de esta red P2P tienen distintos niveles, de manera que los más propensos a reenviar tráfico de otros usuarios (como universidades, donde abundan las IPs públicas) son promocionados a ‘supernodos’. Las versiones móviles de Skype, por el contrario, nunca podrán llegar a ser ssupernodos.

Aunque se utilice la tecnología P2P, Skype también tiene servidores: algo tendrá que haber para SkypeOut, y una especie de B2BUA para distribuir los mensajes entre las múltiples instancias de Skype abiertas para un mismo usuario.

El problema de todo esto es mantener la confianza: poner perros y alambradas para que nadie que no sea Skype entre en la red P2P, ya sea para bien o para mal. Tal vez para que la gente deje de intentarlo, Skyoe lanzó SkypeKit SDK, pero parece que no es suficiente para todos. ;)

Según éste post el algoritmo de expansión de keys Skype RC4 ha sido obtenido gracias a la ingeniería inversa. Todos los detalles serán publicados en el 27C3 en Berlin en diciembre.

¿Veremos un cliente de Eskhipe? ¿Será un hoax? No lo parece…

skype_logo.png

Soporte de IPv6 en Asterisk

Ya lo había comentado Kevin P. Fleming varios meses atrás y hoy me he desayunado con la noticia. Russell Bryant acaba de postear el soporte de IPv6 para Asterisk en reviewboard.

No es que todo el mundo el vaya a utilizar (aún falta para eso…) pero es algo que hay que soportar a día de hoy y que convertirá Asterisk en un buena herramienta para probar IPv6 en otras aplicaciones como clientes SIP.

Añadir soporte para IPv6 en una aplicación del tamaño de Asterisk no ha tenido que ser fácil, así que mi enhorabuena al equipo de Asterisk por haberlo conseguido. :)

ipv6

Happy IPV6ing!

PD: La imágen la he tomado prestada de aquí.

Deteniendo un SIP flood con OpenSIPS y el módulo pike

Esta semana mientras miraba algo en uno de nuestros servidores me di cuenta de que estábamos siendo “atacados” mediante SIP flooding. Lo pongo entre comillas porque no era un ataque suficientemente significativo como para que el servicio se viera afectado, así que decidí aprovechar la ocasión para experimentar un poco y encontrar la manera de evitar éste tipo de ataques.

Como he dicho el ataque no era gran cosa: un montón de REGISTER que ni siquiera intentaban averiguar passwords. El problema de éste tipo de ataques es que pueden dejar nuestro sistema fuera de combate si los recursos no se liberan demasiado rápido. Los descriptores de fichero asignados a un proceso no son ilimitados (aunque ulimit -n diga lo contrario) y si se acaban no podremos hacer casi nada. Por otro lado, si la aplicación que está sufriendo el ataque consume el 100% de la CPU también estaremos afectando a otros procesos del sistema.

En estos casos un IDS como Snort puede ayudarnos, pero ya que se trata de OpenSIPS vamos a utilizar el módulo pike ya que viene incluido y así no necesitamos añadir un elemento más a nuestra infraestructura.

El módulo pike no bloquea el ataque de por si. Es capaz de detectarlo y de darnos las herramientas necesarias para actuar ente un ataque. Podéis ver toda la documentación del módulo, yo aquí comentaré los parámetros más relevantes:

  • sampling_time_unit: Indica el número de segundos que componen una muestra. A priori puede parecer algo abstracto, cobra sentido en combinación con el siguiente parámetro.
  • reqs_density_per_unit: Indica el número de mensajes permitidos en un sampling_time_unit, de manera que al sobrepasar éste número se ‘bloqueará’ la IP que esté originando el tráfico.
  • remove_latency: Indica el tiempo que tendremos una IP marcada como ‘bloqueada’.

Todas las unidades de tiempo se indican en segundos.

Una vez tenemos el módulo configurado vamos a utilizar la función pike_check_req() para detectar el flooding y actuar en consecuencia. Un ejemplo con un poco de pseudocódigo:

modparam("pike", "sampling_time_unit", 2)
modparam("pike", "reqs_density_per_unit", 30)
modparam("pike", "remove_latency", 120)
...
...
if (!(FROM_SELF || FROM_TRUSTED)) {
if (!pike_check_req()) {
exit;
}
}
...
...

Como se puede ver lo que haremos al detectar el flood es llamar a la función exit, para dejar de procesar los requests y liberar los recursos lo antes posible.

Alguno ya se habrá dado cuenta de que en realidad no estamos deteniendo el ataque… veamos como se podría hacer :)

El módulo pike nos generará unas bonitas entradas en syslog:

Jun 14 11:32:39 node03 /usr/sbin/opensips[12654]: PIKE - BLOCKing ip 1.2.3.4, node=0xaf793a68
Jun 14 14:15:43 node03 /usr/sbin/opensips[12659]: PIKE - UNBLOCKing node 0xaf793a68
Jun 14 14:15:45 node03 /usr/sbin/opensips[12623]: PIKE - BLOCKing ip 1.2.3.4, node=0xaf793axx
Jun 14 14:34:25 node03 /usr/sbin/opensips[12659]: PIKE - UNBLOCKing node 0xaf793axx
Jun 14 14:34:27 node03 /usr/sbin/opensips[12646]: PIKE - BLOCKing ip 1.2.3.4, node=0xaf793axx
Jun 14 15:35:39 node03 /usr/sbin/opensips[12659]: PIKE - UNBLOCKing node 0xaf793axx

Todo lo que tendremos que hacer es detectar estos mensajes con Rsyslog (por ejemplo) y añadir temporalmente una regla de iptables para bloquear el tráfico.

Ala, ya tenéis deberes ;)

4005407576_a1e671452d_m

ICE: The ultimate way of beating NAT in SIP

Hoy ha sido el día. A las 15:00 he tenido la oportunidad de dar una charla en el Amoocon 2010. La charla trataba sobre cómo ‘atravesar’ el NAT utilizando ICE, algo de lo que ya hablamos hace algunos posts.

A continuación os dejo las transparencias, que espero sirvan a alguien para aclarar el concepto de ICE en si, y cómo lo hicimos funcionar correctamente con OpenSIPS y MediaProxy.



Njoy! :)

Probando el soporte SRTP de Asterisk

Si ayer comentaba un poco el jaleo del SRTP opcional, hoy vamos a probar soporte SRTP de Asterisk.

Que no se asuste nadie, Asterisk todavía no soporta SRTP, ni siquiera en trunk, así que vamos a probar un branch en el que se está desarrollando el soporte de SRTP. Cabe destacar que nos encontramos ante la que tal vez sea la incidencia no resulta más antigua del bugtracker de Digium ¡va para 5 años! Como os podréis imaginar la incidencia está llena de notas que ya no sirven y añaden confusión al asunto… por eso escribo esto. ;)

Parece que por fin se lo han tomado en serio en Digium y Terry Willson está siendo el encargado de añadir esta funcionalidad que formará parte de Asterisk 1.8.

IMPORTANTE: Aunque utilicemos SIP sobre TLS para señalización y SRTP para media NO tenemos garantizada la encriptación de la señalización de extremo a extremo, por lo que la única manera de tener el RTP seguro es utilizar ZRTP.

Y sin más preámbulos, nos remangamos y ¡a compilar!

1. Instalar libSRTP

Es necesario compilar esta librería de una forma específica, ya que es necesario indicar unos flags para que Asterisk pueda enlazarla correctamente (-fPIC), así que tenemos que compilarla a mano:

wget http://srtp.sourceforge.net/srtp-1.4.2.tgz
tar zxvf srtp-1.4.2.tgz
cd srtp
autoconf
CFLAGS="-Wall -O4 -fexpensive-optimizations -funroll-loops -fPIC" ./configure
make
make install

2. Instalar Asterisk con soporte para SRTP

Tenemos que instalar el branch que se indica en la incidencia del bugtracker de Digium:

svn co http://svn.asterisk.org/svn/asterisk/team/group/srtp_reboot asterisk-srtp
cd asterisk-srtp
./configure
make menuselect
(comprobamos que res_srtp se encuentra habilitado)
make && make install
make samples

3. Configuración

Una vez tenemos Asterisk con soporte para SRTP instalado podemos empezar ha hacer pruebas. Para ello utilizaremos la siguiente configuración:

sip.conf
[saghul]
type=friend
secret=1234
context=test-srtp
host=dynamic
disallow=all
allow=alaw
encryption=yes

extensions.conf
[test-srtp]
exten => 1234,1,NoOp
exten => 1234,n,Answer
exten => 1234,n,Playback(demo-congrats)
exten => 1234,n,Hangup

Llamamos al 1234 y obtendremos este maravilloso SDP como respuesta:
SIP/2.0 200 OK
Via: SIP/2.0/UDP ...
From: "saghul" ;tag=jziCJERZJx5yWuqJ6be.Nsovxw6C-95.
To: ;tag=as5c0e6e41
Call-ID: U.kCq9Jk-6Zfg2kBg1qCb5wYI09voGj-
CSeq: 28567 INVITE
Server: Asterisk PBX SVN-group-srtp_reboot-r262249-/trunk
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH
Supported: replaces, timer
Contact:
Content-Type: application/sdp
Content-Length: 379

v=0
o=root 1054633509 1054633509 IN IP4 192.168.99.53
s=Asterisk PBX SVN-group-srtp_reboot-r262249-/trunk
c=IN IP4 192.168.99.53
t=0 0
m=audio 17064 RTP/SAVP 8 101
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=silenceSupp:off - - - -
a=ptime:20
a=sendrecv
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:5jjBwNWl/zUCvBBJGqLLABLAc2/83XQW0WkDzdUx

¡Funciona! Actualmente solo está soportado el modo mandatory de SRTP, así que el modo opcional en que indicamos RTP/AVP en la línea m y las claves en los atributos crypto no funcionará. Veremos si lo soportan o no.

Además del parámetro encryption=yes en el sip.conf, podemos indicar si queremos encriptación a nivel de señalización o de media con la función CHANNEL:

Set(CHANNEL(secure_bridge_signaling)=yes)
Set(CHANNEL(secure_bridge_media)=yes)

Os animo a los que tengáis terminales con soporte SRTP a probar el branch, happy encrypting!

phone_srtp

Sobre el SRTP opcional

Buenas, amigos del SIP y derivados. Hoy os voy a contar una pequeña batalla que he tenido con el soporte de SRTP en su forma opcional.

Primero unos breves antecedentes, para entrar en materia:

¿Qué es SRTP?

SRTP (RFC 3711) define un perfil de RTP mediante el cual dota de encriptación a este protocolo. Como sabéis, se suele utilizar RTCP junto al RTP, por lo que también existe SRTCP, que se encarga de cifrar el tráfico RTCP. Básicamente lo que haremos será cifrar el audio utilizando AES, de manera que aunque alguien pudiera capturar nuestros paquetes no podrá escuchar nuestra conversación.

Lo más habitual es utilizar SIP sobre UDP, así que la señalización irá sin encriptar, por lo que es posible que las claves de cifrado que se usarán para cifrar el audio sean capturadas y por lo tanto el cifrado pueda romperse. Para solucionar esto podemos cifrar también la señalización, utilizando TLS, o podemos utilizar ZRTP, que permite el intercambio de claves a nivel de RTP, a través de un medio no fiable (Internet es muy hostil). ZRTP utiliza el mecanismo Diffie-Hellman para intercambiar las claves a través de RTP y poder empezar a utilizar así SRTP.

¿Cómo se usa?

Este post no pretende dar una explicación detallada sobre SRTP, básicamente nos bastará con saber que con SRTP ciframos el audio y al menos se lo ponemos más difícil a los malos ;)

Cuando utilizamos SRTP lo podemos hacer de 3 maneras:

  • Deshabilitado: no utilizaremos SRTP.
  • Opcional: se ofrecerá soporte de SRTP en el SDP, y el terminal remoto elegirá si desea utilizarlo o no.
  • Obligatorio: únicamente se aceptarán streams que indiquen que SRTP está siendo utilizado.

Veamos como luce el SDP en los distintos casos:

SRTP desactivado:
v=0
o=- 3482506669 3482506669 IN IP4 192.168.99.53
s=sipsimple 0.14.2
c=IN IP4 192.168.99.53
t=0 0
m=audio 63697 RTP/AVP 103 102 9 0 8 101
a=rtcp:63698 IN IP4 62.131.6.55
a=rtpmap:103 speex/16000
a=rtpmap:102 speex/8000
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=sendrecv

Como se puede ver en la línea m del SDP, el perfil de RTP que estamos utilizando no es cifrado, ya que es RTP/AVP. (en caso de ser cifrado sería RTP/SAVP)

SRTP obligatorio:
v=0
o=- 3482507190 3482507190 IN IP4 192.168.99.53
s=sipsimple 0.14.2
c=IN IP4 192.168.99.53
t=0 0
m=audio 51474 RTP/SAVP 103 102 9 0 8 101
a=rtcp:51475 IN IP4 62.131.6.55
a=rtpmap:103 speex/16000
a=rtpmap:102 speex/8000
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:rbU4YeWyt+bRJPY2FZjIDfkPt659Az0lG05yVdQ9
a=crypto:2 AES_CM_128_HMAC_SHA1_32 inline:/ebgRzBfmUVuD2aaqeZmVfFahAX3hakyi+k8oMn2
a=sendrecv

En este caso se trata de un SDP con SRTP habilitado, ya que el perfil es seguro (RTP/SAVP) y se indican las claves a utilizar mediante los atributos a=crypto.

SRTP opcional:
v=0
o=- 3482507190 3482507190 IN IP4 192.168.99.53
s=sipsimple 0.14.2
c=IN IP4 192.168.99.53
t=0 0
m=audio 51474 RTP/AVP 103 102 9 0 8 101
a=rtcp:51475 IN IP4 62.131.6.55
a=rtpmap:103 speex/16000
a=rtpmap:102 speex/8000
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=sendrecv
m=audio 51474 RTP/SAVP 103 102 9 0 8 101
a=rtcp:51475 IN IP4 62.131.6.55
a=rtpmap:103 speex/16000
a=rtpmap:102 speex/8000
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:rbU4YeWyt+bRJPY2FZjIDfkPt659Az0lG05yVdQ9
a=crypto:2 AES_CM_128_HMAC_SHA1_32 inline:/ebgRzBfmUVuD2aaqeZmVfFahAX3hakyi+k8oMn2
a=sendrecv

Aquí vienen los problemas. Lo que se puede ver arriba es un SDP correcto indicar el soporte opcional de SRTP, pero ahora nos surgen problemas:

Tenemos 2 streams de audio: uno con SRTP y otro sin SRTP, pero no existe relación aparente. ¿Cómo se supone que ha de actuar una aplicación al recibir esto? Podríamos asumir que solo se permite un único stream de audio, ¡pero eso sería limitar las capacidades de SIP y SDP! ¡¿Qué hacemos?!

La solución más ampliamente utilizada (aunque incorrecta) es la siguiente:

v=0
o=- 3482507626 3482507626 IN IP4 192.168.99.53
s=sipsimple 0.14.2
c=IN IP4 192.168.99.53
t=0 0
m=audio 53519 RTP/AVP 103 102 9 0 8 101
a=rtcp:53520 IN IP4 62.131.6.55
a=rtpmap:103 speex/16000
a=rtpmap:102 speex/8000
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:Fx7O4v/cWkY0cdwn/X3G/RE5ei2hXWt9l4zuoDwo
a=crypto:2 AES_CM_128_HMAC_SHA1_32 inline:2rhPrkeZ++0i0aSkrGuPP2c1DM/kq/RUKQsR9BH2
a=sendrecv

Como se puede ver estamos indicando un perfil de transporte inseguro (RTP/AVP) pero indicando las claves SRTP (atributos a=crypto).

La solución estándar

Como he comentado, la forma correcta de expresar el soporte opcional de SRTP es incluyendo 2 streams distintos de audio, pero no hay forma de correlacionarlos. Imaginemos el siguiente caso: estamos viendo una peli por SIP. El vídeo nos viene en un stream (m=video) y el audio en otro (m=audio). Además, tenemos un segundo stream de audio con los comentarios del director. Por tanto tendremos 1 stream de vídeo y 2 de audio, pero no hay relación entre ellos, así que ¿cómo sabemos si en realidad no hay comentarios del director o si se trata de soporte opcional de SRTP? ¿y si aceptamos los 2 streams que hacemos? Un jaleo.

Para solucionar esto tenemos el draft SDP Capability Negotiation (http://tools.ietf.org/html/draft-ietf-mmusic-sdp-capability-negotiation-13) gracias al cual sí que existe una relación entre los streams de audio y el soporte opcional de SRTP tiene sentido:

v=0
o=- 25678 753849 IN IP4 192.0.2.1
s=
c=IN IP4 192.0.2.1
t=0 0
m=audio 53456 RTP/AVP 0 18
a=tcap:1 RTP/SAVP
a=acap:1 crypto:1 AES_CM_128_HMAC_SHA1_80 inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:4
a=pcfg:1 t=1 a=1

Y ahora a esperar a que los fabricantes lo implementen. :-(

“Atravesando” el NAT con OpenSIPS 1.6 y MediaProxy

Hoy vamos a ver como configurar OpenSIPS 1.6 y MediaProxy para solucionar los problemas que el NAT nos pueda traer.

Configurar el proxy para solucionar los problemas de NAT suele ser de las primeras cosas que se pretende o se hace al configurarlo, pero en muchas ocasiones se olvida el primer paso: entender el NAT. En su día escribí un post con una breve introducción al NAT, pero lo que os recomiendo es que os leáis varias veces éste post de Iñaki Baz Castillo explicando como afecta el NAT al protocolo SIP así así como los distintos tipos de soluciones.

En este post vamos a ver como “arreglar” el NAT con OpenSIPS 1.6 y MediaProxy. El módulo mediaproxy de OpenSIPS nos servirá en combinación con el propio MediaProxy para arreglar el SDP y hacer que el RTP pase por MediaProxy, mientras que para arreglar la parte de señalización utilizaremos el módulo nat_traversal.

El módulo nat_traversal

Este módulos nos servirá para arreglar la cabecera Contact de los mensajes SIP nateados y mantener el NAT abierto haciendo keepalive del lado servidor. El módulo nat_traversal tiene una particularidad que le diferencia de otros módulos similares: fue diseñado para tratar entornos multi-proxy, como por ejemplo el caso en que el proxy que mantiene el keepalive no sea el que gestiona los REGISTER (el registrar). No vamos a entrar en detalle en este post, pero conviene tenerlo en cuenta…

El módulo mediaproxy

Este módulo gestiona la comunicación con el servidor MediaProxy, de manera que el módulo modificara el SDP para que el audio pase por MediaProxy, que actuara de media relay. Como comente en un post anterior MediaProxy soporta ICE, por lo que podremos arreglar el NAT en prácticamente todos los casos (salvo los malditos ALGs).

Antes de empezar

Antes de comenzar la descripción de la configuración necesaria es importante comentar que en este post vamos a utilizar el método fácil. El módulo mediaproxy tiene 2 formas de funcionar:

  1. Funciones use_media_proxy y end_media_session: han de ser llamadas por el usuario, por lo tanto este ha de saber exactamente cuando quiere que MediaProxy comience o acabe su funcionamiento. Grosso modo tendremos que encargarnos del primer INVITE, los in-dialog-requests y el BYE. Lo veremos en otro post ;)
  2. Función engage_media_proxy: utiliza el módulo dialog para llamar a las funciones del punto 1 internamente. El usuario solo tiene que llamar a la función engage_media_proxy en el primer INVITE y olvidarse, el resto se realizara internamente. El hecho de que sea tan fácil también trae algunas limitaciones, que veremos mas adelante.

Arreglando la señalización

Primero vamos a configurar el módulo nat_traversal, con el que podremos arreglar el NAT en la señalización.

loadmodule "nat_traversal.so"
...
modparam("nat_traversal", "keepalive_interval", 60)
modparam("nat_traversal", "keepalive_method", "OPTIONS")
modparam("nat_traversal", "keepalive_extra_headers", "X-Keepalive: yes\r\n")
modparam("nat_traversal", "keepalive_state_file", "/var/run/opensips/keepalive_state")

Con los parámetros indicados haremos que el módulo nat_traversal mande un mensaje SIP OPTIONS cada 60 segundos para mantener el NAT abierto. Ademas, añadiremos la cabecera X-Keepalive, simplemente para identificar estos paquetes mejor.

Para hacer que el módulo comience a enviar los OPTIONS será necesario que llamemos a la función nat_keepalive(). La particularidad de esta función es que no provoca que el mecanismo comience de inmediato, sino que será activado en caso de recibir/generar una respuesta positiva a un REGISTER o SUBSCRIBE y cuando un dialogo comience con un INVITE.

...
if ((method=="REGISTER" || method=="SUBSCRIBE" ||
(method=="INVITE" && !has_totag())) && client_nat_test("3"))
{
nat_keepalive();
}
...

Ademas del keepalive, también tendremos que modificar los mensajes SIP para que la cabecera Contact contenga la IP y puerto reales desde los que vino el paquete en lugar de lo que el terminal del usuario haya puesto. También añadiremos el parámetro rport a la cabecera Via, de manera que OpenSIPS anyadira la IP y el puerto desde los que recibió el paquete a la primera cabecera Via aunque el terminal no indicara soporte para rport. Para ello incluiremos lo siguiente en nuestro request route:

if (client_nat_test("3")) {
force_rport();
fix_contact();
}

Es posible que las respuestas también vengan nateadas, porque el terminal al que llamamos esta detrás de NAT así que tenemos que tratarlas en un onreply route:

if (client_nat_test("1")) {
fix_contact();
}

Ya solo nos queda una cosa para terminar con la señalización: fijar el AVP necesario para que OpenSIPS guarde la dirección IP y puerto desde la que se ha registrado el usuario en la tabla location de su base de datos:

modparam("registrar", "received_avp", "$avp(s:received_uri)")
...
$avp(s:received_uri) = $source_uri;

La pseudovariable $source_uri es exportada por el módulo nat_traversal y contiene la IP, puerto y transporte desde los que se recibió el paquete.

Arreglando el RTP

Una vez tenemos la señalización arreglada necesitamos hacer que el RTP pase por nuestro servidor MediaProxy. Para ello modificaremos el SDP de manera que la IP y puerto desde los que se supuestamente se enviara el RTP sean realmente el servidor de MediaProxy.

Como hemos comentado anteriormente vamos a utilizar el método fácil, por lo que solo tendremos que llamar a la función engage_media_proxy() para el primer invite:

if (is_method("INVITE") && !has_totag()) {
engage_media_proxy();
}

Limitaciones

Todo no puede ser perfecto, por lo que el método que aquí hemos utilizado puede no ser el mejor para todos los casos. En concreto estas son las limitaciones que el sistema utilizado conlleva:

  • Se utilizara siempre MediaProxy: dado que la función engage_media_proxy() utiliza el módulo dialog para funcionar es necesario llamarla al comienzo del dialogo, así que llamarla al recibir un 200 OK nateado seria demasiado tarde.
  • Manejo de múltiples diálogos: tanto OpenSIPS como MediaProxy sufren al recibir múltiples respuestas con SDP a un INVITE (dos 183 por ejemplo), por lo que los resultados son inesperados.

Dado que la mayoría de usuarios están detrás de NAT y que gracias a la arquitectura escalable de MediaProxy podemos ampliar los recursos a medida que los vayamos necesitando, tendremos que valorar la facilidad en la configuración frente a la flexibilidad en la gestión de NAT.

Happy NAT traversing!