tcpdump 简介
对于 tcpdump
的使用,大部分管理员会分成两类。有一类管理员,他们熟知 tcpdump
和其中的所有标记;另一类管理员,他们仅了解基本的使用方法,剩下事情都要借助参考手册才能完成。出现这种情况的原因在于, tcpdump
是一个相当高级的命令,使用的时候需要对网络的工作机制有相当深入的了解。
在今天的文章中,我想提供一个快速但相当实用的 tcpdump
参考。我会谈到基本的和一些高级的使用方法。我敢肯定我会忽略一些相当酷的命令,欢迎你补充在评论部分。
在我们深入了解以前,最重要的是了解 tcpdump
是用来做什么的。 tcpdump 命令用来保存和记录网络流量。你可以用它来观察网络上发生了什么,并可用来解决各种各样的问题,包括和网络通信无关的问题。除了网络问题,我经常用 tcpdump
解决应用程序的问题。如果你发现两个应用程序之间无法很好工作,可以用 tcpdump
观察出了什么问题。 tcpdump
可以用来抓取和读取数据包,特别是当通信没有被加密的时候。
基础知识
了解 tcpdump
,首先要知道 tcpdump
中使用的标记(flag)。在这个章节中,我会涵盖到很多基本的标记,这些标记在很多场合下会被用到。
不转换主机名、端口号等
1
|
# tcpdump -n
|
通常情况下, tcpdump 会尝试查找和转换主机名和端口号。
1
|
# tcpdump
|
你可以通过 -n
标记关闭这个功能。我个人总是使用这个标记,因为我喜欢使用 IP 地址而不是主机名,主机名和端口号的转换经常会带来困扰。但是,知道利用 tcpdump
转换或者不转换的功能还是相当有用的,特别是有些时候,知道源流量(source traffic)来自哪个服务器是相当重要的。
1
|
# tcpdump -n
|
增加详细信息
1
|
# tcpdump -v
|
增加一个简单 -v
标记,输出会包含更多信息,例如一个 IP 包的生存时间(ttl, time to live)、长度和其他的选项。
1
|
# tcpdump
|
tcpdump
的详细信息有三个等级,你可以通过在命令行增加 v
标记的个数来获取更多的信息。通常我在使用 tcpmdump
的时候,总是使用最高等级的详细信息,因为我希望看到所有信息,以免后面会用到。
1
|
# tcpdump -vvv -c 1
|
指定网络接口
1
|
# tcpdump -i eth0
|
通常情况下,如果不指定网络接口, tcpdump
在运行时会选择编号最低的网络接口,一般情况下是 eth0
,不过因系统不同可能会有所差异。
1
|
# tcpdump
|
你可以用 -i
标记来指定网络接口。在大多数 Linux 系统上,any
这一特定的网络接口名用来让 tcpdump
监听所有的接口。我发现这在排查服务器(拥有多个网络接口)的问题特别有用,尤其是牵扯到路由的时候。
1
|
# tcpdump -i any
|
写入文件
1
|
# tcpdump -w /path/to/file
|
tcpdump
运行结果会输出在屏幕上。
1
|
# tcpdump
|
但很多时候,你希望把 tcpdump
的输出结果保存在文件中,最简单的方法就是利用 -w
标记。如果你后续还会检查这些网络数据,这样做就特别有用。将这些数据存成一个文件的好处,就是你可以多次读取这个保存下来的文件,并且可以在这个网络流量的快照上使用其它标记或者过滤器(我们后面会讨论到)。
1
|
# tcpdump -w /var/tmp/tcpdata.pcap
|
通常这些数据被缓存而不会被写入文件,直到你用 CTRL+C
结束 tcpdump
命令的时候。
读取文件
1
|
# tcpdump -r /path/to/file
|
一旦你将输出存成文件,就必然需要读取这个文件。要做到这点,你只需要在 -r
标记后指定这个文件的存放路径。
1
|
# tcpdump -r /var/tmp/tcpdata.pcap
|
一个小提醒,如果你熟悉 wireshark 这类网络诊断工具,也可以利用它们来读取 tcpdump
保存的文件。
指定抓包大小
1
|
# tcpdump -s 100
|
较新版本的 tcpdump
通常可以截获 65535 字节,但某些情况下你不需要截获默认大小的数据包。运行 tcpdump
时,你可以通过 -s
标记来指定快照长度。
指定抓包数量
1
|
# tcpdump -c 10
|
tcpdump
会一直运行,直至你用 CTRL+C
让它退出。
1
|
# tcpdump host google.com
|
你也可以通过 -c
标记后面加上抓包的数量,让 tcpdump
在抓到一定数量的数据包后停止操作。当你不希望看到 tcpdump
的输出大量出现在屏幕上,以至于你无法阅读的时候,就会希望使用这个标记。当然,通常更好的方法是借助过滤器来截获特定的流量。
基础知识汇总
1
|
# tcpdump -nvvv -i any -c 100 -s 100
|
你可以将以上这些基础的标记组合起来使用,来让 tcpdump
提供你所需要的信息。
1
|
# tcpdump -w /var/tmp/tcpdata.pcap -i any -c 10 -vvv
|
过滤器
介绍完基础的标记后,我们该介绍过滤器了。 tcpdump
可以通过各式各样的表达式,来过滤所截取或者输出的数据。我在这篇文章里会给出一些简单的例子,以便让你们了解语法规则。你们可以查询 tcpdump
帮助中的 pcap-filter 章节,了解更为详细的信息。
查找特定主机的流量
1
|
# tcpdump -nvvv -i any -c 3 host 10.0.3.1
|
运行上述命令, tcpdump
会像前面一样把结果输出到屏幕上,不过只会显示源 IP 或者目的 IP 地址是 10.0.3.1
的数据包。通过增加主机 10.0.3.1
参数,我们可以让 tcpdump
过滤掉源和目的地址不是 10.0.3.1
的数据包。
1
|
# tcpdump -nvvv -i any -c 3 host 10.0.3.1
|
只显示源地址为特定主机的流量
1
|
# tcpdump -nvvv -i any -c 3 src host 10.0.3.1
|
前面的例子显示了源和目的地址是 10.0.3.1
的流量,而上面的命令只显示数据包源地址是 10.0.3.1
的流量。这是通过在 host
前面增加 src
参数来实现的。这个额外的过滤器告诉 tcpdump
查找特定的源地址。 反过来通过 dst
过滤器,可以指定目的地址。
1
|
# tcpdump -nvvv -i any -c 3 src host 10.0.3.1
|
过滤源和目的端口
1
|
# tcpdump -nvvv -i any -c 3 port 22 and port 60738
|
通过类似 and
操作符,你可以在 tcpdump
上使用更为复杂的过滤器描述。这个就类似 if
语句,你就这么想吧。这个例子中,我们使用 and
操作符告诉 tcpdump
只输出端口号是 22
和 60738
的数据包。这点在分析网络问题的时候很有用,因为可以通过这个方法来关注某一个特定会话(session)的数据包。
1
|
# tcpdump -nvvv -i any -c 3 port 22 and port 60738
|
你可以用两种方式来表示 and
操作符,and
或者 &&
都可以。我个人倾向于两个都使用,特别要记住在使用 &&
的时候,要用单引号或者双引号包住表达式。在 BASH 中,你可以使用 &&
运行一个命令,该命令成功后再执行后面的命令。通常,最好将表达式用引号包起来,这样会避免不预期的结果,特别当过滤器中有一些特殊字符的时候。
1
|
# tcpdump -nvvv -i any -c 3 'port 22 && port 60738'
|
查找两个端口号的流量
1
|
# tcpdump -nvvv -i any -c 20 'port 80 or port 443'
|
你可以用 or
或者 ||
操作符来过滤结果。在这个例子中,我们使用 or
操作符去截获发送和接收端口为 80
或 443
的数据流。这在 Web 服务器上特别有用,因为服务器通常有两个开放的端口,端口号 80
表示 http
连接,443
表示 https
。
1
|
# tcpdump -nvvv -i any -c 20 'port 80 or port 443'
|
查找两个特定端口和来自特定主机的数据流
1
|
# tcpdump -nvvv -i any -c 20 '(port 80 or port 443) and host 10.0.3.169'
|
前面的例子用来排查多端口的协议问题,是非常有效的。如果 Web 服务器的数据流量相当大, tcpdump
的输出可能有点混乱。我们可以通过增加 host
参数进一步限定输出。在这种情况下,我们通过把 or
表达式放在括号中来保持 or
描述。
1
|
# tcpdump -nvvv -i any -c 20 '(port 80 or port 443) and host 10.0.3.169'
|
在一个过滤器中,你可以多次使用括号。在下面的例子中,下面命令可以限定截获满足如下条件的数据包:发送或接收端口号为 80
或 443
,主机来源于 10.0.3.169
或者 10.0.3.1
,且目的地址是 10.0.3.246
。
1
|
# tcpdump -nvvv -i any -c 20 '((port 80 or port 443) and (host 10.0.3.169 or host 10.0.3.1)) and dst host 10.0.3.246'
|
理解输出结果
打开 tcpdump
的所有选项去截获网络流量是相当困难的,但一旦你拿到这些数据你就要对它进行解读。在这个章节,我们将涉及如何判断源/目的 IP 地址,源/目的端口号,以及 TCP
协议类型的数据包。当然这些是相当基础的,你从 tcpdump
里面获取的信息也远不止这些。不过这篇文章主要是粗略的介绍,我们会关注在这些基础知识上。我建议你们可以通过帮助页获取更为详细的信息。
判断源和目的地址
判断源和目的地址和端口号相当简单。
从上面的输出,我们可以看到源 IP 地址是 10.0.3.246
,源端口号是 56894
, 目的 IP 地址是 192.168.0.92
,端口号是 22
。一旦你理解 tcpdump
格式后,这些信息很容易判断。如果你还没有猜到格式,你可以按照 src-ip.src-port > dest-ip.dest-port: Flags[S]
格式来分析。源地址位于 >
前面,后面则是目的地址。你可以把 >
想象成一个指向目的地址的箭头符号。
判断数据包类型
1
|
10.0.3.246.56894 > 192.168.0.92.22: Flags [S], cksum 0xcf28 (incorrect -> 0x0388), seq 682725222, win 29200, options [mss 1460,sackOK,TS val 619989005 ecr 0,nop,wscale 7], length 0
|
从上面的例子,我们可以判断这个数据包是一个 SYN
数据包。我们是通过 tcpdump
输出中的 [S]
标记字段得出这个结论,不同类型的数据包有不同类型的标记。不需要深入了解 TCP
协议中的数据包类型,你就可以通过下面的速查表来加以判断。
- [S] – SYN (开始连接)
- [.] – 没有标记
- [P] – PSH (数据推送)
- [F] – FIN (结束连接)
- [R] – RST (重启连接)
在这个版本的 tcpdump
输出中,[S.]
标记代表这个数据包是 SYN-ACK
数据包。
不好的例子
1
|
15:15:43.323412 IP (tos 0x0, ttl 64, id 51051, offset 0, flags [DF], proto TCP (6), length 60)
|
上面显示了一个不好的通信例子,在这个例子中“不好”,代表通信没有建立起来。我们可以看到 10.0.3.246
发出一个 SYN
数据包给 主机 192.168.0.92
,但是主机并没有应答。
好的例子
1
|
15:18:25.716453 IP (tos 0x10, ttl 64, id 53344, offset 0, flags [DF], proto TCP (6), length 60)
|
好的例子应该向上面这样,我们看到典型的 TCP 3次握手。第一数据包是 SYN
包,从主机 10.0.3.246
发送给 主机192.168.0.110
,第二个包是 SYN-ACK
包,主机192.168.0.110
回应 SYN
包。最后一个包是一个 ACK
或者 SYN – ACK – ACK
包,是主机 10.0.3.246
回应收到了 SYN – ACK
包。从上面看到一个 TCP/IP 连接成功建立。
数据包检查
用十六进制和 ASCII 码打印数据包
1
|
# tcpdump -nvvv -i any -c 1 -XX 'port 80 and host 10.0.3.1'
|
排查应用程序网络问题的通常做法,就是用 tcpdump
的 -XX
标记打印出 16 进制和 ASCII 码格式的数据包。这是一个相当有用的命令,它可以让你看到源地址,目的地址,数据包类型以及数据包本身。但我不是这个命令输出的粉丝,我认为它太难读了。
1
|
# tcpdump -nvvv -i any -c 1 -XX 'port 80 and host 10.0.3.1'
|
只打印 ASCII 码格式的数据包
1
|
# tcpdump -nvvv -i any -c 1 -A 'port 80 and host 10.0.3.1'
|
我倾向于只打印 ASCII 格式数据,这可以帮助我快速定位数据包中发送了什么,哪些是正确的,哪些是错误的。你可以通过 -A
标记来实现这一点。
1
|
# tcpdump -nvvv -i any -c 1 -A 'port 80 and host 10.0.3.1'
|
从上面的输出,你可以看到我们成功获取了一个 http 的 GET
请求包。如果网络通信没有被加密,用人类可阅读的格式打出包中数据,对于解决应用程序的问题是很有帮助。如果你排查一个网络通信被加密的问题,打印包中数据就不是很有用。不过如果你有证书的话,你还是可以使用 ssldump
或者 wireshark
。
非 TCP 数据流
虽然这篇文章主要采用 TCP 传输来讲解 tcpdump
,但是 tcpdump
绝对不是只能抓 TCP 数据包。它还可以用来获取其他类型的数据包,例如 ICMP、 UDP 和 ARP 包。下面是一些简单的例子,说明 tcpdump
可以截获非 TCP 数据包。
ICMP 数据包
1
|
# tcpdump -nvvv -i any -c 2 icmp
|
UDP 数据包
1
|
# tcpdump -nvvv -i any -c 2 udp
|
如果你觉得有好例子进一步说明 tcpdump
命令,请在评论中补充。