TTL, errores ICMP y cómo una app en sandbox lo consigue igualmente
Una cabecera IPv4 lleva un campo TTL de 8 bits (en IPv6 se llama Hop Limit) cuyo único propósito es impedir que los paquetes circulen para siempre. Cada router que reenvía un paquete le resta uno; cuando llega a cero, el router descarta el paquete y devuelve un mensaje ICMP Time Exceeded, tipo 11 código 0, con origen en la interfaz por la que llegó el paquete.
Traceroute convierte ese modo de fallo en una medida. Envía una sonda con TTL 1, que muere en el primer router y provoca un error que lo nombra. Luego TTL 2, luego TTL 3, avanzando hacia fuera salto a salto. Tres sondas por TTL producen los tres tiempos de cada línea. Cómo se reconoce el destino depende del tipo de sonda: el traceroute BSD de macOS envía datagramas UDP a puertos altos improbables a partir del 33434 y trata un ICMP Port Unreachable (tipo 3, código 3) como señal de llegada, mientras que el tracert de Windows envía peticiones ICMP Echo y se detiene ante una Echo Reply. Esa diferencia no es cosmética. Los cortafuegos tratan las sondas UDP y el ICMP Echo de forma muy distinta, así que un mismo destino produce legítimamente saltos con asteriscos distintos desde un Mac y desde una máquina Windows; conviene comprobar ambos antes de culpar a un router.
Lo que nos lleva a la pregunta que todo el mundo contesta mal, incluida una versión anterior de esta página: ¿puede siquiera hacer esto una app en sandbox?
El razonamiento que dice que no va así. /usr/sbin/traceroute se instala setuid root en macOS mientras que /sbin/ping no, luego traceroute debe de necesitar privilegios que ping no necesita; el App Sandbox no concede tales privilegios; por lo tanto, no hay traceroute en una app del App Store. Cada paso suena razonable y la conclusión sigue siendo falsa, porque la premisa confunde el tipo de sonda que eligió el traceroute BSD con el traceroute como técnica. Ese binario sondea con datagramas UDP y necesita un socket raw para leer los errores ICMP que provocan, de ahí el setuid. Sondea con ICMP Echo en su lugar y las respuestas llegan al mismo socket ICMP de datagramas que el ping ya usa sin privilegios. setsockopt(IP_TTL) no es una llamada privilegiada.
Así que SSHive no pilota ningún binario del sistema. Abre socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP), fija el TTL, envía tres peticiones Echo por salto y lee lo que vuelve: un Time Exceeded que nombra a un router, una Echo Reply que significa que el destino respondió, o nada en dos segundos, que se imprime como un asterisco. Eso se midió bajo sandbox-exec con los entitlements reales de la app antes de publicar nada, y reveló una trampa que merece repetirse: con com.apple.security.network.client a secas, la sonda sale y la respuesta se rechaza con EPERM, un fallo silencioso que se parece exactamente a una red que filtra. Hace falta también com.apple.security.network.server. SSHive declara los dos, y por eso la versión del App Store, en su sandbox, traza de verdad en vez de alinear una columna de asteriscos.
iOS llega al mismo sitio por el mismo camino. Network.framework expone TCP, UDP, QUIC y TLS, y nada de ICMP, pero no es la única API disponible: la capa de sockets BSD que hay debajo acepta el mismo socket ICMP de datagramas, y el ejemplo SimplePing del propio Apple lleva años demostrándolo. Las versiones de iPhone y iPad ejecutan el mismo bucle de treinta saltos y tres sondas que el Mac.
Conoce los límites antes de apoyarte en ella. El techo de saltos está fijado en 30, no hay parámetros de protocolo, puerto ni número de sondas, y no hay enriquecimiento de ASN ni de geolocalización. Como las sondas son ICMP Echo y no UDP, un cortafuegos que permita uno y descarte el otro puede producir un patrón de asteriscos distinto del que daría /usr/sbin/traceroute: ninguno de los dos resultados es erróneo, y compararlos es a veces justo la forma de encontrar el cortafuegos.