客户端抓包:client发送client hello,服务器确认后,没有发送server hello,而是等待超时后,发送FIN包,断开连接。
从客户端抓包来看,客户端怀疑服务端未发送server hello,那就再服务端抓包查看 serverhello 和client hello的数量是否一致。
ssl.handshake.type==2 server hello 的过滤
ssl.handshake.type==1 client hello 的过滤
考虑一个问题,如果服务端根本没有收到客户端发来的client hello,那么也就没有对应的 server hello,其数量还是一致的。因此,也有可能是中间网络的原因导致服务端未收到client hello,继而服务端等待超时,断开连接。
综上,可能有两个原因导致客户端与服务端抓包看到的现象:
为了验证以上两个猜想,只能在服务端抓包看。在茫茫流量包中找到对应的无异于大海捞针,还好,有wireshark强大的过滤功能。
开始利用 ssl.handshake.type == 1
与 ssl.handshake.type ==2
来可以验证服务端在第一种假设情况下,是否回复了server hello。很可惜的是,一切正常,client hello 与server hello 的数量一致,那么范围就缩小了。因此在假设1 的情况下,可以确保我们的服务端应用没问题,肯定是发送了server hello 的。那么接下来就是寻找对应的数据包了,看到了客户端的序列号seq=1tcp.seq == 1
,以及client hello中的client random作为过滤条件【后来证明使用client random 过滤是有问题的】,结合客户端出错时间,找出对应的数据流应该不是问题。
遗憾的是,没有找到对应时间点,并且服务端接受client hello,回复server hello 最后等待10秒这样行为的数据包。【心疼自己一秒钟】。
其实还有个思路可以直接判断服务端没有发送server hello,从客户端的抓包来看,服务器发送FIN的序列号为1 ,server hello怎么也得几K的字节量,所以根据这个信息,可以直接判断服务端没有发送server hello!【表扬一下自己的聪明才智!】
那么接下来就只能看第二种假设了:服务端没有收到client hello。那么根据分析,TCP的三次握手应该是有的。那么服务端的行为就应该是TCP三次建连完成,然后空等十秒钟,最后发送fin,断开连接。
根据这个思路,那么继续过滤服务端抓包。tcp.flags.fin == 1
,并且是服务端主动发送的FIN包。
根据这个条件,终于看到了想看到的结果。【欢呼】
至此,可以得出结论,服务端没有收到客户端发来的client hello。
一开始分析包的的时候,没有注意代理这个问题,其实很容易就看出来。
关于TTL的文章:IP协议的TTL
客户端是使用了代理来跟服务端进行https通信的。代理可能是TCP透传或者HTTPS层的代理。
客户端抓包,显示“服务端”(代理)回复了 ACK client hello,意味着服务器已收到client hello,下一步需要回复server hello,但是真实的服务端抓包并没有收到client hello,此client hello ack 可以明确是中间某代理设备所回复。此时客户端在等待服务端回复server hello,服务端在等待客户端发client hello,客户端服务器双方都进入等待环节直到最后超时,服务端发fin包。
更新:
又看了一下客户端正常的数据包,发现对于 client hello 的ACK每次都会重传一次。
从此可以更加肯定的说是中间代理的问题了,也能看出中间代理的工作方式。应该是中间代理在转发client hello 的时候,没有转发成功收到服务端的ACK,就自己私自回复了客户端ACK,导致两端都在等。
刚开始分析包的时候,就注意到了,每个正常的包都会重传一次clienthello 的ACK,并且都是ACK 同一个 client hello消息。当时没有太在意,放过了,哎,做学问还是要严谨、锱铢必较啊。
事情并不是像推理的那么简单—之中间代理:
1- 既然上面提到是中间代理在转发过程中存在问题,那么就要证明中间代理确实出了问题。
2- 至于中间代理在收到服务端后发送fin还是先发送fin这个问题,是属于代理的逻辑问题。
3- 如果中间代理转发了client hello,但是服务端没有收到,那问题可能就不是中间代理的问题,而是中间某一环节出了问题。
抓包证明中间代理【与客户详细沟通,这个中间设备是他们的一个内部设备。】的确发送了 client hello 的数据包,而且重传了三次!
重传了3次我们服务器都没有收到!这就麻烦了,这个锅瞬间又被客户甩给了我们。
怎么办?运营商给弄丢了?我们自己会不会在哪一个环节出现问题?
本着遇到问题,先找自己的原因的法则,假设运营商那里不出问题的情况下,我与网络组的同事了解了公司的网络架构,大致就是从运营商来了数据之后,首先经过我们的核心交换机,然后,向后转发给后端的两个LVS,LVS再转给real server。
那么数据流就是经过交换机–LVS—RS,先前已经证明RS没有收到client hello。那么有可能是交换机收到了但没有转发给LVS,或者LVS收到了但没有转发给RS。那就要交换机抓包了,根据网络组同事普及知识,交换机抓包需要镜像抓包到一台设备上才可以,具体操作,网络组同事完成的。同时在两个LVS设备上抓包。
坐等复现了,然后过滤包,找到出错时间点对应的包,发现了问题:交换机上已经向LVS转发了数据包,但是LVS没有收到数据包!
这下,好了,问题出在我们这里了!确定么?NO!最后再说WHY!
难道是线路出现了问题?呵呵,这个最不可能出现问题的地方,当然直接排除了。不可能每次都是同样的错误在同样的握手阶段。
那就只能怀疑到LVS负载上了?嗯,有可能,负载有问题,那就使用单LVS,这里我们使用的双主LVS,并不是主备LVS。使用单LVS后,果然不再出现错误现象。
那么,问题解决了,但是为什么还不知道,总不能为了这个问题修改网络架构,这个代价有点儿大。况且真正的原因还不知道,单LVS只是避免了错误发生,作为研发人员的职责,并不是避免错误发生,而是杜绝错误发生。那就要继续刨根问底了。
继续分析交换机转发与LVS收到的数据包。
对比正常的数据流与异常数据流,公司一位骨灰级大神级人物出场了:观其面相,鬓发已白,年已过半百,深邃的眼神中永远带着慈善的目光 … …
我介绍了事情的来龙去脉,已经截至目前的所有进展以及发现后,大神直接看抓包!
“唉,我发现一个问题,怎么两次数据包过来的MAC地址不一样啊!”
只见大神眉头一紧,在小黑板上画了一下内部的网络架构,说
“为什么同一个数据流上会被交换机在不同的端口发送出去?”
“找交换机厂商问问!”
那么,问题的直接原因定位了,就是同一个数据流上的数据包,被交换机在不同的端口转发给不同的LVS。
交换机是华为的,第二天,华为那边来了人,简单介绍了一下情况,共同讨论了一下,得出的结论是:运营商导致!
因为一个数据流从源到目的,中间要经过运营商不同的设备,运营商那里把同一条流从不同的设备转发给了我们交换机,那么其中的源mac地址就会发生改变,再经过交换机的负载均衡,如果后面有多个LVS,目的MAC也会发生变化。所以这个问题出在 运营商那边的负载规则,因为无法让运营商做调整,所以我们做本地负载优化。
就是让一个数据流在交换机负载时,不拆分到不同的端口进行转发,也就类似于基于流的负载。而一般标准业务都是按照5元组hash来做选路,如果源目mac地址发生变化,那么选路就会发生变化,但是如果在交换机上做基于源IP和源端口的负载,那么同一条流就不会被拆分。!
华为交换机厂家结论:
此节点多个万兆设备接运营商,当时网络规划为防止单板故障引起整个节点网络异常,负载到6个业务板接入运营商,目前这6块业务板卡类型不同,板卡采用的转发芯片不同,hash算法受芯片不同的影响导致此故障现象。
调整后结果: 调整板卡类型相同后,现象消失。
既然是板卡导致的问题,那么为什么会handshake出现错误,板卡有问题,那么普通的http或者https在成功完成握手后,也会出现这种现象,根据这个推论,去查普通的http以及https在数据传输过程中有没有发送这种错误。
果不其然,http数据传输以及https在数据传输过程中也有reset的情况!
那么,板卡调整后,这些现象也会消失的,于是,再次验证,也消失了,这样,就验证了自己的反思问题。
最终,现象只是ssl handshake failure,其实问题并不是ssl handshake的问题,只是ssl handshake 暴漏了这个问题而已。