最近,我在系统中超融合的虚拟机中发现一个问题,即访问部分服务时会出现无法访问的问题。

现象

[root@test-mtu ~] curl http://bilibili.com # 正常访问 
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr/>Powered by Tengine<hr><center>tengine</center>
</body>
</html>
[root@test-mtu ~] curl -v https://bilibili.com  # 访问失败
*   Trying 8.134.50.24:443...
* Connected to baidu.com (8.134.50.24) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
*  CAfile: /etc/pki/tls/certs/ca-bundle.crt
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* OpenSSL SSL_connect: Connection reset by peer in connection to bilibili.com:443 
* Closing connection 0
curl: (35) OpenSSL SSL_connect: Connection reset by peer in connection to bilibili.com:443 

简单总结为:

ping 或者 curl bilibili.com、baidu.com这些是没有问题的

但是用scp、rysnc、访问https服务就会存在访问不通的问题

排查

使用curl命令访问 http://bilibili.comhttps://bilibili.com

并且使用tcpdump进行抓包

tcpdump -nne -w ./cap -i eth0 host bilibili.com

后面使用wireshark进行分析

cap-mtu

可以看到上面的1-10是正常的http,11-21是出现错误的https请求

注意里面的第16个包,信息为 [TCP Previous segment not captured] 表示前一个tcp包的数据没有被捕获,说明可以前一个数据包丢失了或者tcpdump没有捕获到对应的tcp包,但是后者应该不可能,首先在上面的http请求中tcpdump完整地捕获到了所有的tcp包,然后curl https请求时确实会出现访问失败的问题,所以可以断定是tcp数据包存在丢失的问题。

同时因为16、17的包出现问题,系统没有收到TLS握手时的 server hello ,以为第13个包发送失败,在第18、19个包就是重新发送了[TCP Dup ACK 13#1]

TLS握手流程如下,在客户端发送 Client Hello 后,服务器应该返回Server Hello 和对应的证书信息

tls

修复

后续发现时网卡的mtu配置错误了,该虚拟机的默认配置为 8950 ,在默认情况下,应该要配置跟路由器一致的mtu值

使用该命令来确认对应网关的mtu值

[root@test-mtu ~] ping -M do -S mtu值 网关ip

后续把mtu修改为1450(跟我的路由器一致时),该问题就解决了,可以正常访问服务

总结

我在查看第16个包时,发现它的ack为518,刚好跟第14个包(Client Hello)的确认要求一致(ack=1+tcp.len=517=518),并且在客户端发送的SYN包的MSS为8910,所以感觉就是MTU的配置导致系统以为该包是存在错误的

MSS 是 TCP 协议中的一个概念,它表示 TCP 数据包每次能够传输的最大数据量。MSS 是在建立 TCP 连接时协商确定的,它的值通常要小于 MTU。因为 TCP 数据包在传输过程中需要添加 TCP 头部和 IP 头部,所以 MSS = MTU - IP 头部大小 - TCP 头部大小。例如,在以太网中 MTU 为 1500 字节,IP 头部通常是 20 字节,TCP 头部通常是 20 字节,那么 MSS = 1500 - 20 - 20 =1460 字节。