Módulo 5: Máquinas remotas, redes y scripting

3. Diagnosticar la red desde la terminal: ping, dig, curl y ss

Descripción

Al terminar esta lección vas a poder diagnosticar por capas por qué una conexión de red falla —¿no resuelve el nombre?, ¿no hay alcance?, ¿el puerto no responde?, ¿la respuesta es incorrecta?— y vas a poder elegir la herramienta correcta para cada capa: dig para resolución, ping y traceroute para alcance (con sus límites reales), ss, netstat y lsof -i para puertos locales, y curl -I, -v y -w para inspeccionar la respuesta HTTP completa.

Esto es exactamente lo que hace un ingeniero cuando suena una alerta a las tres de la mañana y el mensaje dice "el endpoint de pagos no responde". Esa frase no dice nada sobre la causa. Puede ser que nadie resuelva el dominio, que el datacenter esté inalcanzable, que el proceso se haya caído y el puerto ya no escuche, o que el servicio responda pero con un error 500. Cada causa apunta a un equipo distinto —redes, infraestructura, la aplicación misma— y quien no sabe descartar capas termina escalando a la persona equivocada o, peor, reiniciando servicios al azar hasta que algo "funciona" sin saber por qué.

Conexión con el módulo: la lección anterior te dio el vocabulario —IP, puertos, DNS, qué significa que un puerto "escuche"—. Esta lección te da las herramientas para observar ese vocabulario en acción y decidir, con evidencia, en qué capa está el problema.

El método: descartar por capas, no probar comandos al azar

Un electricista que no encuentra por qué no prende una lámpara no empieza a cambiar cables al azar. Sigue el circuito desde el tablero: ¿llega corriente al tablero?, ¿llega al interruptor?, ¿llega a la caja de la lámpara?, ¿el foco mismo funciona? En cada punto mide con el tester y avanza solo cuando confirma que esa parte del circuito está bien. Si mide corriente en la caja de la lámpara pero el foco no prende, ya sabe que el problema es el foco, no el cableado — se ahorra abrir paredes que no tenían nada que ver.

Diagnosticar una conexión de red es el mismo ejercicio, con cuatro puntos de medición en vez de un circuito eléctrico:

  1. ¿Resuelve el nombre? El dominio que escribiste, ¿se traduce a una dirección IP? Esto es DNS puro, sin tocar la red hacia el destino todavía.
  2. ¿Hay alcance? Con esa IP en mano, ¿tu máquina puede llegar hasta esa red? Esto es alcance a nivel de host, sin importar todavía qué servicio corre ahí.
  3. ¿El puerto responde? Llegaste al host, pero ¿hay algo escuchando en el puerto específico que te interesa? Un host puede estar perfectamente vivo con el puerto que buscas cerrado.
  4. ¿La respuesta es correcta? El puerto acepta la conexión, pero ¿el servicio que responde hace lo que se supone que debe hacer, con el código de estado y el cuerpo esperados?

Cada capa tiene su propia herramienta, y — esto es la parte que se aprende con la práctica, no con la teoría — cada capa puede fallar de forma distinta a lo que un vistazo superficial sugiere. Un ping que no responde no prueba que el host esté caído. Un curl -I que devuelve un 301 no prueba que el sitio esté roto. El método evita que saques la conclusión equivocada por quedarte en el primer síntoma.

Ejemplo trabajado

Supongamos que necesitas levantar una conexión contra internal-api.example.com, puerto 8443, y el request se queda colgado. (Usamos example.com y direcciones del bloque 203.0.113.0/24 porque son direcciones reservadas para documentación por el RFC 5737 — resuelven de verdad, pero nunca apuntan a un servicio real, así que puedes copiar estos comandos sin miedo a golpear algo ajeno).

Paso 1 — ¿resuelve el nombre?

dig internal-api.example.com +short

Qué esperar:

203.0.113.24

Una sola línea con la IP. Si en cambio no devuelve nada, el problema ya está resuelto: es DNS, y ni siquiera vale la pena intentar conectar todavía.

Paso 2 — ¿hay alcance?

ping -c 3 internal-api.example.com

Qué esperar:

PING internal-api.example.com (203.0.113.24): 56 data bytes
Request timeout for icmp_seq 0
Request timeout for icmp_seq 1
Request timeout for icmp_seq 2

--- internal-api.example.com ping statistics ---
3 packets transmitted, 0 packets received, 100.0% packet loss

Aquí es donde la mayoría se detiene y concluye "está caído". Es el error más común de esta lección — más abajo vemos por qué es una conclusión apurada. Sigamos al siguiente punto de medición antes de sacar conclusiones.

Paso 3 — ¿el puerto responde?

curl -v --connect-timeout 5 https://internal-api.example.com:8443/health

Qué esperar:

*   Trying 203.0.113.24:8443...
* Connected to internal-api.example.com (203.0.113.24) port 8443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN: server accepted h2
> GET /health HTTP/2
> Host: internal-api.example.com:8443
>
< HTTP/2 200
< content-type: application/json
<
{"status":"ok"}

La línea clave es * Connected to internal-api.example.com (203.0.113.24) port 8443. El puerto sí responde, la negociación TLS se completa y el servidor contesta con un 200. El diagnóstico correcto no es "el host está caído": es que ese host tiene el ICMP (lo que usa ping) bloqueado, pero el puerto 8443 (lo que usa tu aplicación de verdad) está perfectamente vivo.

Paso 4 — ¿la respuesta es correcta?

Ya viste que responde con 200 y un cuerpo válido en el paso anterior. Si en cambio el request tarda mucho, el siguiente comando te dice en qué etapa se va el tiempo:

curl -o /dev/null -s -w "dns:%{time_namelookup}s connect:%{time_connect}s ttfb:%{time_starttransfer}s total:%{time_total}s\n" https://internal-api.example.com:8443/health

Qué esperar:

dns:0.045s connect:0.089s ttfb:0.312s total:0.315s

time_namelookup y time_connect bajos, pero time_starttransfer (tiempo hasta el primer byte de respuesta) alto, apunta al servidor procesando lento — no a la red. Si en cambio time_connect fuera el número grande, el problema estaría en la ruta de red, no en el servicio.

Con esas cuatro mediciones ya tienes evidencia, no sospecha: DNS resuelve, el puerto responde, TLS negocia, el servicio contesta 200 en 315 milisegundos. El ping fallido nunca fue el síntoma real.

Profundización: dig y nslookup — mirar más de cerca la resolución

dig +short te da la respuesta rápida, pero dig sin +short te muestra todo lo que hay detrás de esa IP:

dig internal-api.example.com

En la salida completa, la sección que importa es ANSWER SECTION:

;; ANSWER SECTION:
internal-api.example.com. 300 IN A 203.0.113.24

Esa fila te dice cuatro cosas en orden: el nombre consultado, el TTL (300 segundos — cuánto tiempo pueden los resolvers cachear esta respuesta antes de volver a preguntar), la clase (IN, internet) y el tipo de registro (A, una dirección IPv4). Si esperabas un CNAME y ves un A directo, o si el TTL es sospechosamente alto después de que alguien "ya cambió el DNS", ahí está la explicación.

nslookup hace básicamente lo mismo y sigue siendo común, sobre todo en Windows, pero su salida es menos práctica de leer en scripts y ya no recibe desarrollo activo — si tienes dig disponible, úsalo primero.

Profundización: ping y sus límites reales

ping mide una sola cosa: si el host responde a paquetes ICMP Echo. Eso es útil, pero es una medida distinta de "¿el servicio que me interesa funciona?", y la diferencia importa en producción por una razón concreta: es una práctica extendida en firewalls, balanceadores de carga y grupos de seguridad en la nube bloquear ICMP de forma deliberada, como medida de seguridad, sin que eso afecte al tráfico TCP real de la aplicación. Un servidor puede tener el ICMP cerrado a propósito y, al mismo tiempo, servir tráfico HTTP sin ningún problema.

Por eso el orden del método importa: ping es útil como primera señal rápida cuando SÍ responde (confirma alcance sin ambigüedad), pero un ping que falla no es evidencia de nada por sí solo — solo te dice que necesitas seguir a la siguiente capa con una herramienta que hable el protocolo que en verdad te interesa (curl, nc, o el cliente real de tu aplicación).

Profundización: curl -I, -v y -w — encabezados, negociación y tiempos

Tres modos distintos de curl, para tres preguntas distintas:

curl -I hace un request HEAD y muestra solo los encabezados de respuesta, sin cuerpo. Sirve para una verificación rápida de estado y tipo de contenido sin descargar nada:

curl -I https://internal-api.example.com:8443/health
HTTP/1.1 200 OK
content-type: application/json
content-length: 16

curl -v (verbose) muestra el proceso completo: resolución, conexión TCP, negociación TLS, y los encabezados de request y response con los prefijos > (lo que curl envía) y < (lo que el servidor responde). Es la herramienta cuando -I no alcanza porque necesitas ver en qué paso exacto se rompe la conexión — TCP, TLS o la aplicación.

curl -w (write-out) te deja pedir métricas específicas en el formato que quieras, usando variables como %{time_namelookup}, %{time_connect}, %{time_appconnect} (fin del handshake TLS), %{time_starttransfer} y %{time_total}. Es la diferencia entre "el request tardó 2 segundos" y "el request tardó 2 segundos porque el DNS tardó 1.8 de esos 2" — dos diagnósticos completamente distintos que apuntan a arreglos completamente distintos.

Un detalle que confunde seguido: si un sitio redirige (301/302), curl -I sin -L te muestra ese código y se detiene ahí. Eso no es un error del sitio — es una respuesta HTTP válida que dice "el recurso se movió". El encabezado location: te dice adónde. Agregar -L hace que curl siga la redirección automáticamente.

Profundización: ss, netstat y lsof -i — quién escucha en tu propia máquina

Estas tres herramientas responden la pregunta desde el lado local: ¿qué proceso tiene abierto qué puerto en esta máquina?

ss -tulpn (Linux) es la más rápida y la que usa la mayoría de las distribuciones modernas, porque viene del paquete iproute2 que reemplazó a las herramientas más viejas de net-tools:

sudo ss -tulpn
Netid State  Recv-Q Send-Q Local Address:Port   Peer Address:Port  Process
tcp   LISTEN 0      128    0.0.0.0:8443         0.0.0.0:*          users:(("api-server",pid=4821,fd=6))
tcp   LISTEN 0      128    127.0.0.1:5432       0.0.0.0:*          users:(("postgres",pid=1190,fd=7))

Cada letra del flag filtra algo: -t solo TCP, -u solo UDP, -l solo sockets en estado LISTEN (los que aceptan conexiones nuevas, no las ya establecidas), -p muestra el proceso dueño del socket, -n evita que ss intente resolver puertos a nombres de servicio y así responde más rápido. La columna Local Address:Port es la que ya conoces de la lección anterior: 0.0.0.0:8443 escucha en todas las interfaces, 127.0.0.1:5432 solo acepta conexiones desde la propia máquina — postgres, por ejemplo, casi nunca debería escuchar en 0.0.0.0.

netstat -tulpn hace lo mismo en Linux y todavía aparece en documentación vieja, pero está en modo de mantenimiento frente a ss. En macOS, netstat no acepta -p para mostrar el proceso dueño del socket, así que ahí la herramienta equivalente es lsof -i:

sudo lsof -i :8443
COMMAND    PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
api-serv  4821 mikeni    6u  IPv4  0x1a2      0t0  TCP *:8443 (LISTEN)

lsof -i lista archivos abiertos que son sockets de red (-i, de "internet"); filtrar con :8443 limita la salida a ese puerto exacto. Funciona igual en Linux si prefieres su sintaxis a la de ss. La necesitas casi siempre con sudo: sin privilegios, el kernel no te deja ver a qué proceso pertenecen sockets que no son tuyos.

Profundización: traceroute — ubicar dónde se corta el camino

Cuando ya sabes que hay un problema de alcance real (no solo ICMP bloqueado en el destino final), traceroute te muestra cada salto de red entre tu máquina y el destino, con el tiempo de ida y vuelta de cada uno:

traceroute internal-api.example.com
traceroute to internal-api.example.com (203.0.113.24), 64 hops max
 1  192.168.1.1        2.104 ms  1.876 ms  1.654 ms
 2  10.20.0.1          8.221 ms  7.998 ms  8.045 ms
 3  * * *
 4  * * *
 5  203.0.113.1       24.331 ms 23.876 ms 24.001 ms
 6  203.0.113.24      25.012 ms 24.876 ms 24.998 ms

Los * * * en los saltos 3 y 4 no significan, por sí solos, que ahí se cortó el camino: solo significan que ese router en particular no respondió al paquete de sondeo, algo frecuente cuando un router está configurado para no responder o para descartar silenciosamente ese tipo de tráfico. La prueba de que el camino sigue vivo está en los saltos 5 y 6, que sí responden — el paquete atravesó los saltos 3 y 4 sin problema, simplemente esos routers no avisaron que lo hicieron. La señal real de un corte es que los * * * continúen hasta el final sin que ningún salto posterior responda.

Un detalle de plataforma que vale la pena saber: en Linux y macOS, traceroute envía por defecto paquetes UDP a puertos altos; en Windows, tracert envía ICMP. Si un firewall corporativo permite un protocolo y bloquea el otro, el mismo camino de red puede parecer roto en un sistema operativo y sano en otro. La opción -I fuerza a traceroute a usar ICMP igual que ping, útil cuando quieres comparar manzanas con manzanas contra un tracert de Windows.

Honestidad: qué no puedes diagnosticar desde tu propia máquina

Todo lo anterior te da evidencia de la red hasta donde tu máquina puede observar. Hay cosas que ninguna de estas herramientas te va a mostrar:

  • Qué pasa dentro del servidor. Si el puerto responde 200 pero con datos incorrectos, la causa está en los logs de esa aplicación o de su base de datos — no en la red. Tu terminal ve la respuesta, no la razón de la respuesta.
  • Reglas de firewall o de un WAF que solo bloquean tu IP o tu patrón de tráfico específico. Un compañero en otra red puede tener éxito con el mismo comando exacto que a ti te falla, y la diferencia puede estar en una regla que nunca vas a ver desde tu lado.
  • Si el "otro lado" ya sabe que algo está mal. A veces el diagnóstico correcto y más rápido es escribirle al equipo dueño del servicio con la evidencia que ya juntaste (dig resolvió, el puerto no responde desde ningún lado) en vez de seguir intentando comandos nuevos.

Saber decir "esto ya no lo puedo ver desde aquí, hay que preguntarle al otro lado" con evidencia en mano —no como excusa— es parte del método, no una falla de él.

Errores comunes

1. Concluir que el servidor está "caído" porque ping no responde. Qué pasa: como viste en el paso 2 del ejemplo, muchos hosts en producción bloquean ICMP a propósito sin que eso afecte el tráfico real de la aplicación. Por qué ocurre: ping se siente como "la prueba de vida" porque es el comando más conocido, y confundimos "no responde a ICMP" con "no responde a nada". Cómo detectarlo: si sospechas que es esto, prueba el puerto real con curl -v o nc antes de escalar. Cómo corregirlo: nunca declares un servicio caído solo por ping — usa el protocolo real (HTTP, la base de datos, lo que sea) para confirmar.

2. Leer un 301/302 de curl -I como si fuera un error. Qué pasa: el alumno ve un código que no es 200 y asume que el sitio está roto. Por qué ocurre: falta el hábito de leer el código de estado como información, no como veredicto binario de "funciona / no funciona" — un 3xx es una respuesta HTTP perfectamente válida que dice "el recurso está en otro lado". Cómo detectarlo: revisa el encabezado location: en la misma salida; si apunta a una URL válida, el servidor está funcionando bien. Cómo corregirlo: agrega -L para seguir la redirección, o trata el 3xx como el destino real de tu diagnóstico, no como un fallo.

3. Correr ss -tulpn sin privilegios y concluir que nada escucha en ese puerto. Qué pasa: sin sudo, la columna de proceso (users:(...)) puede aparecer vacía para sockets que no son tuyos, aunque el socket sí esté en LISTEN. Por qué ocurre: el kernel oculta el dueño del socket a usuarios sin privilegio suficiente, pero sigue mostrando la fila con el puerto. Cómo detectarlo: si el puerto aparece en LISTEN pero la columna de proceso está vacía o dice -, es una pista de permisos, no de ausencia de proceso. Cómo corregirlo: repite el comando con sudo ss -tulpn (o sudo lsof -i :puerto) antes de concluir que el puerto está libre.

Ejercicios

Ejercicio 1. Un compañero te dice: "intenté hacer ping a payments.internal y no contestó nada, así que el servicio de pagos está caído". Escribe, en orden, la secuencia de comandos (con sus flags) que correrías para confirmar o descartar esa conclusión antes de escalar la alerta.

Ver solución
dig payments.internal +short
curl -v --connect-timeout 5 https://payments.internal:PUERTO/ruta-de-salud
curl -I https://payments.internal:PUERTO/ruta-de-salud

Primero confirmas que el nombre resuelve (descarta DNS). Después pruebas el puerto real con curl -v, que te muestra si la conexión TCP se completa aunque ICMP esté bloqueado — si ves * Connected to ..., el ping fallido no prueba nada y el servicio está vivo. Por último, curl -I te confirma el código de estado real. Funciona porque cada comando mide una capa distinta de las cuatro del método, en el mismo orden en que se pueden romper.

Ejercicio 2. Tienes esta salida de traceroute:

 1  192.168.1.1     1.2 ms  1.1 ms  1.0 ms
 2  10.0.0.1        4.5 ms  4.3 ms  4.1 ms
 3  * * *
 4  * * *
 5  * * *
 6  * * *

y el traceroute se detiene ahí (llegó a los 64 saltos máximos sin que ningún salto después del 2 respondiera, y la conexión final tampoco se completa). ¿Este resultado es evidencia de un corte real en el camino, o solo de un router que no responde a sondeos? Justifica.

Ver solución

Es evidencia de un corte real, a diferencia del ejemplo de la lección donde los saltos 3 y 4 no respondían pero el 5 y 6 sí. Aquí ningún salto posterior al 2 responde nunca, hasta el límite de saltos — no hay ninguna señal de que el paquete haya seguido avanzando después del salto 2. Un router silencioso explica uno o dos saltos sin respuesta en medio de un camino que sigue funcionando; no explica que absolutamente todo lo que sigue quede en silencio. La diferencia entre ambos casos está en si hay algo después del hueco que confirme que el paquete llegó más allá.

Ejercicio 3. Corriste este comando y obtuviste esta salida:

curl -o /dev/null -s -w "dns:%{time_namelookup}s connect:%{time_connect}s ttfb:%{time_starttransfer}s total:%{time_total}s\n" https://api.example.com/reports
dns:0.041s connect:0.098s ttfb:3.812s total:3.815s

¿En qué capa está el cuello de botella, y a qué equipo (red o aplicación) le llevarías este dato?

Ver solución

El cuello de botella está entre time_connect (0.098s, la conexión TCP se establece rápido) y time_starttransfer (3.812s, el tiempo hasta el primer byte de respuesta). Esos 3.7 segundos de diferencia ocurren después de que la red ya hizo su trabajo: DNS resolvió rápido, TCP conectó rápido, así que el tiempo se va en que el servidor procese el request antes de empezar a responder. Es evidencia para llevarle al equipo de aplicación (o de base de datos, si el endpoint hace consultas pesadas), no al equipo de red — la red no es la culpable aquí.

Ejercicio 4. En tu propia máquina, levanta un servidor simple (por ejemplo python3 -m http.server 8000) y, sin detenerlo, usa ss -tulpn (Linux) o sudo lsof -i :8000 (macOS/Linux) para confirmar que está escuchando. Anota en qué dirección aparece enlazado (Local Address) y explica qué significaría si en vez de 127.0.0.1:8000 vieras 0.0.0.0:8000.

Ver solución

Un servidor de prueba levantado con http.server normalmente aparece enlazado a 0.0.0.0:8000 (todas las interfaces) por defecto en muchas configuraciones, o a 127.0.0.1:8000 si se limitó explícitamente al loopback. Si ves 0.0.0.0:8000, cualquier máquina que pueda alcanzar la tuya en la red (no solo procesos locales) puede conectarse a ese puerto — útil para probar desde otro dispositivo en tu misma red, pero también la razón por la que nunca deberías dejar un servicio sin autenticación enlazado así en una máquina con salida a internet. Si ves 127.0.0.1:8000, solo procesos en tu propia máquina pueden conectarse.

Resumen y siguiente paso

El método de esta lección no cambia: resolución, alcance, puerto, respuesta — en ese orden, con una herramienta distinta para cada capa, y sin confundir "esta herramienta no respondió" con "el servicio está caído". ping mide ICMP, no tu aplicación. dig mide DNS, no la red hacia el destino. ss/lsof -i miran tu propia máquina, no la del otro lado. Y siempre hay un punto en el que la evidencia deja de estar de tu lado y toca preguntarle al otro extremo con esa evidencia en mano.

Antes de avanzar deberías poder: explicar, sin ver código de ningún servicio, en qué capa se rompió una conexión con solo dos o tres comandos; explicar por qué un ping fallido no basta como diagnóstico; leer una salida de curl -w y decir si el problema es de red o de aplicación; y encontrar qué proceso escucha en un puerto dado en tu propia máquina.

Todo esto midió si un canal de red existe y responde. La siguiente lección da el paso natural: abrir un canal cifrado y autenticado sobre ese mismo camino de red con SSH, para que en vez de solo diagnosticar una máquina remota, puedas trabajar dentro de ella.

Recursos