Linux server connected to a network switch with Ethernet cables during packet capture troubleshooting
Networking Tutorials
William  

A Practical Linux Tcpdump Workflow for Packet Capture

Start with the interface, the filter, and a capture file. That is how to use tcpdump on Linux without turning packet capture into guesswork: identify where the traffic lives, write a filter that matches only the traffic you need, save enough evidence, then compare it with logs, strace, routing, and socket state. Copy-pasted commands fail when they watch the wrong interface or mistake a packet listing for a diagnosis.

What does tcpdump capture, and when should you use it?

tcpdump captures packets seen by the host's packet capture layer. It shows headers, timing, addresses, ports, TCP flags, packet lengths, and payload bytes when those bytes are available.

That lets you prove useful things:

  • A DNS request left the host.
  • A server sent a TCP reset.
  • A client retransmitted because no acknowledgment arrived.
  • An interface received packets but the application never opened a socket.
  • Traffic went to the wrong address or port.
  • A firewall or route changed the path.

However, tcpdump can't show what never reached the capture point. It can't see packets on another host unless you capture there, and it can't read encrypted application content without the required keys. It also can't tell you why an application chose a bad URL, blocked on a file descriptor, or discarded valid data after receiving it.

That is why packet evidence needs a second source. Use strace to see connect, sendto, recvfrom, and timeout behavior. Use journalctl for service errors, ss for socket state, ip route for routing, and dmesg for kernel drops or driver trouble. The packet trace tells you what crossed the interface. The other tools tell you what the process and kernel did with it.

The tcpdump manual documents the capture options and filter language. Read it when a filter becomes complicated. Guessing at packet offsets is how a short investigation becomes folklore.

What permissions and setup does tcpdump need on Linux?

Administrator connecting Ethernet cables to a Linux server and network switch in a home lab

Run the first capture with sudo. It removes one variable while you establish whether the capture works.

command -v tcpdump
tcpdump --version
sudo tcpdump -D

command -v confirms that the shell can find the program. tcpdump --version shows the installed build and packet-capture library. sudo tcpdump -D lists interfaces that the capture library can open.

Some distributions grant capture rights through a dedicated group or Linux capabilities. Check how your distribution packaged the tool before changing permissions:

getcap "$(command -v tcpdump)"
id

A capture group can be safer than giving every operator full root access. It still grants access to traffic that may contain credentials, tokens, personal data, or internal addresses. Treat capture files as sensitive system data.

Do not start by changing permissions with a broad chmod. If the package already provides a group, add only the required users and make the capture directory private:

sudo install -d -m 0700 /var/tmp/pcap-investigation

The capture location matters. A process in the host network namespace sees host interfaces. A process in a container usually sees only its own network namespace and virtual ethernet device. To inspect a named namespace, run the capture inside it:

sudo ip netns list
sudo ip netns exec NAME tcpdump -ni any

For a running container, find its process ID and enter its network namespace:

PID="$(docker inspect -f '{{.State.Pid}}' CONTAINER)"
sudo nsenter -t "$PID" -n tcpdump -D

Replace CONTAINER and NAME with real values. A bridge capture and a container-side capture answer different questions. Capture on the bridge to inspect forwarding. Capture inside the namespace to inspect what the application sees.

How do you use tcpdump on Linux from the command line?

Use this structure:

tcpdump [options] [filter expression]

A safe starting command looks like this:

sudo tcpdump -ni eth0 -nn -tttt -c COUNT 'host SERVER and tcp port PORT'

Here is what each part does:

  • -i eth0 selects the interface.
  • -n disables name resolution for interface and address display.
  • -nn also keeps port numbers from becoming service names.
  • -t controls timestamp output. Repeating it changes the format.
  • -c COUNT stops after the requested packet count.
  • The quoted expression limits which packets are captured.

Use -nn during diagnosis. Name lookups can delay output, create extra traffic, and turn a packet problem into a resolver problem. You can resolve names later if that helps a reader.

What do the tcpdump output options change?

Add verbosity only when the default summary leaves out the field you need:

sudo tcpdump -ni eth0 -nn -vvv 'udp port 53'

-v, -vv, and -vvv print progressively more protocol detail. More output is not more evidence. It is usually more noise.

Use -s 0 when you need the full packet rather than a truncated snapshot:

sudo tcpdump -ni eth0 -nn -s 0 -c COUNT -w dns.pcap 'udp port 53'

Stop an interactive capture with Ctrl-C. The statistics printed at exit matter. They show how many packets matched the filter and whether the kernel reported drops. A capture that ends with drops is not complete evidence.

Use -c COUNT for a bounded test. Use a shell timeout when you need a time limit instead:

sudo timeout --signal=INT DURATION \
 tcpdump -ni eth0 -nn -s 0 -w investigation.pcap 'host SERVER and tcp port PORT'

DURATION can be a value accepted by timeout, such as 30s or 2m. The interrupt signal gives tcpdump a chance to flush the file and print its statistics. Killing the process without cleanup is an unnecessary way to damage your evidence.

How do you find the correct network interface?

Close-up of Linux server network interfaces and color-coded Ethernet cables beside a switch

Do not assume eth0 exists. Modern systems often use names such as enp1s0, ens18, wlan0, br0, virbr0, or veth....

List capture interfaces first:

sudo tcpdump -D

Then compare that list with the kernel's view:

ip -br link
ip -br addr

The interface is the interface on which the utility captures packets. The -i interface option selects the network interface on which tcpdump captures packets.

Read the interface name as a location clue, not as decoration:

Interface typeWhat it usually tells you
Physical ethernet or wireless interfaceTraffic entering or leaving that device
Bridge such as br0Traffic forwarded through a software bridge
VLAN interfaceTraffic after VLAN handling at that interface
Loopback such as loLocal client-server traffic on the same host
Virtual ethernet such as veth...One side of a namespace or container link
anyA Linux pseudo-interface covering multiple interfaces

-i any is useful for discovery:

sudo tcpdump -ni any -nn -c COUNT

It is not a replacement for choosing the real interface. It can hide which link carried a packet, and link-layer details differ from a physical-interface capture. Once you know the path, capture on that interface.

Run a short unfiltered capture before adding a filter:

sudo tcpdump -ni eth0 -nn -c COUNT

Generate the traffic while it runs. If nothing appears, the filter is not your problem yet. Check the interface, namespace, link state, route, and whether the traffic actually leaves this host.

For a namespace-specific test, list interfaces from that namespace:

sudo ip netns exec NAME ip -br addr
sudo ip netns exec NAME tcpdump -ni any -nn -c COUNT

The capture must be placed where the packets exist. A host capture cannot see traffic that stays inside another namespace.

How do you build Boolean tcpdump filter expressions?

The filter language is a packet expression, not a shell command. Quote it so the shell does not interpret parentheses or operators first.

sudo tcpdump -ni eth0 -nn \
 'host 192.0.2.10 and (tcp port 443 or udp port 53)'

Use these operators:

  • and requires both conditions.
  • or accepts either condition.
  • not excludes a condition.
  • Parentheses make grouping explicit.

The usual precedence is not, then and, then or. Do not rely on that order when the expression matters. Add parentheses.

This filter means something different:

'host 192.0.2.10 and tcp port 443 or udp port 53'

It can match all DNS traffic, even when it does not involve the host you intended. The grouped version is clear:

'host 192.0.2.10 and (tcp port 443 or udp port 53)'

Build filters from the broad condition to the narrow one. Start with a host or network. Add the protocol. Add the port. Then add direction or flags if the output is still too large.

Compile the filter without capturing if you want to catch syntax errors:

tcpdump -d 'host 192.0.2.10 and (tcp port 443 or udp port 53)'

The output is a Berkeley Packet Filter program. You don't need to read every instruction, but a compile failure tells you the expression needs repair before you blame the network.

How do you filter traffic by host, network, and port?

Use host when either direction matters:

'host 192.0.2.10'

Use src host and dst host when direction matters:

'src host 192.0.2.10'
'dst host 192.0.2.10'

For networks, use CIDR notation:

'net 192.0.2.0/24'
'src net 192.0.2.0/24'
'dst net 198.51.100.0/24'

For one port, use port:

'tcp port 443'
'udp port 53'

For a port range, use portrange:

'tcp portrange 8000-8100'

A client-server conversation needs both endpoints if you want to avoid unrelated traffic on the same port:

'host CLIENT and host SERVER and tcp port PORT'

For example:

'host 192.0.2.20 and host 192.0.2.30 and tcp port 443'

That expression matches packets between those two hosts on the service port. It does not match every HTTPS connection on the machine.

Exclude noisy traffic only after you have confirmed the main filter works:

'host SERVER and tcp port PORT and not host MONITOR'

Do not start with a long chain of exclusions. If you exclude the evidence by mistake, tcpdump will not complain. It will print an empty capture with complete confidence.

How do you filter specific protocols and traffic directions?

Use protocol qualifiers when the protocol itself matters:

'tcp'
'udp'
'icmp'
'icmp6'
'arp'
'ip'
'ip6'

Common examples:

sudo tcpdump -ni eth0 -nn 'udp port 53 or tcp port 53'
sudo tcpdump -ni eth0 -nn 'arp'
sudo tcpdump -ni eth0 -nn 'icmp or icmp6'
sudo tcpdump -ni eth0 -nn 'ip6'

DNS can use UDP or TCP. Filtering only UDP misses fallback behavior, large responses, and some failure cases. Group the expression so the port condition applies to both protocols.

For direction, use source and destination qualifiers whenever possible:

'src host CLIENT and dst host SERVER'
'src port CLIENT_PORT and dst port SERVER_PORT'

Linux also supports inbound and outbound in capture filters on link types that provide that information:

'inbound and host SERVER'
'outbound and host SERVER'

If those qualifiers fail or produce confusing output, return to source and destination addresses. They are easier to verify and work across more capture points.

TCP flags show connection state. A SYN without an ACK is a connection attempt:

'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn'

SYN-ACK responses look like this:

'tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)'

Resets are useful when a service refuses a connection or a middlebox tears it down:

'tcp[tcpflags] & tcp-rst != 0'

FIN packets show an orderly close:

'tcp[tcpflags] & tcp-fin != 0'

A TCP packet with an ACK is not automatically application data. Many acknowledgments carry no payload. Read the length field in the packet summary and inspect the sequence and acknowledgment numbers. A connection can be established correctly and still fail later because the application sends nothing, the response is lost, or the peer stops acknowledging.

How do you capture packets without overwhelming the system?

Linux server connected through a network switch during a busy traffic capture

Capture the smallest complete dataset that can answer the question. A huge file is not automatically better. It contains more unrelated credentials, more retransmissions, and more opportunities to misread the timeline.

Use a packet count for a bounded handshake or request:

sudo tcpdump -ni eth0 -nn -s 0 -c COUNT -w request.pcap \
 'host CLIENT and host SERVER and tcp port PORT'

Use a shorter snapshot when headers are enough:

sudo tcpdump -ni eth0 -nn -s SNAPLEN -c COUNT \
 'host CLIENT and host SERVER and tcp port PORT'

A short snapshot can hide the payload or application error you need. Use -s 0 when you are not certain. Storage is cheaper than repeating a failed incident.

What reduces packet loss during a busy capture?

Write directly to a file instead of printing every packet to the terminal:

sudo tcpdump -ni eth0 -nn -s 0 -w investigation.pcap \
 'host SERVER and (tcp port 443 or udp port 53)'

The -B option raises the capture buffer in operating-system units:

sudo tcpdump -ni eth0 -nn -B BUFFER -w investigation.pcap 'FILTER'

This can reduce drops during bursts, but it cannot fix a wrong interface, an overloaded disk, or a filter that matches too much. Check the final drop count.

For long investigations, rotate files by size or time:

sudo tcpdump -ni eth0 -nn -s 0 \
 -G SECONDS -W FILES \
 -w '/var/tmp/pcap-investigation/capture-%Y%m%d-%H%M%S.pcap' \
 'host SERVER and tcp port PORT'

-G starts a new file on the requested time interval. -W limits the number of files when used with rotation. File naming with timestamps preserves the order of the evidence. Keep the directory permissions restrictive.

Use -U when another process needs to read packets while the capture is still running:

sudo tcpdump -ni eth0 -nn -U -w live.pcap 'FILTER'

That changes flushing behavior. It does not make the capture lossless.

How do you save tcpdump traffic to a capture file?

Linux server with external storage attached beside Ethernet cables during packet capture

Use -w for a pcap file. Do not redirect normal terminal output when you need packets later:

sudo tcpdump -ni eth0 -nn -s 0 -w incident.pcap 'FILTER'

A pcap file keeps packet bytes and capture metadata. Terminal output keeps only the summary you happened to print. You can change display filters later when you have the full file.

Record the context beside the capture. At minimum, save:

date --iso-8601=seconds
hostname
ip -br addr
ip route
ss -tulpen
tcpdump --version

Also record the interface, namespace, filter, start time, stop condition, and the event that triggered the capture. Put that information in a text file next to the pcap:

cat > capture-context.txt <<'EOF'
interface: REPLACE_ME
namespace: REPLACE_ME
filter: REPLACE_ME
reason: REPLACE_ME
started: REPLACE_ME
stopped: REPLACE_ME
EOF

Do not edit the original capture while analyzing it. Copy it, hash it, and work on the copy if the investigation may need to be audited:

sha256sum incident.pcap > incident.pcap.sha256

The hash identifies the file you analyzed. It does not prove that the capture itself is complete. That still depends on the interface, filter, timestamps, and drop statistics.

How do you read and analyze a saved tcpdump capture?

Reopen the file with -r:

tcpdump -nn -tttt -r incident.pcap

Apply a new display filter without capturing again:

tcpdump -nn -tttt -r incident.pcap \
 'host CLIENT and host SERVER and tcp port PORT'

This is one of tcpdump's useful properties. Capture broadly enough once, then narrow the view during analysis.

Print link-layer headers with -e:

tcpdump -e -nn -tttt -r incident.pcap

Increase protocol detail with -vvv:

tcpdump -vvv -nn -tttt -r incident.pcap 'udp port 53'

Print payload as ASCII or hexadecimal:

tcpdump -A -nn -r incident.pcap 'tcp port 80'
tcpdump -X -nn -r incident.pcap 'tcp port 80'

-A helps with readable text protocols. -X shows hexadecimal and an ASCII column. Neither option decrypts TLS, reconstructs a full TCP stream, or turns binary data into a useful story.

A TCP conversation is analyzed by its endpoints, flags, sequence numbers, acknowledgments, and timing. Filter it tightly:

tcpdump -nn -tttt -S -r incident.pcap \
 'host CLIENT and host SERVER and tcp port PORT'

-S prints absolute TCP sequence numbers instead of relative ones. Look for this order:

  1. SYN leaves the client.
  2. SYN-ACK returns from the server.
  3. ACK completes setup.
  4. The client sends application data.
  5. The server acknowledges and responds.
  6. Retransmissions appear if acknowledgments or data are lost.
  7. RST or FIN shows how the connection ended.

If the SYN leaves and no SYN-ACK returns, investigate the path, firewall, listener address, and remote host. If the handshake completes but no request data follows, inspect the client with strace. If the server responds and the client retransmits, check loss, receive processing, and offload artifacts before blaming the application.

Tcpdump does not follow and reassemble a TCP stream for you. Use narrow filters and inspect sequence numbers, or hand the pcap to a stream-aware analyzer when reassembly is required. The packet listing remains the evidence. A prettier view does not change the packets.

How do you diagnose failed or misleading tcpdump captures?

Technician tracing Ethernet connections between two network interfaces and a compact switch

Start with the capture statistics. If the file is empty, first remove the filter:

sudo tcpdump -ni INTERFACE -nn -c COUNT

If that is empty too, check these causes:

  • The interface is wrong.
  • The traffic is inside another network namespace.
  • The route uses a different device.
  • The event did not occur during the capture.
  • The process uses loopback.
  • The traffic is handled before or after the point you chose.
  • The user lacks capture permission.

Check the route for a destination:

ip route get SERVER

That tells you which interface and source address the kernel plans to use. It is more reliable than guessing from an interface name.

If output pauses, disable name resolution with -nn. If payload bytes are missing, check whether you used a short snapshot length. If addresses appear but packet counts are low, check for drops at exit and inspect interface counters:

ip -s link show dev INTERFACE

Hardware and kernel offload can make captures look strange. Generic Receive Offload, Large Receive Offload, TCP Segmentation Offload, and checksum offload can change what the host sees compared with what crossed the wire. Check the settings:

sudo ethtool -k INTERFACE

For a controlled test, temporarily disable relevant receive and segmentation offloads:

sudo ethtool -K INTERFACE gro off gso off tso off

Restore them after the test:

sudo ethtool -K INTERFACE gro on gso on tso on

Do this during a maintenance window. Changing offload settings changes host behavior, so it is not a harmless decoration.

Encrypted traffic still shows connection setup, addresses, ports, packet sizes, timing, retransmissions, and resets. It does not show the HTTP path or application body. If the question is what happened inside a VPN or encrypted session, inspect the traffic at the right endpoint or use kernel instrumentation. The post on using eBPF to trace VPN traffic across encryption covers that boundary.

Fragmentation is another trap. A non-first IP fragment may not contain the TCP or UDP header, so a port filter can miss it. Capture the broader host or protocol traffic first when fragmentation is suspected:

sudo tcpdump -ni eth0 -nn -s 0 'host CLIENT and host SERVER'

Finally, test filters with known traffic. A filter can compile correctly and still match the wrong direction, the wrong namespace, or only one half of a conversation. tcpdump checks syntax. It does not check your intent.

How do you turn packet output into a root-cause explanation?

Use a fixed sequence. Do not scroll through a pcap until a familiar line looks guilty.

1. Define the event

Write down the exact failure:

  • Which process failed?
  • Which local address and port did it use?
  • Which remote address and port did it contact?
  • What time did the application report the failure?
  • Was the failure a timeout, refusal, reset, or protocol error?

Use the application log and journalctl first. A packet timestamp without an event timestamp is just a pile of movement.

2. Confirm the socket and route

Check the process and route while the problem is reproducible:

ss -tanp
ip route get SERVER

ss shows listening and connected socket state. ip route get shows the selected path and source address. If these disagree with your filter, fix the investigation before capturing more packets.

3. Capture at the correct boundary

Run a short unfiltered test, then add the narrow filter:

sudo tcpdump -ni INTERFACE -nn -s 0 -w incident.pcap \
 'host CLIENT and host SERVER and tcp port PORT'

Generate one reproducible failure. Note the command start time and the application's failure time. Avoid capturing an entire busy server when one controlled request will answer the question.

4. Read the connection state

Check the handshake, data direction, acknowledgments, retransmissions, resets, and close. Then compare the packet order with the application's system calls:

sudo strace -ff -tt -e trace=network -p PID

strace timestamps calls and shows whether the process reached connect, sent data, waited in recv, or received an error. A SYN in the pcap does not prove that the application successfully completed its operation.

5. Check kernel evidence

Read the kernel ring buffer and service logs around the same event:

dmesg -T | tail -50
journalctl -b --since "10 min ago"

Look for interface resets, receive errors, firewall messages, out-of-memory events, and service restarts. If the packet arrives but the application does not receive it, kernel and socket evidence matters more than another screen of packet summaries.

6. State the cause narrowly

A useful conclusion names the evidence and the boundary:

The client sent the SYN on enp1s0. No SYN-ACK returned. The route selected the expected gateway, and the server log shows no connection attempt. The failure is between the client interface and the server, not in the client application.

That is a diagnosis. “The network is flaky” is not.

For packet drops that do not appear in the capture, use monitoring packet drops with eBPF. For the lower-level network checks that come before packet capture, see how I troubleshoot network Linux. If the traffic crosses a router or OpenWRT device, capturing packets in OpenWRT with eBPF covers the device-side boundary.

What is the smallest tcpdump workflow that gives reliable evidence?

Use this sequence when you need a repeatable capture:

  1. Find the route and interface.
  2. Run an unfiltered test capture.
  3. Reproduce one failure.
  4. Apply a host, protocol, and port filter.
  5. Save full packets to a private pcap file.
  6. Record the filter, interface, namespace, and event time.
  7. Reopen the file with a narrower display filter.
  8. Correlate packet timestamps with logs, strace, ss, routing, and dmesg.
ip route get SERVER
sudo tcpdump -ni INTERFACE -nn -s 0 -w incident.pcap \
 'host CLIENT and host SERVER and tcp port PORT'
tcpdump -nn -tttt -vvv -r incident.pcap \
 'host CLIENT and host SERVER and tcp port PORT'

This proves where the traffic went and how the peer responded. It does not, by itself, explain what the application wanted or why the kernel dropped anything later.

FAQ

Can tcpdump capture traffic from another machine?

No. Tcpdump captures at the system where it runs. Run it on the remote host, on a router or bridge carrying the traffic, or on a mirrored switch port. A capture on your laptop won't explain a packet that never reaches your laptop.

Why does tcpdump show traffic on any but not on the physical interface?

The traffic may be loopback, bridged, VLAN-handled, or attached to another namespace. Compare ip route get DESTINATION with ip -br link, then capture on the interface selected by the route. The any pseudo-interface is useful for discovery, but it won't prove which physical link carried the packet.

Can tcpdump decrypt HTTPS traffic?

No. It can show the TLS handshake, endpoint addresses, timing, packet sizes, retransmissions, and connection errors. It cannot display encrypted HTTP content without separate key material and a tool that supports that decryption.

How do I capture only one process's traffic?

Tcpdump filters packets, not process IDs. Filter by the process's local address and port, then confirm those values with ss -tanp. If several processes share the same socket or address, use strace, network namespaces, cgroups, or eBPF to connect packets to the process. The packet filter has no idea which process you had in mind.

Does tcpdump reassemble a TCP stream?

No. It displays individual packets and their TCP metadata. Use sequence numbers and acknowledgments for packet-level diagnosis, or use a stream-aware analyzer when you need reconstructed application data. A pcap preserves the packets; it does not turn them into a ready-made conversation.