jueves 13 de septiembre de 2007

Códigos SIP de respuesta en "parallel fork"

Tras un larguísimo pero muy didáctico hilo de discusión en la lista de correo de Twinkle he aprendido algo interesante sobre las distintas formas de rechazar un INVITE y las consecuencias que ello tiene en entornos donde se produce un "pararell fork".

Parallel fork

Este término viene a designar un comportamiento del proxy SIP por el cuál una llamada se ramifica en varias "branches" (ramas), bien porque el usuario llamado está registrado en varias localizaciones, bien porque tiene activado un "forwarding" en paralelo, o bien porque se fuerza en el proxy SIP la creación de una nueva "branch" por la razón que fuere. En cualquier caso, el efecto final es que la llamada se ramifica en varias y llega a distintos dispositivos SIP en paralelo, hasta que alguno la acepte, hasta que alguno la rechace con un código 603 (lo explico más adelante) o hasta que en todas las branches se rechace la llamada o expire el temporizador.

Como curiosidad, aquellos que sólo conozcan SIP desde el punto de vista de Asterisk no han podido experimentar este concepto ya que sencillamente la implementación SIP de Asterisk no lo soporta (en Asterisk un mismo usuario SIP no se puede registrar desde varios dispositivos a la vez, ya que el último registro reemplazaría al anterior).

Casos de "parallel fork"

  • Registro en varias localizaciones: Esto sencillamente significa que una misma cuenta SIP esté configurada en varios dispositivos SIP y que todos ellos se registran en el proxy SIP (insisto en que esto no es posible en Asterisk, pero sí en proxy SIP de verdad como SER u OpenSer). Cuando alguien llama a dicho usuario el proxy SIP consulta los registros del mismo y "forkea" el INVITE a todas las localizaciones.
  • Forwarding en paralelo: Supongamos que un usuario tiene configurado en el proxy SIP un desvío en paralelo a otro destino SIP. Esto significa que si recibe una llamada, ésta se rutará a todas las localizaciones de dicho usuario llamado (el primer caso) y también a otro destino en paralelo (que podría ser un móvil a traves de una pasarela SIP_to_PSTN).

Ejemplo de "parallel fork"

Supongamos que un usuario SIP está registrado en OpenSer desde dos softphones (Twinkle y Linphone), y recibe una llamada desde un Minisip. Describiré mínimamente las branches que se crean (una por cada cabecera "Via") así como los conceptos de llamada o diálogo ("from-tag" + "to-tag" + "call-id"):

  • Linphone inicia un INVITE y genera un branch (transacción SIP), una etiqueta "from-tag" y un "call-id" para identificar dicha llamada (diálogo):
    branch=z9hG4bK1306833844 (Minisip-OpenSer)
    From: tag=1436578172 (Minisip)
    To: (vacío, esto lo genera cada llamado en el "180 Ringing")
    Call-id: 1312809482@192.168.1.58
  • El INVITE llega a OpenSer quien localiza al usuario llamado en Twinkle y Linphone y crea un branch para cada uno:
    branch=z9hG4bK035a.0a4552d7.0 (OpenSer-Twinkle)
    branch=z9hG4bK035a.0a4552d7.1 (OpenSer-Linphone)
  • En las respuestas "180 Ringing" en Twinkle y Linphone cada uno de ellos indica un "to-tag":
    To: tag=omjpo (Twinkle)
    To: tag=875927769 (Minisip)
  • En caso de que uno de ellos acepte la llamada (devuelve "200 OK") ya tenemos los campos necesarios que identifican unequívocamente a dicha llamada. Por ejemplo, si responde Twinkle:
    branch=z9hG4bK1306833844 (Minisip-OpenSer)
    branch=z9hG4bK035a.0a4552d7.0 (OpenSer-Twinkle)
    From: tag=1436578172 (Minisip)
    To: tag=omjpo (Twinkle)
    Call-id: 1312809482@192.168.1.58

Códigos SIP de llamada rechazada

Vamos a contemplar sólo los casos donde una llamada es rechazada por decisión "humana". Esto abarca la posibilidad de que el usuario presione el botón "Colgar" o "Rechazar" llamada (el típico teléfono rojo), o bien que tenga activado DND (Don't Disturb) y el teléfono rechace automáticamente la llamada.

480 Temporarily Unavailable

Según el RFC 3261:

The callee's end system was contacted successfully but the callee is currently unavailable (for example, is not logged in, logged in but in a state that precludes communication with the callee, or has activated the "do not disturb" feature). The response MAY indicate a better time to call in the Retry-After header field. The user could also be available elsewhere (unbeknownst to this server). The reason phrase SHOULD indicate a more precise cause as to why the callee is unavailable. This value SHOULD be settable by the UA. Status 486 (Busy Here) MAY be used to more precisely indicate a particular reason for the call failure. This status is also returned by a redirect or proxy server that recognizes the user identified by the Request-URI, but does not currently have a valid forwarding location for that user.

Básicamente significa que el usuario rechaza la llamada "porque no le viene bien aceptarla" o porque ha activado el DND. Pero lo más importante es entender que los códigos 4XX son respuestas que sólo afectan a su propia "branch". Es decir, si se ha producivo "parallel fork" y un dispositivo SIP devuelve un 480 sólo se cancela dicha llamada, pero el resto de "branches" persisten en estado activo, por lo que los restantes teléfonos siguen sonando.

486 Busy Here

Según el RFC 3261:

The callee's end system was contacted successfully, but the callee is currently not willing or able to take additional calls at this end system. The response MAY indicate a better time to all in the Retry-After header field. The user could also be available elsewhere, such as through a voice mail service. Status 600 (Busy Everywhere) SHOULD be used if the client knows that no other end system will be able to accept this call.

Lo que viene a decir algo similar al 480, aunque a efectos prácticos suele haber diferencias ya que muchos dispositivos SIP conciben un 486 como un "ocupado, pero llama de nuevo tan pronto como quieras", lo que provoca que si se rechaza una llamada con un 486 se repita la llamada instantáneamente.

Esto se produce por ejemplo en algunos Nokia al llamar a través de un proveedor SIP_to_GSM que genera un "486 Busy Here" cuando el móvil devuelve un "Busy" (ocupado en ese momento hablando con otra persona). Entonces el Nokia repite acto seguido la llamada, lo cuál puede llegar a ser molesto.

Dejar también claro que éste es un código 4XX que sólo termina su propia branch.

603 Decline

Según el RFC 3261:

The callee's machine was successfully contacted but the user explicitly does not wish to or cannot participate. The response MAY indicate a better time to call in the Retry-After header field. This status response is returned only if the client knows that no other end point will answer the request.

Su significado literal es "rechazar" la llamada. Pero su característica notable es que se trata de un código 6XX, lo que implica la cancelación del resto de branches generadas en el proxy SIP. Es decir, si se produce "parallel fork" y uno de los dispositivos devuelve un 603, el proxy SIP debe cancelar el resto de branches enviándoles un CANCEL.

Distintas implementaciones

Muchos se quejan de que el RFC no deja nada claro qué código usar para rechazar una llamada, ni siquiera queda muy clara la diferencia entre "480 Temporarily Unavailable" y "486 Busy Here" (la diferencia que he explicado viene a ser más fruto de distintas interpretaciones por parte de cada fabricante/proveedor más que algo que realmente figure en el RFC). Así pues la realidad es que cada teléfono SIP envía un código SIP impredecible cuando el usuario presiona el botón de rechazar llamada. Por ejemplo:

  • Twinkle: Responde con un "603 Decline" por lo que el proxy SIP cancela el resto de branches.
  • X-Lite: Reponde con un "486 Busy Here" (el resto de branches continúan).
  • SJphone: Responde con un "480 Temporarily Unavailable" (el resto de branches continúan).

Tal vez haciendo pruebas me encuentre con otro exótico código de respuesta para estos casos, si así fuese actualizaría este post.

4 comentarios:

Anónimo dijo...

Muy claro y un dato curioso a tener en cuenta.

Twinkle parece el único sofphone libre medio decente. Lástima la carencia de video.

¿Para cuando un post sobre los distintos sofphones?

saghul dijo...

Muy buen post Iñaki, la verdad es que aclara bastante unas cuantas cosas.

Soportará Asterisk algún día parallel forking? :)

Iñaki Baz Castillo dijo...

@anónimo: El autor de Twinkle planea añadir vídeo y un montón de chuladas más (XCAP, presencia rica, SIP sobre TCP...). Mira el roadmap (al final de la página).

@saghul: ¿Y quién lo necesita teniendo OpenSer? XD

victor.pascual dijo...

creo que hay alguien que ha visto la luz con Openser :-)

Buen post!