加入收藏 | 设为首页 | 会员中心 | 我要投稿 云计算网_泰州站长网 (http://www.0523zz.com/)- 视觉智能、AI应用、CDN、行业物联网、智能数字人!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

Linux内核分析 - 网络[十七]:NetFilter之连接跟踪

发布时间:2016-01-23 17:21:43 所属栏目:Linux 来源:网络整理
导读:内核版本:2.6.34 前面章节介绍过Netfilter的框架,地址见: http://blog.csdn.net/qy532846454/article/details/6605592,本章节介绍的连接跟踪就是在Netfilte

l4_proto是根据报文中协议号来查找到的,这里是TCP 连接因此l4_proto对应于nf_conntrack_l4proto_tcp4;l4_proto->new()的作用在于设置TCP的状态,即ct- >proto.tcp.state,这个是TCP协议所特有的(TCP有11种状态的迁移图),这里只要知道刚创建时ct->proto.tcp.state会 被设置为TCP_CONNTRACK_NONE,最后将ct->tuplehash加入到了net->ct.unconfirmed,因为这个连接还是没有被确认的, 所以加入的是uncorfirmed链表。

这样,init_conntrack()创建后的连接跟踪情况如下(列出了关键的元素):

tuple  A_ip A_port B_ip B_port ORIG

repl_tuple B_ip B_port A_ip A_port REPLY

tcp.state  NONE

if (!nf_ct_invert_tuple(&repl_tuple, tuple, l3proto, l4proto)) {     
 pr_debug("Can't invert tuple.n");     
 return NULL;     
}     
ct = nf_conntrack_alloc(net, zone, tuple, &repl_tuple, GFP_ATOMIC);     
if (IS_ERR(ct)) {     
 pr_debug("Can't allocate conntrack.n");     
 return (struct nf_conntrack_tuple_hash *)ct;     
}     
         
if (!l4proto->new(ct, skb, dataoff)) {     
 nf_conntrack_free(ct);     
 pr_debug("init conntrack: can't track with proto modulen");     
 return NULL;     
}     
…….     
/* Overload tuple linked list to put us in unconfirmed list. */ 
hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,     
         &net->ct.unconfirmed);

tcp_packet()

函数的作用在于通过连接当前的状态,到达的新报文,得到连接新的状态并进行更新,其实就是一次查询 ,输入是方向+报文信息+旧状态,输出是新状态,因此可以用查询表来简单实现,tcp_conntracks[2][6][TCP_CONNTRACK_MAX] 就是这张查询表,它在nf_conntrack_proto_tcp.c中定义。第一维[2]代表连接的方向,第二维[6]代表6种当前报文所带的信息( 根椐TCP报头中的标志位),第三维[TCP_CONNTRACK_MAX]代表旧状态,而每个元素存储的是新状态。

Linux内核分析 - 网络[十七]:NetFilter之连接跟踪

下面代码完成了表查 询,old_state是旧状态,dir是当前报文的方向(它在resolve_normal_ct中赋值,简单来说是最初的发起方向作为正向),index 是当前报文的信息,get_conntrack_index()函数代码也贴在下面,函数很简单,通过TCP报头的标志位得到报文信息。在此例中 ,收到SYN,old_state是NONE,dir是ORIG,index是TCP_SYN_SET,最终的结果new_state通过查看tcp_conntracks就可以得到了 ,它在nf_conntrack_proto_tcp.c中定义,结果可以自行对照查看,本例中查询的结果应为TCP_CONNTRACK_SYN_SENT。

然后 switch-case语句根据新状态new_state进行其它必要的设置。

old_state = ct->proto.tcp.state;     
dir = CTINFO2DIR(ctinfo);     
index = get_conntrack_index(th);     
new_state = tcp_conntracks[dir][index][old_state];     
switch (new_state) {     
case TCP_CONNTRACK_SYN_SENT:     
 if (old_state < TCP_CONNTRACK_TIME_WAIT)     
  break;     
……     
}
static unsigned int get_conntrack_index(const struct tcphdr *tcph)     
{     
 if (tcph->rst) return TCP_RST_SET;     
 else if (tcph->syn) return (tcph->ack ? TCP_SYNACK_SET : TCP_SYN_SET);     
 else if (tcph->fin) return TCP_FIN_SET;     
 else if (tcph->ack) return TCP_ACK_SET;     
 else return TCP_NONE_SET;     
}

勾子点LOCAL_IN [ipv4_confirm]

ipv4_confirm() -> nf_conntrack_confirm() -> __nf_conntrack_confirm()

这里的ct是之前在PRE_ROUTING中创建的连接跟踪,然后调用hash_conntrack()取得连接跟踪ct的 正向和反向tuple的哈希值hash和repl_hash;报文到达这里表示被接收,即可以被确认,将它从net->ct.unconfirmed链中删 除(PRE_ROUTEING时插入的,那时还是未确认的),然后置ct->status位IPS_CONFIRMED_BIT,表示它已被确认,同时将tuple 和repl_tuple加入net->ct.hash,这一步是由__nf_conntrack_hash_insert()完成的,net->ct.hash中存储所有的连接跟 踪。

zone = nf_ct_zone(ct);     
hash = hash_conntrack(net, zone, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);     
repl_hash = hash_conntrack(net, zone, &ct->tuplehash[IP_CT_DIR_REPLY].tuple);     
/* Remove from unconfirmed list */ 
hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);     
……     
set_bit(IPS_CONFIRMED_BIT, &ct->status);     
……     
__nf_conntrack_hash_insert(ct, hash, repl_hash);     
……

至此,接收SYN报文完成,生成了一条新的连接记录ct,状态为TCP_CONNTRACK_SYN_SENT,status设置了 IPS_CONFIRMED_BIT位。

2. 发送SYN+ACK报文 [local_out -> post_routing]

勾子点LOCAL_OUT  [ipv4_conntrack_local]

ipv4_conntrack_local() -> nf_conntrack_in()

(编辑:云计算网_泰州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读