Tests de brokers

Capturer le trafic MQTT avec tcpdump

Recettes tcpdump pour MQTT sur serveurs, conteneurs et passerelles : filtres par port et type de paquet, captures tournantes, déconnexions et analyse pcap.

Mis à jour · 8 min de lecture

Capturer le trafic MQTT avec tcpdump

tcpdump est l'outil de capture déjà installé — ou à un paquet près — sur presque tous les serveurs Linux, routeurs et passerelles. Il ne comprend pas MQTT, mais il n'en a pas besoin : capturer la conversation TCP sur le port 1883 ou 8883 suffit pour prouver qui s'est connecté, qui a fermé la connexion et si des paquets se sont perdus en route. Quand vous avez besoin des détails MQTT, vous ouvrez le même fichier dans Wireshark.

Ce guide rassemble des recettes tcpdump pour MQTT : captures de base, filtres BPF qui reconnaissent les types de paquets MQTT, captures dans Docker et Kubernetes, tampons tournants de longue durée et analyse de ce que vous avez capturé.

La capture MQTT de base

Capturer MQTT sur toutes les interfaces
# Une ligne par segment (MQTT en clair et TLS)
sudo tcpdump -i any -nn 'tcp port 1883 or tcp port 8883'

# Écrire dans un fichier pour Wireshark
sudo tcpdump -i any -nn -w mqtt.pcap 'tcp port 1883 or tcp port 8883'

# Seulement le trafic d'un appareil
sudo tcpdump -i any -nn 'host 192.168.1.50 and tcp port 1883'

Les options que vous utiliserez le plus :

OptionSignification
-i any / -i eth0Interface de capture (tcpdump -D les liste)
-nnNe pas résoudre les noms d'hôtes ni de ports — plus rapide et sans ambiguïté
-w file.pcapÉcrire les paquets bruts dans un fichier au lieu de les afficher
-r file.pcapLire un fichier de capture
-A / -XAfficher le payload en ASCII / en hexadécimal et ASCII
-c 200S'arrêter après 200 paquets
-UÉcrire chaque paquet immédiatement (nécessaire vers un pipe)
-ttttDate et heure complètes sur chaque ligne

Pour voir tout de suite quelque chose d'utile, publiez sur un topic de test depuis un autre terminal et observez le payload en ASCII :

Observer un message de test sur le fil
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'

Vous verrez le topic testmqtt/tcpdump/hello et le payload en clair dans le segment PUBLISH. Sur le port 8883, vous ne verrez que des octets chiffrés — c'est tout l'intérêt de TLS.

Où capturer

L'endroit où vous capturez détermine ce que vous pouvez prouver. Une capture sur le client montre ce que l'appareil a réellement envoyé et ce qui est revenu, y compris les retransmissions que l'application ne remarque jamais. Une capture sur l'hôte du broker montre ce qui est vraiment arrivé, après le passage du NAT, des load balancers et des proxys. Si les deux divergent, le problème est entre les deux.

Pour un problème tenace, capturez des deux côtés en même temps avec des horodatages -tttt et comparez. Si le client envoie un PUBLISH qui n'apparaît jamais côté broker, examinez le chemin réseau ; si le broker le reçoit et que le subscriber ne l'obtient jamais, regardez plutôt les ACL, les files d'attente et les logs du broker.

Lire la sortie de tcpdump

Une connexion MQTT typique ressemble à ceci (adresses abrégées) :

Une courte session MQTT
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.], ...

Les flags TCP portent l'essentiel du diagnostic :

  • [S] sans réponse [S.] — le SYN n'a jamais obtenu de réponse : pare-feu, routage ou mauvaise IP. Voir diagnostiquer les problèmes réseau MQTT.
  • [S] suivi d'un [R.] — l'hôte est actif, mais rien n'écoute sur le port (Connection refused).
  • [F.] — une fermeture ordonnée. Le côté qui envoie le premier FIN est celui qui a décidé de terminer la session.
  • [R] — une réinitialisation brutale. Si elle vient d'une adresse qui n'est ni le client ni le broker, un équipement intermédiaire a tué la connexion.
  • Le même segment [P.] répété après un délai — une retransmission, donc une perte sur le chemin.

Filtrer par type de paquet MQTT

Le premier octet de chaque paquet MQTT encode son type dans les quatre bits de poids fort. Une expression BPF peut lire cet octet au début du payload TCP, ce qui permet à tcpdump de ne garder que certains types de paquets :

TypePremier octet (masqué par 0xf0)
CONNECT0x10
CONNACK0x20
PUBLISH0x30
SUBSCRIBE0x80
PINGREQ / PINGRESP0xc0 / 0xd0
DISCONNECT0xe0
Uniquement les paquets CONNECT et CONNACK (MQTT en clair, IPv4)
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 est la longueur de l'en-tête TCP : l'expression lit donc le premier octet du payload. La condition tcp-push écarte les segments ACK vides. Deux limites à garder en tête : le filtre ne voit que le premier paquet MQTT de chaque segment (plusieurs petits paquets peuvent en partager un), et les offsets tcp[] ne fonctionnent qu'en IPv4. C'est un filtre pour restreindre une capture, pas un décodeur — pour des réponses exactes, utilisez le filtre mqtt.msgtype de Wireshark.

Repérer déconnexions et reconnexions

Deux filtres courts répondent à la question la plus fréquente en production — « pourquoi ce client se déconnecte-t-il sans arrêt ? » :

Événements du cycle de vie de la connexion
# Chaque nouvelle tentative de connexion (SYN sans ACK)
sudo tcpdump -i any -nn 'tcp port 1883 and tcp[tcpflags] == tcp-syn'

# Chaque fermeture ou réinitialisation, horodatée
sudo tcpdump -i any -nn -tttt 'tcp port 1883 and tcp[tcpflags] & (tcp-fin|tcp-rst) != 0'

Lisez-les ensemble :

  • Un FIN venant du broker environ un intervalle et demi de keep-alive après le dernier paquet du client signifie que le client a cessé d'envoyer des PINGREQ — voir keep alive MQTT.
  • Un RST après une longue inactivité, souvent toujours au même intervalle, indique un timeout de NAT ou de pare-feu — voir timeout NAT avec MQTT.
  • Des centaines de SYN par seconde venant de nombreux appareils après une panne, c'est une tempête de reconnexions.
  • Un FIN du broker juste après un nouveau SYN depuis une autre adresse avec le même client ID signale une prise de contrôle de session. Donnez à chaque client de test un ID distinct avec le générateur de client ID.

Captures de longue durée

Les problèmes intermittents demandent des captures qui tournent des heures sans remplir le disque. tcpdump sait faire tourner les fichiers lui-même :

Tampon circulaire et fichiers horaires
# 10 fichiers d'environ 100 Mo, le plus ancien est écrasé
sudo tcpdump -i any -nn -w /var/tmp/mqtt.pcap -C 100 -W 10 'tcp port 1883 or tcp port 8883'

# Un fichier par heure, nommé selon l'heure
sudo tcpdump -i any -nn -G 3600 -w '/var/tmp/mqtt-%Y%m%d-%H%M.pcap' 'tcp port 1883'

Lancez-le dans tmux ou comme unité systemd temporaire, notez l'heure à laquelle le problème survient et ne récupérez que le fichier concerné.

Conteneurs, Kubernetes et hôtes distants

Un conteneur de broker n'embarque généralement pas tcpdump. Empruntez plutôt son namespace réseau :

Docker : capturer le trafic du conteneur du broker
docker run --rm --net container:mosquitto nicolaka/netshoot \
  tcpdump -i any -nn -U -w - 'tcp port 1883' > broker.pcap
Kubernetes : conteneur de débogage éphémère
kubectl debug -it mosquitto-0 --image=nicolaka/netshoot --target=mosquitto -- \
  tcpdump -i any -nn 'tcp port 1883'

Le conteneur de débogage a besoin du droit de capturer (root et la capability NET_RAW) ; des politiques de sécurité de pods restrictives peuvent le bloquer. Les mappages de ports et les réseaux bridge ont leurs propres pièges, traités dans problèmes réseau MQTT avec Docker.

Pour suivre un serveur distant en direct dans Wireshark, faites passer la capture par SSH (l'utilisateur distant doit pouvoir lancer tcpdump avec sudo sans mot de passe) :

Capture distante en direct dans Wireshark
ssh admin@gateway.example "sudo tcpdump -i any -nn -U -w - 'tcp port 1883'" | wireshark -k -i -

Les microcontrôleurs comme l'ESP32 ne peuvent pas exécuter tcpdump. Capturez sur la passerelle, le routeur Wi-Fi (OpenWrt propose un paquet tcpdump) ou un port miroir du switch, filtré sur l'IP de l'appareil.

Analyser la capture

tcpdump peut faire un premier passage sur un fichier enregistré :

Analyse rapide d'un pcap
# Combien de tentatives de connexion, et d'où ?
tcpdump -r mqtt.pcap -nn 'tcp[tcpflags] == tcp-syn' | awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn

# Lire les payloads en clair autour d'une heure donnée
tcpdump -r mqtt.pcap -nn -tttt -A | less

# Passer la main à tshark pour décoder MQTT
tshark -r mqtt.pcap -Y mqtt -T fields -e frame.time -e ip.src -e mqtt.msgtype -e mqtt.topic

Pour tout ce qui dépasse le simple comptage, ouvrez le fichier dans Wireshark : il décode chaque paquet MQTT et peut déchiffrer TLS avec un key log. La démarche complète est décrite dans déboguer MQTT avec Wireshark.

Confirmer une correction par une capture

Une capture est aussi le meilleur test de non-régression pour une correction réseau. Après avoir modifié le keep-alive, un timeout de pare-feu ou un réglage de load balancer, laissez tourner le filtre de déconnexion plus longtemps que l'ancien intervalle de panne pendant qu'un client reste connecté — par exemple un subscriber dans le client MQTT en ligne sur testmqtt/tcpdump/#. Aucun FIN ni RST dans cette fenêtre : la correction tient.

Gardez un subscriber de test connecté pendant la capture

tcpdump transforme « la connexion tombe parfois » en horodatages, flags et adresses IP. Capturez de façon ciblée, laissez les flags vous dire qui a fermé la connexion et passez à Wireshark quand vous devez lire les paquets MQTT eux-mêmes.

Questions fréquentes

Quelle commande tcpdump utiliser pour capturer MQTT ?

sudo tcpdump -i any -nn -w mqtt.pcap 'tcp port 1883 or tcp port 8883' capture MQTT en clair et en TLS sur toutes les interfaces dans un fichier que vous pouvez ouvrir dans Wireshark. Retirez -w et ajoutez -A pour afficher directement dans le terminal les payloads MQTT en clair.

tcpdump sait-il décoder les paquets MQTT ?

Non. tcpdump affiche les segments TCP, les flags et les octets bruts, mais n'a pas de décodeur MQTT. Capturez avec tcpdump, puis ouvrez le pcap dans Wireshark ou lancez tshark -r fichier.pcap -Y mqtt pour voir CONNECT, PUBLISH et les autres paquets MQTT.

Comment lancer tcpdump pour un broker dans Docker ?

Attachez un conteneur d'outils au namespace réseau du broker, par exemple docker run --rm --net container:mosquitto nicolaka/netshoot tcpdump -i any -nn port 1883. Il voit exactement le trafic du conteneur du broker sans rien y installer.