本章序言
云网络环境下,当问题出现时,往往是没有充足的时间来全栈分析问题出现的原因,此时,“抓包”这个手段就显得尤为重要,它可以在只需要付出少量的开销就可以获得观察到链路上通信数据包的能力,性价比尤为突出。本文主要从定制个性化的Wireshark开始,同时也介绍了Wireshark自带的一些命令行工具,希望可以为大家提供排查问题的一些思路。另外,本文不会探讨如何去使用Wireshark来抓包,而是重点讨论如何通过Wireshark及相关命令行工具去分析抓包,找到问题的结症。
从抓包说起
不知何时开始,“抓包”一词在IT圈内已经变得人尽皆知,任何问题只要涉及到网络时,有很大的概率会使用抓包来排查。我对抓包的态度,也是又爱又恨:出现的频率如此之高,说明这个方法确实有它的效果,否则不会被这么多人提起,但究其本身来看,抓包对问题排查其实是一个投入产出比非常差的方案:它一定需要问题出现,而且成功的抓包了问题现场的包,才能有分析问题的前提条件。
在历史案例处理中,特别是一些疑难案例,往往都需要好几次的问题现场抓包,最终才能定位问题原因。站在客户的角度上,得要非常配合的客户才会事无巨细的做好一些事情,比如部署抓包、复现问题、提供问题现场信息等等。所以每一次问题现场的抓包,都需要尽我们所能提供足够的捕获问题环境的手段、分析问题抓包的方法。
千人千面
Wireshark本身是一个跨平台的工具,可以轻松地在windows、MacOS、Linux三大主流平台上安装并运行。使用Wireshark打开一个网络包后,可以看到一个默认界面,我强烈建议大家对这个界面作一些定制化的修改,比如
- 分析TCP协议时将sequence number、ack number、源端口、目标端口、TCP segment length放到数据列中
- 分析ICMP协议时,可以将三层的TTL、identification放到数据列中
以下展示了我个人比较喜欢的数据列:
理由如下:
- 工作中我经常碰到需要分析TCP数据包的情况,所以我需要一目了然的掌握每一个数据包的seq number和ack number
- TTL是辨别短时间内来自相同的目标IP是否是同一个机器发出的,目前网络上存在非常多的防火墙会截获一些报文,然后生造类似的报文发回,这种情况下TTL字段就派上用场了。
- id(identification)值则是IP层一个不起眼的字段,但是却可以指示这个包的唯一性,机器每发一个包,这个值就自增一次,通常的用处是快速找到两个抓包中同一个IP包,或者是确认某一个包是重传包还是转发包(这里的重传包指的是上层应用的重传,前者id值会自增,后者不会)
- 源目端口的fields值用了一个小技巧,我将tcp和udp的端口过滤条件合并在了一起,中间使用逻辑或来连接,这样这一列就可以同时支持显示TCP、UDP两个协议的端口,不需要来回切换
- TCP Seg Len字段显示了他的载荷字节大小,因为工作中经常会碰到MTU、MSS等类似的问题,可以通过这个字段快速的判断某一个TCP包中是否有载荷。
当然,这里展示的只是我在工作中常用的字段,大家所处的岗位不同,业务场景也不同,所需要的字段也不尽相同,大家可以根据需要手动添加自己需要的fields,Wireshark可以支持所有的过滤条件展示为数据列,当然它也支持直接在Packet Details中,选中一个字段,直接右击,选择Apply as Column即可,非常方便。
Wireshark的伙伴们
当你将Wireshark装进电脑时,在它的程序目录下,其实还附带了很多非常有用的程序,只是这些程序都没有图形界面,所以上手会比较困难,但也正是因为他们舍弃了图形界面,在某些场合下反而比Wireshark要更好用,甚至还能提供Wireshark都不具备的功能。
tshark
首先登场的是tshark,你可以简单理解为它是文本形式的Wireshark,一般会用作以下场景:
一、抓包文件过大,需要对它进行一些操作,比如根据特定的过滤条件导出筛选后的包;
二、提取抓包内特定的数据,作为其他程序的文本输入,实现一些自定义逻辑。比如我要分析某个包中来自特定IP的ICMP包的数量,那么可以结合tshark的显示过滤器,过滤出特定的抓包数据,然后作为输入传给wc。
当然,你依然可以通过-h参数来打印出tshark支持的参数列表,这里提供几个比较有用的参数:
- -R -Y:这两个参数分别为读取过滤器和显示过滤器,他们的区别如名称所示,tshark在展示包时,会有两个阶段,先从pcap文件中读取数据,然后展示出来,那么这两个过滤器分别会应用在对对应的阶段。但一般-Y参数用的会更多一些,一方面是-R参数需要读取两次包(需要配合-2参数),时间较久;另外一方面-R参数会破坏包的序列号,不方便对比分析。两个参数支持的过滤条件,形如" tcp.flags.syn==1"
- -t:这个参数指定time显示格式,一般推荐-r(相对首包的时间)、-a/-ad(绝对时间)
- -e:指定tshark显示的列名,这里支持任何Wireshark的字段名,如tshark -e frame.number -e ip.addr -e udp
其余还有很多参数,限于篇幅这里就不展开介绍了,若有兴趣可以从官方网站上查看到所有支持的参数用法说明:https://www.wireshark.org/docs/man-pages/tshark.html
另外,虽然tshark也支持抓包,但我建议使用dumpcap来完成,毕竟从名字上看,dumpcap就是为抓包而生的。
dumpcap
曾几何时,linux上的tcpdump在问题排障方面大出风头,特别是“循环抓包”,简直是拿“空间换时间”的典范,设定一个足够大的存储空间,甚至可以稳定的回溯近24小时内的所有网络流量。而Windows上,循环抓包一直是一个头疼的问题,系统自带的netsh可以实现循环抓包,但是命令晦涩难懂,同时也不支持rotate,直到我发现了dumpcap这个命令,windows上循环抓包终于有简便的实现方式了。
先上示例:
dumpcap -f tcp -i 2 -w C:\tcp.pcap -b filesize:102400 -b files:10
上面这个命令,指定了抓包的网卡index为2,只抓tcp协议的包,同时每个抓包文件大小上限为102400KB,最大存储10个抓包文件。这里唯一和tcpdump表现不同的是,tcpdump的循环,是固定10个文件,当文件个数占满后,覆盖最早的文件,重新开始写入文件;而dumpcap是删除最早的文件,继续创建新的序列号,比如tcp_00011_xxx.pcap来继续写入抓包数据。
其余常用的参数依然可以参考以下文档:https://www.wireshark.org/docs/man-pages/dumpcap.html
capinfos
在循环抓包结束后,通常会得到非常多的抓包文件,如果用户的业务量非常大,每个抓包文件覆盖的时间可能非常短(比如10秒),那么如何找到准确的一个时间点在哪个抓包里呢?这时候capinfos就派上用场了,它可以对一个包快速的做概览,给出包数量、抓包起止时间、包速率等等,而不需要大动干戈的使用Wireshark打开后再进行summary。
输出样例:
File name: randomicmp.pcap
File type: Wireshark/tcpdump/... - pcap
File encapsulation: Ethernet
File timestamp precision: microseconds (6)
Packet size limit: file hdr: 5000 bytes
Number of packets: 1,000
File size: 2,531 kB
Data size: 2,515 kB
Capture duration: 999.000000 seconds
First packet time: 1970-01-01 08:00:00.000000
Last packet time: 1970-01-01 08:16:39.000000
Data byte rate: 2,517 bytes/s
Data bit rate: 20 kbps
Average packet size: 2515.14 bytes
Average packet rate: 1 packets/s
SHA256: 6c41b814c403f6c9f81686788a0d210feadb14707a3e3b8ceeb8c49663b322d7
RIPEMD160: 9efc88ead1d1c083f96690e441e4090ebabf55e1
SHA1: 837f7da2d96dc11e971d39e78756b6b5096e8fd9
Strict time order: True
Number of interfaces in file: 1
Interface #0 info:
Encapsulation = Ethernet (1 - ether)
Capture length = 5000
Time precision = microseconds (6)
Time ticks per second = 1000000
Number of stat entries = 0
Number of packets = 1000
mergecap& editcap
这两个程序均是对抓包做修改的命令,但前者功能更加纯粹一些,就是单纯的将若干个包合并为1个包,后者原则上是对一个包做修改,但是也可以曲线的实现拆包,即将一个包按照包数量、包流逝的时间拆成若干个包。
mergecap这个工具使用比较简单,一般情况下也无需携带特别的参数,以下命令供参考:
mergecap -w all.pcap input1.pcap input2.pcap input3.pcap
editcap的参数就相对丰富一些:
- -s:可以将每个包的字节大小裁剪至指定的大小
- -c/-i:可以按照包数量/包消逝的时间来对一个很大的抓包进行切割,方便用Wireshark打开做图形化的分析
实战演练
上文说了这么多,这里举一个例子,来说明上述工具分别可以怎么用。
问题概述
用户反馈客户端访问自建nginx上的网站会偶发出现失败的情况,由于问题出现的频率非常低,难以通过传统的日志来进行问题分析。计划在客户端和nginx上部署循环抓包进一步分析。
解决步骤
- 在客户端和nginx服务器上部署循环抓包,客户端是windows,使用dumpcap部署,nginx服务器是linux,使用tcpdump部署(部署方案这里不赘述,可以参考网上的文献)
- 问题复现后,我们发现用户提供的抓包文件非常大,每个抓包200MB,一共有50个。由于用户的业务量非常大,每个抓包覆盖的时间点只有20秒。结合用户提供的日志,挑选了一个问题时间点,通过capinfo找到了问题的包。
- 打开单个抓包后我们发现用户的业务是典型的长连接,单个包里看不出完整的TCP会话过程,我们先通过mergecap将所有的包合并在一起,然后再使用tshark,传入显示过滤器(可以用源端口进行过滤,因为客户端的源端口比较唯一,筛选效果会比较好),将筛选结果输出到文件。这样就得到了比较干净的一个问题现场TCP流的抓包了。