Skip to main content

Packet sniffer basics for network troubleshooting

Capturing packets with a free tool like tcpdump is an essential skill that you need to acquire as a sysadmin. Anthony Critelli shows you the ropes.
Image
packet sniffing

Performing packet captures using a sniffer can be an extremely powerful method for diagnosing complex issues. When all else fails, it’s often helpful to view the raw data being sent across the wire. The packets don’t lie, and analyzing your application traffic at a low level can reveal deeper symptoms of a problem (or reveal a problem that you didn’t even know existed).

In this article, we’ll cover some fundamentals of using the CLI-based tcpdump tool.

Sniffer basics

So first things first: What do we mean when we say "packet sniffer?" A packet sniffer is simply a piece of software that allows you to capture packets on your network. Tcpdump and Wireshark are examples of packet sniffers. Tcpdump provides a CLI packet sniffer, and Wireshark provides a feature-rich GUI for sniffing and analyzing packets.

By default, tcpdump operates in promiscuous mode. This simply means that all packets reaching a host will be sent to tcpdump for inspection. This setting even includes traffic that was not destined for the specific host that you are capturing on, such as broadcast and multicast traffic. Of course, tcpdump isn’t some magical piece of software: It can only capture those packets that somehow reach one of the physical interfaces on your machine.

Installing tcpdump is simple. It’s available in the standard package repositories on your Red Hat system, and you can install it by name:

 # yum install -y tcpdump

Common packet sniffing scenarios

Capturing all of the traffic coming into your machine may sound conceptually cool, but it also sounds fairly low level for many of the activities that we perform in our day-to-day work as sysadmins. So, when would you use a packet capturing tool? I typically turn to a packet sniffer when I’m troubleshooting a network application issue and I’ve exhausted all other options. Often, I have already performed basic network troubleshooting and reviewed any application log files, but I still can’t get to the bottom of an issue. At this point, breaking out a packet sniffer to observe the actual data being sent on the wire can be instructive.

Another great use case for a packet sniffer is pedagogical. Watching the packets involved in an application exchange can go a long way toward improving your understanding of the underlying protocols. For example, it can be invaluable to observe the full packet flow of a recursive DNS query when trying to understand how DNS works.

Performing basic packet captures

The best way to learn is by just diving in, so let’s get started with some basic packet captures. First, let’s try out tcpdump without any special options. Note that you’ll need to be the superuser to perform packet captures (technically, you can run it from a regular account with special capabilities, but it’s usually easier to run it as root). Use Ctrl+C, or send a SIGTERM to the tcpdump process ID (PID) to stop the capture.

Default tcpdump

To see the default output of tcpdump, simply type the command:

# tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:41:59.878706 IP localhost.localdomain > lga34s19-in-f14.1e100.net: ICMP echo request, id 3298, seq 5, length 64
20:41:59.901298 IP lga34s19-in-f14.1e100.net > localhost.localdomain: ICMP echo reply, id 3298, seq 5, length 64
20:42:00.878245 IP localhost.localdomain > lga34s19-in-f14.1e100.net: ICMP echo request, id 3298, seq 6, length 64
20:42:00.888530 STP 802.1d, Config, Flags [none], bridge-id 8000.52:54:00:11:23:84.8002, length 35
20:42:00.901329 IP lga34s19-in-f14.1e100.net > localhost.localdomain: ICMP echo reply, id 3298, seq 6, length 64
20:42:01.112737 ARP, Request who-has localhost.localdomain tell acritelli-laptop, length 28
^C
6 packets captured
6 packets received by filter
0 packets dropped by kernel

Note: Rather than having a lot of output fly by before pressing Ctrl+C, you can specify how many packets you want to see with the -c flag. The command above could instead be tcpdump -c 6 to get the same results (six packets captured).

The output from tcpdump can be a bit daunting at first, but you’ll get used to looking at it after you’ve used this tool a few times. Let’s break down the fields, from left to right:

  • Timestamp
  • Protocol, such as IP
  • Source (localhost.localdomain)
  • Destination (lga34s19-in-f14.1e100.net)
  • Protocol-specific fields (e.g., ICMP echo reply)
  • Basic protocol information (e.g., sequence number and length)

Sniffing a specific interface

Notice that the top of the previous example’s output shows you the interface that tcpdump starts capturing on (eth0), and the bottom of the capture includes summary statistics about the captured packets.

The first thing that you’ll probably want to do when using tcpdump is to specify a particular interface for performing captures. By default, tcpdump will pick the lowest numbered interface that is "up." Many servers have multiple interfaces, and you’ll want to be explicit about the interface that you use for capturing. Additionally, some "special" interface types, such as a netfilter interface, may float to the top of the list. This behavior can cause confusion, so it’s best to specify the interface that you’re interested in.

Let’s start by viewing the interfaces that are available for capturing:

# tcpdump --list-interfaces
1.eth0
2.nflog (Linux netfilter log (NFLOG) interface)
3.nfqueue (Linux netfilter queue (NFQUEUE) interface)
4.usbmon1 (USB bus number 1)
5.usbmon2 (USB bus number 2)
6.usbmon3 (USB bus number 3)
7.usbmon4 (USB bus number 4)
8.ens9
9.any (Pseudo-device that captures on all interfaces)
10.lo [Loopback]

With a list of interfaces at our disposal, we can now specify the interface to listen to with the -i flag. Note that either the interface name or the number from the --list-interfaces command can be used:

# tcpdump -i eth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:50:00.858552 STP 802.1d, Config, Flags [none], bridge-id 8000.52:54:00:11:23:84.8002, length 35
20:50:02.841866 STP 802.1d, Config, Flags [none], bridge-id 8000.52:54:00:11:23:84.8002, length 35
20:50:04.858167 STP 802.1d, Config, Flags [none], bridge-id 8000.52:54:00:11:23:84.8002, length 35
^C
3 packets captured
3 packets received by filter
0 packets dropped by kernel

Looking at the above captures provides us with basic information about the packets traversing our network. It looks like these packets contain Spanning Tree Protocol (STP) output, perhaps from an upstream switch. Technically, these aren’t packets, they’re layer two frames. However, you’ll hear the terms used interchangeably when discussing packet captures.

Getting more information

The previous example’s simple troubleshooting result can be great for identifying obvious issues, but sometimes we need more information to really dig into a complex problem. Knowing how to adjust the verbosity of your capture is important, as it allows you to dig deeper into the actual data contained within the packets.

The verbosity level of tcpdump is controlled by appending between one and three -v flags to the command:

# tcpdump -i eth0 -vvv
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:49:22.844765 STP 802.1d, Config, Flags [none], bridge-id 8000.52:54:00:11:23:84.8002, length 35
         message-age 0.00s, max-age 20.00s, hello-time 2.00s, forwarding-delay 2.00s
         root-id 8000.52:54:00:11:23:84, root-pathcost 0
20:49:24.860576 STP 802.1d, Config, Flags [none], bridge-id 8000.52:54:00:11:23:84.8002, length 35
         message-age 0.00s, max-age 20.00s, hello-time 2.00s, forwarding-delay 2.00s
         root-id 8000.52:54:00:11:23:84, root-pathcost 0
20:49:26.843910 STP 802.1d, Config, Flags [none], bridge-id 8000.52:54:00:11:23:84.8002, length 35
         message-age 0.00s, max-age 20.00s, hello-time 2.00s, forwarding-delay 2.00s
         root-id 8000.52:54:00:11:23:84, root-pathcost 0
^C
3 packets captured
3 packets received by filter
0 packets dropped by kernel

Note that by specifying the maximum level of verbosity, I can see far more information about the packet body. Above, we can see additional information about the data in the STP packet, such as the root bridge ID and root path cost. If you’re not familiar with STP, don’t worry about it. The important thing to notice here is that by increasing the verbosity, we can get additional information about our network traffic.

Seeing a packet’s exact bytes

Increasing the verbosity is handy, but we’re still not seeing the meat of the packets’ contents. If you really want to see the exact bytes that are in the packets, you can use the -x and -X flags. The -x flag prints the data in each packet in hex, while the -X flag also prints the data in ASCII. Here is the output using -x:

# tcpdump -i ens9 -c 1 -x port 53
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens9, link-type EN10MB (Ethernet), capture size 262144 bytes
21:27:41.121804 IP localhost.localdomain.55310 > dns.google.domain: 8656+ [1au] A? www.google.com. (43)
      0x0000: 4500 0047 ff25 0000 4011 302b c0a8 7a9d
      0x0010: 0808 0808 d80e 0035 0033 0246 21d0 0120
      0x0020: 0001 0000 0000 0001 0377 7777 0667 6f6f
      0x0030: 676c 6503 636f 6d00 0001 0001 0000 2910
      0x0040: 0000 0000 0000 00
1 packet captured
5 packets received by filter
0 packets dropped by kernel

And here is the output using -X:

# tcpdump -i ens9 -c 1 -X port 53
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens9, link-type EN10MB (Ethernet), capture size 262144 bytes
21:27:47.269762 IP localhost.localdomain.38787 > dns.google.domain: 48705+ [1au] A? www.google.com. (43)
      0x0000: 4500 0047 0a9c 0000 4011 24b5 c0a8 7a9d E..G....@.$...z.
      0x0010: 0808 0808 9783 0035 0033 a65f be41 0120 .......5.3._.A..
      0x0020: 0001 0000 0000 0001 0377 7777 0667 6f6f .........www.goo
      0x0030: 676c 6503 636f 6d00 0001 0001 0000 2910 gle.com.......).
      0x0040: 0000 0000 0000 00 .......
1 packet captured
5 packets received by filter
0 packets dropped by kernel

Notice the hex and ASCII data in the above examples. In this case, I was performing a DNS A record query for www.google.com. Also, note that I used the -c flag to specify the number of packets to capture, and I provided a capture filter of port 53. We’ll discuss filters in the next article.

Sticking with the numbers

As you’re working with tcpdump, you may notice that its default behavior is to automatically resolve IP addresses to fully qualified domain names (FQDNs). Tcpdump will also translate port numbers (such as 22) into friendly names (such as SSH). While this behavior is nice, we often want to see the numeric data so that we don’t obscure our troubleshooting in any way. This default behavior can be changed by passing either -n to disable IP address lookups, or -nn to disable both IP address and port lookups.

Here is the result when you use -nn:

# tcpdump -i ens9 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens9, link-type EN10MB (Ethernet), capture size 262144 bytes
20:57:50.004451 IP 192.168.122.157.22 > 192.168.122.1.49540: Flags [P.], seq 2961250669:2961250889, ack 823295439, win 313, options [nop,nop,TS val 1210110 ecr 672601542], length 220
20:57:50.004805 IP 192.168.122.1.49540 > 192.168.122.157.22: Flags [.], ack 220, win 1444, options [nop,nop,TS val 672601548 ecr 1210110], length 0
20:57:50.005229 IP 192.168.122.157.22 > 192.168.122.1.49540: Flags [P.], seq 220:624, ack 1, win 313, options [nop,nop,TS val 1210111 ecr 672601548], length 404
20:57:50.005489 IP 192.168.122.1.49540 > 192.168.122.157.22: Flags [.], ack 624, win 1444, options [nop,nop,TS val 672601549 ecr 1210111], length 0
20:57:50.005772 IP 192.168.122.157.22 > 192.168.122.1.49540: Flags [P.], seq 624:1004, ack 1, win 313, options [nop,nop,TS val 1210111 ecr 672601549], length 380
20:57:50.006040 IP 192.168.122.1.49540 > 192.168.122.157.22: Flags [.], ack 1004, win 1444, options [nop,nop,TS val 672601549 ecr 1210111], length 0
20:57:50.006341 IP 192.168.122.157.22 > 192.168.122.1.49540: Flags [P.], seq 1004:1384, ack 1, win 313, options [nop,nop,TS val 1210112 ecr 672601549], length 380

Notice that in the above output, we can see that the IP addresses (192.168.122.157 and 192.168.122.1) remain untouched. The same is true for the ports (22 and 49540).

Saving your dump

At some point, you may want to save your packet captures for later analysis, or for deeper analysis with a graphical tool like Wireshark. This task can be easily accomplished with the -w flag, which allows you to write a packet capture file:

# tcpdump -i ens9 -w my_packet_capture.pcap
tcpdump: listening on ens9, link-type EN10MB (Ethernet), capture size 262144 bytes
^C5 packets captured
7 packets received by filter
0 packets dropped by kernel

# file my_packet_capture.pcap
my_packet_capture.pcap: tcpdump capture file (little-endian) - version 2.4 (Ethernet, capture length 262144)

Note: You can read your saved file using tcpdump with the -r flag, or with a different application that supports the pcap file format.

As we can see above, the -w flag produced a handy pcap file that we can take with us.

Wrapping up

Performing packet captures is a powerful technique in your network troubleshooting skills inventory, especially when you’re stuck on an issue and the rest of the network looks OK. Understanding how to use tcpdump on the command line can save you hours of frustration when trying to solve network application issues, and the syntax is quite intuitive once you get used to it.

[Need more on networking? Download the Linux networking cheat sheet.]

Topics:   Networking  
Author’s photo

Anthony Critelli

Anthony Critelli is a Linux systems engineer with interests in automation, containerization, tracing, and performance. He started his professional career as a network engineer and eventually made the switch to the Linux systems side of IT. He holds a B.S. and an M.S. More about me

Related Content

OUR BEST CONTENT, DELIVERED TO YOUR INBOX