OpenVPN性能优化-MTU


  好久没更blog了,最近比较闲,一直纠结于OpenVPN的性能问题,这实在是个老问题了,几年来一直都是修修补补,直到多线程多处理的实现,解决了server模式服务端的吞吐量问题,使得多个CPU核心可以得到充分的利用。但是对于客户端的优化,一直都没有很好的解决方案。   粗犷的作风实在是非常适合服务端优化,而客户端优化需要的却是对细致入微的细节之关注。对于OpenVPN客户端优化这件事,我遇到了“巨型帧”这个术语。

  效果不错,测试的结果还比较满意,还是老样子,记录一些想法却不记录技术细节,这是为了让自己或者别人日后看到这篇文后,知道有这么个事却又不能直接拿来就用,这有什么好处呢?这会让自己好好地再次理一遍思路而不是拿来主义的复制命令或者代码。自己写的代码或者命令,一周后,可能就和自己没关系了,半年后,自己也不懂了…但是idea是永恒的,以下记录部分网上节选资料。

以太网的一点历史包袱   以太网自打出生之日,一直保持着向下的兼容性,兼容性这个计算机时代可谓最重要的术语在以太网可谓表现得淋漓尽致,完全可以和IA32以及Win32 API相媲美,满足了投资者的心理的同时,方便了消费者,然而对于技术本身,保持兼容性却如临大敌。

  10M以太网时代,对于帧长是有规定的,最大的帧长和最小的帧长都是可以根据线路的物理特性计算出来的,最大的帧长的计算结果是1500Byte。于是,这个1500就成了一个以太网MTU的默认设置,在10M年代,这个长度绝对不短了,实际上它是可以发送的最大长度,超过这个长度的上层数据都要在IP层被分片,然后在接收端重组分片,而这个过程无疑需要更多的处理器时间,消耗资源。为了避免IP分片,传输介质的MTU必须被IP上层的协议所感知,对于TCP而言,这就是MSS协商,细节我就不讲了,因为我也不是很清楚。MSS协商之后,可以保证TCP每次发送的数据的长度均小于介质的MTU,对于UDP而言,需要应用层自己管理数据报的长度,或者通过MTU发现机制来动态调整。不管怎么说,IP分片能避免则避免。

  在下一节我会描述MTU确定的细节,现在我们知道它在兼容的意义上是1500,事实上,在10M年代,这个1500是电气特性决定的,但是在10G年代,以太网经过了天翻地覆的变化,1500早就不再是电气特性无法跨越的坎了,因此1500剩下的只是一个软件含义,你完全可以设置它为15000,但是即使如此,只要数据经由的路径上有一段路经的MTU是1500,那么就会发生IP分片。这就是历史包袱,可以发送更长的数据,但是却无法发送,因为发送了便可能产生IP分片,而IP分片的处理和重组可能会抵消掉大型数据帧带来的空间节省和时间节省。

分组交换的两个极端   为了避免IP分片,网卡总是被期待发送最小的数据,但是为了数据包处理效率的最大化,网卡总是被期待发送最大的数据,这就是一个矛盾,需要代偿计算权衡。我们知道,分组交换网的每一个分组均要携带元数据,由于协议栈是分层的,对于每一个分组,都要封装多个不同层的协议头。

  分组交换网,它是除了电路交换外的另一种传输形式,但是你可以换一种理解方式而调和二者,如果你把一个整个的数据流封装到一个分组,那就是电路交换,该数据流只有一个分组,一个分组只能有一条路径,这条路经在分组传输前就是客观存在的;如果你把一个整个的数据流拆分成多个分组,那就是分组交换,对于每一个分组,它们经过网络的路经可能是不同的,而在一个时间段内,一段路经上可能会经过不同数据流的分组,统计复用即体现于此。

  如果分组过小,虽然可以避免IP分片,但是同一个数据流直到处理完毕需要的数据分组会过多,因此会有更多的数据空间消耗在元数据上,同时更多的处理器时间消耗在根据协议元数据来进行的路由与交换上,如果分组过大,虽然会有更少的空间和时间消耗在协议元数据上,但是会有IP分片重组的开销,注意,在分组交换网上,IP分片是数据分组超过一定长度后必须的,这是由线路物理上的电气特性决定的,如果非要传输超长的数据分组,将会出现电气层面的故障,比如脉冲畸形等。因此能发送的避免全程IP分片的最大数据长度便是全程每一段路经MTU的最小值。

合时宜的巨型帧   虽然为了兼容,1500依然是诸多以太网卡的默认MTU设置,但是厂商对如今1G/10G等高端网卡以及超五类/六类双绞线以及光纤的高大上特性又不能视而不见,因此保留了对MTU的配置接口,由用户自己来决定自己网卡的MTU值,超过1500Byte的以太帧,为了和原汁原味标准以太网的1500Byte区别,叫做巨型帧,名字挺吓人,实际上也没有巨型到哪去。用户完全可以设置自己的网卡的MTU超过1500,但是能到多少呢?

  标准化问题是又一个棘手的问题,因为在交换以太网环境,线路的特征不再仅仅可以通过网卡和单一标准线缆(比如早期的粗缆,细缆)的电气特性来计算,而和交换机配置,双绞线类别,质量,网卡自协商配置,双工模式等息息相关,所以虽然出现了巨型帧,它还真不好驾驭,如果发送巨型帧,中间MTU偏小的窄路由器会将IP分片不说,可能还会影响对端数据帧的接收,因为连接两台设备的线缆很可能不是一根线缆,即便是一根线缆,两端的网卡特征也不一定相同,这就会导致收发设备对电气特性的解释有所不同,导致数据帧的接收失败。

  不管怎么说,巨型帧是合乎时宜的。如果没有1500作为底线,巨型帧长的标准化进程会快很多。

那么最后的结论是

  前面说了那么多看似和OpenVPN的优化不相关的东西,其实是很相关的。如果你到现在还不明白OpenVPN,请看 这里 ,我把OpenVPN当成了一种介质。数据从tap网卡发出进入了字符设备,这就进入了这种特殊的介质,等数据加密后发往对端,解密后写入字符设备,然后被对端tap网卡接收,这就走出了这种特殊的介质。

  既然是一种介质,那么肯定有自己的“难以跨越的电气特性”了。这种电气特性告诉人们它是怎么传输数据帧的。对于OpenVPN来讲,它的电气特性就是对数据进行加密,解密。在具体实施上,和在铜线上传输数据帧时的前导脉冲一样,OpenVPN也会在tap网卡出来的数据帧前封装一个所谓的“前导脉冲”,这就是OpenVPN的协议头。我们来看一下这个特殊的OpenVPN介质的传输开销。开销分为三大块,空间开销,时间开销,系统开销。

  在空间上,OpenVPN协议头无疑占据了一定的数据空间,如果tap网卡的MTU是1500,那么加上OpenVPN协议头的长度L,那么OpenVPN数据通道的数据最大长度将会是1500+L,我们知道这个数据作为一个buffer是通过UDP(暂且不谈TCP)套接字传输至物理网卡的,如果物理网卡的MTU也为1500,那么总长度1500+L+UDP/IP协议头的最大长度则肯定超过1500,而这将导致物理网卡上的IP分片,数据到达对端OpenVPN后首先要进行IP分片重组,然后再进入OpenVPN,解封装,解密…如果tap网卡的MTU过小,且远远小于物理网卡的MTU,虽然加上OpenVPN协议头,UDP/IP协议头可能不过超过物理网卡的MTU而不会IP分片,但是其载荷率将会大大降低,因为对于相同长度的数据流片断,会有大量的数据空间浪费在OpenVPN协议头,UDP/IP协议头上,反之,如果tap的MTU过大,载荷率将会大大提高,但是会引发IP分片。在分析完时间开销和系统开销后,我会说一下结论,现代高端网卡的IP分片与重组开销可以忽略。

  在时间上,OpenVPN的加密/解密,HMAC,压缩/解压缩等要消耗大量的CPU时间,经过测量,频繁加密小包的效率将会小于一次性加密大包的效率,延时效率不会有太大变化,而对吞吐率而言,大包加解密效率会明显提高。这个细节虽然不是什么cache在影响,倒是和CPU的流水线处理相关,细节就不多说了,特别是在RISC处理器上,效率更高。

  在系统开销上,由于OpenVPN要经由tap字符设备和tap虚拟网卡交互,通过socket和对端交互,因此系统调用read/write,send/recv将会导致用户态和内核态的切换,而这种切换的数量会随着数据分组的增大而减少。

  现在已经很清晰了,那就是将tap虚拟网卡的MTU设置成超级大!

  那么我们难道不怕OpenVPN和对端通信的socket发送数据过大而导致的IP分片吗?是的,我们宁可让它在本机进行IP分片,也不会通过物理网卡的巨型帧将其发送出去,第一,我不知道它能否被链路对端的设备正确接收,第二,我也不晓得整个路径上将巨型帧携带的数据分组进行IP分片的那台设备的处理效率。