本文很短,目的在于confirm一下凌乱的《 OpenVPN莫名其妙断线的问题及其解决》,如果看觉得我比较啰嗦,那么一定要看看最后一个小节,好在CSDN为每篇文章都自动添加了目录,可以直接跳转到最后一节。

1.控制通道

控制通道主宰OpenVPN的SSL握手,密钥协商以及重协商。因此其健壮性直接影响到隧道是否能够建立成功。因此优化后burst retransmit直接影响恶劣网络环境下的隧道建立过程,使之更容易建立。一旦窗口由于ACK乱序/丢失而爆满,马上重传ID最小的包,期待收到ACK延展窗口!

原则:你丢包我就以多次重发来稀释掉丢包率,虽然这种方式有点自私,但是恶劣环境中求生是需要自私的。

效果:由于控制通道的数据量有限,因此需要比较极端的方式来展现这个修改是有效的,那就是设置以下几个参数:

ping:设置为5秒,尽可能短,但不要太短
ping-restart:设置成120秒,尽可能和ping拉开距离,这两个参数保证不会因为ping-restart导致断开,这样就将问题全部集中在控制通道了
reneg-sec:将它设置成2,即2秒钟进行一次密钥重协商
hand-window:将它设置成15,即15秒内如果SSL通道上的握手,密钥重协商没有成功,则算断开
tran-window:将它设置成5,即hand-window内失败的话,持续5秒钟隧道断开,该参数是可选的


用retry版本和标准版本测试,发现极端情下,标准版本几乎会瞬间失败,但是retry版本明显好很多。

2.数据通道

平时隧道有数据通过的时候,timer总是会reset,没有数据的时候,就依靠在数据通道发送PING来reset对端的timer。如果ping-restart到期timer都没有reset,则断开隧道。因此没有隧道数据时,更容易断开!注意,只要隧道建立,密钥协商好,除了重协商或者推送,控制通道基本空闲。因此影响隧道接通后断开的因素在ping,ping-restart参数。

3.高丢包率环境二者关系

一旦高丢包率且没有隧道数据过境,数据通道就会完全依靠PING,如果ping-restart过短,PING在恶劣环境下丢包,则隧道就会断开,此时会尝试重新建立隧道,因为此时已经是恶劣环境了,丢包率很高,ACK很容易乱序/丢失,加上OpenVPN在窗口满时不会马上重传,故而隧道久久不能建立。所以是,数据通道断开反映了网络环境已经恶化,这种恶化进而影响接下来的控制通道的握手和协商,所以针对OpenVPN断开的问题要双管齐下,第一拉长ping和ping-restart的距离(但是不能泛PING),第二,如《OpenVPN莫名其妙断线的问题及其解决》一样修改OpenVPN的重传调度逻辑。

4.程序极限

程序员往往希望一厢情愿地彻底解决问题!极端的情况就是,因为TCP是保证传输的,所以即使网线被剪断了,程序员还是希望(也仅仅是希望而已)能写出什么神秘的代码来接通网络,也还是希望发现是由于自己代码写的不够好才导致丢包,断线。但是对于网络工程师,就反过来了,他丝毫不管应用程序如何,上去就是show这show那,检查网线...有时候还真的就是socket阻塞了。事实上,对于OpenVPN断线的问题,也有一种处理极限,那就是网络真的不通了,丢包率真的太大了。那怎么办,那就只能断线了,这是我们所解决不了的,我们能做的就是保证损失降低到最小而已,并且告知用户发生了什么,让用户知道,虽然网络不通了,但是OpenVPN依然在不断努力中。

5.不修改OpenVPN的防断方案

不管怎么说,OpenVPN也是经过很多测试的成熟代码,它的重传调度虽然不及时,单是毕竟会重传!如果有重大问题,早就在社区被修改了。另外要注意的是,大多数时候,控制通道的数据包以及ACK并不是丢了,而是延迟到达,这样的话,仅仅通过延长超时时间就可以解决,幸运的是,OpenVPN本身给出了很多超时时间的配置:

a.增加ping,ping-restart的差值,可以确保不会轻易由于收不到ping而断开;
b.延长hand-window可以给控制通道的密钥协商预留足够的时间;
c.延长tran-window可以进一步在已经发觉要断和隧道真正断开之前的这期间,做reneg的最后尝试;此参数要大于reneg-sec!
...

6.我做的一切都是画蛇添足,但是...

实际上,当我回归看代码的时候,发现我所做的针对OpenVPN的修改本来就已经实现了,只是我研究其参数配置还没有细化到一定程度,所以我就自行进行了修改,对于我能找到点子上,并且设计出了一个schedule接口:

void
reliable_schedule_id (struct reliable *rel, time_t timeout, packet_id_type id);

我很欣慰,但是我还是重新实现了已经实现的东西,重造了轮子!这是大多数初中级程序员的典型行为!
        我们来看一下以下的这个配置参数:

--tls-timeout n
              Packet retransmit timeout on TLS control channel if no acknowledgment from remote within  n  seconds  (default=2).
              When  OpenVPN sends a control packet to its peer, it will expect to receive an acknowledgement within n seconds or
              it will retransmit the packet, subject to a TCP-like exponential backoff algorithm.  This parameter  only  applies
              to  control  channel  packets.   Data  channel packets (which carry encrypted tunnel data) are never acknowledged,
              sequenced, or retransmitted by OpenVPN because the higher level network protocols running on  top  of  the  tunnel
              such as TCP expect this role to be left to them.


默认2秒钟,如果想让重传密集化,那么设置为1好了,虽然依靠这个参数带来的是全局意义的重传超时配置,但是我相信网络会解决好拥塞问题的。比较下来,我的修改只针对ID最小的包,即仅仅重传阻碍窗口推进的包,可能更加合理,但是OpenVPN自带的tls-timeout足以解决已经存在的ACK乱序丢失问题了,要拒绝完美,容忍不完美,这才是成事的关键!如果要完美,那么代码中就会充斥很多的所谓小技巧。

        花了大量的时间考虑如何修改OpenVPN的代码,到头来却是代码回退!

05-11 20:53