分类: LINUX
2010-04-17 12:30:28
然后数据包到达G_W所在端口。进入NF_IP_PREROUTING。如图1所示,进入ipv4_conntrack_in钩子函数。
return (dev_net(), , , );
进而进入nf_conntrack_in函数。
进而
ct = (net, , , , protonum,
l3proto, l4proto, &, &ctinfo);
在resolve_normal_ct函数中,调用
nf_ct_get_tuple(, (),
, , protonum, &, l3proto,
l4proto))
取得tuple信息。
然后查找是否已经在net->ct.hash中?
h = nf_conntrack_find_get(net, &);
若没有找到,则
h = (net, &, l3proto, l4proto, , );
分配一个。并添加到net->ct.unconfirmed中。
然后由于(h) == ,所以*ctinfo = ;
把这些信息保存到skb数据包中:
-> = &ct->;
-> = *ctinfo;
。
回到nf_conntrack_in中,
ret = l4proto->(ct, , , ctinfo, , );
由于是UDP,所以,udp_packet(),它做的工作仅仅是。
之后,由于setreply在resolve_nomal_ct中设置为0,所以if语句不会执行。
ct->[].=
src:C_W:xx
dst:G_W:80
ct->[].
src: G_W:80
dst: C_W:xx
==========nf_conntrack_in调用结束。
然后进入nf_nat_in:
先取出了目的地址,然后调用:
ret
= (, , , , );
由于是NF_IP_PREROUTING,所以以下结果:
enum = ();
maniptype=1;即
然后取出ct,ctinfo
ct = (, &ctinfo);
尝试取出nat
= (ct);
如果不成功,则添加一个nat扩展。
然后对ctinfo进行判断:
case :
/*
Seen it before? This can happen for
loopback, retrans,
or local
packets.. */
if (!(ct, )) {
unsigned int ;
if ( == )
/*
LOCAL_IN hook doesn't have a chain! */
ret = (ct, );
else
ret = (, , ,
,
ct);
if (ret != ) {
return ret;
}
没有initialized,并且,hooknum==NF_INET_PREROUTING,所以,进入else,执行ret = (, , , ,ct);
然后此函数执行
ret = (, , , , ->.);
由netfilter来执行target.
由于是DNAT,所以会执行
进而:
return (ct, &mr->[0], );
之后
int = !(ct-> & );
为1;
之后调用:
(&curr_tuple,
&ct->[].);
取出ct->tuplehash[IP_CT_DIR_ORIGNAL]?????
src:C_W:xx
dst:G_W:80
notice
curr_tuple->dir=ORIG;
之后进入:
(&, &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_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-> |= ;
}
因为new_tuple在get_unique_tuple中已然改变,所以会进入if分支。invert之后,reply.dir=REPLY。
然后进入(ct, &reply)会执行:
ct->[]. = *;
reply=
src:S_L:80
dst:C_W:xx
proto->(, , , ct);
即udp_unique_tuple去完成对端口的改变
到此:
ct->tuplehash[IP_CT_DIR_ORIGNAL].tuple=
src:C_W:xx
dst:G_W:80
ct->tuplehash[IP_CT_DIR_REPLY].tuple=
src:S_L:80
dst:C_W: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_W:xx
dst:S_L:80
然后进入manip_pkt,
p = (proto);
if (!p->(, , target, ))
根据协议号来执行p->manip_pkt,即udp_manip_pkt
if ( == )
进入else分支:
else {
/* Get rid of dst ip and dst pt */
= ->;
= ->..;
= ->.u..;
= &->;
}
最后: * = ;
即把数据包的upd目标端口替换成tuple->dst.u.udp.port.
然后又:
if ( == ) {
(&->, ->, target->..);
-> = target->..;
} else {
(&->, ->, target->..);
-> = target->..;
}
进入else分支:
把ip的目标地址换成了target->dst.u3.ip即S_L
于是数据包的目标地址,目标ip都替换;内容如下:
src:C_W:xx
dst:S_L:80
ctinfo
ct->status , ,
ct->tuplehash[IP_CT_DIR_ORIGNAL].tuple=
src:C_W:xx
dst:G_W:80
ct->tuplehash[IP_CT_DIR_REPLY].tuple=
src:S_L:80
dst:C_W:xx
hlist_add_head_rcu(&nat->, &net->.[]);