OpenVPN 的初始化过程分析

题外话:Eclipse CDT 很给力,至少在我用起来,比 gVim + Cscope 或者 SourceInsight 要来得给力,推荐一下。

更准确的来说,是 OpenVPN 的客户端与服务端之间,从协商密钥、到推送配置,以及最后的网卡与路由配置生效,开始进行 IP 报文的传递,这整个的过程。

完了。嗯,整个过程就像上面说的,这几个步骤而已。

不过重点当然还是代码啦。以前一直以为 OpenVPN 的点对点模式下,两端会进行一个决定谁是客户端,谁是服务端的协商过程,一直都想知道是怎么做的。后来才知道,原来通过配置信息,就已经决定好这个了。当然,我下面说的是客户端-服务器模式。

那么先来看客户端的初始化。客户端进入的还是点对点模式,进入点,也是其事件循环的入口为 tunnel_point_to_point() 。然后是 init_instance() ,在这个函数中进行客户端自身的初始化,包括各种数据结构的建立,像分片啦、压缩啦、socket 啦、MTU 啦等。其中代码量最多的是 SSL 模块的初始化( do_init_cryptodo_init_frame_tls ),要调很多的 OpenSSL 的函数;还有对 socket 的初始化,分为两个阶段,第一个阶段解析域名,第二个阶段发起连接(如果是 TCP 的话)。我一直搞不十分清楚的是 MTU ,也就是 frame 模块,因为太细节而且太分散了。

然后通过 pre_select() 函数进入 check_tls() 过程。因为是客户端,这时就会准备好发送第一个握手报文,命令码为 P_CONTROL_HARD_RESET_CLIENT_V1/2 。按照上篇所说与服务器交换好了密钥之后,会在 tls_process() 函数的 3852 行,设置连接成功建立的标志 connection_established

之后,还是在 pre_select()check_connection_established() 函数中,初始化 push_request_interval 定时器,由客户端开始发送推送配置的请求报文。推送配置功能的报文走的是和交换密钥相同的 SSL 隧道,按理说蛮可靠的,不过还是实现了重传,不知道是基于什么考虑。通过 check_incoming_control_channel() 函数收到服务器推过来的额外配置之后,调用 process_incoming_push_msg() 函数解析一下收到的配置字符串,就要根据这些配置,调用 do_up() 函数进行最终的初始化工作了。

do_up() 中,先调用 do_open_tun() ,根据由服务器发过来的所分配置的虚拟 IP 地址把虚拟网卡给起了,然后把 route_wakeup 定时器给激活一下,告诉 check_add_routes() 函数加一下路由,完了之后就是 initialization_sequence_completed() 了。

服务器嘛,也就是顺序不一样。首先,虚拟网卡和系统路由可以先建起来,因为不需要等其他人推配置。然后就等着客户端发过来第一个报文了,然后握手,然后把配置推过去,然后就完了。

然后就是在虚拟网卡跟物理网卡之间当搬运工啦。因为服务器要服务多个客户端,所以还用二叉树实现了一个调度器。

就这样。

 Share!

 
comments powered by Disqus