基于TCP的VPN的效率问题分析
涛叔最近在研究 AnyConnect 协议和服务实现。AnyConnect 是思科开发的企业虚拟专用网(VPN)系统,其核心卖点之一是基于UDP的DTLS通道。本文就来分析一下为什么要用UDP传输VPN数据。
VPN工作在网络层,VPN隧道上传输的都是IP包,而IP包是不可靠的。为了实现可靠传输,我们需要用TCP协议。那TCP是怎么确保可靠传输的呢?答案是给每个包进行编号,接收方收到包后会把这个编号传回来,也就是所谓的ACK,过程如下:
如果A在一定时间内没有收到ACK,那可能是出现丢包,所以A会重新发送刚才的包:
当然了,如果B回复的ACK发生丢包,A也会重传:
如果反复出现丢包,就会产生大量的重传,严重浪费网络资源。所以TCP引入了指数避让算法。一开始超时的间隔比较短(比如0.5秒)。重传之后还收不到确认,则依次等待 1、1.5、2、2.5 等间隔。也不会无限等下去,超过一定时间就会关闭连接,通知应用出错。
那为会么用 TCP 传输 VPN 数据不好呢?因为 VPN 工作在网络层,而网络层本身不需要可靠传输。可靠传输是在 VPN 之上的传输层做的。如果用 TCP 传输 VPN 数据,那么这个「网络层」就是可靠的,上层 TCP 的可靠传输机制就成了包袱。
如果不看 AnyConnect 那一层,那就只有上层 TCP。应用发的每个数据包都会收到一个ACK包。但因为 AnyConnect 走了 TCP 通道。所以,上层的数据包会交给下层的TCP连接发送。VPN的对端收到数据后会发一个ACK,真正的接收端收到数据后会再发一个ACK。可个过程如下:
显然,实际传输的时候有两次 ack vpn。一般来说不会造成什么问题,但如果有超时,问题就来了。
如果有超时,肯定是下层 TCP 先超时,所以下层 TCP 会重传并等待确认。上层TCP不知道下层TCP已经超时了,还是继续发,结果新发的数据包都没有收到确认,所以上层 TCP 也开始重传。因为下层 TCP 已经处于重传等待状态,并不会发送上层 TCP 的重传数据,所以上层 TCP 继续超时并重传,从而造成雪崩。
这里问题的关键是 TCP 在设计的时候认为底层网络层是不可靠的,但我们用 TCP 实现了一个可靠的网络层,从而产生这样的雪崩。
所以,如果要做 VPN,还是要尽量使用 UDP。而 UDP 则是在 IP 之上简单加了端口信息,本质上跟 IP 包没有什么区别,可以看作是网络层的天然模拟。再辅之以 DTLS,简直就是为 VPN 量身定制的协议。
好,今天就聊到这里。我是涛叔。欢迎留言或者私信讨论。谢谢。