tcpdump und Netzwerk-Monitoring unter Debian 13 und CachyOS
Wenn ein Dienst „langsam“ ist, ein Server „komisch“ sendet oder ein Container „kein Netz“ hat, hilft kein Log
weiter – dann muss man die Pakete ansehen. tcpdump ist dafür das Standardwerkzeug, aber es hat
drei Hürden: Rechte (es braucht CAP_NET_RAW, nicht zwingend root),
Filter (ohne Filter ertrinkt man in Ausgaben) und Auswertung (ein pcap ist
nur Rohmaterial). Dieser Artikel zeigt beide Seiten: den gezielten Mitschnitt mit tcpdump unter
Debian 13 und CachyOS – und das Monitoring ohne root, das im Alltag meist zuerst hilft:
ss, nstat, ethtool, iftop, nethogs,
vnstat und sysstat.
Was tcpdump kann – und was nicht
tcpdump setzt auf libpcap auf und fängt Pakete auf der Ebene des
Netzwerk-Interfaces ab. Der eigentliche Trick ist nicht das Ausgeben, sondern das
Filtern im Kernel: Was nicht passt, wird nie in den Userspace kopiert. Deshalb kann ein
einzelner Prozess auf einem 10-Gbit-Link mitschneiten, ohne die Last der Masse zu erzeugen.
| Du willst … | tcpdump zeigt es | Warum |
|---|---|---|
| Wissen, ob und wann Pakete ankommen | Ja, sekundengenau mit Zeitstempel | Zeitstempel kommen aus dem Kernel (--time-stamp-precision) |
| Flags, Sequenznummern, Ports, ICMP-Typen | Ja, vollständig dekodiert | libpcap dekodiert IP, TCP, UDP, ICMP, DNS, HTTP und viele weitere Protokolle |
| Klartext mitlesen (HTTP, DNS, Mail) | Ja, mit -A oder -X | Die Nutzlast wird mitgeschrieben |
| TLS-Inhalt lesen | Nein – nur Handshake-Metadaten | Der Inhalt ist verschlüsselt; sichtbar bleiben SNI, Zertifikatsketten-Größen, Timing |
| Verläufe über Tage | Nein | Kein Speicher, keine Datenbank – dafür sind vnstat und sysstat da |
| Wissen, welcher Prozess sendet | Nein | Pakete kennen keine PID – dafür ss -p oder nethogs |
ss und nstat (Kernel-Zähler, keine
Rechte nötig), dann tcpdump für den Blick ins Detail. Wer mit dem Mitschnitt anfängt, sucht
meistens in 200.000 Zeilen nach dem Problem, das ein ss -tin in einer Zeile gezeigt hätte.
Installation: Debian 13 und CachyOS
tcpdump gehört nicht zur Grundinstallation: auf unserem CachyOS-Testsystem war
das Paket nicht vorhanden, und Debian 13 bringt es ebenfalls nicht mit. Beides ist schnell nachinstalliert:
# Debian 13 (Trixie)
sudo apt update && sudo apt install tcpdump
# CachyOS / Arch
sudo pacman -S tcpdump
Auf dem Testsystem läuft tcpdump 4.99.6 mit libpcap 1.11.0 – die Version verrät sich selbst, inklusive der Frage, mit welchem TLS-Stack das Binary gebaut wurde:
tcpdump --version
tcpdump version 4.99.6
libpcap version 1.11.0 (64-bit time_t, with TPACKET_V3)
OpenSSL 3.6.4 25 Aug 2026
64-bit build, 64-bit time_t
Interessant ist die Zeile with TPACKET_V3: Das ist der moderne Ringpuffer im Linux-Kernel, über
den libpcap Pakete in Blöcken statt einzeln abholt. Deshalb ist das Mitschneiden heute deutlich
ressourcenschonender als in der tcpdump-Zeit der 2000er.
| Zweck | Debian 13 (Trixie) | CachyOS / Arch | root nötig? |
|---|---|---|---|
| Paket-Mitschnitt | tcpdump 4.99.5-2 | tcpdump 4.99.6-1 | CAP_NET_RAW |
| Auswertung von pcap-Dateien | tshark 4.4.18-0+deb13u1 | wireshark-cli 4.7.3-1 | nein (nur Lesen) |
| Live-Traffic pro Verbindung | iftop 1.0~pre4-9 | iftop 1.0pre4-6 | CAP_NET_RAW |
| Traffic pro Prozess | nethogs 0.8.8-1 | nethogs 0.8.8-2 | CAP_NET_RAW |
| Balken-Diagramm in der Konsole | nload, bmon | nload 0.7.4-9, bmon 4.0-5 | nein (liest Zähler) |
| Verlaufsstatistik (Datenbank) | vnstat 2.13-1 | vnstat 2.13-2 | Service als root |
| Systemstatistik über Zeit | sysstat (u. a. sar) | sysstat 12.8.0-1 | Timer als root |
| Analyse von Netflow-Daten | nfdump | nicht in den offiziellen Repos (AUR) | nein |
sudo pacman -S tcpdump iftop nethogs nload bmon vnstat iptraf-ng mtr termshark bandwhich ngrep tcpflow darkstat conntrack-tools sysstat iperf3 –
bzw. dieselbe Liste unter Debian mit apt install. Für die Auswertung von Mitschnitten reicht
tshark (Arch: wireshark-cli), ein grafisches Wireshark braucht man auf dem Server
nicht.
Rechte: CAP_NET_RAW statt root
Der Rohzugriff auf das Interface ist eine Capability: CAP_NET_RAW. Ohne sie passiert genau
das – ein Mitschnitt als normaler Benutzer scheitert mit einer sehr klaren Meldung:
tcpdump -nn -i enp17s0 -c 1
tcpdump: enp17s0: You don't have permission to perform this capture on that device
(Attempt to create packet socket failed - CAP_NET_RAW may be required)
Dasselbe gilt für iftop, das ebenfalls libpcap benutzt:
iftop -t -s 3
interface: enp17s0
IP address is: 192.168.178.25
MAC address is: 30:56:0f:42:50:2d
pcap_open_live(enp17s0): enp17s0: You don't have permission to perform this capture
on that device (Attempt to create packet socket failed - CAP_NET_RAW may be required)
Es gibt drei saubere Wege, das zu lösen – mit unterschiedlichen Konsequenzen:
| Weg | Befehl | Vorteil | Risiko |
|---|---|---|---|
| Einmalig als root | sudo tcpdump -i any -c 20 | Nichts zu ändern, ideal für die Diagnose | Keines – solange du nichts dauerhaft laufen lässt |
| Capability am Binary | sudo setcap cap_net_raw,cap_net_admin=eip /usr/bin/tcpdump | Auch andere Administratoren dürfen mitschneiden | Jeder lokale Nutzer darf den gesamten Netzwerkverkehr lesen |
| Privilegien abgeben | sudo tcpdump -Z tcpdump -w /var/log/tcpdump/cap.pcap | Pcap-Dateien gehören nicht root, sondern tcpdump | Zielverzeichnis muss dem Benutzer gehören |
setcap und getcap sind auf CachyOS vorhanden; getcap ist der schnellste
Check, ob ein Binary die Capability trägt:
sudo setcap cap_net_raw,cap_net_admin=eip /usr/bin/tcpdump
getcap /usr/bin/tcpdump
/usr/bin/tcpdump cap_net_admin,cap_net_raw=eip
setcap: Erstens überschreibt jedes Paket-Update die Datei –
die Capability ist danach weg und muss neu gesetzt werden (das merkt man meist erst im Notfall). Zweitens
ist ein Binary mit Rohzugriff ein lohnendes Ziel: wer es manipulieren kann, liest jeden Verkehr mit.
Deshalb ist die Variante mit sudo oder einer systemd-Unit mit
AmbientCapabilities= für einen Server die bessere Wahl.
-Z (ausführlich --relinquish-privileges=user) greift genau dann: tcpdump startet als
root, öffnet das Capture-Gerät, wechselt die Benutzerkennung – und öffnet danach erst die
Ausgabedateien. So kommen Mitschnitte mit den Rechten des angegebenen Nutzers auf die Platte, während das
Öffnen des Interfaces weiterhin privilegiert passiert.
Die erste Aufnahme
Vor dem Filter kommt die Schnittstelle. -D listet sie auf – auf einem Docker-Host ist die Liste
länger als erwartet, weil auch die Bridges und die virtuellen Interfaces der Container auftauchen:
tcpdump -D
1.enp17s0 [Up, Running, Connected]
9.any (Pseudo-device that captures on all interfaces) [Up, Running]
10.lo [Up, Running, Loopback]
12.docker0 [Up, Disconnected]
13.wlan0 [Wireless, Not associated]
16.nflog (Linux netfilter log (NFLOG) interface) [none]
17.nfqueue (Linux netfilter queue (NFQUEUE) interface) [none]
18.dbus-system (D-Bus system bus) [none]
Ein Mitschnitt startet man nie ohne Begrenzung. Die Grundform für die Diagnose ist immer dieselbe: Interface, keine Namensauflösung, ein Zähler, ein Filter.
# 20 Pakete auf allen Interfaces, ohne DNS-Lookups, ohne Byte-Grenze
sudo tcpdump -nn -i any -c 20
# Nur Loopback, mit Link-Layer-Kopf (MAC-Adressen)
sudo tcpdump -nn -e -i lo -c 10
# Direkt in eine Datei schreiben (später mit Wireshark & Co. öffnen)
sudo tcpdump -nn -i any -s 128 -w /tmp/mitschnitt.pcap
| Option | Wirkung | Wann im Alltag |
|---|---|---|
-i any | Alle Interfaces auf einmal (Pseudogerät) | Wenn unklar ist, über welches Interface der Verkehr läuft |
-nn | Keine Auflösung von Adressen und Portnamen | Immer – Lookups kosten Zeit und verfälschen den Mitschnitt |
-c 100 | Nach 100 passenden Paketen beenden | Immer, sonst läuft es endlos |
-e | Link-Layer-Kopf ausgeben (MAC-Adressen) | Bei ARP-, VLAN- und Switch-Fragen |
-A / -X | Nutzlast als ASCII / als Hex + ASCII | HTTP-Debugging, DNS-Fragen lesen |
-S | Absolute Sequenznummern statt relativer | Wenn du Pakete über mehrere Mitschnitte vergleichen willst |
-q | Kurze Ausgabe (kein Detail-Header) | Bei hoher Paketrate oder wenn nur der Verlauf zählt |
-s 128 | Snaplen: nur die ersten 128 Byte sichern | Für lange Mitschnitte: Header reichen meist (Default ist 262144) |
-l / -U | Zeilen- bzw. paketgepufferte Ausgabe | Bei | grep, | tee oder in einem systemd-Journal |
Ausgaben lesen
Ein Mitschnitt ohne Filter, aber mit -nn, sieht so aus:
tcpdump -nn -r test.pcap
22:26:40.000400 IP 192.168.178.25.41234 > 9.9.9.9.53: 6699+ A? huuu.biz. (26)
22:26:40.018400 IP 9.9.9.9.53 > 192.168.178.25.41234: 6699 1/0/0 A 203.0.113.7 (42)
22:26:40.020400 IP 192.168.178.25.52344 > 203.0.113.7.443: Flags [S], seq 1000, win 64240, length 0
22:26:40.029400 IP 203.0.113.7.443 > 192.168.178.25.52344: Flags [S.], seq 5000, ack 1001, win 64240, length 0
22:26:40.030400 IP 192.168.178.25.52344 > 203.0.113.7.443: Flags [.], ack 1, win 64240, length 0
22:26:40.033400 IP 192.168.178.25.52344 > 203.0.113.7.443: Flags [P.], seq 1:99, ack 1, win 64240, length 98
22:26:40.038400 IP 192.168.178.25 > 1.1.1.1: ICMP echo request, id 66, seq 1, length 28
22:26:40.049400 IP 1.1.1.1 > 192.168.178.25: ICMP echo reply, id 66, seq 1, length 28
22:26:40.053400 IP 192.168.178.25.60122 > 192.168.178.10.22: Flags [S], seq 2000, win 64240, length 0
| Zeichen / Feld | Bedeutung |
|---|---|
Zeitstempel 22:26:40.033400 | Erfassungszeit im Kernel, Mikrosekunden – mit -tttt kommt das Datum dazu |
> und < | Richtung: senden bzw. empfangen (bezogen auf den mitschneitenden Host) |
[S] | SYN – Verbindungsaufbau |
[S.] | SYN + ACK – die Gegenseite antwortet |
[.] | Reines ACK ohne Daten |
[P.] | Push + ACK: dieses Paket trägt Nutzdaten |
[F.] / [R] | Verbindungsabbau (FIN) bzw. Abbruch (RST) |
seq 1:99, length 98 | 98 Byte Nutzdaten; Sequenznummern sind ohne -S relativ zum Beginn der Verbindung |
6699+ A? huuu.biz. (26) | DNS: Transaktions-ID 6699, + = Rekursion erwünscht, A? = Frage nach einem A-Record |
ICMP echo request | Ping in Richtung Ziel; echo reply ist die Antwort |
Wer absolute Sequenznummern braucht, hängt -S an – dann steht in derselben Zeile
seq 1001:1099, ack 5001 statt seq 1:99, ack 1. Mit -e kommt der
Link-Layer-Kopf davor, inklusive MAC-Adressen und Ethertype:
tcpdump -nn -e -tttt -r test.pcap
2025-09-20 22:26:40.020400 30:56:0f:42:50:2d > a8:bb:50:c1:d2:e3, ethertype IPv4 (0x0800),
length 54: 192.168.178.25.52344 > 203.0.113.7.443: Flags [S], seq 1000, win 64240, length 0
Und wenn Nutzdaten wirklich lesbar sein sollen (HTTP im Klartext), ist -A die Abkürzung,
-X die vollständige Variante mit Hexdump:
tcpdump -nn -A -r test.pcap 'tcp port 443 and tcp[13] & 8 != 0'
22:26:40.033400 IP 192.168.178.25.52344 > 203.0.113.7.443: Flags [P.], seq 1001:1099,
ack 5001, win 64240, length 98
E....4@.@.yp......q..x..........P.......GET /artikel-tcpdump-monitoring HTTP/1.1
Host: huuu.biz
User-Agent: curl/8.21.0
Accept: */*
Filter schreiben (BPF)
Filter sind der wichtigste Teil: Sie entscheiden, ob der Kernel ein Paket überhaupt nach oben kopiert.
Die Bausteine sind wenige – und sie lassen sich mit and, or, not und
Klammern beliebig kombinieren.
| Baustein | Bedeutung | Beispiel | Ergebnis im Testmitschnitt |
|---|---|---|---|
host | Quelle oder Ziel ist diese Adresse | 'host 9.9.9.9' | Anfrage und Antwort des DNS |
src / dst | Nur eine Richtung | 'src 9.9.9.9 and udp' | Eine Zeile – nur die DNS-Antwort |
net | Ganzes Netz, mit CIDR | 'net 192.168.178.0/24 and icmp' | Echo-Request und Echo-Reply |
port | Quell- oder Zielport | 'tcp port 443' | Vier Pakete des TLS-Handshakes |
portrange | Portbereich | 'portrange 40000-65535' | Alle Pakete mit hohen Quellports |
greater / less | Paketlänge in Byte | 'greater 100' | Nur das Paket mit 98 Byte Nutzlast |
ether host | MAC-Adresse (nur mit Link-Layer-Kopf) | 'ether src 30:56:0f:42:50:2d and tcp' | Nur ausgehende TCP-Pakete |
tcp[tcpflags] | TCP-Flags direkt adressieren | 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0' | Nur die beiden SYN-Pakete |
ip6 / icmp6 | IPv6 bzw. ICMPv6 | 'icmp6 and ip6[40] == 134' | Router Advertisements (Typ 134) |
Der Handshake-Filter ist der nützlichste von allen, weil er bei jedem Verbindungsproblem die erste Frage beantwortet: Kommt überhaupt ein SYN an – und antwortet jemand? Im Testmitschnitt liefert er exakt und ausschließlich die beiden Verbindungsaufbauten:
tcpdump -nn -r test.pcap 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0'
22:26:40.020400 IP 192.168.178.25.52344 > 203.0.113.7.443: Flags [S], seq 1000, win 64240, length 0
22:26:40.053400 IP 192.168.178.25.60122 > 192.168.178.10.22: Flags [S], seq 2000, win 64240, length 0
| und
? gehören der Shell, nicht tcpdump – 'host a and (b or c)' ist richtig,
host a and (b or c) scheitert an der Shell. Wer viel filtert, legt den Ausdruck in eine Datei
und nutzt -F filter.txt; darin steht pro Zeile ein Ausdruck, leere Zeilen und
#-Kommentare sind erlaubt.
Für die Praxis hilft noch ein Trick: die Ausgabe mit awk oder sort weiterverarbeiten,
statt sie zu lesen. Wer wissen will, welche Adressen gerade am meisten sprechen, kommt so in einer Zeile
zur Antwort:
# Top-Sprecher auf Port 443 (nach 500 Paketen abbrechen)
sudo tcpdump -nn -i any -c 500 'tcp port 443' 2>/dev/null \
| awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn | head
Was ein Filter wirklich tut
tcpdump kann den kompilierten Filter ausgeben – das ist die beste Erklärung dafür, warum
ein Filter schnell ist und wo seine Grenzen liegen:
tcpdump -d 'tcp port 443'
Warning: assuming Ethernet
(000) ldh [12]
(001) jeq #0x86dd jt 2 jf 8
(002) ldb [20]
(003) jeq #0x6 jt 4 jf 19
(004) ldh [54]
(005) jeq #0x1bb jt 18 jf 6
(006) ldh [56]
(007) jeq #0x1bb jt 18 jf 19
(008) jeq #0x800 jt 9 jf 19
(009) ldb [23]
(010) jeq #0x6 jt 11 jf 19
(011) ldh [20]
(012) jset #0x1fff jt 19 jf 13
(013) ldxb 4*([14]&0xf)
(014) ldh [x + 14]
(015) jeq #0x1bb jt 18 jf 16
(016) ldh [x + 16]
(017) jeq #0x1bb jt 18 jf 19
(018) ret #262144
(019) ret #0
Das lässt sich Zeile für Zeile lesen – und erklärt drei Dinge auf einmal:
| Zeile | Was passiert | Konsequenz für dich |
|---|---|---|
ldh [12] → 0x86dd / 0x800 | Ethertype prüfen: IPv6 oder IPv4 | Ein Filter kann beide Familien enthalten – IPv6 fällt nicht automatisch raus |
ldb [20] jeq 0x6 | Protokollfeld im IP-Kopf: 6 = TCP, 17 = UDP | Ein IP-Header ist immer Voraussetzung – ARP oder Nicht-IP wird verworfen |
ldh [54] / [56] → 0x1bb | 446 in Hex = Port 443, erst Quelle, dann Ziel | tcp port 443 prüft beide Richtungen – deshalb siehst du Request und Response |
jset #0x1fff jt 19 | Fragment-Offset prüfen: Fragmente werden verworfen | Portfilter greifen nur bei nicht fragmentierten Paketen – bei IP-Fragmenten siehst du nichts |
ldxb 4*([14]&0xf) | IP-Header-Länge auslesen (bei Optionen > 20 Byte) | Der Filter bleibt korrekt, auch wenn Optionen im IP-Kopf stehen |
ret #262144 / ret #0 | Paket übernehmen (bis 262144 Byte) oder verwerfen | 262144 ist der Standard-Snaplen – passt zu tcpdump -s |
Die Länge dieses Programms ist der Grund, warum ein Filter im Kernel läuft: Der Kernel entscheidet
Byte für Byte, ohne ein Paket zu kopieren, das er nicht braucht. Ein komplexer Filter (port 80 or
port 443 or port 53) kostet nur wenige zusätzliche Instruktionen – der Unterschied liegt nicht im
Filter, sondern in der Menge der Pakete, die er durchlässt.
Ein Tippfehler im Ausdruck wird übrigens klar gemeldet – nichts läuft stillschweigend schief:
tcpdump -nn -r test.pcap 'tcp port'
tcpdump: can't parse filter expression: syntax error
tcpdump -i any ohne Filter auf einer belebten Leitung erzeugt
Plattenlast, CPU-Last und – bei voller Platte – einen Folgeschaden. Immer -c setzen, immer
filtern, im Zweifel -s 128.
Mitschneiden mit Rotation
Für längere Mitschnitte schreibt man in Dateien. Schreiben und wieder lesen funktioniert dabei auch kombiniert – praktisch, um einen unübersichtlichen Mitschnitt auf das Wesentliche zu reduzieren:
# aus dem großen Mitschnitt nur den TLS-Verkehr herausfiltern
tcpdump -nn -r test.pcap -w nur-tls.pcap 'tcp and port 443'
# und wieder ansehen
tcpdump -nn -r nur-tls.pcap
| Option | Wirkung | Praxis |
|---|---|---|
-w datei.pcap | Schreibt Rohpakete in eine Datei (Standardformat pcap) | Immer mit Zeitstempel im Namen oder mit Rotation |
-r datei.pcap | Liest eine Datei statt zu mitschneiden | Auswertung in Ruhe, auch auf einem anderen Rechner |
-C 100 | Neue Datei, sobald die aktuelle 100 MB (100.000.000 Byte) groß ist | Dateinamen bekommen eine laufende Nummer ab 1 |
-W 12 | Nur 12 Dateien anlegen, dann von vorne überschreiben (Ringpuffer) | Harte Obergrenze gegen volle Platten; Nummern werden mit führenden Nullen sortierbar gemacht |
-G 3600 | Alle 3600 Sekunden rotieren; im Namen darf ein Zeitformat stehen | Für „eine Datei pro Stunde“ – -w cap-%Y%m%d-%H%M.pcap |
-z gzip | Nach jeder Rotation das fertige File komprimieren | Läuft parallel zum Mitschnitt mit niedriger Priorität |
-s 128 | Nur die ersten 128 Byte je Paket sichern | Header reichen für Adressen, Ports und Flags – spart 90 % der Dateigröße |
-B 4096 | 4 MB Kernel-Puffer für die Aufnahme (Angabe in KiB) | Bei hoher Rate die erste Stellschraube gegen verlorene Pakete |
# Ringpuffer: max. 12 Dateien à 100 MB, komprimiert – deckelt den Platz auf der Platte
sudo install -d -o tcpdump -g tcpdump -m 0700 /var/log/tcpdump
sudo tcpdump -i any -s 128 -C 100 -W 12 -z gzip -Z tcpdump \
-w /var/log/tcpdump/cap.pcap 'not port 22'
# Oder: eine Datei pro Stunde, komprimiert – Aufräumen übernimmt ein Timer
sudo tcpdump -i any -s 128 -G 3600 -z gzip -Z tcpdump \
-w '/var/log/tcpdump/cap-%Y%m%d-%H%M.pcap' 'udp port 53 or tcp port 853'
-G ohne Zeitformat: tcpdump überschreibt dann bei jeder Rotation
die vorherige Datei – der Mitschnitt ist nach einer Stunde weg. Das Zeitformat im Dateinamen
(%Y%m%d-%H%M) ist nicht Kosmetik, sondern die Bedingung dafür, dass die Rotation etwas
aufbewahrt. Bei -C ohne -W läuft die Nummerierung dagegen endlos weiter und die
Platte füllt sich.
Mitschnitt als systemd-Dienst
Wer regelmäßig Fehlerbilder festhalten will, startet den Mitschnitt nicht von Hand, sondern als Unit. Auf
Debian 13 und CachyOS ist der Ablauf identisch, weil beide systemd benutzen. Der elegante Weg kommt ohne
setcap und ohne dauerhaftes root aus: systemd gibt dem Prozess genau eine Capability und
wechselt vorher auf einen eigenen Benutzer.
# Benutzer und Verzeichnis
sudo useradd --system --no-create-home --shell /usr/sbin/nologin tcpdump
sudo install -d -o tcpdump -g tcpdump -m 0700 /var/log/tcpdump
# /etc/systemd/system/netcapture.service
[Unit]
Description=tcpdump-Ringmitschnitt (Netzwerkdiagnose)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/tcpdump -i any -s 128 -C 100 -W 12 -z gzip -Z tcpdump \
-w /var/log/tcpdump/cap.pcap 'not port 22'
User=tcpdump
Group=tcpdump
AmbientCapabilities=CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_RAW
Nice=-5
IOSchedulingClass=idle
Restart=on-failure
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/log/tcpdump
[Install]
WantedBy=multi-user.target
# Aktivieren – auf beiden Distributionen gleich
sudo systemctl daemon-reload
sudo systemctl enable --now netcapture
systemctl status netcapture
journalctl -u netcapture -f
# Platzverbrauch und Dateien prüfen
du -sh /var/log/tcpdump/
ls -lt /var/log/tcpdump/ | head
Die Unit bringt drei Dinge mit, die eine Handstart-Version nicht hat: AmbientCapabilities
ersetzt setcap (und übersteht Updates), Restart=on-failure fängt Abstürze ab,
und IOSchedulingClass=idle sorgt dafür, dass das Schreiben der pcap-Dateien die Applikation
nicht ausbremst. Wichtig ist der Filter im ExecStart: Ohne ihn schreibt der Dienst alles mit,
was über das Interface läuft – inklusive SSH-Sitzungen und Passwörtern, die im Klartext übertragen werden.
-z gzip und -Z tcpdump zusammen: Das Komprimieren läuft als
Unterprozess mit den Rechten des Benutzers, auf den tcpdump gewechselt ist – das Verzeichnis muss also
tcpdump gehören (daher install -d -o tcpdump). Läuft es als root, entstehen
root-Dateien im Verzeichnis und der Dienst scheitert nach der ersten Rotation.
Praxisrezepte
Die folgenden Rezepte decken die Fälle ab, die im Serveralltag wirklich auftreten.
| Frage | Befehl | Worauf du achtest |
|---|---|---|
| Wer fragt ständig DNS? | sudo tcpdump -nn -i any -c 50 'udp port 53' | Viele kurze Anfragen ohne Cache-Treffer deuten auf fehlendes DNS-Caching |
| Kommt ein SYN an – und antwortet der Server? | sudo tcpdump -nn -i any 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0' | Kein SYN = Firewall/Routing; SYN ohne SYN-ACK = Dienst lauscht nicht oder ist überlastet |
| Ist es ein TLS-Problem oder ein Netzproblem? | sudo tcpdump -nn -i any -c 30 'tcp port 443' | Handshake bis zum FIN = Netz in Ordnung, Problem liegt in der Anwendung |
| DHCP-Vergabe hängt | sudo tcpdump -nn -i any -e 'port 67 or port 68' | DISCOVER ohne OFFER = kein erreichbarer DHCP-Server im Segment |
| Router Advertisement fehlt (IPv6) | sudo tcpdump -nn -i any 'icmp6 and ip6[40] == 134' | Ein RA pro 200–600 Sekunden ist normal; Aushungern führt zu SLAAC-Ausfällen |
| Läuft der WireGuard-Tunnel? | sudo tcpdump -nn -i any -c 20 'udp port 51820' | Regelmäßige Pakete bekannter Größe; Stille bedeutet NAT-Timeout oder falscher Port |
| Traffic aus einem Container | sudo tcpdump -nn -i br-14e365259a6e 'port 8080' | Container-Bridges heißen br-<ID>; das -D oben zeigt sie |
| Wer schreit ausgehend ins Internet? | sudo tcpdump -nn -i any -c 200 'tcp and not port 22 and not port 53 and not port 443' | Unerwartete Ziele und Ports – der erste Schritt jeder Kompromittierungs-Prüfung |
Ein Rezept verdient Hervorhebung, weil es in der Praxis am häufigsten falsch gemacht wird: die Suche nach einem Verbindungsabbruch. Hier ist nicht das SYN interessant, sondern das RST – und wer testet, ob die Firewall zurücksetzt oder verwirft, sieht den Unterschied direkt im Mitschnitt:
# Setzt die Gegenseite zurück (RST) oder schweigt sie (Drop)?
sudo tcpdump -nn -i any -c 50 'tcp port 8080'
# RST sofort nach dem SYN -> Firewall hat aktiv verworfen (REJECT)
# nichts nach dem SYN -> Paket wird verworfen (DROP) oder Ziel ist aus
sudo ss -tulpn beantwortet sie sofort und ohne
pcap-Datei.
Monitoring ohne root
Für 90 % aller Fragen braucht es keinen Paketmitschnitt. Diese Werkzeuge gehören zur Grundausstattung und laufen ohne Sonderrechte, weil sie Zähler aus dem Kernel lesen:
| Werkzeug | Was es zeigt | root nötig? |
|---|---|---|
ss -s | Zusammenfassung aller Sockets | nein |
ss -tulpn | Lauschende Ports inklusive Prozess | nein (PID nur für eigene Prozesse) |
ss -tin | TCP-Interna: RTT, cwnd, Retransmits, RTO | nein |
ss -o | Timer (Keepalive, TIME_WAIT) | nein |
nstat -az | Kernel-Statistiken (SNMP-Zähler) für IP, TCP, UDP, ICMP | nein |
ip -s -s link | Interface-Zähler inklusive Fehlerarten | nein |
ethtool -S | Zähler direkt aus dem NIC-Treiber | nein |
lnstat | Fortlaufende Kernel-Statistiken im Sekundentakt | nein |
Die Übersicht ist die schnellste erste Antwort auf „läuft alles normal?“:
ss -s
Total: 1939
TCP: 74 (estab 28, closed 27, orphaned 0, timewait 1)
Transport Total IP IPv6
RAW 1 0 1
UDP 67 36 31
TCP 47 37 10
INET 115 73 42
Die Zeile estab 28 ist die interessante: langsam wachsende oder nie abnehmende
closed- und orphaned-Werte deuten auf Anwendungen, die Verbindungen nicht
sauber schließen. Für einzelne Verbindungen liefert ss -tin die TCP-Interna – im Testsystem
zum Beispiel:
ss -tin state established
cubic wscale:11,10 rto:231 rtt:30.412/0.094 ato:43 mss:1368 pmtu:1420 cwnd:10
bytes_sent:14678 bytes_acked:14679 bytes_received:9472 segs_out:368 segs_in:662
send 3598580bps pacing_rate 7197096bps delivery_rate 693136bps minrtt:29.642
Aus dieser einen Zeile lassen sich drei typische Fehlerbilder ablesen: ein hohes rtt mit
kleinem cwnd (Bandbreite wird durch Latenz begrenzt), ein retrans-Feld, das nur
erscheint, wenn es Retransmits gab, und ein app_limited-Marker, wenn die Anwendung selbst
zu langsam Daten nachschiebt – nicht das Netz.
Die Interface-Zähler und die Kernel-Statistik sind das zweite Standbein. Die folgende Ausgabe stammt von einem Rechner, der seit dem letzten Start 1,23 GB empfangen hat – mit 3528 Verwerfungen, ein Wert, den man im Auge behalten sollte:
ip -s -s link show enp17s0
2: enp17s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP
RX: bytes packets errors dropped missed mcast
1226845829 896593 0 3362 0 2301
TX: bytes packets errors dropped carrier collsns
138298598 386284 0 0 0 0
ethtool -S enp17s0 | head -5
NIC statistics:
tx_packets: 386373
rx_packets: 896593
tx_errors: 0
rx_errors: 0
| Feld | Bedeutung | Wann kritisch |
|---|---|---|
errors | Fehlerhafte Frames (CRC, Länge, Rahmen) | Steigende Werte deuten auf Kabel, Port oder Duplex-Problem |
dropped | Verworfen, weil kein Speicher frei war | Bei hoher Last normal in kleinen Zahlen – dauerhaft steigend heißt: Puffer sind zu klein |
missed | Der Treiber hat Pakete verloren, bevor der Kernel sie sah | Immer kritisch: hier leidet die Netzqualität, nicht nur die Statistik |
collsns | Kollisionen (bei modernen Vollduplex-Links fast immer 0) | Werte > 0 bei Vollduplex deuten auf Hardware oder Halbduplex-Zwang |
Für die schnelle Frage „was passiert gerade auf Protokollebene?“ ist nstat -az der beste
Einstieg – es zeigt absolute Zähler aus dem Kernel, ohne einen einzigen Paketmitschnitt:
nstat -az | head -12
IpInReceives 1476152 0.0
IpInHdrErrors 0 0.0
IpInAddrErrors 2 0.0
IpForwDatagrams 592041 0.0
IpInDiscards 0 0.0
IpInDelivers 882596 0.0
IpOutRequests 478462 0.0
IpOutNoRoutes 0 0.0
IpInHdrErrors, IpOutNoRoutes, steigende dropped), ist das der
Anlass für einen gezielten Mitschnitt – nicht der Ersatz dafür.
Interaktive Werkzeuge
Wenn man vor dem Terminal sitzt und „einfach mal sehen“ will, sind die Live-Werkzeuge schneller als
jeder Mitschnitt. Sie alle brauchen außer nload und bmon die
CAP_NET_RAW-Rechte – also ein sudo beim Start.
| Werkzeug | Zeigt | Besonderheit |
|---|---|---|
iftop | Verbindungen live, sortiert nach Bandbreite | Der Klassiker für „wer ist gerade laut“ – mit -t -s 5 auch für Skripte |
nethogs | Bandbreite pro Prozess | Antwort auf „welches Programm zieht Daten“ – ohne PID-Sucherei |
bandwhich | Bandbreite pro Prozess und Verbindung | Moderne Rust-Alternative, liest zusätzlich DNS-Namen mit |
nload | Ein- und Ausgang als Balken | Braucht keine Sonderrechte – liest /proc/net/dev |
bmon | Grafische Kurven pro Interface | Ideal für einen Terminal-Tab auf dem Server |
iptraf-ng | Menüführung mit Statistik, Ports, Paketgrößen | Sehr viele Ansichten, dafür gewöhnungsbedürftige Bedienung |
termshark | Wireshark-Oberfläche im Terminal | Auswertung großer pcap-Dateien ohne X11 |
# Debian 13
sudo apt install iftop nethogs nload bmon iptraf-ng termshark bandwhich
# CachyOS / Arch
sudo pacman -S iftop nethogs nload bmon iptraf-ng termshark bandwhich
# Loslegen (iftop und nethogs brauchen die Capability)
sudo iftop -i enp17s0
sudo nethogs enp17s0
nload enp17s0
sudo nethogs oder
bandwhich. Beide zeigen den Prozess, während iftop nur Adressen kennt. Für
„warum ist die Leitung voll, obwohl niemand etwas tut“ bleiben nur Mitschnitt und
ss -tin.
Verlauf und Auswertung
Mitschnitte sind Momentaufnahmen. Sobald die Frage „war das gestern auch schon so?“ lautet, braucht man Verlaufsdaten – und die liefern andere Werkzeuge, weil sie klein und dauerhaft mitschreiben:
| Werkzeug | Datenhaltung | Typische Frage |
|---|---|---|
vnstat | Kompakte eigene Datenbank, Systemd-Service vnstat | Wie viel habe ich diesen Monat übertragen – und wann war der Peak? |
sysstat (sar) | Binärdateien unter /var/log/sysstat/, Timer sysstat-collect | Wie war die Netzlast gestern um 3 Uhr morgens? |
darkstat | Eigene kleine Datenbank + Web-Oberfläche | Welche Hosts reden überhaupt mit meinem Server? |
tshark | Keine – liest pcap und rechnet | Welche TCP-Verbindung hatte die meisten Retransmits? |
| Netdata | Eigene Zeitreihen-Datenbank | Live-Dashboards für Interface, Sockets und Dienste (siehe unseren Netdata-Artikel) |
# Debian 13
sudo apt install vnstat sysstat darkstat
# CachyOS / Arch
sudo pacman -S vnstat sysstat darkstat
# vnstat: Nutzung pro Stunde, Tag und Monat
sudo systemctl enable --now vnstat
vnstat -h # Stunden
vnstat -d # Tage
vnstat -m # Monate
vnstat --live # Live-Anzeige im Sekundentakt
Für die Auswertung eines Mitschnitts ist tshark das Werkzeug der Wahl, weil es dieselbe
Bibliothek benutzt wie Wireshark, aber skriptbar ist:
# Verkehrsaufkommen pro Sekunde
tshark -r /var/log/tcpdump/cap.pcap -q -z io,stat,1
# Top-Gesprächspartner (TCP)
tshark -r cap.pcap -q -z conv,tcp
# Nur die DNS-Fragen auflisten
tshark -r cap.pcap -Y 'dns.flags.response == 0' -T fields -e dns.qry.name | sort | uniq -c | sort -rn
vnstat,
sar, Netdata), Detailfragen aus pcap-Dateien (tcpdump, tshark).
Wer beides verwechselt, schreibt entweder zu viele Mitschnitte oder sucht in Zählern nach Details, die
dort nicht stehen können.
Durchsatz und Latenz messen
„Das Netz ist langsam“ ist eine Vermutung, keine Messung. Zwei Werkzeuge trennen die Ursachen sauber:
iperf3 misst reinen Durchsatz zwischen zwei Endpunkten, curl zerlegt den Weg zur
Antwort in Abschnitte – DNS, Verbindungsaufbau, TLS, erster Byte.
# iperf3: auf dem Server starten, auf dem Client messen
iperf3 -s
iperf3 -c server.example -t 10 # TCP, 10 Sekunden
iperf3 -c server.example -u -b 500M # UDP mit 500 Mbit/s (Paketverlust sichtbar)
Die Latenz zerlegt curl mit -w – die folgende Ausgabe stammt von zwei Aufrufen
direkt hintereinander. Der Unterschied zeigt, was DNS-Caching wirklich bringt:
curl -o /dev/null -s -w 'dns=%{time_namelookup} connect=%{time_connect} \
tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' https://example.com
dns=0.036961 connect=0.071372 tls=0.111542 ttfb=0.149867 total=0.149913 # erster Aufruf
dns=0.000828 connect=0.045902 tls=0.098911 ttfb=0.152365 total=0.152411 # direkt danach
| Messwert | Was er misst | Typische Ursache eines Ausreißers |
|---|---|---|
time_namelookup | DNS-Auflösung | Kein lokaler Resolver, langsame Nameserver, DNSSEC-Validierung |
time_connect | TCP-Handshake (bis SYN-ACK) | Hohe RTT, Paketverlust, Firewall mit SYN-Proxy |
time_appconnect | TLS-Handshake | Viele Round-Trips, OCSP-Abruf, alte TLS-Versionen ohne Session-Wiederverwendung |
time_starttransfer | Bis zum ersten Byte (TTFB) | Langsame Anwendung oder Datenbank – das Netz ist hier nicht schuld |
time_total | Kompletter Austausch | Übertragungsgröße, Bandbreite, Kompression |
Für die Wegstrecke zwischen zwei Hosts ist mtr der Klassiker – es kombiniert
ping und traceroute über die Zeit und zeigt, an welchem Hop die Werte
auseinanderlaufen:
mtr -n -c 20 server.example # Debian: Paket mtr-tiny
mtr -n -c 20 -T -P 443 server.example # als TCP-Test, wenn ICMP gefiltert wird
iperf3 und mtr vergleichen die
Sicht des Servers und die des Clients. Ein Unterschied zwischen beiden Richtungen ist fast immer ein
Hinweis auf asymmetrische Routen oder Verlust auf dem Rückweg.
Fehlerbild → Befehl
| Symptom | Erster Befehl | Was du siehst |
|---|---|---|
You don't have permission to perform this capture | getcap $(command -v tcpdump) | Fehlende CAP_NET_RAW – sudo nutzen oder Capability setzen |
can't parse filter expression: syntax error | tcpdump -d 'dein filter' | Filter kompiliert nicht – meist ein Shell-Problem (Klammern nicht gequotet) |
| Mitschnitt bleibt leer, obwohl Traffic läuft | tcpdump -nn -i any -c 5 | Falsches Interface (z. B. lo statt any) oder Filter zu eng |
| „N packets dropped by kernel“ beim Beenden | sudo tcpdump -i any -B 4096 -w datei.pcap | Kernel-Puffer zu klein – größerer Puffer, kleinerer Snaplen oder engerer Filter |
| Container-Verkehr fehlt im Mitschnitt | tcpdump -D | grep br- | Traffic läuft über br-… oder veth…, nicht über das physische Interface |
| TLS-Nutzlast nicht lesbar | tcpdump -nn -A 'tcp port 443' | Nur Handshake-Metadaten – das ist keine Fehlfunktion, sondern der Sinn von TLS |
| Mitschnitt ist riesig, Auswertung stockt | tcpdump -nn -s 128 -C 100 -W 12 -w datei.pcap | Nutzlast fehlt (Absicht), Dateien sind rotiert und begrenzt |
| Verbindungen hängen nach Sekunden | ss -tin state established | Retransmits, wachsende RTO oder app_limited – drei verschiedene Ursachen |
Datenschutz und Aufbewahrung
Ein pcap ist kein technisches Artefakt, sondern eine personenbezogene Datensammlung: Es enthält IP-Adressen, Hostnamen, DNS-Anfragen, TLS-Server-Namen (SNI) – und bei unverschlüsselten Protokollen auch Inhalte wie HTTP-Header, Cookies oder E-Mail-Verkehr im Klartext.
| Maßnahme | Befehl / Einstellung | Warum |
|---|---|---|
| Rechte einschränken | chmod 0700 /var/log/tcpdump, Dateien via -Z tcpdump | Mitschnitte sind lesbare Kommunikationsdaten |
| Nutzlast klein halten | -s 128 | Header statt Inhalte – 128 Byte reichen für Adressen, Ports, Flags |
| Aufbewahrung deckeln | -C 100 -W 12 oder -G 3600 plus Aufräum-Timer | Was nicht existiert, muss auch nicht gelöscht werden |
| Zugriff protokollieren | auditctl -w /var/log/tcpdump -p rwa (auditd) | Nachvollziehbar, wer Mitschnitte gelesen hat |
| Nur anlassbezogen mitschneiden | Timer statt Dauerbetrieb: OnCalendar= bei systemd-Timer | Daueraufzeichnung ohne Anlass ist schwer zu rechtfertigen |
| Fremdverkehr ausschließen | Filter auf die eigene IP: 'host 203.0.113.7' | Nur die eigene Infrastruktur, nicht der ganze Link |
host oder net der eigenen Infrastruktur und kurze Aufbewahrung sind hier keine
Formalität, sondern die Bedingung dafür, dass die Diagnose verhältnismäßig bleibt.
Was hier nicht getestet wurde
Damit du einschätzen kannst, worauf die Angaben in diesem Artikel beruhen – und worauf nicht:
| Angabe | Status |
|---|---|
| Version, Bibliothek und Optionen von tcpdump 4.99.6 | Direkt geprüft (--version, --help, Manpage des Pakets) |
Filter, BPF-Code, Fehlermeldungen, -r/-w, Ausgabeformate | Direkt geprüft – an einem eigens erzeugten Testmitschnitt (9 Pakete) sowie durch Kompilieren der Filter mit -d/-dd |
ss, nstat, ip -s -s, ethtool -S, curl -w | Direkt geprüft – die gezeigten Zahlen stammen vom Testsystem |
Rechteproblem und Fehlermeldung (CAP_NET_RAW) | Direkt geprüft – Mitschnitt als normaler Benutzer war nicht möglich, ebenso mit iftop |
| Paketnamen und Versionen für Debian 13 und CachyOS | Aus den Distributionsquellen geprüft (Paket-Listen und Repository-Metadaten), aber nicht installiert |
| Eine echte Live-Aufnahme über das Netzwerk | Nicht geprüft – im Testsystem fehlten root-Rechte; die Beispielausgaben stammen aus pcap-Dateien |
| Die systemd-Unit aus dem Abschnitt „Mitschnitt als Dienst“ | Nicht gestartet – sie ist aus der Manpage abgeleitet (Optionen, Capabilities, -Z) |
iperf3, mtr, tshark, vnstat, nethogs, darkstat | Nicht ausgeführt – Befehle und Optionen entsprechen der jeweiligen Dokumentation |
Verhalten von -i any im Cooked-Modus (keine MAC-Adressen) | Nicht geprüft – die Geräteliste zeigt any als Pseudogerät; MAC-Filter funktionieren dort nur eingeschränkt |
| Mitschnitt aus Containernamespaces | Nicht geprüft – der Docker-Socket war für den Testaccount nicht erreichbar (Gruppe docker fehlt) |
Fazit
tcpdump ist kein Monitoring-System, sondern ein Mikroskop. Der Unterschied
entscheidet, ob man damit zwei Minuten oder zwei Stunden verbringt: Erst die Zähler
(ss, nstat, vnstat, sar) zeigen, dass etwas
nicht stimmt – dann zeigt tcpdump, was los ist. Und weil ein guter Filter mehr wert
ist als jede Rechenleistung, gehört die Filterfrage vor die Aufnahmefrage.
1. Zähler zuerst:
ss -s, ss -tin, nstat -az,
ip -s -s link2. Rechte:
sudo oder CAP_NET_RAW per systemd-Unit – nicht
setcap und vergessen3. Mitschnitt immer begrenzen:
-nn -i any -c 100 und ein Filter4. Lange Mitschnitte nur mit Rotation (
-C … -W …) und kleiner
-s-Grenze5. Verlauf aus Zählern (
vnstat, sar, Netdata), Details aus pcap
(tshark)6. Aufbewahrung definieren: Rechte
0700, harte Obergrenze, kurze Löschfrist