OpenVPN莫名其妙断线的问题及其解决分析.docx
《OpenVPN莫名其妙断线的问题及其解决分析.docx》由会员分享,可在线阅读,更多相关《OpenVPN莫名其妙断线的问题及其解决分析.docx(18页珍藏版)》请在冰豆网上搜索。
![OpenVPN莫名其妙断线的问题及其解决分析.docx](https://file1.bdocx.com/fileroot1/2022-12/7/f97b57e9-000b-46d9-a603-1c8a78df88b9/f97b57e9-000b-46d9-a603-1c8a78df88b91.gif)
OpenVPN莫名其妙断线的问题及其解决分析
OpenVPN莫名其妙断线的问题及其解决
1.问题
不得不说,这是一个OpenVPN的问题,该问题几乎每个使用OpenVPN的人都碰到过,也有很多人在网上发问,然而一直都没有人能给出解决办法,甚至很多帖子上表示因为这个问题而放弃了使用OpenVPN。
说实话,我面临这个问题已经两年有余,自从第一次接触OpenVPN,这个问题就一直困扰着我,去过国内外各大论坛也没有找到满意的结果。
这几天终于有点闲暇,我决定自己去摸索一下,要感谢公司能给我提供一个环境!
最终,我取得了突破性的进展,还是那句话,我把这个结果贴了出来,就是为了以后人们再面临这个问题时可以多一个可选的答案。
顺便说一下,并不能说明网上就没人解决过这个问题,因为我所能看到并理解的,只有中文或者英文的帖子或者文章,虽然日文的也在我老婆的帮忙翻译下看过一些,但是还有大量的德文,意大利文,韩文等作为母语的人写出的东西我无法找到并且理解它,因此为了通用性,我本应该用英文来写这篇文章,然而英文水平太垃圾,怕那样连中国人都不能理解了...
问题是这样的,OpenVPN在跨越公网上连接时,会莫名其妙的时不时断开,但不经常,也不绝对!
由于大部分人使用Windows版本的作为OpenVPN客户端,因此起初一直一为是Windows本身的问题,然而当我用Linux客户端连接时,还是一样,这就是说,很大程度上冤枉了Windows(也并不是完全冤枉,起码Linux就没有DHCP租约的问题),于是既然有了环境,那就折腾一番,因此又是一个惊魂48小时。
以下是在客户端断开时服务端的日志(频繁的断开就会有频繁的日志,现在仅仅截取一段):
2013-07-2416:
53:
15MULTI:
REAPrange208->224
...
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[5]1234
2013-07-2416:
53:
16GETINSTBYREAL:
218.242.253.131:
18014[succeeded]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4READ[22]from218.242.253.131:
18014:
P_ACK_V1kid=0[1]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4WRITE[114]to218.242.253.131:
18014:
P_CONTROL_V1kid=0[]pid=5DATAlen=100
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[6]5234
2013-07-2416:
53:
16GETINSTBYREAL:
218.242.253.131:
18014[succeeded]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4READ[22]from218.242.253.131:
18014:
P_ACK_V1kid=0[2]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4WRITE[114]to218.242.253.131:
18014:
P_CONTROL_V1kid=0[]pid=6DATAlen=100
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[7]5634
2013-07-2416:
53:
16GETINSTBYREAL:
218.242.253.131:
18014[succeeded]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4READ[22]from218.242.253.131:
18014:
P_ACK_V1kid=0[3]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4WRITE[114]to218.242.253.131:
18014:
P_CONTROL_V1kid=0[]pid=7DATAlen=100
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[8]5674
2013-07-2416:
53:
16GETINSTBYREAL:
218.242.253.131:
18014[succeeded]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4READ[22]from218.242.253.131:
18014:
P_ACK_V1kid=0[4]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4WRITE[114]to218.242.253.131:
18014:
P_CONTROL_V1kid=0[]pid=8DATAlen=100
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[9]5678
2013-07-2416:
53:
16GETINSTBYREAL:
218.242.253.131:
18014[succeeded]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4READ[22]from218.242.253.131:
18014:
P_ACK_V1kid=0[5]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4WRITE[114]to218.242.253.131:
18014:
P_CONTROL_V1kid=0[]pid=9DATAlen=100
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[10]9678
2013-07-2416:
53:
16GETINSTBYREAL:
218.242.253.131:
18014[succeeded]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4READ[22]from218.242.253.131:
18014:
P_ACK_V1kid=0[6]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4WRITE[114]to218.242.253.131:
18014:
P_CONTROL_V1kid=0[]pid=10DATAlen=100
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[11]91078
2013-07-2416:
53:
16GETINSTBYREAL:
218.242.253.131:
18014[succeeded]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4READ[22]from218.242.253.131:
18014:
P_ACK_V1kid=0[7]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4WRITE[114]to218.242.253.131:
18014:
P_CONTROL_V1kid=0[]pid=11DATAlen=100
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[12]910118
2013-07-2416:
53:
16GETINSTBYREAL:
218.242.253.131:
18014[succeeded]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4READ[22]from218.242.253.131:
18014:
P_ACK_V1kid=0[9]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[12]10118
2013-07-2416:
53:
17MULTI:
REAPrange240->256
2013-07-2416:
53:
17GETINSTBYREAL:
218.242.253.131:
18014[succeeded]
2013-07-2416:
53:
17Test证书/218.242.253.131:
18014UDPv4READ[22]from218.242.253.131:
18014:
P_ACK_V1kid=0[10]
2013-07-2416:
53:
17Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[12]118
2013-07-2416:
53:
17GETINSTBYREAL:
218.242.253.131:
18014[succeeded]
2013-07-2416:
53:
17Test证书/218.242.253.131:
18014UDPv4READ[22]from218.242.253.131:
18014:
P_ACK_V1kid=0[11]
2013-07-2416:
53:
17Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[12]8
2013-07-2416:
53:
18MULTI:
REAPrange0->16
2013-07-2416:
53:
18Test证书/218.242.253.131:
18014TLS:
tls_pre_encrypt:
key_id=0
2013-07-2416:
53:
18Test证书/218.242.253.131:
18014SENTPING
2013-07-2416:
53:
18Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[12]8
2013-07-2416:
53:
18Test证书/218.242.253.131:
18014UDPv4WRITE[53]to218.242.253.131:
18014:
P_DATA_V1kid=0DATAlen=52
2013-07-2416:
53:
18Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[12]8
....持续了60秒没有收到ID为8的ACK,因此一直都是ACKoutputsequencebroken:
[12]8
2013-07-2416:
54:
15Test证书/218.242.253.131:
18014TLSError:
TLSkeynegotiationfailedtooccurwithin60seconds(checkyournetworkconnectivity)
2013-07-2416:
54:
15Test证书/218.242.253.131:
18014TLSError:
TLShandshakefailedwithpeer0.0.0.0
没隔一段时间就会断一次,并且重连还不一定总能重连成功!
因此这里的问题有两点:
a.连接正常时断开(ping-restart的情况,上述日志没有展示)
b.重连时不成功(上述日志展示的)
2.分析
使用UDP的OpenVPN就是事多,为了避免重传叠加,在恶劣环境下还真得用UDP。
然而OpenVPN实现的UDPreliable层是一个高度简化的“按序确认连接”层,它仅仅确保了数据安序到达,并且有确认机制,和TCP那是没法比。
不过如果看一下TCP最初的方案,你会发现,TCP的精髓其实就是OpenVPN的reliable层,后来的复杂性都是针对特定情况的优化!
和TCP的实现一样,不对ACK进行ACK对发送端提出了重传滑动窗口未确认包的要求,因为纯ACK可能会丢失,这里先不讨论捎带ACK。
ACK一旦丢失,发送端肯定就要重传没有被ACK的包,关键是“什么时候去重传它?
”,协议本身一般都有一个或者多个Timer,Timer到期就重传,然而我个人认为这个Timer不能依赖上层,而要在协议本身实现,毕竟重传这种事对上层是不可见的!
然而,OpenVPN的reliable层在ACK丢失的应对方面却什么都没有实现,通过以上的日志可以看出,连续的:
Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[12]8
说明ID为8的包一直都得不到重传,并且从:
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4READ[22]from218.242.253.131:
18014:
P_ACK_V1kid=0[6]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4WRITE[114]to218.242.253.131:
18014:
P_CONTROL_V1kid=0[]pid=10DATAlen=100
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[11]91078
2013-07-2416:
53:
16GETINSTBYREAL:
218.242.253.131:
18014[succeeded]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4READ[22]from218.242.253.131:
18014:
P_ACK_V1kid=0[7]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4WRITE[114]to218.242.253.131:
18014:
P_CONTROL_V1kid=0[]pid=11DATAlen=100
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[12]910118
2013-07-2416:
53:
16GETINSTBYREAL:
218.242.253.131:
18014[succeeded]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014UDPv4READ[22]from218.242.253.131:
18014:
P_ACK_V1kid=0[9]
2013-07-2416:
53:
16Test证书/218.242.253.131:
18014ACKoutputsequencebroken:
[12]10118
2013-07-2416:
53:
17MULTI:
REAPrange240->256
2013-07-2416:
53:
17GETINSTBYREAL:
218.242.253.131:
18014[succeeded]
2013-07-2416:
53:
17Test证书/218.242.253.131:
18014UDPv4READ[22]from218.242.253.131:
18014:
P_ACK_V1kid=0[10]
这几行日志可以看出,确实是没有收到ID为8的包地ACK,说明它丢失了,接下来发送的数据包将持续填充发送窗口,直到填满,ID为8的包还未重传并且收到对端对其的ACK,因此就导致了ACKoutputsequencebroken,通过查代码,12-8=4,而4正是发送窗口的长度。
持续了很久ACKoutputsequencebroken之后,还是没有重传,直到:
a.隧道建立之后的ping-restart过期
b.隧道建立阶段的TLShandshakefailed
实际上,正确的方式应该是,检测到窗口爆满就应该马上重传。
TCP通过三次重复ACK知晓丢包,而OpenVPN的reliable则通过ACKoutputsequencebroken知晓ACK丢失,这是一个信号,应该在获取这个信号后做点什么了!
3.原始方案
方案很简单,那就是在打印ACKoutputsequencebroken的逻辑块内重传所有未确认的包,然而作为一种优化,仅仅重传ID最小的包即可。
这是因为,之所以到达ACKoutputsequencebroken,是因为窗口满了,之所以满是因为ID最小的包未确认,占据了很大的一块空间以及其后面的实际上可能已经确认了的空间,因此只要ID最小的包被确认,窗口就放开了,故而仅重传ID最小的包,以期待对端能再次给出确认。
方案虽简单,但是不落实到代码还是0,以下是一些尝试
4.第一次尝试-出错
7月25日下班后,又睡不着了,自己躲在女儿的小屋,开始了coding。
首先确认一下对于乱序或者重放的包,对端也能ACK,如果不能,那就要大改了,找到了ssl.c的代码,在tls_pre_decrypt中:
[plain]viewplaincopy
if(op!
=P_ACK_V1&&reliable_can_get(ks->rec_reliable)){
packet_id_typeid;
/*ExtractthepacketIDfromthepacket*/
if(reliable_ack_read_packet_id(buf,&id)){
/*Avoiddeadlockbyrejectingpacketthatwouldde-sequentializereceivebuffer*/
if(reliable_wont_break_tiality(ks->rec_reliable,id)){
if(reliable_not_replay(ks->rec_reliable,id)){
/*Saveincomingciphertextpackettoreliablebuffer*/
structbuffer*in=reliable_get_buf(ks->rec_reliable);
ASSERT(in);
ASSERT(buf_copy(in,buf));
reliable_mark_active_incoming(ks->rec_reliable,in,id,op);
}
//注意这个注释,即使是重放包也ACK!
而我解决ACK丢失的思路正是重放那个迟迟收不到
//ACK的包,期待对端发送ACK,按照随机丢包概率,针对该包的ACK总不能一直丢失吧!
/*Processoutgoingacknowledgmentforpacketjustreceived,evenifit'sareplay*/
reliable_ack_acknowledge_packet_id(ks->rec_ack,id);
}
}
}
有了以上的基础,起码我知道,针对OpenVPN的reliable层修改的代码不多!
接下来就是找到修改哪里了,当然是哪里出问题修改哪里!
之所以僵持在那里,就是因为“ACKoutputsequencebroken”,所以说我找到了打印这个的地方,在reliable_get_buf_output_sequenced函数中:
[plain]viewplaincopy
structbuffer*
reliable_get_buf_output_sequenced(structreliable*rel)
{
structgc_arenagc=gc_new();
inti;
packet_id_typemin_id=0;
boolmin_id_defined=false;
structbuffer*ret=NULL;
/*findminimumactivepacket_id*/
for(i=0;isize;++i){
conststructreliable_entry*e=&rel->array[i];
if(e->active){
if(!
min_id_defined||e->packet_idmin_id_defined=true;
min_id=e->packet_id;
}
}
}
//以下判断没有通过的原因,在上面的日志中已经找到了:
//...ACKoutputsequencebroken:
[12]8
//12-8=4,而#defineTLS_RELIABLE_N_SEND_BUFFERS4
if(!
min_id_defined||(int)(rel->packet_id-min_id)size){
ret=reliable_get_buf(rel);
}else{
dmsg(D_REL_LOW,"ACKoutputsequencebroken:
%s",reliable_print_ids(