tcpdump is a core command-line utility for monitoring one or more wired and wireless network interfaces including non-Ethernet interfaces, such as USB. We make extensive use of tcpdump on both Linux and macOS systems during network testing. This article is primarly devoted to its use on Linux although some of the examples below are taken from a macOS system. On Windows, we typically just run Wireshark.
Invoking tcpdump on Linux for an Ethernet device configures the network interface for promiscuous mode, which means that the Linux kernel, libpcap library, and the tcpdump application can receive all packets received on the specified network interface(s). Keep in mind that a network interface on a switched Ethernet network will only receive packets destined to it based on its Ethernet MAC address and the destination MAC address embedded in the Ethernet frame. For example, it won't receive unicast packets between two other hosts that are communicating with each other. However, it will receive broadcast frames destined to MAC address ff:ff:ff:ff:ff:ff.
The command line examples shown below are taken from various systems including a Yocto-built system running an embedded Linux kernel and a macOS system. In most cases, the underlying system doesn't matter. However, each system may support different interfaces and have different characteristics at the lower layers of the interface (e.g, Ethernet MAC & link layer).
For the purpose of establishing a baseline, the version and library dependencies are shown below for our Linux system running on a Yocto-built embedded system:
# tcpdump --version tcpdump version 4.9.0 libpcap version 1.8.1 OpenSSL 1.0.2k 26 Jan 2017
# ldd /usr/sbin/tcpdump linux-vdso32.so.1 (0x00100000) libcrypto.so.1.0.0 => /usr/lib/libcrypto.so.1.0.0 (0x0fdf4000) libpcap.so.1 => /usr/lib/libpcap.so.1 (0x0fd93000) libc.so.6 => /lib/libc.so.6 (0x0fbdf000) libdl.so.2 => /lib/libdl.so.2 (0x0fbae000) /lib/ld.so.1 (0xb7cbb000)
Note the reference to OpenSSL and the inclusion of libcrypto. tcpdump provides some support for decoding protocols that use encryption (e.g., IPsec). Future revisions of this article will provide examples of this.
tcpdump supports many options, and you can read about all of them on its man page. However, listed below are some of the options we find essential:
- -i <dev>: Specify interface / device (run "tcpdump -D" to get list, see example below)
- -n : Show addresses, not names (e.g., port or host name)
- -v, -vv, -vvv : more and more verbosity
- -X : Dump the packet in both hex and ASCII.
- -x : Dump the packet in hex
- -s <snaplen> : Specify the number of bytes to capture.
- -K: If you're seeing "bad cksum", you can suppress this with this option.
- -t, -tt, -ttt,...: many different time stamp options
1. List the interfaces that can be used with tcpdump on the particular machine:
# tcpdump -D # Linux Embedded System 1.wlan0 [Up, Running] 2.eth1 [Up, Running] 3.eth2 [Up, Running] 4.any (Pseudo-device that captures on all interfaces) [Up, Running] 5.lo [Up, Running, Loopback] 6.eth0 [Up] 7.usbmon0 (All USB buses) 8.nflog (Linux netfilter log (NFLOG) interface) 9.nfqueue (Linux netfilter queue (NFQUEUE) interface) 10.usbmon1 (USB bus number 1)
2. Only show ICMP packets (typically ping):
# tcpdump -i eth2 icmp tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth2, link-type EN10MB (Ethernet), capture size 262144 bytes 15:01:38.331760 IP macos > 192.168.3.200: ICMP echo request, id 47879, seq 0, length 64 15:01:38.331808 IP 192.168.3.200 > macos: ICMP echo reply, id 47879, seq 0, length 64
3. Do it again but tweak the output with the use of options:
# tcpdump -vvvnxs 62 -i eth2 icmp tcpdump: listening on eth2, link-type EN10MB (Ethernet), capture size 62 bytes 16:08:36.831555 IP (tos 0x0, ttl 64, id 52036, offset 0, flags [none], proto ICMP (1), length 84) 192.168.3.24 > 192.168.3.200: ICMP echo request, id 63495, seq 0, length 64 0x0000: 4500 0054 cb44 0000 4001 2734 c0a8 0318 0x0010: c0a8 03c8 0800 d111 f807 0000 590c dbc4 0x0020: 000d 0f05 0809 0a0b 0c0d 0e0f 1011 1213 16:08:36.831605 IP (tos 0x0, ttl 64, id 40840, offset 0, flags [none], proto ICMP (1), length 84) 192.168.3.200 > 192.168.3.24: ICMP echo reply, id 63495, seq 0, length 64 0x0000: 4500 0054 9f88 0000 4001 52f0 c0a8 03c8 0x0010: c0a8 0318 0000 d911 f807 0000 590c dbc4 0x0020: 000d 0f05 0809 0a0b 0c0d 0e0f 1011 1213
4. Local loopback (lo) on port 8000, useful for local HTTP Server Development.
$ sudo tcpdump -vvXn -i lo0 port 8000 Password: tcpdump: listening on lo0, link-type NULL (BSD loopback), capture size 262144 bytes 16:48:21.236898 IP (tos 0x0, ttl 64, id 3555, offset 0, flags [DF], proto TCP (6), length 64, bad cksum 0 (->a554)!) 192.168.3.24.64451 > 192.168.3.24.8000: Flags [S], cksum 0x87b3 (incorrect -> 0x3129), seq 4093117193, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 1248323521 ecr 0,sackOK,eol], length 0 0x0000: 4500 0040 0de3 4000 4006 0000 c0a8 0318 E..@..@.@....... 0x0010: c0a8 0318 fbc3 1f40 f3f8 0309 0000 0000 .......@........ 0x0020: b002 ffff 87b3 0000 0204 3fd8 0103 0305 ..........?..... 0x0030: 0101 080a 4a67 e7c1 0000 0000 0402 0000 ....Jg.......... 16:48:21.236967 IP (tos 0x0, ttl 64, id 46580, offset 0, flags [DF], proto TCP (6), length 64, bad cksum 0 (->fd42)!) 192.168.3.24.8000 > 192.168.3.24.64451: Flags [S.], cksum 0x87b3 (incorrect -> 0xed6c), seq 1597026897, ack 4093117194, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 1248323521 ecr 1248323521,sackOK,eol], length 0 0x0000: 4500 0040 b5f4 4000 4006 0000 c0a8 0318 E..@..@.@....... 0x0010: c0a8 0318 1f40 fbc3 5f30 b251 f3f8 030a .....@.._0.Q.... 0x0020: b012 ffff 87b3 0000 0204 3fd8 0103 0305 ..........?..... 0x0030: 0101 080a 4a67 e7c1 4a67 e7c1 0402 0000 ....Jg..Jg......
Note above that the tcpdump output shows "bad cksum 0". As you can see in the dump, the sixth word, which is the IP checksum field is 0. More than likely the checksum was discarded in the driver or at the Ethernet MAC layer (the device that terminated the packet). This can be suppressed with the "-K" option.
5. For the case of when you're logged into a target system remotely and don't want to dump your own traffic, you can do something like this:
# tcpdump -i eth2 not net <ip addr1> and not net <ip addr2>
6. You can also do the same thing with host names (e.g., names in /etc/hosts):
# tcpdump -i eth2 not host <host name>
7. This example provides a dump of TCP connect and tear down handshaking (either the SYN or FIN flags are set):
# tcpdump -i eth2 not host <host name> and 'tcp[tcpflags] & (tcp-syn|tcp-fin) != 0'
More examples are coming...