1、 succeeded16 Test证书/ UDPv4 READ 22 from P_ACK_V1 kid=0 1 16 Test证书/ UDPv4 WRITE 114 to P_CONTROL_V1 kid=0 pid=5 DATA len=100 6 5 2 3 416 Test证书/ UDPv4 READ 22 from P_ACK_V1 kid=0 2 16 Test证书/ UDPv4 WRITE 114 to P_CONTROL_V1 kid=0 pid=6 DATA len=100 7 5 6 3 416 Test证书/ UDPv4 READ 22 from P_ACK_V1 kid
2、=0 3 16 Test证书/ UDPv4 WRITE 114 to P_CONTROL_V1 kid=0 pid=7 DATA len=100 8 5 6 7 416 Test证书/ UDPv4 READ 22 from P_ACK_V1 kid=0 4 16 Test证书/ UDPv4 WRITE 114 to P_CONTROL_V1 kid=0 pid=8 DATA len=100 9 5 6 7 816 Test证书/ UDPv4 READ 22 from P_ACK_V1 kid=0 5 16 Test证书/ UDPv4 WRITE 114 to P_CONTROL_V1 kid=
3、0 pid=9 DATA len=100 10 9 6 7 816 Test证书/ UDPv4 READ 22 from P_ACK_V1 kid=0 6 16 Test证书/ UDPv4 WRITE 114 to P_CONTROL_V1 kid=0 pid=10 DATA len=100 11 9 10 7 816 Test证书/ UDPv4 READ 22 from P_ACK_V1 kid=0 7 16 Test证书/ UDPv4 WRITE 114 to P_CONTROL_V1 kid=0 pid=11 DATA len=100 12 9 10 11 816 Test证书/ UDP
4、v4 READ 22 from P_ACK_V1 kid=0 9 12 10 11 817 MULTI: REAP range 240 - 25617 GET INST BY REAL:17 Test证书/ UDPv4 READ 22 from P_ACK_V1 kid=0 10 17 Test证书/ ACK output sequence broken: 12 11 817 Test证书/ UDPv4 READ 22 from P_ACK_V1 kid=0 11 12 818 MULTI: REAP range 0 - 1618 Test证书/ TLS: tls_pre_encrypt: k
5、ey_id=018 Test证书/ SENT PING18 Test证书/ ACK output sequence broken:18 Test证书/ UDPv4 WRITE 53 to P_DATA_V1 kid=0 DATA len=52.持续了60秒没有收到ID为8的ACK,因此一直都是ACK output sequence broken:54:15 Test证书/ TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity) TLS handshake
6、 failed with peer 没隔一段时间就会断一次,并且重连还不一定总能重连成功!因此这里的问题有两点:a.连接正常时断开(ping-restart的情况,上述日志没有展示)b.重连时不成功(上述日志展示的)2.分析使用UDP的OpenVPN就是事多,为了避免重传叠加,在恶劣环境下还真得用UDP。然而OpenVPN实现的UDP reliable层是一个高度简化的“按序确认连接”层,它仅仅确保了数据安序到达,并且有确认机制,和TCP那是没法比。不过如果看一下TCP最初的方案,你会发现,TCP的精髓其实就是OpenVPN的reliable层,后来的复杂性都是针对特定情况的优化! 和TCP的
7、实现一样,不对ACK进行ACK对发送端提出了重传滑动窗口未确认包的要求,因为纯ACK可能会丢失,这里先不讨论捎带ACK。ACK一旦丢失,发送端肯定就要重传没有被ACK的包,关键是“什么时候去重传它”,协议本身一般都有一个或者多个Timer,Timer到期就重传,然而我个人认为这个Timer不能依赖上层,而要在协议本身实现,毕竟重传这种事对上层是不可见的! 然而,OpenVPN的reliable层在ACK丢失的应对方面却什么都没有实现,通过以上的日志可以看出,连续的:Test证书/ ACK output sequence broken:说明ID为8的包一直都得不到重传,并且从:这几行日志可以看出
8、,确实是没有收到ID为8的包地ACK,说明它丢失了,接下来发送的数据包将持续填充发送窗口,直到填满,ID为8的包还未重传并且收到对端对其的ACK,因此就导致了ACK output sequence broken,通过查代码,12-8=4,而4正是发送窗口的长度。持续了很久ACK output sequence broken之后,还是没有重传,直到:a.隧道建立之后的ping-restart过期b.隧道建立阶段的TLS handshake failed实际上,正确的方式应该是,检测到窗口爆满就应该马上重传。TCP通过三次重复ACK知晓丢包,而OpenVPN的reliable则通过ACK outp
9、ut sequence broken知晓ACK丢失,这是一个信号,应该在获取这个信号后做点什么了!3.原始方案方案很简单,那就是在打印ACK output sequence broken的逻辑块内重传所有未确认的包,然而作为一种优化,仅仅重传ID最小的包即可。这是因为,之所以到达ACK output sequence broken,是因为窗口满了,之所以满是因为ID最小的包未确认,占据了很大的一块空间以及其后面的实际上可能已经确认了的空间,因此只要ID最小的包被确认,窗口就放开了,故而仅重传ID最小的包,以期待对端能再次给出确认。 方案虽简单,但是不落实到代码还是0,以下是一些尝试4.第一次尝
10、试-出错7月25日下班后,又睡不着了,自己躲在女儿的小屋,开始了coding。首先确认一下对于乱序或者重放的包,对端也能ACK,如果不能,那就要大改了,找到了的代码,在tls_pre_decrypt中:plain view plain copyif (op != P_ACK_V1 & reliable_can_get (ks-rec_reliable) packet_id_type id; /* Extract the packet ID from the packet */ if (reliable_ack_read_packet_id (buf, &id) /* Avoid deadloc
11、k by rejecting packet that would de-sequentialize receive buffer */ if (ks-rec_reliable, id) if (reliable_not_replay (ks- /* Save incoming ciphertext packet to reliable buffer */ struct buffer *in = reliable_get_buf (ks-rec_reliable); ASSERT (in); ASSERT (buf_copy (in, buf); reliable_mark_active_inc
12、oming (ks-rec_reliable, in, id, op); . ACK output sequence broken: 12 8 二次尝试-成功失败!夜以沉默,心思向谁说 然而这个问题没有那么复杂,案件的侦破很简单,那就是看代码,终于找到了reliable_schedule_now函数,关键是它的注释:/* schedule all pending packets for immediate retransmit */重传!对的,是重传!既然OpenVPN本身有了重传,那么我的那个重传就是多此一举了!因此还是按照步骤来吧,直接调用这个接口即可,话说一定要用既有的接口,千万不要重复
13、实现既有逻辑!于是patch变得更加简单了,仅仅修改一个reliable_get_buf_output_sequenced函数即可:struct buffer * reliable_get_buf_output_sequenced (struct reliable *rel) struct gc_arena gc = gc_new (); int i; packet_id_type min_id = 0; bool min_id_defined = false; struct buffer *ret = NULL; /* find minimum active packet_id */ for
14、 (i = 0; i size; +i) const struct reliable_entry *e = &rel-arrayi; if (e-active) if (!min_id_defined | e-packet_id packet_id;min_id_defined | (int)(rel-packet_id - min_id) packet_id = id) next_try = now + timeout;timeout = rel-initial_timeout; break; e-#endif /* schedule all pending packets for immediate retransmit */ reliable_schedule_now (struct reliable *rel) #调用新接口函数 return reliable_schedule_id (rel, 0, 0);#else f
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1