NAT之windows实现二

中间驱动所做的事情:
中间驱动把从上层驱动传递下来的数据包发到应用程序,应用程序处理之后再负责发回到中间驱动,中间驱动再接着发到网卡
中间驱动接收从网卡传递上来的数据包,把它发送到应用层,应用程序处理之后再发回到中间驱动,中间驱动再接着投递给上层驱动。
NDIS中间驱动原理什么的,就不在这里赘述了,对驱动开发了解的人都能清楚。
这里比较关键一点是这些数据包的IO处理的一些算法,为了尽可能的提高IO效率。
首先,肯定是采用直接IO方式,就是应用层和用户层交换数据使用共享内存。
采用DeviceIoControl方式处理这些数据包,一共有4个命令,
1)中间驱动读底层网卡数据包,发送到应用层,2)应用层写回数据包到中间驱动,中间驱动接着投递给上层驱动,
3)中间驱动接收上层驱动的数据包,发到应用层,4)应用层写回数据包到中间驱动,中间驱动接着投递给网卡。

在1)中,驱动排队多个IRP,同时还开辟以内存池来缓存还没来得及处理的数据包,当接收到网卡数据包时候,
首先检查有无等待的IRP,如有,则复制数据包并完成IRP;如无,把数据包缓存到内存池中,等待有IRP请求到来。
在2)中,投递的数据包直接交给底层网卡,不复制数据包,直到底层网卡处理完成才返回IRP。
3),4)的处理跟1),2)的处理类似。

在应用层程序的IO线程中,采用如下方式处理:
开辟多个IO线程,每个线程都调用DeviceIoControl 阻塞接收数据包,
这样其实是多个线程同步接收数据包,效率是挺高的。
同时,提高IO线程的优先级,这点很重要,对于网络 IO,必须优先处理,这样才能提高数据包的吞吐效率。
把对驱动的应用层处理,封装成一个函数库,函数库提供回调函数,回调函数在每个IO线程里被调用。如下
int read_callback( char* data,int data_len, struct dev_t* dev);
int write_callback( char* data, int data_len, struct dev_t* dev);
IO线程通过回调函数的返回值来判断要不要再次把数据包投递给中间驱动。
这样每当有数据包到达,这些回调函数都会被调用。NAT的业务逻辑,就集中在这些回调函数中处理。
实际上,做到这一步之后,驱动的工作就已经完成,剩下的就是跟平台无关的NAT逻辑的处理。
如果是换做是其他系统,比如Linux,他内核中有netfilter,只需简单设置挂钩回调函数,也一样能抓去数据包,
可想而知windows总喜欢简单问题复杂化。

NAT要实现的功能包括:
1)一台机器有多个网卡,其中一个外网网卡,其他是子网网卡,各个子网能通过外网网卡连接internet。
2)实现PPPoE宽带拨号协议和一般的以太网网卡上网。
3)各个子网之间要能互相通讯(NAT必须要能正确路由),
4)实现DNS转发
5)实现DHCP功能
NAT处理中,必须考虑IP分片情况。

以上说的功能比较多,不过其实实现起来并不复杂,至少在一共一个月时间中,大部分时间是花在折腾中间驱动上,
剩下大概一周多点的时间处理NAT功能逻辑。

一首先要做的事情,提供一整套函数库来维护和管理端口映射表,这是基础,
数据结构的处理,是采用Linux内核的红黑树源代码(可见先前的文章提到的)来存储端口映射关系。
二提供一套跟跟中间驱动打交道的函数库。
三在回调函数中处理NAT。

提供两种类型的回调,一种是外网卡的读和写回调函数,
另一种是子网网卡的读和写回调函数。

在子网网卡的读回调中做如下处理:
a)首先如果是ARP协议,缓存IP-MAC关系,主要目的是为了在多个子网之间正确路由。
b)如果不是发给本网卡的,不处理;如果不是IP协议,不处理。NAT只需要处理三种IP封装协议:ICMP,TCP,UDP。
c)如果是发给本子网的数据包,判断是不是DNS数据包,如果不是不处理,如果是DNS数据包,接着下面的处理
d)判断是不是发给其他子网的数据包,如果是就投递给那个子网,同时通知IO线程不再往下传递数据包。
e)判断是不是发给本外网网卡自身的,如果是则发给他,不是则接着下面的处理,
f)判断是不是IP分片并且不是第一个分片,如果是则从原来第一个分片中找到相关信息,如果不是接着下面的处理。
g)使用映射表接口函数映射一个外网端口到内网IP-PORT的映射,如果不存在就创建这个映射。
h)修改数据包的源IP为外网网卡IP,修改源端口为外网端口,重新增量方式计算校验。
i)判断是不是IP分片,并且是第一个分片,如果是,则保留此分片相关信息。
j)如果是DNS数据包,修改目的IP为外网网卡对应的DNS服务器IP,重新计算校验。
k)最后通过外网网卡发送数据包,首先判断是不是PPPoE,如果是封装成PPPoE协议发送,否则按照以太网协议发送。
   并且通知IO线程不再往下传递数据包。

外网网卡读回调做如下处理:
a)如果 不是发给被网卡的,不处理,如果不是IP协议,不处理。
b)判断是不是IP分片并且不是第一个分片,如果是则从原来第一个分片中找到相关信息,如果不是接着下面的处理。
c)从映射表查找外网端口对应的内网信息,如没找到则不处理,找到接着下面的处理
d)修改目的IP为内网IP,修改目的端口为内网端口,重新计算校验
e)判断是不是IP分片,并且是第一个分片,如果是,则保留此分片相关信息。
f)最后通过内网网卡发送给相应机器,并且通知IO线程不再往下传递数据包。

基本上大致思路如上,其实还有许多细节处理。

NAT不单用在内网机器上外网的场所,
在大型服务器环境下,常常使用它来做IP层的负载均衡。至于如何实现,把我们通常做NAT的思路反过来理解就行了。
跟反向代理差不多的意思。
至此NAT功能大致介绍完,其实还有许多相关的东西,没有介绍清楚,

你可能感兴趣的:(驱动开发)