Configurando DUNDi en Elastix (asterisk+freePBX)

Información sobre DUNDi:
http://en.wikipedia.org/wiki/Distributed_Universal_Number_Discovery
En pocas palabras DUNDi es un protocolo que permite consultar planes de marcado (mejor dicho, contextos) de otros equipos.
Como ejemplo, digamos que tenemos equipos con Elastix (o cualquier distro que use freePBX) en dos ciudades distintas y queremos que se comuniquen entre si sus extensiones.
Podríamos crear una troncal entre los equipos y poner un plan de marcado con "series de numeros" a mano para que se puedan llamar, es cierto, pero cada que creemos una extensión en un lado necesitaremos modificar el plan de marcado de los otros equipos.

Sin duda es mucho mas eficiente si podemos hacer que sea dinámico, es decir, que si creamos una extensión en un lado, este disponible en todos los demás equipos sin realizar ninguna configuración adicional, completamente transparente; esa es la meta, y eso es lo que obtenemos con DUNDi.
Esto también es util si quisiéramos hacer balanceo de carga entre varios equipos.

Aqui describo La forma como configurar dundi en Elastix con tres servidores y ademas otro tipo de integraciones que se podrian realizar como el parqueo de llamadas entre la nube DUNDi, IVRs globales, entre otras cosas.

Existen diversos manuales para configurar DUNDI con asterisk pero este manual describe como configurarlo e integrarlo con freePBX.

En este escenario tenemos 3 localidades que queremos integrar con DUNDi, así que tendré tres equipos (SRV01, SRV02 y SRV03) con los siguientes datos:
SRV01:
IP: 192.168.255.201
MAC: 00:21:5A:49:24:98
SRV02:
IP:192.168.255.202
MAC:00:21:5A:49:7E:9E
SRV02:
IP:192.168.255.203
MAC:00:22:64:08:F4:FE

Empecemos!

Tags:

Certificados de encriptación para DUNDi

DUNDi usa certificados de encriptación RSA para compartir sus planes de marcado, además por que las respuestas a una consulta incluye el passwd de la troncal (que es dinámico). }
Usaremos la misma clave para la comunicacion entre todos los servidores.
Así que el primer paso es generar los certificados de encriptación uno de los equipos (en este ejemplo en SRV01):
SRV01:
cd /var/lib/asterisk/keys
astgenkey -n SERVERS-DUNDI

Ahora necesitamos compartir los certificados entre los dos servidores; esto es para que cada uno pueda desencriptar al otro.
Copiar hacia SRV02:
scp /var/lib/asterisk/keys/SERVERS-DUNDI.* 192.168.255.202:/var/lib/asterisk/keys

Copiar hacia SRV03:
scp /var/lib/asterisk/keys/SERVERS-DUNDI.* 192.168.255.203:/var/lib/asterisk/keys

Archivo dundi.conf (general)

Vamos a generar este archivo desde el principio, sección por sección:
Sección [general]:
En esta sección del archivo definimos nuestra propia identificación en la nube DUNDi, así como las opciones globales, debemos rellenar los campos con los datos reales de nuestros equipos (MAC, etc):
Para SRV01:
[general]
;
department=SERVER01
organization=AlgunaOrganizacion
locality=Quito
stateprov=Pichincha
country=EC
email=info[at]elajonjoli.org
phone=+593-0000000
;
; Aqui podemos definir en que red va a escuchar dundi
;bindaddr=0.0.0.0
; El puerto en el que va a escuchar, por defecto 4520
port=4520
; Aqui nos identificamos con nuestra MAC address
entityid=00:21:5A:49:24:98
; El numero de consultas que va a hacer dundi hasta alcanzar un plan de marcado
ttl=12
; Finaliza las conexiones fallidas
autokill=yes

Para SRV02:
[general]
;
department=SERVER02
organization=AlgunaOrganizacion
locality=Quito
stateprov=Pichincha
country=EC
email=info[at]elajonjoli.org
phone=+593-0000000
;
;Aqui podemos definir en que red va a escuchar dundi
;bindaddr=0.0.0.0
; El puerto en el que va a escuchar, por defecto 4520
port=4520
; Aqui nos identificamos con nuestra MAC address
entityid=00:21:5A:49:7E:9E
; El numero de consultas que va a hacer dundi hasta alcanzar un plan de marcado
ttl=12;
; Finaliza las conexiones fallidas
autokill=yes

Para SRV03:
[general]
;
department=SERVER03
organization=AlgunaOrganizacion
locality=Quito
stateprov=Pichincha
country=EC
email=info[at]elajonjoli.org
phone=+593-0000000
;
;Aqui podemos definir en que red va a escuchar dundi
;bindaddr=0.0.0.0
; El puerto en el que va a escuchar, por defecto 4520
port=4520
; Aqui nos identificamos con nuestra MAC address
entityid=00:22:64:08:F4:FE
; El numero de consultas que va a hacer dundi hasta alcanzar un plan de marcado
ttl=12;
; Finaliza las conexiones fallidas
autokill=yes

Archivo dundi.conf (mappings)

Sección [mappings]:
Básicamente aquí definimos nuestra respuesta a una consulta hacia determinado numero que tengamos localmente.
Para SRV01:
[mappings]
priv => dundi-priv-canonical,0,IAX2,dundi:${SECRET}@192.168.251.251/${NUMBER},nopartial
priv => dundi-priv-customers,100,IAX2,dundi:${SECRET}@192.168.251.251/${NUMBER},nopartial
priv => dundi-priv-via-pstn,400,IAX2,dundi:${SECRET}@192.168.251.251/${NUMBER},nopartial

Para SRV02:
[mappings]
priv => dundi-priv-canonical,0,IAX2,dundi:${SECRET}@192.168.251.252/${NUMBER},nopartial
priv => dundi-priv-customers,100,IAX2,dundi:${SECRET}@192.168.251.252/${NUMBER},nopartial
priv => dundi-priv-via-pstn,400,IAX2,dundi:${SECRET}@192.168.251.252/${NUMBER},nopartial

Para SRV03:
[mappings]
priv => dundi-priv-canonical,0,IAX2,dundi:${SECRET}@192.168.251.253/${NUMBER},nopartial
priv => dundi-priv-customers,100,IAX2,dundi:${SECRET}@192.168.251.253/${NUMBER},nopartial
priv => dundi-priv-via-pstn,400,IAX2,dundi:${SECRET}@192.168.251.253/${NUMBER},nopartial

Los valores de ${NUMBER} y ${SECRET} se reemplazaran automáticamente, según el numero que se consulte y la clave aleatoria que se haya generado en ese momento.

Archivo dundi.conf (peers)

Adicionalmente debemos definir los PEERS que consultaran y serán consultados (con la opción symmetric), esto lo hacemos asi:
Para SRV01:
; Identificamos al servidor 2 (SRV02) por su entityid
[00:21:5A:49:7E:9E]
;permitir y realizar conexiones
model=symmetric
; IP ser servidor SRV02
host=192.168.255.202
inkey=SERVERS-DUNDI
outkey=SERVERS-DUNDI
; Incluimos el contexto antes definido en la sección mappings
include=priv
permit=priv
qualify=yes
order=primary

; Identificamos al servidor 3 (SRV03) por su entityid
[00:22:64:08:F4:FE]
;permitir y realizar conexiones
model=symmetric
; IP ser servidor SRV03
host=192.168.255.203
inkey=SERVERS-DUNDI
outkey=SERVERS-DUNDI
; Incluimos el contexto antes definido en la sección mappings
include=priv
permit=priv
qualify=yes
order=primary

Para SRV02:
; Identificamos al servidor 1 (SRV01) por su entityid
[00:21:5A:49:24:98]
;permitir y realizar conexiones
model=symmetric
; IP ser servidor SRV01
host=192.168.255.201
inkey=SERVERS-DUNDI
outkey=SERVERS-DUNDI
; Incluimos el contexto antes definido en la sección mappings
include=priv
permit=priv
qualify=yes
order=primary

; Identificamos al servidor 3 (SRV03) por su entityid
[00:22:64:08:F4:FE]
;permitir y realizar conexiones
model=symmetric
; IP ser servidor SRV03
host=192.168.255.203
inkey=SERVERS-DUNDI
outkey=SERVERS-DUNDI
; Incluimos el contexto antes definido en la sección mappings
include=priv
permit=priv
qualify=yes
order=primary

Para SRV03:
; Identificamos al servidor 1 (SRV01) por su entityid
[00:21:5A:49:24:98]
;permitir y realizar conexiones
model=symmetric
; IP ser servidor SRV01
host=192.168.255.201
inkey=SERVERS-DUNDI
outkey=SERVERS-DUNDI
; Incluimos el contexto antes definido en la sección mappings
include=priv
permit=priv
qualify=yes
order=primary

; Identificamos al servidor 1 (SRV01) por su entityid
[00:21:5A:49:24:98]
;permitir y realizar conexiones
model=symmetric
; IP ser servidor SRV01
host=192.168.255.201
inkey=SERVERS-DUNDI
outkey=SERVERS-DUNDI
; Incluimos el contexto antes definido en la sección mappings
include=priv
permit=priv
qualify=yes
order=primary

DUNDi: Plan de marcado

iax_custom.conf
Aquí debemos definir la configuración de la troncal; como dundi va a hacer todo el trabajo de enviar la cadena exacta de como contactarlos solo creamos un usuario que recibe las llamadas autenticadas de la nube DUNDi:
En SRV01 y en SRV02
[dundi]
type=user
dbsecret=dundi/secret
context=ext-local
; Podemos restringir ciertos codecs para mejorar la calidad de voz, o dar prioridad al ancho de banda (con gsm o g729)
; disallow=all
; allow=ulaw

extensions.conf
Para lograr la integración con freePBX debemos modificar un poco el contexto "from-internal" que es el contexto que usa freePBX para las extensiones.
Teniendo en cuenta que cuando un numero no es encontrado en el plan de marcado local se usa el contexto [bad-number] es alli donde debemos modificar y la configuración debe ser exactamente la misma en los tres servidores:
En el archivo extensions.conf buscamos la sección:
[from-internal]
include => from-internal-xfer
include => bad-number

y comentamos la linea
include => bad-number poniendo un punto y coma adelante asi: ;include => bad-number
y agregamos la linea
include => dundi-priv-lookup
que es un contexto que crearemos luego en el archivo extensions_custom.conf, quedando asi:
[from-internal]
include => from-internal-xfer
;include => bad-number
include => dundi-priv-lookup

extensions_custom.conf
Aqui definimos los contextos personalizados para que dundi mapee nuestra extensiones, asi como una macro para hacer las busquedas en otros equipos (lo que evita loops) y la sentencia para redirigir las llamadas (swith).
Debemos añadir las siguientes lineas al final del archivo extensions_custom.conf en los dos servidores:
; ********************************************
; CONFIGURACION PARA DUDNi
[dundi-priv-canonical]
; Aqui incluimos el contexto que contiene las extensiones.
include => ext-local
; Aqui incluimos el contexto que contiene las colas de atención o queues.
include => ext-queues

[dundi-priv-customers]
; Si tenemos clientes (o revendemos servicios) podemos listarlos aqui

[dundi-priv-via-pstn]
; Aqui podemos incluir el contexto con nuestras troncales hacia la PSTN,
; si queremos que los demas equipos puedan usar nuestras troncales
; include => outbound-allroutes

[dundi-priv-local]
; En este contexto unificamos los tres contextos, este lo podemos usar como
; contexto de la troncal iax de dundi
include => dundi-priv-canonical
include => dundi-priv-customers
include => dundi-priv-via-pstn

[dundi-priv-lookup]
; Este contexto se encarga de hacer la busqueda de un numero por dundi
; Antes de hacer la busqueda definimos apropiadamente nuestro caller id.
; ya que si no tendremos un caller id como "device<0000>".
exten => _X.,1,Macro(user-callerid)
exten => _X.,n,Macro(dundi-priv,${EXTEN})
exten => _X.,n,GotoIf($["${DIALSTATUS}" = "BUSY"]?100)
exten => _X.,n,Goto(bad-number,${EXTEN},1)
exten => _X.,100,Playtones(congestion)
exten => _X.,101,Congestion(10)

[macro-dundi-priv]
; Esta es la macro que llamamos desde el contexto [dundi-priv-lookup]
; Tambien evita que hayan loops en las consultas dundi.
exten => s,1,Goto(${ARG1},1)
switch => DUNDi/priv
; ********************************************

Aplicar la configuración
Para que los cambios se hagan efectivos podemos ejecutar el comando:
asterisk -rx "restart when convenient"
o si no queremos esperar:
asterisk -rx "restart now"

Resumen
Ahora cada extensión que creemos desde freePBX se añadirá al contexto [ext-local] el cual esta incluido en dundi-priv-canonical que va a usar dundi para indicar que extensiones tenemos localmente.
Si nosotros tenemos la extensión responderemos con los datos para la comunicación hacia esa extensión; esos datos a su ves los interpreta el servidor que esta preguntando y usa la función switch que pusimos en el archivo extensions_custom.conf para comunicarse con la extensión local.

Integrando IVRs en una red DUNDi con Elastix (usando FreePBX)

Cuando tenemos varios equipos y lineas de la PSTN que ingresan a uno de ellos, es muy posible que queramos que el IVR que recibe esas lineas pueda llamar directamente a las extensiones de la red DUNDi; aqui describo como lo he logrado:
Lo primero que hay que hacer es localizar el contexto de IVR que hemos creado desde freePBX, este ultimo siempre los crea asi:
[ivr-N]
donde N es un numero incremental que asigna automaticamente.
Tenemos que revisar en el archivo /etc/asterisk/extensions_additional.conf y buscar el contexto adecuado.
Suponiendo que el contexto del IVR es [ivr-2] tendriamos que definir la siguiente configuracion en el archivo /etc/asterisk/extensions_additional.conf:
[dundi-priv-lookup-ivr]
exten => _XXXX,1,Macro(dundi-priv,${EXTEN})
exten => _XXXX,n,GotoIf($["${DIALSTATUS}" = "BUSY"]?100)
exten => _XXXX,n,Goto(ivr-2,i,1)
exten => _XXXX,n,Hangup()
exten => _XXXX,100,Playtones(congestion)
exten => _XXXX,101,Congestion(10)
exten => _XXXX,102,Hangup()
[ivr-2-custom]
include => ext-local
include => ext-queues
include => dundi-priv-lookup-ivr

Esto teniendo en cuenta que nuestras extensiones internas tienen 4 digitos, si no debemos modificar el contexto [dundi-priv-lookup-ivr] y en vez de _XXXX poner el numero de digitos que usamos.