--- title: "How to Capture and Analyze Traffic with tcpdump on Linux" description: "Capture traffic with tcpdump on Ubuntu 24.04, filter it by host, port and TCP flags, save pcap files for Wireshark and debug connection, DNS and TLS issues." category: "Network Configuration" language: en source: https://cubepath.com/docs/network-configuration/network-traffic-analysis-with-tcpdump updated: 2026-09-25 --- # How to Capture and Analyze Traffic with tcpdump on Linux tcpdump is the standard command-line packet analyzer on Linux. It shows exactly what goes over the wire, which settles questions that logs cannot answer: did the request reach the server, did the server reply, and who closed the connection. In this tutorial you will install tcpdump on Ubuntu 24.04, capture traffic on the right interface, write filters that isolate the packets you care about, save captures for Wireshark and walk through three real troubleshooting cases. ## Prerequisites To follow this guide you need: - A server running Ubuntu 24.04 LTS, for example a CubePath VPS. The commands are the same on Debian 12 and Rocky Linux 9. - A non-root user with `sudo` privileges. Capturing packets requires root privileges. - Optionally, Wireshark on your local machine to open saved captures. > Important: captures can contain passwords, cookies and personal data sent in clear text. Only capture traffic on systems you administer, keep capture files private and delete them when you are done. ## Step 1 - Installing tcpdump and choosing an interface Install the package: ```bash sudo apt update sudo apt install tcpdump ``` Check the version: ```bash tcpdump --version ``` ```output tcpdump version 4.99.4 libpcap version 1.10.4 (with TPACKET_V3) OpenSSL 3.0.13 30 Jan 2024 ``` tcpdump captures on one interface at a time. List the interfaces it can use: ```bash sudo tcpdump -D ``` ```output 1.eth0 [Up, Running, Connected] 2.any (Pseudo-device that captures on all interfaces) [Up, Running] 3.lo [Up, Running, Loopback] ``` To find the interface that carries your public traffic, ask the kernel which one it uses to reach the internet: ```bash ip route get 1.1.1.1 ``` ```output 1.1.1.1 via 198.51.100.1 dev eth0 src 198.51.100.20 uid 1000 ``` The name after `dev` is the interface. This guide uses `eth0`; replace it with yours (for example `ens18`). Use `lo` for traffic between local services, such as an application talking to a database on `127.0.0.1`. ## Step 2 - Running a first capture Capture ten packets on `eth0` and stop: ```bash sudo tcpdump -i eth0 -nn -c 10 not port 22 ``` - `-i eth0` selects the interface. - `-nn` shows IP addresses and port numbers instead of resolving names. Name resolution is slow and generates its own DNS traffic, which then shows up in the capture. - `-c 10` stops after ten packets. Without it, tcpdump runs until you press `Ctrl+C`. - `not port 22` excludes your own SSH session. Without it, every line tcpdump prints generates more SSH packets, which fill the screen. A typical TCP line looks like this: ```output 10:21:07.118532 IP 203.0.113.25.51544 > 198.51.100.20.443: Flags [S], seq 3120938841, win 64240, options [mss 1460,sackOK,TS val 1822 ecr 0,nop,wscale 7], length 0 ``` Read it from left to right: the timestamp, the protocol (`IP` or `IP6`), the source address and port, the destination address and port, the TCP flags, the sequence number, the receive window, TCP options and the payload length. The flags are the most useful field: | Flag | Meaning | |------|---------| | `[S]` | SYN, a client opens a connection | | `[S.]` | SYN-ACK, the server accepts it | | `[.]` | ACK only | | `[P.]` | PUSH with data | | `[F.]` | FIN, one side closes the connection | | `[R]` or `[R.]` | RST, the connection is refused or aborted | ## Step 3 - Filtering traffic Filters use the Berkeley Packet Filter syntax and are evaluated in the kernel, so only matching packets reach tcpdump. A precise filter is the difference between a readable capture and millions of irrelevant lines. Quote the filter when it contains parentheses or other characters the shell would interpret. Filter by host, network or direction: ```bash sudo tcpdump -i eth0 -nn host 203.0.113.25 sudo tcpdump -i eth0 -nn src net 192.0.2.0/24 sudo tcpdump -i eth0 -nn dst host 198.51.100.20 ``` Filter by port or protocol: ```bash sudo tcpdump -i eth0 -nn port 443 sudo tcpdump -i eth0 -nn udp port 53 sudo tcpdump -i eth0 -nn icmp sudo tcpdump -i eth0 -nn portrange 8000-8100 ``` Combine conditions with `and`, `or` and `not`: ```bash sudo tcpdump -i eth0 -nn 'host 203.0.113.25 and (port 80 or port 443)' ``` Match TCP flags to see only connection attempts or resets. The first command shows SYN packets without ACK (new connections), the second any packet with RST set: ```bash sudo tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn' sudo tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0' ``` IPv6 traffic needs its own keyword, since `icmp` only matches ICMP for IPv4: ```bash sudo tcpdump -i eth0 -nn ip6 sudo tcpdump -i eth0 -nn icmp6 ``` ## Step 4 - Showing packet contents For unencrypted protocols, printing the payload shows the actual request and response. `-A` prints it as ASCII, which suits HTTP, SMTP or Redis commands: ```bash sudo tcpdump -i lo -nn -A -s 0 'tcp port 8080 and tcp[tcpflags] & tcp-push != 0' ``` The flag filter keeps only packets that carry data. `-s 0` captures whole packets, which is already the default in current versions but makes the intent explicit. For binary protocols, `-X` prints the payload in hex and ASCII side by side. Two more display options are useful in practice: - `-v` or `-vv` adds IP header details such as TTL and checksums, and decodes more of protocols like DNS. - `-tttt` prints full dates, which helps when comparing with application logs. ## Step 5 - Saving and reading capture files Printing to the terminal is fine for quick checks. For anything longer, write raw packets to a pcap file with `-w` and analyze them afterwards with tcpdump or Wireshark. On Ubuntu two protections affect where tcpdump can write: it drops root privileges to the `tcpdump` user before opening the output file, and its AppArmor profile only allows writing files with a `.pcap` extension. Write to `/tmp` and use the `.pcap` extension to avoid permission errors: ```bash sudo tcpdump -i eth0 -nn -w /tmp/web.pcap -c 1000 'port 80 or port 443' ``` ```output tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes 1000 packets captured 1003 packets received by filter 0 packets dropped by kernel ``` Check the last line: if the kernel dropped packets, the capture is incomplete and you should use a narrower filter. Read the file back, applying another filter if needed: ```bash tcpdump -nn -r /tmp/web.pcap 'tcp[tcpflags] & tcp-rst != 0' ``` To open the capture in Wireshark, copy it to your local machine: ```bash scp your_user@your_server_ip:/tmp/web.pcap . ``` For longer captures, limit the size and duration. `-s 128` keeps only the first 128 bytes of each packet (enough for the headers), and `-G` with `-W` rotates to a new file every hour and stops after 24 files. The file name must contain a date format so each file gets a new name: ```bash sudo tcpdump -i eth0 -nn -s 128 -G 3600 -W 24 -w '/tmp/eth0-%Y%m%d-%H%M.pcap' 'port 443' ``` Run long captures inside `tmux` or `screen` so they survive a dropped SSH session, and check free disk space with `df -h /tmp` before starting. ## Step 6 - Troubleshooting real problems ### Case 1: a client cannot connect to a web server Capture the handshake between a specific client and the server port: ```bash sudo tcpdump -i eth0 -nn 'host 203.0.113.25 and tcp port 443' ``` Then compare what you see with these patterns: - **SYN arrives, SYN-ACK goes back, then data**: the network and the server are fine. Look at the application or TLS configuration. - **SYN arrives, RST goes back** (`Flags [R.]`): nothing listens on that port. Check `sudo ss -tlnp` on the server. - **SYN arrives, no reply**: the local firewall drops the packet. Check UFW, iptables or nftables rules. - **No SYN at all**: the packet never reaches the server. The problem is upstream, in DNS, routing or a firewall in front of the server. ### Case 2: slow or failing DNS resolution Capture DNS queries and answers with verbose decoding: ```bash sudo tcpdump -i eth0 -nn -v udp port 53 ``` ```output 10:40:12.301442 IP (tos 0x0, ttl 64, id 3312, offset 0, flags [none], proto UDP (17), length 60) 198.51.100.20.40211 > 1.1.1.1.53: 45110+ A? example.com. (32) 10:40:12.313007 IP (tos 0x0, ttl 58, id 0, offset 0, flags [DF], proto UDP (17), length 76) 1.1.1.1.53 > 198.51.100.20.40211: 45110 1/0/0 example.com. A 93.184.215.14 (48) ``` Each query has an ID (`45110`) that the answer repeats, and the timestamps show how long the resolver took. Queries without answers, or answers containing `NXDomain` or `ServFail`, identify the failing lookup. On Ubuntu, applications query the local stub resolver at `127.0.0.53`, so capture on `lo` to see which names they ask for and on `eth0` to see what systemd-resolved sends upstream. ### Case 3: checking which TLS name a client requests HTTPS payloads are encrypted, but the start of a TLS handshake is not. With verbose output, tcpdump decodes the Server Name Indication (SNI) that the client sends in its ClientHello, which tells you which site a client is asking for on a server that hosts several: ```bash sudo tcpdump -i eth0 -nn -v -c 20 'tcp dst port 443 and tcp[tcpflags] & tcp-push != 0' ``` For a detailed look at the handshake (versions, cipher suites, alerts), save a capture as in Step 5 and open it in Wireshark, which decodes TLS fully. ## Troubleshooting - **`tcpdump: eth0: You don't have permission to perform this capture on that device`**: run tcpdump with `sudo`. - **`tcpdump: /home/your_user/capture.pcap: Permission denied`**: tcpdump dropped privileges to the `tcpdump` user, or the file name lacks the `.pcap` extension required by AppArmor. Write to `/tmp/name.pcap` instead. - **No packets appear**: you are capturing on the wrong interface or the filter is too strict. Try `-i any` with a broad filter such as `host 203.0.113.25`. - **`packets dropped by kernel` is not zero**: tcpdump could not keep up. Add a narrower filter, remove `-v` and payload printing, or write to a file with `-w` instead of the terminal. ## Conclusion You installed tcpdump, picked the right interface, filtered traffic by host, port, protocol and TCP flags, saved rotating pcap files and used captures to diagnose connection, DNS and TLS problems. As next steps, open your saved captures in Wireshark to use its protocol decoders and flow graphs, combine tcpdump with `ss` and `mtr` for a complete troubleshooting routine, and review your firewall rules when a capture shows packets arriving without replies.