tcpdump imprime les paquets qui traversent une interface réseau — et seulement ceux que vous demandez. Cette deuxième moitié, c’est tout l’intérêt. Vous lui donnez une expression de filtre comme tcp port 443 and host 10.0.3.9, le noyau confronte chaque paquet à ce filtre, et seuls les survivants sont copiés vers tcpdump pour être décodés. Ce qui arrive sur votre terminal, c’est une ligne par paquet, en ordre, avec un horodatage à la microseconde. Ça répond à une question qu’aucun journal applicatif ne peut trancher : le paquet est-il vraiment parti de cette machine, quelque chose est-il revenu, et quel était l’écart. Quand votre appli dit « délai de connexion dépassé » et que l’équipe d’en face dit « on n’a jamais reçu de requête », tcpdump est ce qui règle la question.
Ce que vous allez accomplir
- Capturer sur la bonne interface avec un filtre assez étroit pour que la sortie soit lisible.
- Écrire des expressions BPF par
host,port,netet drapeau TCP — y compris les motifs SYN-sans-SYN-ACK et RST. - Sauvegarder une capture avec
-w, la relire avec-r, et la faire tourner avec-C/-Wpour qu’une longue capture ne remplisse jamais le disque. - Passer le même fichier à Wireshark quand vous avez besoin d’une analyse par flux plutôt que d’une liste à plat.
- Distinguer quatre pannes qui se ressemblent toutes vues de l’application : rien d’envoyé, rien d’arrivé, refus actif, ou perte suivie de retransmissions.
Prérequis
- tcpdump installé. Vérifiez avec
tcpdump --version; vous devriez voir quelque chose commetcpdump version 4.99.4suivi delibpcap version 1.10.4. La ligne 4.99 est l’actuelle ; toute version 4.9 ou plus récente prend en charge chaque drapeau de ce guide. Installez-le avecapt install tcpdump,dnf install tcpdump; sur macOS, il est fourni avec le système. - Le compte root, ou les capacités qui en tiennent lieu. La capture de paquets exige
CAP_NET_RAWetCAP_NET_ADMIN, donc en pratique vous lancezsudo tcpdump. Certaines distributions les accordent plutôt au binaire :sudo setcap cap_net_raw,cap_net_admin=eip /usr/sbin/tcpdumppermet à un utilisateur normal de capturer sans sudo. Là où vous n’avez ni l’un ni l’autre, vous ne capturez pas — il n’y a pas de solution de repli sans privilèges. - Connaître le nom de votre interface. Linux moderne utilise des noms prévisibles (
ens5,enp0s3,eno1) plutôt queeth0; les images infonuagiques varient selon l’hyperviseur. macOS utiliseen0. Linux offre aussi la pseudo-interfaceany, qui capture sur toutes les interfaces à la fois — elle n’existe pas sur macOS ni sur BSD. - Wireshark sur votre portable, si vous voulez l’étape d’analyse en profondeur. Vous ne l’installez pas sur le serveur ; vous rapatriez le fichier pcap.
Comment ça marche
tcpdump ne lit pas les paquets un à un pour jeter ceux dont vous ne voulez pas. Votre expression de filtre est compilée en bytecode BPF et remise au noyau, qui l’applique à chaque trame dès son arrivée. Les paquets qui échouent au filtre sont écartés dans le noyau et ne sont jamais copiés vers l’espace utilisateur. C’est pourquoi un filtre est une décision de performance et pas seulement de propreté : sur un hôte occupé, tcpdump -i eth0 sans filtre peut perdre des paquets simplement parce que la boucle copie-puis-décode ne suit pas — et les paquets perdus sont peut-être exactement ceux qu’il vous fallait.
Deux conséquences en découlent. D’abord, le filtre que vous passez en ligne de commande est un filtre de capture — il décide de ce qui est enregistré, et ce qu’il rejette est perdu pour de bon. Ce n’est pas la même chose qu’un filtre d’affichage Wireshark, qui masque des paquets déjà capturés et qu’on peut changer librement après coup. Ensuite, la portion de chaque paquet que vous gardez est une décision distincte, contrôlée par -s (le snaplen).
Étape par étape
1. Trouvez sur quoi vous pouvez capturer. -D liste les interfaces que libpcap voit, ce qui est plus rapide et plus honnête que de deviner à partir de ip addr :
sudo tcpdump -D
1.eth0 [Up, Running, Connected]
2.any (Pseudo-device that captures on all interfaces) [Up, Running]
3.lo [Up, Running, Loopback]
4.docker0 [Up, Running]
2. Prenez la plus petite capture qui prouve que tcpdump fonctionne. -i choisit l’interface, -c arrête après N paquets pour que vous ne restiez jamais le doigt sur Ctrl-C, et -nn coupe à la fois les recherches DNS inverses et la traduction des noms de ports. Utilisez toujours -nn sur un serveur : sans lui, tcpdump fait une recherche DNS par adresse, ce qui est lent et ajoute son propre trafic à la capture que vous êtes en train de lire.
sudo tcpdump -i eth0 -nn -c 5
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
09:14:02.184417 IP 10.0.3.11.51234 > 10.0.3.9.443: Flags [S], seq 2934857021, win 64240, length 0
09:14:02.184903 IP 10.0.3.9.443 > 10.0.3.11.51234: Flags [S.], seq 118392, ack 2934857022, win 65160, length 0
09:14:02.184961 IP 10.0.3.11.51234 > 10.0.3.9.443: Flags [.], ack 1, win 502, length 0
09:14:02.186740 IP 10.0.3.11.51234 > 10.0.3.9.443: Flags [P.], seq 1:518, ack 1, win 502, length 517
09:14:02.187201 IP 10.0.3.9.443 > 10.0.3.11.51234: Flags [.], ack 518, win 509, length 0
5 packets captured
Lisez une ligne : horodatage, IP, adresse et port source, >, adresse et port destination, puis les drapeaux TCP. [S] c’est SYN, [S.] c’est SYN-ACK (le point signifie ACK), [.] c’est un ACK seul, [P.] c’est un push-ACK qui transporte des données, [F.] c’est FIN, [R] c’est RST.
3. Resserrez avec les primitives. Une expression BPF se bâtit à partir de quelques primitives combinées avec and, or et not. Celles qui comptent :
sudo tcpdump -i eth0 -nn host 10.0.3.9
sudo tcpdump -i eth0 -nn port 443
sudo tcpdump -i eth0 -nn net 10.0.3.0/24
sudo tcpdump -i eth0 -nn tcp and dst port 5432
sudo tcpdump -i eth0 -nn 'host 10.0.3.9 and not port 22'
host correspond dans les deux sens ; src host et dst host le fixent à un seul. Pareil pour port. Encadrez toute l’expression de guillemets simples dès qu’elle contient quoi que ce soit que le shell toucherait — parenthèses, |, !. Ce dernier exemple est celui que vous taperez le plus : un pair précis, moins votre propre session SSH, qui autrement inonderait la sortie de chaque frappe.
4. Posez la première vraie question : le SYN a-t-il eu une réponse ? C’est le diagnostic derrière la plupart des signalements de « délai de connexion dépassé ». Capturez chaque paquet vers ce pair qui porte un SYN ou un ACK — dans une session en santé, c’est presque tous ; dans une session brisée, ce ne sont que les SYN, et c’est justement l’indice :
sudo tcpdump -i eth0 -nn -c 20 'host 10.0.3.9 and tcp[tcpflags] & (tcp-syn|tcp-ack) != 0'
09:22:41.003912 IP 10.0.3.11.44120 > 10.0.3.9.5432: Flags [S], seq 771002455, win 64240, length 0
09:23:42.014883 IP 10.0.3.11.44120 > 10.0.3.9.5432: Flags [S], seq 771002455, win 64240, length 0
09:24:44.032190 IP 10.0.3.11.44120 > 10.0.3.9.5432: Flags [S], seq 771002455, win 64240, length 0
Trois SYN, même numéro de séquence, à environ une minute d’intervalle, et jamais de SYN-ACK : le noyau réessaie la poignée de main dans le vide. Votre paquet est parti de cette machine et rien n’a répondu. C’est un pare-feu, un groupe de sécurité ou une route — pas votre application. La syntaxe tcp[tcpflags] lit directement l’octet de drapeaux TCP ; tcp-syn, tcp-ack, tcp-rst, tcp-fin, tcp-push sont des constantes nommées pour ses bits, et & est un ET binaire. Si vous voulez les SYN qui ne sont pas aussi des ACK — les tentatives de connexion seulement, sans les réponses — testez plutôt l’égalité : 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn'.
5. Distinguez ça d’un refus actif. Un délai dépassé et un refus sont deux pannes différentes qui donnent le même message d’erreur chez certains clients. Cherchez les RST :
sudo tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0'
09:31:18.442091 IP 10.0.3.9.5432 > 10.0.3.11.44980: Flags [R.], seq 0, ack 1, win 0, length 0
Un RST qui arrive une milliseconde après votre SYN veut dire que le paquet s’est rendu à l’hôte et que l’hôte a dit non — rien n’écoute sur ce port, ou quelque chose a réinitialisé la connexion délibérément. Le silence veut dire qu’il ne s’y est jamais rendu. Ces deux constats vous envoient vers deux équipes différentes.
6. Surveillez la résolution de noms. Les pannes DNS se manifestent en erreurs applicatives qui ne mentionnent jamais le DNS. Capturez les deux transports, puisque les grosses réponses basculent en TCP :
sudo tcpdump -i eth0 -nn -s 0 'udp port 53 or tcp port 53'
09:35:07.112004 IP 10.0.3.11.39114 > 10.0.0.2.53: 41288+ A? db.internal.example.com. (41)
09:35:07.113990 IP 10.0.0.2.53 > 10.0.3.11.39114: 41288 1/0/0 A 10.0.3.9 (57)
Une requête sans réponse correspondante veut dire que votre résolveur est injoignable. Une réponse qui porte la mauvaise adresse veut dire que votre résolveur va bien et que vos données de zone, non.
7. Gardez tout le paquet — ou volontairement pas. -s fixe le snaplen, le nombre d’octets enregistrés par paquet. tcpdump moderne utilise 262144 par défaut, soit en pratique le paquet complet, et -s 0 demande le maximum explicitement. Réduisez-le quand seuls les en-têtes vous intéressent et que vous voulez couper la taille du fichier et le CPU sur un lien chargé :
sudo tcpdump -i eth0 -nn -s 96 -c 5 tcp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 96 bytes
09:41:12.660713 IP 10.0.3.11.44688 > 10.0.3.9.5432: Flags [P.], seq 1:88, ack 1, win 502, length 87
09:41:12.661204 IP 10.0.3.9.5432 > 10.0.3.11.44688: Flags [.], ack 88, win 509, length 0
5 packets captured
Le signal de succès, c’est snapshot length 96 bytes à la deuxième ligne — c’est tcpdump qui confirme que -s a pris effet. Remarquez que length 87 donne toujours la taille réelle du paquet, lue dans l’en-tête IP, même si seulement 96 octets ont été conservés.
96 octets couvrent les en-têtes Ethernet, IP et TCP avec de la marge, donc les drapeaux, les numéros de séquence et les ports survivent tous — la charge utile, non. Si l’analyse finit par exiger la charge utile, vous ne pouvez plus la récupérer : ne tronquez que si vous êtes certain que l’histoire est dans les en-têtes.
8. Écrivez dans un fichier au lieu de l’écran. -w écrit du pcap brut : pas de décodage, pas de DNS inverse, beaucoup moins de CPU, et un fichier que vous pouvez analyser n’importe où. C’est ce que vous lancez quand la panne est intermittente :
sudo tcpdump -i eth0 -w /var/tmp/db.pcap 'host 10.0.3.9 and tcp port 5432'
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
^C1843 packets captured
1847 packets received by filter
0 packets dropped by kernel
Ces trois dernières lignes sont le signal de succès, et 0 packets dropped by kernel est celle qui compte. Un compte de pertes non nul veut dire que votre filtre était trop large ou l’hôte trop occupé, et que la capture a des trous.
9. Bornez-la avec un tampon circulaire. Un -w laissé sans surveillance remplira joyeusement le disque et emportera l’hôte avec lui. -C plafonne chaque fichier à N millions d’octets et -W plafonne le nombre de fichiers conservés, en écrasant le plus vieux — un plafond fixe sur l’usage disque, pour toujours :
sudo tcpdump -i eth0 -s 128 -C 100 -W 10 -w /var/tmp/ring.pcap 'tcp port 5432'
Laissez-la tourner, puis vérifiez que la rotation se fait vraiment :
ls -1 /var/tmp/ring.pcap*
/var/tmp/ring.pcap0
/var/tmp/ring.pcap1
/var/tmp/ring.pcap2
Les fichiers qui apparaissent et dont le nombre ne dépasse jamais -W, c’est le signal de succès.
Ça laisse au plus dix fichiers d’environ 100 Mo chacun — un gigaoctet, jamais plus — nommés ring.pcap0 à ring.pcap9. Quand la panne se déclenche, arrêtez la capture : les derniers fichiers contiennent les minutes qui l’ont précédée. Utilisez plutôt -G 3600 avec un motif strftime dans -w si vous préférez faire tourner selon le temps plutôt que selon la taille.
10. Relisez-la. -r lit un pcap et lui applique un filtre neuf, de sorte qu’une capture large peut être réexaminée étroitement autant de fois que vous voulez — sans root, puisque rien n’est capturé :
tcpdump -r /var/tmp/db.pcap -nn 'tcp[tcpflags] & tcp-rst != 0' | head -5
reading from file /var/tmp/db.pcap, link-type EN10MB (Ethernet), snapshot length 262144
09:31:18.442091 IP 10.0.3.9.5432 > 10.0.3.11.44980: Flags [R.], seq 0, ack 1, win 0, length 0
La ligne reading from file confirme que le pcap s’est ouvert ; chaque ligne suivante est un paquet qui a survécu au nouveau filtre. Aucune correspondance veut dire que la capture ne contient aucun RST — ce qui est déjà une réponse.
Pour tout ce qui dépasse la liste à plat — suivre une connexion, voir les temps d’aller-retour par flux, repérer des retransmissions sans lire les numéros de séquence à l’œil — rapatriez le fichier sur votre portable et ouvrez-le dans Wireshark. Son filtre d’affichage tcp.analysis.retransmission et la vue Statistics > Conversations font en deux clics ce qui demanderait une heure à plisser les yeux devant du texte de terminal.
Valider que ça fonctionne
Prouvez toute la boucle de bout en bout contre du trafic que vous causez vous-même. Dans un terminal, démarrez une capture filtrée sur un hôte que vous pouvez joindre :
sudo tcpdump -i eth0 -nn -c 10 'host 1.1.1.1 and tcp port 443'
Dans un deuxième terminal, générez exactement une connexion :
curl -s -o /dev/null https://1.1.1.1/
Le premier terminal devrait imprimer la poignée de main puis s’arrêter tout seul à dix paquets :
10:02:55.771092 IP 10.0.3.11.55302 > 1.1.1.1.443: Flags [S], seq 1855049103, win 64240, length 0
10:02:55.781553 IP 1.1.1.1.443 > 10.0.3.11.55302: Flags [S.], seq 3277430901, ack 1855049104, win 65535, length 0
10:02:55.781601 IP 10.0.3.11.55302 > 1.1.1.1.443: Flags [.], ack 1, win 502, length 0
10 packets captured
SYN sortant, SYN-ACK de retour une dizaine de millisecondes plus tard, ACK sortant : une poignée de main en santé, et le temps d’aller-retour est là, dans les horodatages. C’est votre référence de ce à quoi « ça marche » ressemble — chaque diagnostic de ce guide est un écart par rapport à ces trois lignes.
Dépannage
You don't have permission to capture on that device. La capture exigeCAP_NET_RAW; vous êtes un utilisateur normal. Utilisezsudo, ou accordez les capacités au binaire :sudo setcap cap_net_raw,cap_net_admin=eip $(which tcpdump). Dans un conteneur, il vous faut aussi la capacité sur le conteneur lui-même, pas seulement sur le binaire — un conteneur Docker par défaut ne peut pas capturer, et c’est--cap-add=NET_ADMIN --cap-add=NET_RAWqui change ça.- Rien ne s’imprime du tout. Neuf fois sur dix, c’est la mauvaise interface, pas le mauvais filtre. Confirmez avec
tcpdump -D, puis laissez tomber le filtre complètement et lancezsudo tcpdump -i any -nn -c 5— si ça imprime, votre filtre était fautif ; sinon, le trafic ne traverse pas cet hôte. Méfiez-vous du trafic surlo: deux processus sur la même machine se parlent par la boucle locale, et une capture sureth0ne les verra jamais. Les conteneurs ont leur propre espace de noms réseau, alors une capture sur l’hôte rate le trafic interne au conteneur. packets dropped by kerneln’est pas à zéro. Le noyau a capturé plus que ce que tcpdump pouvait consommer. Resserrez le filtre, coupez le snaplen avec-s 96, et écrivez avec-wplutôt que de décoder vers un terminal — c’est le décodage qui est lent. Une capture avec des pertes a des trous silencieux, et un paquet « manquant » est peut-être simplement un paquet que vous n’avez jamais enregistré.- Le filtre de capture n’est pas un filtre d’affichage.
tcpdump 'tcp port 443'ettcp.port == 443de Wireshark se ressemblent sans être le même langage, et surtout ne sont pas le même moment. Le filtre de tcpdump s’exécute avant l’enregistrement ; ce qu’il rejette est irrécupérable. Celui de Wireshark s’exécute après, sur un fichier qui contient déjà tout. Dans le doute pendant un incident, capturez un peu plus large que nécessaire vers un fichier, et resserrez ensuite avec-r. - Vous ne voyez que le trafic de votre propre machine. tcpdump met l’interface en mode promiscuous par défaut, mais sur une VM infonuagique, le commutateur virtuel ne vous livrera pas les trames des autres locataires, peu importe le mode que vous demandez — le vieux modèle « branche-toi sur un concentrateur et vois tout » n’existe pas là. Pour voir le trafic entre deux autres hôtes, il vous faut un port miroir, un TAP, ou une capture sur l’un des points terminaux. Utilisez
-ppour sauter le mode promiscuous quand vous ne voulez que le trafic de cet hôte, ce qui risque aussi moins de déclencher une alerte de détection d’intrusion.
Quand c’est la mauvaise approche
tcpdump vous dit ce qui a traversé le fil, ce qui cesse d’être utile dès que la partie intéressante est chiffrée. Une session TLS vous montre la poignée de main, le SNI, puis des milliers de paquets [P.] opaques — vous voyez que des données ont circulé et à quelle vitesse, jamais ce qu’elles disaient. Si votre question porte sur des codes de statut HTTP ou des corps de réponse, les journaux applicatifs et décomposer la latence d’une requête avec curl vous y mènent plus vite et sans root. Si votre question est « est-ce que quelque chose écoute sur ce port », ss -ltnp y répond en une milliseconde plutôt qu’en une capture. Et si vous poursuivez un mandataire qui renvoie des erreurs plutôt qu’un chemin réseau qui flanche, commencez par pourquoi nginx renvoie 502 Bad Gateway — l’amont est généralement joignable et simplement mécontent. La capture de paquets est aussi une surface de vie privée : une capture non filtrée sur un hôte de production enregistre les données d’autrui, alors gardez les filtres étroits et les fichiers pcap éphémères. Les pages de manuel et la version courante vivent sur tcpdump.org.
Si c’est le genre de travail que vous préféreriez ne plus jamais porter, c’est le quotidien d’Opérations Cloud — réservez une revue d’infrastructure.