Números comerciales y menús telefónicos para Estados Unidos y Canadá. ENFR(510) 288-6288[email protected]Iniciar sesión

Inicio › VoIP de código abierto › Kamailio

VoIP de código abierto

Kamailio a fondo: el servidor SIP que enruta y nada más.

Kamailio es un servidor de señalización SIP: registra los teléfonos, los autentica y decide a dónde va cada mensaje SIP, a gran velocidad. Nunca toca un paquete de voz. Esa limitación es justamente su razón de ser, y por eso Kamailio se coloca delante de Asterisk y FreeSWITCH en lugar de reemplazarlos. Esta guía explica la máquina por dentro, luego arma una configuración de borde que funciona y la recorre paso a paso.

Revisado el 26 de octubre de 2026 · 38 fuentes citadas · 30 min de lectura

A high-throughput SIP proxy

Qué es Kamailio y qué no es

El propio repositorio del proyecto lo resume en una línea: "Kamailio is an open source implementation of a SIP Signaling Server" (una implementación de código abierto de un servidor de señalización SIP).3 La página de inicio agrega la cifra de escala, "able to handle thousands of call setups per second" (capaz de manejar miles de establecimientos de llamada por segundo), y nombra los papeles que cumple: balanceador de carga SIP, enrutamiento de menor costo, firewall SIP, proxy de borde, SBC, registrador y servicio de ubicación, presencia, WebRTC e IMS.1 La página de características enumera los papeles clásicos de un servidor SIP: registrador, ubicación, proxy, aplicación y redirección.4

En términos más sencillos, en la mayoría de las instalaciones Kamailio hace cuatro trabajos:

  • Registrador. Los teléfonos envían REGISTER. Kamailio los autentica y guarda dónde se puede localizar a cada uno.1314
  • Servidor de ubicación. Cuando llega una llamada para un usuario, busca ese contacto guardado y reescribe la solicitud hacia el teléfono.13
  • Proxy. Reenvía solicitudes, sin estado o con estado completo de transacción, y se encarga de las retransmisiones, la bifurcación (forking) y la conmutación por falla.1011
  • Balanceador de carga. Reparte las llamadas entre un conjunto de PBX, servidores de medios o pasarelas de operadores, y deja de enviar a los que fallan.17

Lo que no hace es manejar medios. No reproduce mensajes, no graba buzones de voz, no une audio, no ejecuta un IVR. Cuando hay que retransmitir el audio (normalmente porque un teléfono está detrás de un NAT), Kamailio controla un proxy RTP aparte: el módulo rtpengine "enables media streams to be proxied via an RTP proxy" (permite que los flujos de medios pasen por un proxy RTP), y el único proxy con el que "currently known to work" (se sabe que funciona actualmente) es Sipwise rtpengine.18 La página de inicio es explícita sobre el reparto de tareas: Kamailio "can be easily used for scaling up SIP-to-PSTN gateways, PBX systems or media servers like Asterisk, FreeSWITCH or SEMS" (se puede usar fácilmente para escalar pasarelas SIP a PSTN, sistemas PBX o servidores de medios como Asterisk, FreeSWITCH o SEMS).1

Kamailio decide a dónde van los mensajes SIP. Asterisk o FreeSWITCH decide qué pasa con la llamada cuando llega. Si su problema es un menú telefónico, un buzón de voz o una sala de conferencias, Kamailio no es la herramienta. Si su problema son mil teléfonos, tres PBX y ningún punto único de falla, probablemente sí lo es.

Las cifras de rendimiento que publica el proyecto son propias y no tienen fecha: "used as load balancer in stateless mode, Kamailio can handle over 5000 call setups per second" (usado como balanceador de carga sin estado, Kamailio puede manejar más de 5000 establecimientos de llamada por segundo), y "on systems with 4GB memory, Kamailio can serve a population over 300 000 online subscribers" (en sistemas con 4 GB de memoria, Kamailio puede atender a más de 300 000 suscriptores en línea).4 Tómelas como afirmaciones de orden de magnitud del proyecto y no como una prueba de rendimiento que usted pueda reproducir. No encontramos ninguna prueba independiente con método publicado con la cual contrastarlas.

Está escrito "in pure C for Unix/Linux-like systems" (en C puro para sistemas tipo Unix/Linux),1 admite UDP, TCP, TLS y SCTP sobre IPv4 e IPv6, y WebSocket para WebRTC.4 La licencia principal es GPLv2; el repositorio indica que las contribuciones nuevas al núcleo y a varios módulos principales (auth, corex, sl, tls, tm) deben hacerse bajo la licencia BSD.3

De SER a Kamailio: un linaje de 25 años

Kamailio es más antiguo que la mayoría de los productos de VoIP construidos sobre él. La página de historia del proyecto y la de inicio dan las fechas:12

FechaHecho
2 sep 2001SIP Express Router (SER) comienza en el instituto de investigación Fraunhofer FOKUS, en Berlín.
19 sep 2002SER se publica como código abierto bajo GPLv2.
14 jun 2005Se crea OpenSER como una escisión de SER; primera versión 0.9.4, bifurcada de la rama SER 0.9.0.
28 jul 2008"due to trademark issues, OpenSER was renamed to Kamailio" (por problemas de marca, OpenSER pasó a llamarse Kamailio).
4 nov 2008Kamailio y SER inician el proyecto SIP Router para volver a fusionar sus árboles de código.
11 ene 2010Kamailio 3.0.0, la primera versión construida a partir de los árboles integrados.
20 dic 2012Integración completa; el proyecto conserva el nombre Kamailio.

Desplace la tabla hacia los lados para ver todas las columnas.

La página de historia atribuye la bifurcación de 2005 a visiones distintas sobre "the management and development of the open source project (policy about contributions, release cycles and development effort)" (la gestión y el desarrollo del proyecto de código abierto: política de contribuciones, ciclos de versiones y esfuerzo de desarrollo).2 La serie 0.9.x a 1.5.x de OpenSER corrió de forma independiente a SER; desde la 3.0.0, las versiones salieron del árbol fusionado de SIP Router.2 El README del repositorio agrega que Fraunhofer FOKUS "is no longer actively involved in the evolution of the project" (ya no participa activamente en la evolución del proyecto) y que Kamailio "is now developed and managed by its world wide community" (ahora lo desarrolla y gestiona su comunidad mundial).3

El cambio de nombre de 2008 es también el origen de OpenSIPS, y los dos proyectos cuentan esa historia de forma distinta. Volvemos sobre ello en la comparación con OpenSIPS, más abajo.

Por qué importa la historia en la práctica: todavía encontrará "ser" y "openser" en nombres de módulos antiguos, respuestas de listas de correo y tutoriales, y las configuraciones escritas para las versiones 1.x o 3.x no funcionan sin cambios en la 6.x. Cuando busque ayuda, filtre por versión.

Arquitectura: procesos, memoria compartida y un script compilado

Tres decisiones de diseño explican la mayor parte del comportamiento de Kamailio, para bien y para mal.

Es multiproceso. Kamailio lanza un conjunto de procesos de trabajo por cada socket de escucha. El parámetro children es el "Number of children to fork for the UDP interfaces (one set for each interface - ip:port)" (número de hijos que se lanzan para las interfaces UDP, un conjunto por cada interfaz ip:puerto), con valor predeterminado 8, de modo que tres receptores UDP crean tres veces esa cantidad de procesos.5 TCP y TLS tienen su propio grupo, tcp_children, que por defecto es igual a children si no se define.5 Los módulos pueden agregar procesos auxiliares propios; la configuración predeterminada, por ejemplo, le da a usrloc un proceso temporizador con timer_procs.28 Desde la 6.0 también se puede pasar la recepción UDP a un modo multihilo, de forma global o por socket,29 y la 6.1 agregó un modo TLS multihilo opcional.30

Administra su propia memoria, de dos tipos. La guía de diagnóstico de memoria describe "private memory - allocated for each Kamailio process" (memoria privada, asignada a cada proceso de Kamailio), llamada pkg, que no necesita sincronización, y "shared memory - allocated for entire Kamailio instances" (memoria compartida, asignada a toda la instancia de Kamailio), llamada shm, donde "all processes use the same zone" (todos los procesos usan la misma zona) y un mutex protege el acceso.6 Desde la 4.2.0 los valores predeterminados son 8 MB de pkg por proceso y 64 MB de shm; se cambian en la línea de comandos con -M (pkg) y -m (shm).65 Todo lo que cada proceso debe ver vive en shm: la tabla de transacciones, la caché de registros cuando se mantiene en memoria, las tablas hash del módulo htable ("The hash table is stored in shared memory", la tabla hash se guarda en memoria compartida).22

En operación, esto es lo primero que da problemas. Un registrador ocupado o un htable grande agotan los 64 MB de shm mucho antes de agotar la CPU, y el síntoma son errores "no more memory" en syslog, no una caída con una causa clara.6 Dimensione shm según su cantidad de registros y su tasa de transacciones desde el primer día, y vigílela con kamcmd mod.stats all shm.6

El script nativo se compila una sola vez. La documentación de KEMI es franca sobre esta contrapartida: el intérprete "was designed to precompile kamailio.cfg at startup, doing specific optimization for fast runtime operations" (se diseñó para precompilar kamailio.cfg al arrancar, con optimizaciones específicas para operar rápido), y agregar la recarga "will result in losing those optimizations" (haría perder esas optimizaciones).7 Por eso, un cambio en la lógica de enrutamiento nativa exige reiniciar. Muchos parámetros globales y de módulos se pueden cambiar en ejecución mediante comandos RPC,7 y la 6.1 agregó comandos RPC para leer y definir parámetros de módulos en ejecución,30 pero la lógica de enrutamiento queda fija hasta reiniciar, salvo que use KEMI (más abajo).

Además, por defecto Kamailio mantiene estado por transacción, no por llamada. La documentación del módulo rr lo dice sin rodeos: "Kamailio is basically only a transaction stateful proxy, without any dialog support build in." (Kamailio es básicamente un proxy con estado de transacción, sin soporte de diálogos incorporado.)12 Si necesita saber qué llamadas están activas (por ejemplo, para limitar las llamadas simultáneas por cliente), cargue el módulo dialog, que existe justamente porque "stateful" en tm "refers to transaction state, not dialog state" (se refiere al estado de transacción, no al de diálogo).23

kamailio.cfg: tres secciones y una familia de rutas

El archivo de configuración tiene tres partes, en este orden: parámetros globales, luego la sección de módulos con loadmodule y modparam, y por último los bloques de enrutamiento, "typically the biggest one" (normalmente la más grande).5 En los bloques de enrutamiento vive la lógica, y hay varios tipos, cada uno activado por un evento distinto:5

BloqueCuándo se ejecutaQuién lo activa
request_route"executed for each SIP request" (se ejecuta para cada solicitud SIP); el equivalente de main(). Es el único bloque obligatorio.Siempre se ejecuta
route[NAME]Una subruta que se llama como función con route(NAME); puede devolver un valor que se lee con $rc.Se llama explícitamente
branch_route[NAME]Una vez por cada rama de salida de una solicitud reenviada.t_on_branch()
failure_route[NAME]Cuando todas las ramas de una transacción recibieron solo respuestas negativas (≥300). Trabaja sobre la solicitud original, no sobre la respuesta.t_on_failure()
reply_routePara cada respuesta recibida de la red; la ejecuta el núcleo antes que cualquier módulo.Se ejecuta siempre si está definida
onreply_route[NAME]Respuestas dentro de una transacción activa; las ejecuta tm después de reply_route.t_on_reply()
onsend_routeCuando se envía una solicitud; admite un conjunto limitado de comandos.Se ejecuta siempre si está definida
event_route[group:event]Eventos del núcleo, de módulos o de temporizadores, como xhttp:request o dispatcher:dst-down.El módulo que dispara el evento

Desplace la tabla hacia los lados para ver todas las columnas.

Dos comportamientos de esa lista sorprenden a quienes empiezan. Primero, la acción implícita al final de request_route es descartar la solicitud: "To send a reply or forward the request, explicit actions (e.g., sl_send_reply(), forward(), t_relay()) must be called" (para enviar una respuesta o reenviar la solicitud hay que invocar acciones explícitas, como sl_send_reply(), forward() o t_relay()).5 Un script que olvida retransmitir un método lo descarta en silencio. Segundo, los valores de retorno de las subrutas siguen su propia regla: negativo es falso, positivo es verdadero y 0 significa salir. Una subruta sin retorno explícito devuelve el valor de su última acción, por lo que la documentación recomienda devolver siempre un valor de forma explícita.5

El preprocesador

Las líneas que empiezan con #! no son comentarios; son directivas del preprocesador que se evalúan antes de analizar el archivo.5 Las más útiles son #!define, #!ifdef / #!ifndef / #!else / #!endif, #!trydef ("add a define if not already defined", agrega una definición si no existe ya), #!redefine, y #!subst / #!substdef para buscar y reemplazar dentro de cadenas. Kamailio predefine palabras clave como KAMAILIO_6 y MOD_acc (verdadera cuando ese módulo está cargado).5 include_file incorpora otro archivo antes del análisis; import_file hace lo mismo pero no falla si el archivo no existe.5

El manual (cookbook) enumera tres ventajas: activar y desactivar funciones, control condicional donde un if no cabe (parámetros globales y de módulos), y velocidad, porque nada se evalúa en ejecución.5 La configuración predeterminada que viene en el repositorio está construida enteramente así: se activan la autenticación, el manejo de NAT, rtpengine o la protección contra inundaciones con líneas como #!define WITH_AUTH y #!define WITH_NAT.28 También hace import_file "kamailio-local.cfg" para que los ajustes locales vivan fuera del archivo principal.28

Nuestra opinión: lea de principio a fin ese kamailio.cfg predeterminado antes de escribir el suyo. Tiene unas 1,100 líneas, viene con cada rama de versión y recoge años de lecciones sobre retransmisiones, manejo de CANCEL y NAT que es fácil hacer mal partiendo de cero. El ejemplo práctico que sigue es una versión recortada y adaptada de esa configuración.

KEMI: la lógica de enrutamiento en Lua, Python o JavaScript

El lenguaje nativo es un lenguaje específico de dominio con "initial design going back to years 2001-2002" (diseño inicial que se remonta a los años 2001-2002).7 Es rápido, pero incómodo para todo lo que deba comunicarse con sistemas externos y, como se vio, no se puede recargar. El marco Kamailio Embedded Interface (KEMI), "added first in Kamailio v5.0.0" (incorporado por primera vez en Kamailio v5.0.0), permite escribir los bloques de enrutamiento en un lenguaje de propósito general.7

Con KEMI, kamailio.cfg conserva solo los parámetros globales, la carga de módulos y sus ajustes; "the routing blocks are now functions written in a KEMI supported scripting language" (los bloques de enrutamiento pasan a ser funciones escritas en un lenguaje de scripting compatible con KEMI).7 Los lenguajes y módulos admitidos son JavaScript (app_jsdt), Lua (app_lua), Python 2 (app_python), Python 3 (app_python3, y app_python3s desde la 5.7), Ruby (app_ruby) y Squirrel (app_sqlang).78 Existen módulos de Perl, .NET y Java, pero no implementan KEMI.7

El motor se elige con cfgengine y se apunta el módulo a su script. En el script, funciones con nombres fijos ocupan el lugar de los bloques nativos: ksr_request_route() para request_route, ksr_reply_route() para reply_route, ksr_onsend_route() para onsend_route. Las rutas de rama, de respuesta y de falla son funciones normales cuyos nombres se pasan a KSR.tm.t_on_branch(), t_on_reply() y t_on_failure().8 Se admite de Lua 5.1 a 5.4; el motor de JavaScript es DukTape, incrustado en el módulo, así que no necesita biblioteca externa.8 Un script mínimo de enrutamiento en Lua, adaptado del ejemplo del proyecto:8

-- /etc/kamailio/kamailio.lua (with cfgengine "lua" in kamailio.cfg)
function ksr_request_route()
    if KSR.maxfwd.process_maxfwd(10) < 0 then
        KSR.sl.send_reply(483, "Too Many Hops")
        return
    end
    KSR.tm.t_on_failure("ksr_failure_route_one")
    if KSR.tm.t_relay() < 0 then
        KSR.sl.send_reply(500, "Server error")
    end
end

function ksr_failure_route_one()
    KSR.info("call failed on every branch\n")
end

Las líneas correspondientes de kamailio.cfg son loadmodule "app_lua.so", modparam("app_lua", "load", "/etc/kamailio/kamailio.lua") y cfgengine "lua".8 Las funciones de los módulos aparecen bajo KSR.<módulo>, de modo que t_relay() se convierte en KSR.tm.t_relay().

La ventaja práctica es la recarga. Con el parámetro reload de app_lua activado (el valor predeterminado), el comando RPC app_lua.reload marca el script para recarga, y "the actual reload is done by every working process when the next call to lua_run function is executed" (la recarga real la hace cada proceso de trabajo cuando se ejecuta la siguiente llamada a la función lua_run).9 Cambia la lógica de enrutamiento sin perder registros ni transacciones en curso.

Nuestra opinión: para un equipo que ya escribe Python o Lua, KEMI es la mejor opción predeterminada para todo lo que vaya más allá de un proxy simple. Obtiene estructuras de datos reales, pruebas que se pueden ejecutar fuera de Kamailio y recarga sin reinicio, que la página de KEMI cuenta entre sus principales beneficios.7 El lenguaje nativo conserva dos ventajas: casi todos los ejemplos de la documentación de módulos y de la lista de correo están escritos en él, y no hay intérprete que incrustar. Aprenda primero el lenguaje nativo, porque lo leerá en la documentación de cada módulo, y después decida.

Los módulos que importan

El núcleo de Kamailio analiza SIP y ejecuta el script; casi todo lo demás es un módulo. Estos son los que carga casi cualquier configuración de producción, con lo que dice la documentación de cada uno sobre su función:

MóduloQué haceNotas importantes en producción
tmProcesamiento con estado de las transacciones SIP.10Necesario para cualquier bifurcación, conmutación por falla o contabilidad. "Stateful logic is costly in terms of memory and CPU." (La lógica con estado es costosa en memoria y CPU.)10 fr_timer vale 30 s por defecto si no hay respuesta, y fr_inv_timer 120 s tras una respuesta provisional.10
slPermite que el servidor "act as a stateless UA server and generate replies to SIP requests without keeping state" (actúe como servidor UA sin estado y genere respuestas a solicitudes SIP sin guardar estado).11Úselo para respuestas que nunca necesita retransmitir ni seguir, como el rechazo de escáneres.
registrarProcesa REGISTER; "The actual location database is managed by the USRLOC module." (La base de datos de ubicación real la administra el módulo USRLOC.)13save() guarda los contactos y responde 200 OK; lookup() reescribe el Request-URI y devuelve -1 (no encontrado), -2 (método no admitido) o -3 (error).13 Admite Path (RFC 3327).
usrlocLa tabla de ubicación de usuarios; no exporta funciones de script propias.14db_mode 0 es solo memoria (se pierde al reiniciar), 1 escritura inmediata, 2 escritura diferida con temporizador, 3 solo base de datos, 4 carga al inicio y luego solo memoria. Valor predeterminado: 0.14
auth, auth_dbAutenticación digest genérica y su respaldo en base de datos.1516auth_check() hace autenticación WWW para REGISTER y autenticación de proxy en los demás casos; la bandera 1 también verifica que el usuario autenticado coincida con From/To.16 La 6.1 agregó los digests SHA-512 y SHA-512-256.30
dispatcher"SIP load balancer functionality" (funcionalidad de balanceador de carga SIP); round-robin, por peso, por carga de llamadas, por hash y más.17Puede funcionar sin estado; necesita tm solo para sondear activamente los destinos caídos.17 La 6.1 agregó un algoritmo que combina round-robin con prioridad.3017
rtpengineControla un relé de medios externo Sipwise rtpengine.18rtpengine_manage() determina si es oferta, respuesta o eliminación según el tipo de mensaje.18 La 6.1 agregó funciones SIPREC con rtpengine.30
nathelper"help with NAT traversal and reuse of TCP connections" (ayuda con el cruce de NAT y la reutilización de conexiones TCP).19Heurísticas de nat_uac_test(), fix_nated_register(), set_contact_alias() / handle_ruri_alias(), pings de keepalive.19
pikeMide la tasa de solicitudes por IP de origen e informa las que superan un límite.20"does not implement any actions on blocking" (no implementa ninguna acción de bloqueo); su script decide, normalmente bloqueando la IP en un htable.20
secfilterListas de permitidos y bloqueados para agentes de usuario, IP, países, dominios y usuarios; destinos bloqueados; verificaciones contra inyección SQL.21Los datos se cargan en memoria al arrancar y se recargan por RPC.21
htableTablas hash en memoria compartida, que se usan desde el script como $sht(table=>key).22Vencimiento por elemento; replicación opcional entre servidores mediante el módulo DMQ.22 El lugar habitual para cachés, bloqueos y contadores.
dialogConocimiento de diálogos (llamadas) por encima de las transacciones.23Necesario para límites de llamadas simultáneas y para todo lo que deba saber que una llamada sigue activa.23
toposOcultamiento de topología: elimina las cabeceras de enrutamiento SIP que revelan su estructura interna.24Transparente para el script; almacenamiento en base de datos (predeterminado), Redis o htable. REGISTER y PUBLISH se omiten.24
websocketUn servidor WebSocket (RFC 6455) para SIP y MSRP sobre WebSocket (RFC 7118, RFC 7977), ws y wss.25El handshake llega como un HTTP GET a través del módulo xhttp.25

Desplace la tabla hacia los lados para ver todas las columnas.

Elegir entre sl y tm es la primera decisión de diseño real en cualquier script. El reenvío sin estado casi no usa memoria y es el que más escala, por lo que el módulo dispatcher puede usarse como balanceador puramente sin estado.17 Pero en cuanto quiere "probar el siguiente servidor si este no contesta", necesita una transacción, porque algo tiene que recordar la solicitud y detectar el vencimiento del plazo. La documentación de tm enuncia la regla: "Any kind of forking must be implemented transaction statefully." (Cualquier tipo de bifurcación debe implementarse con estado de transacción.)10

Modelo de versiones y versiones actuales

Kamailio numera sus versiones como mayor.menor.parche. Una nueva serie menor (6.0, 6.1) se anuncia como "major release" (versión mayor): las notas de la 6.0.0 la describen como "a major release, meaning that it is introducing a consistent number of new features as well as improvements to existing components" (una versión mayor, es decir, que introduce un buen número de funciones nuevas y mejoras a componentes existentes).29 Las versiones de parche (6.1.1, 6.1.2, ...) son correcciones, y los anuncios prometen compatibilidad: para la 6.1.5, "The configuration file and database schema compatibility is preserved, which means you don't have to change anything to update." (Se conserva la compatibilidad del archivo de configuración y del esquema de la base de datos, es decir, no tiene que cambiar nada para actualizar.)31

Al 26 de octubre de 2026, según los anuncios del propio proyecto:

RamaVersión mayorÚltimo parcheEstado según el anuncio
6.16.1.0, 18 feb 20261306.1.5, 15 oct 202631"the latest stable version" (la última versión estable), basada en la rama git 6.131
6.06.0.0, 29 ene 2025296.0.8, 16 sep 202632Sigue recibiendo versiones de corrección desde la rama 6.032
5.8No verificado5.8.8, versión publicada en GitHub el 6 mar 202633La versión 5.8 más reciente que encontramos

Desplace la tabla hacia los lados para ver todas las columnas.

Dos advertencias sobre esas fechas. Los objetos de versión de GitHub no siempre coinciden con los anuncios: la 6.1.0 se anuncia en kamailio.org para el 18 de febrero de 2026, mientras que su entrada de versión en GitHub se publicó el 3 de marzo de 2026.3033 Usamos las fechas de los anuncios del proyecto. Y no encontramos en kamailio.org una política de soporte publicada ni una fecha de fin de vida para ninguna rama de Kamailio. En la práctica, el historial de versiones muestra que las dos series más nuevas reciben correcciones en paralelo (la 6.0.6 y la 6.1.1 aparecieron en marzo de 2026; la 6.0.8 y la 6.1.4/6.1.5 con pocas semanas de diferencia),33 pero eso es un patrón observado, no un compromiso.

Conviene conocer qué cambió la 6.x si actualiza desde la 5.x. La 6.0 trajo la recepción UDP multihilo como opción, la escucha en un rango de puertos, variables en la mayoría de los parámetros de funciones de módulos, compilación con CMake, soporte de cliente y servidor HTTP/2, y la selección por solicitud del hash de autenticación.29 La 6.1 agregó cuatro módulos (auth_arnacon, auth_web3, peerstate, ptimer), TLS multihilo, SIPREC con rtpengine, hashes más robustos y lectura/escritura en ejecución de los parámetros de módulos por RPC.30

Nuestra opinión: use la serie más reciente (hoy la 6.1) en instalaciones nuevas, y planee pasar una plataforma 6.0 existente dentro del año siguiente a la aparición de una nueva serie. Sin una fecha de fin de vida publicada, lo prudente es suponer que una rama deja de recibir correcciones cuando existen dos series más nuevas.

Patrones de despliegue

1. Proxy de borde y registrador delante de Asterisk o FreeSWITCH

Es el clásico. Los teléfonos y las troncales hablan solo con Kamailio. Kamailio autentica y registra los teléfonos, absorbe escáneres e inundaciones, corrige el NAT y entrega las llamadas a un conjunto de servidores de medios mediante el dispatcher. Los servidores de medios se ocupan del plan de marcación, el IVR, el buzón de voz y las conferencias, y envían las llamadas destinadas a teléfonos de vuelta por Kamailio, que encuentra el teléfono en su tabla de ubicación. Las PBX nunca guardan registros, de modo que puede agregar, drenar o reemplazar una sin tocar ningún teléfono. Este es el patrón que construye el ejemplo práctico.

2. Pasarela WebRTC

Los navegadores hablan SIP sobre WebSocket seguro. El módulo websocket termina el lado WebSocket: la conexión comienza como un HTTP GET que se atiende en event_route[xhttp:request], donde el script debe validar el Host y, idealmente, la cabecera Origin antes de llamar a ws_handle_handshake().25 Como "a JavaScript stack running in a browser has no way to determine the local address from which the WebSocket connection is made" (una pila de JavaScript que corre en un navegador no tiene forma de determinar la dirección local desde la que se hace la conexión WebSocket), las cabeceras Contact, Via y Record-Route llevan direcciones a las que no se puede enrutar, y la solución documentada es tratar a todo cliente WebSocket como si estuviera detrás de NAT: probar con nat_uac_test(64) y luego usar fix_nated_register(), add_contact_alias() y handle_ruri_alias().2519 Del lado de los medios, rtpengine convierte entre los medios WebRTC del navegador (ICE, DTLS-SRTP, RTP/SAVPF) y el RTP simple de la PBX, con banderas como ICE=force y RTP/SAVPF.18

3. Balanceador de carga de operadores o troncales

El módulo dispatcher en modo sin estado reparte el tráfico entre pasarelas SIP, y el sondeo activo saca de la rotación a las pasarelas caídas. El ejemplo de configuración del propio proyecto para el módulo es exactamente este: "load balancing of VoIP calls with round robin" (balanceo de carga de llamadas VoIP con round robin), sin despachar REGISTER ni presencia.17

4. Frente de seguridad

Incluso una PBX pequeña expuesta a internet recibe escaneos de registro constantes. Un Kamailio delante, con pike, bloqueos en htable, secfilter y topos, ofrece limitación de tasa, listas de bloqueo y ocultamiento de topología que la PBX quizá no haga igual de bien.202124 La configuración predeterminada ya descarta los agentes de usuario que coinciden con friendly|scanner|sipcli|sipvicious|VaxSIPUserAgent|pplsip.28

Llamadas de emergencia

Un proxy delante de una PBX queda en la ruta del 911. El enrutamiento, la ubicación de quien llama y la conmutación por falla de las llamadas de emergencia son responsabilidad de su diseño, no de Kamailio. Pruebe el enrutamiento del 911 de forma explícita cada vez que cambie el dispatcher o la lógica de troncales. Nuestra guía de llamadas de emergencia explica lo que exigen las normas a una línea telefónica de empresa en EE. UU. y Canadá.

Ejemplo práctico: registrador y dispatcher, con rtpengine para NAT

El objetivo: teléfonos en internet, muchos detrás de un NAT doméstico o de oficina, se registran en sip.example.com, en un servidor Kamailio con la dirección 203.0.113.10. Cada llamada que hace un teléfono va a uno de dos servidores de medios (Asterisk o FreeSWITCH, da igual) en 192.0.2.21 y 192.0.2.22, en round-robin y con conmutación automática por falla. Las llamadas que los servidores de medios envían a un teléfono regresan por Kamailio y se entregan al contacto registrado del teléfono. rtpengine retransmite el audio de toda llamada que involucre un teléfono detrás de NAT. Las direcciones salen de los bloques reservados para documentación, TEST-NET-1 y TEST-NET-3.37

El script es un derivado recortado de la configuración predeterminada de la 6.1,28 con la lógica del dispatcher tomada del ejemplo del propio módulo.17 Las partes siguientes van en un solo kamailio.cfg, en el orden indicado. Se supone una base de datos MySQL con el esquema de Kamailio, suscriptores agregados con kamctl o kamcli como describe la configuración predeterminada,28 y rtpengine corriendo en el mismo equipo con su socket de control en 127.0.0.1:2223, que es la sugerencia de la propia configuración predeterminada (rtpengine --listen-ng=127.0.0.1:2223).28 Pruébela en un laboratorio antes de confiarle tráfico; es una configuración didáctica, no una configuración endurecida.

Parte 1: definiciones y parámetros globales

#!KAMAILIO
#
# Edge registrar + dispatcher to two media servers, rtpengine for NAT.
# Written against Kamailio 6.1.x. Addresses are documentation ranges.

#!ifdef WITH_DEBUG
#!define DBGLEVEL 3
#!else
#!define DBGLEVEL 2
#!endif

#!trydef DBURL "mysql://kamailio:[email protected]/kamailio"
#!define DS_MEDIA "1"

# message (transaction) flags and branch flags
#!define FLT_NATS 5
#!define FLB_NATB 6
#!define FLB_NATSIPPING 7

####### Global parameters #######
debug=DBGLEVEL
log_stderror=no
log_facility=LOG_LOCAL0
children=8
auto_aliases=no
listen=udp:203.0.113.10:5060
listen=tcp:203.0.113.10:5060
alias="sip.example.com"
  • #!KAMAILIO marca el archivo con la sintaxis de Kamailio.28
  • El bloque #!ifdef WITH_DEBUG define el nivel de registro durante el preprocesamiento; si arranca Kamailio con ese símbolo definido, obtiene el nivel 3 sin editar el archivo.5
  • #!trydef DBURL define la URL de la base de datos solo si nada la definió antes, de modo que un archivo local incluido pueda reemplazarla.5 Cambie la contraseña.
  • DS_MEDIA "1" nombra el conjunto 1 del dispatcher para que la lógica de enrutamiento exprese una intención y no un número mágico.
  • Los números de las banderas siguen la configuración predeterminada: FLT_NATS es una bandera por mensaje ("esta solicitud vino de detrás de un NAT"), las banderas FLB_ son por rama y se guardan junto con el registro.28
  • children=8 es el número predeterminado de procesos de trabajo por socket UDP.5 listen enlaza UDP y TCP en la dirección pública. alias le dice a Kamailio que sip.example.com es él mismo, lo que hace que funcionen uri==myself y from_uri==myself.5

Parte 2: módulos y sus parámetros

####### Modules #######
loadmodule "db_mysql.so"
loadmodule "jsonrpcs.so"
loadmodule "kex.so"
loadmodule "corex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "ctl.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so"
loadmodule "dispatcher.so"
loadmodule "htable.so"
loadmodule "pike.so"

modparam("tm", "failure_reply_mode", 3)
modparam("tm", "fr_timer", 5000)
modparam("tm", "fr_inv_timer", 120000)

modparam("rr", "enable_full_lr", 0)
modparam("rr", "append_fromtag", 0)

modparam("registrar", "method_filtering", 1)
modparam("registrar", "max_expires", 3600)
modparam("usrloc", "db_url", DBURL)
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "nat_bflag", FLB_NATB)

modparam("auth_db", "db_url", DBURL)
modparam("auth_db", "calculate_ha1", yes)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "load_credentials", "")

modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)")
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping_bflag", FLB_NATSIPPING)
modparam("nathelper", "sipping_from", "sip:[email protected]")

modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")

modparam("dispatcher", "list_file", "/etc/kamailio/dispatcher.list")
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "ds_ping_interval", 15)
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "ds_ping_from", "sip:[email protected]")

modparam("pike", "sampling_time_unit", 2)
modparam("pike", "reqs_density_per_unit", 16)
modparam("pike", "remove_latency", 4)
modparam("htable", "htable", "ipban=>size=8;autoexpire=300;")
  • La lista de módulos es el conjunto básico de la configuración predeterminada más auth, nathelper, rtpengine, dispatcher, htable y pike.28
  • tm. failure_reply_mode 3 descarta automáticamente las ramas de un tramo previo de bifurcación en serie, como en la configuración predeterminada.28 fr_timer se reduce de los 30 segundos predeterminados a 5 segundos10 para abandonar rápido un servidor de medios caído; el ejemplo del dispatcher usa 2 segundos por la misma razón.17 Este temporizador solo se dispara cuando no hay ninguna respuesta, así que los teléfonos que están timbrando dependen de fr_inv_timer (120 s).10
  • usrloc. db_mode 2 es escritura diferida: los cambios van a memoria y se vuelcan a la base de datos con un temporizador, de modo que los registros sobreviven a un reinicio con poco costo de latencia.14 nat_bflag le dice a usrloc qué bandera de rama marca un contacto detrás de NAT.28
  • auth_db. calculate_ha1 yes con password_column "password" significa contraseñas en texto plano en la tabla de suscriptores, el ajuste de la configuración predeterminada.28 En producción, guarde mejor los hashes HA1.
  • received_avp. Debe ser idéntico para nathelper y registrar, porque fix_nated_register() guarda la dirección de origen real en ese AVP y save() la escribe en la columna received de la tabla de ubicación.19
  • Pings de nathelper. Cada 30 segundos Kamailio envía keepalives solo a los contactos detrás de NAT, como una solicitud SIP (OPTIONS por defecto) para los contactos que llevan la bandera de rama FLB_NATSIPPING en lugar de un paquete UDP vacío, para que la asignación del NAT siga abierta.1928
  • rtpengine_sock es el socket de control del demonio rtpengine; por defecto está desactivado.18
  • dispatcher. flags 2 activa la conmutación por falla: los demás destinos del conjunto se guardan en XAVP para ds_next_dst().17 ds_probing_mode 1 prueba todos los destinos, y un keepalive fallido hacia uno activo lo pasa al estado "trying".17 Las pruebas son SIP OPTIONS por defecto.17 ds_ping_interval debe ser distinto de cero o las pasarelas caídas nunca se vuelven a sondear (el valor predeterminado es 0).17
  • pike y htable. Copiados de los ajustes antiinundación predeterminados, cuyo comentario los describe como un bloqueo "if more than 16 requests in 2 seconds and ban for 300 seconds" (si hay más de 16 solicitudes en 2 segundos, bloquear por 300 segundos); los 300 segundos son el autoexpire del htable.28

Parte 3: la lista del dispatcher

# /etc/kamailio/dispatcher.list
# setid  destination            flags  priority  attributes
1        sip:192.0.2.21:5060    0      0         duid=media-a
1        sip:192.0.2.22:5060    0      0         duid=media-b

Cada línea es setid destination flags priority attributes (conjunto, destino, banderas, prioridad, atributos).17 Ambos servidores están en el conjunto 1, banderas 0 (activo), con la misma prioridad. El atributo duid es un identificador único que exige el algoritmo de carga de llamadas (10); con round-robin simplemente etiqueta el servidor.17 Después de editar el archivo, el comando RPC dispatcher.reload del módulo lo carga sin reiniciar.17

Parte 4: request_route

####### Routing logic #######
request_route {
    route(REQINIT);
    route(NATDETECT);

    if (is_method("CANCEL")) {
        if (t_check_trans()) {
            route(RELAY);
        }
        exit;
    }

    if (!is_method("ACK")) {
        if (t_precheck_trans()) {
            t_check_trans();
            exit;
        }
        t_check_trans();
    }

    route(WITHINDLG);

    # --- initial requests only from here on ---
    remove_hf("Route");
    if (is_method("INVITE|SUBSCRIBE")) {
        record_route();
    }

    # A media server is calling one of our registered phones.
    if (ds_is_from_list(DS_MEDIA)) {
        route(LOCATION);
    }

    route(AUTH);
    route(REGISTRAR);

    if ($rU == $null) {
        sl_send_reply("484", "Address Incomplete");
        exit;
    }

    # Every authenticated call goes to a media server.
    route(TOMEDIA);
}
  • route(REQINIT) ejecuta las verificaciones de sanidad, inundación y escáneres antes que nada.
  • route(NATDETECT) decide temprano si el remitente está detrás de un NAT, porque tanto el registro como el manejo de llamadas dependen de ello.
  • CANCEL solo se retransmite si coincide con una transacción conocida (t_check_trans()); luego el script termina.28
  • Retransmisiones. t_precheck_trans() detecta la retransmisión de una solicitud cuya transacción todavía está siendo creada por otro proceso; t_check_trans() absorbe las retransmisiones de transacciones existentes.28 Este bloque se copió textualmente de la configuración predeterminada por una buena razón: si se hace mal, cada INVITE retransmitido se convierte en una segunda llamada.
  • route(WITHINDLG) atiende las solicitudes que ya pertenecen a un diálogo (tienen etiqueta To) y termina; todo lo que sigue es una solicitud inicial.
  • remove_hf("Route") elimina las cabeceras Route que un cliente haya precargado, y record_route() mantiene a Kamailio en la ruta de señalización durante toda la llamada, de modo que el BYE regrese por él.2812
  • ds_is_from_list(DS_MEDIA) es verdadero cuando la IP y el puerto de origen coinciden con un destino del conjunto 1 (si solo se indica el grupo, el protocolo se ignora).17 Esas solicitudes son de confianza: omiten la autenticación y pasan directo a la búsqueda de ubicación. Esta es la decisión de diseño de todo el script: los teléfonos deben autenticarse; los servidores de medios son de confianza por su dirección. Nunca ponga en la lista del dispatcher una dirección que usted no controle.
  • Todo lo demás debe autenticarse (route(AUTH)). Luego REGISTER se atiende y termina dentro de route(REGISTRAR). Una solicitud sin parte de usuario recibe 484.28 Lo que queda es una llamada autenticada de un teléfono, y va a un servidor de medios.

Parte 5: las subrutas

route[REQINIT] {
    force_rport();

    if (src_ip != myself && !ds_is_from_list(DS_MEDIA)) {
        if ($sht(ipban=>$si) != $null) {
            exit;
        }
        if (!pike_check_req()) {
            xalert("pike blocking $rm from $fu (IP:$si:$sp)\n");
            $sht(ipban=>$si) = 1;
            exit;
        }
    }

    if ($ua =~ "friendly|scanner|sipcli|sipvicious|VaxSIPUserAgent|pplsip") {
        exit;
    }

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

    if (is_method("OPTIONS") && uri == myself && $rU == $null) {
        sl_send_reply("200", "Keepalive");
        exit;
    }

    if (!sanity_check("17895", "7")) {
        xlog("Malformed SIP request from $si:$sp\n");
        exit;
    }
}

route[AUTH] {
    if (from_uri != myself) {
        sl_send_reply("403", "Not relaying");
        exit;
    }
    if (!auth_check("$fd", "subscriber", "1")) {
        auth_challenge("$fd", "0");
        exit;
    }
    if (!is_method("REGISTER")) {
        consume_credentials();
    }
}

route[REGISTRAR] {
    if (!is_method("REGISTER")) return;

    if (isflagset(FLT_NATS)) {
        setbflag(FLB_NATB);
        setbflag(FLB_NATSIPPING);
    }
    if (!save("location")) {
        sl_reply_error();
    }
    exit;
}

route[LOCATION] {
    if (!lookup("location")) {
        $var(rc) = $rc;
        t_newtran();
        switch ($var(rc)) {
            case -1:
            case -3:
                send_reply("404", "Not Found");
                exit;
            case -2:
                send_reply("405", "Method Not Allowed");
                exit;
        }
    }
    route(RELAY);
    exit;
}

route[TOMEDIA] {
    # 4 = round-robin; with flags=2 the other server is kept for failover
    if (!ds_select_dst(DS_MEDIA, "4")) {
        send_reply("503", "No media server available");
        exit;
    }
    t_on_failure("MEDIA_FAILOVER");
    route(RELAY);
    exit;
}

route[RELAY] {
    if (is_method("INVITE|BYE|UPDATE")) {
        if (!t_is_set("branch_route")) t_on_branch("MANAGE_BRANCH");
    }
    if (is_method("INVITE|UPDATE")) {
        if (!t_is_set("onreply_route")) t_on_reply("MANAGE_REPLY");
    }
    if (is_method("INVITE")) {
        if (!t_is_set("failure_route")) t_on_failure("MANAGE_FAILURE");
    }
    if (!t_relay()) {
        sl_reply_error();
    }
    exit;
}

route[WITHINDLG] {
    if (!has_totag()) return;

    if (loose_route()) {
        if (!isdsturiset()) {
            handle_ruri_alias();
        }
        if (is_method("ACK")) {
            route(NATMANAGE);
        }
        route(RELAY);
        exit;
    }
    if (is_method("ACK")) {
        if (t_check_trans()) {
            route(RELAY);
        }
        exit;
    }
    sl_send_reply("404", "Not here");
    exit;
}

REQINIT. force_rport() hace que las respuestas vuelvan al puerto desde el que realmente llegó la solicitud, lo que necesita el NAT (RFC 3581).5 Pike solo se aplica a orígenes que no son el propio servidor ni los servidores de medios; la configuración predeterminada advierte que se debe "exclude checking trusted peers, such as pstn gateways" (excluir de la verificación a los pares de confianza, como las pasarelas PSTN).28 Una IP que ya está en la tabla ipban se descarta en silencio; un nuevo inundador se registra en el log y se bloquea. Los códigos de retorno de pike distinguen a un inundador conocido (-1) de uno nuevo (-2), y ante un error interno devuelve deliberadamente verdadero para no bloquear por error.20 Los agentes de usuario de escáneres se descartan sin respuesta. mf_process_maxfwd_header("10") detiene los bucles. Los keepalives OPTIONS dirigidos al propio servidor reciben un 200 sin estado. sanity_check("17895", "7") aplica el conjunto de verificaciones de sanidad RFC de la configuración predeterminada.2826

AUTH. La primera prueba rechaza toda solicitud cuyo dominio From no sea el nuestro: este servidor no es un relé abierto. Luego auth_check("$fd", "subscriber", "1") hace el desafío con el dominio de From como realm, busca al usuario en la tabla subscriber y, con la bandera 1, exige que el nombre de usuario autenticado coincida con el usuario de From (y, en REGISTER, de To), de modo que un teléfono no pueda hacer llamadas como otro.16 Si falla, auth_challenge() envía un 401 para REGISTER y un 407 para todo lo demás, y el script termina.15 consume_credentials() elimina las credenciales que usó este servidor antes de reenviar la solicitud, para no revelarlas al servidor de medios.15

REGISTRAR. Si NATDETECT marcó la solicitud, se activan la bandera de rama de NAT y la de SIP-ping antes de save("location"). El registrador guarda las banderas de rama junto con el contacto,13 de modo que en cada llamada posterior a ese teléfono Kamailio sabe que está detrás de un NAT sin tener que adivinarlo de nuevo. save() envía el 200 OK por sí mismo.13

LOCATION. lookup("location") reemplaza el Request-URI por el contacto registrado del teléfono. Si falla, se conserva el código de retorno, se crea una transacción para que la respuesta se envíe con estado, y el código se traduce a 404 o 405 como lo hace la documentación del registrador.1328

TOMEDIA. ds_select_dst(DS_MEDIA, "4") elige el siguiente servidor en round-robin y lo define como URI de destino ($du), sin cambiar el Request-URI, de modo que el servidor de medios sigue viendo qué número se marcó.17 Si todos los servidores están inactivos, quien llama recibe 503. En caso contrario se arma la ruta de conmutación por falla y se retransmite la solicitud.

RELAY. Arma las rutas de rama, de respuesta y de falla para los métodos que requieren manejo de NAT, salvo que algo ya las haya armado. Esa verificación con t_is_set() es la que permite que sobreviva el MEDIA_FAILOVER de TOMEDIA. Luego t_relay() reenvía con estado.2810

WITHINDLG. Una solicitud con etiqueta To (has_totag(), de siputils, que también ofrece is_first_hop(), is_request() e is_reply())27 pertenece a un diálogo existente. Las solicitudes dentro de un diálogo siguen el conjunto Route registrado al establecer la llamada (loose_route()). Si no se definió destino, handle_ruri_alias() recupera la dirección NAT real del teléfono a partir del parámetro ;alias= que set_contact_alias() agregó antes.19 Un ACK pasa por NATMANAGE para que rtpengine vea el SDP que pueda traer. Un ACK suelto sin transacción se descarta, y todo lo demás recibe 404.28

Parte 6: manejo de NAT, rtpengine y conmutación por falla

route[NATDETECT] {
    if (nat_uac_test("19")) {
        if (is_method("REGISTER")) {
            fix_nated_register();
        } else {
            if (is_first_hop()) {
                set_contact_alias();
            }
        }
        setflag(FLT_NATS);
    }
}

route[NATMANAGE] {
    if (is_request()) {
        if (has_totag()) {
            if (check_route_param("nat=yes")) {
                setbflag(FLB_NATB);
            }
        }
    }
    if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB))) return;

    if (nat_uac_test("8")) {
        rtpengine_manage("SIP-source-address replace-origin replace-session-connection");
    } else {
        rtpengine_manage("replace-origin replace-session-connection");
    }

    if (is_request()) {
        if (!has_totag()) {
            if (t_is_branch_route()) {
                add_rr_param(";nat=yes");
            }
        }
    }
    if (is_reply()) {
        if (isbflagset(FLB_NATB)) {
            if (is_first_hop()) {
                set_contact_alias();
            }
        }
    }
}

branch_route[MANAGE_BRANCH] {
    route(NATMANAGE);
}

onreply_route[MANAGE_REPLY] {
    if (status =~ "[12][0-9][0-9]") {
        route(NATMANAGE);
    }
}

failure_route[MANAGE_FAILURE] {
    route(NATMANAGE);
    if (t_is_canceled()) exit;
}

failure_route[MEDIA_FAILOVER] {
    route(NATMANAGE);
    if (t_is_canceled()) exit;

    # next media server only on a 500/503 or a local timeout
    if (t_check_status("500|503")
            || (t_branch_timeout() && !t_branch_replied())) {
        ds_mark_dst("ip");
        if (ds_next_dst()) {
            t_on_failure("MEDIA_FAILOVER");
            route(RELAY);
            exit;
        }
    }
}

NATDETECT. nat_uac_test("19") equivale a las banderas 1 + 2 + 16: una dirección privada (RFC 1918 o RFC 6598) en el Contact, una dirección Via distinta del origen del paquete, o un puerto de origen distinto del puerto de Via.19 En un REGISTER, fix_nated_register() registra el origen verdadero para que el registrador lo guarde.19 En las demás solicitudes del primer salto, set_contact_alias() agrega ;alias=ip~port~transport al Contact sin reescribir el búfer del mensaje.19

NATMANAGE. Es el bloque de rtpengine de la configuración predeterminada.28 Para solicitudes dentro de un diálogo, el parámetro de Record-Route nat=yes (agregado en la primera rama) restablece la bandera de NAT. Si ninguna de las partes está detrás de NAT, retorna y los medios fluyen directamente. En caso contrario, rtpengine_manage() hace lo que corresponde según el mensaje: una oferta para un INVITE con SDP, una respuesta para la respuesta 1xx/2xx o un ACK con SDP, y una eliminación en BYE, CANCEL, una respuesta final de falla o en una ruta de falla.18 replace-origin y replace-session-connection reescriben las líneas SDP o= y c= a nivel de sesión con la dirección de rtpengine.18 Cuando el propio SDP contiene una dirección privada (nat_uac_test("8")), SIP-source-address le indica a rtpengine que ignore la dirección del SDP y use el origen de la señalización.1918 t_is_branch_route() viene de tmx.38

Las rutas de respuesta y de falla. La ruta de rama ejecuta NATMANAGE en cada rama de salida; la ruta de respuesta lo ejecuta en las respuestas provisionales y de éxito, que traen la respuesta SDP; MANAGE_FAILURE lo ejecuta para que la sesión de rtpengine se elimine cuando una llamada falla.28 Las tres vienen de la configuración predeterminada.

MEDIA_FAILOVER. Basada en el ejemplo del dispatcher.17 Si quien llamó colgó, se detiene. Si el servidor respondió 500 o 503, o no respondió en absoluto (t_branch_timeout() sin t_branch_replied()), lo marca como inactivo y en sondeo con ds_mark_dst("ip"), de modo que sale de la rotación hasta que una prueba tenga éxito,17 y luego toma el siguiente servidor de la lista XAVP con ds_next_dst(), vuelve a armar la misma ruta de falla y retransmite de nuevo.17 Un 486 (ocupado) o un 404 del servidor de medios es una respuesta real y vuelve al teléfono.

Lo que este ejemplo deja fuera

  • TLS. Agregue el módulo tls y listen=tls:...; en producción, registre los teléfonos por TLS en el puerto 5061.
  • Retransmitir siempre los medios. Si los servidores de medios están en una red privada a la que los teléfonos no llegan, rtpengine debe retransmitir todas las llamadas, no solo las que están detrás de NAT. La opción WITH_RTPRELAY de la configuración predeterminada elimina el retorno anticipado de NATMANAGE justamente para este caso.28
  • Ocultamiento de topología y límites de llamadas. Cargue topos para ocultar direcciones internas y dialog para limitar las llamadas simultáneas por cuenta.2423
  • El lado del servidor de medios. Asterisk o FreeSWITCH debe aceptar SIP sin autenticar solo desde la dirección de Kamailio, y enviar las llamadas destinadas a teléfonos a sip:<extensión>@203.0.113.10. Esa configuración queda fuera de esta página.

Antes de reiniciar, kamailio -c -f /etc/kamailio/kamailio.cfg revisa el archivo en busca de errores de sintaxis.5 Una vez en marcha, el comando RPC dispatcher.list muestra el estado de ambos servidores de medios.17

Kamailio y OpenSIPS: orígenes comunes, caminos distintos

OpenSIPS y Kamailio comparten una base de código hasta 2008, y sus lenguajes de configuración y muchos nombres de módulos todavía se parecen. Cómo se separaron depende de a quién se le pregunte. La página de historia de Kamailio dice que, tras el cambio de nombre, quien había registrado el dominio openser.org "created a fork named opensips shortly after OpenSER to Kamailio renaming and few days later redirected openser.org to his new project" (creó una bifurcación llamada opensips poco después del cambio de nombre de OpenSER a Kamailio y pocos días después redirigió openser.org a su nuevo proyecto).2 OpenSIPS se describe a sí mismo como "started as a fork of Fokus Fraunhofer SIP Express Router (SER) project" (nacido como una bifurcación del proyecto SIP Express Router de Fokus Fraunhofer) y dice que "wants to be a more open project, not only from license point of view, but more open as project management, especially for external contributions" (quiere ser un proyecto más abierto, no solo por la licencia, sino también en la gestión del proyecto, en especial para las contribuciones externas).34 Su primera serie de versiones, la 1.4.x, salió el 4 de agosto de 2008,36 una semana después del cambio de nombre a Kamailio.2 No vamos a arbitrar una disputa de 2008; para quien compra en 2026, lo que importa es cómo se diferencian los proyectos hoy.

KamailioOpenSIPS
LicenciaGPLv2 como licencia principal, algunas partes BSD3GPL34
Gobernanza"developed and managed by its world wide community" (desarrollado y gestionado por su comunidad mundial)3"maintained by OpenSIPS Solutions" (mantenido por OpenSIPS Solutions) junto con desarrolladores originales de SER34
ScriptingDSL nativo, o KEMI en Lua, Python, JavaScript, Ruby, Squirrel7Un lenguaje de scripting propio, similar a C35
Estable actual6.1 (6.1.5, 15 oct 2026) y 6.0 (6.0.8)31324.0 y 3.6 LTS36
Fechas de soporte publicadasNo encontramos ninguna4.0 con soporte hasta dic 2027; 3.6 LTS hasta el 21 may 202836
Herramientas que destacaControl por RPC (kamcmd, kamctl), replicación DMQ para htable622Generador de scripts menuconfig, interfaces MI y de eventos, el panel web OpenSIPS-CP35
Papeles que enumeraProxy, registrador, ubicación, redirección, servidor de aplicaciones4Los mismos, más "SIP back-to-back User Agent" entre sus funciones35

Desplace la tabla hacia los lados para ver todas las columnas.

OpenSIPS publica una fecha de soporte para cada rama y etiqueta las versiones de soporte a largo plazo; su página de versiones muestra la 3.6 (publicada el 23 de julio de 2025) como LTS con soporte hasta mayo de 2028, y la 4.1 en desarrollo con una beta prevista para mayo de 2027.36 Si su organización necesita un horizonte de soporte por escrito para aprobar una plataforma, esa es una ventaja concreta.

Nuestra opinión: cualquiera de los dos sirve para lo que explica esta guía; la configuración de arriba se podría llevar a OpenSIPS con cambios a nivel de módulos, no con un rediseño. Elija Kamailio si su equipo quiere escribir la lógica de enrutamiento en Python o Lua y recargarla en vivo, o si valora un proyecto gobernado por la comunidad. Elija OpenSIPS si le importan más las fechas LTS publicadas, un B2BUA integrado o su panel de control web. En cualquier caso, el factor decisivo suele ser la gente: qué documentación, qué lista de correo y qué consultores conoce ya su equipo.

Cuándo Kamailio es la herramienta adecuada y cuándo no

Kamailio se justifica cuando hay escala, redundancia o exposición: más teléfonos de los que una sola PBX debería llevar, más de un servidor de medios, varios operadores, clientes WebRTC, o un puerto SIP público que lo escanean las 24 horas. En todos esos casos, una capa de señalización dedicada que pueda registrar, autenticar, balancear y bloquear a miles de establecimientos de llamada por segundo1 vale la máquina adicional.

No es la herramienta adecuada cuando el problema son las funciones de llamada. Una operadora automática (auto attendant), un buzón de voz, la música en espera, una cola: son trabajos de servidor de medios, y Kamailio no tiene ruta de medios propia.18 Un negocio de una sola sede con una PBX y unos pocos teléfonos gana poco con poner un Kamailio delante, y a cambio suma otro sistema que parchear, monitorear y entender a las 2 de la mañana.

Y es un entorno de programación, no un producto. No trae pantalla de administración, el lenguaje nativo no perdona, y los valores predeterminados suponen que usted leyó la documentación de los módulos. Presupueste un ingeniero que sepa leer una traza SIP. Los equipos que no lo tienen suelen estar mejor atendidos con una plataforma alojada o una PBX empaquetada, y no hay nada de qué avergonzarse.

Lecturas recomendadas

Artículos de investigación más largos sobre este tema (en inglés), cada uno con sus fuentes y un PDF descargable.

Fuentes

Cada afirmación sobre normas o precios en esta página enlaza a su fuente primaria, a menudo en inglés. Las normas y los precios cambian: verifique antes de confiar en una cifra.

  1. Proyecto Kamailio, home page: project description, roles, development timeline and latest release (6.1.5, 15 Oct 2026). Consultado el 26 de octubre de 2026
    https://www.kamailio.org/w/
  2. Proyecto Kamailio, "History": SER at Fraunhofer FOKUS, the 2005 OpenSER fork, the 2008 rename and the SER merger, and its account of the OpenSIPS fork. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/w/history/
  3. Repositorio kamailio/kamailio en GitHub, README: "SIP Signaling Server", project history, licence rules for new contributions. Consultado el 26 de octubre de 2026
    https://github.com/kamailio/kamailio
  4. Proyecto Kamailio, "Features": server roles, transports, database back ends, and the project's throughput and subscriber-capacity claims. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/w/features/
  5. Wiki de Kamailio, Core Cookbook 6.1.x: configuration file structure, routing blocks, preprocessor directives, children/tcp_children, shm_mem_size, force_rport, command-line options. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/wikidocs/cookbooks/6.1.x/core/
  6. Wiki de Kamailio, "Troubleshooting Memory": private (pkg) and shared (shm) memory, default sizes, -M/-m options, memory statistics commands. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/wikidocs/tutorials/troubleshooting/memory/
  7. Kamailio, documentación del marco KEMI, introduction and overview: native DSL limits, no reload, KEMI added in v5.0.0, supported languages and modules. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/tutorials/devel/kamailio-kemi-framework/
  8. Kamailio, documentación del marco KEMI, "KEMI Interpreters": cfgengine identifiers, ksr_request_route and other callback names, Lua and Python examples. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/tutorials/devel/kamailio-kemi-framework/kemi/
  9. Documentación de módulos de Kamailio (6.1.x), app_lua: reload parameter and app_lua.reload RPC command. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/app_lua.html
  10. Documentación de módulos de Kamailio (6.1.x), tm: stateful transaction processing, fr_timer and fr_inv_timer defaults, t_relay and failure-route functions. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/tm.html
  11. Documentación de módulos de Kamailio (6.1.x), sl: stateless replies. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/sl.html
  12. Documentación de módulos de Kamailio (6.1.x), rr: record routing; "basically only a transaction stateful proxy". Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/rr.html
  13. Documentación de módulos de Kamailio (6.1.x), registrar: save() and lookup() behaviour and return codes, Path support. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/registrar.html
  14. Documentación de módulos de Kamailio (6.1.x), usrloc: location table and the five db_mode values. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/usrloc.html
  15. Documentación de módulos de Kamailio (6.1.x), auth: generic authentication functions. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/auth.html
  16. Documentación de módulos de Kamailio (6.1.x), auth_db: database authentication and auth_check() flags. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/auth_db.html
  17. Documentación de módulos de Kamailio (6.1.x), dispatcher: algorithms, flags, probing parameters, list file format, ds_select_dst, ds_next_dst, ds_mark_dst, ds_is_from_list, sample configuration. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/dispatcher.html
  18. Documentación de módulos de Kamailio (6.1.x), rtpengine: rtpengine_sock, rtpengine_manage() behaviour, SDP and WebRTC flags. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/rtpengine.html
  19. Documentación de módulos de Kamailio (6.1.x), nathelper: nat_uac_test flags, fix_nated_register, set_contact_alias, handle_ruri_alias. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/nathelper.html
  20. Documentación de módulos de Kamailio (6.1.x), pike: flood detection, parameters and pike_check_req() return codes. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/pike.html
  21. Documentación de módulos de Kamailio (6.1.x), secfilter: allow/block lists and SQL-injection checks. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/secfilter.html
  22. Documentación de módulos de Kamailio (6.1.x), htable: shared-memory hash tables, expiry, DMQ replication. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/htable.html
  23. Documentación de módulos de Kamailio (6.1.x), dialog: dialog awareness versus transaction state. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/dialog.html
  24. Documentación de módulos de Kamailio (6.1.x), topos: topology hiding and storage back ends. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/topos.html
  25. Documentación de módulos de Kamailio (6.1.x), websocket: RFC 6455 server, handshake via xhttp, SIP routing for WebSocket clients. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/websocket.html
  26. Documentación de módulos de Kamailio (6.1.x), sanity: RFC sanity checks on incoming requests. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/sanity.html
  27. Documentación de módulos de Kamailio (6.1.x), siputils: has_totag, is_request, is_reply, is_first_hop. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/siputils.html
  28. Repositorio kamailio/kamailio, etc/kamailio.cfg on branch 6.1: the default configuration script the worked example is derived from. Consultado el 26 de octubre de 2026
    https://github.com/kamailio/kamailio/blob/6.1/etc/kamailio.cfg
  29. Proyecto Kamailio, "Kamailio v6.0.0 Release Notes" (29 January 2025). 29 de enero de 2025
    https://www.kamailio.org/w/kamailio-v6-0-0-release-notes/
  30. Proyecto Kamailio, "Kamailio v6.1.0 Release Notes" (18 February 2026): new modules and features. 18 de febrero de 2026
    https://www.kamailio.org/w/kamailio-v6-1-0-release-notes/
  31. Proyecto Kamailio, "Kamailio v6.1.5 Released". 15 de octubre de 2026
    https://www.kamailio.org/w/2026/10/kamailio-v6-1-5-released/
  32. Proyecto Kamailio, "Kamailio v6.0.8 Released". 16 de septiembre de 2026
    https://www.kamailio.org/w/2026/09/kamailio-v6-0-8-released/
  33. Repositorio kamailio/kamailio, GitHub releases list with publication dates (6.0.2 to 6.1.5, including 5.8.8). Leído mediante la API de GitHub, 26 de octubre de 2026
    https://github.com/kamailio/kamailio/releases
  34. Repositorio OpenSIPS/opensips en GitHub, README: origin as a SER fork, project aims, maintainer. Consultado el 26 de octubre de 2026
    https://github.com/OpenSIPS/opensips
  35. Proyecto OpenSIPS, "About": scripting language, menuconfig, MI and Event interfaces, OpenSIPS-CP, feature list. Consultado el 26 de octubre de 2026
    https://opensips.org/About/About
  36. Proyecto OpenSIPS, "Available Versions": supported releases, LTS labels, release and supported-until dates, 1.4.x release date. Consultado el 26 de octubre de 2026
    https://opensips.org/About/AvailableVersions
  37. IETF RFC 5737, "IPv4 Address Blocks Reserved for Documentation" (TEST-NET-1 and TEST-NET-3). enero de 2010
    https://www.rfc-editor.org/rfc/rfc5737
  38. Documentación de módulos de Kamailio (6.1.x), tmx: t_is_branch_route() and other transaction helpers. Consultado el 26 de octubre de 2026
    https://www.kamailio.org/docs/modules/6.1.x/modules/tmx.html