int hlen /* IP 头的长度 */
)
返回值: 为 FALSE时表示报文处理正常,协议栈可继续转发或处理
为TRUE 时指示系统丢弃该报文
由于无法找到协议栈输出报文的钩子,我们打算把输出报文 NAT 转换放在 _ipFilterHook 的 LAN 口钩子中处理,即在 LAN口报文进入协议栈之前就更改源IP和端口地址。但这样做的缺点是:此时系统还没有检索过该报文的路由目的接口,需要人为增加查找路由表算法,当发现是发往指定 WAN 口时再行转换,处理的效率较低。
为充分利用协议栈的报文路由处理功能,决定采用更改协议栈代码的方式,在 ip_output 函数适当位置处人为增加一个钩子 _func_natOutput,重新编译 vxworks 协议栈库函数。 这样,NAT 模块初始化时即可将NAT输出报文处理函数绑定至该钩子。函数的输入输出关系格式如下:
int NatOutput
(
struct mbuf** m0, /* 数据报文地址 */
struct ip **ip, /* IP 头部地址 */
struct in_ifaddr* ia /* 报文目的接口 */
)
返回值: OK 或 ERROR (ERROR 时将丢弃并释放该报文)
7.3 NAT模块主要算法
7.3.1 NAT 端口地址转换HASH算法
NAPT 转换表查找算法可分为按端口查找和按地址查找两种。在实际的 ADSL接入环境中,局端很少会为一个连接分配多个 IP,因此我们采用按端口查找的算法来简单实现。
1) session 结构数组的初始化
NAT初始化时根据系统支持的最大转换数目建立一个按端口分布的 nat_session结构数组(nat_session 节点的定义见7.2.1), 同时建立一个指向nat_session 的空 hash 表。Hash节点结构如下:
typedef struct nat_hash_bucket_
{
struct nat_hash_bucket_ *next; //指向下一个节点
struct nat_hash_bucket_ *prev; //指向前一个节点
unsigned nat_session *pSession; //指向nat链表中相应的节点
}nat_hash_bucket;
2) 新建 session
get_free_session 在session静态表(结构数组)中获得一个free session,并将其从Hash表中unlink出来。查找的依据为时间戳, 顺序遍历session表,直到找到第一个超时的session。若未找到,则覆盖当前指针指向的session。
填充该 session 结构,返回该session 所对应的端口号。根据 TCP/UDP 报文的源 IP 和源端口号计算出 hash 头,增加到 hash 头所对应的链表后。其中,hash 头的算法如下:
LOCAL UINT16 ipnat_hash_fn(ipaddrtype addr1,
ipaddrtype addr2,
UINT16 port1,
UINT16 port2)
{
UINT16 bucket;
bucket = addr1 >> 16;
bucket ^= addr1 & 0xffff;
bucket ^= port1;
bucket ^= addr2 >> 16;
bucket ^= addr2 & 0xffff;
bucket ^= port2;
bucket = bucket % IPNAT_HASHLEN;