分类: LINUX
2010-04-17 14:08:24
@auther
nat源码追踪
ct->[].=
src:C_L:xx
dst:G_W:80
ct->[].
src: G_W:80
dst: C_L:xx
同时*ctinfo 设置为new
==========nf_conntrack_in调用结束。
然后进入nf_nat_in:
调用
ret
= (, , , , );
maniptype=
因为case :
所以调用 ret = (, , , , ct);
再由ret = (, , , , ->.);执行target.即会执行
进而:
return (ct, &mr->[0], );
之后
int = !(ct-> & );
为1;
之后调用:
(&curr_tuple,
&ct->[].);
取出 curr_tuple:
src:C_L:xx
dst:G_W:80
之后进入:
(&, &curr_tuple, , ct, );
此函数中,执行:
/* 2) Select the least-used / combination in the given
range. */
* = *;
(, , ct, );
用于在range中选择一个最少使用的ip由于maintype=DST,所以
= &->..;
最后* = ->;完成对dst.u3.ip的改变。
find_best返回后,new_tuple=
src:C_L:xx
dst:S_L:80
;回到后,会执行:
if (!(&, &curr_tuple)) {
struct ;
/*
Alter conntrack table so will recognize replies. */
(&reply, &);
(ct, &reply);
/*
Non-atomic: we own this at the moment. */
if ( == )
ct-> |= ;
else
ct-> |= ;
}
因为new_tuple在get_unique_tuple中已然改变,所以会进入if分支。invert之后,reply.dir=REPLY。
然后进入(ct, &reply)会执行:
ct->[]. = *;
reply=
src:S_L:80
dst:C_L:xx
注意,这个reply是在做了DNAT而没有做NAT的期望reply..
到此:
ct->tuplehash[IP_CT_DIR_ORIGNAL].tuple=
src:C_L:xx
dst:G_W:80
ct->tuplehash[IP_CT_DIR_REPLY].tuple=
src:S_L:80
dst:C_L:xx
之后由于have_to_hash为1,所以,进入
/*
Place in source hash if this is the first time. */
if () {
unsigned int ;
= (&ct->[].);
(&);
/*
nf_conntrack_alter_reply might re-allocate exntension aera */
nat = (ct);
nat->ct = ct;
hlist_add_head_rcu(&nat->,
&net->.[]);
(&);
}
对源地址端口进行hash,然后添加到net->ipv4.nat_bysource.
然后执行:
if ( == )
(, &ct->);
至此完成对tuple的dnat,并且设置了ct->status为。并且设置了
然后一层层返回到nf_nat_fn进入
return (ct, ctinfo, , );
这个函数的主要目的就是把数据改变写到skb里面。
先找出dir,mtype
enum = ();
unsigned long ;
enum = ();
当然,dir=ORIGNAL,mtype=DST.所以:
statusbit = ;
所以进入:
if (ct-> & statusbit) {
struct ;
/*
We are aiming to look like inverse of other direction. */
(&target, &ct->[!dir].);
if (!(target.., , 0, &target, ))
return ;
}
对ct->[!dir].进行invert,而dir=ORIG,得
target
=invert(ct->tuplehash[REPLY].tuple)=
src:C_L:xx
dst:S_L:80
然后又:
-> = target->..;
}
进入else分支:
把ip的目标地址换成了target->dst.u3.ip即S_L
于是数据包的目标地址,目标ip都替换;内容如下:
src:C_L:xx
dst:S_L:80
ctinfo
ct->status , ,
ct->tuplehash[IP_CT_DIR_ORIGNAL].tuple=
src:C_L:xx
dst:G_W:80
ct->tuplehash[IP_CT_DIR_REPLY].tuple=
src:S_L:80
dst:C_L:xx
hlist_add_head_rcu(&nat->, &net->.[]);
由于目的地址为S_L:80,所以当数据包传递给上层时,会被路由到G_L所在端口。
然后进入挂钩点:
,。
于图1.进入,,再进入,
同样进入:
ret = (, , , , );
于是:
enum = ();
=0,即maniptype=,
同样,由于连接状态没有改变:
case : 现样由于src未曾初始化,所以进入:
ret
= (, , , , ct);
进入ret = (, , ,
, ->.);
进而调用SNAT的target;
进入:
return (ct, &mr->[0], );
然后进入了nf_nat_setup_info;
int = !(ct-> & );
由于之前设置了,所以
得到have_to_hash=0;
(&curr_tuple,
&ct->[].);
curr_tuple=:
src: C_L:xx
dst:
S_L:80
然后:
(&, &curr_tuple, , ct, );
----- -- - >
进而调用:
(, , ct, );
if ( == )
= &->..;
* = ->;
range->minip=G_W;
执行之后
new_tuple:
src:G_W:xx
dst:S_L:80
所以,回到中,
if (!(&, &curr_tuple)) {
struct ;
/* Alter conntrack table so will recognize replies. */
(&reply, &);
(ct, &reply);
/* Non-atomic: we own this at the moment. */
if ( == )
ct-> |= ;
else
ct-> |= ;
}
reply=:
src:S_L:80
dst:G_W:xx
设置ct-> |= ;表示这个为SNAT。
最后
(, &ct->);
。
回到nf_nat_fn
进入
return (ct, ctinfo, , );
enum = ();
enum = ();
dir为ORIG,mtype为SRC。
所以:
if ( == )
statusbit = ;
ct->status设置了
进入执行,替换数据包的源。
& statusbit)
然后结束执行。
钩子执行完毕。
于是进入,
调用
return ();
if
( && != &) {
if (!() && !())
= ();
然后,在__nf_conntrack_confirm中:
hlist_nulls_del_rcu(&ct->[].);
hash = (&ct->[].);
repl_hash = (&ct->[].);
(ct, hash, repl_hash);
将skb中保存的ct->tuplehash分别添加到net->ct.hash,完成confirmed;
(, &ct->);
ctinfo
ct->status
, ,
ct->tuplehash[IP_CT_DIR_ORIGNAL].tuple=
src:C_L:xx
dst:G_W:80
ct->tuplehash[IP_CT_DIR_REPLY].tuple=
src:S_L:80
dst:G_W:xx