又是好久没来这儿了啊。
最近因为工作需要(其实也没需要那么多),一直在断断续续地看 OpenVPN 的代码,终于大概搞清楚了它的握手是怎么个流程了。简单来说的话其实非常的简单,首先在 reliable
模块中实现了一个可靠的 UDP 报文协议,就是加上超时重传和确认报文的功能;然后用该协议交换一个 Hard Reset 命令,开始握手;最后建立 SSL 对象,并且通过内存 BIO
在可靠 UDP 协议的基础上转发 OpenSSL 的握手协议报文,通过这个 SSL 连接交换 OpenVPN 自己的密钥。接下来就是用这些密钥,该干嘛干嘛了。
本来还写了好多,不过太乱,写不下去了,还是简单点吧。具体实现就是,首先在 key_state_init()
函数中,初始化了 ssl
、 ssl_bio
、 ct_in
、 ct_out
这几个相关的成员, ssl
就是普通 SSL 程序里面的 SSL
对象, ssl_bio
是以 BIO
接口来读写这个 ssl
成员而准备的,tls_process()
中通过 ssl_bio
这个成员完成 OpenVPN 自己的密钥交换,完成从 S_START
到 S_GOT_KEY
这几个状态的转换。而 ct_in
和 ct_out
则是这个 ssl
对象的后端,不是通常用的 socket BIO 对象,而是两个 memory BIO 对象,为的就是得到 ssl
对象要发给对端的密文,然后通过可靠 UDP 协议来转发。这样, tls_process()
的主要工作就是操作这几个对象,外加可靠 UDP 模块的那几个队列了。
这里的关键就是通过 OpenSSL 实现提供的 memory BIO 对象抓取密文包的功能。如果是在其他没有这个功能接口的语言实现或封装中,则可以使用 socketpair 这种方法来抓,我今天就试着改了一下 OpenVPN ,这种方法是可行的。具体就是把其中一个 socket 给 ssl 当后端,另一个 socket 就可以用来读写密文了。注意 socketpair()
调用中的 type
参数要加上 SOCK_NONBLOCK
。
不过在 Erlang 这个语言中,甚至就连 socketpair
这个调用都没封。搜了一下它的源码,才发现自己实现一个也非常的简单,怪不得。下面摘自 otp_src_R14B/lib/kernel/test/inet_sockopt_SUITE.erl
文件:
|
|
至于前面的 ?line
是啥,咱就不需要关心了。