Network Address Translate(NAT)Windows平台(一)

 

在windows平台下,利用NDIS中间驱动实现NAT。在其他平台实现NAT,也是同样的原理,唯一不同的是对网卡数据包的读取和写入操作是与平台相关的。

而叫人蛋疼的是,对windows 平台下的直接操作网卡的实现是各种平台中最复杂的。

比如Linux下,内核态下设置简单的钩子函数即可拦截网卡数据包,vxworks也跟Linux类似的机制。
而windows平台下,NDIS中间驱动真是一个复杂的东西,
基本上你花在驱动本身的处理上的时间,可能比实现NAT逻辑还多。
虽然Win7之后,有个比中间驱动更简单的filter实现网卡数据包的拦截,
但是:一它不能在XP及以下的系统中使用,二实际上它也不算简单,
三为了实现跨多个版本windows,必须维护两套驱动代码,
所以最后还是打算统一使用中间驱动来实现。

这里先介绍一种纯应用层实现NAT的办法,借此也可以展示vmware的NAT的实现原理。
这也是我先前打算用NDIS协议驱动实现NAT失败之后,研究vmware的NAT之后想到的一种办法,
虽然我没亲自实现它,但是从原理上讲,是肯定行得通的。

之所以用协议驱动实现NAT失败,
原因是这样:协议驱动无法拦截数据包,根据NAT的特点,
每当转发一个数据包,会占用系统一个端口,而系统上层的TCP/IP栈并不认识这个端口,因此会被自动复位。
就是每当internet回复一个数据包,系统发现目标端口是未知端口,会发送复位包给internet。
结果造成数据包不能正确处理。
因此必须对这种情况拦截处理,这也就是协议驱动失败的原因。
这种现象只发生在外网网卡,内网网卡不会发生这种现象,这个原因就不多说,有兴趣可以自己研究。
其实只要找到一种办法,拦截上层TCP/IP栈对未知端口的处理,
但是很遗憾,到目前为止,一直没找到一个有效的办法。
但是如果稍微换一种思路,对内网网卡任然协议协议驱动抓包,
但是如果对内网发到internet的数据包,在应用层使用socket套接字主动连接到internet的机器,岂不是一种办法。
应用层实现NAT就是这个原理。
如果你不懂NDIS协议驱动,可是使用WinPcap开发包,或者使用windows的原始套接字来抓取内网卡数据包。
vmwrae虚拟机中有个服务程序vmnat.exe,就是处理NAT转发。
其实虚拟机里的操作系统每个到internet的连接, vmnat.exe都会创建相应的套接字连接,
有兴趣的朋友,可以亲自验证一下,从而对vmware的NAT上网机制更加了解。

这里使用NDIS中间驱动,而实际上,我并不是在驱动层处理NAT逻辑,
采用的是把数据包通过中间驱动发送到应用层,由应用层负责拦截处理数据包,
处理之后的数据包再发回到中间驱动,再由中间驱动负责发送。
这个办法不是所谓的用户层比如SPI等拦截数据包的办法。
这里的应用层截包,说的是在驱动层截包,然后发送到应用层程序处理。

之所以采用这种架构,主要是因为这样更加灵活,开发周期更短。
关键是灵活,你可以在应用层干任何只有驱动层才能干的坏事,而且付出的代价更低。
比如实现多功能个人防火墙,VPN,NAT, 协议转换等等。
大家可能担心的是数据包在用户层内核层来回的跑上一圈,无端的增加IO,效率可能不会太高。
当初我也有这方面的担心,但是等开发完,测试之后,效果相当理想,
基本上在100MBps的网卡上,效率能达到原始状态的90%以上或者更高,
当然要达到100%的肯定不行的,毕竟有IO损失。
我家宽带30M左右,基本上用它实现NAT之后,平均下载速度2M-3M字节之间,速度看不出来损失多少。
对这个测试结果至少我是挺满意的。
当然这里有一点很重要:必须提高应用程序的IO线程的优先级,否则达不到预期效果。

总得来说这种架构,对100Mbps以下的网卡是可行的,对应高带宽并不合适,
其实高带宽下,使用软件方式实现NAT,使用windows平台基本上都算是一种错误选择。

NDIS中间驱动的开发原理,就不详细描述,网上或者专门将windows驱动开发的书,都有介绍,
WDK源码中的passthru例子就是一个中间驱动的经典例子。
因为一向讨厌借用别人的框架,所以开发中间驱动中,
都采用自己的开发架构,完全重写整个NDIS中间架构。
一个源文件实现初始化,一个源文件实现公用函数,一个源文件实现发送,一个源文件实现接收,
一个源文件实现参数查询,一个源文件实现跟用户层IO交互。

待续。。。

posted @ 2022-11-04 15:02  信易达  阅读(114)  评论(0编辑  收藏  举报