在上一篇《TCP的MTU Probe和MSS(1)》介绍了TCP使用MTU Probe来避免PMTU变小而导致发送失败的方法。其主要思想是在TCP发送失败时,发送方会不断尝试降低MSS的大小,直至满足PMTU的限制,成功发送数据。这样虽然解决了PMTU变小引起发送失败的问题,但是却降低了传输效率 —— 数据报文越小,传输效率越低。作为完整的MTU Probe还要有一套机制,用于及时增大MSS,从而可以发送更大的报文。
今天,就让我们看看TCP的PMTU Probe如何解决这一问题的。
在函数tcp_write_xmit中
当skb数据包不需要push的时候,则可以进行MTU探测。TCP的PUSH标志的含义是尽快将数据包发送出去(对于发送端)。反过来,没有PUSH标志的时候,则表示数据包不是特别“紧急”。这时候就可以做点额外的工作,即进行MTU探测。
接下来进入tcp_mtu_probe,其入口先进行“合法性”检查,判定哪些情况不适合做MTU探测。
例如没有打开TCP MTU Probe,则这个函数直接返回。
接下来开始计算这次探测报文的大小。
每次使用“上限”search_high和“下限”search_low的中间值作为探测报文的大小。
后面的代码也是一些合法性检查,比如发送缓存中是否有足够的数据(TCP探测报文不可能“伪造”数据),发送窗口是否有足够的大小等。在这里不再罗列合法性检查的代码了。
接下来就是构造数据报文并发送了。
忽略一些处理代码。
这里数据包发送出去后,至少进行了探测。如果探测失败,即该TCP报文由于丢失了怎么办?
TCP数据包丢失时,会发生什么情况?在处理快速重传的函数tcp_fastretrans_alert中,判定MTU探测失败。
进入tcp_mtup_probe_failed。
当MTU探测失败时,search_high就减小到探测报文-1的大小。还有一种情况:TCP报文丢失而重传时,MTU probe功能会自动减小MSS。
如果探测成功会怎么样?数据包成功的发送到了对端,本端的TCP再次进入MTU探测函数tcp_mtu_probe。
如果前面计算探测报文的大小probe_size超过了当前search_high计算的MSS值(前面的探测成功,探测报文逐渐增大)或者上限与下限的间隔已经小于配置的阀值,则进入tcp_mtu_check_reprobe。
探测报文的发送时间间隔超过配置值,则更新探测上限为可能MTU的最大值(MSS上限 TCP首部 IP报文首部),下限为根据当前MSS计算的MTU值。这个探测过程,很像二分查找的方式,来获取最终的值。
至此,TCP MTU Probe的原理已经分析完毕,做一个简单的总结:当PMTU变小时,MTU Probe通过丢包发现这种情况,从而不断的降低当前MSS值,达到成功发送的目的。当PMTU变大时,利用非PUSH报文,尝试发送更大的TCP数据报文。如果成功,则进一步提高探测上限,从而逐步探测到该PMTU可传送的最大TCP报文。