6 Tcpdump commands for Network Troubleshooting

6 Tcpdump commands for Network Troubleshooting

Want to identify the Network Traffic that is going in and out of a Linux system? Have you ever wished you could just use a couple of commands on Linux and prove 'this is not a network issue'?

Tcpdump is a Linux command-line utility that allows you to capture and analyze all network traffic going through a Linux system. Tcpdump is very useful for Network Troubleshooting where installing Wireshark is not possible. Our ultimate goal here is to capture and analyze the traffic going to and from the Linux machine.

Here are 6 commands that are sure to help.

Installation

Tcpdump comes pre-installed with most of the Linux distros. If you don't have it installed on your system, you can easily install it using the appropriate commands. For example, use sudo apt install tcpdump for Debian and  sudo yum install tcpdump for CentOS/Fedora.

pi@raspberrypi:~ $ sudo apt install tcpdump
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following packages were automatically installed and are no longer required:
[.....]
pi@raspberrypi:~ $ tcpdump --version
tcpdump version 4.9.3
libpcap version 1.8.1
OpenSSL 1.1.1d  10 Sep 2019

I have two interfaces on my Raspberry Pi (Wired and Wi-Fi) as shown below so, you want to be specific with which interfaces to take the captures on.

root@raspberrypi:/home/pi# show ip
bash: show: command not found
root@raspberrypi:/home/pi# ip ad
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether dc:a6:32:04:d2:56 brd ff:ff:ff:ff:ff:ff
    inet 10.10.0.1/16 brd 10.10.255.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::a7c0:e802:57e3:90a6/64 scope link 
       valid_lft forever preferred_lft forever
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether dc:a6:32:04:d2:57 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.5/24 brd 192.168.0.255 scope global noprefixroute wlan0
       valid_lft forever preferred_lft forever
    inet6 fe80::981a:3238:f19:c228/64 scope link 
       valid_lft forever preferred_lft forever
You can also run tcpdump -D to list all the interfaces.

1. Start the capture

You can just run tcpdump command to start the capture. As you can see below, by default traffic on the eth0 interface is being captured. You will have to terminate the capture with CTRL + C once you have taken enough captures.

root@raspberrypi:/home/pi# tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
12:40:07.641494 ARP, Request who-has 10.10.0.1 (dc:a6:32:04:d2:56 (oui Unknown)) tell 10.10.0.10, length 46
12:40:07.641560 ARP, Reply 10.10.0.1 is-at dc:a6:32:04:d2:56 (oui Unknown), length 28
12:40:10.159445 IP 10.10.0.1.58760 > 239.255.255.250.1900: UDP, length 101
12:40:11.487595 IP 10.10.0.1.52766 > 10.10.255.255.32414: UDP, length 21
12:40:11.488865 IP 10.10.0.1.56177 > 10.10.255.255.32412: UDP, length 21
12:40:11.604242 IP 10.10.0.10.1900 > 239.255.255.250.1900: UDP, length 518
12:40:11.891715 IP 10.10.0.10.1900 > 239.255.255.250.1900: UDP, length 504
12:40:12.025215 IP 10.10.0.10.1900 > 239.255.255.250.1900: UDP, length 502
12:40:12.280478 IP 10.10.0.10.1900 > 239.255.255.250.1900: UDP, length 438
12:40:12.725948 IP 10.10.0.10.1900 > 239.255.255.250.1900: UDP, length 447

2. Capture from a specific interface

You can pass -i flag to specify the interface. The below example shows how to capture the packets from the wlan0 interface. You can also use any argument to capture packets on all the interfaces tcpdump -i any

root@raspberrypi:/home/pi# tcpdump -i wlan0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlan0, link-type EN10MB (Ethernet), capture size 262144 bytes
12:45:24.893757 IP raspberrypi.ssh > 192.168.0.26.60705: Flags [P.], seq 3467135140:3467135380, ack 147024809, win 1002, length 240
12:45:24.899025 IP raspberrypi.ssh > 192.168.0.26.60705: Flags [P.], seq 240:464, ack 1, win 1002, length 224
12:45:24.899344 IP raspberrypi.ssh > 192.168.0.26.60705: Flags [P.], seq 464:672, ack 1, win 1002, length 208
12:45:24.899630 IP raspberrypi.ssh > 192.168.0.26.60705: Flags [P.], seq 672:880, ack 1, win 1002, length 208
12:45:24.899912 IP raspberrypi.ssh > 192.168.0.26.60705: Flags [P.], seq 880:1088, ack 1, win 1002, length 208
12:45:24.900195 IP raspberrypi.ssh > 192.168.0.26.60705: Flags [P.], seq 1088:1296, ack 1, win 1002, length 208
12:45:24.900475 IP raspberrypi.ssh > 192.168.0.26.60705: Flags [P.], seq 1296:1504, ack 1, win 1002, length 208
12:45:24.900753 IP raspberrypi.ssh > 192.168.0.26.60705: Flags [P.], seq 1504:1712, ack 1, win 1002, length 208
12:45:24.901032 IP raspberrypi.ssh > 192.168.0.26.60705: Flags [P.], seq 1712:1920, ack 1, win 1002, length 208
12:45:24.901310 IP raspberrypi.ssh > 192.168.0.26.60705: Flags [P.], seq 1920:2128, ack 1, win 1002, length 208
12:45:24.903405 IP 192.168.0.26.60705 > raspberrypi.ssh: Flags [.], ack 464, win 1021, length 0
12:45:24.903471 IP raspberrypi.ssh > 192.168.0.26.60705: Flags [P.], seq 2128:2336, ack 1, win 1002, length 208
💡
You can pass -n flag to instruct tcpdump not to convert IP addresses to names.
root@raspberrypi:/home/pi# tcpdump -i wlan0 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlan0, link-type EN10MB (Ethernet), capture size 262144 bytes

12:47:41.012805 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 143632:143840, ack 1, win 1002, length 208
12:47:41.012951 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 143840:144048, ack 1, win 1002, length 208
12:47:41.013096 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 144048:144256, ack 1, win 1002, length 208
12:47:41.024185 IP 192.168.0.26.60705 > 192.168.0.5.22: Flags [.], ack 109520, win 1024, length 0
12:47:41.024221 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 144256:144464, ack 1, win 1002, length 208
12:47:41.024501 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 144464:144768, ack 1, win 1002, length 304
12:47:41.024525 IP 192.168.0.26.60705 > 192.168.0.5.22: Flags [.], ack 110768, win 1026, length 0
12:47:41.024564 IP 192.168.0.26.60705 > 192.168.0.5.22: Flags [.], ack 111184, win 1024, length 0
12:47:41.024668 IP 192.168.0.26.60705 > 192.168.0.5.22: Flags [P.], seq 1:97, ack 111184, win 1024, length 96
12:47:41.024688 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [.], ack 97, win 1002, length 0

3. Capture only a specific number of packets

There are thousands of packets that traverse the interface at any given moment so, you might get overwhelmed by the number of packets on the screen. You can pass -c flag to capture only a specific number of packets. Let's say I only want to capture 10 packets.

root@raspberrypi:/home/pi# tcpdump -i wlan0 -n -c 10
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlan0, link-type EN10MB (Ethernet), capture size 262144 bytes
12:54:34.228382 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 3468053764:3468054004, ack 147028649, win 1002, length 240
12:54:34.228947 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 240:464, ack 1, win 1002, length 224
12:54:34.229263 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 464:656, ack 1, win 1002, length 192
12:54:34.229564 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 656:848, ack 1, win 1002, length 192
12:54:34.229855 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 848:1040, ack 1, win 1002, length 192
12:54:34.230101 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 1040:1248, ack 1, win 1002, length 208
12:54:34.230399 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 1248:1456, ack 1, win 1002, length 208
12:54:34.230691 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 1456:1664, ack 1, win 1002, length 208
12:54:34.230997 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 1664:1872, ack 1, win 1002, length 208
12:54:34.231272 IP 192.168.0.5.22 > 192.168.0.26.60705: Flags [P.], seq 1872:2080, ack 1, win 1002, length 208
10 packets captured
10 packets received by filter
0 packets dropped by kernel

4. Capture traffic based on specific IP addresses

You can pass src or dst arguments to specify the IP addresses. For example, let's say you only want to capture the traffic destined to example.net website (93.184.216.34)

root@raspberrypi:/home/pi# tcpdump -i wlan0 -n dst 93.184.216.34
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlan0, link-type EN10MB (Ethernet), capture size 262144 bytes
13:01:01.235437 IP 192.168.0.5.58062 > 93.184.216.34.80: Flags [S], seq 4160194639, win 64240, options [mss 1460,sackOK,TS val 2249672768 ecr 0,nop,wscale 6], length 0
13:01:01.351815 IP 192.168.0.5.58062 > 93.184.216.34.80: Flags [.], ack 2194641446, win 1004, options [nop,nop,TS val 2249672885 ecr 4221015811], length 0
13:01:01.351997 IP 192.168.0.5.58062 > 93.184.216.34.80: Flags [P.], seq 0:75, ack 1, win 1004, options [nop,nop,TS val 2249672885 ecr 4221015811], length 75: HTTP: GET / HTTP/1.1
13:01:01.468563 IP 192.168.0.5.58062 > 93.184.216.34.80: Flags [.], ack 1449, win 1002, options [nop,nop,TS val 2249673002 ecr 4221015927], length 0
13:01:01.470168 IP 192.168.0.5.58062 > 93.184.216.34.80: Flags [.], ack 1592, win 1002, options [nop,nop,TS val 2249673003 ecr 4221015927], length 0
13:01:01.470340 IP 192.168.0.5.58062 > 93.184.216.34.80: Flags [F.], seq 75, ack 1592, win 1002, options [nop,nop,TS val 2249673003 ecr 4221015927], length 0
13:01:01.579412 IP 192.168.0.5.58062 > 93.184.216.34.80: Flags [.], ack 1593, win 1002, options [nop,nop,TS val 2249673112 ecr 4221016040], length 0

If you want to specify both source and destination IPs then ensure to pass the and keyword, for example tcpdump -i wlan0 -n src 192.168.0.5 and dst 8.8.8.8

5. Capture traffic based on ports

Using the previous example, you can pass port argument to specify ports and protocols.  If you capture only ICMP traffic:

root@raspberrypi:/home/pi# tcpdump -i wlan0 -n icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlan0, link-type EN10MB (Ethernet), capture size 262144 bytes
13:07:38.143760 IP 192.168.0.5 > 8.8.8.8: ICMP echo request, id 13732, seq 1, length 64
13:07:38.171984 IP 8.8.8.8 > 192.168.0.5: ICMP echo reply, id 13732, seq 1, length 64
13:07:39.145356 IP 192.168.0.5 > 8.8.8.8: ICMP echo request, id 13732, seq 2, length 64
13:07:39.167068 IP 8.8.8.8 > 192.168.0.5: ICMP echo reply, id 13732, seq 2, length 64

You can also capture only UDP based traffic as shown below

root@raspberrypi:/home/pi# tcpdump -i wlan0 -n udp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlan0, link-type EN10MB (Ethernet), capture size 262144 bytes
13:08:40.236619 IP 192.168.0.5.60199 > 239.255.255.250.1900: UDP, length 101
13:08:40.245771 IP 192.168.0.23.50476 > 192.168.0.5.60199: UDP, length 597
13:08:40.299378 IP 192.168.0.1.1900 > 192.168.0.5.60199: UDP, length 371
13:08:42.982488 IP 192.168.0.26.51188 > 192.168.0.5.53: 24124+ A? dl.acronis.com. (32)
13:08:42.983268 IP 192.168.0.5.45444 > 208.67.220.220.53: 38912+ A? dl.acronis.com. (32)
13:08:43.008583 IP 208.67.220.220.53 > 192.168.0.5.45444: 38912 4/0/0 CNAME acronis-dl.akamaized.net., CNAME a1936.d.akamai.net., A 62.253.3.242, A 62.253.3.209 (131)
13:08:43.009292 IP 192.168.0.5.53 > 192.168.0.26.51188: 24124 4/0/0 CNAME acronis-dl.akamaized.net., CNAME a1936.d.akamai.net., A 62.253.3.242, A 62.253.3.209 (131)
13:08:43.013431 IP 192.168.0.26.51188 > 192.168.0.5.53: 24124+ A? dl.acronis.com. (32)
13:08:43.014118 IP 192.168.0.5.53 > 192.168.0.26.51188: 24124 4/0/0 CNAME acronis-dl.akamaized.net., CNAME a1936.d.akamai.net., A 62.253.3.209, A 62.253.3.242 (134)

You can also go wild and capture traffic based on packet flags. For example, TCP-SYN, TCP-ACK or ICMP echo-request or echo-reply.    

root@raspberrypi:/home/pi# tcpdump -i wlan0 -n 'icmp[icmptype] = icmp-echoreply'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlan0, link-type EN10MB (Ethernet), capture size 262144 bytes
13:21:13.039739 IP 8.8.8.8 > 192.168.0.5: ICMP echo reply, id 14071, seq 1, length 64
13:21:14.035844 IP 8.8.8.8 > 192.168.0.5: ICMP echo reply, id 14071, seq 2, length 64
13:21:15.032210 IP 8.8.8.8 > 192.168.0.5: ICMP echo reply, id 14071, seq 3, length 64
13:21:16.034525 IP 8.8.8.8 > 192.168.0.5: ICMP echo reply, id 14071, seq 4, length 64

6. View and export the captures

You can pass -w flag to save the captures as a PCAP file.  Use -s 0  to capture full-sized packets.

root@raspberrypi:/home/pi# tcpdump -i wlan0 -s 0 -n udp -w /home/pi/udp_capture.pcap
tcpdump: listening on wlan0, link-type EN10MB (Ethernet), capture size 262144 bytes
^C
5 packets captured
5 packets received by filter
0 packets dropped by kernel

You can use -r flag to view the file locally.

root@raspberrypi:/home/pi# tcpdump -r /home/pi/udp_capture.pcap 
reading from file /home/pi/udp_capture.pcap, link-type EN10MB (Ethernet)
13:52:36.778713 IP 192.168.0.23.1901 > 239.255.255.250.1900: UDP, length 125
13:52:36.779044 IP 192.168.0.28.60525 > 239.255.255.250.1900: UDP, length 101
13:52:36.982694 IP raspberrypi.60199 > 192.168.0.255.32414: UDP, length 21
13:52:36.985011 IP raspberrypi.52759 > 192.168.0.255.32412: UDP, length 21
13:52:36.989049 IP 192.168.0.28.32414 > raspberrypi.60199: UDP, length 261

You can also export the capture file (using FTP/SCP etc) and later view it on Wireshark.

I hope I covered the basics to successfully take Tcpdump off a Linux host. Please let me know in the comments if there is a better way to do this or if I missed out on anything.