2.6.17中ip_nat_info结构的变化

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn

1. 前言
struct ip_nat_info结构是连接结构struct ip_conntrack在NAT功能打开的情况下的直接组成部分,非指针,在2.4和2.6.17中差异变化较大,本文分析其变化后的使用情况。
以下代码版本2.4的是2.4.26,2.6的是2.6.17.11。
2. 结构变化
/* include/linux/netfilter_ipv4/ip_nat.h */
2.4.26:
/* The structure embedded in the conntrack structure. */
struct ip_nat_info
{
/* Set to zero when conntrack created: bitmask of maniptypes */
int initialized;

unsigned int num_manips;

/* Manipulations to be done on this conntrack. */
struct ip_nat_info_manip manips[IP_NAT_MAX_MANIPS];

/* The mapping type which created us (NULL for null mapping). */
const struct ip_nat_mapping_type *mtype;

struct ip_nat_hash bysource, byipsproto;
/* Helper (NULL if none). */
struct ip_nat_helper *helper;
struct ip_nat_seq seq[IP_CT_DIR_MAX];
};

2.6.17.11:
struct ip_nat_info
{
struct list_head bysource;
struct ip_nat_seq seq[IP_CT_DIR_MAX];
};
两个结构差异巨大,但明显新结构大小比以前的小了很多。
3. 差异分析
3.1 initialized
在新结构中已经没有了initalized参数,该参数原先是用来检测数据包是否进行过某类NAT操作了,包括(SNAT、DNAT),新版中没有了此参数,但提供了以下函数来完成同样的功能,也就是实际上这些信息已经包含在连接结构的status参数中:
/* include/linux/netfilter_ipv4/ip_conntrack.h */
static inline int ip_nat_initialized(struct ip_conntrack *conntrack,
enum ip_nat_manip_type manip)
{
if (manip == IP_NAT_MANIP_SRC)
return test_bit(IPS_SRC_NAT_DONE_BIT, &conntrack->status);
return test_bit(IPS_DST_NAT_DONE_BIT, &conntrack->status);
}

3.2 bysource
bysource既是ip_nat_info结构中的参数,它其实只是用于挂接链表;同名的还有一个静态的全局变量,是个动态分配HASH数组指针,数组中每个元素表示一个连接链表,先看看这个参数记录的是什么样的连接:
分配:
/* net/ipv/netfilter/ip_nat_core.c */
static int __init ip_nat_init(void)
{
......
bysource = vmalloc(sizeof(struct list_head) * ip_nat_htable_size);
if (!bysource)
return -ENOMEM;
......
bysource这个数组大小和连接HASH数组大小是一样的

元素增加:
unsigned int
ip_nat_setup_info(struct ip_conntrack *conntrack,
const struct ip_nat_range *range,
unsigned int hooknum)
{
......
int have_to_hash = !(conntrack->status & IPS_NAT_DONE_MASK);
......
if (have_to_hash) {
unsigned int srchash
= hash_by_src(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
.tuple);
write_lock_bh(&ip_nat_lock);
list_add(&info->bysource, &bysource[srchash]);
write_unlock_bh(&ip_nat_lock);
}
......
也就是说加入此HASH数组中的连接是那些还没有进行任何NAT设置(包括SNAT和DNAT)的连接,也就是新连接,因此这个HASH数组中的元素个数和连接数应该是相同的,但不占用新的内存。
而struct ip_nat_info中的bysource参数就是链表的连接指针。

元素删除:
链表元素的删除是由以下函数完成的:
static void ip_nat_cleanup_conntrack(struct ip_conntrack *conn)
{
if (!(conn->status & IPS_NAT_DONE_MASK))
return;
write_lock_bh(&ip_nat_lock);
list_del(&conn->nat.info.bysource);
write_unlock_bh(&ip_nat_lock);
}
这个函数实际就是ip_conntrack_destroyed:
ip_conntrack_destroyed = &ip_nat_cleanup_conntrack;
而ip_conntrack_destroyed函数又是在destroy_conntrack(struct nf_conntrack *nfct)时调用的,也就是在连接删除的时候才删除。
3.3 manips
2.6.17中一个最明显的变化就是struct ip_nat_info_manip结构消失了,struct ip_nat_info中也没有了相应2.4中的结构参数:struct ip_nat_info_manip manips[IP_NAT_MAX_MANIPS]。2.4中这些参数是用来描述进行NAT的操作类型以及变化前后的IP地址信息和传输层参数信息等。现在既然在2.6.17中没有了这个参数,netfilter又是如何查找NAT修改后信息的呢?这需要从函数ip_nat_setup_info()说起。

因为无论是SNAT还是DNAT规则都要调用ip_nat_setup_info()函数
unsigned int
ip_nat_setup_info(struct ip_conntrack *conntrack,
const struct ip_nat_range *range,
unsigned int hooknum)
{
struct ip_conntrack_tuple curr_tuple, new_tuple;
struct ip_nat_info *info = &conntrack->nat.info;
int have_to_hash = !(conntrack->status & IPS_NAT_DONE_MASK);
enum ip_nat_manip_type maniptype = HOOK2MANIP(hooknum);
IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING
|| hooknum == NF_IP_POST_ROUTING
|| hooknum == NF_IP_LOCAL_IN
|| hooknum == NF_IP_LOCAL_OUT);
BUG_ON(ip_nat_initialized(conntrack, maniptype));
/* What we've got will look like inverse of reply. Normally
this is what is in the conntrack, except for prior
manipulations (future optimization: if num_manips == 0,
orig_tp =
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple) */
// 通过连接反方向的tuple获取连接正方向的tuple值存到curr_tuple中,
// 也就是NAT转换前的tuple
invert_tuplepr(&curr_tuple,
&conntrack->tuplehash[IP_CT_DIR_REPLY].tuple);
// 获取地址转换后的新的tuple值到new_tuple中,已经包括了传输层上的转换
get_unique_tuple(&new_tuple, &curr_tuple, range, conntrack, maniptype);
// 检查转换前后的tuple值是否相同,new_tuple是NAT后的新的原始方向的tuple
if (!ip_ct_tuple_equal(&new_tuple, &curr_tuple)) {
// 不同,进行NAT转换
struct ip_conntrack_tuple reply;
/* Alter conntrack table so will recognize replies. */
// 获取转换后的连接响应方向的tuple值到reply中
invert_tuplepr(&reply, &new_tuple);
// 修改连接中的响应方向的tuple值
// 即conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = reply
ip_conntrack_alter_reply(conntrack, &reply);
/* Non-atomic: we own this at the moment. */
// 设置NAT操作标志
if (maniptype == IP_NAT_MANIP_SRC)
conntrack->status |= IPS_SRC_NAT;
else
conntrack->status |= IPS_DST_NAT;
}
/* Place in source hash if this is the first time. */
// 连接到基于起始方向源IP的HASH链表中
if (have_to_hash) {
unsigned int srchash
= hash_by_src(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
.tuple);
write_lock_bh(&ip_nat_lock);
list_add(&info->bysource, &bysource[srchash]);
write_unlock_bh(&ip_nat_lock);
}
// 在连接的状态值中设置源或目的NAT完成标志
/* It's done. */
if (maniptype == IP_NAT_MANIP_DST)
set_bit(IPS_DST_NAT_DONE_BIT, &conntrack->status);
else
set_bit(IPS_SRC_NAT_DONE_BIT, &conntrack->status);
return NF_ACCEPT;
}
由此可见,连接中tuple值中记录了转换前后的参数,因此在进行NAT修改的函数ip_nat_packet()函数中就直接利用tuple 中的值进行修改了,不需要再用ip_nat_info_manip结构记录转换前后的地址端口信息。优点是减少了内存消耗,也比较直观。
/* Do packet manipulations according to ip_nat_setup_info. */
unsigned int ip_nat_packet(struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo,
unsigned int hooknum,
struct sk_buff **pskb)
{
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
unsigned long statusbit;
enum ip_nat_manip_type mtype = HOOK2MANIP(hooknum);
if (mtype == IP_NAT_MANIP_SRC)
statusbit = IPS_SRC_NAT;
else
statusbit = IPS_DST_NAT;
/* Invert if this is reply dir. */
if (dir == IP_CT_DIR_REPLY)
statusbit ^= IPS_NAT_MASK;
/* Non-atomic: these bits don't change. */
if (ct->status & statusbit) {
struct ip_conntrack_tuple target;
/* We are aiming to look like inverse of other direction. */
// 根据当前数据的反方向tuple获取转换后的地址端口的tuple信息到target中
invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);
// 根据target中信息修改当前包中的信息
if (!manip_pkt(target.dst.protonum, pskb, 0, &target, mtype))
return NF_DROP;
}
return NF_ACCEPT;
}

3.4 helper
在2.6.17中struct ip_nat_helper整个也被取消,因此也不会包括了。

3.5 seq
seq参数算是唯一NAT价值的保留参数,和原来一样,也是在记录连接的TCP序列号的变化情况的。

4. 结论
struct ip_nat_info的变化表示了在2.6.17.11中NAT的转换信息已经不再用专门的数据进行保存,而是完全根据连接的tuple值进行NAT。

发表于: 2006-09-11,修改于: 2006-09-11 08:48,已浏览3068次,有评论5条 推荐 投诉
网友: 本站网友 时间:2007-03-07 10:37:53 IP地址:218.5.3.★


因为我没找到2.6的代码,所以想请教一个问题:

在2.6中是通过conntrack节点来完成大部分操作的,特别是status域.在你上面提到的应该多了IPS_SRC_NAT_DONE_BIT和IPS_DST_NAT_DONE_BIT宏,但你另外一篇介绍2.4和2.6的不同的时候,并没有说这些有变化?


网友: yfydz 时间:2007-03-08 09:08:52 IP地址:218.247.216.★


以前那篇是针对2.6.8.1的,那时和2.4的还差不多,2.6.1*后才改的


网友: 本站网友 时间:2008-03-05 23:10:22 IP地址:121.34.72.★


分析不错,不过我还是对中间那些转换有疑问,为什么看到代码中老是通过取得方向tuple的反转tuple,而不是直接使用该ct的原始IPC_DIR_ORIG呢?

还有,比如这种:

invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);

为什么要这样去呢?直接取得该取得该ct的对应方向的tuple不就可以了吗?

不知道为什么要反转来反转去的,脑袋都搞晕了。。。。


网友: 本站网友 时间:2008-03-05 23:10:30 IP地址:121.34.72.★


分析不错,不过我还是对中间那些转换有疑问,为什么看到代码中老是通过取得方向tuple的反转tuple,而不是直接使用该ct的原始IPC_DIR_ORIG呢?

还有,比如这种:

invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);

为什么要这样去呢?直接取得该取得该ct的对应方向的tuple不就可以了吗?

不知道为什么要反转来反转去的,脑袋都搞晕了。。。。


网友: yfydz 时间:2008-03-06 20:26:59 IP地址:58.31.246.★


因为NAT情况下反向tuple的IP和正向的不一定相同
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值