# Cómo funciona traceroute: Implementación en Rust desde cero
Traceroute es una herramienta de diagnóstico de red que revela la ruta que siguen los paquetes a través de Internet. Se basa en dos mecanismos clave: TTL (tiempo de vida) en los encabezados IP e mensajes de error ICMP. Al incrementar el TTL en cada paso, los enrutadores envían notificaciones cuando expira la vida del paquete, construyendo un mapa de ruta. Implementar nuestra propia versión en Rust en solo 80 líneas de código muestra cómo las operaciones de red de bajo nivel se vuelven claras con un profundo conocimiento de los protocolos.
Arquitectura de las sondas UDP
El elemento crítico de traceroute es enviar paquetes dirigidos con TTL limitado. Se usa UDP en lugar de TCP por tres razones:
- Sin apretón de manos reduce la sobrecarga
- Sin garantía de entrega: los paquetes están pensados para ser descartados
- Números de puerto altos (empezando por 33434) minimizan conflictos con servicios en ejecución
El código inicializa dos sockets:
let send_sock = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?;
send_sock.set_ttl_v4(ttl)?;
let recv_sock = Socket::new(
Domain::IPV4,
Type::from(libc::SOCK_RAW),
Some(Protocol::ICMPV4),
)?;
El primer socket envía paquetes condenados con el TTL especificado; el segundo intercepta las respuestas ICMP. Importante: los sockets raw requieren privilegios de root, ya que operan a nivel de la pila de red.
Análisis de respuestas ICMP
Cuando un enrutador descarta un paquete al llegar a TTL=0, envía un mensaje ICMP de tipo 11 (Time Exceeded). La estructura de datos contiene:
- Primeros 20 bytes: encabezado IP de la respuesta
- Bytes 12-15: dirección IP del enrutador
- Byte 20: tipo de mensaje ICMP
La implementación inicial solo analizaba la dirección IP:
if buf.len() >= 20 {
let ip = Ipv4Addr::new(buf[12], buf[13], buf[14], buf[15]);
Ok(Some(ip))
}
Esto causaba errores al detectar el punto final. El manejo correcto requiere verificar el tipo ICMP:
match buf[20] {
11 => Ok(ProbeResult::Hop(ip)),
3 if ip == target => Ok(ProbeResult::Reached(ip)),
3 => Ok(ProbeResult::Hop(ip)),
_ => Ok(ProbeResult::Timeout),
}
El tipo 3 (Destination Unreachable) indica que se ha alcanzado el destino solo si la IP coincide.
Optimizaciones de traceroute
El traceroute original usa dos técnicas ausentes en la implementación básica:
- Incremento del número de puerto: cada paquete subsiguiente se envía al puerto +1. Esto permite una coincidencia inequívoca de las respuestas ICMP con las solicitudes mediante el campo Identification en el encabezado UDP.
- Soporte para modo TCP: cuando UDP está bloqueado por firewalls, se usa un paquete SYN con TTL bajo. El mecanismo de detección de saltos sigue siendo el mismo.
Un error crítico en versiones tempranas del código: ignorar la verificación de la dirección IP al recibir tipo 3. Sin la condición if ip == target, el rastreo se detenía en el primer enrutador que devolvía Destination Unreachable.
Lo más importante
- TTL como herramienta de control: incrementar gradualmente el valor permite descubrir secuencialmente cada salto
- Mensajes ICMP: la base del diagnóstico: Time Exceeded (tipo 11) y Destination Unreachable (tipo 3) forman el mapa de ruta
- Sockets raw requieren privilegios: las operaciones de red de bajo nivel son imposibles sin derechos de root
- UDP frente a TCP: la elección del protocolo afecta la evasión de firewalls, pero no el algoritmo básico
- Análisis de datos binarios: el acceso directo a los bytes del paquete requiere conocimiento de las estructuras de encabezados IP/ICMP
Implementación de la condición de parada
Una mejora clave: detectar correctamente cuándo se ha alcanzado el destino. El enum ProbeResult reemplaza el simple Option<Ipv4Addr>:
enum ProbeResult {
Hop(Ipv4Addr),
Reached(Ipv4Addr),
Timeout,
}
En el bucle principal, el manejo de resultados es así:
match hop {
ProbeResult::Hop(ip) => println!("{:>2} {}", ttl, ip),
ProbeResult::Reached(ip) => {
println!("{:>2} {}", ttl, ip);
break;
}
ProbeResult::Timeout => println!("{:>2} *", ttl),
}
Esto asegura que el rastreo se detenga al alcanzar el host destino, no por tiempo de espera. En escenarios reales, también agregar:
- Tres sondas por TTL para estadísticas de latencia
- Manejo de paquetes fragmentados
- Soporte para IPv6
- Tiempos de espera basados en distancia
Resumen y limitaciones
La implementación construida demuestra el núcleo de traceroute, pero tiene limitaciones:
- No considera rutas asimétricas
- No maneja limitación de tasa ICMP
- Ignora etiquetas MPLS en redes modernas
- Requiere privilegios de superusuario
Para soluciones de producción, usa bibliotecas como pnet_packet para evitar el análisis manual de bytes. Sin embargo, escribir una versión raw es la mejor forma de entender los fundamentos de red. Todo ingeniero de redes debería implementar traceroute al menos una vez para comprender cómo viajan los datos a través de Internet.
— Editorial Team
Aún no hay comentarios.