This post was originally published on go2linux.org. The domain is no longer mine, but I am the original author. I am republishing it here on garron.me with corrections and improvements.
tcpdump is the classic Linux packet sniffer. It prints one line per packet that crosses a network interface, or saves the packets to a file for later analysis.
Years ago, as part of my job, I had to help implement VLANs in a satellite hub. We needed to check whether the VLAN tags were attached to the Ethernet frames, and we had two sniffers: a professional Fluke analyzer and my IBM T30 laptop running tcpdump. The Fluke found the tagged and untagged frames, and so did the laptop, at a fraction of the cost.
Install
It is usually installed already. If not:
sudo apt install tcpdump # Debian, Ubuntu
sudo dnf install tcpdump # Fedora, RHEL
sudo pacman -S tcpdump # Arch
Capturing packets needs root, so every example here uses sudo.
Choose the interface
List the interfaces tcpdump can capture on:
sudo tcpdump -D
Then capture on one of them with -i:
sudo tcpdump -i enp3s0
Interface names like eth0 are rare today; ip -br link shows the names on your machine. To listen on all interfaces at once use the pseudo-interface any:
sudo tcpdump -i any
Captures on any are not done in promiscuous mode, so you only see traffic addressed to or sent from the machine itself.
Press Ctrl+C to stop. tcpdump then reports how many packets were captured and how many the kernel dropped.
Options you will use every time
sudo tcpdump -i enp3s0 -nn -c 20
-ndoes not resolve IP addresses to host names. Without ittcpdumpmakes DNS lookups, which is slow and adds its own traffic to the capture.-nnalso leaves port numbers alone, so you see443instead ofhttps.-c 20stops after 20 packets.
A line of output looks like this:
12:01:15.123456 IP 192.0.2.10.51514 > 198.51.100.7.443: Flags [S], seq 1234567890, win 64240, length 0
That is the timestamp, the protocol, source address and port, destination address and port, the TCP flags (S is SYN, . is ACK, P is PUSH, F is FIN, R is RST) and the payload length.
Capture filters
Without a filter you get everything, which on a busy server is useless. Add a filter expression at the end of the command.
By 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
By network:
sudo tcpdump -i enp3s0 -nn net 192.0.2.0/24
By port or port range:
sudo tcpdump -i enp3s0 -nn port 53
sudo tcpdump -i enp3s0 -nn portrange 8000-8100
By protocol:
sudo tcpdump -i enp3s0 -nn icmp
sudo tcpdump -i enp3s0 -nn udp
sudo tcpdump -i enp3s0 -nn arp
Combine them with and, or and not. Put the expression in single quotes when it has parentheses, so the shell does not interpret them:
sudo tcpdump -i enp3s0 -nn 'host 192.0.2.10 and (port 80 or port 443)'
When you are connected over SSH, your own session floods the output. Exclude it:
sudo tcpdump -i enp3s0 -nn not port 22
Only the packets that open new TCP connections (SYN without ACK):
sudo tcpdump -i enp3s0 -nn 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn'
Look inside the packets
-A prints the payload as text, which is enough for plain protocols such as HTTP or SMTP:
sudo tcpdump -i enp3s0 -nn -A port 80
-X prints it in hexadecimal and text side by side. Encrypted traffic (HTTPS, SSH) shows only noise, as it should.
Save to a file and read it back
-w writes the raw packets to a file instead of printing them:
sudo tcpdump -i enp3s0 -nn -w capture.pcap port 443
Read the file later with -r. Filters work here too, so you can capture broadly and narrow down afterwards:
tcpdump -nn -r capture.pcap
tcpdump -nn -r capture.pcap host 192.0.2.10
The same file opens in Wireshark, which is the comfortable way to follow a TCP stream or decode a protocol. The original version of this post recommended Ethereal; that project was renamed Wireshark in 2006.
On some distributions tcpdump drops root privileges to an unprivileged user after opening the interface, and then cannot write in your home directory. If you get Permission denied on the output file, write to /tmp or add -Z root.
Long captures: rotate the files
To leave a capture running while you wait for a problem to show up, limit the size of each file with -C (in millions of bytes) and the number of files with -W. When the last file is full tcpdump overwrites the first one:
sudo tcpdump -i enp3s0 -nn -w trace.pcap -C 100 -W 10
That keeps at most 1 GB on disk. To rotate by time instead, use -G with a file name containing strftime fields:
sudo tcpdump -i enp3s0 -nn -w 'trace-%Y%m%d-%H%M.pcap' -G 3600
See VLAN tags
This is the case from the story above. -e prints the link-level header, which includes the 802.1Q tag, and the vlan filter keeps only tagged frames:
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
For one VLAN only:
sudo tcpdump -i enp3s0 -e -nn vlan 100
Capture on the physical interface. On a VLAN sub-interface such as enp3s0.100 the kernel has already removed the tag.
Pipe the output
tcpdump buffers its output when it is not writing to a terminal. Add -l to make it line buffered when you pipe it into another command:
sudo tcpdump -i enp3s0 -nn -l port 53 | grep example.com
Practical examples
DNS queries leaving the machine:
sudo tcpdump -i any -nn udp port 53
Check whether packets from a client reach the server at all:
sudo tcpdump -i enp3s0 -nn host 192.0.2.10 and port 443
Ping requests and replies:
sudo tcpdump -i enp3s0 -nn icmp
DHCP traffic:
sudo tcpdump -i enp3s0 -nn port 67 or port 68
See also
man tcpdump — full reference. man pcap-filter — the complete filter syntax.