tcpdump es la herramienta de captura de paquetes que ya está instalada —o a un paquete de distancia— en casi cualquier servidor Linux, router o gateway. No entiende MQTT, pero no le hace falta: capturar la conversación TCP en el puerto 1883 o 8883 basta para demostrar quién se conectó, quién cerró la conexión y si se perdieron paquetes por el camino. Cuando necesitas los detalles de MQTT, abres el mismo archivo en Wireshark.
Esta guía es un conjunto de recetas de tcpdump para MQTT: capturas básicas, filtros BPF que reconocen tipos de paquete MQTT, capturas en Docker y Kubernetes, buffers rotativos de larga duración y cómo analizar lo que capturaste.
La captura MQTT básica
# Una línea por segmento (MQTT sin cifrar y TLS)
sudo tcpdump -i any -nn 'tcp port 1883 or tcp port 8883'
# Escribir en un archivo para Wireshark
sudo tcpdump -i any -nn -w mqtt.pcap 'tcp port 1883 or tcp port 8883'
# Solo el tráfico con un dispositivo
sudo tcpdump -i any -nn 'host 192.168.1.50 and tcp port 1883'Las opciones que más usarás:
| Opción | Significado |
|---|---|
-i any / -i eth0 | Interfaz en la que capturar (tcpdump -D las lista) |
-nn | No resolver nombres de host ni de puerto: más rápido y sin ambigüedad |
-w file.pcap | Escribir los paquetes en un archivo en lugar de imprimirlos |
-r file.pcap | Leer un archivo de captura |
-A / -X | Mostrar el payload en ASCII / en hex y ASCII |
-c 200 | Parar tras 200 paquetes |
-U | Volcar cada paquete inmediatamente (necesario al escribir en un pipe) |
-tttt | Fecha y hora completas en cada línea |
Para ver algo útil enseguida, publica en un topic de prueba desde otra terminal y mira el payload en ASCII:
sudo tcpdump -i any -nn -A 'tcp port 1883' &
mosquitto_pub -h broker.connectmq.com -p 1883 -t 'testmqtt/tcpdump/hello' -m 'hello from tcpdump'Verás el topic testmqtt/tcpdump/hello y el payload en texto plano dentro del segmento PUBLISH. En el puerto 8883 solo verás bytes cifrados, que es justo lo que busca TLS.
Dónde capturar
El lugar donde capturas decide qué puedes demostrar. Una captura en el cliente muestra lo que el dispositivo envió realmente y lo que volvió, incluidas las retransmisiones que la aplicación nunca nota. Una captura en el host del broker muestra lo que llegó de verdad, después de que NAT, balanceadores de carga y proxies hayan hecho su trabajo. Cuando ambas no coinciden, el problema está en medio.
Para un problema persistente, captura en ambos lados a la vez con marcas de tiempo -tttt y compara. Si el cliente envía un PUBLISH que nunca aparece del lado del broker, revisa la ruta de red; si el broker lo recibe y el suscriptor nunca lo obtiene, revisa las ACL, las colas y los logs del broker.
Leer la salida de tcpdump
Una conexión MQTT típica se ve así (direcciones abreviadas):
10:15:02.114 IP 10.0.0.12.52144 > 203.0.113.10.1883: Flags [S], seq 1902...
10:15:02.139 IP 203.0.113.10.1883 > 10.0.0.12.52144: Flags [S.], seq 4471..., ack 1902...
10:15:02.139 IP 10.0.0.12.52144 > 203.0.113.10.1883: Flags [.], ack 1
10:15:02.140 IP 10.0.0.12.52144 > 203.0.113.10.1883: Flags [P.], length 35 <- CONNECT
10:15:02.166 IP 203.0.113.10.1883 > 10.0.0.12.52144: Flags [P.], length 4 <- CONNACK
10:15:02.167 IP 10.0.0.12.52144 > 203.0.113.10.1883: Flags [P.], length 42 <- PUBLISH
10:15:02.168 IP 10.0.0.12.52144 > 203.0.113.10.1883: Flags [P.], length 2 <- DISCONNECT
10:15:02.168 IP 10.0.0.12.52144 > 203.0.113.10.1883: Flags [F.], ...Los flags TCP llevan la mayor parte del diagnóstico:
[S]sin respuesta[S.]: el SYN nunca obtuvo respuesta. Firewall, enrutamiento o IP equivocada. Consulta diagnosticar problemas de red en MQTT.[S]respondido con[R.]: el host está activo, pero nada escucha en el puerto (Connection refused).[F.]: un cierre ordenado. El lado que envía el primer FIN es el que decidió terminar la sesión.[R]: un reset abrupto. Si viene de una dirección que no es ni el cliente ni el broker, un equipo intermedio mató la conexión.- El mismo segmento
[P.]repetido tras un retraso: una retransmisión, es decir, pérdida en la ruta.
Filtrar por tipo de paquete MQTT
El primer byte de cada paquete MQTT codifica su tipo en los cuatro bits superiores. Una expresión BPF puede leer ese byte al inicio del payload TCP, lo que permite a tcpdump quedarse solo con ciertos tipos de paquete:
| Tipo | Primer byte (con máscara 0xf0) |
|---|---|
| CONNECT | 0x10 |
| CONNACK | 0x20 |
| PUBLISH | 0x30 |
| SUBSCRIBE | 0x80 |
| PINGREQ / PINGRESP | 0xc0 / 0xd0 |
| DISCONNECT | 0xe0 |
sudo tcpdump -i any -nn -A \
'tcp port 1883 and tcp[tcpflags] & tcp-push != 0 and
((tcp[((tcp[12:1] & 0xf0) >> 2):1] & 0xf0) = 0x10 or
(tcp[((tcp[12:1] & 0xf0) >> 2):1] & 0xf0) = 0x20)'(tcp[12:1] & 0xf0) >> 2 es la longitud de la cabecera TCP, así que la expresión lee el primer byte del payload. La condición tcp-push descarta los segmentos ACK vacíos. Dos límites a tener en cuenta: solo ve el primer paquete MQTT de cada segmento (varios paquetes pequeños pueden compartir uno), y los offsets tcp[] solo funcionan con IPv4. Es un filtro para acotar una captura, no un decodificador; para respuestas exactas, usa el filtro mqtt.msgtype de Wireshark.
Detectar desconexiones y reconexiones
Dos filtros cortos responden a la pregunta más común en producción, "¿por qué este cliente se desconecta una y otra vez?":
# Cada nuevo intento de conexión (SYN sin ACK)
sudo tcpdump -i any -nn 'tcp port 1883 and tcp[tcpflags] == tcp-syn'
# Cada cierre o reset, con marcas de tiempo
sudo tcpdump -i any -nn -tttt 'tcp port 1883 and tcp[tcpflags] & (tcp-fin|tcp-rst) != 0'Léelos juntos:
- Un FIN del broker aproximadamente un intervalo y medio de keep-alive después del último paquete del cliente significa que el cliente dejó de enviar PINGREQ. Consulta keep alive en MQTT.
- Un RST tras un largo periodo de inactividad, a menudo siempre con el mismo intervalo, apunta a un timeout de NAT o firewall. Consulta timeout de NAT en MQTT.
- Cientos de SYN por segundo desde muchos dispositivos tras una caída es una tormenta de reconexiones.
- Un FIN del broker justo después de un nuevo SYN desde otra dirección con el mismo client ID indica una toma de sesión. Da a cada cliente de prueba un ID distinto con el generador de client ID.
Capturas de larga duración
Los problemas intermitentes necesitan capturas que duren horas sin llenar el disco. tcpdump puede rotar los archivos por sí mismo:
# 10 archivos de ~100 MB cada uno; el más antiguo se sobrescribe
sudo tcpdump -i any -nn -w /var/tmp/mqtt.pcap -C 100 -W 10 'tcp port 1883 or tcp port 8883'
# Un archivo por hora, con la hora en el nombre
sudo tcpdump -i any -nn -G 3600 -w '/var/tmp/mqtt-%Y%m%d-%H%M.pcap' 'tcp port 1883'Lánzalo en tmux o como una unidad temporal de systemd, anota la hora en que ocurre el problema y recupera solo el archivo relevante.
Contenedores, Kubernetes y hosts remotos
Un contenedor de broker normalmente no tiene tcpdump dentro. Toma prestado su namespace de red:
docker run --rm --net container:mosquitto nicolaka/netshoot \
tcpdump -i any -nn -U -w - 'tcp port 1883' > broker.pcapkubectl debug -it mosquitto-0 --image=nicolaka/netshoot --target=mosquitto -- \
tcpdump -i any -nn 'tcp port 1883'El contenedor de depuración necesita permiso para capturar (root y la capability NET_RAW); las políticas de seguridad de pods restrictivas pueden bloquearlo. Los mapeos de puertos y las redes bridge tienen sus propias trampas, tratadas en problemas de red de MQTT en Docker.
Para ver un servidor remoto en vivo en Wireshark, transmite la captura por SSH (el usuario remoto necesita sudo sin contraseña para tcpdump):
ssh admin@gateway.example "sudo tcpdump -i any -nn -U -w - 'tcp port 1883'" | wireshark -k -i -Los microcontroladores como el ESP32 no pueden ejecutar tcpdump. Captura en el gateway, en el router Wi-Fi (OpenWrt tiene un paquete de tcpdump) o en un puerto espejo del switch, filtrando por la IP del dispositivo.
Analizar la captura
tcpdump puede hacer una primera pasada sobre un archivo guardado:
# ¿Cuántos intentos de conexión, y desde dónde?
tcpdump -r mqtt.pcap -nn 'tcp[tcpflags] == tcp-syn' | awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn
# Leer payloads en texto plano alrededor de una hora concreta
tcpdump -r mqtt.pcap -nn -tttt -A | less
# Pasar a tshark para decodificar MQTT
tshark -r mqtt.pcap -Y mqtt -T fields -e frame.time -e ip.src -e mqtt.msgtype -e mqtt.topicPara cualquier cosa más allá de contar, abre el archivo en Wireshark: decodifica cada paquete MQTT y puede descifrar TLS con un key log. El flujo completo está en depurar MQTT con Wireshark.
Confirmar una solución con una captura
Una captura es también la mejor prueba de regresión para un arreglo de red. Tras cambiar el keep-alive, un timeout del firewall o un ajuste del balanceador, ejecuta el filtro de desconexiones durante más tiempo que el antiguo intervalo de fallo mientras un cliente permanece conectado, por ejemplo un suscriptor en el cliente MQTT online en testmqtt/tcpdump/#. Si no aparece ningún FIN ni RST en esa ventana, el arreglo funciona.
Mantén un suscriptor de prueba conectado mientras capturas
tcpdump convierte "la conexión se cae a veces" en marcas de tiempo, flags y direcciones IP. Captura de forma acotada, deja que los flags te digan quién cerró la conexión y pasa a Wireshark cuando necesites leer los propios paquetes MQTT.
Preguntas frecuentes
¿Cuál es el comando de tcpdump para capturar MQTT?
sudo tcpdump -i any -nn -w mqtt.pcap 'tcp port 1883 or tcp port 8883' captura MQTT sin cifrar y con TLS en todas las interfaces en un archivo que puedes abrir en Wireshark. Quita -w y añade -A para ver los payloads de MQTT sin cifrar directamente en la terminal.
¿Puede tcpdump decodificar paquetes MQTT?
No. tcpdump muestra segmentos TCP, flags y bytes en bruto, pero no tiene decodificador MQTT. Captura con tcpdump y luego abre el pcap en Wireshark o ejecuta tshark -r archivo.pcap -Y mqtt para ver CONNECT, PUBLISH y los demás paquetes MQTT.
¿Cómo ejecuto tcpdump para un broker dentro de Docker?
Conecta un contenedor de herramientas al namespace de red del broker, por ejemplo docker run --rm --net container:mosquitto nicolaka/netshoot tcpdump -i any -nn port 1883. Ve exactamente el tráfico del contenedor del broker sin instalar nada en él.



