OpenVPN优化之-TLS握手控制通道的建立
关于OpenVPN数据通道的一次优化正在进行中,当我参考了”巨型帧“的概念和思想后,我仔细思考了TCP/IP协议栈的设计和实现,于是我得出了一个
可能是错误但是起码在我的场景中很实用的结论:上层协议尽管把数据发出吧,不要管数据的大小,如果真的需要拆分,那就由下层来完成吧。
这个结论的一个反例就是TCP协议的数据分段,它感知了传输链路的MTU,也许在早期的网络中,IP分片重组确实影响了TCP的端到端流控和拥塞控制,也
许还为状态防火墙带来了难题,但是现在不了,现在绝不再是这样了。如果在应用层感知了MTU,那么增加的处理复杂性完全抵消了IP分片避免带来的好处。
OpenVPN协议分析
我
在3年前曾经逐字节地分析过OpenVPN协议,那个时候几乎没有人分析过,我的分析也只是兴趣所致,没有什么实际用途。看着OpenVPN那凌乱的代
码,然后通过抓包分析协议实在太痛苦了,我当时有一种编写OpenVPN协议Wireshark插件的冲动,若不是受制于Windows以及
Gnome/QT编程环境,也许我早就实现了,不喜欢也玩不转IDE,旧梦让我急于将时间快速用在问题本身而不是其它任何事。虽然当前的编程工具,框架几
乎都在声称”让你将精力集中在自己的逻辑,而不必在意XXYYOO“,但我看不到这一点,学习成本太高本身就抵消了你对外围的关注。为了一杯牛奶养一头牛
本身就是一种愚蠢的想法。
Wireshark对OpenVPN协议的支持最终还是来了,但是来的太晚了。
抓取OpenVPN隧道建立时的数据包,确认SSL如何在OpenVPN协议内部被封装。显然,SSL握手协议需要被封装在OpenVPN协议内部,具体如何封装的,抓包结果如下图:
这
个图本应该显示了ClienHello的封装,但是呢?没有但是,它就是ClientHello,你看到数据的前几字节:16030100...折腾过
SSL协议或者被SSL协议残忍蹂躏过的一看到这个就没脾气了。但是为何OpenVPN协议没有解析出这是一个ClientHello呢?因为它分段
了...这么一个短短的ClientHello也要分段?也要!OpenVPN协议头占据了一些空间,加上ClientHello本身,我们看到这个
OpenVPN数据的长度为100,它的另一部分在数据段29里面,那么看一下29:
确实,数据段28和数据段29拼接起来就是ClientHello了,是这样吗?当然是!
为什么一个ClientHello要分割成100字节的大小呢?早在3年前我在一篇文章中写下了”这样,即使一个1000字节的SSL握手消息,reliable层也可以将其拆成10个100多字节的UDP包,数据到达对端后,每一个100多字节的UDP包进入其SSL BIO的内存BIO中,由SSL协议的实现负责重组数据。“那篇文章是《OpenVPN协议解析-握手数据包分析》,当时费了那么大的劲也没有给出原因。
纵一个ClientHello都要分割,要是ServerHello以及后续的Certificate呢?当然也要分割了。我们看一下这个夸张的包,分了那么多的段:
UDP下的握手性能
从
抓包分析可以看到,一次SSL握手要来来回回交互那么多的包,每一个包在Reliability层都要经过确认,这将极大影响效率。为何不将分割处理交给
下层呢?IP层或者网卡会做得更好,即便做的不好,你也不必加班调试自己的程序和排错,对于协议栈的故障,你只需浏览Maillist,更新驱动...
在测试之前,我先要找出在哪个地方将发送段的长度设置成了100。对于OpenVPN,有两个frame,一个是数据通道的,一个是控制通道的,这次我们
只关注控制通道。这个frame在tls_multi的tls_options字段中,它的初始化在
tls_init_control_channel_frame_parameters:
static void tls_init_control_channel_frame_parameters(const struct frame *data_channel_frame, struct frame *frame) { ... /* set dynamic link MTU to minimum value */ //WHY?WHY?说了多少次了,两端的MTU要一致!而这个是最可能一致的!毕竟控制通道不建立,一切无法协商! frame_set_mtu_dynamic (frame, 0, SET_MTU_TUN); }
我们把最后这个调用中的0改为1500或者更大的任何数即可!它直接影响了tls_process的过程,在tls_process中,SSL握手消息存储在内存中,每次IO的粒度是PAYLOAD_SIZE_DYNAMIC (&multi->opt.frame),而它就是frame_set_mtu_dynamic设置的结果。
在修改了控制通道的MTU之后,再次运行,数据包的个数几乎就是SSL握手的数据包个数!比较一下前后的性能,修改后好了几乎一倍!我的测试方式比较简略,就是在x_msg里面加了一个计数器,获取毫秒值,SSL握手完成后计算时间差。
原生代码为何要将控制通道的MTU设置为100呢?因为两端的MTU值必须一致,在控制通道建立之前,一切无从协商。
测试时的吐槽
我
通过OpenVPN传输大数据,旨在计算一个性能值,作为对比,我关闭了OpenVPN进程但是并没有撤去机器,之前运行OpenVPN的机器只做
forward,传输同样的大数据。这时有人说话了,而且不止一人,他们非要撤去两台本来运行OpenVPN的机器,非要将测试机用网线直连!有这个必要
吗?就你那点数据还指望能压满路由器?如果Linux的转发机制能让你的那点儿数据压出个区分来,那世界上多少设备得下架啊!难道不知道,这世界上又有多
少设备在真实环境中是网线直连的,难道要把交换机砸烂?难道路由器交换机的发明是个错误?
我明白非研发人员的心理,他们当然要挑出你的哪怕一点点毛病,毕竟他们要直接面对客户,这对于研发来讲也是好事,可以精益求精,之所以要撤掉设备做直连是
因为他们想做出最大的差别。但是也麻烦专业一点,如果不知道一台设备的纯转发性能,就不要总嚷嚷它是瓶颈,如果一个软件有问题,那是用肉眼都能看出来的,
如果不是排错而是求精,麻烦给出数据。如果真的那么希望做差别,麻烦别总是使用虚拟机,我本身是十分讨厌虚拟机的,特别是做压力测试时。
一边吵着要撤掉还挺高端的纯forward设备,一边还坚持在笔记本上跑好几个虚拟机进行测试...唉
盲写socket代码
敢问谁能不看man,不google,不baidu,...直接盲写出一个TCP socket服务端,不用select/poll,如果能的话,请试试写一个select,接着poll,最后epoll...反正我一个也不能...
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。