一个功能强大、灵活的命令行工具可以帮助你解决网络故障。
tcpdump是一个命令行程序,可让你捕获和分析通过系统的网络流量。 tcpdump是一个功能强大且用途广泛的工具,其中包括许多选项和过滤器,可以在多种情况下使用。 由于它是命令行工具,适合在没有GUI的远程服务器中运行,以收集可以分析的数据。
在本文中,我们将介绍tcpdump的一些最常见功能。
1. tcpdump的工作原理
tcpdump运行在用户态,底层调用的是libpcap
库的各种api
来实现数据包的捕获。
libpcap
是一个捕获网络流量的C/C++库,通过libpcap库可以构建不同的应用程序,数据包格式为pcap
。
在上图中,我们通过ftp在本机和ftp.arpa.net
之间传输数据,数据包到达网卡(Network Interface Card,简称NIC)后,经过数据包过滤器(Berkeley Packet Filter,简称BPF)筛选后,将数据包拷贝到tcpdump,然后tcpdump进行数据包的解码,可以在终端显示,也可将数据包以pcap
格式保存到本地磁盘。
从上图中,我们可以看出数据到达bpf进行包过滤是在tcp协议栈之前操作的。
2. tcpdump捕获包
tcpdump包含在多个Linux发行版中,因此可能已经安装了它。 使用以下命令检查系统上是否安装了tcpdump:
$ which tcpdump
/usr/sbin/tcpdump
如果tcpdump没有安装,可以Google进行安装。
现在可以开始抓包了。
为了捕获数据包以进行故障排除或分析,tcpdump需要root权限,因此在以下示例中,大多数命令都以sudo
前缀。
首先,使用命令tcpdump --list-interfaces(或简称-D)查看哪些接口可用于捕获:
$ sudo tcpdump -D
1.eth0 [Up, Running]
2.any (Pseudo-device that captures on all interfaces) [Up, Running]
3.lo [Up, Running, Loopback]
4.nflog (Linux netfilter log (NFLOG) interface)
5.nfqueue (Linux netfilter queue (NFQUEUE) interface)
6.usbmon1 (USB bus number 1)
在上面的示例中,你可以看我所有可用的接口。特殊接口any可以允许抓任意接口的包。
让我们开始抓所有网卡的包:
$ sudo tcpdump -i any
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
21:34:51.424030 IP iZwz9czknxjqmstiji1vpmZ.ssh > 183.230.12.174.3821: Flags [P.], seq 2488863953:2488864085, ack 607933093, win 249, options [nop,nop,TS val 2433154148 ecr 1140831183], length 132
21:34:51.424062 IP iZwz9czknxjqmstiji1vpmZ.ssh > 183.230.12.174.3821: Flags [P.], seq 132:184, ack 1, win 249, options [nop,nop,TS val 2433154148 ecr 1140831183], length 52
21:34:51.424091 IP iZwz9czknxjqmstiji1vpmZ.ssh > 183.230.12.174.3821: Flags [P.], seq 184:316, ack 1, win 249, options
...
^C
465 packets captured
532 packets received by filter
67 packets dropped by kernel
$
tcpdump会一直抓包,直到按下Ctrl+C中断。
我们可以通过限制抓包的数量来停止tcpdump,使用-c(count)选项。
$ sudo tcpdump -i eth0 -c 3
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
21:39:54.163860 IP iZwz9czknxjqmstiji1vpmZ.ssh > 183.230.12.174.3821: Flags [P.], seq 2488983077:2488983209, ack 607934845, win 249, options [nop,nop,TS val 2433229833 ecr 1141133924], length 132
21:39:54.163890 IP iZwz9czknxjqmstiji1vpmZ.ssh > 183.230.12.174.3821: Flags [P.], seq 132:184, ack 1, win 249, options [nop,nop,TS val 2433229833 ecr 1141133924], length 52
21:39:54.163918 IP iZwz9czknxjqmstiji1vpmZ.ssh > 183.230.12.174.3821: Flags [P.], seq 184:316, ack 1, win 249, options [nop,nop,TS val 2433229833 ecr 1141133924], length 132
3 packets captured
23 packets received by filter
14 packets dropped by kernel
这个例子中,tcpdump在抓了eth0网卡的3个包之后自动停止。
默认情况下,tcpdump将IP地址和端口解析为名称,如上例所示。 指定 -n
选项,不解析主机和端口名。这个参数很关键,会影响抓包的性能,一般抓包时都需要指定该选项:
$ sudo tcpdump -i eth0 -c 3 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
21:42:18.091676 IP 172.18.158.34.22 > 183.230.12.174.3821: Flags [P.], seq 2488985253:2488985385, ack 607935361, win 249, options [nop,nop,TS val 2433265815 ecr 1141277854], length 132
21:42:18.091707 IP 172.18.158.34.22 > 183.230.12.174.3821: Flags [P.], seq 132:184, ack 1, win 249, options [nop,nop,TS val 2433265815 ecr 1141277854], length 52
21:42:18.091735 IP 172.18.158.34.22 > 183.230.12.174.3821: Flags [P.], seq 184:316, ack 1, win 249, options [nop,nop,TS val 2433265815 ecr 1141277854], length 132
3 packets captured
9 packets received by filter
0 packets dropped by kernel
现在我们已经可以捕获网络数据包,让我们看看输出的含义。
3. 捕获包的输出格式
tcpdump能够捕获和解码许多不同的协议,例如TCP,UDP,ICMP等。这里让我们探究常见的TCP数据包。 您可以在tcpdump的手册页中找到有关不同协议格式的更多详细信息。 tcpdump捕获的典型TCP数据包如下所示:
21:48:44.091859 IP 172.18.158.34.22 > 183.230.12.174.3821: Flags [P.], seq 132:184, ack 1, win 249, options [nop,nop,TS val 2433362315 ecr 1141663861], length 52
第一个字段,21:48:44.091859代表根据本地时钟接收的数据包的时间戳。
接下来,IP代表网络层协议,在本例中为IPv4。 对于IPv6数据包,该值为IP6。
下一个字段172.18.158.34.22是源IP地址和端口。 随后是目标IP地址和端口,由183.230.12.174.3821表示。
在之后,可以找到TCP标志Flags [P.]
。 该字段的典型值包括:
Value | Flag Type | Description |
---|---|---|
S | SYN | Connection Start |
F | FIN | Connection Finish |
P | PUSH | Data push |
R | RST | Connection reset |
. | ACK | Acknowledgment |
字段也可以是这些值的组合,例如[S.]表示SYN-ACK数据包。
接下来是数据的序列号。 对于第一个捕获的数据包,这是一个绝对数。 后续数据包使用相对编号,便于跟踪。 在此示例中,序列为seq 132:184,这意味着此数据包为132至184的字节。
其后是ack 1
。在这种情况下,它是1,因为这是发送数据的一方。 对于接收数据的一方,此字段表示该数据流中的下一个预期字节。 例如,该数据流下一个数据包的ack编号将为184。
下一个字段是win 249
,它表示接收缓冲区中可用的字节数,其后是TCP选项,例如MSS(Maximum Segment Size)或窗口大小。有关TCP协议选项的详细信息,请查看Transmission Control Protocol (TCP) Parameters。
最后,length 52
表示数据包长度。
现在,让我们学习如何过滤数据包来缩小抓包范围。
4. 过滤包
tcpdump最强大的功能之一就是能使用各种参数(例如源和目标IP地址,端口,协议等)过滤数据包。让我们来看一些最常见的参数。
- 协议
通过协议来过滤包,比如抓ICMP包:
$ sudo tcpdump -ni eth0 -c5 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
22:10:54.475196 IP 172.18.158.34 > 54.204.39.132: ICMP echo request, id 613, seq 28, length 64
22:10:54.744406 IP 54.204.39.132 > 172.18.158.34: ICMP echo reply, id 613, seq 28, length 64
22:10:55.476358 IP 172.18.158.34 > 54.204.39.132: ICMP echo request, id 613, seq 29, length 64
22:10:55.744416 IP 54.204.39.132 > 172.18.158.34: ICMP echo reply, id 613, seq 29, length 64
22:10:56.477287 IP 172.18.158.34 > 54.204.39.132: ICMP echo request, id 613, seq 30, length 64
5 packets captured
5 packets received by filter
0 packets dropped by kernel
在另一个终端中,对另一台计算机执行ping操作:
$ ping opensource.com
PING opensource.com (54.204.39.132) 56(84) bytes of data.
64 bytes from ec2-54-204-39-132.compute-1.amazonaws.com (54.204.39.132): icmp_seq=1 ttl=26 time=267 ms
- host
$ sudo tcpdump -ni eth0 -c5 host 54.204.39.132
- port
根据端口过滤数据包。 例如,使用以下命令捕获与Web(HTTP)服务相关的数据包:
$ sudo tcpdump -ni eth0 -c5 port 80
- 源IP、目的IP和主机名
$ sudo tcpdump -ni eth0 -c5 src 172.18.158.34
$ sudo tcpdump -ni eth0 -c5 dst 172.18.158.34
- 复杂表达式
通过使用逻辑运算符and和or创建更复杂的表达式来组合过滤器。 例如,仅过滤来自源IP地址192.168.122.98并且是HTTP的数据包:
$ sudo tcpdump -ni eth0 -c5 src 172.18.158.34 and port 80
还可以将过滤器与括号分组来创建更复杂的表达式。在这种情况下,请用引号将整个过滤器表达式引起来,以防止shell程序将它们与shell表达式混淆:
$ sudo tcpdump -ni any -c5 "port 80 and (src 172.18.158.34 or src 54.204.39.132)"
- 打印出数据包的内容
- -A:以ASCII打印内容
- -X:以十六进制打印内容
$ sudo tcpdump -ni eth0 -c5 -A
$ sudo tcpdump -ni eth0 -c5 -X
- 将抓包内容保存到文件中,
-w
(write)
$ sudo tcpdump -ni any -c10 -w webserver.pcap port 80
此命令将抓到的包保存在webserver.pcap的文件中。 .pcap扩展名表示“数据包捕获”,并且是此文件格式的约定,可通过wireshark分析捕获的包。任何内容都不会显示在屏幕上,并且在捕获10个数据包之后完成。 如果需要显示捕获了数据包,请使用选项-v
。
要读取文件的内容,可以用-r(用于读取)选项执行tcpdump:
$ tcpdump -n -r webserver.pcap
6. 参考信息来源
- An introduction to using tcpdump at the Linux command line
- libpcap: An Architecture and Optimization Methodology for Packet Capture Steve McCanne, CTO Riverbed Technology - Sharkfest’11.
- Let’s learn tcpdump! by Julia Evans.
- tcpdump website
- Man page of TCPDUMP