Enviado por admin el
WebRTC (Web Real-Time Communication) representa una auténtica revolución en las comunicaciones en tiempo real a través de navegadores web. Esta tecnología permite la comunicación directa entre navegadores sin necesidad de plugins o software adicional, convirtiéndose en un estándar fundamental para aplicaciones VoIP modernas.
En este artículo profundizaremos en la configuración de Kamailio para procesar correctamente señalización SIP sobre WebSocket, permitiendo establecer llamadas entre diferentes tipos de endpoints: UDP tradicional, TLS y WebSocket Secure (WSS).
¿Qué es WebRTC?
WebRTC proporciona tres APIs principales en JavaScript que permiten a los desarrolladores crear aplicaciones de comunicación interactivas:
APIs Fundamentales
- getUserMedia: Solicita acceso a dispositivos de audio/vídeo presentes en computadores, tablets y smartphones
- RTCPeerConnection: Establece un flujo de media directo (peer-to-peer) entre los interlocutores
- RTCDataChannel: Permite el intercambio de datos fuera del flujo de media (mensajes de texto, fotos, archivos)
Protocolos y Codecs
El flujo de media en WebRTC utiliza:
- SRTP sobre DTLS: Protocolo cifrado basado en TLS específicamente diseñado para datagramas UDP
- Codecs de audio: alaw, ulaw y Opus
- Codecs de vídeo: VP8 y H.264 (según RFC7742)
- ICE (RFC8445): Protocolo para atravesar NAT con 99% de efectividad
Señalización Flexible
WebRTC no impone un protocolo de señalización específico. Puede utilizarse cualquier protocolo, siendo SIP y XMPP los más populares. La RFC7118 (enero 2014) estandariza el uso de WebSocket como transporte para SIP, permitiendo la integración con infraestructuras VoIP existentes.
La configuración que implementaremos permitirá los siguientes escenarios de llamadas:
UDP ←→ UDP
UDP ←→ WSS
WSS ←→ UDP
WSS ←→ WSS
Opcionalmente, también se puede configurar para:
TLS ←→ TLS
TLS ←→ WSS
WSS ←→ TLS
WSS ←→ WSS
La construcción del enrutamiento se basa en principios fundamentales de:
- Detección de protocolo: Identificación correcta del protocolo de transporte (UDP, TLS, WSS)
- Bridge/Puente de protocolos: Conversión transparente entre diferentes transportes
- Gestión de NAT: Manejo específico para clientes WebRTC tras NAT
- Manipulación de media: Configuración dinámica de RTPEngine según el escenario
route[PUENTE] {
if (!has_totag()) {
if ($proto =~ "wss" && !($ru =~ "transport=wss")) {
xlog("L_INFO","Puente WSS-UDP branch=$bf proto=$proto");
setbflag(FLB_PUENTE);
} else if (!($proto =~ "wss") && $ru =~ "transport=wss") {
xlog("L_INFO","Puente UDP-WSS branch=$bf proto=$proto");
setbflag(FLB_PUENTE);
}
}
}
Este bloque utiliza:
- Verificación de
has_totag()para detectar diálogos iniciales - Pattern matching con expresiones regulares para detectar protocolo de origen y destino
- Branch flags (bflags) para marcar transacciones que requieren conversión
- Logging detallado para troubleshooting
Ventaja: Permite decisiones de enrutamiento contextuales sin requerir verificaciones repetidas en cada punto del flujo.
#!ifdef WITH_WEBSOCKET
if (nat_uac_test(64)) {
if (is_method("REGISTER")) {
fix_nated_register();
} else if (!add_contact_alias()) {
xlog("L_ERR", "Error aliasing contact <$ct>\n");
sl_send_reply("400", "Bad Request");
exit;
}
}
#!endif
Implementación que:
- Usa
nat_uac_test(64)específicamente para detectar WebSocket - Aplica correcciones diferenciadas según el método SIP
- Maneja errores apropiadamente con respuestas SIP estándar
- Utiliza contact aliasing para tracking de conexiones WebSocket
Punto clave: El flag 64 es específico para WebSocket, diferenciándolo de otras detecciones NAT tradicionales.
Este es el corazón de la solución y merece un análisis detallado:
route[RTPENGINE] {
if(!is_method("INVITE")) return;
$xavp(r=>$T_branch_idx) = "replace-origin replace-session-connection";
if(nat_uac_test("8")) {
$xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + " trust-address";
}
# Gestión de via-branch para tracking
if (is_request()) {
if (!has_totag()) {
if (!t_is_failure_route()) {
$avp(extra_id) = @via[1].branch + $T_branch_idx;
$xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + " via-branch=extra";
}
}
}
if (is_reply()) {
$avp(extra_id) = @via[2].branch + $T_branch_idx;
$xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + " via-branch=extra";
}
# Configuración específica de conversión
if (isbflagset(FLB_PUENTE)) {
if ($proto =~ "wss") {
# De WSS a SIP (UDP/TCP)
$xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) +
" rtcp-mux-demux DTLS=off SDES-off ICE=remove RTP/AVP";
} else {
# De SIP a WSS
$xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) +
" rtcp-mux-offer generate-mid DTLS=passive SDES-off ICE=force RTP/SAVPF";
}
} else {
if ($proto =~ "ws") {
# WSS a WSS
$xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) +
" generate-mid DTLS=passive SDES-off ICE=force";
}
}
xlog("L_INFO", "NATMANAGE branch_id:$T_branch_idx ruri: $ru, metodo:$rm,
CseQ:$cs status:$rs, extra_id: $avp(extra_id),
rtpengine_manage: $xavp(r=>$T_branch_idx)\n");
rtpengine_manage($xavp(r=>$T_branch_idx));
}
a) Uso de XAVP para Branch-Level Configuration
- Cada rama de transacción (
$T_branch_idx) tiene su propia configuración - Ventaja: Permite forking con diferentes configuraciones RTPEngine por branch
- Caso de uso: Usuario registrado en múltiples dispositivos (UDP + WSS simultáneamente)
b) Parámetros de Conversión WSS→UDP
rtcp-mux-demux DTLS=off SDES-off ICE=remove RTP/AVP
| Parámetro | Función | Justificación |
|---|---|---|
rtcp-mux-demux |
Desmultiplexa RTP y RTCP | WebRTC usa un solo puerto, SIP tradicional espera puertos separados |
DTLS=off |
Desactiva DTLS | El endpoint UDP no soporta cifrado DTLS |
SDES-off |
Desactiva SDES | No se usa SDES para key exchange en esta dirección |
ICE=remove |
Elimina candidates ICE | El endpoint UDP no procesa ICE |
RTP/AVP |
Perfil RTP básico | Sin cifrado, compatible con dispositivos legacy |
c) Parámetros de Conversión UDP→WSS
rtcp-mux-offer generate-mid DTLS=passive SDES-off ICE=force RTP/SAVPF
| Parámetro | Función | Justificación |
|---|---|---|
rtcp-mux-offer |
Ofrece multiplexado RTP/RTCP | Requerido por WebRTC |
generate-mid |
Genera media identification | Necesario para BUNDLE |
DTLS=passive |
RTPEngine actúa como servidor DTLS | Espera handshake del cliente |
SDES-off |
Desactiva SDES | WebRTC usa DTLS-SRTP |
ICE=force |
Fuerza generación de candidates | Obligatorio para WebRTC |
RTP/SAVPF |
Secure Audio/Video Profile with Feedback | Estándar WebRTC con cifrado |
d) Via-Branch Tracking
$avp(extra_id) = @via[1].branch + $T_branch_idx;
Propósito: Crear identificadores únicos para sesiones RTPEngine que persisten a través de re-INVITEs y actualizaciones. Crucial para:
- Call hold/resume
- Codec renegotiation
- Session updates
Configuración Paso a Paso
1. Prerequisitos
Los certificados TLS/DTLS deben estar creados previamente (reutilizados de configuración TLS de Kamailio).
2. Activación de Módulos
Agregar en kamailio.cfg:
#!define WITH_WEBSOCKET
Cargar módulos necesarios:
#!ifdef WITH_WEBSOCKET
loadmodule "xhttp.so"
loadmodule "websocket.so"
#!endif
Módulos y sus funciones:
- xhttp: Servidor web básico para manejar peticiones HTTP
- websocket: Servidor WebSocket para señalización SIP
3. Configuración de Listeners
listen=tls:IP_PUBLICA:8443
Nota: Se usa puerto 8443 por convención (puede personalizarse).
4. Parámetros Globales TCP
#!ifdef WITH_WEBSOCKET
tcp_accept_no_cl=yes
http_reply_parse=yes
#!endif
Explicación:
tcp_accept_no_cl=yes: Acepta requests sin headerContent-Lengthhttp_reply_parse=yes: Permite parsear respuestas HTTP (necesario para WebSocket handshake)
5. Event Routes para WebSocket
Gestión de Conexiones (event_route[xhttp:request])
event_route[xhttp:request] {
set_reply_close();
set_reply_no_connect();
# Validar puerto
if ($Rp != 8443) {
xlog("L_WARN", "HTTP request received on $Rp\n");
xhttp_reply("403", "Forbidden", "", "");
exit;
}
xlog("L_DBG", "HTTP Request Received $hu\n");
# Validar WebSocket upgrade
if ($hdr(Upgrade)=~"websocket"
&& $hdr(Connection)=~"Upgrade"
&& $rm=~"GET") {
# Validar Host
if ($hdr(Host) == $null || !is_myself("sip:" + $hdr(Host))) {
xlog("L_WARN", "Bad host $hdr(Host)\n");
xhttp_reply("403", "Forbidden", "", "");
exit;
}
# Ejecutar handshake
if (ws_handle_handshake()) {
exit;
}
}
xhttp_reply("404", "Not found", "", "");
}
Análisis:
- Implementa security best practices
- Valida upgrade headers según RFC6455
- Verifica que el Host es válido
Logging de Cierre (event_route[websocket:closed])
event_route[websocket:closed] {
xlog("L_INFO", "WebSocket connection with id $ws_conid from $si:$sp has closed\n");
}
Utilidad: Debugging y monitoreo de conexiones activas.
6. Gestión de Respuestas
#!ifdef WITH_WEBSOCKET
onreply_route {
if (nat_uac_test(64)) {
add_contact_alias();
}
}
#!endif
Función: Añade alias de contacto en respuestas para mantener routing correcto.
7. Firewall (IPTables)
-A INPUT -p tcp -m state --state NEW -m tcp --dport 8443 -j ACCEPT
Seguridad Avanzada: CORS (Cross-Origin Resource Sharing) y Origin Validation
Para restringir acceso solo a dominios autorizados:
if ($hdr(Origin) != "https://campus.miodominio.org") {
xlog("L_WARN", "Unauthorised client $hdr(Origin)\n");
xhttp_reply("403", "Forbidden", "", "");
exit;
}
Consideraciones:
- Implementa CORS a nivel SIP
- Previene uso no autorizado del servidor
- Lista blanca de dominios permitidos
Testing y Validación
1. Verificación de Configuración
kamailio -c /etc/kamailio/kamailio.cfg
Salida esperada:
Listening on
udp: IP_PUBLICA [IP_PUBLICA]:5060
udp: IP_PRIVADA [IP_PRIVADA]:5060
tls: IP_PUBLICA [IP_PUBLICA]:8443
Aliases:
config file ok, exiting...
2. Creación de Usuario WebRTC
kamctl add 1003@sip1.miodominio.org PassWord3. Cliente WebRTC Recomendado
Sugiero usar Browser-Phone, un softphone WebRTC amigable.
Configuración típica:
- WebSocket URI:
wss://sip1.miodominio.org:8443 - Usuario:
1003 - Password: PassWord
- Display Name:
Usuario WebRTC
4. Escenarios de Prueba
| Origen | Destino | Resultado Esperado |
|---|---|---|
| WSS (1003) | UDP (1001) | Conversión WSS→UDP, audio bidireccional |
| UDP (1001) | WSS (1003) | Conversión UDP→WSS, video si disponible |
| WSS (1003) | WSS (1004) | Comunicación nativa WebRTC, máxima calidad |
| UDP (1001) | UDP (1002) | Sin conversión, routing tradicional |
5. Troubleshooting
Llamadas no se establecen
Verificar:
- RTPEngine está corriendo:
systemctl status rtpengine - Puertos UDP abiertos:
10000-20000/udppara RTP - Logs de Kamailio:
kamlog | grep -i error - SDP en INVITE:
sngreppara captura de paquetes
Audio unidireccional
Causas comunes:
- Configuración incorrecta de
rtcp-mux-demux - ICE candidates no alcanzan el endpoint
- Firewall bloqueando RTP
Solución:
# Verificar flags de RTPEngine
kamcmd rtpengine.show all
Configuraciones Alternativas
Solo TLS + WebRTC
Para entornos que requieren cifrado end-to-end:
TLS ←→ TLS
TLS ←→ WSS
WSS ←→ TLS
WSS ←→ WSS
Modificación en route[PUENTE]:
route[PUENTE] {
if (!has_totag()) {
# Detectar cualquier combinación de protocolos seguros
if (($proto =~ "wss" || $proto =~ "tls") &&
!($ru =~ "transport=wss" || $ru =~ "transport=TLS")) {
xlog("L_INFO","Puente seguro $proto a estándar");
setbflag(FLB_PUENTE);
} else if (!($proto =~ "wss" || $proto =~ "tls") &&
($ru =~ "transport=wss" || $ru =~ "transport=TLS")) {
xlog("L_INFO","Puente estándar a seguro");
setbflag(FLB_PUENTE);
}
}
}
Monitoreo y Métricas
Comandos Útiles
# Ver conexiones WebSocket activaskamcmd ws.dump# Estadísticas de RTPEngine kamcmd rtpengine.show all # Dialogos activoskamcmd dlg.list
Métricas Clave
- Conexiones WebSocket activas: Debe correlacionar con registros activos
- Sesiones RTPEngine: Una por llamada activa
- Branch failures: Idealmente < 1% de llamadas totales
- Latencia de handshake: < 500ms para buena experiencia
Próximos Pasos
- Implementar Rate Limiting para prevenir ataques
- Configurar Load Balancing de RTPEngine
- Integrar con sistema de Billing
- Implementar QoS monitoring
Recursos Adicionales
- RFC7118: WebSocket como transporte para SIP
- RFC8445: ICE para NAT Traversal
- Documentación Kamailio: https://www.kamailio.org/wikidocs/
- RTPEngine GitHub: https://github.com/sipwise/rtpengine
Tags: #Kamailio #WebRTC #VoIP #SIP #RTPEngine #RealTimeCommunications #TelecomEngineering
Comentarios recientes