VoIP: Mecanismos de supervivencia local con Kamailio y Edge Router

Hola !

De nuevo, continuamos con la senda VoIP, pero ya volveremos pronto a temas de networking puro, seguro 😉

Planteando el escenario

Tanto empresas desarrolladoras, integradoras como nuestros propios clientes de soluciones VoIP, todos hemos interiorizado ya que el escenario de una empresa multi-delegación es ideal para el despliegue de soluciones VoIP. Si existe ya una red IP controlada que une todas las delegaciones, bien sea gestionada directamente con túneles o una MPLS contratada a un operador, el terreno ya es llano y como diríamos «ready» para la llegada de los VoIP Team’s a analizar, plantear y desplegar la solución 😉

En estos escenarios, generalmente, todos hemos visto ya de todo:

  • Dos sedes principales y luego sedes satelitales.
  • Una única sede principal y muchas pequeñas.
  • Varias, todas iguales.
  • Sede principal, dos pequeñas críticas y muchas más

La supervivencia local, que podríamos definir como: En caso de caída de la conectividad, el sistema seguirá funcionando, en modo degradado, pero garantizando tráfico in-out básico , supongo que será así como todos lo entendemos,  es por tanto un trabajo, como solemos decir: granular, cada situación es diferente, los presupuestos de infraestructuras tb lo son.Wikipedia HA Percents

Ootras opciones pasan por garantizar la conectividad con MPLS’s de otros operadores paralelas y luego BGP/OSPF, o similares, pero siempre se tiene que asumir que alguna vez caerá, sobre todo aquí, que no siempre se pueden conseguir líneas 100% independientes de operadores que no se revenden circuitos entre sí.

Lo que si tenemos como punto común, es que la supervivencia local pasa por tener enlaces PSTN locales, que, por mucho que nos duela a los amantes confesos del RFC3261, tienen una disponibilidad de unos cuantos nueves desde hace lustros  🙂  Es decir, si una sede se queda sin conectividad IP que le una con la central o la nube o x dónde tengamos nuestros Proxy’s SIP / B2BUA, la única forma de llegar a ella no es por IP, es por telefonía tradicional. Aunque, efectivamente, … alguno podría pensar: Si tenemos telefonía tradicional, tenemos IP, eso te lo hago yo con un modem y un RAS en un periquetePero que nos sale mejor, levantar un ppp over isdn para tener 128kbs totales (64+64) y por tanto 2 llamadas g729max, así como tener que mantener un RAS server en el CPD / Nube (casi inviable de pedir, con sus líneas necesarias), o permitir recibir dos llamadas por PSTN y conmutar el destino failover a nivel de servidor?

Soluciones comunes

Si nos hemos encaminado ya por la solución de enlaces PSTN para tener supervivencia en caso de caída de enlace IP, lo primero es preguntarse como conectarse:

  1. ¿Servidor Asterisk local y tarjetas Digium o similares para conectarse?
  2. ¿Gateway fanless, firmware específico para SIP<>BRI / SIP<>Analog FXO?

En nuestro caso, la primera solución no nos parece muy gestionable a la larga, tanto por temas de costes como por formato de entrega de solución (hay muchos casos de sedes pequeñas tipo gasolineras, plantas de procesamiento, oficinas en despachos mini’s, … dónde no hay ni rack ni entorno controlado, todos lo hemos vivido seguro) y mantenimiento posterior, ya que no es lo mismo gestionar actualizaciones de firmware y monitorizar por SNMP desde el minuto cero que gestionar al completo un server con su SO, con ciclos de vida y probabilidades de fallo, en nuestra humilde opinión, mayores. Eso sí, hay muchos casos en los que por posibilidades de crecimiento, por criticidad absoluta, por shared-services que se pueden dar en IT (VoIP / Systems), la opción de un servidor, con todo su potencial es la que encaja sí o sí.

En este caso, para este artículo, asumiremos que se estamos en la opción común de gateway PSTN, con 1 BRI, 2BRI’s o 1xFXO o lo que proceda que queramos dotar de capacidad de supervivencia. En este caso, existen numerosos fabricantes ya hoy por hoy, cada cual con su garantía, sus features, su aspecto físico, sus posibilidades de integración en red (Syslog, SNMP, Radius, …), pero lo que si suele ser común es que su configuración en cuanto a call routing no es del todo potente, y sobre todo, no suelen tener paralel forking (muchas veces si que tienen serial forking, pero no siempre para casos de no answer).

Por así decirlo, muchos permiten enviar la llamada entrante por BRI a una URI SIP, haciendo cierto rewrite y tal, y algunos incluso permiten hacer «hunt group» con paralel forking de hasta 5 Endpoints.

En este caso, las llamadas entrantes las tenemos mas o menos garantizadas, que suelen ser las mas importantes, para no dejar a la sede aislada, será un funcionamiento degradado (no locuciones, no control horario, entrada siempre a lo mismos SIP UAC’s …), pero aceptable.

¿Pero que pasa con las llamadas salientes? Si nuestros flamantes IP Phones / Softphones tienen puesto A.B.C.D como proxy, y A.B.C.D está unreachable, que hacemos?

Muchos de los hardphones soportan la configuración de backup’s servers, los softphones al ser multi-account tb, los gateways generalmente no permiten actuar como REGISTRAR (aunque algunos sí), así que nos tendremos que pelear un poco para permitr que usen la línea sin estar registrados y tal.

Pero, cuando conmutan de nuevo al primero ? ¿En que condiciones registran en uno y en otro? ¿Y si es un hardphone muy económico con una sola línea y sin esas posibilidades? ¿Aplican siempre las soluciones de DNS SRV / NAPTR con prioridades bien? ¿Todos los vendors lo respetan?

En nuestra opinión, tras muchos casos de supervivencias estudiadas, consultorías varias, documentaciones detalladas: Se quedan cortos.

Opción de la huerta: Plantando Tomates

Imagen cogida de www.wikibooks.comPor estos lares, se han citado ya nuestras pasiones por Tomato Firmware con algunas de sus posibilidades, la verdad es que nosotros estamos sinceramente encantados. En nuestro caso, en Irontec, como todo buen amanten del open source, nuestros primeros routers/firewalls eran GNU/Linux, con ipchains sobre kernel 2.2 y tal, y cada vez que queríamos unir una sede, pues otro pc o mini-pc o lo que encontrarás; posteriormente, con los años, te das cuenta de que eso no escala y optas mas bien por Forti, Cisco, Mikrotik (que tenemos mucho que hablar tb de ello) y otros vendors similares con electrónica fan-less y firmware totalmente específico para su función. Pero de nuevo, GNU/Linux nos vuelve a calentar la sangre, ya con mucho mas recorrido y madurez tecnológica, y en lo que respecta routing de estos escenarios lo aplicamos de forma mucho mas concisa, estando mas que satisfechos, de verdad 🙂

Esto no es ninguna exposición comercial, está claro que técnicamente con Mikrotik por el mismo precio tenemos muchísimas features, pero no las que nos interesan a nosotros:

Así que continuemos con la siembra, y tengamos una arquitectura de sede central con delegaciones unidas por VPN con Tomato Router’s para continuar con el hilo de este post, que está mas centrado en temas VoIP que de routing y embedded devices 😉

En lo que respecta el proceso de instalación, nuestra experiencia sobre los Netgear WNR3500L2 es totalmente positiva, haciéndose todo a golpe de click en escasos minutos, lo único importante a tener en cuenta es que nos interesa si o si tener desplegado Optware, existen numerosas guías disponibles.

Al tener Optware, vía IPKG podremos instalar fácilmente:

  • Kamailio 4.2 , Asterisk 1.8.25
  • Tcpdump, ngrep y tal

Es decir, tenemos full power, y con el root fs en un stick usb, todo el espacio que necesitemos 🙂

Arquitectura que queremos ilustrar

De forma resumida, el esquema sería algo así como:

arqui_global

Este esquema-borrador, en cuanto al plano físico, si nos enfocamos en la arquitectura VoIP, el planteamiento que os hacemos es el de aprovechar la posibilidad de desplegar un Proxy SIP Puro (Kamailio) en el Tomato Router y que este haga las funciones:

  • SIP Routing [hacia|desde] el CPD / Cloud /X
  • Condicional SIP Routing: Si Cloud no está disponible => Enviar a local gateway
  • SIP Routing desde gateway: Si se recibe un INVITE, paralel forking a N devices.

Es decir, algo así como:

arqui_global_con_sip

De nuevo, parece un single point of failure, está claro, pero al igual que lo sería si pones cualquier otro router 🙂 De alguna forma la sede tiene que aslir hacia Internet / MPLS no ? Nos referimos a sustituir dicho dispositivo. Al ser GNU/Linux, podemos fácilmente gestionar VRRPD, Heartbeat o lo que necesitemos, con lo que tendremos Alta Disponibilidad real.

Opción de la ciudad: Ubiquiti Edge Router-X

 

Sinceramente, la opción de los routers de Ubiquiti la adoptamos de nuestras muchas aventuras y proyectos Wifi, dónde siempre hemos tenido buenas experiencias con esta marca. Así que tanto por estabilidad, como por funcionalidad y precio (todo en este orden), nos parecen sinceramente una opción mas que recomendable.

Entrando en la cocina, los keypoints que nos interesan:

  • ubiquitiArquitectura MIPS.
  • Debian Based (6.x – Squeeze, con nuevo firmware: 7.x – Wheezy).
  • Posibilidad de instalar custom software.

Así que si lo comparamos, con la opción de Tomato Firmware, las principales diferencias:

  • Hardware/Firmware concebidos como uno.
    • Realmente, aunque todos los routers compatibles con Tomato / DD-WRT y derivados sean broadcom chipset based, no se han desarrollado como tal, no con dicha idea en mente, de que venga alguien y se haga una Tomatina.
  • Firmware desarrollado de forma continua.
    • Si analizamos los diferentes firmwares, forks y opciones de estos lares, son es fácil tener y apoyar un camino, no se ve / no vemos uno claro.
  • Sin accesorios externos necesarios
    • Para poder tener el poder completo de tcpdump, Quagga, Kamailio, Asterisk, necesitamos si o si un pendrive USB y montarlo, con lo que ya estamos añadiendo un nuevo componente y sobre todo, que no no están tampoco pensados para estar continuamente read/write.
    • Espacio. Esto sería algo a favor de Tomato, que podemos tener espacio sin problemas, a diferencia de Ubiquiti, donde tendremos que pelear por los megas 😉

 

Preparando el equipamiento

Lo bueno, como comentábamos antes, de la opción de Ubiquiti Edge Router es que es realmente Plug and Play, no hay que hacer ninguna gran ni mini ñapa, prácticamente nada mas sacarlo de la caja estamos ya running 🙂

Nada más hacer el «unboxing», del device, se puede acceder en https://192.168.1.1 ubnt/ubnt y lo primero de todo sería actualizar el firmware, a Octubre 2015 este sería el último disponible.

Firmware updating Vía web:
ubiquiti_firmware

reboot

 

 

 

 

Lo siguiente sería activar los repos:

Activación de Debian Repository:

Accedemos vía ssh (ubnt/ubnt) y ejecutamos:

configure
set system package repository wheezy components 'main contrib non-free'
set system package repository wheezy distribution wheezy 
set system package repository wheezy url http://http.us.debian.org/debian
set system package repository wheezy-security components main
set system package repository wheezy-security distribution wheezy/updates
set system package repository wheezy-security url http://security.debian.org
commit
save
exit

Una vez activado el repositorio y confirmado que tenemos conectividad, el classic apt-get update y tal para tener ya la bbdd de paquetes local ready.

Y por tanto, podremos instalar tcpdump, ngrep, … lo que queramos y/o necesitemos, rtpproxy tb por ejemplo:

root@ubnt:~# apt-cache show rtpproxy
Package: rtpproxy
Version: 1.2.1-1.1
Installed-Size: 121
Maintainer: Debian VoIP Team <[email protected]>
Architecture: mipsel

 

Compilación/Paquetización Kamailio para MIPS – Debian Wheezy (7.x)

Kamailio se encuentra en los repos oficiales de Debian, y por tanto, siempre disponible para todas las arquitecturas soportadas oficialmente. Sin embargo, solo a partir de Debian Jessie (8.x), y nuestros queridos Ubiquiti Edge Router son Wheezy Based, así que si no queremos peleas con libc6 y demás grandes paquetes, lo mejor es tirar de de paquetes built for ad-hoc. En la web oficial de Kamailio, hay paquetes auto-built, pero son para i386 y amd64.

Pero estáis de suerte, si queréis sumaros: os ahorramos las infinitas horas que tarda en compilar Kamailio en MIPSEL, nos hemos lanzado una box con QEMU-MIPS y gracias al excelente trabajo de Victor Seva, la compilación=>empaquetado es super automática.

Aquí tenéis, todo el zip: kamailio_4.3.2-MIPSEL_ALLDEBS

Si no estamos haciendo nada muy especial, el deb principal nos vale y, con la instalación del firmware default no se genera ninguna dependencia, así que básicamente:

root@ubnt:/tmp# dpkg -i kamailio_4.3.2_mipsel.deb 
Selecting previously unselected package kamailio.
(Reading database ... 30316 files and directories currently installed.)
Unpacking kamailio (from kamailio_4.3.2_mipsel.deb) ...
Setting up kamailio (4.3.2) ...
[FAIL] Kamailio not yet configured. Edit /etc/default/kamailio first. ... failed!

El paquete base de Kamailio se nos instala con todos estos módulos:

acc.so            benchmark.so      db2_ops.so        dmq.so            imc.so            mi_datagram.so    nathelper.so      prefix_route.so   rtpengine.so      siputils.so       tcpops.so         uac.so            usrloc.so
alias_db.so       blst.so           db_cluster.so     dmq_usrloc.so     ipops.so          mi_fifo.so        nosip.so          pv.so             rtpproxy.so       sl.so             textops.so        uac_redirect.so   xhttp.so
async.so          call_control.so   db_flatstore.so   domain.so         jsonrpc-s.so      mi_rpc.so         p_usrloc.so       qos.so            sanity.so         sms.so            textopsx.so       uid_auth_db.so    xhttp_rpc.so
auth.so           cfg_db.so         db_text.so        domainpolicy.so   kex.so            mohqueue.so       path.so           ratelimit.so      sca.so            speeddial.so      timer.so          uid_avp_db.so     xlog.so
auth_db.so        cfg_rpc.so        debugger.so       drouting.so       lcr.so            mqueue.so         pdb.so            regex.so          sdpops.so         sqlops.so         tm.so             uid_domain.so     xprint.so
auth_diameter.so  cfgutils.so       dialog.so         enum.so           mangler.so        msilo.so          pdt.so            registrar.so      seas.so           sst.so            tmrec.so          uid_gflags.so
auth_xkeys.so     corex.so          dialplan.so       exec.so           matrix.so         msrp.so           permissions.so    rr.so             sipcapture.so     statistics.so     tmx.so            uid_uri_db.so
avp.so            counters.so       dispatcher.so     group.so          maxfwd.so         mtree.so          pike.so           rtimer.so         sipt.so           statsd.so         topoh.so          uri_db.so
avpops.so         ctl.so            diversion.so      htable.so         mediaproxy.so     nat_traversal.so  pipelimit.so      rtjson.so         siptrace.so       stun.so           tsilo.so          userblacklist.so

Es decir, para hacer lo que queremos hacer / incluso más, nos vale de sobra 🙂

A continuación, hay que acordarse de /etc/default/kamailio y tal, como en cualquier servidor Debian.

El único punto especial a tener en cuenta en este pequeño dispositivo es que en boot-time, no sigue la secuencia init oficial, y, las actualizaciones de firmware borrarían nuestras modificaciones (tal y como lo explican muy bien en el soporte de Ubiquiti) así que basta con que nos hagamos un mini-script en /config:

root@ubnt:~# cat /config/scripts/post-config.d/kamailio 
#!/bin/bash

/etc/init.d/kamailio start

y listo, ahora en cada reboot arrancará Kamailio 🙂

Por otra parte, dejamos tb aquí disponible sngrep compilado para MIPSEL (os pedirá libncurses5 / libpcap0.8)

Configuración del escenario

Los puntos clave a tener en cuenta:

  • Los terminales tienen que tener como SIP DOMAIN / SIP PROXY: el que corresponda, el cloud / CPD / … lo que tengan normalmente.
  • Pero tb tienen que tener Outbound Proxy configurado, así se simplifica totalmente la config, y casi con la default config funcionará todo perfectamente.
  • Kamailio tiene que saber si el upstream Proxy / B2BUA / X está vivo o no, para conmutar automágicamente

Gestión de los Registers

Tal y como comentamos en el post sobre el módulo Path basta con cargar el módulo, añadir «Supported: path» y encaminarlo hacia Asterisk (que debe estar preparado).

# Register Management:
add_path();
append_hf("Supported: path\r\n");

Routing de requests

En este caso lo que mas nos interesa saber es si la request se ha originado por un SIP UAC local, ya que si es el caso, activaremos el mecanismo de supervivencia. En caso contrario, si la request viene de «arriba», no tenemos que gestionar ningún tipo de failure (no tenemos / no queremos / no hace falta 😉 ).

Dicho esto, el módulo IPOPS del gran IBC  nos encaja perfectamente:

1. Overview

The IPops module offers operations for handling IP addresses, both IPv4 and IPv6.

Así que básicamente, nos definimos la local LAN (o viceversa):

survival.localnet = "10.222.237.0/24"
survival.localgw  = "10.10.1.3/32"

y, básicamente, en las requests que inician diálogo, comprobamos si quien la origina está dentro de la LAN de supervivencia:

        if (is_method("INVITE"))                          
        {                                                      
                if (is_in_subnet($si, $sel(cfg_get.survival.localnet))){
                        xlog("L_INFO", "Request from subnet, activating survival way\n");
                        if ($rd != $sel(cfg_get.survival.localgw))     
                                  t_on_failure("MANAGE_FAILURE_SURVIVAL");

                  }                                          
        }

Gestión de failure route

Un bloque sencillo de gestión:

  • Lo activamos sólo en caso de timeout, ya que otro tipo de errores (404, 500, …) significa que «hay vida vida allá arriba», así que no queremos influenciar.
  • Respondemos con 380 y añadimos un contact
failure_route[MANAGE_FAILURE_SURVIVAL] {                              
        route(NATMANAGE);                                                     
        if (t_is_canceled()) {                                          
                exit;                                                  
        }                                                             
        if ( t_any_timeout() )                                           
        {                                                                      
                append_to_reply("Contact: sip:"+ $rU + "@" + $sel(cfg_get.survival.localgw)+"\r\n");
                send_reply("380","Try another way, follow this contact header please");                         
                exit();                                                         
        }                                                                      
}

Con esto, se gestionan bien los fallos en los que no hay respuesta del upstream proxy/b2bua, a modo de ejemplo:

flujo_30

Sin embargo, sinceramente, el tiempo de timeout / retrans default es bastante alto, el usuario seguro que no esperará tanto, así que conviene bajarlo, tal y como lo explican siempre muy bien en la documentación oficial de Kamailio:

modparam("tm", "fr_timer", 10000)

 

sngrep_tm_timer

Es decir, tenemos 10 segundos de timeout, algo que parece ya algo mas razonable 🙂

¿Que hacemos con las llamada entrantes desde el GW?

Pues lo mismo, pero a la inversa, y controlando dónde queremos que acabe:

  • ¿Envíamos siempre al mismo usuario?
  • ¿Envíamos en paralel/serial a un grupo?

 

La redirección básica, algo así como:

survival.mainuac  = "sip:xxxx@yyyy"
survival.localnet = "10.222.237.0/24"
survival.localgw  = "10.10.1.3/32"

[...]


if (is_method("INVITE")){                                                      
          if (is_in_subnet($si, $sel(cfg_get.survival.localgw))){
              xlog("L_INFO", "Req from gw: redirecting to mainuac:" + cfg_get.survival.mainuac +"\n");
              $ru= $sel(cfg_get.survival.mainuac);
              }                                  
}

¿Y como haríamos para enviar en paralelo a un grupo de usuarios (SIP UAC’s)?

Basta con usar apprend_branch, añadiendo al destination set una sip uri por cada uac al que queramos enviar en paralelo y listo.

Información al usuario

Los que llevamos años ya con Asterisk on the battlefront, sabemos que el hecho de que sea B2BUA nos da una flexibilidad muy interesante en lo que a lógicas en base a dialplan se refiere. Es decir, si una llamada falla en salida, antes de «retransmitir» con el best choice posible ese resultado a la primera pata, siempre podemos hacer un failover fácil, reproducir un playback en session progress, enviar un email, enviar audio multicast para avisar a todo el mundo que la ruta esta muerta, casi lo que nos de la gana.

Con lo que, efectivamente, tb nos podríamos instalar Asterisk en este Ubiquiti Edge Router y hacer que haga esa labora, o al menos que reproduzca el media (y encima está en los repos oficiales, sin peleas de paquetización). Pero en este caso, para hacerlo de forma elegante, utilicemos SIP, que para algo estamos en esto no ?

Si una llamada falla, no queremos que el usuario tenga que volver a marcar con algun prefix code o similares, así que vamos a informarle de forma amable que su sesión ha fallado pero que hay otra forma (380 Alternative Service), aunque realmente, no hemos encontrado ninguna documentación ni RFC oficioso que indique que es un buen camino.

Lo que si podemos hacer es responder una respuesta temporal y acto seguido encaminar por dónde proceda:

failure_route[MANAGE_FAILURE_SURVIVAL] {                                                            
        route(NATMANAGE);                                  
        if (t_is_canceled()) {                                                      
                exit;                                                 
        }                                                                                   
        if ( t_any_timeout() )                                       
        {                                                                       
                sl_send_reply("181", "Survival Activated - please standby");
                sleep(3);                                                   
                                                                            
                append_to_reply("Contact: sip:"+ $rU + "@" + $sel(cfg_get.survival.localgw)+"\r\n");
                send_reply("302","Try another way");                                                
                exit();                                                                             
        }                                                                                           
}

¿Y como se comportan los user agent? Hemos querido probar con un par de marcas por aquí:

Yealink

  • No muestran nada en pantalla.

Cisco SMB

  • Muestra la información tal cual la recibe:

survivalactivated

En el caso de softphones, el flamante Blink de AG:

  • El 181 no se muestra, se mantiene «connecting».
  • Lo que si hace es es mostrar el status reason del reply final (en este caso un 302 que estamos probando):

try_anotherWAY

Conclusión y opinión muy personal

La verdad es que parece que todo está volviendo a empezar, si, al principio – pero con control, como la industria, que pasan de acerías a «micro acerías» jejeje 🙂

Todos los que somos amantes de GNU/Linux desde los tiempos de ipchains (pre iptables), hemos tirado en algún momento de soluciones tipo «transatlántico» para transportar ovejitas. Es decir, hemos acabando montando un servidor entero en una ofi pequeña solo para actuar de pseudo-firewall y cliente/server VPN – luego te das cuenta que nada de eso tiene sentido, que hay que montar routers / devices que hagan su función directamente; sin partes móviles, con firmwares taylored para la ocasión.

Y, de repente, devops por todos lados, la construcción y autoprovisión de plataformas GNU/Linux se integran agilmente en los flujos productivos del curro profesional (Despliegue, Actualización, Mantenimiento, Métricas, Alarmas, Visibilidad …) –  y, tachánnnnnnnnnnn, un pequeño device con Linux embedded es lo que mejor encaja tanto en solución técnica como en precio 😉  (para todos estos escenarios que comentábamos).

 



¿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?

Queremos tu opinión :)