Broker-Tests

MQTT mit Wireshark debuggen: jedes Paket lesen

MQTT-Traffic in Wireshark mitschneiden und dekodieren: Filter für CONNECT, PUBLISH und CONNACK, TLS auf Port 8883 per Key-Log entschlüsseln, tshark-Rezepte.

Aktualisiert · 9 Min. Lesezeit

MQTT mit Wireshark debuggen: jedes Paket lesen

Logs zeigen, was ein Client zu senden glaubt. Ein Paketmitschnitt zeigt, was tatsächlich über die Leitung ging. Wireshark bringt einen vollständigen MQTT-Dissector mit: Er dekodiert CONNECT-Flags, Topic-Namen, QoS, Packet Identifier, MQTT-5-Properties und Reason Codes. So siehst du in Sekunden, ob eine fehlende Nachricht nie publiziert, nie bestätigt oder nie zugestellt wurde.

Diese Anleitung zeigt, wie du MQTT-Traffic mitschneidest, welche Anzeigefilter du täglich brauchst, wie du TLS auf Port 8883 entschlüsselst, und ein paar tshark-Einzeiler für Server ohne GUI.

Den richtigen Traffic mitschneiden

Starte Wireshark auf der Maschine, auf der der Client läuft (oder der Broker, falls du ihn betreibst), und wähle das Interface, über das der Traffic läuft: deinen WLAN- oder Ethernet-Adapter für entfernte Broker, das Loopback-Interface für einen Broker auf localhost.

Nutze einen Capture-Filter, damit die Datei klein bleibt. Capture-Filter verwenden BPF-Syntax und werden vor dem Start gesetzt:

Capture-Filter
tcp port 1883                        # unverschlüsseltes MQTT
tcp port 1883 or tcp port 8883       # unverschlüsselt und TLS
host broker.connectmq.com            # alles von und zum Broker

Erzeuge dann den Traffic, den du analysieren willst — zum Beispiel einen Subscriber und einen Publisher auf einem eindeutigen Topic:

Test-Traffic erzeugen
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'

Auf einem Server ist tcpdump -w oft bequemer; die Datei öffnest du anschließend in Wireshark. Siehe MQTT mit tcpdump mitschneiden.

Die Anzeigefilter, die du brauchst

Anzeigefilter werden nach dem Mitschnitt angewendet und nutzen Wiresharks Feldnamen. Die wichtigsten Felder des MQTT-Dissectors:

ZielAnzeigefilter
Alle MQTT-Paketemqtt
Nur CONNECT / CONNACKmqtt.msgtype == 1 || mqtt.msgtype == 2
Nur PUBLISHmqtt.msgtype == 3
Ein Topic oder Topic-Präfixmqtt.topic contains "testmqtt/wireshark"
Der CONNECT eines Clientsmqtt.clientid == "sensor-01"
Abgelehnte Verbindungenmqtt.conack.val != 0
Retained Publishesmqtt.retain == 1
PUBLISH mit QoS 1 und 2mqtt.msgtype == 3 && mqtt.qos > 0
Keep-Alive-Trafficmqtt.msgtype == 12 || mqtt.msgtype == 13
Zurückgesetzte Verbindungentcp.flags.reset == 1
TCP-Retransmissionstcp.analysis.retransmission

Die numerischen Werte von mqtt.msgtype sind die MQTT-Control-Packet-Typen:

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

Typ 15 ist AUTH (nur MQTT 5). Den Aufbau auf Byte-Ebene hinter diesen Feldern erklärt MQTT-Paketstruktur.

CONNECT und CONNACK lesen

Wähle ein CONNECT-Paket und klappe MQ Telemetry Transport Protocol auf. Du siehst:

  • Protokollname und -version — MQTT mit Version 4 (3.1.1) oder 5. Akzeptiert der Broker die Version nicht, antwortet er mit einem unacceptable protocol version-Fehler.
  • Connect Flags — Clean Session / Clean Start, Will Flag, Will QoS, Will Retain und ob Username und Passwort enthalten sind.
  • Keep Alive — das Intervall in Sekunden, das der Client zugesagt hat.
  • Client ID und auf dem unverschlüsselten Port 1883 Username und Passwort im Klartext.
  • MQTT-5-Properties wie Session Expiry Interval, Receive Maximum und Maximum Packet Size.

Das folgende CONNACK enthält das Ergebnis. Bei MQTT 3.1.1 ist es ein Return Code (0 heißt akzeptiert), bei MQTT 5 ein Reason Code wie 0x86 Bad User Name or Password oder 0x87 Not authorized, manchmal mit Reason String. Jeden Wert kannst du im Reason-Code-Tool nachschlagen. Die feldgenaue Aufschlüsselung findest du unter das MQTT-CONNECT-Paket.

Kommt gar kein CONNACK und der Broker schickt stattdessen ein TCP-RST oder FIN, hat er die Verbindung abgelehnt, bevor MQTT überhaupt ins Spiel kam — oft ein TLS-Listener, der unverschlüsseltes MQTT empfängt, ein fehlerhaftes Paket oder ein Verbindungslimit.

Eine Nachricht Ende-zu-Ende verfolgen

Um zu beweisen, wo eine Nachricht verloren ging, verfolge sie durch den Mitschnitt:

  1. Filtere mqtt.topic contains "testmqtt/wireshark" und finde den PUBLISH des Publishers. Notiere bei QoS 1 oder 2 seinen Message Identifier.
  2. Filtere mqtt.msgid == <id>, um den PUBACK (QoS 1) oder die Folge PUBREC / PUBREL / PUBCOMP (QoS 2) zu sehen. Kein PUBACK heißt: Der Broker hat die Nachricht nie angenommen — prüfe ACLs und Payload-Größe.
  3. Hast du auch auf Subscriber-Seite mitgeschnitten, suche den PUBLISH, den der Broker ausgeliefert hat. Der Packet Identifier ist ein anderer, denn jeder Hop hat seinen eigenen.

Statistics → Flow Graph mit „Displayed packets" zeichnet den Austausch als Sequenzdiagramm — der schnellste Weg, eine fehlende Bestätigung zu entdecken.

MQTT über TLS entschlüsseln (Port 8883)

Auf Port 8883 siehst du nur TLS-Records, bis Wireshark die Sitzungsschlüssel hat. Der Standardweg dorthin ist eine Key-Log-Datei, die viele TLS-Bibliotheken schreiben können.

Python (paho-mqtt 2.x) — setze keylog_filename am SSL-Kontext:

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) — nutze das eingebaute Flag, ohne Codeänderung:

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

Browser und curl beachten die Umgebungsvariable SSLKEYLOGFILE. Öffne dann in Wireshark Edit → Preferences → Protocols → TLS und trage unter (Pre)-Master-Secret log filename den Pfad zum Key-Log ein. Die TLS-Records auf 8883 werden zu dekodierten MQTT-Paketen, bei TLS 1.2 wie bei 1.3.

Schlägt schon der TLS-Handshake fehl, brauchst du keine Entschlüsselung: Der Alert-Record (etwa Unknown CA oder Handshake Failure) ist offen sichtbar. Siehe TLS handshake failed.

Nicht-Standard-Ports und WebSockets

Wireshark nimmt MQTT nur auf den registrierten Ports an. Lauscht dein Broker etwa auf 11883, klicke mit rechts auf ein Paket, wähle Decode As… und ordne diesen TCP-Port MQTT zu.

Bei MQTT über WebSockets (8083 / 8084) zeigt Wireshark den HTTP-Upgrade-Request und die WebSocket-Frames; die MQTT-Pakete stecken in binären Frames. Bei Browser-Apps ist es meist schneller, diese Frames direkt im Browser zu lesen — siehe MQTT in Chrome DevTools debuggen.

tshark-Einzeiler

tshark ist der Kommandozeilen-Zwilling von Wireshark und nutzt dieselben Filter:

tshark-Rezepte
# Live: Zeit, Pakettyp, Topic und Payload ausgeben
tshark -i any -f "tcp port 1883" -Y mqtt \
  -T fields -e frame.time_relative -e mqtt.msgtype -e mqtt.topic -e mqtt.msg

# Aus einer Datei: PUBLISH-Pakete pro Topic zählen
tshark -r mqtt.pcap -Y "mqtt.msgtype == 3" -T fields -e mqtt.topic | sort | uniq -c | sort -rn

# Abstände zwischen Keep-Alive-Pings (auf Werte nahe am Keep-Alive-Limit achten)
tshark -r mqtt.pcap -Y "mqtt.msgtype == 12" -T fields -e frame.time_delta_displayed

Der letzte hilft bei Keep-Alive-Timeouts: Übersteigen die Abstände das 1,5-Fache des Keep-Alive-Werts, darf der Broker den Client trennen.

Worauf du je nach Symptom achtest

SymptomWas der Mitschnitt zeigt
Client „verbindet nicht"CONNECT, gefolgt von CONNACK mit Code ungleich null, oder RST ohne CONNACK
Nachrichten bei QoS 1 verlorenPUBLISH ohne PUBACK, nach dem Reconnect ein Duplikat (DUP-Flag gesetzt)
Zufällige VerbindungsabbrücheLücken bei PINGREQ, TCP-Retransmissions, dann FIN oder RST von einer Seite
Große Nachrichten verschwindenGroßer PUBLISH, danach schließt der Broker die Verbindung — mit der Maximum Packet Size vergleichen und Größen im Paketgrößen-Rechner prüfen
Subscriber empfängt nichtsSUBACK mit Fehlercode 0x80 oder ein Topic-Filter, der nicht passt

Um einen Fall schnell nachzustellen, starte den Mitschnitt, während du aus dem Online-MQTT-Client publizierst, und sieh den Paketen in Echtzeit zu.

Wireshark ersetzt Raten durch Belege. Schneide gezielt mit, filtere nach Pakettyp und Topic, entschlüssele TLS nur bei Bedarf und bewahre einen Mitschnitt einer funktionierenden Sitzung auf — der Vergleich eines defekten Mitschnitts mit einem guten ist oft die schnellste Diagnose überhaupt.

Häufig gestellte Fragen

Wie lautet der Wireshark-Anzeigefilter für MQTT?

Mit 'mqtt' siehst du alle dekodierten MQTT-Pakete. Eingrenzen kannst du mit Feldern wie mqtt.msgtype == 3 für PUBLISH, mqtt.topic contains "sensors" für ein Topic oder mqtt.clientid == "sensor-01" für den CONNECT eines Clients.

Warum zeigt Wireshark auf Port 8883 TLS statt MQTT?

Die MQTT-Pakete sind verschlüsselt. Wireshark kann sie nur dekodieren, wenn du die TLS-Sitzungsschlüssel lieferst: Lass den Client eine Key-Log-Datei schreiben (SSLKEYLOGFILE, keylog_filename in Python, --tls-keylog in Node) und trage diese Datei in den TLS-Einstellungen von Wireshark ein.

Warum erscheint mein MQTT-Traffic nur als rohe TCP-Daten?

Wireshark dekodiert MQTT automatisch nur auf dem Standardport 1883 (und 8883 nach der Entschlüsselung). Für jeden anderen Port: Rechtsklick auf ein Paket, Decode As wählen und den TCP-Port auf MQTT setzen.