Linux连接跟踪源码分析.doc_第1页
Linux连接跟踪源码分析.doc_第2页
Linux连接跟踪源码分析.doc_第3页
Linux连接跟踪源码分析.doc_第4页
Linux连接跟踪源码分析.doc_第5页
已阅读5页,还剩29页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Linux连接跟踪源码分析IP Connection tracking 连接跟踪用来跟踪和记录连接状态,是netfilter的一部份,也是通过在hook点上注册相应的结构来工作的。无论是发送,接收,还是转发的数据包,都要经过两个conntrack模块。第一个conntrack点的优先级是最高的,所有数据包进入netfilter后都会首先被它处理,其作用是创建ip_conntrack结构。而最后一个conntrack的优先级最低,总是在数据包离开netfilter之前做最后的处理,它的作用是将该数据包的连接跟踪结构添加到系统的连接状态表中1. ip_conntarck结构 ip_conntrack.h内核中用一个ip_conntrack结构来描述一个连接的状态struct ip_conntrack/* nf_conntrack结构定义于include/linux/skbuff.h,Line89,其中包括一个计数器use和一个destroy函数。计数器use对本连接记录的公开引用次数进行计数 */struct nf_conntrack ct_general;/*其中的IP_CT_DIR_MAX是一个枚举类型ip_conntrack_dir(位于include/linux/netfilter_ipv4/ip_conntrack_tuple.h,Line65)的第3个成员,从这个结构实例在源码中的使用看来,实际上这是定义了两个tuple多元组的hash表项tuplehashIP_CT_DIR_ORIGINAL/0和tuplehashIP_CT_DIR_REPLY/1,利用两个不同方向的tuple定位一个连接,同时也可以方便地对ORIGINAL以及REPLY两个方向进行追溯*/struct ip_conntrack_tuple_hash tuplehashIP_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),表示这个连接已经被确认(初始包已经发出) */unsigned long status;/*其类型timer_list位于include/linux/timer.h,Line11,其核心是一个处理函数。这个成员表示当发生连接超时时,将调用此处理函数*/struct timer_list timeout;/*所谓“预期的连接”的链表,其中存放的是我们所期望的其它相关连接*/struct list_head sibling_list;/*目前的预期连接数量*/unsigned int expecting;/*结构ip_conntrack_expect位于ip_conntrack.h,这个结构用于将一个预期的连接分配给现有的连接,也就是说本连接是这个master的一个预期连接*/struct ip_conntrack_expect *master;/* helper模块。这个结构定义于ip_conntrack_helper.h,这个模块提供了一个可以用于扩展Conntrack功能的接口。经过连接跟踪HOOK的每个数据报都将被发给每个已经注册的helper模块(注册以及卸载函数分别为ip_conntrack_helper_register()以及ip_conntrack_helper_unregister(),分别位于ip_conntrack_core.c)。这样我们就可以进行一些动态的连接管理了*/struct ip_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仅存于一个方向上 */struct nf_ct_info infosIP_CT_NUMBER;/* 为其他模块保留的部分 */union ip_conntrack_proto proto;union ip_conntrack_help help;#ifdef CONFIG_IP_NF_NAT_NEEDEDstruct struct ip_nat_info info;union ip_conntrack_nat_help help;#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) | defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE)int masq_index;#endif#if defined(CONFIG_IP_NF_RTSP) | defined(CONFIG_IP_NF_RTSP_MODULE) struct ip_nat_rtsp_info rtsp_info;#endif nat;#endif /* CONFIG_IP_NF_NAT_NEEDED */#if defined(CONFIG_IP_NF_CONNTRACK_MARK)unsigned long mark;#endif;struct ip_conntrack_tuple_hash结构描述链表中的节点,这个数组包含“初始”和“应答”两个成员(tuplehashIP_CT_DIR_ORIGINAL和tuplehashIP_CT_DIR_REPLY),所以,当一个数据包进入连接跟踪模块后,先根据这个数据包的套接字对转换成一个“初始的”tuple,赋值给tuplehashIP_CT_DIR_ORIGINAL,然后对这个数据包“取反”,计算出“应答”的tuple,赋值给tuplehashIP_CT_DIR_REPLY,这样,一条完整的连接已经跃然纸上了。enum ip_conntrack_dirIP_CT_DIR_ORIGINAL,IP_CT_DIR_REPLY,IP_CT_DIR_MAX;2. 连接跟踪表Netfilter用“来源地址/来源端口+目的地址/目的端口”,即一个“tuple”,来唯一标识一个连接。用一张连接跟踪表来描述所有的连接状态,该表用了hash算法。hash表用一个全局指针来描述(ip_conntrack_core.c)struct list_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结构:struct ip_conntrack_tuple_hash/* 用来组织链表 */struct list_head list;/* 用来描述一个tuple */struct ip_conntrack_tuple tuple;/* this = &ctrack-tuplehashDIRECTION(this). */struct ip_conntrack *ctrack;实际描述一个tuple的是ip_conntrack_tuple结构 ip_conntrack_tuple.hstruct ip_conntrack_tuple/* 源 */struct ip_conntrack_manip src;/* These are the parts of the tuple which are fixed. */struct /* 目的地址 */u_int32_t ip;union /* Add other protocols here. */u_int64_t all;struct u_int16_t port; tcp;struct u_int16_t port; udp;struct u_int8_t type, code; icmp;struct u_int16_t protocol;u_int8_t version;u_int32_t key; gre;struct u_int16_t spi; esp; u;/* 协议类型 */u_int16_t protonum; 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_proto ip_conntrack_tuple.hstruct ip_conntrack_manipu_int32_t ip;union ip_conntrack_manip_proto u;union ip_conntrack_manip_proto/* Add other protocols here. */u_int32_t all;struct u_int16_t port; tcp;struct u_int16_t port; udp;struct u_int16_t id; icmp;struct u_int32_t key; gre;struct u_int16_t spi; esp;Netfilter将每一个数据包转换成tuple,再根据tuple计算出hash值,这样,就可以使用ip_conntrack_hashhash_id找到hash表中链表的入口,并组织链表;找到hash表中链表入口后,如果链表中不存在此“tuple”,则是一个新连接,就把tuple插入到链表的合适位置;两个节点tupleORIGINAL和tupleREPLY虽然是分开的,在两个链表当中,但是如前所述,它们同时又被封装在ip_conntrack结构的tuplehash数组中3. 连接跟踪初始化初始化函数init()调用init_or_cleanup(1)函数 ip_conntrack_standalone.cstatic int _init init(void)return init_or_cleanup(1);3.1 init_or_cleanup()函数int init_or_cleanup函数,(ip_conntrack_standalone.c)参数为1则执行init,为0则执行clean,它主要做三件工作:1 调用ip_conntrack_init()初始化连接跟踪表的相关变量,见3.2 2 初始化proc文件系统节点3 为连接跟踪注册hookstatic int init_or_cleanup(int init)struct proc_dir_entry *proc;int ret = 0;if (!init) goto cleanup;/* 初始化连接跟踪的一些变量和数据结构,如连接跟踪表的大小,Hash表的大小等 */ret = ip_conntrack_init();if (ret owner = THIS_MODULE;/* 为连接跟踪注册hook,一共六个,所在的hook点、注册的hook函数和优先级分别如下(和最开始的图是一致的): NF_IP_PRE_ROUTING:ip_conntrack_defrag NF_IP_PRI_CONNTRACK_DEFRAGip_conntrack_in NF_IP_PRI_CONNTRACKNF_IP_LOCAL_OUT:ip_conntrack_defrag NF_IP_PRI_CONNTRACK_DEFRAGip_conntrack_local NF_IP_PRI_CONNTRACKNF_IP_POST_ROUTING:ip_refrag NF_IP_PRI_LASTNF_IP_LOCAL_IN:ip_confirm NF_IP_PRI_LAST-1优先级的顺序为:NF_IP_PRI_FIRST (最高)NF_IP_PRI_CONNTRACK_DEFRAGNF_IP_PRI_CONNTRACK NF_IP_PRI_MANGLENF_IP_PRI_NAT_DSTNF_IP_PRI_FILTER NF_IP_PRI_NAT_SRCNF_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: cant register pre-routing defrag hook.n);goto cleanup_proc;ret = nf_register_hook(&ip_conntrack_defrag_local_out_ops);if (ret 0) printk(ip_conntrack: cant register local_out defrag hook.n);goto cleanup_defragops;ret = nf_register_hook(&ip_conntrack_in_ops);if (ret 0) printk(ip_conntrack: cant register pre-routing hook.n);goto cleanup_defraglocalops;ret = nf_register_hook(&ip_conntrack_local_out_ops);if (ret 0) printk(ip_conntrack: cant register local out hook.n);goto cleanup_inops;ret = nf_register_hook(&ip_conntrack_out_ops);if (ret 0) printk(ip_conntrack: cant register post-routing hook.n);goto cleanup_inandlocalops;ret = nf_register_hook(&ip_conntrack_local_in_ops);if (ret = 1GB machines have 8192 buckets. */* 如果指定hash表的大小则用制定值,否则根据内存计算 */ if (hashsize) ip_conntrack_htable_size = hashsize; else ip_conntrack_htable_size= (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;#ifdef CONFIG_MIPS_BRCMip_conntrack_max=0;#endifprintk(ip_conntrack version %s (%u buckets, %d max) - %Zd bytes per conntrackn, IP_CONNTRACK_VERSION, ip_conntrack_htable_size, ip_conntrack_max, sizeof(struct ip_conntrack);/*注册socket选项*/ret = nf_register_sockopt(&so_getorigdst);if (ret != 0) printk(KERN_ERR Unable to register netfilter socket optionn);return ret;/* 为hash表分配连续内存页 */ip_conntrack_hash = vmalloc(sizeof(struct list_head) * ip_conntrack_htable_size);if (!ip_conntrack_hash) printk(KERN_ERR Unable to create ip_conntrack_hashn);goto err_unreg_sockopt;/* 分配高速缓存 */ip_conntrack_cachep = kmem_cache_create(ip_conntrack, sizeof(struct ip_conntrack), 0, SLAB_HWCACHE_ALIGN,NULL, NULL);if (!ip_conntrack_cachep) printk(KERN_ERR Unable to create ip_conntrack slab cachen);goto err_free_hash;/* Dont NEED lock here, but good form anyway. */WRITE_LOCK(&ip_conntrack_lock);/* netfilter中对每个要进行跟踪的IP协议定义了一个ip_conntrack_protocol结构,每个IP协议的连接跟踪处理就是要填写这样一个结构,这里的ip_conntrack_protocol_tcp,ip_conntrack_protocol_udp等都是这个结构,用list_append将这些需要跟踪的协议组织成链表 */list_append(&protocol_list, &ip_conntrack_protocol_tcp);list_append(&protocol_list, &ip_conntrack_protocol_udp);list_append(&protocol_list, &ip_conntrack_protocol_icmp);list_append(&protocol_list, &ip_conntrack_protocol_esp);WRITE_UNLOCK(&ip_conntrack_lock);/* 初始化hash表 */for (i = 0; i nh.iph-frag_off & htons(IP_OFFSET) return NF_ACCEPT;if (net_ratelimit() printk(KERN_ERR ip_conntrack_in: Frag of proto %u (hook=%u)n, (*pskb)-nh.iph-protocol, hooknum);return NF_DROP;/* 将当前数据包设置为未修改 */(*pskb)-nfcache |= NFC_UNKNOWN;/* 判断当前数据包是否已被检查过了 */if (*pskb)-nfct)return NF_ACCEPT;/* 根据当前数据包的协议,查找与之相应的struct ip_conntrack_protocol结构 */proto = ip_ct_find_proto(*pskb)-nh.iph-protocol);/* 如果是icmp错误报文 */if (*pskb)-nh.iph-protocol = IPPROTO_ICMP & icmp_error_track(*pskb, &ctinfo, hooknum)return NF_ACCEPT;/* 在全局的连接表中,查找与当前包相匹配的连接结构,返回的是struct ip_conntrack *类型指针,它用于描述一个数据包的连接状态 */if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo)return NF_ACCEPT;if (IS_ERR(ct)return NF_DROP;IP_NF_ASSERT(*pskb)-nfct);/* 如果注册了相应的协议的ip_conntrack_protocol结构,则在这里调用其中的packet函数做一些检查 */ret = proto-packet(ct, *pskb, ctinfo);if (ret = -1) /* Invalid */nf_conntrack_put(*pskb)-nfct);(*pskb)-nfct = NULL;return NF_ACCEPT;/* 如果注册了相应协议的ip_conntrack_helper结构,则在这里调用其help函数 */if (ret != NF_DROP & ct-helper) ret = ct-helper-help(*pskb, ct, ctinfo);if (ret = -1) /* Invalid */nf_conntrack_put(*pskb)-nfct);(*pskb)-nfct = NULL;return NF_ACCEPT;if (set_reply)set_bit(IPS_SEEN_REPLY_BIT, &ct-status);return ret;连接跟踪模块将所有支持的协议,都使用struct ip_conntrack_protocol 结构封装,注册至全局链表中,这里首先调用函数ip_ct_find_proto根据当前数据包的协议值,找到协议注册对应的模块。然后调用resolve_normal_ct 函数进一步处理。5.12 resolve_normal_ct()函数 ip_conntrack_core.c 函数判断数据包在连接跟踪表是否存在,如果不存在,则为数据包分配相应的连接跟踪节点空间并初始化,然后设置连接状态。static inline struct ip_conntrack *resolve_normal_ct(struct sk_buff *skb, struct ip_conntrack_protocol *proto, int *set_reply, unsigned int hooknum, enum ip_conntrack_info *ctinfo)struct ip_conntrack_tuple tuple;struct ip_conntrack_tuple_hash *h;IP_NF_ASSERT(skb-nh.iph-frag_off & htons(IP_OFFSET) = 0);/* 将数据包转换成tuple */if (!get_tuple(skb-nh.iph, skb, skb-nh.iph-ihl*4, &tuple, proto)return NULL;/* 查找对应的tuple在连接跟踪表中是否存在 */h = ip_conntrack_find_get(&tuple, NULL);/* 如果不存在,初始化该连接 */if (!h) h = init_conntrack(&tuple, proto, skb);if (!h)return NULL;if (IS_ERR(h)return (void *)h;/* 判断连接方向 */if (DIRECTION(h) = IP_CT_DIR_REPLY) *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;/* Please set reply bit if this packet OK */*set_reply = 1; else /* Once weve had two way comms, always ESTABLISHED. */if (test_bit(IPS_SEEN_REPLY_BIT, &h-ctrack-status) DEBUGP(ip_conntrack_in: normal packet for %pn, h-ctrack); *ctinfo = IP_CT_ESTABLISHED; else if (test_bit(IPS_EXPECTED_BIT, &h-ctrack-status) DEBUGP(ip_conntrack_in: related packet for %pn, h-ctrack);*ctinfo = IP_CT_RELATED; else DEBUGP(ip_conntrack_in: new packet for %pn, h-ctrack);*ctinfo = IP_CT_NEW;*set_reply = 0;/* 设置skb的对应成员,如使用计数器、数据包状态标记 */skb-nfct = &h-ctrack-infos*ctinfo;return h-ctrack;5.1.3 获取tuple结构 ip_conntrack_core.cget_tuple()函数将数据包转换成tuple结构int get_tuple(const struct iphdr *iph, const struct sk_buff *skb, unsigned int dataoff, struct ip_conntrack_tuple *tuple, const struct ip_conntrack_protocol *protocol)/* Never happen */if (iph-frag_off & htons(IP_OFFSET) printk(ip_conntrack_core: Frag of proto %u.n, iph-protocol);return 0;/* 设置来源、目的地址和协议号 */tuple-sr

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论