一、解决驱动问题
由于Windows下并没有tun/tap驱动,应此对Windows版本的移植变得十分困难,不过幸运的是OpenVPN有一个开源项目《tap-windows》。借助于它的源代码,博主编写了qtun自己的驱动,姑且命名为《qtun_sys》。其主要在NDIS框架之上构建了一张虚拟网卡,借助这张虚拟网卡我们可以像Linux下的tun驱动那样来使用它。不过,令人不爽的是Windows下的驱动并不符合POSIX标准,我们不能像Linux底下那样使用open来打开它,同样也不能使用select来查看它有没有数据被送出。我们只能用Windows的标准函数CreateFile、WriteFile、ReadFile和DeviceIoControl等函数来与该驱动通信。由于不能使用select函数来得知该驱动是否有数据被发出,应此我在该驱动中定义了接口IOCTL_HAVE_DATA,它返回一个字节的数据,表示是否有数据抵达这张网卡。
二、解决跨平台问题
在新的代码中,你将看到许许多多的#ifdef WIN32之类的代码,以此来隔离Windows与Linux之间的代码不兼容问题,这里不一一展开进行描述。
三、各种坑
- 在Windows中位域总是按照某种类型对齐的,既:
typedef struct { unsigned char a : 4; unsigned short b : 4; } A;
以上结构在Windows下的大小为3个字节,在Windows下编译器总是先将某一种结构填满,然后继续填充下一种结构。由于属性a为unsigned char结构,编译器在看到b类型为unsigned short时,发现其与a的类型不同。应此它将a后面的4个比特位填充,使其占满一个unsigned char长度,同样的b结构后的12个比特位同样被填充了。应此类型A的大小为3个字节,这一点是与gcc与clang不同的地方。
- 由于Windows驱动不符合POSIX标准,应此无法使用select函数,按照上文的说法我们采用自定义接口IOCTL_HAVE_DATA来得知是否有数据抵达该虚拟网卡,又由于qtun是单线程的。所以我们不能将select阻塞太长时间,应此我们将服务端和客户端的select时间调整为1微秒。
- 由于底层驱动没有处理ARP包,应此ARP表需要上层程序进行维护,所以在链接建立后需要使用命令"arp -s xxx ff-ff-ff-ff-ff-ff"来将对端的IP地址加入到ARP表中,其中xxx表示对端的内部IP地址。由于我们使用的是tun设备,应此MAC地址可以随意指定,这里将其MAC地址设置为广播地址。
四、完整代码
完整代码可到step13中找到
五、注意
通过反复安装驱动,可得到多张网卡,应此qtun在启动时若检测到当前机器含有多张qtun虚拟网卡,则会询问用户选择哪张网卡。
六、请关注
请关注http://blog.q-devel.com,您将第一时间的得到当前qtun的开发进展。