Linux连接跟踪源码分析报告.docx
- 文档编号:7053093
- 上传时间:2023-01-16
- 格式:DOCX
- 页数:40
- 大小:37.45KB
Linux连接跟踪源码分析报告.docx
《Linux连接跟踪源码分析报告.docx》由会员分享,可在线阅读,更多相关《Linux连接跟踪源码分析报告.docx(40页珍藏版)》请在冰豆网上搜索。
Linux连接跟踪源码分析报告
Linux连接跟踪源码分析
IPConnectiontracking
连接跟踪用来跟踪和记录连接状态,是netfilter的一部份,也是通过在hook点上注册相应的结构来工作的。
无论是发送,接收,还是转发的数据包,都要经过两个conntrack模块。
第一个conntrack点的优先级是最高的,所有数据包进入netfilter后都会首先被它处理,其作用是创建ip_conntrack结构。
而最后一个conntrack的优先级最低,总是在数据包离开netfilter之前做最后的处理,它的作用是将该数据包的连接跟踪结构添加到系统的连接状态表中
1.ip_conntarck结构ip_conntrack.h
内核中用一个ip_conntrack结构来描述一个连接的状态
structip_conntrack
{
/*nf_conntrack结构定义于include/linux/skbuff.h,Line89,其中包括一个计数器use和一个destroy函数。
计数器use对本连接记录的公开引用次数进行计数*/
structnf_conntrackct_general;
/*其中的IP_CT_DIR_MAX是一个枚举类型ip_conntrack_dir(位于include/linux/netfilter_ipv4/ip_conntrack_tuple.h,Line65)的第3个成员,从这个结构实例在源码中的使用看来,实际上这是定义了两个tuple多元组的hash表项tuplehash[IP_CT_DIR_ORIGINAL/0]和tuplehash[IP_CT_DIR_REPLY/1],利用两个不同方向的tuple定位一个连接,同时也可以方便地对ORIGINAL以及REPLY两个方向进行追溯*/
structip_conntrack_tuple_hashtuplehash[IP_CT_DIR_MAX];
/*这是一个位图,是一个状态域。
在实际的使用中,它通常与一个枚举类型ip_conntrack_status(位于include/linux/netfilter_ipv4/ip_conntrack.h,Line33)进行位运算来判断连接的状态。
其中主要的状态包括:
IPS_EXPECTED(_BIT),表示一个预期的连接
IPS_SEEN_REPLY(_BIT),表示一个双向的连接
IPS_ASSURED(_BIT),表示这个连接即使发生超时也不能提早被删除
IPS_CONFIRMED(_BIT),表示这个连接已经被确认(初始包已经发出)*/
unsignedlongstatus;
/*其类型timer_list位于include/linux/timer.h,Line11,其核心是一个处理函数。
这个成员表示当发生连接超时时,将调用此处理函数*/
structtimer_listtimeout;
/*所谓“预期的连接”的链表,其中存放的是我们所期望的其它相关连接*/
structlist_headsibling_list;
/*目前的预期连接数量*/
unsignedintexpecting;
/*结构ip_conntrack_expect位于ip_conntrack.h,这个结构用于将一个预期的连接分配给现有的连接,也就是说本连接是这个master的一个预期连接*/
structip_conntrack_expect*master;
/*helper模块。
这个结构定义于ip_conntrack_helper.h,这个模块提供了一个可以用于扩展Conntrack功能的接口。
经过连接跟踪HOOK的每个数据报都将被发给每个已经注册的helper模块(注册以及卸载函数分别为ip_conntrack_helper_register()以及ip_conntrack_helper_unregister(),分别位于ip_conntrack_core.c)。
这样我们就可以进行一些动态的连接管理了*/
structip_conntrack_helper*helper;
/*一系列的nf_ct_info类型(定义于include/linux/skbuff.h,Line92,实际上就是nf_conntrack结构)的结构,每个结构对应于某种状态的连接。
这一系列的结构会被sk_buff结构的nfct指针所引用,描述了所有与此连接有关系的数据报。
其状态由枚举类型ip_conntrack_info定义(位于include/linux/netfilter_ipv4/ip_conntrack.h,Line12)共有5个成员:
IP_CT_ESTABLISHED:
数据报属于已经完全建立的连接
IP_CT_RELATED:
数据报属于一个新的连接,但此连接与一个现有连接相关(预期连接);或者是ICMP错误
IP_CT_NEW:
数据报属于一个新的连接
IP_CT_IS_REPLY:
数据报属于一个连接的回复
IP_CT_NUMBER:
不同IP_CT类型的数量,这里为7,NEW仅存于一个方向上*/
structnf_ct_infoinfos[IP_CT_NUMBER];
/*为其他模块保留的部分*/
unionip_conntrack_protoproto;
unionip_conntrack_helphelp;
#ifdefCONFIG_IP_NF_NAT_NEEDED
struct{
structip_nat_infoinfo;
unionip_conntrack_nat_helphelp;
#ifdefined(CONFIG_IP_NF_TARGET_MASQUERADE)||\
defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE)
intmasq_index;
#endif
#ifdefined(CONFIG_IP_NF_RTSP)||defined(CONFIG_IP_NF_RTSP_MODULE)
structip_nat_rtsp_infortsp_info;
#endif
}nat;
#endif/*CONFIG_IP_NF_NAT_NEEDED*/
#ifdefined(CONFIG_IP_NF_CONNTRACK_MARK)
unsignedlongmark;
#endif
};
structip_conntrack_tuple_hash结构描述链表中的节点,这个数组包含“初始”和“应答”两个成员(tuplehash[IP_CT_DIR_ORIGINAL]和tuplehash[IP_CT_DIR_REPLY]),所以,当一个数据包进入连接跟踪模块后,先根据这个数据包的套接字对转换成一个“初始的”tuple,赋值给tuplehash[IP_CT_DIR_ORIGINAL],然后对这个数据包“取反”,计算出“应答”的tuple,赋值给tuplehash[IP_CT_DIR_REPLY],这样,一条完整的连接已经跃然纸上了。
enumip_conntrack_dir
{
IP_CT_DIR_ORIGINAL,
IP_CT_DIR_REPLY,
IP_CT_DIR_MAX
};
2.连接跟踪表
Netfilter用“来源地址/来源端口+目的地址/目的端口”,即一个“tuple”,来唯一标识一个连接。
用一张连接跟踪表来描述所有的连接状态,该表用了hash算法。
hash表用一个全局指针来描述(ip_conntrack_core.c)
structlist_head*ip_conntrack_hash;
表的大小,即hash节点的个数由ip_conntrack_htable_size全局变量决定,默认是根据内存计算出来的。
而每个hash节点又是一条链表的首部,所以,连接跟踪表就是一个由ip_conntrack_htable_size条链表构成的一个hash表,整个连接跟踪表大小使用全局变量ip_conntrack_max描述,与hash表的关系是ip_conntrack_max=8*ip_conntrack_htable_size。
链表的每个节点,都是一个ip_conntrack_tuple_hash结构:
structip_conntrack_tuple_hash
{
/*用来组织链表*/
structlist_headlist;
/*用来描述一个tuple*/
structip_conntrack_tupletuple;
/*this==&ctrack->tuplehash[DIRECTION(this)].*/
structip_conntrack*ctrack;
};
实际描述一个tuple的是ip_conntrack_tuple结构ip_conntrack_tuple.h
structip_conntrack_tuple
{
/*源*/
structip_conntrack_manipsrc;
/*Thesearethepartsofthetuplewhicharefixed.*/
struct{
/*目的地址*/
u_int32_tip;
union{
/*Addotherprotocolshere.*/
u_int64_tall;
struct{
u_int16_tport;
}tcp;
struct{
u_int16_tport;
}udp;
struct{
u_int8_ttype,code;
}icmp;
struct{
u_int16_tprotocol;
u_int8_tversion;
u_int32_tkey;
}gre;
struct{
u_int16_tspi;
}esp;
}u;
/*协议类型*/
u_int16_tprotonum;
}dst;
};
对于所有IP协议,协议类型、源地址、目的地址这三个参数是识别连接所必须的,具体到各个协议,就要提取出各协议的唯一特征数据,如TCP、UDP的源端口、目的端口,ICMP的ID、TYPE、CODE等值,这些值就是tuple结构要处理的数据。
各协议相关数据是以联合(union)形式定义在tuple结构中的,netfilter缺省支持TCP、UDP和ICMP协议,如果还要支持其他IP协议,如GRE、ESP、AH、SCTP等,需要在联合中添加相应的协议参数值。
ip_conntrack_manip和ip_conntrack_manip_protoip_conntrack_tuple.h
structip_conntrack_manip
{
u_int32_tip;
unionip_conntrack_manip_protou;
};
unionip_conntrack_manip_proto
{
/*Addotherprotocolshere.*/
u_int32_tall;
struct{
u_int16_tport;
}tcp;
struct{
u_int16_tport;
}udp;
struct{
u_int16_tid;
}icmp;
struct{
u_int32_tkey;
}gre;
struct{
u_int16_tspi;
}esp;
};
Netfilter将每一个数据包转换成tuple,再根据tuple计算出hash值,这样,就可以使用ip_conntrack_hash[hash_id]找到hash表中链表的入口,并组织链表;找到hash表中链表入口后,如果链表中不存在此“tuple”,则是一个新连接,就把tuple插入到链表的合适位置;两个节点tuple[ORIGINAL]和tuple[REPLY]虽然是分开的,在两个链表当中,但是如前所述,它们同时又被封装在ip_conntrack结构的tuplehash数组中
3. 连接跟踪初始化
初始化函数init()调用init_or_cleanup
(1)函数ip_conntrack_standalone.c
staticint__initinit(void)
{
returninit_or_cleanup
(1);
}
3.1init_or_cleanup()函数
intinit_or_cleanup函数,(ip_conntrack_standalone.c)参数为1则执行init,为0则执行clean,它主要做三件工作:
1. 调用ip_conntrack_init()初始化连接跟踪表的相关变量,见3.2
2. 初始化proc文件系统节点
3. 为连接跟踪注册hook
staticintinit_or_cleanup(intinit)
{
structproc_dir_entry*proc;
intret=0;
if(!
init)gotocleanup;
/*初始化连接跟踪的一些变量和数据结构,如连接跟踪表的大小,Hash表的大小等*/
ret=ip_conntrack_init();
if(ret<0)
gotocleanup_nothing;
/*初始化proc文件系统*/
proc=proc_net_create("ip_conntrack",0440,list_conntracks);
proc=proc_net_create("ip_clear_dnsconntrack",0644,clear_dns_conntracks);
if(!
proc)gotocleanup_init;
proc->owner=THIS_MODULE;
/*为连接跟踪注册hook,一共六个,所在的hook点、注册的hook函数和优先级分别如下(和最开始的图是一致的):
NF_IP_PRE_ROUTING:
ip_conntrack_defragNF_IP_PRI_CONNTRACK_DEFRAG
ip_conntrack_inNF_IP_PRI_CONNTRACK
NF_IP_LOCAL_OUT:
ip_conntrack_defragNF_IP_PRI_CONNTRACK_DEFRAG
ip_conntrack_localNF_IP_PRI_CONNTRACK
NF_IP_POST_ROUTING:
ip_refragNF_IP_PRI_LAST
NF_IP_LOCAL_IN:
ip_confirmNF_IP_PRI_LAST-1
优先级的顺序为:
NF_IP_PRI_FIRST(最高)
NF_IP_PRI_CONNTRACK_DEFRAG
NF_IP_PRI_CONNTRACK
NF_IP_PRI_MANGLE
NF_IP_PRI_NAT_DST
NF_IP_PRI_FILTER
NF_IP_PRI_NAT_SRC
NF_IP_PRI_LAST(最低)
我们知道,LOCAL_OUT和PRE_ROUTING点可以看作是netfilter的入口,而POST_ROUTING和LOCAL_IN可以看作是出口。
也就是说,在每个数据包刚一进入netfilter之后首先都会调用ip_conntrack_defrag做分片处理,紧接着就是对收到的和发出的数据包分别进行ip_conntrack_in和ip_conntrack_loacl(ip_conntrack_loacl里还是调用了ip_conntrack_in)。
而在数据包即将离开netfilter之前,会对进入主机的数据包进行ip_confirm处理,对发出的数据包进行ip_refrag处理(ip_refrag里也会调用ip_confirm)。
这就是整个连接跟踪模块在netfilter中的分布情况。
另外,我们分析的是2.6.8的内核,在linux2.6.12中,这个地方还增加了两个hook,分别是ip_conntrack_helper_out_ops和ip_conntrack_helper_in_ops。
将helper模块相关的处理提前了。
*/
ret=nf_register_hook(&ip_conntrack_defrag_ops);
if(ret<0){
printk("ip_conntrack:
can'tregisterpre-routingdefraghook.\n");
gotocleanup_proc;
}
ret=nf_register_hook(&ip_conntrack_defrag_local_out_ops);
if(ret<0){
printk("ip_conntrack:
can'tregisterlocal_outdefraghook.\n");
gotocleanup_defragops;
}
ret=nf_register_hook(&ip_conntrack_in_ops);
if(ret<0){
printk("ip_conntrack:
can'tregisterpre-routinghook.\n");
gotocleanup_defraglocalops;
}
ret=nf_register_hook(&ip_conntrack_local_out_ops);
if(ret<0){
printk("ip_conntrack:
can'tregisterlocalouthook.\n");
gotocleanup_inops;
}
ret=nf_register_hook(&ip_conntrack_out_ops);
if(ret<0){
printk("ip_conntrack:
can'tregisterpost-routinghook.\n");
gotocleanup_inandlocalops;
}
ret=nf_register_hook(&ip_conntrack_local_in_ops);
if(ret<0){
printk("ip_conntrack:
can'tregisterlocalinhook.\n");
gotocleanup_inoutandlocalops;
}
#ifdefCONFIG_SYSCTL
ip_ct_sysctl_header=register_sysctl_table(ip_ct_net_table,0);
if(ip_ct_sysctl_header==NULL){
printk("ip_conntrack:
can'tregistertosysctl.\n");
gotocleanup;
}
#endif
returnret;
cleanup:
#ifdefCONFIG_SYSCTL
unregister_sysctl_table(ip_ct_sysctl_header);
#endif
nf_unregister_hook(&ip_conntrack_local_in_ops);
cleanup_inoutandlocalops:
nf_unregister_hook(&ip_conntrack_out_ops);
cleanup_inandlocalops:
nf_unregister_hook(&ip_conntrack_local_out_ops);
cleanup_inops:
nf_unregister_hook(&ip_conntrack_in_ops);
cleanup_defraglocalops:
nf_unregister_hook(&ip_conntrack_defrag_local_out_ops);
cleanup_defragops:
nf_unregister_hook(&ip_conntrack_defrag_ops);
cleanup_proc:
proc_net_remove("ip_conntrack");
proc_net_remove("ip_clear_dnsconntrack");
cleanup_init:
ip_conntrack_cleanup();
cleanup_nothing:
returnret;
}
3.2ip_conntrack_init()函数
ip_conntrack_init函数(ip_conntrack_core.c)用于初始化连接跟踪的包括hash表相关参数在内一些重要的变量:
int__initip_conntrack_init(void)
{
unsignedinti;
intret;
/*Ideafromtcp.c:
use1/16384ofmemory.Oni386:
32MB
*machinehas256buckets.>=1GBmachineshave8192buckets.*/
/*如果指定hash表的大小则用制定值,否则根据内存计算*/
if(hashsize){
ip_conntrack_htable_size=hashsize;
}else{
ip_conntrack_htable_size
=(((num_physpages< /sizeof(structlist_head)); if(num_physpages>(1024*1024*1024/PAGE_SIZE)) ip_conntrack_htable_size=8192; if(ip_conntrack_htable_size<16) ip_conntrack_htable_size=16; } ip_conntrack_max=8*ip_conntrack_htable_size; #ifdefCONFIG_MIPS_BRCM ip_conntrack_max=0; #endif printk("ip_conntrackversion%s(%ubuckets,%dmax)" "-%Zdbytesperconntrack\n",IP_CONNTRACK_VERSION,
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 连接 跟踪 源码 分析 报告