Tests de brokers

Déboguer MQTT avec Wireshark : lire chaque paquet

Capturez et décodez le trafic MQTT dans Wireshark : filtres CONNECT, PUBLISH et CONNACK, déchiffrement TLS sur le port 8883 via un key log et recettes tshark.

Mis à jour · 9 min de lecture

Déboguer MQTT avec Wireshark : lire chaque paquet

Les logs disent ce qu'un client pense avoir envoyé. Une capture réseau montre ce qui est vraiment passé sur le fil. Wireshark intègre un dissecteur MQTT complet : il décode les flags du CONNECT, les noms de topic, le QoS, les packet identifiers, les propriétés MQTT 5 et les reason codes. Vous voyez en quelques secondes si un message manquant n'a jamais été publié, jamais acquitté ou jamais livré.

Ce guide couvre la capture du trafic MQTT, les filtres d'affichage que vous utiliserez tous les jours, le déchiffrement de TLS sur le port 8883 et quelques commandes tshark pour les serveurs sans interface graphique.

Capturer le bon trafic

Lancez Wireshark sur la machine qui exécute le client (ou le broker, si vous le maîtrisez) et choisissez l'interface qui porte le trafic : l'adaptateur Wi-Fi ou Ethernet pour un broker distant, l'interface loopback pour un broker sur localhost.

Utilisez un filtre de capture pour garder un fichier léger. Les filtres de capture utilisent la syntaxe BPF et se définissent avant de démarrer :

Filtres de capture
tcp port 1883                        # MQTT en clair
tcp port 1883 or tcp port 8883       # en clair et TLS
host broker.connectmq.com            # tout le trafic vers et depuis le broker

Générez ensuite le trafic à analyser, par exemple un subscriber et un publisher sur un topic unique :

Générer du trafic de test
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'

Sur un serveur, tcpdump -w est souvent plus pratique ; vous ouvrez ensuite le fichier dans Wireshark. Voir capturer MQTT avec tcpdump.

Les filtres d'affichage indispensables

Les filtres d'affichage s'appliquent après la capture et utilisent les noms de champs de Wireshark. Les principaux champs du dissecteur MQTT :

ObjectifFiltre d'affichage
Tous les paquets MQTTmqtt
Seulement CONNECT / CONNACKmqtt.msgtype == 1 || mqtt.msgtype == 2
Seulement PUBLISHmqtt.msgtype == 3
Un topic ou un préfixe de topicmqtt.topic contains "testmqtt/wireshark"
Le CONNECT d'un clientmqtt.clientid == "sensor-01"
Connexions refuséesmqtt.conack.val != 0
Publications retenuesmqtt.retain == 1
PUBLISH en QoS 1 et 2mqtt.msgtype == 3 && mqtt.qos > 0
Trafic keep-alivemqtt.msgtype == 12 || mqtt.msgtype == 13
Connexions réinitialiséestcp.flags.reset == 1
Retransmissions TCPtcp.analysis.retransmission

Les valeurs numériques de mqtt.msgtype correspondent aux types de paquets de contrôle MQTT :

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

Le type 15 est AUTH (MQTT 5 uniquement). Pour la structure au niveau des octets derrière ces champs, voir structure des paquets MQTT.

Lire un CONNECT et un CONNACK

Sélectionnez un paquet CONNECT et dépliez MQ Telemetry Transport Protocol. Vous verrez :

  • Le nom et la version du protocole — MQTT en version 4 (3.1.1) ou 5. Si le broker n'accepte pas la version, il répond par une erreur unacceptable protocol version.
  • Les connect flags — clean session / clean start, will flag, will QoS, will retain, et la présence d'un username et d'un password.
  • Le keep alive — l'intervalle en secondes promis par le client.
  • Le Client ID et, sur le port 1883 en clair, le username et le password en texte lisible.
  • Les propriétés MQTT 5, comme session expiry interval, receive maximum et maximum packet size.

Le CONNACK qui suit porte le résultat. En MQTT 3.1.1 c'est un return code (0 signifie accepté) ; en MQTT 5 c'est un reason code comme 0x86 Bad User Name or Password ou 0x87 Not authorized, parfois accompagné d'un reason string. Consultez n'importe quelle valeur avec l'outil des reason codes. Le détail champ par champ se trouve dans le paquet CONNECT de MQTT.

S'il n'y a aucun CONNACK et que le broker envoie un RST ou un FIN TCP, il a rejeté la connexion avant même d'arriver à MQTT — souvent un listener TLS qui reçoit du MQTT en clair, un paquet malformé ou une limite de connexions.

Suivre un message de bout en bout

Pour prouver où un message s'est perdu, suivez-le dans la capture :

  1. Filtrez mqtt.topic contains "testmqtt/wireshark" et trouvez le PUBLISH du publisher. Notez son Message Identifier en QoS 1 ou 2.
  2. Filtrez mqtt.msgid == <id> pour voir le PUBACK (QoS 1) ou la séquence PUBREC / PUBREL / PUBCOMP (QoS 2). Pas de PUBACK : le broker ne l'a jamais accepté — vérifiez les ACL et la taille du payload.
  3. Si vous avez aussi capturé côté subscriber, cherchez le PUBLISH livré par le broker. Le packet identifier est différent, car chaque saut a le sien.

Statistics → Flow Graph avec « Displayed packets » dessine l'échange sous forme de diagramme de séquence : le moyen le plus rapide de repérer un acquittement manquant.

Déchiffrer MQTT sur TLS (port 8883)

Sur le port 8883, vous ne verrez que des enregistrements TLS tant que Wireshark n'a pas les clés de session. La méthode standard pour les obtenir est un fichier de key log, que beaucoup de bibliothèques TLS savent écrire.

Python (paho-mqtt 2.x) — définissez keylog_filename sur le contexte 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) — utilisez l'option intégrée, sans modifier le code :

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

Les navigateurs et curl respectent la variable d'environnement SSLKEYLOGFILE. Dans Wireshark, ouvrez ensuite Edit → Preferences → Protocols → TLS et renseignez le chemin du key log dans (Pre)-Master-Secret log filename. Les enregistrements TLS du port 8883 deviennent des paquets MQTT décodés, en TLS 1.2 comme en 1.3.

Si c'est le handshake TLS lui-même qui échoue, inutile de déchiffrer : l'enregistrement d'alerte (par exemple Unknown CA ou Handshake Failure) est visible en clair. Voir TLS handshake failed.

Ports non standards et WebSockets

Wireshark ne suppose MQTT que sur ses ports enregistrés. Si votre broker écoute par exemple sur 11883, faites un clic droit sur un paquet, choisissez Decode As… et associez ce port TCP à MQTT.

Pour MQTT sur WebSockets (8083 / 8084), Wireshark affiche la requête HTTP d'upgrade et les frames WebSocket ; les paquets MQTT sont dans des frames binaires. Pour les applications web, il est souvent plus rapide de lire ces frames dans le navigateur lui-même — voir déboguer MQTT dans Chrome DevTools.

Recettes tshark

tshark est le jumeau en ligne de commande de Wireshark et utilise les mêmes filtres :

Recettes tshark
# En direct : affiche l'heure, le type de paquet, le topic et le 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

# Depuis un fichier : compte les PUBLISH par topic
tshark -r mqtt.pcap -Y "mqtt.msgtype == 3" -T fields -e mqtt.topic | sort | uniq -c | sort -rn

# Écarts entre pings keep-alive (cherchez des valeurs proches de la limite de keep-alive)
tshark -r mqtt.pcap -Y "mqtt.msgtype == 12" -T fields -e frame.time_delta_displayed

La dernière est utile pour les timeouts de keep-alive : si les écarts dépassent 1,5 fois la valeur du keep-alive, le broker est en droit de déconnecter le client.

Que chercher selon le symptôme

SymptômeCe que montre la capture
Le client « ne se connecte pas »CONNECT suivi d'un CONNACK avec un code non nul, ou un RST sans CONNACK
Messages perdus en QoS 1PUBLISH sans PUBACK, puis un doublon (flag DUP) après reconnexion
Déconnexions aléatoiresTrous dans les PINGREQ, retransmissions TCP, puis FIN ou RST d'un côté
Les gros messages disparaissentGros PUBLISH suivi de la fermeture de la connexion par le broker — comparez avec le maximum packet size et vérifiez les tailles avec le calculateur de taille de paquet
Le subscriber ne reçoit rienSUBACK avec le code d'échec 0x80 ou un topic filter qui ne correspond pas

Pour reproduire un cas rapidement, lancez la capture pendant que vous publiez depuis le client MQTT en ligne et regardez les paquets arriver en temps réel.

Wireshark remplace les suppositions par des preuves. Capturez de façon ciblée, filtrez par type de paquet et par topic, ne déchiffrez TLS que si nécessaire, et gardez une capture d'une session qui fonctionne : comparer une capture défaillante à une bonne est souvent le diagnostic le plus rapide.

Questions fréquentes

Quel est le filtre d'affichage Wireshark pour MQTT ?

Utilisez 'mqtt' pour afficher tous les paquets MQTT décodés. Affinez avec des champs comme mqtt.msgtype == 3 pour PUBLISH, mqtt.topic contains "sensors" pour un topic, ou mqtt.clientid == "sensor-01" pour le CONNECT d'un client.

Pourquoi Wireshark affiche-t-il du TLS au lieu de MQTT sur le port 8883 ?

Les paquets MQTT sont chiffrés. Wireshark ne peut les décoder que si vous lui fournissez les clés de session TLS : faites écrire au client un fichier de key log (SSLKEYLOGFILE, keylog_filename en Python, --tls-keylog avec Node) et indiquez ce fichier dans les préférences TLS de Wireshark.

Pourquoi mon trafic MQTT apparaît-il comme de simples données TCP ?

Wireshark ne décode MQTT automatiquement que sur le port standard 1883 (et 8883 après déchiffrement). Pour tout autre port, faites un clic droit sur un paquet, choisissez Decode As et associez le port TCP à MQTT.