Find Communities by: Category | Product

工程师手记

7 Posts authored by: iEMC APJ

作为中国网民,我们享有学习网络知识的天然优势,这是很多老外一辈子都不敢奢望的。还记得刚学会上网的时候,某知名搜索网站突然就连不上了,有位学长说这是域名被封,直接连IP就可以了,还帮我修改了hosts文件。于是我沿着这个方向研究,很快就理解了DNS协议。在实践中学到的本领,比捧着课本背诵的不知道高到哪里去。

又过了一阵,竟然连IP都连不上了。我在探索过程中,又学会了HTTP代理和VPN等科学上网技术。就这样,十几年下来身经百战,不知不觉中掌握了很多网络技术,每天都能到外网和同行们谈笑风生。现在回忆起来,我的知识真没多少是刻意去学的,而是在和网络问题斗争时被动学会的。被虐久了还得了斯德哥尔摩综合症,去年到国外出差了一个月,便觉得食不知味,因为根本找不到学习的机会。回到国内赶紧打开浏览器,Duang~~立即弹出运营商推送的广告。还是那个熟悉的味道,回家的温馨顿时涌上心头。

本文要讲述的也是一个颇有中国特色的网络技术,其实很多人都遇到过,但没有去深究。最早向我反馈的是一位细心的网友,他在打开www.17g.com这个游戏网站时,有一定概率会加载出其他网站的游戏,比如xunlei的。他觉得很好奇,便采取了一些措施来排查。

1一开始怀疑是电脑中毒,于是在同网络下的其他电脑上测试,症状还是一样。

2其他地区的网友(包括我)打开这个网站时没有发现相同问题。

3他怀疑是当地运营商(哪家运营商我就不说了)搞的鬼,于是换了个宽带,果然就没问题了。

这位网友很生气,想知道运营商究竟对他的网络做了什么手脚,所以抓了个出问题时的包来找我。我刚开始以为很简单,肯定是运营商的DNS劫持,即故意在收到DNS查询时回应一个假的IP地址,从而导致客户端加载错误的广告页面。于是我打开网络包,用dns作了过滤,发现www.17g.com被解析到了IP地址123.125.29.243(它还有一个别名叫w3.dpool.sina.com.cn,见图1)。可是经过进一步测试验证,发现这个IP地址竟然是对的,并没有被劫持。

图1.png

1

 

既然不是DNS劫持,那又是什么原因导致的呢?可惜这位网友抓包的时候电脑上开了太多应用,所以干扰包很多,无法采用暴力方式来分析(就是指不过滤,用肉眼把所有包都一一看过的分析方式)。如果用“ip.addr eq 123.125.29.243”过滤则得到图2的结果,似乎平淡无奇,只是显示有些包乱序了,但不知道这意味着什么。后来我才知道这就是线索之一,具体原因后面会讲到。

图2.png

2

 

由于这是我第一次分析劫持包,所以不得要领,当晚分析到凌晨都没有弄明白。第二天早上只好到技术群求援了,一位在运营商工作的朋友给我科普了HTTP劫持的几种方式。其中有一种引起了我的注意,其大概工作方式如图3所示,实线箭头表示正常的网络包,虚线箭头表示运营商做的手脚。

图3.png

3

 

注意:这只是简单的示意图,不完全等同于真实过程。

在正常情况下,用户发出的HTTP请求(即图中的①)经过层层路由才能到达真实的Web服务器,然后真实的HTTP响应(即图中的③)又经过层层路由才能回到用户端。而在做了手脚的网络中,运营商可以在路由器上复制HTTP请求,再交给假的Web服务器。然后赶在真实的HTTP响应之前,把假的HTTP响应(即图中的②)送达用户。这个抢先应答会导致用户在收到真实的HTTP响应时,以为是无效包而丢弃。

根据这个工作原理,我们能否推测出假的HTTP响应有什么特征呢?如果能,那就能据此过滤出关键包了。我首先考虑到的是网络层的特征:因为假Web服务器是抢先应答的,所以它发出的包到达用户时,TTLTime to Live可能和真实的包不一样。那要怎么知道真实的TTL应该是多少呢?考虑到3次握手发生在HTTP劫持之前,所以我们可以假定参与3次握手的那台服务器是真的,从图4可见其TTL54

图4.png

4

 

接下来就要动手过滤出假的包了。根据其源地址同样为123.125.29.243,但TTL不等于54的特征,我用“(ip.srceq 123.125.29.243) && !(ip.ttl == 54)”过滤,得到图5的两个包,即8号包和9号包。看看右下角显示了什么信息?这不正是我要寻找的假页面“src=http://jump.niu.xunlei.com:8080/6zma2a”吗?

图5.png

5

 

这个发现令我信心大增,有种拨云见日的感觉。再往下看几个包,果然发现了和jump.niu.xunlei.com的新连接。接着这个连接又把页面跳转到了“http://niu.xunlei.com/actives/welcome1426……”上(见图6)。跳来跳去地非常难以追寻。

图6.png

6

 

再后面的包就没必要分析了,以上证据已经足以向工信部投诉。据说投诉后运营商解决起问题来还挺爽快的,百度曾经上诉某运营商的劫持案件也获赔了。商场上的黑暗故事,就不在本书里展开讨论了,我们还是继续关注技术问题吧。

在这个案例中,万一真假网络包的TTL恰好一样,还有什么办法可以找出假的包吗?仔细想想还是有的。比如服务器每发送一个包,就会对其网络层的Identification作加1递增。由于4号包的Identification4078(见图7),那它的下一个包,也就是8号包的Identification就大概是4079了(或者略大一些)。可是从图8可见,它的Identification一下子跳到了55872,这也是一个被劫持的明显的特征。

图7.png

7

 

图8.png

8

 

那万一运营商技术高超,把TTLIdentification都给对上号了,我们还有什么特征可以找吗?还是有的!刚刚介绍的两个特征都在网络层,接下来我们可以到TCP层找找。在图5可以看到8号和9号这两个假冒的包都声明了“win=2102400”,表示服务器的接收窗口是2102400字节。对比一下其他网络包,你会发现这个数字大得出奇。为什么会这样呢?这是因为真正的Web服务器在和客户端建立3次握手时,约好了它所声明的接收窗口要乘以128(见图9)才是真正的窗口大小。假的那台服务器不知道这个约定,所以直接把真正的窗口值(win=16425)发出来,被这么一乘就变成了16425×128=2102400字节,大得夸张。

图9.png

9

 

这个特征在本案例中非常明显,但不是每个TCP连接被劫持后都会表现出来的。假如3次握手时没有声明图9所示的Window Scale值,那就无此特征了。

其实我在一开始还提到了另一个现象,即图2Wireshark提示的[TCP Previous segment not captured]和[TCP Out-of-Order],意味着存在乱序。为什么会有这些提示呢?这是因为假服务器伪造的包抢先到达,增加了Seq号,因此等到真服务器发出的包到达时,Seq号已经对不上了。Wireshark还没有智能到能判断真假包的程度,只能根据Seq号的大小提示乱序了。

总而言之,在理解了劫持原理之后,我们便能推理出假包的特征,然后再根据这些特征过滤出关键包。但不是所有特征都能在每次劫持中体现出来的,比如接收窗口的大小就很可能是正常的,所以一定要逐层认真分析。这还只是众多劫持方式中的一种,如果采用了其他方式,那么在包里看到的现象又会有所不同。等我下次遇到了,再写一篇跟大家分享。


作者信息:

林沛满

林沛满是一位有近十年存储经验的技术专家,擅长文件存储的性能分析、归档和备份。同时也专注于网络协议分析,比如CIFS/NFS/HTTP/TCP/UDP等,是《Wireshark网络分析就这么简单》、《Wireshark网络分析的艺术》等书的作者。

 

注:本《大咖讲网络》系列文章取自《Wireshark网络分析的艺术》一书。

不知道为什么,我的微博有时候会很卡,比如刷新时会一直Loading(见图1)。这不只是我的个人感受,很多网友都抱怨过。而装在同一个手机上的微信,连的也是同一个Wi-Fi,却没有这个症状。虽然这个问题出现的并不频繁,但假如我是微博的开发人员,肯定要把原因找出来。

1.png

1

当我的手机抓包环境搭好时,第一个想解决的问题就是这个。我随意发了一条微博,虽然没有碰到卡顿,但还是把包抓下来了。开头几个网络包如图2所示。

2.png

2

我又发了一条测试私信,可惜也没有卡顿。开头几个网络包如图3所示。

3.png

3

虽然两次都没有重现问题,但是从网络包可见,微博的工作方式严重依赖DNS。它在调用任何功能之前都要先向DNS服务器查询,得到提供该功能的服务器IP,然后再建立TCP连接。最神奇的是它不会缓存查询结果,所以需要频繁地重复查询DNS。我才抓了两分钟包,竟然就看到了上百个查询,这会不会就是微博卡顿的原因呢?我又抓了一个发微信的包作对比,如图4所示。

4.png

4

果然,微信客户端直接就和一个IP地址建立了连接。不管这个IP是写在配置文件中的,还是之前就存在手机的缓存里的,这至少说明了微信不像微博那样依赖DNS

为了进一步验证这个猜测,我故意把手机上的DNS服务器配成一个不存在的地址。不出所料,微信还是能照常工作,但微博就再也刷不出来了。之前我手机上配的DNS服务器位于美国,可能有时候跨国连接不稳定,所以导致了微博的卡顿现象。考虑到这一点,我尝试配了一个国内的DNS(见图5),果然从此再也没卡过了,刷起来异常流畅。

5.jpg

5

当你看到这篇文章的时候,也许这个问题已经被新浪解决了,因为我已经向微博的技术人员反馈过(或者他们早已经知道)。相信解决起来也不复杂,只要像微信一样缓存IP就可以了。据我所知,苹果的App Store和小米电视也遭遇过DNS导致的性能问题,所以相信还有很多设备或者程序可以利用Wireshark来优化,只要把使用过程的包都抓下来,说不定就能发现值得改进的地方。

最后再补充一个小发现。我发的微博内容是“capture test, will delete it soon.”,分享范围设成“仅自己可见”。没想到在Wireshark上直接就看到了明文(见图6底部),发私信就没有这个问题。因此我们连公共WiFi发微博的时候,还是要小心一点。不要以为设成“分组可见”或者“仅自己可见”就够私密了,其实在Wireshark上都能看到。

6.png

6

 

作者信息:

林沛满

林沛满是一位有近十年存储经验的技术专家,擅长文件存储的性能分析、归档和备份。同时也专注于网络协议分析,比如CIFS/NFS/HTTP/TCP/UDP等,是《Wireshark网络分析就这么简单》、《Wireshark网络分析的艺术》等书的作者。

 

注:本《大咖讲网络》系列文章取自《Wireshark网络分析的艺术》一书。

MTU带来的问题实在太多了,但凡做过运维、实施或者技术支持的工程师,或多或少都会遇到。一个典型的MTU问题发生在类似图1的环境中,即两个子网的MTU大小不一样。

pic1.png

1

当客户端发给服务器的巨帧经过路由器时,或者被丢包,或者被分片。这取决于该巨帧是否在网络层携带了DFDon’t fragment标志。如果带了就被丢弃,如果没带就被分片。Wireshark上很容易看到DF标志,如图2中的方框内所示。分片的情况往往被忽略,因为它只影响一点点性能,大多数时候甚至察觉不出。丢包的情况就无法忽略了,因为丢包之后再重传多少遍都没用,会一直丢,整个传输就像掉进了黑洞,所以往往会导致严重的后果。

pic2.png

2

我有个实验环境恰好就是图1这样的,可以来做个实验加深理解。我从客户端给服务器发送了两个ping请求,第一个携带1472字节,第二个携带1473字节,并都用了“-f”参数设置了DF标志。命令及结果请看图3,第一个ping成功,第二个则失败了。

pic3.png

3

由于ICMP头为8字节,IP头为20字节,所以第一个ping请求在网络层的长度为1472+8+20=1500字节,第二个ping请求则为1473+8+20=1501字节。我的路由器MTU1500字节,不难理解第一个ping请求的长度没有超过MTU,所以可以传输成功;而第二个ping请求的长度超过了路由器出口的MTU,又不允许被切分,所以不能传输成功。在图3底部可以看到路由器提示了“Packet needs to be fragmented but DF set”。

这个过程的网络包可以从图4中看到,请注意最后一个包是路由器回复的“Fragmentation needed”,而不是服务器回复的。假如ping的时候没有用“-f”设置DF标志,那么1473字节也是能ping成功的,只是在路上会被切分成两个包。

pic4.png

4

理论说起来很简单,实验做出来也不难,但在生产环境中的症状就没这么明显了,要发现MTU问题往往需要一些想象力。我收藏了不少MTU相关的案例,在本文挑出三个最有代表性的来分享。

案例1 用户浏览某些共享目录时客户端会死机,浏览其他目录则不会。

碰到这种症状,恐怕没有人会想到是MTU导致的,所以经过长时间徒劳无功的排错之后,工程师不得不抓了个包。这个包是在服务器上抓的(因为客户端死机,根本没法抓),如图5所示,服务器回复的包“Seq=193Len=1460”在持续重传,但客户端一直没有确认,似乎是发生丢包了。从图5底部还可以看到这个包携带的信息是该目录的子文件(夹)列表。

pic5.png

5

 

导致丢包的可能性有很多,我为什么认定是MTU导致的呢?推理过程如下。

1.如果端口被防火墙阻止了也可能丢包,但是会从三次握手时就开始丢,而不是等到浏览目录的时候。

2.如果网络拥塞也可能丢包,但一段时间后能恢复,而不是这样持续地丢。

3.丢的那个包携带了1460字节(相当于占满了整个1500字节的MTU),算是比较大的。而没被丢弃的2号包和4号包都携带了很少的字节数,只丢大包的症状说明很可能就是MTU导致的。

4.我用“ping <server_ip> -l 1472 -f”测试,果然失败了。逐渐减小每次ping的长度,到了1400字节左右才成功,这说明网络上有个设备的MTU比较小。

5.于是把服务器上网卡的MTU相应改小,问题果然就消失了。

6.之所以浏览其他目录没有死机,可能是因为这些目录中的子文件(夹)比较少,凑不满一个大包。

我曾经访问公司内网时出现问题,在抓包里也看到类似于图5的症状,后来也是通过修改MTU解决的。

案例2 客户端的MTU1500字节,服务器端的MTU9000字节,平时连接正常。运维人员听说两端的MTU最好一致,所以把客户端的MTU提高到9000字节,没想到连接反而出问题了。

虽然该案例听上去不太科学,但如果网络路径上有个设备的MTU1500字节,这个问题就真会发生。原先客户端和服务器在三次握手时,双方会“协商”使用一个1460字节(MTU-TCP-IP头)的MSS,所以可以顺利通过那个MTU1500的网络设备。如果两端都是9000字节了,那三次握手时就会得到8960字节的MSS,因此通不过那个网络设备。

案例3 无法完成Kerberos身份认证,在客户端抓到的包如图6所示。

由图6可见客户端在持续地向KDC发送TGS-REQ,但是收不到任何回复。本来碰到这种情况最好在KDC上也抓个包看看的,但是KDC一般不让人登上去。怎么办呢?

pic6.png

6

从这几个网络包里是挖掘不出更多线索了,我们只能推测哪些因素会导致TGS-REQ得不到回复。有一个可能是端口被防火墙封掉,但那样的话之前的其他Kerberos包(比如AS-REQ)也得不到回复,不可能走到TGS-REQ这一步,因此防火墙可以排除。还有一个可能就是MTU导致的丢包了,假如网络路径上有个交换机的MTU偏小,大包无法通过就可能出现此症状。仅从Wireshark中我们无法判断是客户端发给KDC时丢包了,还是KDC回复客户端时丢包了,只能先试着把客户端的MTU改小一点,问题果然就消失了。其实利用“ping -f -l <字节数>”试探出路径上的最小MTU也可以,前提是网络中没有禁用 ICMP

最近有不少同事开始学习Wireshark,他们遇到的第一个困难就是理解不了主界面上的提示信息,于是跑来问我。问的人多了,我也总结成一篇文章,希望对大家有所帮助。Wireshark的提示可是其最有价值之处,对于初学者来说,如果能理解这些提示所隐含的意义,学起来定能事半功倍。

1[Packet size limited during capture]

当你看到这个提示,说明被标记的那个包没有抓全。以图14号包为例,它全长有171字节,但只有前96个字节被抓到了,因此Wireshark给了此提示。

图1.png

1

 

这种情况一般是由抓包方式引起的。在有些操作系统中,tcpdump默认只抓每个帧的前96个字节,我们可以用“-s”参数来指定想要抓到的字节数,比如下面这条命令可以抓到1000字节。

[root@my_server /]# tcpdump -i eth0 -s 1000 -w /tmp/tcpdump.cap

 

 

2TCP Previous segment not captured

TCP传输过程中,同一台主机发出的数据段应该是连续的,即后一个包的Seq号等于前一个包的Seq+Len(三次握手和四次挥手是例外)。如果Wireshark发现后一个包的Seq号大于前一个包的Seq+Len,就知道中间缺失了一段数据。假如缺失的那段数据在整个网络包中都找不到(即排除了乱序),就会提示[TCP Previous segment not captured]。比如在图2这个例子中,6号包的Seq1449大于5号包的Seq+Len=1+0=1,说明中间有个携带1448字节的包没被抓到,它就是“Seq=1, Len=1448”。

图2.png

2

 

网络包没被抓到还分两种情况:一种是真的丢了;另一种是实际上没有丢,但被抓包工具漏掉了。在Wireshark中如何区分这两种情况呢?只要看对方回复的确认(Ack)就行了。如果该确认包含了没抓到的那个包,那就是抓包工具漏掉而已,否则就是真的丢了。

顺便分析一下图2这个网络包,它是HTTPS传输异常时在客户端抓的。因为“Len: 667”的小包(即6号包)可以送达,但“Len: 1448”的大包却丢了,说明路径上可能有个网络设备的MTU比较小,会丢弃大包。后来的解决方式证实了这个猜测,只要把整个网络路径的MTU保持一致,问题就消失了。


3[TCP ACKed unseen segment]

Wireshark发现被Ack的那个包没被抓到,就会提示 [TCP ACKed unseen segment]。这可能是最常见的Wireshark提示了,幸好它几乎是永远可以忽略的。以图3为例,32号包的Seq+Len=6889+1448=8337,说明服务器发出的下一个包应该是Seq=8337。而我们看到的却是35号包的Seq=11233,这意味着833711232这段数据没有被抓到。这段数据本应该出现在34号之前,所以Wireshark提示了[TCP ACKed unseen segment]

图3.png

3

 

不难想象,在一个网络包的开头会经常看到这个提示,因为只抓到了后面的Ack但没抓到前面的数据包。

4[TCP Out-of-Order]

TCP传输过程中(不包括三次握手和四次挥手),同一台主机发出的数据包应该是连续的,即后一个包的Seq号等于前一个包的Seq+Len。也可以说,后一个包的Seq会大于或等于前一个包的SeqWireshark发现后一个包的Seq号小于前一个包的Seq+Len,就会认为是乱序了,因此提示 [TCP Out-of-Order] 。如图4所示,3362号包的Seq=2685642小于3360号包的Seq=2712622,所以就是乱序。

图4.png

4

 

小跨度的乱序影响不大,比如原本顺序为12345号包被打乱成21345就没事。但跨度大的乱序却可能触发快速重传,比如打乱成23451时,就会触发足够多的Dup ACK,从而导致1号包的重传。

5[TCP Dup ACK]

当乱序或者丢包发生时,接收方会收到一些Seq号比期望值大的包。它每收到一个这种包就会Ack一次期望的Seq值,以此方式来提醒发送方,于是就产生了一些重复的AckWireshark会在这种重复的Ack上标记[TCP Dup ACK]

以图5为例,服务器收到的7号包为“Seq=29303, Len=1460”,所以它期望下一个包应该是Seq+Len=29303+1460=30763,没想到实际收到的却是8号包Seq=32223,说明Seq=30763那个包可能丢失了。因此服务器立即在9号包发了Ack=30763,表示“我要的是Seq=30763”。由于接下来服务器收到的10号、12号、14号也都是大于Seq=30763的,因此它每收到一个就回复一次Ack=30763,从图中可见Wireshark在这些回复上都标记了[TCP Dup ACK]

图5.png

5

 

6[TCP Fast Retransmission]

当发送方收到3个或以上[TCP Dup ACK],就意识到之前发的包可能丢了,于是快速重传它(这是RFC的规定)。以图6为例,客户端收到了4Ack=991851,于是在1177号包重传了Seq=991851

图6.png

6

 

7[TCP Retransmission]

如果一个包真的丢了,又没有后续包可以在接收方触发[Dup Ack],就不会快速重传。这种情况下发送方只好等到超时了再重传,此类重传包就会被Wireshark标上[TCP Retransmission]。以图7为例,客户端发了原始包(包号1053)之后,一直等不到相应的Ack,于是只能在100多毫秒之后重传了(包号1225)。

图7.png

7

 

超时重传是一个非常有技术含量的知识点,比如等待时间的长短就大有学问,本文就不细说了,毕竟需要懂这个的人很少。

8[TCP zerowindow]

TCP包中的“win=”代表接收窗口的大小,即表示这个包的发送方当前还有多少缓存区可以接收数据。当Wireshark在一个包中发现“win=0”时,就会给它打上“TCP zerowindow”的标志,表示缓存区已满,不能再接受数据了。比如图8就是服务器的缓存区已满,所以通知客户端不要再发数据了。我们甚至可以在32583263这几个包中看出它的窗口逐渐减少的过程,即从win=15872减小到win=1472

图8.png

8

 

9[TCP window Full]

Wireshark在一个包中打上[TCP window Full]标志时,就表示这个包的发送方已经把对方所声明的接收窗口耗尽了。以图9为例,Britain一直声明它的接收窗口只有65535,意味着Middle East最多能给它发送65535字节的数据而无需确认,即“在途字节数”最多为65535字节。当Wireshark在包中计算出Middle East已经有65535字节未被确认时,就会发出此提示。至于Wireshark是怎么计算的,请参考本书的《计算“在途字节数”》一文。

图9.png

9

 

[TCP window Full]很容易跟[TCP zerowindow]混淆,实际上它们也有相似之处。前者表示这个包的发送方暂时没办法再发送数据了,后者表示这个包的发送方暂时没办法再接收数据了,也就是说两者都意味着传输暂停,都必须引起重视。

10[TCP segment of a reassembled PDU]

  • 当你收到这个提示,肯定已经在EditàPreferencesààTCP菜单里启用了Allow sub dissector to reassemble TCP streams。它表示Wireshark可以把属于同一个应用层PDU(比如SMBRead ResponseWrite Request之类)的TCP包虚拟地集中起来。如图10所示,这一个SMB Read Response3948号包共同完成,因此Wireshark在最后一个包中虚拟地把所有包集中起来。这样做有个好处,就是可以右键点击图10底部的方框,选择CopyàBytesàPrintable Text Only,从而复制整个应用层的PDU。做研发的同学可能比较需要这个功能。

图10.png
10

 

11Continuation to #

  • 你看到这个提示,说明已经在EditàPreferencesàProtocolsàTCP菜单里关闭了Allow sub dissector to reassemble TCP streams比如图10的那些包,一关闭就变成图11这样。

图11.png

11

 

仔细对比图10和图11,你会发现Read Response在图10中被算在了48号包头上,而在图11中被算到了39号包头上。这样会带来一个诡异的结果:图10的读响应时间为2.528毫秒(38号包和48号包的时间差),而图11的读响应时间为2.476毫秒(38号包和39号包的时间差)。究竟哪个算正确呢?这个问题很难回答,如果在乎的是实际的总性能,那就看前者;如果想忽略TCP/IP协议的损耗,单看服务器的响应速度,那就看后者。在某些特殊情况下,这两者相差非常大,所以必须搞清楚。

12.[Time-to-live exceeded (Fragment reassembly time exceeded)

ICMP的报错有好多种,大都不难理解,所以我们只举其中的一种为例。 [Fragment reassembly time exceeded]表示这个包的发送方之前收到了一些分片,但是由于某些原因迟迟无法组装起来。比如在图12中,由于上海发往北京的一些包被分片传输,且有一部分在路上丢失了,所以北京方无法组装起来,便只好用这个ICMP报错告知上海方。

图12.png

12

 

作者信息:

林沛满

林沛满是一位有近十年存储经验的技术专家,擅长文件存储的性能分析、归档和备份。同时也专注于网络协议分析,比如CIFS/NFS/HTTP/TCP/UDP等,是《Wireshark网络分析就这么简单》、《Wireshark网络分析的艺术》等书的作者。

 

注:本《大咖讲网络》系列文章取自《Wireshark网络分析的艺术》一书。

这也许称得上最经典的网络问题,很多大师从理论上分析过的,我能在现实中遇到也算三生有幸。

 

事情是这样的。有家公司来咨询一个性能问题,说是从AIX备份数据到Windows极其缓慢,只有1 MB/s,备份所用的协议是SFTP。我的第一反应就是抓个包,然后试试Wireshark的性能三板斧。

 

1.从Statistics->Summary菜单可见,平均速度是11 Mbit/s,的确只比1 MB/s高一些,见图1

图1.png

1

 

 

2.从Analyze->Expert Infos 菜单看,网络状况堪称完美。请看图2,连一个WarningsNotes都没有。这样的网络性能怎么会差?

图2.png

2

 

 

      3.选定一个从AIX发往Windows的包,然后点击Statistics->TCP StreamGraph ->TCP Sequence GraphStevens)菜单,从图3可见,这60秒中数据传输得很均匀,没有TCP Zero Window或者死机导致的暂停。

 

图3.png

 

3

 

 

试完三板斧,我们只能得到一个结论:备份的确进行得很慢,但是仅凭Wireshark自带的分析工具找不出根本原因,这也许意味着问题不在网络上,而是在接收方或者发送方上。幸好Wireshark不但能发现网络上的问题,也能反映出接收方和发送方的一些配置,只是需要一些技巧来发现。

 

因为数据是从AIX备份到Windows的,所以如果把SFTPSSH File Transfer Protocol)包过滤出来,理论上应该看到大多数时间花在了从AIXWindows的传输上。可是由图4发现,从AIXWindows的包虽然占多数,但没花多少时间。反而从WindowsAIX的两个包(533535)之间竟然隔了0.2秒。该现象在整个传输过程中出现得很频繁,说不定性能差的原因就在此处了。只要把根本原因找出来,就有希望解决问题。

图4.png

4

 

 

那么这0.2秒之间究竟发生了什么呢?我把过滤条件去掉后得到了图5所示的包。可见Windows发出533号包之后就停下来等,直到0.2秒之后AIXAck534号包)到达了才发出535号。Windows停下来的原因是什么呢?它在这两个包里总共才发了700多字节(96+656)的数据,肯定不会是因为TCP窗口受约束所致。

 

图5.png

 

5

 

 

如果你研究过TCP协议,可能已经想到了愚笨窗口综合症Silly window syndrome纳格Nagle算法。在某些情况下,应用层传递给TCP层的数据量很小,比如在SSH客户端以一般速度打字时,几乎是逐个字节传递到TCP层的。传输这么少的数据量却要耗费20字节IP+20字节TCP头,是非常浪费的,这种情况称为发送方的愚笨窗口综合症,也叫“小包问题”(small packet problem)。为了提高传输效率,纳格提出了一个算法,用程序员喜闻乐见的方式表达就是这样的:

 

if有新数据要发送

 

  if数据量超过MSS(即一个TCP包所能携带的最大数据量)

 

    立即发送

 

  else

 

    if前发出去的数据尚未确认

 

      把新数据缓存起来,凑够MSS或等确认到达再发送

 

    else

 

      立即发送

 

    end if

 

  end if

 

end if

 

 

 

5所示的状况恰好就是小包问题。Windows发了533号包之后,本应该立即发送535的,但是由于535携带的数据量小于MSS,是个小包,根据Nagle算法只好等到533被确认(即收到534)才能接着发。这一等就是0.2秒,所以性能受到了大幅度影响。那为什么AIX要等那么久才确认呢?因为它启用延迟确认了,具体可参考本书的《一篇关于VMware的文章》。

 

Nagle和延迟确认本身都没有问题,但一起用就会影响性能。解决方法很简单,要么在Windows上关闭Nagle算法,要么在AIX上关闭延迟确认。这位客户最终选择了前者,性能立即提升了20倍。

 

  • 如果你足够细心,也许已经意识到图3有问题—既然传输过程中会频繁地停顿0.2秒,为什么图3显示数据传输很均匀呢?这是因为抓包时间太长了,有60秒,所以0.2秒的停顿在图上看不出来。假如只截取其中的一秒钟来分析,再点击Statistics ->TCP StreamGraph->TCP Sequence GraphStevens)菜单,你就能看到图6的结果,0.2秒的停顿就很明显了。

 

图6.png

 

6

 

 

按理说,世界上到处都有启用了Nagle和延迟确认的设备在通信,为什么很少有人说起呢?我猜测大多数受害者并没有发现这个原因,以为是带宽不足所致,所以就一直忍着。我要不是用了Wireshark,也是发现不了的。根据我后来的搜索,只有斯坦福大学的博士Stuart系统地阐述过一个现实中的问题,文章在他的个人博客上http://www.stuartcheshire.org/papers/nagledelayedack/。我读完这篇文章的感觉就像遇到了知音,很想把这哥们约出来喝一杯:

 

“这么隐蔽的问题你是怎么发现的?太厉害了!”

 

“和满兄一样看包啦,分分钟的事!”

 

“论天下英雄,唯司徒君与满耳。”

 

……

 

本系列文章取自林沛满的《Wireshark网络分析的艺术》一书。

有位读者在豆瓣上评论我的上一本书,说有阅读侦探小说的感觉。我对此并不觉得惊讶,因为Wireshark排查问题,和侦探破案的思路是一致的。神探福尔摩斯的破案秘诀是“溯因推理”先观察所有细节,比如鞋根上的泥疙瘩甚至烟灰;然后作出多种推理和假设;接着刨去各种不可能,最后剩下的“无论多么难以置信,肯定没错。”用Wireshark分析网络包时也类似,我们先要在网络包中寻找各种线索,然后根据网络协议作出推理,接着刨去人为(有意或无意)掩盖的证据,才能得到最后的真相。尤其是和保密机构打交道的时候,工程师进不了机房,文档也不能公开,所以一切线索只能自己在包里找,感觉就更像破案了。

我最近帮一位读者解决的问题就非常典型。他供职的机构内部网站有时候会发生诡异的现象,比如Web服务器的端口号会随机发生变化(具体症状就不多讲了,和本文关系不大)。后来做了排查,把客户端和Web服务器直连,问题就消失了,确认了Web服务器和客户端都没有问题。难道根本原因就出在网络路径上了?可是管理员又声称网络拓扑非常简单,不会出问题的。见图1客户端和Web服务器在不同的子网里,中间由一个路由器转发

111.png

1

凭我的经验,这个网络拓扑的确简单到没有出问题的可能。可是已经到了山穷水尽的地步了,只好抓包试试。Web服务器不允许我们登录,所以只能在客户端抓,更糟糕的是抓包时那个诡异的现象并没有发生。你一定会纳闷,正常状况抓的包有什么看头啊?人在走投无路的时候,要求都是很低的,能抓到一点算一点。图2就是抓到的包,看起来一切都很正常:前3个包是三次握手,接着客户端发了个HTTP GET请求,服务器也确认收到了。

222.png

2

既然表面上都是好的,我们再看看每个包的详细信息。1号包的详情见图3,客户端把包交给了一个叫c0:62:6b:e2:bd:88MAC地址,该地址属于默认网关。将包交给默认网关是合理的,因为Web服务器在另一个子网中,需要路由转发。也就是说,从1号包中没有发现任何异常。

333.png

3

再看看图42号包详情。这个包让人眼前一亮,信息量实在太大了。在阅读下面的文字之前,建议你自己先在图中找找亮点。

444.png

4

首先这个包竟然是从MAC地址00:10:f3:27:61:86发过来的,而不是之前提到的默认网关c0:62:6b:e2:bd:88。我不知道这个MAC地址属于什么设备,但这至少说明2号包和1号包走了条不一样的路径。再看其Time to liveTTL)居然是64,理论上经过一次路由的包,TTL应该减去1,变成63才对。根据这两条信息,可以推测管理员提供的拓扑图有误。真正的网络包流向应该接近图5即客户端发出去的包是经过路由的,而Web服务器发过来的包没经过路由

555.png

5

其实到这里就可以去找管理员说理了,不过别急,继续往下看。到了图6的第5号包,发现Identification竟然是49031,而同样是来自Web服务器的2号包(见图4)中,Identification却是0。一般发出Identification0的机器永远都发0,不会一下子跳到49031。也就是说,其实2号包和5号包是两台不同的设备发出来的,这意味着在Web服务器和客户端之间,可能存在一台设备在代理三次握手,而能够代理握手的设备很可能是应对Syn flood攻击的防火墙。

666.png

6

因此图5的拓扑图还不够准确,应该更正成图7的样子。管理员忽视了这台防火墙,可能就错过了发现问题根源的机会。

 

777.png

7

把以上分析反馈给管理员之后,他果然通过MAC地址00:10:f3:27:61:86找到了一台防火墙。也正是防火墙上的一些错误配置,导致他们遇到了那些诡异症状,改正之后症状就消失了。本文的目的是演示如何在网络包中寻找被掩盖的线索,而不是防火墙知识,所以就不展开了。

从头到尾再复习一下整个过程,是不是很有当侦探的感觉?

 

注意:为了保护客户隐私,本文截图里的IP地址和MAC地址都被PS过,这就是为什么有些截图看上去不太自然。

大咖前言

 

在过去几年中,有不少读者、同事和网友向我咨询过网络问题,其中大部分都记录在案。我一直把这些案例视为珍贵的财富,因为既真实又有广泛的代表性,比我自己在实验室中“制造”出来的好多了。本书人中选择了最经典的部分,希望读者会感兴趣。如果你在工作或生活中遇到网络问题,也欢迎抓个包来找我分析。



Linux为什么卡住了?

 

到今天为止,已经有5位读者向我求助过这个问题了。症状请看图1,他们通过SSH登录Linux服务器时,输完用户名就卡住了,要等待10秒钟才提示密码输入。这究竟是什么原因导致的呢?其实我也是Linux菜鸟,虽然尝试过搜索“ssh hang”等关键词,但是没找到相关信息。

111.png

1

10秒钟的时间并不算长,吃个薯片喝口咖啡就过去了。但是作为强迫症患者,我还是容不得它的存在,因此便决定写篇文章,向大家演示一下怎样用Wireshark一步步解决这个问题。

首先是抓包,步骤如下。

1.在Linux服务器上启动抓包。

2.从笔记本SSHLinux服务器,输入用户名并回车。

3.等待10秒左右,直到登录界面提示输入密码。

4.停止抓包。

这样就可以得到一个涵盖该现象的网络包了。一般在实验室中没有干扰流量,不用过滤也可以分析,不过我们最好在做实验时就养成过滤的习惯,以适应生产环境中抓到的包。因为我们是通过SSH协议登录的,所以可以直接用“ssh”来过滤,如图2所示。SSH包都是加密了的,因此我们看不出每个包代表了什么意思,不过这并不影响分析。从图2中可以看到,21号包和25号包之间恰好就相隔10秒。


222.png

2

这两个包之间所发生的事件,可能就是导致这个现象的原因。于是我再用“frame.number> 21 && frame.number< 25”过滤,结果如图3所示。

333.png

3

从图3中可以看到,Linux服务器当时正忙着向DNS服务器查询10.32.200.23PTR记录(即反向解析),试图获得这个IP地址所对应的域名。该IP属于我们测试所用的笔记本,但由于DNS服务器上没有它的PTR记录,所以两次查询都等了5秒钟还没结果,总共浪费了10秒钟。

我们由此可以推出,这台Linux服务器在收到SSH访问请求时,会先查询该客户端IP所对应的PTR记录。假如经过5秒钟还没有收到回复,就再发一次查询。如果第二次查询还是等了5秒还没回复,就彻底放弃查询。我们甚至可以进一步猜测,如果DNS查询能成功,就不用白等那10秒钟了。

为了验证这个猜测,我在DNS服务器中添加了10.32.200.23PTR记录,如图4所示,然后再次登录。

444.png

4

这一次果然立即登录进去了。从图5Wireshark截屏可见,DNS查询是成功的,所以21号包和26号包之间几乎是没有时间停顿的。

5

明白了DNS查询就是问题的起因,接下来就知道怎么进一步研究了。只要在Google搜索“ssh dns”,第一页出来的链接都是关于这个问题的。随便挑几篇阅读一下,就连我这样的Linux初学者都能把这个问题研究透了。原来这个行为是定义在“/etc/ssh/sshd_config”文件中的,默认配置是这样的:

[root@Linux_Server ~]# cat /etc/ssh/sshd_config |grep -i usedns

#UseDNS yes

改成下面这样就可以解决了,不用去动DNS服务器上的配置:

[root@Linux_Server~]# cat /etc/ssh/sshd_config |grep -i usedns

UseDNS no

我经常说技能比知识更重要,这就是例子之一。学会了使用Wireshark,其他知识也会跟着来的。


作者简介:

 

林沛满


    林沛满是一位有近十年存储经验的技术专家,擅长文件存储的性能分析、归档和备份。同时也专注于网络协议分析,比如CIFS/NFS/HTTP/TCP/UDP等等,是《Wireshark网络分析就这么简单》的作者。

Filter Blog

By date:
By tag: