Este artículo fue publicado originalmente en go2linux.org. El dominio ya no me pertenece, pero soy el autor original. Lo republico aquí en garron.me con correcciones y mejoras.

tcpdump es el sniffer de paquetes clásico de Linux. Imprime una línea por cada paquete que pasa por una interfaz de red, o guarda los paquetes en un archivo para analizarlos después.

Hace años, como parte de mi trabajo, tuve que ayudar a implementar VLANs en un hub satelital. Necesitábamos comprobar si las etiquetas VLAN estaban presentes en las tramas Ethernet, y teníamos dos sniffers: un analizador profesional Fluke y mi laptop IBM T30 con tcpdump. El Fluke encontró las tramas etiquetadas y las no etiquetadas, y la laptop también, por una fracción del costo.

Instalación

Normalmente ya está instalado. Si no:

sudo apt install tcpdump      # Debian, Ubuntu
sudo dnf install tcpdump      # Fedora, RHEL
sudo pacman -S tcpdump        # Arch

Capturar paquetes requiere root, así que todos los ejemplos usan sudo.

Elegir la interfaz

Lista las interfaces en las que tcpdump puede capturar:

sudo tcpdump -D

Luego captura en una de ellas con -i:

sudo tcpdump -i enp3s0

Nombres como eth0 son raros hoy; ip -br link muestra los nombres en tu equipo. Para escuchar en todas las interfaces a la vez usa la pseudointerfaz any:

sudo tcpdump -i any

Las capturas en any no se hacen en modo promiscuo, así que solo ves el tráfico dirigido al equipo o enviado por él.

Presiona Ctrl+C para detenerlo. tcpdump informa entonces cuántos paquetes capturó y cuántos descartó el kernel.

Opciones que usarás siempre

sudo tcpdump -i enp3s0 -nn -c 20
  • -n no resuelve las direcciones IP a nombres. Sin esta opción tcpdump hace consultas DNS, lo que es lento y agrega su propio tráfico a la captura.
  • -nn tampoco traduce los números de puerto, así que ves 443 en lugar de https.
  • -c 20 se detiene después de 20 paquetes.

Una línea de salida se ve así:

12:01:15.123456 IP 192.0.2.10.51514 > 198.51.100.7.443: Flags [S], seq 1234567890, win 64240, length 0

Es la marca de tiempo, el protocolo, dirección y puerto de origen, dirección y puerto de destino, las banderas TCP (S es SYN, . es ACK, P es PUSH, F es FIN, R es RST) y el tamaño de los datos.

Filtros de captura

Sin un filtro lo recibes todo, y en un servidor con tráfico eso no sirve de nada. Agrega una expresión de filtro al final del comando.

Por host:

sudo tcpdump -i enp3s0 -nn host 192.0.2.10
sudo tcpdump -i enp3s0 -nn src 192.0.2.10
sudo tcpdump -i enp3s0 -nn dst 192.0.2.10

Por red:

sudo tcpdump -i enp3s0 -nn net 192.0.2.0/24

Por puerto o rango de puertos:

sudo tcpdump -i enp3s0 -nn port 53
sudo tcpdump -i enp3s0 -nn portrange 8000-8100

Por protocolo:

sudo tcpdump -i enp3s0 -nn icmp
sudo tcpdump -i enp3s0 -nn udp
sudo tcpdump -i enp3s0 -nn arp

Combínalos con and, or y not. Pon la expresión entre comillas simples cuando tenga paréntesis, para que el shell no los interprete:

sudo tcpdump -i enp3s0 -nn 'host 192.0.2.10 and (port 80 or port 443)'

Cuando estás conectado por SSH, tu propia sesión inunda la salida. Exclúyela:

sudo tcpdump -i enp3s0 -nn not port 22

Hay más ejemplos de este tipo en Excluyendo puertos o IPs de la salida de tcpdump.

Solo los paquetes que abren conexiones TCP nuevas (SYN sin ACK):

sudo tcpdump -i enp3s0 -nn 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn'

Ver el contenido de los paquetes

-A imprime el contenido como texto, suficiente para protocolos en texto plano como HTTP o SMTP:

sudo tcpdump -i enp3s0 -nn -A port 80

-X lo imprime en hexadecimal y texto lado a lado. El tráfico cifrado (HTTPS, SSH) solo muestra ruido, como debe ser.

Guardar en un archivo y leerlo después

-w escribe los paquetes en bruto en un archivo en lugar de imprimirlos:

sudo tcpdump -i enp3s0 -nn -w captura.pcap port 443

Lee el archivo después con -r. Los filtros también funcionan aquí, así que puedes capturar de forma amplia y afinar después:

tcpdump -nn -r captura.pcap
tcpdump -nn -r captura.pcap host 192.0.2.10

El mismo archivo se abre en Wireshark, que es la forma cómoda de seguir un flujo TCP o decodificar un protocolo. La versión original de este artículo recomendaba Ethereal; ese proyecto pasó a llamarse Wireshark en 2006.

En algunas distribuciones tcpdump abandona los privilegios de root y pasa a un usuario sin privilegios después de abrir la interfaz, y entonces no puede escribir en tu directorio personal. Si obtienes Permission denied en el archivo de salida, escribe en /tmp o agrega -Z root.

Capturas largas: rotar los archivos

Para dejar una captura corriendo mientras esperas que aparezca un problema, limita el tamaño de cada archivo con -C (en millones de bytes) y la cantidad de archivos con -W. Cuando el último archivo se llena, tcpdump sobrescribe el primero:

sudo tcpdump -i enp3s0 -nn -w traza.pcap -C 100 -W 10

Eso ocupa como máximo 1 GB en disco. Para rotar por tiempo, usa -G con un nombre de archivo que contenga campos de strftime:

sudo tcpdump -i enp3s0 -nn -w 'traza-%Y%m%d-%H%M.pcap' -G 3600

Ver etiquetas VLAN

Este es el caso de la historia de arriba. -e imprime la cabecera de nivel de enlace, que incluye la etiqueta 802.1Q, y el filtro vlan deja solo las tramas etiquetadas:

sudo tcpdump -i enp3s0 -e -nn vlan
12:01:15.123456 00:11:22:33:44:55 > 66:77:88:99:aa:bb, ethertype 802.1Q (0x8100), length 102: vlan 100, p 0, ethertype IPv4 (0x0800), 192.0.2.10 > 192.0.2.1: ICMP echo request, id 1, seq 1, length 64

Para una sola VLAN:

sudo tcpdump -i enp3s0 -e -nn vlan 100

Captura en la interfaz física. En una subinterfaz VLAN como enp3s0.100 el kernel ya quitó la etiqueta.

Enviar la salida a otro comando

tcpdump almacena su salida en un búfer cuando no escribe en una terminal. Agrega -l para que la envíe línea por línea cuando la pasas a otro comando:

sudo tcpdump -i enp3s0 -nn -l port 53 | grep example.com

Ejemplos prácticos

Consultas DNS que salen del equipo:

sudo tcpdump -i any -nn udp port 53

Comprobar si los paquetes de un cliente llegan al servidor:

sudo tcpdump -i enp3s0 -nn host 192.0.2.10 and port 443

Peticiones y respuestas de ping:

sudo tcpdump -i enp3s0 -nn icmp

Tráfico DHCP:

sudo tcpdump -i enp3s0 -nn port 67 or port 68

Ver también

man tcpdump — referencia completa. man pcap-filter — la sintaxis completa de los filtros.