1、在Linux 中安装 tcpdump

tcpdump支持多种 Linux 发行版,所以你的系统中很有可能已经安装了它。用下面的命令检查一下是否已经安装了tcpdump:
$ which tcpdump/usr/sbin/tcpdump
如果还没有安装tcpdump,你可以用软件包管理器安装它。 例如,在CentOS 或者 Red Hat Enterprise 系统中,用如下命令安装tcpdump:
$ sudoyum install -y tcpdump
tcpdump依赖于libpcap,该库文件用于捕获网络数据包。如果该库文件也没有安装,系统会根据依赖关系自动安装它。
现在你可以开始抓包了。

2、用tcpdump 抓包

使用tcpdump抓包,需要管理员权限,因此下面的示例中绝大多数命令都是以sudo开头。
首先,先用tcpdump -D命令列出可以抓包的网络接口:
$ sudotcpdump-D1.eth02.virbr03.eth14.any(Pseudo-device that captures on all interfaces)5.lo[Loopback]
如上所示,可以看到我的机器中所有可以抓包的网络接口。其中特殊接口any可用于抓取所有活动的网络接口的数据包。
我们就用如下命令先对any接口进行抓包:
$ sudotcpdump-i anytcpdump: verbose output suppressed,use-v or-vv for full protocol decodelistening on any,link-type LINUX_SLL (Linux cooked), capture size262144 bytes09:56:18.293641 IP rhel75.localdomain.ssh>192.168.64.1.56322:Flags[P.], seq 3770820720:3770820916, ack 3503648727, win 309, options [nop,nop,TS val 76577898 ecr 510770929], length 19609:56:18.293794 IP 192.168.64.1.56322> rhel75.localdomain.ssh:Flags[.], ack 196, win 391, options [nop,nop,TS val 510771017 ecr 76577898], length 009:56:18.295058 IP rhel75.59883> gateway.domain:2486+ PTR?1.64.168.192.in-addr.arpa.(43)---- SKIPPING LONG OUTPUT -----09:56:19.342939 IP 192.168.64.1.56322> rhel75.localdomain.ssh:Flags[.], ack 1752016, win 1444, options [nop,nop,TS val 510772067 ecr 76578948], length 0^C9003 packets captured9010 packets received by filter7 packets dropped by kernel$
tcpdump会持续抓包直到收到中断信号。你可以按Ctrl+C来停止抓包。正如上面示例所示,tcpdump抓取了超过 9000 个数据包。在这个示例中,由于我是通过ssh连接到服务器,所以tcpdump也捕获了所有这类数据包。-c选项可以用于限制tcpdump抓包的数量:
$ sudotcpdump-i any -c 5tcpdump: verbose output suppressed,use-v or-vv for full protocol decodelistening on any,link-type LINUX_SLL (Linux cooked), capture size262144 bytes11:21:30.242740 IP rhel75.localdomain.ssh>192.168.64.1.56322:Flags[P.], seq 3772575680:3772575876, ack 3503651743, win 309, options [nop,nop,TS val 81689848 ecr 515883153], length 19611:21:30.242906 IP 192.168.64.1.56322> rhel75.localdomain.ssh:Flags[.], ack 196, win 1443, options [nop,nop,TS val 515883235 ecr 81689848], length 011:21:30.244442 IP rhel75.43634> gateway.domain:57680+ PTR?1.64.168.192.in-addr.arpa.(43)11:21:30.244829 IP gateway.domain > rhel75.43634:57680NXDomain0/0/0(43)11:21:30.247048 IP rhel75.33696> gateway.domain:37429+ PTR?28.64.168.192.in-addr.arpa.(44)5 packets captured12 packets received by filter0 packets dropped by kernel$
如上所示,tcpdump在抓取 5 个数据包后自动停止了抓包。这在有些场景中十分有用 —— 比如你只需要抓取少量的数据包用于分析。当我们需要使用过滤规则抓取特定的数据包(如下所示)时,-c的作用就十分突出了。
在上面示例中,tcpdump默认是将 IP 地址和端口号解析为对应的接口名以及服务协议名称。而通常在网络故障排查中,使用 IP 地址和端口号更便于分析问题;用-n选项显示 IP 地址,-nn选项显示端口号:
$ sudotcpdump-i any -c5 -nntcpdump: verbose output suppressed,use-v or-vv for full protocol decodelistening on any,link-type LINUX_SLL (Linux cooked), capture size262144 bytes23:56:24.292206 IP 192.168.64.28.22>192.168.64.1.35110:Flags[P.], seq 166198580:166198776, ack 2414541257, win 309, options [nop,nop,TS val 615664 ecr 540031155], length 19623:56:24.292357 IP 192.168.64.1.35110>192.168.64.28.22:Flags[.], ack 196, win 1377, options [nop,nop,TS val 540031229 ecr 615664], length 023:56:24.292570 IP 192.168.64.28.22>192.168.64.1.35110:Flags[P.], seq 196:568, ack 1, win 309, options [nop,nop,TS val 615664 ecr 540031229], length 37223:56:24.292655 IP 192.168.64.1.35110>192.168.64.28.22:Flags[.], ack 568, win 1400, options [nop,nop,TS val 540031229 ecr 615664], length 023:56:24.292752 IP 192.168.64.28.22>192.168.64.1.35110:Flags[P.], seq 568:908, ack 1, win 309, options [nop,nop,TS val 615664 ecr 540031229], length 3405 packets captured6 packets received by filter0 packets dropped by kernel
如上所示,抓取的数据包中显示 IP 地址和端口号。这样还可以阻止tcpdump发出 DNS 查找,有助于在网络故障排查中减少数据流量。
现在你已经会抓包了,让我们来分析一下这些抓包输出的含义吧。

3、理解抓取的报文

tcpdump能够抓取并解码多种协议类型的数据报文,如TCP、UDP、ICMP 等等。虽然这里我们不可能介绍所有的数据报文类型,但可以分析下 TCP 类型的数据报文,来帮助你入门。更多有关tcpdump的详细介绍可以参考其帮助手册。tcpdump抓取的 TCP 报文看起来如下:
08:41:13.729687 IP 192.168.64.28.22>192.168.64.1.41916:Flags[P.], seq 196:568, ack 1, win 309, options [nop,nop,TS val 117964079 ecr 816509256], length 372
具体的字段根据不同的报文类型会有不同,但上面这个例子是一般的格式形式。
第一个字段08:41:13.729687是该数据报文被抓取的系统本地时间戳。
然后,IP是网络层协议类型,这里是IPv4,如果是IPv6协议,该字段值是IP6。
192.168.64.28.22是源 ip 地址和端口号,紧跟其后的是目的 ip 地址和其端口号,这里是192.168.64.1.41916。
在源 IP 和目的 IP 之后,可以看到是 TCP 报文标记段Flags [P.]。该字段通常取值如下:
值标志类型描述SSYNConnection StartFFINConnection FinishPPUSHData pushRRSTConnection reset.ACKAcknowledgment
该字段也可以是这些值的组合,例如[S.]代表SYN-ACK数据包。
接下来是该数据包中数据的序列号。对于抓取的第一个数据包,该字段值是一个绝对数字,后续包使用相对数值,以便更容易查询跟踪。例如此处seq 196:568代表该数据包包含该数据流的第 196 到568 字节。
接下来是 ack 值:ack 1。该数据包是数据发送方,ack 值为 1。在数据接收方,该字段代表数据流上的下一个预期字节数据,例如,该数据流中下一个数据包的 ack 值应该是 568。
接下来字段是接收窗口大小win 309,它表示接收缓冲区中可用的字节数,后跟 TCP 选项如 MSS(最大段大小)或者窗口比例值。更详尽的 TCP 协议内容请参考Transmission Control Protocol(TCP) Parameters。
最后,length 372代表数据包有效载荷字节长度。这个长度和 seq 序列号中字节数值长度是不一样的。
现在让我们学习如何过滤数据报文以便更容易的分析定位问题。

4、过滤数据包

正如上面所提,tcpdump可以抓取很多种类型的数据报文,其中很多可能和我们需要查找的问题并没有关系。举个例子,假设你正在定位一个与 web 服务器连接的网络问题,就不必关系 SSH 数据报文,因此在抓包结果中过滤掉 SSH 报文可能更便于你分析问题。
tcpdump有很多参数选项可以设置数据包过滤规则,例如根据源 IP 以及目的 IP 地址,端口号,协议等等规则来过滤数据包。下面就介绍一些最常用的过滤方法。

协议

在命令中指定协议便可以按照协议类型来筛选数据包。比方说用如下命令只要抓取 ICMP 报文:
$ sudotcpdump-i any -c5 icmptcpdump: verbose output suppressed,use-v or-vv for full protocol decodelistening on any,link-type LINUX_SLL (Linux cooked), capture size262144 bytes
然后再打开一个终端,去ping 另一台机器:
$ ping opensource.comPING 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=47time=39.6 ms
回到运行tcpdump命令的终端中,可以看到它筛选出了 ICMP 报文。这里tcpdump并没有显示有关opensource.com的域名解析数据包:
09:34:20.136766 IP rhel75 > ec2-54-204-39-132.compute-1.amazonaws.com: ICMP echo request,id20361, seq 1, length 6409:34:20.176402 IP ec2-54-204-39-132.compute-1.amazonaws.com > rhel75: ICMP echo reply,id20361, seq 1, length 6409:34:21.140230 IP rhel75 > ec2-54-204-39-132.compute-1.amazonaws.com: ICMP echo request,id20361, seq 2, length 6409:34:21.180020 IP ec2-54-204-39-132.compute-1.amazonaws.com > rhel75: ICMP echo reply,id20361, seq 2, length 6409:34:22.141777 IP rhel75 > ec2-54-204-39-132.compute-1.amazonaws.com: ICMP echo request,id20361, seq 3, length 645 packets captured5 packets received by filter0 packets dropped by kernel

主机

用host参数只抓取和特定主机相关的数据包:
$ sudotcpdump-i any -c5 -nn host 54.204.39.132tcpdump: verbose output suppressed,use-v or-vv for full protocol decodelistening on any,link-type LINUX_SLL (Linux cooked), capture size262144 bytes09:54:20.042023 IP 192.168.122.98.39326>54.204.39.132.80:Flags[S], seq 1375157070, win 29200, options [mss 1460,sackOK,TS val 122350391 ecr 0,nop,wscale 7], length 009:54:20.088127 IP 54.204.39.132.80>192.168.122.98.39326:Flags[S.], seq 1935542841, ack 1375157071, win 28960, options [mss 1460,sackOK,TS val 522713542 ecr 122350391,nop,wscale 9], length 009:54:20.088204 IP 192.168.122.98.39326>54.204.39.132.80:Flags[.], ack 1, win 229, options [nop,nop,TS val 122350437 ecr 522713542], length 009:54:20.088734 IP 192.168.122.98.39326>54.204.39.132.80:Flags[P.], seq 1:113, ack 1, win 229, options [nop,nop,TS val 122350438 ecr 522713542], length 112: HTTP: GET / HTTP/1.109:54:20.129733 IP 54.204.39.132.80>192.168.122.98.39326:Flags[.], ack 113, win 57, options [nop,nop,TS val 522713552 ecr 122350438], length 05 packets captured5 packets received by filter0 packets dropped by kernel
如上所示,只抓取和显示与54.204.39.132有关的数据包。

端口号

tcpdump可以根据服务类型或者端口号来筛选数据包。例如,抓取和 HTTP 服务相关的数据包:
$ sudotcpdump-i any -c5 -nn port 80tcpdump: verbose output suppressed,use-v or-vv for full protocol decodelistening on any,link-type LINUX_SLL (Linux cooked), capture size262144 bytes09:58:28.790548 IP 192.168.122.98.39330>54.204.39.132.80:Flags[S], seq 1745665159, win 29200, options [mss 1460,sackOK,TS val 122599140 ecr 0,nop,wscale 7], length 009:58:28.834026 IP 54.204.39.132.80>192.168.122.98.39330:Flags[S.], seq 4063583040, ack 1745665160, win 28960, options [mss 1460,sackOK,TS val 522775728 ecr 122599140,nop,wscale 9], length 009:58:28.834093 IP 192.168.122.98.39330>54.204.39.132.80:Flags[.], ack 1, win 229, options [nop,nop,TS val 122599183 ecr 522775728], length 009:58:28.834588 IP 192.168.122.98.39330>54.204.39.132.80:Flags[P.], seq 1:113, ack 1, win 229, options [nop,nop,TS val 122599184 ecr 522775728], length 112: HTTP: GET / HTTP/1.109:58:28.878445 IP 54.204.39.132.80>192.168.122.98.39330:Flags[.], ack 113, win 57, options [nop,nop,TS val 522775739 ecr 122599184], length 05 packets captured5 packets received by filter0 packets dropped by kernel

IP 地址/主机名

同样,你也可以根据源 IP 地址或者目的 IP 地址或者主机名来筛选数据包。例如抓取源 IP 地址为192.168.122.98的数据包:
$ sudotcpdump-i any -c5 -nn src 192.168.122.98tcpdump: verbose output suppressed,use-v or-vv for full protocol decodelistening on any,link-type LINUX_SLL (Linux cooked), capture size262144 bytes10:02:15.220824 IP 192.168.122.98.39436>192.168.122.1.53:59332+ A? opensource.com.(32)10:02:15.220862 IP 192.168.122.98.39436>192.168.122.1.53:20749+ AAAA? opensource.com.(32)10:02:15.364062 IP 192.168.122.98.39334>54.204.39.132.80:Flags[S], seq 1108640533, win 29200, options [mss 1460,sackOK,TS val 122825713 ecr 0,nop,wscale 7], length 010:02:15.409229 IP 192.168.122.98.39334>54.204.39.132.80:Flags[.], ack 669337581, win 229, options [nop,nop,TS val 122825758 ecr 522832372], length 010:02:15.409667 IP 192.168.122.98.39334>54.204.39.132.80:Flags[P.], seq 0:112, ack 1, win 229, options [nop,nop,TS val 122825759 ecr 522832372], length 112: HTTP: GET / HTTP/1.15 packets captured5 packets received by filter0 packets dropped by kernel
注意此处示例中抓取了来自源 IP 地址192.168.122.98的53 端口以及 80 端口的数据包,它们的应答包没有显示出来因为那些包的源 IP 地址已经变了。
相对的,使用dst就是按目的 IP/主机名来筛选数据包。
$ sudotcpdump-i any -c5 -nn dst 192.168.122.98tcpdump: verbose output suppressed,use-v or-vv for full protocol decodelistening on any,link-type LINUX_SLL (Linux cooked), capture size262144 bytes>192.168.122.98.47049:22481/0/0 A 54.204.39.132(48)10:05:03.572944 IP 192.168.122.1.53>192.168.122.98.47049:337700/0/0(32)10:05:03.621833 IP 54.204.39.132.80>192.168.122.98.39338:Flags[S.], seq 3474204576, ack 3256851264, win 28960, options [mss 1460,sackOK,TS val 522874425 ecr 122993922,nop,wscale 9], length 010:05:03.667767 IP 54.204.39.132.80>192.168.122.98.39338:Flags[.], ack 113, win 57, options [nop,nop,TS val 522874436 ecr 122993972], length 010:05:03.672221 IP 54.204.39.132.80>192.168.122.98.39338:Flags[P.], seq 1:643, ack 113, win 57, options [nop,nop,TS val 522874437 ecr 122993972], length 642: HTTP: HTTP/1.1302Found5 packets captured5 packets received by filter0 packets dropped by kernel

多条件筛选

当然,可以使用多条件组合来筛选数据包,使用and以及or逻辑操作符来创建过滤规则。例如,筛选来自源 IP 地址192.168.122.98的HTTP 数据包:
$ sudotcpdump-i any -c5 -nn src 192.168.122.98and port 80tcpdump: verbose output suppressed,use-v or-vv for full protocol decodelistening on any,link-type LINUX_SLL (Linux cooked), capture size262144 bytes10:08:00.472696 IP 192.168.122.98.39342>54.204.39.132.80:Flags[S], seq 2712685325, win 29200, options [mss 1460,sackOK,TS val 123170822 ecr 0,nop,wscale 7], length 010:08:00.516118 IP 192.168.122.98.39342>54.204.39.132.80:Flags[.], ack 268723504, win 229, options [nop,nop,TS val 123170865 ecr 522918648], length 010:08:00.516583 IP 192.168.122.98.39342>54.204.39.132.80:Flags[P.], seq 0:112, ack 1, win 229, options [nop,nop,TS val 123170866 ecr 522918648], length 112: HTTP: GET / HTTP/1.110:08:00.567044 IP 192.168.122.98.39342>54.204.39.132.80:Flags[.], ack 643, win 239, options [nop,nop,TS val 123170916 ecr 522918661], length 010:08:00.788153 IP 192.168.122.98.39342>54.204.39.132.80:Flags[F.], seq 112, ack 643, win 239, options [nop,nop,TS val 123171137 ecr 522918661], length 05 packets captured5 packets received by filter0 packets dropped by kernel
你也可以使用括号来创建更为复杂的过滤规则,但在 shell 中请用引号包含你的过滤规则以防止被识别为 shell 表达式:
$ sudotcpdump-i any -c5 -nn "port 80 and (src 192.168.122.98 or src 54.204.39.132)"tcpdump: verbose output suppressed,use-v or-vv for full protocol decodelistening on any,link-type LINUX_SLL (Linux cooked), capture size262144 bytes10:10:37.602214 IP 192.168.122.98.39346>54.204.39.132.80:Flags[S], seq 871108679, win 29200, options [mss 1460,sackOK,TS val 123327951 ecr 0,nop,wscale 7], length 010:10:37.650651 IP 54.204.39.132.80>192.168.122.98.39346:Flags[S.], seq 854753193, ack 871108680, win 28960, options [mss 1460,sackOK,TS val 522957932 ecr 123327951,nop,wscale 9], length 010:10:37.650708 IP 192.168.122.98.39346>54.204.39.132.80:Flags[.], ack 1, win 229, options [nop,nop,TS val 123328000 ecr 522957932], length 010:10:37.651097 IP 192.168.122.98.39346>54.204.39.132.80:Flags[P.], seq 1:113, ack 1, win 229, options [nop,nop,TS val 123328000 ecr 522957932], length 112: HTTP: GET / HTTP/1.110:10:37.692900 IP 54.204.39.132.80>192.168.122.98.39346:Flags[.], ack 113, win 57, options [nop,nop,TS val 522957942 ecr 123328000], length 05 packets captured5 packets received by filter0 packets dropped by kernel