Tráfico RTP directo entre dispositivos con NAT (y parche)
En asterisk, el parámetro “canreinvite=yes” en sip.conf a la hora de definir un dispositivo SIP, indica la disponibilidad de dicho dispositivo a aceptar tráfico RTP directamente desde el otro dispositivo involucrado en la llamada, sin que asterisk tenga que estar en medio de la comunicación, reenviando la información de uno a otro.
En una red local, no hay gran problema… cualquier red que se precie usa cable CAT5 y switches 10/100… más que de sobra como para que afecte ese extra de paquetes circulando. Sin embargo, cuando tenemos un asterisk en una sede, y varios teléfonos SIP en otra sede distinta (conectados mediante ADSL, por ejemplo), la cosa cambia.
El ancho de banda de una ADSL es asimétrico, y aunque tengamos 3 “megas” de bajada, la subida no suele pasar de unos míseros 320kbs (bits, no bytes). De ahi que sea un recurso muy valioso, como para malgastarlo cuando no es realmente necesario. Con este ancho de banda, podemos mantener unas 10 conversaciones con el códec G729, y unas 3 con G711. (Usando esta calculadora de ancho de banda)
Un caso típico
Imaginemos el siguiente escenario:
- Un servidor asterisk en la sede central
- Teléfonos SIP en la sede A (A1,A2,A3..An). Todos ellos configurados con nat=yes.
- Otros tantos en la sede B (B1..Bn).También con nat=yes.
- En ninguna de las dos sedes se abren puertos hacia los diferentes teléfonos.
Si impedimos el tráfico de paquetes de voz (RTP) directo entre teléfonos (canreinvite=no), al hacer una llamada interna, desde A1 a A2, habría dos flujos de paquetes:
- Desde A1 hasta el servidor asterisk, y desde aquí de vuelta a A2
- Justo el camino contrario, ya que la comunicación es full-duplex.
Fíjemonos que para una simple conversación entre extensiones, estamos utilizando nuestra conexión ADSL por partida doble. Estamos utilizando 2 de las llamadas simultáneas disponibles con nuestra humilde conexión a internet.
En una llamada desde A1 a B1 pasaría lo mismo, salvo que en este caso es lo que esperamos. A1 y B1 están en sedes distintas, y no hay forma de que se pueda establecer una comunicación directa entre ambos dispositivos. Lo que queremos es que asterisk haga de enlace entre ambos. Cada sede estará gastando un canal de los que tenga disponible.
Queremos evitar este consumo innecesario de ancho de banda cuando los dos teléfonos pueden establecer una comunicación RTP directa. Para ello, activaremos “canreinvite=yes” en la configuración de todos los dispositivos. Repetimos el experimento.
Llamamos desde A1 a A2. Asterisk, que hace de intermediario, indica a A1 que envíe su tráfico RTP a la dirección de A2, y hace lo propio con A2. Las direcciones IP utilizadas son las IP’s locales de cada teléfono (192.168.0.x, por ejemplo), y no la IP pública de la ADSL de la sede.
El beneficio es palpable, mientras asterisk se encarga de la señalización (indicando cuando un teléfono cuelga, cuando se hace una transferencia, etc), el tráfico RTP de voz va directamente de un teléfono a otro, sin salir a Internet en ningún momento, y sin gastar ningún canal.
Pero todo tiene un “pero”. Al hacer una llamada desde A1 a B1, asterisk también indicará a los dispositivos que se envíen el tráfico RTP directamente entre ellos. El problema está en que, al utilizar las IP’s privadas y ser dos redes distintas, separadas, que podrían incluso compartir la misma dirección de red, la llamada se establecerá pero no habrá audio en ningún sentido. Mal asunto.
¿Por qué? En la actualidad, cuando ambos dispositivos están configurados con “canreinvite=yes”, asterisk obedece y siempre que puede intenta que se envíen el tráfico RTP entre ellos directamente. Lamentablemente, cuando están en redes distintas, es imposible. La solución estaría en comprobar, cuando los dispositivos están detrás de routers haciendo NAT, si la dirección IP pública de ambos coincide. Si fuese el caso, podremos estar seguros de que están en la misma red, y podrán encontrarse. Si la IP pública es diferente, asterisk simplemente debe quedarse en medio.
Las llamadas dentro de cada sede funcionarán bien, sin malgastar ancho de banda de la ADSL, mientras que las llamadas entre sedes pasaran a través de asterisk.
Una solución
Y después de esta soberana paliza (que no se cuántos habréis aguantado), os dejo aquí el parche para la revisión r79171 del svn de asterisk/branches/1.4.
Descargar asterisk_nat_rtp_recv_patch_v3.diff
Para aplicar
$ cd /usr/src/asterisk $ patch -p0 < /ruta/a/asterisk_nat_rtp_recv_patch_v3.diff $ make $ make install
Añadimos natonlyreinvitesameip=yes a la sección [general] de nuestro sip.conf.
No he podido testear a fondo este parche, así que si alguien puede reproducir este escenario, agradecería comentarios sobre si funciona o si es una soberana estupidez ![]()



Hola Julian!!
Muy interesante el parche! Pero tengo una dudilla: tu has planteado que si ambos están detrás de NAT, y si las IPs externas coinciden, podríamos haces que el audio fuese de uno a otro… Pero y si son subredes distintas no visibles entre ellas? y si hay “algo” en medio haciendo otro NAT, por ejemplo un AP WiFi o algo así?
No se, se me ocurre que en esos casos podría fallar la cosa, pero no se me ocurre como podría detectarse…
Comment by Saúl Ibarra
— 13 August 2007 @ 7:25
Hombre, el método no es perfecto, pero mejora lo que viene de serie
Ahora bien, se podría además comparar las IP’s privadas de cada teléfono, y ver si pertenecen a la misma red. Podríamos suponer que están en una red de clase C (máscara de red 255.255.255.0, por ejemplo).
De todas formas, para escenarios complicados, lo mejor es un canreinvite=no
Comment by julianjm
— 13 August 2007 @ 7:43
Tienes toda la razón, y la verdad es que es una buena solución para muuuchos casos… Has pensado en ponerla en el trunk de Asterisk? yo creo que sería interesante!
Comment by Saúl Ibarra
— 13 August 2007 @ 11:53
Sí, es la idea, aunque no se yo si llegará a entrar, porque hay un cambio en el API de definición de los diferentes canales, que solo sirven para el canal SIP…
A ver si alguien más lo prueba con éxito y lo subo al bugzilla.
Comment by julianjm
— 13 August 2007 @ 12:04
Lo que a mí no me acaba de convencer de Asterisk es esta definición estática sobre si un peer está tras NAT o no, ¿qué hay de la movilidad? ¿qué ocurre si alguien trabaja con su portátil y su softphone y a veces está en la oficina y a veces fuera en un hotel?
Personalmente me gustaría que hubiese algún método como en OpenSer, en el que detectas si el llamante/llamado están tras NAT (y comparas IP’s públicas) para decidir si el audio es directo o no.
Es decir, me gustaría que fuese algo dinámico en vez de tener que asumir en qué escenario va a estar permanentemente cada teléfono SIP.
Por lo demás, a ver si saco un rato esta semana y pruebo el parche.
Saludos.
Comment by Iñaki Baz
— 13 August 2007 @ 13:50
No creo que sea muy complicado controlar ese tema. Sería cuestión de determinar si en el SDP hay una IP privada o pública, y de si coincide o no con la IP real de la que vienen los paquetes.
Estoy de acuerdo en que sería mucho más sencillo que asterisk se encargase de detectar el nat automáticamente, y en lo casos complicados, poder ponerselo a mano.
Comment by julianjm
— 13 August 2007 @ 14:15
al final se acaba siempre en los mismo… VPN. Fijaos la cantidad de problemas que se quita uno. Nada, a convencer a los jefes que pongan linksys con OpenVPN y seguro que os quitáis muchos problemas de encima…
Comment by PACO
— 26 August 2007 @ 7:50
Hola Julian,
En mi empresa utilizamos la version 1.2.18 de asterisk.
Algun parche para esta version ?…
Comment by rcrooke
— 20 September 2007 @ 16:41
Pues no que yo sepa… aunque no creo que sea dificil portar los cambios a la rama 1.2.
Comment by julianjm
— 20 September 2007 @ 18:12
con que version de Asterisk funciona el patch, intento con 1.4.21.2 y no me compila
chan_sip.c:3814: error: too few arguments to function ’sip_set_rtp_peer’
make[1]: *** [chan_sip.o] Error 1
make: *** [channels] Error 2
Gracias.
Comment by Javier
— 20 August 2008 @ 15:56
Javier, el post es de agosto del 2007, así que busca una versión de asterisk sacada por esas fechas.
De todas formas, el parche te aplicó sin problemas?
Comment by julianjm
— 21 August 2008 @ 8:59
Esta fue la salida:
patching file channels/chan_sip.c
Hunk #1 succeeded at 570 (offset 8 lines).
Hunk #2 succeeded at 1537 (offset 15 lines).
Hunk #3 succeeded at 15985 (offset 458 lines).
Hunk #4 succeeded at 17051 (offset 510 lines).
Hunk #5 succeeded at 17296 (offset 512 lines).
Hunk #6 succeeded at 17689 (offset 520 lines).
Hunk #7 succeeded at 17717 (offset 520 lines).
patching file include/asterisk/rtp.h
patching file main/rtp.c
Hunk #1 succeeded at 1551 (offset 10 lines).
Hunk #2 succeeded at 1622 (offset 10 lines).
Hunk #3 succeeded at 2861 (offset 53 lines).
Hunk #4 succeeded at 2869 (offset 53 lines).
Hunk #5 succeeded at 2892 with fuzz 1 (offset 54 lines).
Hunk #6 succeeded at 2924 (offset 54 lines).
Hunk #7 succeeded at 2938 (offset 54 lines).
Hunk #8 succeeded at 2948 (offset 54 lines).
Hunk #9 succeeded at 2971 (offset 54 lines).
Hunk #10 succeeded at 2985 with fuzz 2 (offset 55 lines).
Hunk #11 succeeded at 3032 (offset 65 lines).
Comment by Javier
— 21 August 2008 @ 12:30
Seguramente hayan añadido esa nueva llamada a sip_rtp_set_peer, y claro, le falta el parámetro adicional que añadía el parche… Fíjate en cómo están el resto a intenta añadir el parámetro a esa llamada
Comment by julianjm
— 21 August 2008 @ 14:46
Hola Julian, te queria hacer una consulta, como es posible deshabilitar Packet2Packet bridging, y vengo hace un tiempo luchando con esto. Al usar DISA se ve que me llegan paquetes vacios por un tiempo + o - de 30 segundos se escucha audio solo de un lado luego comienza a funcionar normalmente. Lo que si siempre veo es ese maldito “bridging”, uso obviamente canreinvite=no pero no funciona, simpre hace el maldito puente. Alguna solucion?
Asterisk 1.4.18
Comment by Johnny
— 4 October 2008 @ 19:55
Johnny, que asterisk haga bridging no es malo… es necesario, ya que “bridge” significa puente, es decir, puente entre los dos canales que intervienen en una llamada.
Ahora bien, hay varios tipos de bridging:
Packet2packet es un modo light (en lo referente a recursos de CPU), en el que asterisk simplemente reenvía al otro peer los paquetes RTP que llegan, pero sin “procesarlos”… Es decir, no decodifica el audio.
Luego está el native bridge. Si los dos peers son SIP, y no hay nada que lo prohiba (canreinvite=no), asterisk intentará que los dispositivos se envíen audio directamente entre ellos.
Pero ojo, canreinvite=no tiene que estar definido en los dos peers si quieres que todo el audio pase por asterisk.
[peerA]
canreinvite=yes
…
[peerB]
canreinvite=no
Si asterisk conecta estos dos peers, lo que probablemente ocurrirá es que B enviará siempre su tráfico RTP a asterisk, pero recibirá directamente el tráfico RTP del peer A, ya que este tiene canreinvite=yes.
Tráfico RTP en ambos sentidos:
A-> B
B -> Asterisk -> A
Julian J. M.
Comment by julianjm
— 4 October 2008 @ 23:27
Hola Julian, al final tuve que meter en todos lados canreinvite=yes para que funcione. No pude hacer que todas las llamadas entren y salgan a mi Asterisk con canreinvite=yes, mi Asterisk simpre intentaba hacer bridge, ahi esta el problema.
Despues tengo otro problemita con tos mi debug: Unable to set TOS to 184. Tengo en sip.conf bajo [general] tos=0×68. pero Asterisk parece que no quiere saber nada
De todas maneras Julian te agradesco mucho por predisposicion. Suerte
Johnny
Comment by Johnny
— 5 October 2008 @ 18:08