Testes de brokers

Depurando MQTT com Wireshark: leia cada pacote

Capture e decodifique tráfego MQTT no Wireshark: filtros para CONNECT, PUBLISH e CONNACK, descriptografia de TLS na porta 8883 com key log e receitas de tshark.

Atualizado · 9 min de leitura

Depurando MQTT com Wireshark: leia cada pacote

Logs mostram o que um cliente acha que enviou. Uma captura de pacotes mostra o que realmente passou pelo fio. O Wireshark tem um dissector MQTT completo: ele decodifica os flags do CONNECT, nomes de topic, QoS, packet identifiers, propriedades do MQTT 5 e reason codes, então em segundos você vê se uma mensagem perdida nunca foi publicada, nunca foi confirmada ou nunca foi entregue.

Este guia cobre a captura de tráfego MQTT, os filtros de exibição que você vai usar todo dia, a descriptografia de TLS na porta 8883 e alguns comandos tshark para quando você estiver em um servidor sem interface gráfica.

Capture o tráfego certo

Abra o Wireshark na máquina que roda o cliente (ou o broker, se você o controla) e escolha a interface por onde passa o tráfego: o adaptador Wi-Fi ou Ethernet para brokers remotos, a interface de loopback para um broker em localhost.

Use um filtro de captura para manter o arquivo pequeno. Filtros de captura usam a sintaxe BPF e são definidos antes de iniciar:

Filtros de captura
tcp port 1883                        # MQTT sem criptografia
tcp port 1883 or tcp port 8883       # sem criptografia e TLS
host broker.connectmq.com            # tudo de e para o broker

Depois gere o tráfego que deseja analisar — por exemplo, um subscriber e um publisher em um topic exclusivo:

Gerar tráfego de teste
mosquitto_sub -h broker.connectmq.com -p 1883 -t 'testmqtt/wireshark/#' -q 1 -v &
mosquitto_pub -h broker.connectmq.com -p 1883 -t 'testmqtt/wireshark/hello' -q 1 -m 'ping'

Se você captura em um servidor, tcpdump -w costuma ser mais prático, e você abre o arquivo no Wireshark depois; veja capturar MQTT com tcpdump.

Os filtros de exibição de que você precisa

Filtros de exibição são aplicados depois da captura e usam os nomes de campo do Wireshark. Os principais campos do dissector MQTT:

ObjetivoFiltro de exibição
Todos os pacotes MQTTmqtt
Só CONNECT / CONNACKmqtt.msgtype == 1 || mqtt.msgtype == 2
Só PUBLISHmqtt.msgtype == 3
Um topic ou prefixo de topicmqtt.topic contains "testmqtt/wireshark"
O CONNECT de um clientemqtt.clientid == "sensor-01"
Conexões recusadasmqtt.conack.val != 0
Publicações retidasmqtt.retain == 1
PUBLISH com QoS 1 e 2mqtt.msgtype == 3 && mqtt.qos > 0
Tráfego de keep-alivemqtt.msgtype == 12 || mqtt.msgtype == 13
Conexões resetadastcp.flags.reset == 1
Retransmissões TCPtcp.analysis.retransmission

Os valores numéricos de mqtt.msgtype são os tipos de pacote de controle do MQTT:

ValorPacoteValorPacote
1CONNECT8SUBSCRIBE
2CONNACK9SUBACK
3PUBLISH10UNSUBSCRIBE
4PUBACK11UNSUBACK
5PUBREC12PINGREQ
6PUBREL13PINGRESP
7PUBCOMP14DISCONNECT

O tipo 15 é AUTH (só MQTT 5). Para o layout em bytes por trás desses campos, veja estrutura dos pacotes MQTT.

Lendo um CONNECT e um CONNACK

Selecione um pacote CONNECT e expanda MQ Telemetry Transport Protocol. Você verá:

  • Nome e versão do protocolo — MQTT com versão 4 (3.1.1) ou 5. Se o broker não aceita a versão, ele responde com um erro de unacceptable protocol version.
  • Connect flags — clean session / clean start, will flag, will QoS, will retain e se há username e password.
  • Keep alive — o intervalo em segundos que o cliente prometeu.
  • Client ID e, na porta 1883 sem criptografia, o username e o password em texto puro.
  • Propriedades do MQTT 5, como session expiry interval, receive maximum e maximum packet size.

O CONNACK seguinte traz o resultado. No MQTT 3.1.1 é um return code (0 significa aceito); no MQTT 5 é um reason code como 0x86 Bad User Name or Password ou 0x87 Not authorized, às vezes com um reason string. Consulte qualquer valor na ferramenta de reason codes. O detalhamento campo a campo está em o pacote CONNECT do MQTT.

Se não houver CONNACK e o broker mandar um RST ou FIN TCP, ele rejeitou a conexão antes mesmo de chegar ao MQTT — muitas vezes um listener TLS recebendo MQTT sem criptografia, um pacote malformado ou um limite de conexões.

Seguindo uma mensagem de ponta a ponta

Para provar onde uma mensagem se perdeu, siga-a pela captura:

  1. Filtre mqtt.topic contains "testmqtt/wireshark" e encontre o PUBLISH do publisher. Anote o Message Identifier se for QoS 1 ou 2.
  2. Filtre mqtt.msgid == <id> para ver o PUBACK (QoS 1) ou a sequência PUBREC / PUBREL / PUBCOMP (QoS 2). Sem PUBACK, o broker nunca aceitou a mensagem — verifique ACLs e o tamanho do payload.
  3. Se você também capturou do lado do subscriber, procure o PUBLISH que o broker entregou. O packet identifier é diferente, porque cada salto tem o seu.

Statistics → Flow Graph com "Displayed packets" desenha a troca como um diagrama de sequência — a forma mais rápida de achar uma confirmação faltando.

Descriptografando MQTT sobre TLS (porta 8883)

Na porta 8883 você só verá registros TLS até o Wireshark ter as chaves de sessão. A forma padrão de obtê-las é um arquivo de key log, que muitas bibliotecas TLS conseguem gravar.

Python (paho-mqtt 2.x) — defina keylog_filename no contexto SSL:

tls_keylog.py
import ssl
import paho.mqtt.client as mqtt

ctx = ssl.create_default_context()
ctx.keylog_filename = "/tmp/mqtt-tls-keys.log"   # Python 3.8+

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.tls_set_context(ctx)
client.connect("broker.connectmq.com", 8883)
client.loop_start()
client.publish("testmqtt/wireshark/tls", "hello over TLS", qos=1).wait_for_publish()
client.loop_stop()
client.disconnect()

Node.js (MQTT.js) — use o flag nativo, sem mudar o código:

Key log no Node.js
node --tls-keylog=/tmp/mqtt-tls-keys.log publish.js

Navegadores e curl respeitam a variável de ambiente SSLKEYLOGFILE. Depois, no Wireshark, abra Edit → Preferences → Protocols → TLS e defina (Pre)-Master-Secret log filename com o caminho do key log. Os registros TLS na 8883 viram pacotes MQTT decodificados, tanto em TLS 1.2 quanto em 1.3.

Se o próprio handshake TLS falha, você não precisa descriptografar: o registro de alerta (por exemplo Unknown CA ou Handshake Failure) fica visível. Veja TLS handshake failed.

Portas não padrão e WebSockets

O Wireshark só assume MQTT nas portas registradas. Se seu broker escuta, por exemplo, na 11883, clique com o botão direito em um pacote, escolha Decode As… e associe essa porta TCP ao MQTT.

Para MQTT sobre WebSockets (8083 / 8084), o Wireshark mostra a requisição HTTP de upgrade e os frames WebSocket; os pacotes MQTT ficam dentro de frames binários. Em apps de navegador, costuma ser mais rápido ler esses frames no próprio navegador — veja depurar MQTT no Chrome DevTools.

Receitas de tshark

O tshark é o gêmeo de linha de comando do Wireshark e usa os mesmos filtros:

Receitas de tshark
# Ao vivo: imprime tempo, tipo de pacote, topic e payload
tshark -i any -f "tcp port 1883" -Y mqtt \
  -T fields -e frame.time_relative -e mqtt.msgtype -e mqtt.topic -e mqtt.msg

# De um arquivo: conta os PUBLISH por topic
tshark -r mqtt.pcap -Y "mqtt.msgtype == 3" -T fields -e mqtt.topic | sort | uniq -c | sort -rn

# Intervalos entre pings de keep-alive (procure valores próximos ao limite de keep-alive)
tshark -r mqtt.pcap -Y "mqtt.msgtype == 12" -T fields -e frame.time_delta_displayed

A última é útil para timeouts de keep-alive: se os intervalos passarem de 1,5 vez o valor de keep-alive, o broker tem o direito de desconectar o cliente.

O que procurar, por sintoma

SintomaO que a captura mostra
Cliente "não conecta"CONNECT seguido de CONNACK com código diferente de zero, ou RST sem CONNACK
Mensagens perdidas em QoS 1PUBLISH sem PUBACK e, após reconectar, uma duplicata (flag DUP ativo)
Desconexões aleatóriasLacunas nos PINGREQ, retransmissões TCP e depois FIN ou RST de um dos lados
Mensagens grandes somemUm PUBLISH grande seguido do broker fechando a conexão — compare com o maximum packet size e confira tamanhos na calculadora de tamanho de pacote
Subscriber não recebe nadaSUBACK com código de falha 0x80 ou um topic filter que não casa

Para reproduzir um caso rapidamente, rode a captura enquanto publica pelo cliente MQTT online e veja os pacotes chegando em tempo real.

O Wireshark troca suposições por evidências. Capture de forma restrita, filtre por tipo de pacote e topic, descriptografe TLS só quando precisar e guarde uma captura de uma sessão que funciona — comparar uma captura com problema com uma boa costuma ser o diagnóstico mais rápido de todos.

Perguntas frequentes

Qual é o filtro de exibição do Wireshark para MQTT?

Use 'mqtt' para mostrar todos os pacotes MQTT decodificados. Refine com campos como mqtt.msgtype == 3 para PUBLISH, mqtt.topic contains "sensors" para um topic, ou mqtt.clientid == "sensor-01" para o CONNECT de um cliente.

Por que o Wireshark mostra TLS em vez de MQTT na porta 8883?

Os pacotes MQTT estão criptografados. O Wireshark só consegue decodificá-los se você fornecer as chaves de sessão TLS: faça o cliente gravar um arquivo de key log (SSLKEYLOGFILE, keylog_filename no Python, --tls-keylog no Node) e aponte as preferências de TLS do Wireshark para esse arquivo.

Por que meu tráfego MQTT aparece como dados TCP comuns?

O Wireshark decodifica MQTT automaticamente apenas na porta padrão 1883 (e na 8883 após descriptografar). Para qualquer outra porta, clique com o botão direito em um pacote, escolha Decode As e associe a porta TCP ao MQTT.