El Módulo TM de Kamailio - Gestión de Transacciones

El módulo TM (Transaction Manager) es uno de los componentes más críticos de Kamailio. Se encarga de la gestión completa de transacciones SIP, proporcionando funcionalidades esenciales como el reenvío de mensajes, manejo de respuestas, timeouts, y la implementación de características avanzadas como el forking paralelo y secuencial.

Configuración Básica del Módulo

Carga del Módulo

loadmodule "tm.so"

Parámetros Principales

# Tiempo de espera para transacciones (en segundos)
modparam("tm", "fr_timer", 30)

# Tiempo de espera para respuestas INVITE (en segundos)
modparam("tm", "fr_inv_timer", 120)

# Habilitar/deshabilitar el reenvío automático de ACK
modparam("tm", "auto_inv_100", 1)

# Tamaño del hash table para transacciones
modparam("tm", "hash_size", 2048)

# Número máximo de ramas (forking)
modparam("tm", "max_branches", 10)

Funciones Principales del Módulo TM

1. t_relay()

La función más utilizada del módulo TM. Reenvía el mensaje actual creando una nueva transacción.

route[REENVIO_BASICO] {
    xlog("L_INFO", "Reenviando mensaje $rm desde $si:$sp\n");
    
    if (!t_relay()) {
        xlog("L_ERR", "Error al reenviar mensaje\n");
        sl_reply_error();
        exit;
    }
    
    xlog("L_INFO", "Mensaje reenviado exitosamente\n");
    exit;
}

2. t_relay_to_udp() y t_relay_to_tcp()

Funciones para reenviar mensajes especificando el protocolo de transporte.

route[REENVIO_PROTOCOLO] {
    if ($ru =~ "transport=tcp") {
        xlog("L_INFO", "Forzando reenvío por TCP a $ru\n");
        if (!t_relay_to_tcp()) {
            xlog("L_ERR", "Fallo en reenvío TCP\n");
            t_reply("500", "Error interno del servidor");
        }
    } else {
        xlog("L_INFO", "Enviando por UDP a $ru\n");
        if (!t_relay_to_udp()) {
            xlog("L_ERR", "Fallo en reenvío UDP\n");
            t_reply("500", "Error interno del servidor");
        }
    }
    exit;
}

3. t_reply()

Genera respuestas SIP desde el servidor.

route[RESPUESTA_PERSONALIZADA] {
    if ($rU == "test") {
        xlog("L_INFO", "Respondiendo a solicitud de prueba\n");
        t_reply("200", "Servicio de prueba activo");
        exit;
    }
    
    if (!lookup("location")) {
        xlog("L_INFO", "Usuario $rU no encontrado en registro\n");
        t_reply("404", "Usuario no disponible");
        exit;
    }
}

4. t_check_trans()

Verifica si el mensaje actual pertenece a una transacción existente.

request_route {
    # Verificar transacciones existentes
    if (t_check_trans()) {
        xlog("L_INFO", "Mensaje pertenece a transacción existente\n");
        exit;
    }
    
    xlog("L_INFO", "Nuevo mensaje: $rm desde $si\n");
    
    # Continuar procesamiento para nuevos mensajes...
}

5. t_newtran()

Crea una nueva transacción manualmente.

route[CREAR_TRANSACCION] {
    if (!t_newtran()) {
        xlog("L_ERR", "Error al crear nueva transacción\n");
        sl_reply_error();
        exit;
    }
    
    xlog("L_INFO", "Nueva transacción creada para $rm\n");
    
    # Procesar la transacción...
}

6. t_cancel_branch()

Cancela una rama específica en caso de forking.

failure_route[CANCELAR_RAMA] {
    xlog("L_INFO", "Procesando fallo en failure_route\n");
    
    if (t_was_cancelled()) {
        xlog("L_INFO", "Transacción ya cancelada\n");
        exit;
    }
    
    if (t_check_status("408")) {
        xlog("L_INFO", "Timeout detectado, cancelando rama\n");
        t_cancel_branch();
    }
}

Configuración de Forking

Forking Paralelo

route[FORKING_PARALELO] {
    if (!lookup("location")) {
        t_reply("404", "Usuario no encontrado");
        exit;
    }
    
    # Si hay múltiples contactos, se envía a todos simultáneamente
    xlog("L_INFO", "Iniciando forking paralelo para $rU\n");
    
    t_on_failure("MANEJO_FALLO_FORKING");
    
    if (!t_relay()) {
        xlog("L_ERR", "Error en forking paralelo\n");
        t_reply("500", "Error interno");
    }
    exit;
}

failure_route[MANEJO_FALLO_FORKING] {
    xlog("L_INFO", "Respuesta de fallo: $T_reply_code $T_reply_reason\n");
    
    if (t_check_status("486|600")) {
        xlog("L_INFO", "Usuario ocupado o rechaza llamada\n");
        # Continuar con próxima rama si existe
        exit;
    }
}

Forking Secuencial

route[FORKING_SECUENCIAL] {
    # Cargar contactos ordenados por prioridad
    if (!lookup("location")) {
        t_reply("404", "No registrado");
        exit;
    }
    
    # Configurar para forking secuencial
    $avp(contador_intentos) = 0;
    
    t_on_failure("SIGUIENTE_CONTACTO");
    
    xlog("L_INFO", "Intentando primer contacto para $rU\n");
    t_relay();
    exit;
}

failure_route[SIGUIENTE_CONTACTO] {
    $avp(contador_intentos) = $avp(contador_intentos) + 1;
    
    xlog("L_INFO", "Intento $avp(contador_intentos) falló: $T_reply_code\n");
    
    if ($avp(contador_intentos) < 3 && t_check_status("408|486|603")) {
        xlog("L_INFO", "Probando siguiente contacto\n");
        # Lógica para seleccionar siguiente destino
        append_branch();
        t_relay();
        exit;
    }
    
    xlog("L_INFO", "Todos los contactos fallaron\n");
}

Manejo Avanzado de Transacciones

Timeouts Personalizados

route[TIMEOUTS_PERSONALIZADOS] {
    # Configurar timeouts específicos según el destino
    if ($rd =~ "gateway-lento\.com") {
        xlog("L_INFO", "Configurando timeout extendido para gateway lento\n");
        t_set_fr(0, 180000);  # 3 minutos para INVITE
    } else {
        xlog("L_INFO", "Usando timeout estándar\n");
        t_set_fr(0, 30000);   # 30 segundos estándar
    }
    
    t_relay();
    exit;
}

Reintento Automático

route[REINTENTO_AUTOMATICO] {
    $avp(intentos) = 0;
    t_on_failure("LOGICA_REINTENTO");
    
    xlog("L_INFO", "Primer intento de envío a $rd\n");
    t_relay();
    exit;
}

failure_route[LOGICA_REINTENTO] {
    $avp(intentos) = $avp(intentos) + 1;
    
    xlog("L_INFO", "Fallo en intento $avp(intentos): $T_reply_code\n");
    
    if ($avp(intentos) < 3 && t_check_status("5[0-9][0-9]")) {
        xlog("L_INFO", "Reintentando envío (intento $avp(intentos))\n");
        
        # Esperar un poco antes del reintento
        sleep("2");
        
        if (t_relay()) {
            xlog("L_INFO", "Reintento enviado exitosamente\n");
        } else {
            xlog("L_ERR", "Fallo en reintento\n");
        }
        exit;
    }
    
    xlog("L_INFO", "Máximo de reintentos alcanzado\n");
}

Integración con Otros Módulos

Con Módulo Dispatcher

route[TM_CON_DISPATCHER] {
    if (!ds_select_dst("1", "4")) {
        xlog("L_ERR", "No hay destinos disponibles en dispatcher\n");
        t_reply("503", "Servicio no disponible");
        exit;
    }
    
    xlog("L_INFO", "Destino seleccionado: $du\n");
    
    t_on_failure("FAILOVER_DISPATCHER");
    
    if (!t_relay()) {
        xlog("L_ERR", "Error al reenviar via dispatcher\n");
        t_reply("500", "Error interno");
    }
    exit;
}

failure_route[FAILOVER_DISPATCHER] {
    if (t_check_status("5[0-9][0-9]|4[0-9][0-9]")) {
        xlog("L_INFO", "Probando siguiente servidor en dispatcher\n");
        
        if (ds_next_dst()) {
            xlog("L_INFO", "Nuevo destino: $du\n");
            t_relay();
            exit;
        }
    }
    
    xlog("L_ERR", "Todos los servidores fallaron\n");
}

Con Módulo Dialog

route[TM_CON_DIALOG] {
    if (is_method("INVITE")) {
        xlog("L_INFO", "Creando diálogo para llamada $ci\n");
        
        # Crear diálogo antes de reenviar
        create_dialog();
        
        # Configurar callbacks de diálogo
        dlg_manage();
        
        t_on_failure("FALLO_DIALOG");
    }
    
    t_relay();
    exit;
}

failure_route[FALLO_DIALOG] {
    xlog("L_INFO", "Fallo en llamada con diálogo: $T_reply_code\n");
    
    # El módulo dialog manejará automáticamente la limpieza
}

Monitoreo y DEBUG

Logging Detallado de Transacciones

route[LOGGING_TRANSACCIONES] {
    # Log antes del procesamiento
    xlog("L_INFO", "=== INICIO TRANSACCION ===\n");
    xlog("L_INFO", "Método: $rm | Call-ID: $ci\n");
    xlog("L_INFO", "Origen: $si:$sp | Destino: $rd:$rp\n");
    xlog("L_INFO", "Usuario: $fU -> $rU\n");
    
    t_on_failure("LOG_FAILURE");
    t_on_reply("LOG_REPLY");
    
    if (!t_relay()) {
        xlog("L_ERR", "FALLO AL CREAR TRANSACCION\n");
        xlog("L_ERR", "=== FIN TRANSACCION (ERROR) ===\n");
        t_reply("500", "Error interno del servidor");
        exit;
    }
    
    xlog("L_INFO", "Transacción creada exitosamente\n");
    exit;
}

onreply_route[LOG_REPLY] {
    xlog("L_INFO", "RESPUESTA: $rs $rr desde $si\n");
    xlog("L_INFO", "Tiempo transcurrido: $TV(s) segundos\n");
}

failure_route[LOG_FAILURE] {
    xlog("L_ERR", "FALLO FINAL: $T_reply_code $T_reply_reason\n");
    xlog("L_ERR", "=== FIN TRANSACCION (FALLO) ===\n");
}

Mejores Prácticas

  1. Siempre verificar transacciones existentes con t_check_trans() al inicio del request_route

  2. Usar failure_route apropiadamente para manejar fallos y implementar lógica de failover

  3. Configurar timeouts adecuados según el tipo de destino y red

  4. Implementar logging detallado para facilitar la depuración

  5. Limitar el número de branches en forking para evitar sobrecarga

  6. Usar onreply_route para procesamiento de respuestas cuando sea necesario

Conclusión

El módulo TM es fundamental para el funcionamiento de Kamailio como proxy SIP. Su correcta configuración y uso determina la confiabilidad, rendimiento y capacidades avanzadas del servidor. La comprensión profunda de sus funciones permite implementar soluciones SIP robustas y escalables.

Las funciones aquí descritas cubren los casos de uso más comunes, pero el módulo TM ofrece muchas más capacidades para escenarios específicos y avanzados.

Vota el Articulo: 

Sin votos (todavía)
Evalúa la calidad del articulo
Suscribirse a Comentarios de "El Módulo TM de Kamailio - Gestión de Transacciones" Suscribirse a VozToVoice - Todos los comentarios