上周因为发烧没办法坚持每“周一”更 —— 再次感叹“每周一更”这个名字,先给大家道声抱歉。
在上一篇细说TCP的MSS选项(1)中给出的了影响MSS的因素:一般都是由出口路由的MTU决定。但这只是TCP的syn报文的情况,今天就要分析syn+ack报文中的MSS的情况。
首先我们做个实验,检测本地的MTU是否会影响到对端的MSS。
我本地出口网卡的MTU为1500,减去40,即syn报文中MSS值,1460。 而百度服务器回复syn+ack报文的mss为1400。
我本地出口网卡的MTU为1000,减去40,即syn报文中MSS值,960。 而百度服务器回复syn+ack报文的mss值也为960?!
从这个测试结果看,syn+ack报文的MSS值还要受到syn报文的MSS值影响,可能会取本地计算结果的MSS值和syn报文中的MSS值中的较小值。
为了确定这个结果,让我们从内核源码中寻找答案。函数tcp_make_synack是用于生成syn+ack报文,其中
tcp_mss_clamp用于获得syn+ack报文的mss值。
而tcp_mss_clamp仅是使用user_mss(该TCP套接字配置的MSS选项)与抽口dst的MSS进行对比。只有设置了user_mss,且其值又小于dst的MSS时,才会使用user_mss,否则syn+ack报文就是dst的MSS值——也就是前文中所述,出口路由的MTU-40。
这样分析,syn+ack报文的MSS值,在通常情况下,也是由出口网卡的MTU决定,即MTU-40。那么为什么我修改本地的MTU,会影响到百度服务器的syn+ack报文中的MSS值呢? —— 说实话,我为这个问题困扰了半天,来回的翻看内核的相关代码,担心遗漏了其它情况。但是内核回复syn报文的逻辑还是相对清晰的,从入口函数tcp_v4_conn_request开始,直到tcp_v4_send_synack,只有这个函数与syn+ack的MSS值相关。这时,就要相信自己对代码的分析没有问题。
原始内核产生的syn+ack中的MSS同样只跟出口dst的MTU有关。
为了验证自己的想法,在本地用nc来做个简单的服务端,进行测试。抓包结果如下:
客户端的网卡MTU依然是1000,所以syn报文中的MSS还是960。但是syn+ack报文中的MSS没有变化,仍然是默认MTU1500-40,即1460。通过简单的测试,真相也就大白了。
总结一下:
专注于网络技术开发,坚持“每周一更”~