Broker-Tests

MQTT-Traffic mit tcpdump mitschneiden

tcpdump-Rezepte für MQTT auf Servern, Containern und Gateways: Port- und Pakettyp-Filter, rotierende Mitschnitte, Verbindungsabbrüche und pcap-Analyse.

Aktualisiert · 8 Min. Lesezeit

MQTT-Traffic mit tcpdump mitschneiden

tcpdump ist das Mitschnitt-Tool, das auf fast jedem Linux-Server, Router und Gateway schon installiert ist — oder nur ein Paket entfernt. Es versteht kein MQTT, muss es aber auch nicht: Der Mitschnitt der TCP-Unterhaltung auf Port 1883 oder 8883 reicht, um zu beweisen, wer sich verbunden hat, wer die Verbindung geschlossen hat und ob unterwegs Pakete verloren gingen. Brauchst du die MQTT-Details, öffnest du dieselbe Datei in Wireshark.

Diese Anleitung ist eine Sammlung von tcpdump-Rezepten für MQTT: einfache Mitschnitte, BPF-Filter für MQTT-Pakettypen, Mitschnitte in Docker und Kubernetes, lang laufende Ringpuffer und die Analyse dessen, was du eingefangen hast.

Der einfache MQTT-Mitschnitt

MQTT auf allen Interfaces mitschneiden
# Eine Zeile pro Segment (unverschlüsseltes MQTT und TLS)
sudo tcpdump -i any -nn 'tcp port 1883 or tcp port 8883'

# Stattdessen in eine Datei für Wireshark schreiben
sudo tcpdump -i any -nn -w mqtt.pcap 'tcp port 1883 or tcp port 8883'

# Nur Traffic mit einem bestimmten Gerät
sudo tcpdump -i any -nn 'host 192.168.1.50 and tcp port 1883'

Die Optionen, die du am häufigsten brauchst:

OptionBedeutung
-i any / -i eth0Interface für den Mitschnitt (tcpdump -D listet sie auf)
-nnKeine Host- oder Portnamen auflösen — schneller und eindeutig
-w file.pcapRohe Pakete in eine Datei schreiben statt ausgeben
-r file.pcapEine Mitschnittdatei lesen
-A / -XPayload als ASCII / als Hex und ASCII ausgeben
-c 200Nach 200 Paketen stoppen
-UJedes Paket sofort schreiben (nötig beim Schreiben in eine Pipe)
-ttttVollständiges Datum und Uhrzeit in jeder Zeile

Um sofort etwas Nützliches zu sehen, publiziere aus einem zweiten Terminal auf ein Test-Topic und sieh dir die Payload in ASCII an:

Eine Testnachricht auf der Leitung beobachten
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'

Du siehst das Topic testmqtt/tcpdump/hello und die Payload im Klartext im PUBLISH-Segment. Auf Port 8883 siehst du nur verschlüsselte Bytes — genau dafür ist TLS da.

Wo mitschneiden

Der Ort des Mitschnitts bestimmt, was du beweisen kannst. Ein Mitschnitt auf dem Client zeigt, was das Gerät wirklich gesendet hat und was zurückkam, inklusive Retransmissions, die die Anwendung nie bemerkt. Ein Mitschnitt auf dem Broker-Host zeigt, was tatsächlich angekommen ist, nachdem NAT, Load Balancer und Proxys ihre Arbeit getan haben. Widersprechen sich beide, liegt das Problem dazwischen.

Bei einem hartnäckigen Problem schneidest du auf beiden Seiten gleichzeitig mit -tttt-Zeitstempeln mit und vergleichst. Sendet der Client einen PUBLISH, der auf Broker-Seite nie auftaucht, prüfe den Netzwerkpfad; empfängt der Broker ihn, der Subscriber aber nie, sieh dir ACLs, Queues und Logs des Brokers an.

tcpdump-Ausgabe lesen

Eine typische MQTT-Verbindung sieht so aus (Adressen gekürzt):

Eine kurze MQTT-Sitzung
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.], ...

Die TCP-Flags tragen den Großteil der Diagnose:

  • [S] ohne [S.]-Antwort — der SYN wurde nie beantwortet: Firewall, Routing oder falsche IP. Siehe MQTT-Netzwerkprobleme diagnostizieren.
  • [S], beantwortet mit [R.] — der Host läuft, aber auf dem Port lauscht nichts (Connection refused).
  • [F.] — ein geordnetes Schließen. Die Seite, die das erste FIN sendet, hat entschieden, die Sitzung zu beenden.
  • [R] — ein abrupter Reset. Kommt er von einer Adresse, die weder Client noch Broker ist, hat eine Middlebox die Verbindung gekappt.
  • Dasselbe [P.]-Segment nach einer Verzögerung wiederholt — eine Retransmission, also Verlust auf dem Pfad.

Nach MQTT-Pakettyp filtern

Das erste Byte jedes MQTT-Pakets kodiert in den oberen vier Bits seinen Typ. Ein BPF-Ausdruck kann dieses Byte am Anfang der TCP-Payload lesen, sodass tcpdump nur bestimmte Pakettypen behält:

TypErstes Byte (maskiert mit 0xf0)
CONNECT0x10
CONNACK0x20
PUBLISH0x30
SUBSCRIBE0x80
PINGREQ / PINGRESP0xc0 / 0xd0
DISCONNECT0xe0
Nur CONNECT- und CONNACK-Pakete (unverschlüsseltes MQTT, 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 ist die Länge des TCP-Headers, der Ausdruck liest also das erste Payload-Byte. Die Bedingung tcp-push überspringt leere ACK-Segmente. Zwei Grenzen: Der Filter sieht nur das erste MQTT-Paket pro Segment (mehrere kleine Pakete können sich eines teilen), und tcp[]-Offsets funktionieren nur mit IPv4. Er dient dazu, einen Mitschnitt einzugrenzen, und ist kein Decoder — für exakte Antworten nutze Wiresharks mqtt.msgtype-Filter.

Verbindungsabbrüche und Reconnects erkennen

Zwei kurze Filter beantworten die häufigste Frage im Betrieb — „Warum trennt sich dieser Client ständig?":

Ereignisse im Lebenszyklus einer Verbindung
# Jeder neue Verbindungsversuch (SYN ohne ACK)
sudo tcpdump -i any -nn 'tcp port 1883 and tcp[tcpflags] == tcp-syn'

# Jedes Schließen oder Zurücksetzen, mit Zeitstempeln
sudo tcpdump -i any -nn -tttt 'tcp port 1883 and tcp[tcpflags] & (tcp-fin|tcp-rst) != 0'

Lies beide zusammen:

  • Ein FIN vom Broker etwa anderthalb Keep-Alive-Intervalle nach dem letzten Paket des Clients bedeutet, dass der Client keine PINGREQ mehr gesendet hat — siehe MQTT Keep Alive.
  • Ein RST nach langer Inaktivität, oft jedes Mal nach demselben Intervall, deutet auf ein NAT- oder Firewall-Timeout hin — siehe MQTT-NAT-Timeout.
  • Hunderte SYNs pro Sekunde von vielen Geräten nach einem Ausfall sind ein Reconnect-Sturm.
  • Ein FIN vom Broker direkt nach einem neuen SYN von einer anderen Adresse mit derselben Client ID bedeutet eine Session-Übernahme. Gib jedem Test-Client mit dem Client-ID-Generator eine eigene ID.

Lang laufende Mitschnitte

Sporadische Probleme brauchen Mitschnitte, die stundenlang laufen, ohne die Platte zu füllen. tcpdump kann Dateien selbst rotieren:

Ringpuffer und stündliche Dateien
# 10 Dateien zu je ~100 MB, die älteste wird überschrieben
sudo tcpdump -i any -nn -w /var/tmp/mqtt.pcap -C 100 -W 10 'tcp port 1883 or tcp port 8883'

# Eine Datei pro Stunde, nach Uhrzeit benannt
sudo tcpdump -i any -nn -G 3600 -w '/var/tmp/mqtt-%Y%m%d-%H%M.pcap' 'tcp port 1883'

Starte das in tmux oder als temporäre systemd-Unit, notiere den Zeitpunkt des Problems und hol dir nur die passende Datei.

Container, Kubernetes und entfernte Hosts

Ein Broker-Container hat meist kein tcpdump an Bord. Leih dir stattdessen seinen Netzwerk-Namespace:

Docker: Traffic des Broker-Containers mitschneiden
docker run --rm --net container:mosquitto nicolaka/netshoot \
  tcpdump -i any -nn -U -w - 'tcp port 1883' > broker.pcap
Kubernetes: ephemerer Debug-Container
kubectl debug -it mosquitto-0 --image=nicolaka/netshoot --target=mosquitto -- \
  tcpdump -i any -nn 'tcp port 1883'

Der Debug-Container braucht die Berechtigung zum Mitschneiden (root und die Capability NET_RAW); restriktive Pod-Security-Richtlinien können ihn blockieren. Portmappings und Bridge-Netzwerke haben eigene Fallstricke, die in MQTT-Netzwerkprobleme mit Docker beschrieben sind.

Um einen entfernten Server live in Wireshark zu beobachten, streamst du den Mitschnitt über SSH (der entfernte Benutzer braucht passwortloses sudo für tcpdump):

Live-Mitschnitt aus der Ferne in Wireshark
ssh admin@gateway.example "sudo tcpdump -i any -nn -U -w - 'tcp port 1883'" | wireshark -k -i -

Mikrocontroller wie der ESP32 können kein tcpdump ausführen. Schneide auf dem Gateway, dem WLAN-Router (OpenWrt hat ein tcpdump-Paket) oder einem gespiegelten Switch-Port mit und filtere nach der IP des Geräts.

Den Mitschnitt analysieren

tcpdump schafft einen ersten Durchgang über eine gespeicherte Datei:

Schnelle Analyse eines pcap
# Wie viele Verbindungsversuche, und von wo?
tcpdump -r mqtt.pcap -nn 'tcp[tcpflags] == tcp-syn' | awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn

# Klartext-Payloads rund um einen Zeitpunkt lesen
tcpdump -r mqtt.pcap -nn -tttt -A | less

# Zur MQTT-Dekodierung an tshark übergeben
tshark -r mqtt.pcap -Y mqtt -T fields -e frame.time -e ip.src -e mqtt.msgtype -e mqtt.topic

Für alles, was über Zählen hinausgeht, öffne die Datei in Wireshark: Es dekodiert jedes MQTT-Paket und kann TLS mit einem Key-Log entschlüsseln. Den kompletten Ablauf beschreibt MQTT mit Wireshark debuggen.

Eine Lösung per Mitschnitt bestätigen

Ein Mitschnitt ist auch der beste Regressionstest für einen Netzwerk-Fix. Nachdem du Keep-Alive, ein Firewall-Timeout oder eine Load-Balancer-Einstellung geändert hast, lässt du den Abbruch-Filter länger laufen als das alte Fehlerintervall, während ein Client verbunden bleibt — etwa ein Subscriber im Online-MQTT-Client auf testmqtt/tcpdump/#. Kein FIN und kein RST in diesem Zeitfenster heißt: Der Fix hält.

Test-Subscriber während des Mitschnitts verbunden halten

tcpdump macht aus „die Verbindung bricht manchmal ab" Zeitstempel, Flags und IP-Adressen. Schneide gezielt mit, lass dir von den Flags sagen, wer die Verbindung geschlossen hat, und wechsle zu Wireshark, wenn du die MQTT-Pakete selbst lesen musst.

Häufig gestellte Fragen

Mit welchem tcpdump-Befehl schneide ich MQTT mit?

sudo tcpdump -i any -nn -w mqtt.pcap 'tcp port 1883 or tcp port 8883' schneidet unverschlüsseltes und TLS-MQTT auf allen Interfaces in eine Datei mit, die du in Wireshark öffnen kannst. Lass -w weg und füge -A hinzu, um Payloads von unverschlüsseltem MQTT direkt im Terminal zu sehen.

Kann tcpdump MQTT-Pakete dekodieren?

Nein. tcpdump zeigt TCP-Segmente, Flags und rohe Bytes, hat aber keinen MQTT-Decoder. Schneide mit tcpdump mit und öffne das pcap danach in Wireshark oder führe tshark -r datei.pcap -Y mqtt aus, um CONNECT, PUBLISH und die übrigen MQTT-Pakete zu sehen.

Wie nutze ich tcpdump für einen Broker in Docker?

Hänge einen Tools-Container an den Netzwerk-Namespace des Brokers, zum Beispiel docker run --rm --net container:mosquitto nicolaka/netshoot tcpdump -i any -nn port 1883. Er sieht genau den Traffic des Broker-Containers, ohne dass du dort etwas installierst.