1. Overview

tcpdump is a powerful and versatile command-line tool for capturing and analyzing network traffic. However, sometimes tcpdump may not be able to capture and display long packets.

For example, most modern NIC drivers handle the TCP segmentation on systems using TCP Segmentation Offload (TSO) to free the CPU and improve the network throughput. In this case, tcpdump receives TCP segments reassembled by the NIC. The size of these segments might be much larger than the MTU size on the network.

In this tutorial, we’ll discuss how to capture long packets using tcpdump. First, we’ll examine the problem by sending a file from one host to another using TCP. Then, we’ll seek a solution by tuning the snapshot length of tcpdump.

2. Examination of the Problem

We’ll use two hosts, namely host1 and host2, to examine the problem. The IP addresses of host1 and host2 are 10.0.2.4 and 10.0.2.5, respectively.

We’ll send the contents of a file from host1 to host2 using TCP and inspect the packets sent using tcpdump.

The version of tcpdump we use in our examples is 4.9.3.

2.1. Creating a Test File

Let’s create the file for our test on host1 using the seq command:

$ seq 1 10000 > file_to_be_sent

Here, we tell the seq command to print numbers from 1 to 10000, in steps of 1. We redirect the output to a file named file_to_be_sent.

Let’s check the first and last three lines in file_to_be_sent using the head and tail commands:

$ head -3 file_to_be_sent
1
2
3
$ tail -3 file_to_be_sent
9998
9999
10000

The content of the file is as expected. Let’s also check the number of lines and the size of the file using the wc command:

$ wc -lc file_to_be_sent
10000 48894 file_to_be_sent

The file contains 10000 lines as expected. Its size is 48894 bytes.

2.2. Starting the TCP Server

Let’s start a TCP server on host2 using the nc command:

$ nc -l 1234

The -l option specifies that nc should listen for incoming connections, and 1234 is the port number it’ll listen on for incoming connections. Notably, nc uses TCP by default.

2.3. Starting tcpdump

Before starting the TCP client on host1, let’s start tcpdump to examine the packets:

$ sudo tcpdump -i any tcp and port 1234 -vvv -X 
dropped privs to tcpdump
tcpdump: listening on any, link-type LINUX_SLL (Linux cooked v1), capture size 262144 bytes

Using tcpdump requires root privileges, so we start tcpdump using the sudo command.

We use the -i option of tcpdump to specify the network interface to capture traffic from. Specifying any as the interface tells tcpdump to capture packets from all interfaces.

The tcp and port 1234 filter expression instructs tcpdump to capture TCP packets having 1234 as either the source or destination port.

Finally, the -vvv option specifies that we want verbose output. The -X option, in addition to printing the headers of packets, prints the data within each packet in hexadecimal and ASCII format.

2.4. Starting the TCP Client and Sending Data

Now, it’s time to start the TCP client on host1 using nc and send data to the TCP server on host2:

$ nc 10.0.2.5 1234 < file_to_be_sent

Running this command opens a TCP connection to the TCP server on host2 that is waiting for connections at port 1234. Additionally, it sends the content of the file thanks to the input redirection < file_to_be_sent.

2.5. Checking the Received Data on the Server

Let’s check whether the data we sent from host1 was received by host2:

$ nc -l 1234
1
2
3
…
9998
9999
10000

As is apparent from the output of the server on host2, the server successfully received all the data sent from host1.

2.6. Checking the Output of tcpdump

Let’s check the output of tcpdump we’ve started on host1. As the output of tcpdump is long, we’ll only check the end of the last packet sent:

    0x27a0:  370a 3530 3338 0a35 3033 390a 3530 3430  7.5038.5039.5040
    0x27b0:  0a35 3034 310a 3530 3432 0a35 3034 330a  .5041.5042.5043.
    0x27c0:  3530 3434 0a35 3034 350a 3530            5044.5045.50

Surprisingly, according to the output of tcpdump, the last number we sent successfully in the file_to_be_sent file is 5045. It seems that only the first two digits (50) of 5046 were sent. The dots between the numbers correspond to the newline characters in the file.

Therefore, tcpdump wasn’t successful in capturing all the data sent. We’ll seek a solution to this problem in the next section.

3. The -s Option of tcpdump

Let’s run tcpdump again to examine the information printed:

$ sudo tcpdump -i any tcp and port 1234 -vvv -X 
dropped privs to tcpdump
tcpdump: listening on any, link-type LINUX_SLL (Linux cooked v1), capture size 262144 bytes

The capture size 262144 bytes statement in the output means that tcpdump can take snapshots of packets having a size of 256 Kbytes by default.

The size of file_to_be_sent is 48894 bytes, which is much less than 256 Kbytes. We can change the snapshot length of tcpdump using the -s option. Notably, the default snapshot length of 262144 bytes is also the maximum snapshot length allowed.

Using a large snapshot length isn’t always the right choice for capturing long packets as we observed in our example. A large snapshot length increases the amount of time to process the packets. Additionally, it decreases the amount of packet buffering. Therefore, packets might be lost while capturing.

Consequently, we must tune the snapshot length according to the observed packet lengths.

3.1. Tuning the Snapshot Length

Let’s send the same file from host1 to host2 once more using nc 10.0.2.5 1234 < file_to_be_sent. We’ll see that the end of the last packet in the output of tcpdump is different from the previous try:

    0x2d50:  0a35 3332 390a 3533 3330 0a35 3333 310a  .5329.5330.5331.
    0x2d60:  3533 3332 0a35 3333 330a 3533 3334 0a35  5332.5333.5334.5
    0x2d70:  3333 350a                                335.

The last successfully captured number in file_to_be_sent is 5335 in this case. Let’s inspect the beginning of this packet in the output of tcpdump:

    host1.local.56762 > 10.0.2.5.search-agent: Flags [.], cksum 0x456f (incorrect -> 0x7d19), seq 13985:25569, ack 1, win 229, options [nop,nop,TS val 1596384357 ecr 1411590835], length 11584
    0x0000:  4500 2d74 ca99 4000 4006 2ae2 0a00 0204  E.-t..@.@.*.....
    0x0010:  0a00 0205 ddba 04d2 1543 a619 992d 9d8f  .........C...-..
    0x0020:  8010 00e5 456f 0000 0101 080a 5f26 e465  ....Eo......_&.e
    0x0030:  5423 2ab3 3031 390a 3330 3230 0a33 3032  T#*.019.3020.302
    0x0040:  310a 3330 3232 0a33 3032 330a 3330 3234  1.3022.3023.3024

The output shows that the length of the TCP packet is 11584 bytes. Therefore, we can try a snapshot length greater than but close to this size. Let’s start tcpdump by passing a snapshot length of 16000 bytes:

$ sudo tcpdump -i any tcp and port 1234 -vvv -X -s 16000
dropped privs to tcpdump
tcpdump: listening on any, link-type LINUX_SLL (Linux cooked v1), capture size 16000 bytes

Resending the file from host1 to host2 using nc 10.0.2.5 1234 < file_to_be_sent, we inspect the end of the last packet in the output of tcpdump:

    0x0c00:  3934 0a39 3939 350a 3939 3936 0a39 3939  94.9995.9996.999
    0x0c10:  370a 3939 3938 0a39 3939 390a 3130 3030  7.9998.9999.1000
    0x0c20:  300a                                     

The last three lines in the output of tcpdump contain the last six lines, from 9995 to 10000, in file_to_be_sent.

Let’s also look at the beginning of this packet to check the TCP packet length:

    host1.local.52800 > 10.0.2.5.search-agent: Flags [FP.], cksum 0x241d (incorrect -> 0x34ac), seq 45841:48895, ack 1, win 229, options [nop,nop,TS val 1801629344 ecr 1376555782], length 3054
    0x0000:  4500 0c22 d0ac 4000 4006 4621 0a00 0204  E.."..@[email protected]!....
    0x0010:  0a00 0205 ce40 04d2 5105 c55d 53bd 0256  [email protected]..]S..V
    0x0020:  8019 00e5 241d 0000 0101 080a 6b62 aea0  ....$.......kb..
    0x0030:  520c 9306 3930 0a39 3339 310a 3933 3932  R...90.9391.9392
    0x0040:  0a39 3339 330a 3933 3934 0a39 3339 350a  .9393.9394.9395.

The length of the last TCP packet is 3054 bytes. The result of summing the lengths of all the TCP packets gives 48894 bytes, which is equal to the size of file_to_be_sent.

Therefore, we’re successful in capturing all the data sent by tuning the snapshot length of tcpdump.

4. Conclusion

In this article, we discussed how to capture long packets using tcpdump. First, we examined the problem by sending a file from one host to another using TCP.

Then, we saw that the default snapshot length of tcpdump is also its maximum snapshot length, 262144 bytes. We learned that using large snapshot lengths may cause packet loss as tcpdump captures packets.

Finally, we saw that tuning the snapshot length of tcpdump according to the expected packet lengths leads to the successful capture of packets without any loss.