ICE !

Iep!

SIP (SDP) y algo de networking 😉 ICE! (RFC5245) es el #topic de hoy.

Hoy por hoy, es más que habitual tener una solución de VoIP hosteada en nuestro datacenter y/o cloud vPBX. En dicho escenario, a parte de todas las opciones de superviviencia que hemos comentado varias veces por aquí, el media path es algo que nos preocupa siempre a todos.

En la parte de la unión con la red pública, no hay mucha magia negra, si recibimos el audio vía SIP Trunking, nuestro RTP realizará el path completo desde la ubicación del SIP UA hasta la ubicación del servidor, lo mismo si optamos por un PRI centralizado en una ubicación. La excepción al path completo es la presencia de un enlace PSTN local en cada sede, para poder conectar directamente el media localmente.

Caso de las vPBX

En las centralitas «virtuales», dónde  es «pan para todos», no es habitual tener soporte de gateways locales y se tiende a permitir el acceso desde redes no gestionadas, para no obligar al cliente a  tener túneles o conexiones específicas – a lo sumo se le recomienda tener una salida dedicada o gestionar bien su traffic shapping / QoS.

De la misma forma, se combinan redes diferentes tanto WAN como LAN, pudiendo llegar a esquemas tipo:

blogpost_ice_escenarios_totales

En estos escenarios, que levante la mano el integrador o desarrollador VoIP que nunca ha tenido escenarios de One-Way Audio 😉 Siendo indiferente que inviertas 60k en una solución top-vendor 😉 o eches a andar una solución opensource (no necesariamente más barata 😉 ).

De hecho, aún recordamos en el equipo VoIP de Irontec cuando una clienta estaba conectada desde una red con muchas pérdidas de paquetes – no controlada, y nos dijo «La VozIP es así?«, desde entonces, se ha convertido en una frase-broma que recordamos habitualmente en los diagnósticos del equipo de soporte 😉

Es posible que se haya ganado infinito en integración y apertura, es posible que se haya gana infinito en gestión , pero el cambio de circuitos conmutados a paquetes IP y, añadiendo el componente Internet, en media paths garantizados no creo que nadie pueda decir que en global no hay muchísimas más peleas que cuando se pagaban circuitos ATM-WAN para lanzar E1-QSIG’s a precios millonarios entre las PBX de turno 😉

¿Por qué tanto lío con el NAT?

Para entrar en materia, recomendamos la lectura a partir de la página 39 de la documentación que liberamos hace bastante tiempo sobre SIP/VoIP/Asterisk en general, la verdad es que su momento le pusimos bastantes ganas, con esquemitas muy ilustrativos y todo jiji 😉 aquí van:

Realmente, el NAT no sólo es EVIL, sino que que es un demonio polimórfico 😉 , si, como aquel primer virus polimórfico del grupo 29A que tanto agradaba en antiguas épocas a nuestro verdadero Yoda del Norte 😉 NO hay un sólo tipo de NAT, ni mucho menos!, ni tampoco hay pocos routers con SIP ALG o UPNP (Que algunos incluso consideran que habría que prohibir jiji).

 

ICE: La solución definitiva

No es nada nuevo, Saghul lo comentaba ya hace más de 6 años !!! Tal y como suele repetir N^M veces OEJ, la solución no estaba en el lado del servidor, no había que repetir los errores de verticalización de las LegacyPBX de siempre, la solución está en la colaboración del SIP UA !  SIP World: The User is the King!

El proceso, muy resumido, se basa en las tecnologías ya existentes:

  • SDP: Para describir la sesión que queremos establecer, esta vez con candidatos, que luego comentarmos.
  • STUN: Nos permitirá obtener nuestra dirección IP pública y tipo de NAT,
  • TURN: En casos extremos, como «intercambiador» público 😉

La idea, en súper breve: el SIP UAC descubre sus direcciones y las añade al SDP con diferentes prioridades (primero quiere hablar directamente a su IP privada tras el clásico NAT, luego a su IP WAN de salida, y si esto no funciona, al TURN Server), en el 200 OK se envían paquetes entre las diferentes IP’s hasta saber cuales son válidas y en función de la prioridad quedarse con una.

Estas dos referencias: la de Saghul y la de Olle son muy muy buenas en cuanto a esquemas y descripción detallada de cada paso.

RTPEngine: El Media KING !

La verdad es que la gente de SIPWISE se lo ha currado muchísimo con RTPEngine, no sólo por sus posibilidades de bridging entre clientes DTLS (WebRTC DTLS is mandatory) que están tan de moda, sino por sus más que interesantes features 🙂 Entre ellas, el soporte de ICE, para gestionarlo y/o añadirse como candidato 🙂

Esto es realmente killer, sobre todo porque es control-soft-seleccionable, es decir, desde el SIP Proxy que llama a RTPEngine, se puede indicar si queremos o no mantener los candidatos ICE y añadirnos o no, o directamente quitar todos.

 

Estado del Arte en cuanto a ICE en los SIP UA’s

A fecha de hoy, no es que podamos tocar campanadas a todo volumen, es posible que sea por su complejidad de implementación o tb porque en un escenario en el que sólo queremos salvar «nuestro lado», independientemente del media path: el ITSP que usemos seguramente haga media el nat traversal / media relay.

Sea como fuere, lo que hemos podido ver en cuanto al status actual:

FabricanteModeloICE Support Status (SIP)Comentarios
YealinkTodos los modelos compatibles con firmware v81.SiGracias a Hassan Ali de SPC Universe por el firmware Beta para pruebas !
Dicho firmware será publicado en Sep 2016
SNOMTodosSoporte únicamente para firmware LyncGracias a Alberto Sagredo por la información.
CiscoTodosNo hay soporte en Cisco SMB.
Si hay soporte en Cisco 78XX con 3rd Party Firmware.
Gracias a Joaquin Lopez por la info !
AG ProjectsBLINKSi
CounterpathBRIA,XLITESi
JITSIJITSISoporte únicamente para XMPP, no hay soporte de ICE en cuentas SIP.
BelleDone CommunicationsLinphoneSoporte completo

Servidores TURN-STUN

De forma independiente a soluciones «full built-in», existen varios servidores open source de calidad, entre ellos

Lo que comparten todos entre sí es que son tanto STUN como TURN Servers, con sus respectivos usuarios.

Cabe destacar que para desplegar correctamente STUN, debemos tener 2 IP’s públicas usables.

Montando el escenario para un mini-lab

ReSiprocate Return en Debian Jessie

Quite easy 😉 Poco especial que comentar, está en los repos oficiales:

root@icestunturnzgor:~# apt-cache show resiprocate-turn-server | head -n 3
Package: resiprocate-turn-server
Source: resiprocate
Version: 1:1.9.7-5

Una vez  instalado, se configura en: /etc/reTurn/reTurnServer.config, dónde lo más destacado a comentar:

  • TurnAddress y AltStunAddress: Dódne indicamos las IP’s públicas a usar.
  • UserDatabaseFile: Para definir la ubicación del fichero de usuarios.
  • LoggingLevel: Conviene ponerlo en Info si queremos ver lo que está pasando realmente.

Los usuarios se crean por defecto en: /etc/reTurn/users.txt, con la contraseña hasheada tal que:

root@icestunturnzgor:~# echo -n irontec:turn.irontec.com:superpassXD | md5sum
466d11u6f74nothere1c11b7wtfXD11ed2bea11

Para probarlo, tan fácil como, desde este cliente consola:

Probamos  la parte STUN

Con este cliente mismamente, con PIP tal cual (pip install pystun):

zgor@zhost:~$ pystun -H turn.irontec.com
NAT Type: Full Cone
External IP: 85.85.288.3327 ;)
External Port: 54320

Esto tras una conexión con cablemodem default (Thomsom…).

Probamos la parte TURN

Para esto, podemos ir por ejemplo a esta URL de Test WebRTC  y definir que TURN Server queremos usar, clickando en el icono de settings:

settings

y posteriormente, tras dara START Test, debemos acabar con:

turntestOK
Es decir, tenemos los 3 posibles candidatos 🙂

En el log del ReSiprocate Turn Server veremos algo similar a:

INFO | 20160806-120524.563 | reTurnServer | RETURN | 140124557031168 | TurnAllocation.cxx:44 | TurnAllocation created: clientLocal=[UDP 151.aa.XXX.27:3478] clientRemote=[UDP 85.XXXX.1FF8.AAA7:37943] allocation=...
INFO | 20160806-120524.564 | reTurnServer | RETURN | 140124557031168 | TurnAllocation.cxx:114 | TurnAllocation refreshed: clientLocal=[UDP 151.aa.JJJ.27:3478] clientRemote=[UDP 85.CC.1E8.XXX7:37943] allocation= ..
INFO | 20160806-120524.564 | reTurnServer | RETURN | 140124557031168 | UdpRelayServer.cxx:30 | UdpRelayServer started.  [151.80.161.27:49154]

Muy bien, pues con esto tenemos validado que nuestro STUN/TURN Server funciona correctamente, podemos continuar con lo siguiente 🙂

Kamailio

Poco que comentar que no se haya dicho ya en N ocasiones, con los repos   es coser y cantar 🙂

No nos tenemos que preocupar de módulos, el módulo de control de RTPEngine que nos interesa está ya en el propio paquete principal:

dpkg -S rtpengine.so 
kamailio: /usr/lib/x86_64-linux-gnu/kamailio/modules/rtpengine.so

RTPEngine

Para Debian, es increíblemente fácil:

git clone https://github.com/sipwise/rtpengine rtpengine
./debian/flavors/no_ngcp
dpkg-buildpackage 
(está en dpkg-dev)

Si nos falta alguna dependencia ya nos irá avisando el propio sistema de Building de Debian.

Una vez acabamos con los paquetes necesarios para echarlo a andar:

ngcp-rtpengine_4.6.0.0+0~mr5.0.0.0_all.deb
ngcp-rtpengine-daemon_4.6.0.0+0~mr5.0.0.0_amd64.deb
ngcp-rtpengine-dbg_4.6.0.0+0~mr5.0.0.0_amd64.deb
ngcp-rtpengine-iptables_4.6.0.0+0~mr5.0.0.0_amd64.deb
ngcp-rtpengine-kernel-dkms_4.6.0.0+0~mr5.0.0.0_all.deb
ngcp-rtpengine-kernel-source_4.6.0.0+0~mr5.0.0.0_all.deb
ngcp-rtpengine-utils_4.6.0.0+0~mr5.0.0.0_all.deb

 

Una vez instalado, conviene validar que tenemos bien cargado el módulo para in-kernel packet forwarding:

root@rtpenginezgor:/usr/src# lsmod | grep RTP
xt_RTPENGINE 26683 3 
x_tables 27308 5 ip6table_filter,ip_tables,xt_RTPENGINE,iptable_filter,ip6_tables

La configuración en Debian, en su ubicación últimamente habitual: /etc/default/ngcp-rtpengine-daemon

Nos interesa principalmente:

LISTEN_TCP=127.0.0.1:25060
LISTEN_UDP=127.0.0.1:12222
LISTEN_NG=127.0.0.1:22222
LISTEN_CLI=127.0.0.1:9900
INTERFACES="public/151.80.XXX.YYY"

Con ello, tenemos definido que RTPEngine escuche en los puertos de control que queremos, y en loopback, para evitar problemas de seguridad, y definimos su IP Pública (la que anunciará en los SDP’s y utilizará).

Primeras pruebas

Para estas primeras pruebas vamos a utilizar un Yealink T46G con soporte ICE, y un softphone XLite, en redes inicialmente accesibles y finalmente totalmente separadas.

Algo tan simple como:

esquema_brias_yealink

Configuración en ambos

La configuración en ambos, bastante trivial:

 

yealink_xlite

 

Prueba de llamada en red enrutada

En este primera prueba, realizamos la llamada entre XLite (10.10.4.66) y el Yealink con el firmware Beta (10.10.1.230).

Lo que es el call-flow:

call_flow_global

Como se ve, nada realmente especial que comentar, salvo el ReInvite por parte del Xlite (SIP UAC en este caso), si los comparamos, gracias a nuestro amado SNGREP:

comparativa_sdps

Como podemos observar, en la transacción inicial (INVITE) que inicia el diálogo, XLite propone tanto su IP nativa (host) como su public WAN IP (server reflexive), y, en su Reinvite, ya ha recibido tráfico por parte del terminal Yealink desde su IP Host tb (se intercambian tb con STUN), por lo que el terminal acaba diciendo que quiere hablar en su NIC nativa y que la IP origen desde la que recibe el tráfico ahí es 10.10.1.230, es decir: ICE ha hecho su trabajo perfectamente !

Cambio de red y prueba de llamada en redes independientes 100% aisladas

Muy bien, hasta aquí todo «fácil», si no hubiera existido ICE tb hubiera funcionado, su IP’s plenamente accesibles, pero que pasa si les aislamos?

Pasamos al escenario del esquema (el de la derecha), y de nuevo, el flujo:

kam_final

Por comentar el flujo: En el centro el Kamailio (sin media proxy, ni nada), y en los lados las IP’s de WAN de salida diferentes, esta vez hemos optado por poner aliases en sngreprc 😉

Se observa el mismo ReInvite, escogiendo al mejor candidato

Peroooooooooooooo:

¿Cómo es posible que haya audio bidireccional sin media proxy y sin haber realizado ningún tipo de NAT? ¿WTF?

Esto es posible porque estamos ante un full-cone nat 😉 Si, lo que decíamos, NAT es una bestia de múltiples caras, más allá del clásico port-fowarding. Estamos ante este esquema exactamente:

nat_fullcone

El cliente SIP, gracias a STUN, ha conseguido saber su IP Pública, y, gracias a las siguientes fases de STUN ha conseguido saber que está en un NAT Fullcone y «se ha quedado» con el puerto allocateado 😉

Si, así es la vida, cualquiera que mande tráfico hacia esa WAN:PUERTO está permitido hacia dentro 😉

De hecho, si miramos desde la perspectiva diferenciada de ambos usuarios SIP:

Perspectiva XLite (UAC)

Activamos sngrep con -r y vemos el flujo rtp:

ice_perspectiva_XLITE

Perspectiva Yealink

ice_perspectiva_yealink

Continuando con las pruebas con RTPEngine

En la fase anterior, hemos visto que dos usuarios SIP pueden hablar entre ellos perfectamente, simplemente con la ayuda de un STUN Server.

¿Pero que pasa por ejemplo con los Fortinet? Por defecto no activan el comportamiento Full Cone, debe hacerse a mano, desde el CLI, acorde al handbook de FortiOS:

fortinet_no_fullcone

Es decir, en este escenario, no habría forma de hacer el nat piercing desde ambos lados a la vez.

Soluciones:

  • TURN.
  • RTPEngine como candidato adicional, que será el caso que ilustremos.

Configuración en Kamailio para invocar RTPEngine

La documentación del módulo, como siempre en el proyecto Kamailio, muy detallada, nos indica claramente:

ice_rtpengine

Así que para probar, con la configuración por defecto de Kamailio mismamente, en la ruta de NAT_MANAGE, podemos añadir:

 

Overwrite de todo y único candidato RTPENGINE (ICE=force)

if (has_body("application/sdp"))
rtpengine_manage("ICE=force");

Con esto, el flujo general, desde la perspectiva del servidor Kamailio:

kamailio_rtpengine_forceICE

De este gráfico, cabe destacar que el RTPEngine, tiene que saber el origen del cliente Yealink, que saldrá tras NAT, y se hará el «learning» de su puerto público.

Al recibir tráfico «early» desde el cliente XLite (tras WAN2), decide hacer el relay del RTP hacia la IP del SDP del 183 inicial, hasta que hace el proceso comentado de aprendizaje de su IP y puerto, y entonces y sólo entonces cambia el destino del RTP.

Si comparamos los SDP’s que nos llegan del INVITE:

comparativa_sdp_invite

y del 200 OK:

ice_200OK

Respetar ICE y añadirse únicamente como candidato relay

if (has_body("application/sdp"))
rtpengine_manage("ICE=force-relay);

El diálogo completo nos quedaría tal que:

additional_relay

Como vemos, a pesar de haber activado la captura RTP, no se ve ni un sólo paquete, ya que RTPEngine se ha añadido  como candidato relay, al ser ambas conexiones full cone NAT, el candidato ICE de tipo Server Reflexive ha ganado la negocicación y el audio va entre ellos 🙂

Realmente, este sería el caso buscado generalmente, así podemos conseguir que el media path de Alice y Bob sea «el best of»:

alicebobmapafinal

Concluyendo …

De ICE se puede hablar muchísimo, de hecho, basta con buscar un poco de info – NO necesariamente SIP RELATED – para encontrar muchísimas referencias que lo explican francamente bien, nos quedamos con estas como referencias ampliadas a este humilde post:

 

Trickle ICE

La verdad es que el path de ICE si que es solución definitiva, buscar el P2P es siempre mejor, al contrario que tender a centralizarlo siempre todo, ahora con mediaproxys y tal, en escenarios internacionales con saltos cross-oceánicos, es un camino hacia el delay; de hecho, en la galaxia WebRTC es todo mandatory lo que respecta ICE.

Sin embargo, lo que si es cierto es que desde el punto de vista de la usabilidad, los tiempos de candidate gathering pueden ser muy altos – sobre todo si estamos con por ejemplo interfaces VPN levantados que no tienen conectividad con el endpoint contrario.

Por ello, Trickle ICE, con  el envío progresivo de candidatos ha emergido como la solución evolucionada, en este post de Emil Ivov (con intro del grandísimo WebRTC Master Victor Pascual)

Pues nada, nada más por hoy en estos lares !

PD Final: Todas las IP’s que hemos puesto aqui, las hemos apagado, ya no existen, así no nos liábamos a tener que hacer el masking de los datos de IP’s 😉 😉



¿Te gusta este post? Es solo un ejemplo de cómo podemos ayudar a tu empresa...
Sobre Gorka Gorrotxategi

CTO en Irontec, en el frente técnico desde un par de lustros ya, para todo lo que tenga que ver con Networking, VoIP y Sistemas, en ese orden :D) Desde @zetagor escribo algo, pero poco verbose la verdad

1 Comentario

¿Por qué no comentas tú también?


  • Como siempre, grande el artículo! Para enmarcarlo!
    Ilustrativo, pedagógico y bien explicado.
    Toca leerlo de nuevo con más calma, pero chapeaux!

    Elio Rojano Ruiz Hace 8 años Responde


  • Mola. Bien explicado Zgor.

    manwe Hace 8 años Responde


  • […] NAT en SIP se ha hablado largo y tendido en todos lados, incluso por aquí hemos comentado tb algo, creo que cualquier ingeniero de voz ip la primera vez que habla con el ingeniero de redes […]

    SIP Behind NAT: Engañar al NAT con más NAT - Blog Irontec Hace 7 años Responde


Queremos tu opinión :)