Issabel: una alternativa de código abierto

13 Dic

Hace poco menos de una semana, Elio Rojano nos dió la noticia de la compra de Elastix por parte de 3CX, movimiento con el cual todos sabríamos que una de las distros con Asterisk más usadas se vendría abajo y pasaría a ser completamente de código cerrado.

A pesar de diversos comentarios sobre los directivos de 3CX sobre que mantendrían la comunidad, lo que pasó fue todo lo contrario y comenzamos a ver a varios ex-integrantes de la comunidad siendo bloqueados del acceso a los foros de discusión por el simple hecho de tener algo que ver con FreePBX, la «competencia» open source de 3CX:

Sin embargo, con cada puerta que se cierra se abre una ventana, y es aquí donde existe una oportunidad con Issabel.

En la página principal no nos aparece demasiada información, pero a partir de los comentarios que tienen en sus foros de discusión y de varios otros que he percibido en Twitter, creo que podemos asumir lo siguiente:

  • Se basará en la versión 4.0 de Elastix, la cual usaba CentOS 7.
  • Será un drop-in replacement para Elastix, por lo que debería poder actualizarse de manera semi automatizada (cambiando repositorios).
  • El acercamiento al código deberá ser mucho más transparente, ya que ahora existirá un repositorio abierto en Github para control y monitoreo de cambios.
  • Será creada a partir de una comunidad, sin un respaldo único empresarial por detrás (a diferencia de Elastix cuyo patrocinador Palosanto dictaba todos los cambios y rumbos a seguir).

Quienes me conocen saben que desde hace algún tiempo dejé de utilizar Elastix como mi principal distro por el muy lento nivel de desarrollo que tuvo en los últimos meses (¿o años?) sobre sus componentes de código abierto, y que en su lugar comenzó a concentrarse mucho en funcionalidades comerciales, tales como su versión Cloud o la versión Business Edition. Personalmente, solo me queda esperar que al ser un esfuerzo de la comunidad para la comunidad, la historia a contar ahora sea diferente, y que pronto aquellos que le dieron el apoyo con su tiempo, dinero y esfuerzo obtengan el producto que realmente se merecen.

Descomercializando a FreePBX

8 Dic

Noticias como la del día de ayer de que 3CX adquirió a Elastix nos tomaron a muchos por sorpresa, pues una de las distribuciones de código abierto de Asterisk más conocidas del mundo pasaba a ser propiedad de una empresa que, de entrada, cambió por mucho el alcance del proyecto al poner limitantes en cuanto al uso de la versión «gratuita» del nuevo Elastix 5.

Como resultado de lo que muchos llaman la muerte del proyecto Elastix como lo conocemos, las miradas empezaron a voltear hacia FreePBX: la interfaz gráfica de facto para Asterisk, y de las pocas (junto con IncrediblePBX y Xivo) que siguen operando bajo la modalidad libre.

FreePBX también pasó por lo mismo en enero de 2015, cuando Sangoma adquirió a Schmooze, la empresa que dirigía el proyecto original. Al hacerlo, Sangoma también se ganó los derechos de enfocar el código hacia su propia visión y comenzar a incluir varios (si no es que muchos) componentes comerciales en las nuevas versiones del software.

Esto último molestó a muchos (yo me incluyo), pues el feeling que me dejó es que un proyecto abierto pasaba a recibir comerciales, y si bien esta comercialización del producto es una de las cosas que mantiene viva a la empresa que le da mantenimiento (entiéndase: Sangoma), al ser un proyecto bajo licencia GPL el usuario final no está obligado a usar ninguno de estos módulos, que de hecho, son muy fáciles de retirar.

Si eres como yo y quieres quedarte con un producto que sea 100% de código abierto sin sentirte molestado por los módulos comerciales, simplemente ejecuta los siguientes comandos desde tu consola de FreePBX:

for i in $(fwconsole ma list|grep Commercial|grep -v 'Not Installed'|cut -d' ' -f 2); do fwconsole ma uninstall $i; done

Esta simple línea de código buscará todos los módulos comerciales que se encuentren instalados en el sistema y los eliminará, uno por uno. Si acaso el comando te da un error diciendo que no es posible desinstalar un módulo porque otros dependen de él, simplemente repite el comando 2 o 3 veces para que primero se desinstalen las dependencias base y después el módulo que necesitas.

Ya después de eso solo necesitas aplicar los cambios en el GUI para que todo funcione como antes, y tendrás un sistema FreePBX 100% descomercializado.

¡Suerte!

La cultura del «todo gratis» está matando al Open Source.

7 Dic

Antier por la tarde un tweet de un desarrollador de FreePBX me llamó la atención. «Wow… Acabo de escuchar que 3CX está comprando a Elastix…»

De inmediato pregunté a sus creadores si el rumor era cierto. No obtuve respuesta.

Hoy por la mañana Elio Rojano a través de su sitio Sinologic.net ha confirmado lo peor: Elastix ha sido vendido a una empresa cerrada que trabaja bajo Windows y que ahora renombrará Elastix como 3CX for Linux

Todos los detalles pueden encontrarse en el sitio de Elio: https://www.sinologic.net/blog/2016-12/adios-elastix.html

Lo que a mi me preocupa y que la «comunidad» no ha alcanzado a ver es que la cultura del Todo gratis mata lentamente los proyectos FOSS, nos pasó con Trixbox hace unos años, nos pasó con MySQL y existen varios ejemplos más de cómo proyectos se vienen abajo por nada más y nada menos que la plata.

Ya lo decía alguien -que no encuentro la fuente- en mi timeline de Twitter, que aquel que quiera hacer negocio basado en Open Source (OS) debe estar loco o engañandose. Y es cierto todas las comunidades de OS están repletas de esos usuarios que ven una oportunidad de venta sin sacrificio. Lo he repetido una y otra vez hablando de Elastix, siempre hubo usuarios que sólo entraban a los foros a preguntar «¿Cómo hago esto?» «¿Cómo implemento aquello?» «Tengo una empresa de VoIP y me urge resolver esto a mis clientes». En cuanto se les mencionaba el soporte de pago huían (en el mejor de los casos). En el peor decían que era obligación de los creadores dar soporte gratis. Yo lo viví cuando trabajé en 2 de las empresas más fuertes de VoIP de la Ciudad de México: recibí llamadas de gente exigiendo soporte gratuito porque Asterisk es OS.

Esa es la gente que daña a la comunidad. También está la otra cara de la moneda: aquellos que apoyan mediante donaciones, participando en foros de discusión, reportando bugs, creando parches, módulos, etcétera. Lamentablemente son los menos.

Ahora bien como lo comenta Christian en un tweet, habrá que esperar y ver si esto puede ser un paso a las mejoras:

Siempre he sido un escéptico de las versiones enlatadas y sé que esto para los que saben Asterisk puro no representa ninguna tragedia. Sin embargo, como la única distribución creada por latinos para latinos es un fuerte golpe a la comunidad de habla hispana, o una estrategia para solo dejar FreePBX en el mapa. Cualquiera que fuese el resultado, la ventaja de que Asterisk sea un toolkit es que cualquiera con mínimos conocimientos podría crear la nueva distro enlatada para ser el una de las alternativas.

¿Cuantos más 2016, cuántos más?

Valida tus teléfonos móviles y fijos con la base de datos del IFT

13 Ene

Plan de numeración Instituto Federal de Telecomunicaciones

La versión más reciente de este código la podrás encontrar en Github.

En México tenemos diferentes mecanismos de marcación de un número, dependiendo si este es fijo (01) o celular (044 o 045). Esto complica un poco las cosas, ya que el identificador de llamadas (callerid) siempre es de 10 dígitos, por lo que si recibimos una llamada de un número X, no podemos simplemente devolver la llamada a este número ya que tenemos que prefijarlo según el tipo de teléfono destino que es.

Afortunadamente, el Instituto Federal de Telecomunicaciones (IFT) publica una base de datos que determina si una serie telefónica corresponde a un número móvil o fijo. Dado que aquí los teléfonos los marcamos diferentes dependiendo del tipo de línea, es muy importante tener presente siempre que tipo de teléfono es el que estamos llamando para ajustar nuestro plan de marcación acorde.

Esto resulta muy conveniente de hacer en sistemas de marcación predictiva donde las listas pueden estar capturadas a 10 dígitos y nosotros convertir cada número a fijo o móvil según la base nos lo diga. En Asterisk, esta funcionalidad es muy fácil de implementar con una base de datos, una función personalizada de ODBC (func_odbc) o un AGI. Aquí te dejo la guía de como lograrlo utilizando un AGI.

Antes de comenzar, asumimos lo siguiente:

  • Que ya tienes instalado PHP en tu sistema.
  • Que ya tienes instalado y cuentas con experiencia con MySQL y que sabes como importar tablas a partir de un archivo SQL.
  • Que la base de datos de nombre asterisk ya está creada.
  • Que entiendes el plan de marcación básico.

Dando esto por sentado, comencemos:

Paso 1. Consigue el listado de numeración del IFT

Ya me encargué de limpiar la lista y dejar solamente estos 5 campos:

  • Código de área (int)
  • Serie (int)
  • Numeración inicial (int)
  • Numeración final (int)
  • Móvil (bool)

Puedes descargar el archivo ya en formato SQL listo para importar. Si prefieres conseguir el archivo por tu cuenta directamente del IFT y actualizarlo tu, puedes conseguirlo desde esta liga.

Una vez descargado, descomprímelo e impórtalo en MySQL. En este tutorial asumiremos que la tabla se creará en la base de datos asterisk. Si lo quieres poner en una base de datos diferente, edita el archivo SQL y apúntalo a donde necesites.

Para proceder, necesitas la contraseña de root de MySQL:

mysql -p asterisk < bdift_safe.sql

Esto creará la tabla ift en la base de datos asterisk. Ahora crearemos el AGI para poder consultarla.

Paso 2. Crea el AGI ift.php

Para el siguiente paso, crearemos un AGI que se encargue de consultar la tabla creada en el paso anterior. El AGI lo crearemos con el nombre /var/lib/asterisk/agi-bin/ift.php.

#!/usr/bin/php -q
<?php

// Cambiar esta constante por el codigo de area local desde donde se originan las llamadas
define('LOCAL','55');

// Parametros de acceso a la base de datos.
$db['user'] = 'asteriskuser';
$db['pass'] = 'asteriskpass';
$db['name'] = 'asterisk';
$db['host'] = 'sql.enlaza.mx';

// ***************** No cambiar nada a partir de este punto

require_once "phpagi.php";
$agi = new AGI();

if ( (!is_numeric($argv[1])) || (strlen($argv[1]) < 10) ) {
	$agi->verbose('No se proporciono un numero de 10 digitos');
	$agi->set_variable('MOVIL',0);
	$agi->set_variable('PREFIJO','');
	$agi->set_variable('COMPLETO','');
	exit;
}

// Nos quedamos solo con los ultimos 10 digitos para asegurar que quitamos cualquier prefijo
$numero = substr($argv[1],-10);

if (!$data = mysql_connect($db['host'],$db['user'],$db['pass'])) {
	$agi->verbose('Error de conexion a la BD');
	$agi->set_variable('MOVIL',0);
	$agi->set_variable('PREFIJO','');
	$agi->set_variable('COMPLETO','');
	exit;
}

// Definimos codigos de area de 2 digitos para conocer cual es el codigo de area y cual es el numero local
$areas = array('55','81','33');
if (in_array(substr($numero,0,2),$areas)) {
	$area = substr($numero,0,2);
	$local= substr($numero,2);
	$serie= substr($local,0,4);
}
else {
	$area = substr($numero,0,3);
	$local= substr($numero,3);
	$serie= substr($local,0,3);
}

$query = "SELECT movil FROM ".$db['name'].".ift WHERE SUBSTRING('$numero',7) BETWEEN inicial AND final AND `area` = $area AND serie = $serie LIMIT 1";
$result = mysql_query($query,$data);
$row = mysql_fetch_array($result);
$agi->set_variable('MOVIL',$row['movil']);

if (substr($numero,0,strlen(LOCAL)) == LOCAL) {
	if ($row['movil'] == 1) {
		$agi->set_variable('PREFIJO','044');
		$agi->set_variable('COMPLETO', '044'.$numero);
	}
	else {
		$agi->set_variable('PREFIJO','');
		$agi->set_variable('COMPLETO', $local);
	}
}
else {
	if ($row['movil'] == 1) {
		$agi->set_variable('PREFIJO','045');
		$agi->set_variable('COMPLETO', '045'.$numero);
	}
	else {
		$agi->set_variable('PREFIJO','01');
		$agi->set_variable('COMPLETO', '01'.$numero);
	}
}
exit;
?>

Para que funcione es necesario cambiar:

  • La constante LOCAL. Reemplázala por el código de área de tu población.
  • Los parámetros de conexión a tu base de datos.

El AGI es bastante sencillo y lo que hace es:

  1. Recibe como argumento un número de 10 dígitos. No debe tener más ni menos.
  2. Descompone ese número y extrae el código de área, ya sea de 2 dígitos (México, Guadalajara o Monterrey) o de 3 dígitos (resto del país) y la serie del número local.
  3. Compara estos números contra la base de datos creada.
    1. Si encuentra el registro, lo cataloga de acuerdo al campo movil de la tabla.
    2. Si no lo encuentra, lo cataloga como fijo.
  4. Para la entrega de resultados, el AGI crea las siguientes variables:
    1. ${MOVIL}. Es 0 o 1, dependiendo si el número se considera fijo o móvil, respectivamente.
    2. ${PREFIJO} es el prefijo de marcación: 01, 044 o 045.
    3. ${COMPLETO} contiene el número tal cual se tiene que marcar de acuerdo al plan de numeración de México. Es la combinación de ${PREFIJO} y de ${EXTEN}. Si el número es fijo y está en el mismo código de área que el AGI, entonces te lo entrega en 7 dígitos.

Ejemplos de corrida. Asumimos que la variable LOCAL es configurada con 33 (Guadalajara):

  • AGI(ift.php,5546144400) te da:
    • ${MOVIL} es 0, ya que el número es fijo.
    • ${PREFIJO} es 01, ya que es considerado larga distancia nacional.
    • ${COMPLETO} es 013346144400
  • AGI(ift.php,5513208860) te da:
    • ${MOVIL} es 1, ya que el número es celular.
    • ${PREFIJO} es 045, ya que es considerado celular con LDN.
    • ${COMPLETO} es 0455513208860.

Paso 3. Crea el plan de marcación

El último paso es utilizar las variables que el AGI crea para hacer la marcación. El plan de marcación se colocará en el extensions.conf de /etc/asterisk, y será algo más o menos así:

exten => _NXXXXXXXXX,1,AGI(ift.php,${EXTEN})
same => n,Dial(DAHDI/g0/${COMPLETO},,Tt)

Si estás utilizando FreePBX o Elastix, el plan de marcación lo pondrás dentro del extensions_custom.conf, y la troncal la crearás de tipo custom para que pueda apuntar a este plan de marcación personalizado.

Con este AGI puedes implementar una marcación uniforme y evitar muchos posibles errores de marcación (muy útil si esto lo haces en un callcenter). Actualmente, muchos de nuestros clientes utilizan una variante de este script y sus sistemas funcionan sin problema.

¿Alguna duda? Déjala en los comentarios.

¡Suerte!

Generador de contraseñas seguras para Asterisk/Elastix

8 Sep

CapturaDice el dicho que una cadena es tan fuerte como su eslabón más débil, y en muchos de los casos, las contraseñas inseguras que ocupamos en las extensiones de nuestro conmutador son la principal causa por la que nos llevamos desagradables sorpresas como recibir cuentas millonarias porque un atacante ganó acceso a nuestro equipo y curso tráfico no autorizado con alto costo para nosotros.

Como instalar equipos basados en Asterisk es parte del pan de todos los días, optamos por crear un generador de contraseñas seguras que te permita de manera rápida:

  1. Crear un pool de cadenas de texto aptas para ser utilizadas como contraseñas.
  2. Que permita generar la configuración de un sistema con interfaz gráfica (por ejemplo: Elastix) de manera rápida y con la plantilla default creada, descargándola como un CSV listo para importar.

Hemos estado utilizando esta herramienta ya por varios meses, pero hoy la damos a conocer para que todos ustedes puedan aprovecharla. Para usarla, solo deben proporcionar 3 datos:

  1. La lista de extensiones sobre la cual generar las contraseñas. Pueden usar valores individuales (ej. 101, 102, 105, etc) o rangos (ej. 101-200).
  2. Una clave inicial que servirá como semilla para generar las contraseñas seguras.
  3. La longitud deseada para tus contraseñas.

El sistema te regresa una cadena de texto con el alfabeto 0-9a-zA-Z. No utilizamos caracteres especiales porque sabemos que hay algunos sistemas que no te los reconocen, así que preferimos irnos a la segura y desplegar lo universal.

La aplicación es libre y puedes utilizarlas las veces que necesites. La única restricción es que por cuestiones de rendimiento, solo puedes generar lotes de hasta 500 extensiones.

Puedes comenzar a utilizar esta herramienta desde la siguiente liga (nota: se encuentra hospedada en el dominio de Enlaza Comunicaciones).

¡Suerte!

Elastix Addon: EasyVPN

2 Jun

Easy VPN Logo.PNG.Hola, como ya lo venía diciendo en mi cuenta de Twitter, en Enlaza Comunicaciones hemos estado trabajando en un Addon para los usuarios de Elastix. Después de algunas semanas, finalmente ha sido publicado en el sitio de Elastix Addons y ya está disponible para su descarga e instalación desde su Elastix (PDF de instrucciones [4.2MB]).

¿Pero de que va este Addon? En Enlaza tomamos mucho en cuenta la seguridad de los PBX dado que sabemos que al día se reciben cientos de ataques a PBX por los puertos expuestos de SIP, IAX, SSH, entre otros. Es por esta razón que nosotros usamos, siempre que es posible, una conexión a través de OpenVPN tanto para administrar los sistemas a los que les damos soporte como para crear extensiones remotas.

Como la mayoría de ustedes saben y conocen, OpenVPN es una herramienta Open Source que nos permite crear VPNs seguras entre nuestros equipos, esta herramienta es la más popular en ambientes Unix para crear túneles seguros entre nuestras redes. Si bien existen miles de tutoriales en la WEB para crear los túneles y configuraciones de OpenVPN y seguramente existen ya muchos usuarios con esta herramienta instalada en sus PBX, nos decidimos a crear un front-end web intuitivo y fácil de usar para usuarios que prefieran lo visual(WEB) al texto(consola de Linux).

Con el addon EasyVPN podrán:

  • Crear la configuración de la VPN en tan solo 5 sencillos pasos.

  • Crear los certificados de los clientes con un par de clicks .

  • Ver y administrar los clientes conectados al momento.

  • Crear certificados para clientes Windows.
  • Crear certificados para clientes Linux.
  • Crear certificados para teléfonos Yealink.
  • Crear certificados para teléfonos SNOM.

Entre los beneficios de utilizar EasyVPN están:

  • Utilizar sólo un puerto para la comunicación desde el exterior. Ya no será necesario abrir o natear puertos en el firewall para servicios específicos (SIP, IAX, SSH, HTTPS, etc).
  • Toda la comunicación entre los clientes y el servidor estará cifrada, nadie podrá leer los paquetes que se transmitan.
  • Los accesos estarán controlados. El administrador sabrá quien está conectado y desde qué IP.
  • Ideal para troncalizar Asterisk en distintas ubicaciones físicas.
  • Utilizar OpenVPN Connect en iOS y Android para registrar extensiones remotas en smartphones y/o tablets.

Todo el feedback, sugerencias y reportes de bugs serán bien recibidos en el thread oficial «Nuevo Addon para Elastix EasyVPN» en los foros Asterisk México. Así que a probarlo a fondo y de ser posible reportar fallos.

Asterisk 12

20 Ene

Hola, como muchos de ustedes ya se habrán dado cuenta desde el pasado Diciembre el grupo de desarrolladores de Asterisk liberó la primer versión  estable de Asterisk 12. A pesar de que no es un Branch LTS, Asterisk 12 será (en mi opinión) un parte-aguas con respecto al modo de trabajar y desarrollar soluciones basadas en Asterisk.

Se preguntarán: ¿Qué es lo que lo hace tan especial? Bueno, técnicamente hablando Asterisk 12 es la fusión del proyecto Asterisk y Asterisk SCF, lo cual da como resultado una nueva arquitectura, los desarrolladores están pensando en dejar de lado el «bugguiento» chan_sip (aún se puede usar en Asterisk 12) por el nuevo core de SIP que usa PJSIP; además de un nuevo modelo de desarrollo usando ARI (no confundir con la interfaz de usuario de FreePBX) y de STATSIS.

Siguiendo la línea técnica habrá mucho que aprender ya sea para desarrollar soluciones basadas en ARI (Asterisk REST Interface), familiarizarse con el bus de mensajes de STASIS, comprender los nuevos registros que generará el CDR, aunque el más importante será la migración a CHAN_PJSIP.

De este último podemos decir que la configuración cambia bastante con respecto al «viejo» CHAN_SIP, y es que ahora no solo basta con crear un peer y las definiciones que generalmente usamos como: secret, host, allow, disallow, qualify, context, etc. No, ahora necesitaremos: definir un tipo de transporte para ser usado por el endpoint, crear el endpoint, crear el AOR (Address Of Record) del endpoint, crear las autenticaciones del endpoint (auths), entre otras. Por ejemplo: si se tratase de una línea SIP o si hay que registrarse o si estamos fuera de la red. Suena complejo, ¿no? Lo cierto es que con el uso diario uno se va acostumbrando. Ejemplifico lo anterior:

Sintaxis para Asterisk 1.X, 10 y 11

[1000]
context=home
secret=mysupersecret
host=dynamic
disallow=all
allow=ulaw
allow=g729
callerid="Casa"<1000>
qualify=yes
....

Sintaxis para Asterisk 12:

;TRANSPORTE
[simpletrans]
type=transport
protocol=udp
bind=0.0.0.0

;Dispositivo 1000
[1000]
type=endpoint
context=casa
disallow=all
allow=ulaw
allow=g729
transport=simpletrans
auth=1000
aors=1000

;AUTH
[1000]
type=auth
auth_type=userpass
password=mysupersecret
username=1000

;AOR
[1000]
type=aor
max_contacts=5

Tuve la fortuna de probar las versiones alphas de Asterisk 12 cuando el CHAN_PJSIP se llamaba CHAN_GULP y una de las funciones más atractivas tanto para los desarrolladores como para los usuarios es la posibilidad de registrar más de un dispositivo SIP a la misma cuenta y llamarlas sin necesidad de usar el & en la aplicación Dial. Bueno, la primera vez que lo usas es como comer helado. Para poder hacer esto hay que especificar en la sección AOR cuantos contactos/dispositivos pueden «registrarse» usando la misma información, para el ejemplo anterior serán 5 los dispositivos capaces de usar la información del ENDPOINT 1000, o sea que podemos registrar el télefono IP de la oficina, el teléfono IP de la casa, el softphone del Celular y aún así nos quedan 2 libres para poder conectar un softphone basado en WS por ejemplo o un softphone en cualquier otro sitio de emergencia. Para llamar a todos los contactos registrados basta invocar esta línea en nuestro dialplan:

exten => 1000,1,Dial(${PJSIP_DIAL_CONTACTS(1000)})

Eso sí, tendremos que poner especial atención al CDR cuando llamemos a nuestros contactos porque los demás aparecerán como UNANSWERED.

Hablando de la Interfaz REST de Asterisk(ARI) surgen un sin fin de posibilidades para programar lo que se les ocurra en el lenguaje de programación que deseen, un ejemplo muy vistoso de como usar esta nueva interfaz esta en esta dirección http://ari.asterisk.org/ y usa swagger-ui como ‘core’ para crear la documentación interactiva. Para aquellos que estén muy familiarizados con las APIs REStfull y JSON se les hará pan comido, a su servidor le da dolores de cabeza. Actualmente existen proyectos dedicados a usar ARI para reemplazar las applicaciones Voicemail y APP_QUEUE de Asterisk, lo cual lo hace muy interesante, en el canal de Youtube de  Asterisk está la conferencia de Paul Belanger sobre esto.

Sobre el nuevo bus de mensajes llamado STASIS no tengo mucho que decir ya que aún lo sigo probando y tratando de entender, así que para que doy mala información, lo único que puedo decir es que con la aplicación STASIS podremos generar eventos bajo demanda de un canal para después obtener dichos eventos vía un Websocket. Útil para esas aplicaciones que necesitan estar monitoreando llamadas(y no, no es como AMI de hecho ni ARI ni STASIS están hechas para reemplazar AMI).

Desde la perspectiva del usuario no habrá mucho cambio, salvo que tendrá un sistema mas robusto y si usan PJSIP un canal mas estable con menos BUGS.

Desde la perspectiva del integrador o desarrollador, sólo es cuestión de leer y leer; pero seamos honestos en este mundo de Linux siempre estamos leyendo y leyendo, lo cuál hará  el proceso mas llevadero. Hablando de la instalación surgen nuevas dependencias como PJPROJECT, JANSSON, LibSRTP(esta última la deberías instalar con todos tus asterisk en realidad). Para aquellos que siempre quisieron un Asterisk enfocado a ‘multi-tennant’, Asterisk 12 será su mejor amigo. Y lo mejor de todo es que para aquellos que son FAN FAN de las WEB GUI de Asterisk, la excelente noticia es que FreePBX ha sacado ya una versión beta para usar Asterisk 12, ha sido un gran trabajo en conjunto con los desarrolladores de Digium y sólo como probadita te dejan escoger entre Asterisk 11 o Asterisk 12, si deseas usar sólo CHAN_SIP, o sólo CHAN_PJSIP o ambos.

Viene un camino largo por recorrer con Asterisk 12 y parafraseando a Elio Rojano, «Asterisk 12 es la maduración de un proyecto» que lleva ya 10 años(o más) de vida.

Les dejo estos enlaces para saber más de Asterisk 12:

Instalación y Configuración de Asterisk 12.

Iniciándose con ARI.

Especificaciones del CDR y CEL en Asterisk 12.

Ejemplo de ARI en ASTERISK usando Swagger-UI(necesitas tener un Asterisk12 corriendo y soportando ARI).

¿Quieres saber de Multi-tennant y más información de Asterisk 12? Elio Rojano te regala una presentación I:M:P:R:E:S:I:O:N:A:N:T:E

Desarollo de Paul Belanger para dejar de usar APP_QUEUE basado en ARI.

El github de Payload.

Video acerca de Payload.

Balanceador de Llamadas con OpenSIPS y ASIRI

5 Nov

Hola de nuevo, durante mi presentación en el Addons Challenge del ElastixWorld comente un caso de estudio: Implementación de Centros de Contacto de Entrada balanceados por un PROXY SIP. Afortunadamente pude asistir al taller de OpenSIPS que se dió también durante el ElastixWorld y gracias a la gente de AG PROJECTS (Saúl y Adrian) conocí acerca de los usos de OpenSIPS.

Entonces tenemos: Un caso de estudio + un Proxy SIP + una placa ASIRI, vamos a demostrar que es posible lo que comente durante la presentación y que el ElastixWorld dió sus frutos (implementación de OpenSIPS y la placa ASIRI).

Instalación de OpenSIPS.

Una vez que ya tenemos nuestra placa ASIRI funcionando y tenemos acceso via SSH lo primero que vamos a hacer es instalar OpenSIPS:

1.- Instala las dependencias necesarias de los módulos que pretendas usar, en este caso solo habilitaré el módulo «db_mysql» por lo que necesitamos las librerías devel de MySQL, ¡aha! uElastix ya las trae instaladas. Pero aún así necesitamos unas cuantas(estoy instalando paquetes de más para su uso futuro) :

# yum install -y git bison flex perl-Frontier-RPC radiusclient-ng-devel lynx libconfuse libconfuse-devel pcre-devel librabbitmq-devel  expat-devel json-c-devel xmlrpc-c-devel mongodb-devel libmicrohttpd-devel GeoIP-devel perl-ExtUtils-Embed python-devel

2.- Una vez que haya terminado la instalación vamos a crear el directorio para bajar las fuentes de OpenSIPS:

# cd /usr/src/
# mkdir opensips
# cd opensips/

3.- Descargamos las fuentes de OpenSIPS desde el repositorio de GIT. Uso GIT porque hubo un problema con las fuentes originales y la arquitectura ARM, después de reportar el problema los desarrolladores de OpenSIPS amablemente lo corrigieron y actualizaron las fuentes en GIT.

# git clone https://github.com/OpenSIPS/opensips.git -b 1.10 opensips_1_10
# cd opensips_1_10/

4.- A continuación compilamos OpenSIPS con el módulo db_mysql(puedes irte a jugar una partida de FIFA en el XBOX, un café o algo el proceso va  a tardar):

# make include_modules="db_mysql" modules
# make include_modules="db_mysql" install
# make install

5.- Después de que haya terminado vamos a crear el directorio en /etc que contedrá los archivos de configuración:

# mkdir /etc/opensips

6.- Ahora crearemos el archivo de configuración para crear la base de datos de OpenSIPS:

# nano /etc/opensips/opensipsctlrc

Con el siguiente contenido, recuerda cambiar DBNAME por el nombre de la base de datos que desees,  DBRWUSER por usuario de la base que desees y DBRWPW por el password que desees que use la base:

# $Id$
#
# The OpenSIPS configuration file for the control tools.
#
# Here you can set variables used in the opensipsctl and opensipsdbctl setup
# scripts. Per default all variables here are commented out, the control tools
# will use their internal default values.

## your SIP domain
# SIP_DOMAIN=opensips.org

## chrooted directory
# $CHROOT_DIR="/path/to/chrooted/directory"

## database type: MYSQL, PGSQL, ORACLE, DB_BERKELEY, or DBTEXT,
## by default none is loaded
# If you want to setup a database with opensipsdbctl, you must at least specify
# this parameter.
DBENGINE=MYSQL

## database host
DBHOST=localhost

## database name (for ORACLE this is TNS name)
DBNAME=opensips

# database path used by dbtext or db_berkeley
# DB_PATH="/usr/local/etc/opensips/dbtext"
## database read/write user
DBRWUSER=opensips

## password for database read/write user
DBRWPW="opensipsrw"

## database super user (for ORACLE this is 'scheme-creator' user)
DBROOTUSER="root"

# user name column
# USERCOL="username"

# SQL definitions
# If you change this definitions here, then you must change them
# in db/schema/entities.xml too.
# FIXME

# FOREVER="2020-05-28 21:32:15"
# DEFAULT_ALIASES_EXPIRES=$FOREVER
# DEFAULT_Q="1.0"
# DEFAULT_CALLID="Default-Call-ID"
# DEFAULT_CSEQ="13"
# DEFAULT_LOCATION_EXPIRES=$FOREVER

# Program to calculate a message-digest fingerprint
# MD5="md5sum"

# awk tool
# AWK="awk"

# grep tool
# GREP="grep"

# sed tool
# SED="sed"

# Describe what additional tables to install. Valid values for the variables
# below are yes/no/ask. With ask (default) it will interactively ask the user
# for an answer, while yes/no allow for automated, unassisted installs.
#

# If to install tables for the modules in the EXTRA_MODULES variable.
# INSTALL_EXTRA_TABLES=ask

# If to install presence related tables.
# INSTALL_PRESENCE_TABLES=ask

# Define what module tables should be installed.
# If you use the postgres database and want to change the installed tables,
# then you must also adjust the STANDARD_TABLES or EXTRA_TABLES variable
# accordingly in the opensipsdbctl.base script.

# opensips standard modules
# STANDARD_MODULES="standard acc domain group permissions registrar usrloc
#                   msilo alias_db uri_db speeddial avpops auth_db pdt dialog
#                   dispatcher dialplan drouting nathelper load_balancer"

# opensips extra modules
# EXTRA_MODULES="imc cpl siptrace domainpolicy carrierroute userblacklist b2b registrant"

## type of aliases used: DB - database aliases; UL - usrloc aliases
## - default: none
# ALIASES_TYPE="DB"

## control engine: FIFO or UNIXSOCK
## - default FIFO
# CTLENGINE=xmlrpc

## path to FIFO file
# OSIPS_FIFO="/tmp/opensips_fifo"

## MI_CONNECTOR control engine: FIFO, UNIXSOCK, UDP, XMLRPC
# MI_CONNECTOR=FIFO:/tmp/opensips_fifo
# MI_CONNECTOR=UNIXSOCK:/tmp/opensips.sock
# MI_CONNECTOR=UDP:192.168.2.133:8000
# MI_CONNECTOR=XMLRPC:192.168.2.133:8000

## check ACL names; default on (1); off (0)
# VERIFY_ACL=1

## ACL names - if VERIFY_ACL is set, only the ACL names from below list
## are accepted
# ACL_GROUPS="local ld int voicemail free-pstn"

## verbose - debug purposes - default '0'
# VERBOSE=1

## do (1) or don't (0) store plaintext passwords
## in the subscriber table - default '1'
# STORE_PLAINTEXT_PW=0

## do not display the output highlighted
# NOHLPRINT=1

## OPENSIPS START Options
## PID file path - default is: /var/run/opensips.pid
# PID_FILE=/var/run/opensips.pid

## Extra start options - default is: not set
# example: start opensips with 64MB share memory: STARTOPTIONS="-m 64"
# STARTOPTIONS=

7.- A continuación vamos a instalar la base de datos, para ello cambiamos al directrio «scripts» y ejecutamos el programa «opensipsdbctl» mas la instrucción «create» más el nombre de la base de datos que definimos en el archivo anterior:

# cd scripts/
# opensipsdbctl create opensips

Nos preguntará por la contraseña de root de MySQL, por defecto en uElastix es «palosanto». Después nos preguntará si queremos instalar las tablas extras, eso ya es su decisión:

 

MySQL password for root:  <----palosanto
INFO: test server charset
INFO: creating database opensisps ...
INFO: Core OpenSIPS tables succesfully created.
Install presence related tables? (y/n): y
INFO: creating presence tables into opensips ...
INFO: Presence tables succesfully created.
Install tables for imc cpl siptrace domainpolicy carrierroute userblacklist registrant? (y/n): y
INFO: creating extra tables into opensips ...
INFO: Extra tables succesfully created.

8.- Ahora hay que crear el archivo de configuración de OpenSIPS, el que cargará los módulos y contendrá las reglas del proxy, balanceo de carga etc. Esto se puede hacer utilizando el programa «osipsconfig» y escoger crear script para balanceo de cargas.

Como en este ejemplo la idea es que OpenSIPS reciba las llamadas de un proveedor SIP y las redireccione a los nodos con menor carga para su procesamiento necesitamos de 3 módulos principalmente:

a) Módulo «load_balancer.so«, el cuál se encargará de balancear la carga de llamadas.
b) Módulo «uac_registrant.so«, el cuál se encargara de hacer el registro de las lineas SIP a recibir
c) Módulo «uac_auth.so«, el cuál es necesario para que el módulo anterior funcione correctamente.

Para configurar el módulo  «load_balancer.so» escribimos(para más información sobre la configuración: http://www.opensips.org/html/docs/modules/1.10.x/load_balancer.html):

#### LOAD BALANCER module
loadmodule "load_balancer.so"
modparam("load_balancer", "db_url","mysql://opensips:opensipsrw@localhost/opensips")
modparam("load_balancer", "probing_interval", 30)
modparam("load_balancer", "probing_method", "OPTIONS")
modparam("load_balancer", "probing_from", "sip:ospinger@10.0.1.120")
modparam("load_balancer", "probing_reply_codes", "501, 403,200")

Para configurar el módulo «uac_registrant.so» escribimos(para más información sobre la configuración: http://www.opensips.org/html/docs/modules/1.10.x/uac_registrant.html ):

#### UAC MODULE
loadmodule "uac_auth.so"
loadmodule "uac_registrant.so"
modparam("uac_registrant", "hash_size", 1)
modparam("uac_registrant", "timer_interval", 120)
modparam("uac_registrant", "db_url", "mysql://opensips:opensipsrw@localhost/opensips")
modparam("uac_registrant", "table_name", "registrant")
modparam("uac_registrant", "registrar_column", "registrar")
modparam("uac_registrant", "proxy_column", "proxy")
modparam("uac_registrant", "aor_column", "aor")
modparam("uac_registrant", "third_party_registrant_column", "third_party_registrant")
modparam("uac_registrant", "username_column", "username")
modparam("uac_registrant", "password_column", "password")
modparam("uac_registrant", "binding_URI_column", "binding_URI")
modparam("uac_registrant", "binding_params_column", "binding_params")
modparam("uac_registrant", "expiry_column", "expiry")
modparam("uac_registrant", "forced_socket_column", "forced_socket")

Ya que nuestro hardware es limitado tenemos que cambiar el tamaño del hash a solo 128, esto dentro del módulo dialog, debe quedar así:

#### DIALOG module
loadmodule "dialog.so"
modparam("dialog", "dlg_match_mode", 1)
modparam("dialog", "hash_size", 128)
modparam("dialog", "default_timeout", 21600)  # 6 hours timeout
modparam("dialog", "db_mode", 2)
modparam("dialog", "db_url","mysql://opensips:opensipsrw@localhost/opensips")

Si queremos habilitar el log tenemos que añadir esto:

debug=6
log_stderror=no
log_facility=LOG_LOCAL0
log_name="opensip_LB_5260"

La primera línea implica el verbose que veremos.
La segunda línea si escribiremos al stderr.
La tercera línea hacia donde vamos a escribir el log, por default escribimos al syslog del sistema.
La cuarta línea es el nombre que saldrá en el log, en mi caso indico que es: opensips como load balancer en el puerto 5260.

También tenemos que decirle en que IP y puerto vamos a escuchar las peticiones SIP:

listen=udp:10.0.1.120:5260

Como ya tenemos el Asterisk escuchando en el puerto 5060 yo he elegido el puerto 5260 para OpenSIPS.

Algo muy importante es indicar la ruta de los módulos, en este caso los modulos se encuentran en: «/usr/lib/opensips/modules/»:

mpath="/usr/lib/opensips/modules/"

Acerca de la lógica del balanceo de carga podemos usar el ejemplo que trae por default, en mi caso lo estoy utilizando de esta manera:

if(load_balance("1","pstn")){
    xlog("sending call to $du\n");
    t_relay();
    exit;
}else{
   send_reply("500","No Destination available");
   exit;
}

La sintaxis de la función load_balance es:  load_balance(grp,resource[,alg]) donde(más información: http://www.opensips.org/html/docs/modules/1.10.x/load_balancer.html#id250102):

grp: Es el ID del cluster de los nodos, en mi caso el grupo es «1».

resource: Una cadena o lista de cadenas separadas por punto y coma que indican los recursos a usar, en mi caso «pstn».

alg: Es el algoritmo a usar para definir la carga en uso, puede ser 0 o 1. 0 es para usar un algoritmo absoluto y 1 para un algoritmo relativo.

A continuación dejo el archivo que estoy usando:

#
# $Id$
#
# OpenSIPS loadbalancer script
#     by OpenSIPS Solutions <team@opensips-solutions.com>
#
# This script was generated via "make menuconfig", from
#   the "Load Balancer" scenario.
# You can enable / disable more features / functionalities by
#   re-generating the scenario with different options.
#
# Please refer to the Core CookBook at:
#      http://www.opensips.org/Resources/DocsCookbooks
# for a explanation of possible statements, functions and parameters.
#

####### Global Parameters #########
debug=6
log_stderror=no
log_facility=LOG_LOCAL0
log_name="opensip_LB_5260"

fork=yes
children=4

/* uncomment the following lines to enable debugging */
#debug=6
#fork=no
#log_stderror=yes

/* uncomment the next line to enable the auto temporary blacklisting of
   not available destinations (default disabled) */
#disable_dns_blacklist=no

/* uncomment the next line to enable IPv6 lookup after IPv4 dns
   lookup failures (default disabled) */
#dns_try_ipv6=yes

/* comment the next line to enable the auto discovery of local aliases
   based on revers DNS on IPs */
auto_aliases=no

listen=udp:10.0.1.120:5260   # CUSTOMIZE ME

####### Modules Section ########
#set module path
mpath="/usr/lib/opensips/modules/"

#### SIGNALING module
loadmodule "signaling.so"

#### StateLess module
loadmodule "sl.so"

#### Transaction Module
loadmodule "tm.so"
modparam("tm", "fr_timer", 5)
modparam("tm", "fr_inv_timer", 30)
modparam("tm", "restart_fr_on_each_reply", 0)
modparam("tm", "onreply_avp_mode", 1)

#### Record Route Module
loadmodule "rr.so"
/* do not append from tag to the RR (no need for this script) */
modparam("rr", "append_fromtag", 0)

#### MAX ForWarD module
loadmodule "maxfwd.so"

#### SIP MSG OPerationS module
loadmodule "sipmsgops.so"

#### FIFO Management Interface
loadmodule "mi_fifo.so"
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
modparam("mi_fifo", "fifo_mode", 0666)

#### URI module
loadmodule "uri.so"
modparam("uri", "use_uri_table", 0)

#### MYSQL module
loadmodule "db_mysql.so"

#### AVPOPS module
loadmodule "avpops.so"

#### ACCounting module
loadmodule "acc.so"
/* what special events should be accounted ? */
modparam("acc", "early_media", 0)
modparam("acc", "report_cancels", 0)
/* by default we 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)
modparam("acc", "failed_transaction_flag", "ACC_FAILED")
/* account triggers (flags) */
modparam("acc", "log_flag", "ACC_DO")
modparam("acc", "log_missed_flag", "ACC_MISSED")

#### DIALOG module
loadmodule "dialog.so"
modparam("dialog", "dlg_match_mode", 1)
modparam("dialog", "hash_size", 128)
modparam("dialog", "default_timeout", 21600)  # 6 hours timeout
modparam("dialog", "db_mode", 2)
modparam("dialog", "db_url",
"mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME

#### LOAD BALANCER module
loadmodule "load_balancer.so"
modparam("load_balancer", "db_url","mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME
modparam("load_balancer", "probing_interval", 30)
modparam("load_balancer", "probing_method", "OPTIONS")
modparam("load_balancer", "probing_from", "sip:ospinger@10.0.1.102")
modparam("load_balancer", "probing_reply_codes", "501, 403,200")

#### UAC MODULE
loadmodule "uac_auth.so"
loadmodule "uac_registrant.so"
modparam("uac_registrant", "hash_size", 1)
modparam("uac_registrant", "timer_interval", 120)
modparam("uac_registrant", "db_url", "mysql://opensips:opensipsrw@localhost/opensips")
modparam("uac_registrant", "table_name", "registrant")
modparam("uac_registrant", "registrar_column", "registrar")
modparam("uac_registrant", "proxy_column", "proxy")
modparam("uac_registrant", "aor_column", "aor")
modparam("uac_registrant", "third_party_registrant_column", "third_party_registrant")
modparam("uac_registrant", "username_column", "username")
modparam("uac_registrant", "password_column", "password")
modparam("uac_registrant", "binding_URI_column", "binding_URI")
modparam("uac_registrant", "binding_params_column", "binding_params")
modparam("uac_registrant", "expiry_column", "expiry")
modparam("uac_registrant", "forced_socket_column", "forced_socket")

####### Routing Logic ########

# main request routing logic

route{

if (!mf_process_maxfwd_header("10")) {
sl_send_reply("483","Too Many Hops");
exit;
}

if (has_totag()) {
# sequential request withing a dialog should
# take the path determined by record-routing
if (loose_route()) {

# validate the sequential request against dialog
if ( $DLG_status!=NULL && !validate_dialog() ) {
xlog("In-Dialog $rm from $si (callid=$ci) is not valid according to dialog\n");
## exit;
}

if (is_method("BYE")) {
setflag(ACC_DO); # do accounting ...
setflag(ACC_FAILED); # ... even if the transaction fails
} else if (is_method("INVITE")) {
# even if in most of the cases is useless, do RR for
# re-INVITEs alos, as some buggy clients do change route set
# during the dialog.
record_route();
}

# route it out to whatever destination was set by loose_route()
# in $du (destination URI).
route(RELAY);
} else {
if ( is_method("ACK") ) {
if ( t_check_trans() ) {
# non 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;
}

#### INITIAL REQUESTS

# CANCEL processing
if (is_method("CANCEL")) {
if (t_check_trans())
t_relay();
exit;
} else if (!is_method("INVITE")) {
send_reply("405","Method Not Allowed");
exit;
}

if ($rU==NULL) {
# request with no Username in RURI
sl_send_reply("484","Address Incomplete");
exit;
}

t_check_trans();

# preloaded route checking
if (loose_route()) {
xlog("L_ERR",
"Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]");
if (!is_method("ACK"))
sl_send_reply("403","Preload Route denied");
exit;
}

# record routing

record_route();

setflag(ACC_DO); # do accounting

if(load_balance("1","pstn")){
xlog("sending call to $du\n");
t_relay();
exit;
}else{
send_reply("500","No Destination available");
exit;
}

t_on_failure("GW_FAILOVER");
route(RELAY);
}

route[RELAY] {
if (!t_relay()) {
sl_reply_error();
};
exit;
}

failure_route[GW_FAILOVER] {
if (t_was_cancelled()) {
exit;
}

# failure detection with redirect to next available trunk
if (t_check_status("(408)|([56][0-9][0-9])")) {
xlog("Failed trunk $rd/$du detected \n");

if ( load_balance("1","channel") ) {
t_on_failure("GW_FAILOVER");
t_relay();
exit;
}
send_reply("500","All GW are down");
}
}

local_route {
if (is_method("BYE") && $DLG_dir=="UPSTREAM") {
acc_log_request("200 Dialog Timeout");
}
}

9.- Ahora hay que crear el script de inicio:

# nano /etc/init.d/opensips

Con el siguiente contenido:

#!/bin/bash

#
# Startup script for OpenSIPS
#
# chkconfig: - 85 15
# description: OpenSIPS is a fast SIP Server.
#
# processname: opensips
# pidfile: /var/run/opensips.pid
# config: /etc/opensips/opensips.cfg
#
### BEGIN INIT INFO
# Provides: opensips
# Required-Start: $local_fs $network $named
# Should-Start: mysqld postgresql
# Short-Description: start, stop OpenSIPS
# Description: OpenSIPS is a very fast and flexible SIP (RFC3261) server.
### END INIT INFO

# Source function library.
. /etc/rc.d/init.d/functions

prog=opensips
oser=/usr/sbin/$prog
pidfile="/var/run/$prog.pid"
lockfile="/var/lock/subsys/$prog"
configfile="/etc/$prog/$prog.cfg"
m4configfile="/etc/$prog/$prog.m4"
m4archivedir="/etc/$prog/archive"
OPTIONS=""
RETVAL=0

[ -f /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
start() {
        echo -n $"Starting $prog: "

        # check whether OpenSIPs was already started
        if status -p $pidfile $prog > /dev/null 2>&1 ; then
                echo -n "already running" && warning && echo
                return 0
        fi

        # Generate config from M4
        if [ -f $m4configfile ]; then
                m4 -Q $m4configfile >$configfile.tmp
                if [ $? != 0 ]; then
                        log "cannot process m4 macro"
                        rm "$configfile.tmp"
                        return 1
                fi

                [ -e $configfile ] || touch $configfile

                # compare configs
                if [ `md5sum $configfile|awk '{print $1}'` != `md5sum $configfile.tmp|awk '{print $1}'` ]; then
                        mkdir -p "$m4archivedir"
                        mv "$configfile" "$m4archivedir/$prog.cfg-`date +%Y%m%d_%H%M%S`"
                fi

                mv "$configfile.tmp" "$configfile"
                chown root:root $configfile
                chmod 640 $configfile
        fi

        # there is something at end of this output which is needed to
        # report proper [ OK ] status in Fedora scripts
        daemon $oser -u root -g root -P $pidfile -f $configfile $OPTIONS 2>/dev/null | tail -1
        RETVAL=$?
        echo
        [ $RETVAL = 0 ] && touch $lockfile
        return $RETVAL
}

stop() {
        echo -n $"Stopping $prog: "
        # check whether OpenSIPs is running
        if ! status -p $pidfile $prog > /dev/null 2>&1 ; then
                echo -n "not running" && warning && echo
                return 0
        fi

        killproc $prog 2> /dev/null
        RETVAL=$?
        echo
        [ $RETVAL = 0 ] && rm -f $lockfile $pidfile
        return $RETVAL
}

# See how we were called.
case "$1" in
        start)
                start
                ;;
        stop)
                stop
                ;;
        status)
                status -p $pidfile $prog
                RETVAL=$?
                ;;
        restart|reload)
                stop
                start
                ;;
        condrestart|try-restart)
                if [ -f $pidfile ] ; then
                        stop
                        start
                fi
                ;;
        *)
                echo $"Usage: $prog {start|stop|reload|restart|condrestart|status|help}"
                RETVAL=2
esac

exit $RETVAL

10.- Ya falta poco, ahora hay que añadir los valores a nuestra base de datos antes de iniciar el servicio de OpenSIPS. Entramos al cli de MySQL y añadimos los nodos que vamos a usar para balancear las llamadas(recuerda la contraseña de MySQL es: palosanto):

# mysql -p opensips

mysql>  INSERT INTO load_balancer(group_id,dst_uri,resources,probe_mode,description) VALUES('1','sip:10.0.1.103:5060','pstn=1','1','Asterisk1-onRPi');

mysql>  INSERT INTO load_balancer(group_id,dst_uri,resources,probe_mode,description) VALUES('1','sip:10.0.1.102:5060','pstn=1','1','Asterisk2-onShaka');

mysql>  INSERT INTO load_balancer(group_id,dst_uri,resources,probe_mode,description) VALUES('1','sip:10.0.1.120:5060','pstn=1','1','Asterisk3-onAsiri');

mysql> SELECT * FROM load_balancer;

+----+----------+---------------------+-----------+------------+-----------------------+
| id | group_id | dst_uri             | resources | probe_mode | description           |
+----+----------+---------------------+-----------+------------+-----------------------+
|  1 |        1 | sip:10.0.1.103:5060 | pstn=1    |          1 | Asterisk1-onRPi | 
|  2 |        1 | sip:10.0.1.102:5060 | pstn=1    |          1 | Asterisk2-onShaka       |
|  3 |        1 | sip:10.0.1.120:5060 | pstn=1    |          1 | Asterisk3-onAsiri |
+----+----------+---------------------+-----------+------------+-----------------------+

Se añadieron 3 servidores al grupo «1», cada uno con un recurso llamado «pstn» el cuál tiene un máximo de 1 llamada, para ambientes reales sustituir con las llamadas que se desean.

11.- Ahora vamos a añadir los valores de la cuenta sip que vamos a registrar:

mysql> INSERT INTO registrant(registrar,aor,username,password,binding_URI,expiry,) VALUES('sip:my.provider.host','sip:myusername@my.provider.host','myusername','mysuperpassword','sip:myDID@myAsiriIP','3600');

12.- Salimos de MySQL e intentamos iniciar el servicio de OpenSIPS, si todo va bien veremos una salida parecida a esta:

Oct 31 17:02:20 AsiriShaka opensips: INFO:core:init_tcp: using epoll_lt as the TCP io watch method (auto detected)
Oct 31 17:02:21 AsiriShaka opensip_LB_5260[3836]: NOTICE:core:main: version: opensips 1.10.0-notls (armv5tejl/linux)
Oct 31 17:02:21 AsiriShaka opensip_LB_5260[3836]: INFO:core:main: using 32 Mb shared memory
Oct 31 17:02:21 AsiriShaka opensip_LB_5260[3836]: INFO:core:main: using 2 Mb private memory per process
Oct 31 17:02:21 AsiriShaka opensip_LB_5260[3836]: INFO:core:evi_publish_event: Registered event <E_CORE_THRESHOLD(0)>
Oct 31 17:02:21 AsiriShaka opensip_LB_5260[3836]: INFO:core:evi_publish_event: Registered event <E_CORE_SHM_THRESHOLD(1)>
Oct 31 17:02:21 AsiriShaka opensip_LB_5260[3836]: INFO:core:evi_publish_event: Registered event <E_CORE_PKG_THRESHOLD(2)>
Oct 31 17:02:21 AsiriShaka opensip_LB_5260[3836]: NOTICE:signaling:mod_init: initializing module ...
Oct 31 17:02:21 AsiriShaka opensip_LB_5260[3836]: INFO:sl:mod_init: Initializing StateLess engine
Oct 31 17:02:21 AsiriShaka opensip_LB_5260[3836]: INFO:tm:mod_init: TM - initializing...
Oct 31 17:02:21 AsiriShaka opensip_LB_5260[3836]: CRITICAL:tm:lock_set_init: semget (..., 251, 0700) failed: Invalid argument
Oct 31 17:02:21 AsiriShaka opensip_LB_5260[3836]: INFO:tm:lock_initialize: semaphore arrays of size 250 allocated
Oct 31 17:02:21 AsiriShaka opensip_LB_5260[3836]: INFO:rr:mod_init: rr - initializing
Oct 31 17:02:22 AsiriShaka opensip_LB_5260[3836]: INFO:maxfwd:mod_init: initializing...
Oct 31 17:02:22 AsiriShaka opensip_LB_5260[3836]: INFO:sipmsgops:mod_init: initializing...
Oct 31 17:02:22 AsiriShaka opensip_LB_5260[3836]: INFO:core:evi_publish_event: Registered event <E_MYSQL_CONNECTION(3)>
Oct 31 17:02:22 AsiriShaka opensip_LB_5260[3836]: INFO:avpops:avpops_init: initializing...
Oct 31 17:02:22 AsiriShaka opensip_LB_5260[3836]: INFO:acc:mod_init: initializing...
Oct 31 17:02:22 AsiriShaka opensip_LB_5260[3836]: INFO:core:evi_publish_event: Registered event <E_ACC_EVENT(4)>
Oct 31 17:02:22 AsiriShaka opensip_LB_5260[3836]: INFO:core:evi_publish_event: Registered event <E_ACC_CDR(5)>
Oct 31 17:02:22 AsiriShaka opensip_LB_5260[3836]: INFO:core:evi_publish_event: Registered event <E_ACC_MISSED_EVENT(6)>
Oct 31 17:02:22 AsiriShaka opensip_LB_5260[3836]: INFO:dialog:mod_init: Dialog module - initializing
Oct 31 17:02:23 AsiriShaka opensip_LB_5260[3836]: INFO:load_balancer:mod_init: Load-Balancer module - initializing
Oct 31 17:02:23 AsiriShaka rsyslogd-2177: imuxsock begins to drop messages from pid 3836 due to rate-limiting
Oct 31 17:02:25 AsiriShaka opensips: INFO:core:daemonize: pre-daemon process exiting with 0

13.- Para saber el estado del cluster de servidores usaremos el comando:

# opensipsctl fifo lb_list

Y veremos una salida similar a:

Destination:: sip:10.0.1.120:5060 id=1 group=1 enabled=yes auto-re=on
        Resource:: pstn max=1 load=0
Destination:: sip:10.0.1.103:5060 id=2 group=1 enabled=yes auto-re=on
        Resource:: pstn max=1 load=0
Destination:: sip:10.0.1.105:5060 id=3 group=1 enabled=yes auto-re=on
        Resource:: pstn max=1 load=0

Como podemos ver este comando es muy útil para saber cuántos nodos hay, cuál es su capacidad máxima y por último cuántas llamadas/carga tiene actualmente.

14.- Para saber el estado del registro de la línea SIP usaremos el comando:

# opensipsctl fifo reg_list

Y veremos un salida similar a:

AOR:: sip:myuser@mysip.provider:5060 expires=3600
state:: REGISTERED_STATE
last_register_sent:: Mon Nov  4 10:39:20 2013
registration_t_out:: Mon Nov  4 11:37:21 2013
registrar:: sip:mysip.provider:5060
binding:: sip:mydid@10.0.1.120:5261
dst_IP:: IPv4 ip=mysipprovider

 

Video demo:

Les dejo un video donde hago 2 llamadas y estas son ruteadas a dos diferentes Asterisk en mi red.

Conclusiones.

Como podemos ver OpenSips es capaz de correr en arquitecturas pequeñas como la Asiri o la Raspberry Pi, este mini-proyecto puede servir para hacer un cluster de muchas Asiris o RPis para armar un sistema de llamadas Inbound muy grande y a bajo costo.

Como lo comenté al inicio, es mi primera vez usando OpenSips de modo que puede haber un sin fin de errores en la redacciones o en la lógica del ruteo de llamadas. Para nada soy un experto en este proxy.

TO DO.

Queda por implementar, la detección de caídas de un nodo del cluster para rutear a otro Asterisk las llamadas o retenerlas hasta que otro nodo sea capaz de aceptar las llamadas.

Actualmente el registro de la línea SIP usa un puerto random, a pesar de que (según yo) estoy forzando el puerto, pero mi proveedor me muestra un puerto diferente lo cual complica el redireccionamiento en el firewall cuando no se tiene acceso a ese puerto.

Interfaz Visual del manejo de las Llamadas.

Saludos 😉

Review Grandstream DP715: buen teléfono inalámbrico de bajo costo

22 Ago

DP715 con su base

Nuestros amigos de Grandstream nos hicieron llegar hace unos meses una muestra de su familia de teléfonos inalámbricos: el DP715. Este apunta a ser un teléfono inalámbrico VoIP de altas funcionalidades y bajo costo, compitiendo en un nivel económico que lo situa más abajo de teléfonos de su competencia como lo es el Aastra MBU400 (ahora descontinuado) o el Yealink W52G.

En esta nueva familia de teléfonos inalámbricos hay 2 variantes: el DP715 y el DP710. La diferencia entre ambas parece mínima en simple apariencia pero muy significativa en operación. El DP715posee una estación base que además de ofrecer la carga de la unidad, controla las comunicaciones y actúa como el puente entre la parte inalámbrica (DECT) y la parte VoIP (SIP), mientras que el DP710 solo posee una base cargadora que provee de energía a las baterías recargables de la unidad, por lo que debemos ligar nuestro DP710 con la base de algún DP715 que tengamos. Cabe mencionar que es posible usar una sola base de un DP715 para controlar hasta otros 4 handsets DP710 por separado.

Características generales

  • Teléfono inalámbrico DECT/GAP
  • Protocolo SIP
  • Rango de frecuencias:
    • 1880 ~ 1900 MHz (Europa)
    • 1920 ~ 1930 MHz (USA)
    • 1910 ~ 1920 MHz (Brasil)
  • Potencia de emisión: 10mW
  • Rango: 300m en exteriores, 50m en interiores.
  • Display LCD (con backlight azul) de 1.7 pulgadas con resolución de 102×80 pixeles.
  • Codecs soportados: G711u/a, G723.1, G726-32, G729A/B e iLBC
  • Funcionalidades de caller Id, llamada en espera, transferencia atendida/desatendida, redireccionamiento, hold, DND (no molestar), llamada tripartita
  • Configuración por interfaz web. Provisionamiento por HTTP, HTTPS o TFTP.
  • Soporte para TLS, sRTP y HTTPS.
  • Multi-lenguaje (inglés, francés, alemán, español, alemán, italiano, checo, danés, griego, noruego, polaco, portugués, ruso, sueco o turco)
  • Estación base con 4 LEDs indicadores: encendido, red, registro y llamada.
  • Duración de la batería catalogado en 10/80/16 horas (llamada/standby/carga)
  • Una sola estación base (DP715) puede controlar hasta 5 handsets, con la posibilidad de tener 4 llamadas simultáneas.

Contenido de la caja

El contenido de la caja

El contenido de la caja

Al momento de abrir la caja que contiene al aparato, el teléfono cuenta con todos los componentes comunes que esperaríamos encontrar:

  • Teléfono inalámbrico (handset)
  • Estación base
  • Cable de red
  • Eliminador de corriente
    • Entrada: 110V 0.15A
    • Salida: 6v 0.5A
  • Baterias recargables NiMH (2x AAA 500mAh)
  • Tapa
  • Funda con sujetador para cinturón
  • Manual

Fabricación y primeras impresiones

El teléfono en su caja original

El teléfono en su caja original

Tras abrir el paquete, una muy grata sorpresa fue la batería, ya que se trata de 2 pilas recargables AAA convencionales. Esto quiere que no hay que preocuparse por conseguir una batería de reemplazo especial, ya que podemos adquirir nuevas baterías en cualquier tienda de autoservicio y dicho sea de paso, también tenemos la posibilidad de tener un juego adicional «cargado» en todo momento para aquellos casos donde tengamos una necesidad de uso larga y que no deseemos esperar a que la carga concluya.

Los controles del teléfono son sencillos e intuitivos: posee el teclado habitual (dígitos 0-9, #, *), botón de llamado/Send, botón de colgado/hangup, teclas de menu izquierdo/derecho y un pequeño pad de 4 direcciones para navegar por el menu. El botón de encendido y apagado viene integrado dentro del botón de colgado, mientras que el de altavoz/speaker, viene integrado dentro del propio botón de send, simplificando aún más el diseño.

Como se aprecia en las fotos, el teléfono tiene una pantalla LCD de 1.7″ monocromática retroiluminada con luz azul. Al no tener pantalla a color, ayuda a preservar la batería en lo que más nos interesa: las llamadas. El teléfono soporta multi idiomas con lo que es posible visualizarlo todo en español desde un inicio (no es necesario instalar ningún paquete de lenguaje, ya viene listo). Tiene soporte de 18 melodías base instaladas en formato polifónico, por lo que no es posible personalizar el ringtone con música de mayor calidad.

Configuración

El teléfono soporta configuración via provisionamiento por HTTP, HTTPS o TFTP, así como la típica configuración web amarillo/naranja que caracteriza a los Grandstream. Hay muchas opciones que se pueden configurar dentro del área avanzada y los perfiles, aquí dejamos los screenshots de la configuración para que puedan ver los parámetros aceptados por el teléfono:

Pantalla de estado

Pantalla de estado

Pantalla de configuración de perfil

Pantalla de configuración de perfil

Pantalla de configuración avanzada

Pantalla de configuración avanzada

Pantalla de handsets

Pantalla de handsets

Pantalla de configuración básica

Pantalla de configuración básica

Un detalle que notamos es que en el DP715 cada teléfono tiene que estar relacionado con un usuario SIP diferente en el conmutador, por lo que no te es posible crear «grupos» de handsets que reciban las llamadas entrantes por la misma cuenta. Esta es una funcionalidad que tienes que darle explícitamente al conmutador (Asterisk en nuestro caso), y que el teléfono no tiene posibilidad de elegir.

El manual de configuración (en inglés) es bastante fácil de conseguir y viene muy bien documentado con las partes, botones, funciones y campos a configurar en el teléfono.

Experiencias

Indicadores de la base: power, ethernet, registro y llamada

Indicadores de la base: power, ethernet, registro y llamada

La conexión del teléfono es muy sencilla: un puerto es el ethernet y otro el de la alimentación eléctrica (sería interesante ver que el teléfono solo ocupara PoE, para así no requerir el conector eléctrico). Tras arrancar, lo primero que la base tratará de hacer es registrarse hacia el servidor que hayamos especificado en su configuración.

Un tema que siempre es importante en un teléfono inalámbrico es la autonomía de la batería. La duración de fábrica está catalogada como 10 horas en habla, 80 horas en standby. Sin embargo, haciendo nuestras pruebas en laboratorio pudimos comprobar que el teléfono fue capaz de permanecer hasta 23 horas en llamada contínua usando una sola carga de las pilas AAA incluidas. Si hubiéramos reemplazado las pilas por unas de mayor calidad (Duracell, Energizer) y de mayor capacidad, probablemente habríamos estado cerca de duplicar el ya de por si sorprendente tiempo de habla contínua.

Al momento de probar el auricular notamos una excelente calidad en la llamada al usar codecs de narrowband. Soporta G729, G711u/a, iLBC, G723 y G726-32. Una característica que echamos de menos es el soporte para G722, con lo que podríamos tener conversaciones usando un codec wideband, con lo que la calidad mejora notablemente comparada con los codecs tradicionales de narrowband. Aún así, la calidad del teléfono en las pruebas fue muy buena, no padeciendo de problemas de estática/interferencia mientras que estuviera dentro del alcance esperado del teléfono.

El altavoz del teléfono es bueno, pero tampoco lo usemos esperando que sea un teléfono para conferencias. El volumen máximo que ofrece es un tanto bajo, pero dado que está pensado solamente para usarlo como una alternativa de «manos libres», es una buena funcionalidad que hoy en día ya damos por sentada en todos los teléfonos VoIP.

Otra característica que echamos de menos fue la falta de un jack de 2.5mm que permitiera conectar un manos libres al teléfonos, lo que permitiría total movilidad dentro del ambiente de trabajo. Sería una característica muy bien recibida ya que aunque el tamaño del teléfono resulta relativamente cómodo para sostener el aparato presionándolo entre la oreja y los hombros, si se pudiera contemplar un headset reduciría los dolores de cuello y aumentaría la comodidad de usar el aparato por largos periodos de tiempo.

Nuestras pruebas de alcance fueron satisfactorias. Contamos con una oficina de 100m2 ubicada en un 1er piso con frente a la calle, con lo que era posible tomar la unidad, establecer una llamada y recorrer todos los rincones de la oficina e inclusive, tomar las escaleras del edificio, descender a nivel de calle, cruzarla y aún así la llamada seguía arriba, sin problemas. También tomamos el teléfono, cruzamos el estacionamiento que tenemos al lado y entramos a la tienda de autoservicio que está a 2 locales sobre nuestra calle. Aún con el ruido e interferencia generado por los aparatos eléctricos de la tienda de autoservicio, la llamada aún era entendible. Esto fue un caso extremo, pero es bueno saber que si tuviéramos que bajar o subir pisos con el teléfono, aún así sería posible mantener la llamada arriba.

Desafortunadamente en el tema del alcance, el teléfono no soporta roaming. Esto quiere decir que solo puede tener cobertura dentro del área de transmisión de la base en la que esté registrado, sin posibilidad de saltar hacia otros access points o radio bases.

Conclusiones

El DP715 es sin duda un excelente producto orientado al mercado PyME que desea tener movilidad entre múltiples aparatos dentro del un área de trabajo relativamente pequeña. La duración y facilidad de reemplazo de sus baterías son fuertes puntos a favor, pero carece de algunas de las características más avanzadas que vemos en otros teléfonos (como el roaming o la integración con manos libres). Sin embargo, cuando hacemos la comparación de relación costo/beneficio, vemos rápidamente que el DP715es un excelente teléfono con las funcionalidades que por su rango de precio, esperamos obtener.

Ventajas

  • Relativo bajo costo por unidad (precio público < 145 USD en el mercado).
  • Configuración sencilla e intuitiva, idéntica a otros productos Grandstream.
  • Buena calidad en audio y buen alcance de transmisión.
  • Pilas AA recargables genéricas, por lo que su reemplazo y carga es muy sencilla.
  • Larga duración de batería.

Desventajas

  • El teléfono tiene el mismo atractivo visual que un teléfono inalámbrico de gama baja (sin pantalla a color).
  • Sin soporte para audio HD.
  • No soporta roaming.
  • No tiene entrada para manos libres.
  • No utiliza PoE.

Configurando OpenVPN en un teléfono Denwa DW-310P

21 Ago

denwa+openvpnUna funcionalidad muy bienvenida en los nuevos teléfonos de VoIP es la posibilidad de soportar la creación de una VPN, ya que de esta manera podemos asegurar el tráfico que pasa por nuestro teléfono y además, garantizar que no sufriremos de los problemas de NAT característicos de SIP ya que toda la voz pasará por una red «local» y no por un firewall que podría detener parte de lo que estamos enviando.

En particular, las redes creadas sobre TLS (como es el caso de OpenVPN) son atractivas porque pueden levantarse completamente en software sin la necesidad de adquirir hardware externo para que se dedique a esto. Si bien las VPNs en hardware son de mayor rendimiento, la sencillez con la que podemos instalar un servicio de OpenVPN dentro del mismo servidor donde tenemos nuestro conmutador Asterisk hace que sea la opción más clara a seguir.

Existe en internet una gran cantidad de tutoriales que explican como configurar un teléfono Yealink con el servicio de OpenVPN. Sin embargo, existen otros teléfonos menos conocidos (y también más económicos) que ofrecen esta misma funcionalidad pero que hasta el momento, encontrar el procedimiento para configurar el servicio no había sido clara. Esperamos que con estos pasos que presentamos a continuación se aclaren muchas dudas.

En esta ocasión vamos a mostrar como configurar OpenVPN con un teléfono Denwa DW-310P (haremos un review próximamente del teléfono). Partiremos de los siguientes supuestos:

  • Ya tienes Asterisk instalado (no importa la versión).
  • El servicio de OpenVPN ya está instalado. Hay muchos tutoriales que te indican como hacerlo.
  • Entiendes que Asterisk posee 2 direcciones IP:
    • La «regular» que es la que usas para conexiones en tu misma red y/o fuera de la VPN.
    • La de la VPN, que es a la que apuntarán los equipos que se encuentren en un ambiente remoto.
  • El equipo donde estás trabajando es Windows.

Los pasos a seguir son muy sencillos:

  1. Descarga el archivo con los ejecutables para crear el security file que el teléfono tiene. Atención: es probable que un sistema antivirus catalogue su contenido como una amenaza, pero es normal.
  2. Descomprime el archivo. Te deberá quedar la siguiente estructura:
    1. bintoarray.exe
    2. OpenVpn.bat
    3. mkromfs.exe
    4. Directorio ‘config’ (está vacío)
  3. Dado que ya tienes tu servidor OpenVPN corriendo y estás familiarizado con el proceso, consigue los archivos necesarios para crear tu conexión VPN y ponlos en el directorio recién creado config siguiendo los siguientes nombres de archivo:
    1. ca.crt: El certificado de la entidad certificadora (CA)
    2. client.ovpn: Archivo de texto que contiene la configuración del cliente de OpenVPN (en Linux tendría extensión .conf o .cnf)
    3. client.crt: El certificado para el cliente
    4. client.key: La llave privada para el cliente
  4. Con los 4 archivos dentro del directorio config, dale doble-click al archivo OpenVpn.bat para ejecutarlo. Tras hacerlo, se generarán dos archivos: OpenVpn.bin OpenVpn.h.
  5. Entra a la interfaz web del teléfono. Dentro de las pestañas de Seguridad / Seguridad existe una sección que te permite «Actualizar archivo de seguridad». Entra a dicha opción y sube el archivo OpenVpn.bin que creaste con anterioridad.
  6. Cuando el archivo termine de subirse, en la parte inferior te aparecerán los nombres de los archivos originates (ca.crt, client.key, client.crt, client.ovpn), indicando que fueron reconocidos exitosamente (Recuerda: tu solo subes un archivo y en la pantalla aparecerán 4 diferentes).
  7. Con el archivo subido, acceder a Seguridad –> VPN –> Modo VPN. Habilitar el checkbox que dice «Habilitar VPN», seleccionar el uso de OpenVPN, guardar los cambios y reiniciar el teléfono.
  8. Tras reiniciar, si accedes a Seguridad –> VPN en la parte superior se te mostrará la IP que tu servidor de VPN le entregó al teléfono.

Con la VPN creada, la configuración del teléfono queda exactamente igual que como lo harías en cualquier otro caso:

  • Entras a la configuración de la cuenta.
  • Das de alta la IP de Asterisk, el usuario y la contraseña de tu cuenta SIP.
    • Recuerda: la IP de Asterisk ya no es la que usas normalmente, sino la que el equipo de Asterisk posee en el segmento de tu VPN.
  • Guardas y aplicas los cambios.

Desafortunadamente el teléfono donde validamos este procedimiento ya fue devuelto al proveedor, pero tan pronto tenga acceso a uno nuevamente tomaré unos screenshots del proceso para que no queden dudas.

¡Suerte!