Integración Kamailio y Asterisk con: PATH

¡Hola! Esta vez nos queremos centrar en VoIP (SIP) de forma directa 🙂

Parece que fue ayer cuando allá por el 2006 empezábamos nuestros primeros proyectos con Asterisk, donde casi lo que más te interesa es que funcione «y punto», que bastante logro era en aquella época 😉 Suponemos que como al resto de integradores, tras pasar por las épocas de buscar estabilidad (sacando en su momento Asterisk RSP), lo que nos llegó a todos es buscar el crecimiento  en capacidad, pudiendo escalar, de ahí que entren en escena Kamailio, RtpProxy / RTPEngine 🙂

Dentro de las posibles integraciones, las que más se conocen:

Integración más bien «tradicional»

  • Para Asterisk, todos los UA’s están en la IP de Kamailio, no se registran contran él, ni tampoco autentican.post_kamailio_esquemarewrite
  • Kamailio gestiona los registers y la autenticación, así como todo el tema del NAT – incluyendo rtpproxy o RTPEngine.

A nivel de routing en sí, muchos optan por enviar todo lo interno tb hacia Asterisk para que controle todo (y se simplifiquen BLF’s y derivados), o sino, que el propio Kamailio haga ese dispatching inicial.

Integración con Outboundproxy

  • En Asterisk se configuran los «friends/peers» 😀 con OutboundProxy, soportado en 1.4 bien gracias a los patches de Olle, y en 1.8 y posteriores post_kamailio_esquemaoutboundigualmente. Al estar configurados así, cualquier diálogo que inicie Asterisk hacia dicho sip endpoint lo hará a través del outbound proxy.
  • Es decir, es indiferente lo que Asterisk tenga en memoria/realtime host, o lo que tenga en su contact, siempre lanzará la petición a través del proxy.

La pseudo-ventaja de la integración con Outboundproxy es que el esquema es realmente muy sencillo, Kamailio recibirá las requests originadas por Asterisk con la URI SIP Real del contact del usuario, con lo que nos ahorramos transformaciones, simplifica mucho el escenario. En las requests originadas en el lado de los UA’s, Kamailio actuará como Record-Route y listo.

 

Integración con PATH

La que queremos comentar nosotros en este post es la integración usando: PATH

El concepto de PATH, está definido en la extensión de la biblia: RFC3327 , que en sus primeras secciones expone:

   Scenario:

   UA1----P1-----P2-----P3------REGISTRAR

   UA1 wishes to register with REGISTRAR.  However, due to network
   topology, UA1 must use P1 as an "outbound proxy", and all requests
   between UA1 and REGISTRAR must also traverse P1, P2, and P3 before
   reaching REGISTRAR.  Likewise, all requests between REGISTRAR and UA1
   must also traverse P3, P2, and P1 before reaching UA1.

   UA1 has a standing relationship with REGISTRAR.  How UA1 establishes
   this relationship is outside the scope of this document.  UA1
   discovers P1 as a result of configuration, DHCP assignment or other
   similar operation, also outside the scope of this document.
   REGISTRAR has a similar "default outbound proxy" relationship with
   P3.

   Eventually, REGISTRAR or a "home proxy" (a proxy serving as the
   terminal point for routing an address-of-record) closely related to
   it will receive a request destined for UA1.  It needs to know which
   proxies must be transited by that request in order to get back to
   UA1.  In some cases, this information may be deducible from SIP
   routing configuration tables or from DNS entries.  In other cases,
   such as that raised by 3GPP, the information is not readily available
   outside of the SIP REGISTER transaction.

   The Path extension header field allows accumulating and transmitting
   the list of proxies between UA1 and REGISTRAR.  Intermediate nodes
   such as P1 may statefully retain Path information if needed by
   operational policy.  This mechanism is in many ways similar to the
   operation of Record-Route in dialog-initiating requests.  The routing
   established by the Path header field mechanism applies only to
   requests transiting or originating in the home domain.

Es decir, al contrario que en outbound proxy, lo que conseguimos con el PATH support es (de una forma bastante oficiosa) que Asterisk sepa que cada vez que le tiene que lanzar un INVITE iniciando un diálogo, lo haga a través del punto por donde le ha entrado el Register (que es Kamailio).

En lo que respecta el flujo del Register inicial (ejemplo sin autenticación):

sip_register_path_flow

En lo respecta el contenido de los registers, Register recibido por Kamailio (centro del flujo):

REGISTER sip:voip.****.es SIP/2.0
Via: SIP/2.0/UDP 10.10.1.122:5065;branch=z9hG4bK-f908b614
From: "prod1006" <sip:[email protected]>;tag=d6186420f11b3b23o5
To: "prod1006" <sip:prod1006@voip.****.es>
Call-ID: [email protected]
CSeq: 63267 REGISTER
Max-Forwards: 70
Authorization: Digest username="prod1006",realm="voip.****.es",nonce="____________",uri="sip:voip.****.es",algorithm=MD5,response="______"
Contact: "prod1006" <sip:[email protected]:5065>;expires=3600
User-Agent: Cisco/SPA509G-7.5.6a
Content-Length: 0
Allow: ACK, BYE, CANCEL, INFO, INVITE, NOTIFY, OPTIONS, REFER, UPDATE, MESSAGE
Allow-Events: hold,talk,conference
Supported: replaces

Register reenviado hacia Asterisk:

REGISTER sip:voip.****.es SIP/2.0
Via: SIP/2.0/UDP 151________78;branch=z9hG4bKa6d9.109e137ffc16023d7143af70c0f8f9da.0
Via: SIP/2.0/UDP 10.10.1.122:5065;rport=9498;received=62.______;branch=z9hG4bK-f908b614
From: "prod1006" <sip:prod1006@voip.****.es>;tag=d6186420f11b3b23o5
To: "prod1006" <sip:[email protected]>
Call-ID: [email protected]
CSeq: 63267 REGISTER
Max-Forwards: 69
Contact: "prod1006" <sip:[email protected]:5065;alias=62._____~9498~1>;expires=300
User-Agent: Cisco/SPA509G-7.5.6a
Content-Length: 0
Allow: ACK, BYE, CANCEL, INFO, INVITE, NOTIFY, OPTIONS, REFER, UPDATE, MESSAGE
Allow-Events: hold,talk,conference
Supported: replaces
Path: <sip:151.______78;lr>
Supported: path

Nótese la cabecera: Path: <sip:151.______78;lr> , que esl a clave de toda la cuestión 🙂

 

¿Cuál es la principal ventaja respecto a Outboundproxy?

En Asterisk, el uso de Outboundproxy es más bien estático (o pseudo estático, porque tenemos Realtime o Sorcery), es decir, el que tengamos configurado, es el que se usará siempre para dicho usuario.

Sin embargo, con PATH, lo que conseguimos es que Asterisk use como Outboundproxy en los diálogos iniciados por Asterisk: el servidor por el que ha entrado el Register, que añade la cabecera Path a su paso 🙂 En los diálogos no iniciados por Asterisk, se sigue el camino standard (initial response usando Via Headers, sequential requests vía loose routing)

Es decir, podríamos tener uno o varios servidores Asterisk, que a su vez están flanqueados por Kamailios geolocalizados / balanceados, y que Asterisk use el que corresponda en base al Register inicial que le indica. «Hola, soy BOB, estoy aquí y para hablar conmigo tienes que pasar por este Kamailio». (obviamente, Bob al inicio solo ha dicho que está ahí, el Path lo añade el Kamailio 😉 )

De forma simplificada, algo así como:

post_kamailio_path_big

Configuraciones necesarias para funcionar con PATH

Kamailio

En el lado Kamailio, lo primero de todo es tener activado el módulo path:

loadmodule "path.so"

¿Dónde aplicamos/añadimos las cabeceras Path?

Sólo debe ser añadida en los casos de REGISTER, es decir, lo ideal sería tener un script de configuración de Kamailio que tenga routes diferentes (sanity checks, security checks, … y luego rutar los REGISTER).

Una vez que hemos identificado y estamos ya gestionando el REGISTER, tendremos que decidir bien primero quién autentica, si liberamos a Asterisk de dicho proceso de autenticación, únicamente reenviaremos el Register si está autenticado.

En tal caso, la función que exporta el módulo y la que tenemos que usar, es: add_path()

add_path();

¿Qué debemos tener en cuenta?

Si lo que vamos a tener detrás es un Asterisk 12 o 13, es necesario añadir la respectiva cabecera indicativa:

append_hf("Supported: path\r\n");

Igualmente, es altamente probable que el UAC envíe un Contact con IP Privada, con lo que antes de reenviar a Asterisk, tb conviene fixearlo:

    if (isflagset(FLT_NATS)) {
        xlog("L_INFO", "REGISTER: Cliente tras NAT, set_contact_alias");
        set_contact_alias();
    }

Asterisk

Lo primero de todo, es tener el módulo cargado si lo que usamos es PJSIP (si utilizamos chan_sip no es necesario cargar módulos adicionales), en este caso, modules.conf:

modules.conf:load => res_pjsip_path.so

Y ya está :)= , It Works 😉

Si utilizamos chan_sip.so, y Asterisk > 1.8 :

supportpath=yes

Si estamos con Asterisk 1.8, por desgracia no está soportado de base, hay que aplicar patches. Siendo el patch set de Olle E.Johansson el camino recomendado. Está tb accesible y con varios comentarios en el JIRA de Asterisk, así como en el ReviewBoard.

En el caso de 1.8, lo que sí hemos visto es que los OPTIONS (qualify=yes), no parecen respetar el Path.

Una vez que tenemos ya activado el soporte para Path y activado el supportpath, si estamos usando PJSIP, al hacer un pjsip show aor XXX, deberíamos ver:

support_path : true

y, por mucho que miremos, no hay ningún comando desde el CLI que nos del PATH que tiene guardado. Así que nos tocará tirar de consulta a BBDD (si estamos con Sorcery, asumiendo que nuestra tabla se llama algo parecido):

mysql> select uri,path from ast_ps_contacts;
+------------------------------------------------------------+-------------------------+
| uri                                                        | path                    |
+------------------------------------------------------------+-------------------------+
| sip:[email protected]:5063^3Balias=62.AA.BB.CC~8862~1     | <sip:5.XX.YY.131^3Blr> |
| sip:[email protected]:5062^3Balias=62.AA.BB.CC~62664~1 | <sip:5.XX.YY.131^3Blr> |
| sip:[email protected]:5062^3Balias=62.AA.BB.CC~58262~1 | <sip:5.XX.YY.131^3Blr> |
| sip:[email protected]:5062^3Balias=62.AA.BB.CC~47799~1 | <sip:5.XX.YY.131^3Blr> |
| sip:[email protected]:5060^3Balias=62.AA.BB.CC~2953~1  | <sip:5.XX.YY.131^3Blr> |
+------------------------------------------------------------+-------------------------+
5 rows in set (0.01 sec)

Como se puede observar, Asterisk ha guardado como contact directamente lo que ha recibido desde Kamailio, que en este caso podemos observar que son 5 SIP UA’s, que están tras el mismo NAT (detrás de la IP Pública 62.AA.BB.CC, en lo que parece tb el mismo rango de IP’s de LAN).

Y sin analizamos un diálogo iniciado por Asterisk tal que:

flujo_INVITE

Si vemos la request que inicia el diálogo (INVITE):

2015/05/04 12:23:53.643122 5.AA.BB.130:5060 -> 5.AA.BB.131:5060
INVITE sip:[email protected]:5062;alias=62.99.78.6~58262~1 SIP/2.0
Via: SIP/2.0/UDP 5.AA.BB.130:5060;rport;branch=z9hG4bKPja1ab94b6-22b2-4b23-9e10-6bb512a656ec
From: "Mikel Madalenas" <sip:[email protected]>;tag=45ebbbf8-3983-4234-8f5a-f0a350181a15
To: <sip:[email protected];alias=62.CC.DD.EE~58262~1>
Contact: <sip:[email protected]:5060>
Call-ID: 230ebc24-30cb-42dd-9b52-084c01bcce16
CSeq: 13607 INVITE
Allow: OPTIONS, SUBSCRIBE, NOTIFY, PUBLISH, INVITE, ACK, BYE, CANCEL, UPDATE, PRACK, MESSAGE, REGISTER, REFER
Supported: 100rel, timer, replaces, norefersub, path
X-CID: [email protected]
Route: <sip:5.AA.BB.131;lr>
Content-Type: application/sdp
Content-Length:   179

v=0
o=- 680262441 680262441 IN IP4 5.AA.BB.130
s=Asterisk
c=IN IP4 5.AA.BB.130
t=0 0
m=audio 17402 RTP/AVP 8
a=rtpmap:8 PCMA/8000
a=ptime:20
a=maxptime:150
a=sendrecv

Salen dos puntos interesantes a comentar:

  • Request  URI. Como podemos ver, aunque el destino sea 5.AA.BB.131, es «simplemente» / «estilo» outbound-proxy, es decir, Asterisk se lo envía a Kamailio para que lo gestione y lo rute según proceda.
  • Cabecera Route preseteada. Asterisk lanza ya el Invite con una cabecera route preparada (podrían haber sido varias en caso de que el Path en el Register sea múltiple).

¿Y que pasa con los NOTIFY’s?

El SUBSCRIBE en sí inicia un dialogo, y parte del SIP UA, en SUBSCRIBE no está soportada la cabecera PATH. En este caso, simplemente con Record-Route’ar es suficiente. Es decir,  «lo bueno» es que es un diálogo iniciado por el UA 🙂 Todo el tema del PATH es principalmente para los diálogos iniciados por Asterisk (que generalmente suelen ser la segunda pata, actuando como B2BUA).

Resumiendo, esta integración es bastante simple, y permite ahorrarse transformaciones/asociaciones en las r-uri y derivados, en nuestra humilde opinión, bastante «clean», y como siempre en este tipo de proyectos, es importante empezar con un planteamiento limpio de base 🙂

 



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


  • Gran artículo Zgor 🙂 La verdad es que el soporte de Path de pjsip simplifica mucho las integraciones. Lástima que en muchos casos aún no esté disponible.

    Un comentario acerca de la integración con outboundproxy:

    El problema en este caso es que Asterisk guarda el contact tal cual lo manda el UA. Por ejemplo ‘sip:[email protected]’. En caso de nat y de que no se use el outbound proxy, eso no es problema si tenemos el ‘nat=yes’ pero en el caso de outboundproxy y nat, el kamailio recibe un OPTIONS o un INVITE con el ruri tal que ‘sip:[email protected]’ por lo que no es rutable al UA que está tras NAT porque no tenemos la información de IP:Puerto externo.

    En el caso de la integración más tradicional, que es mi preferida cuando es posible, el problema viene cuando lo que quieres integrar detrás está embeded con su propia DB de gestión de usuario o tiene su propio schema como en el caso de FreePBX. En ese caso la autenticación en el lado de Kamailio no es posible y nos tenemos que ir a otra integración tipo outboundproxy con los problemas que eso acarrea.

    Así que mi consejo a navegantes: Haced caso a Zgor y si tenéis una instalación limpia que poder hacer usad pjsip y la integración con Path. Las integraciones con instalaciones existentes de Asterisk y derivados puedes ser bastante tricky.

    un saludo!!

    Jon Bonilla (Manwe) Hace 9 años Responde


  • […] su momento ya comentamos las ventajas de utilizar el módulo path, permitiendo que todo funcione sin tocar las R-URI y siendo todo mas limpio, así que nos pasaremos […]

    VoIP y la Guerra por el Crecimiento Horizontal con el trío: Docker, Kamailio y Asterisk | Blog Irontec Hace 9 años Responde


  • […] 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 […]

    VoIP: Mecanismos de supervivencia local con Kamailio y Edge Router | Blog Irontec Hace 8 años Responde


  • […] Integración Kamailio y Asterisk con: PATH, de Gorka Gorrotxategi. […]

    Los 10 post más leídos del Mejor Blog de Software Libre | Blog Irontec Hace 8 años Responde


Queremos tu opinión :)