Linux连接跟踪源码分析报告_第1页
Linux连接跟踪源码分析报告_第2页
Linux连接跟踪源码分析报告_第3页
已阅读5页,还剩24页未读 继续免费阅读

下载本文档

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

文档简介

1、Linux 连接跟踪源码分析IP Connection tracking连接跟踪用来跟踪和记录连接状态,是 netfilter 的一部份,也是通过在 hook 点上注册相应的结构来工作的。无论是发送,接收,还是转发的数据包,都要经过两个 conntrack 模块第一个 conntrack 点的优先级是最高的, 所有数据包进入 netfilter 后都会 首先被它处理,其作用是创建 ip_conntrack 结构。而最后一个 conntrack 的优 先级最低, 总是在数据包离开 netfilter 之前做最后的处理, 它的作用是将该数 据包的连接跟踪结构添加到系统的连接状态表中1. ip_co

2、nntarck结构 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_MA是一个枚举类型ip_conntrack_dir(位于include/linux/netfilter_ipv4/ip_conntrac

3、k_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

4、_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

5、,其核心是一个处理函数。这个成员表示当发生连接超时时,将调用此处理函数 */struct timer_list timeout;/* 所谓“预期的连接”的链表, 其中存放的是我们所期望的其它相关连接 */ struct list_head sibling_list;/* 目前的预期连接数量 */unsigned int expecting;/* 结构 ip_conntrack_expect 位于 ip_conntrack.h ,这个结构用于将一个预 期的连接分配给现有的连接,也就是说本连接是这个 master 的一个预期连接 */struct ip_conntrack_expect *mast

6、er;/* 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_in

7、fo 类型(定义于 include/linux/skbuff.h, Li ne92 ,实际上就是 nf_conntrack 结构)的结构,每个结构对应于某种状态的连接。这一 系列的结构会被 sk_buff 结构的 nfct 指针所引用,描述了所有与此连接有关系 的数据报。其状态由枚举类型 ip_conntrack_info 定义(位于include/linux/netfilter_ipv4/ip_conntrack.h, Line12 )共有 5 个成员:IP_CT_ESTABLISHE:D 数据报属于已经完全建立的连接IP_CT_RELATE:DIP_CT_NEW:IP_CT_IS_REPL

8、Y:IP_CT_NUMBE: R数据报属于一个新的连接, 但此连接与一个现有连接相 关(预期连接);或者是ICMP错误 数据报属于一个新的连接 数据报属于一个连接的回复不同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_

9、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(CON

10、FIG_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_

11、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_conn

12、track_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/* 用来组织链表 */stru

13、ct list_head list;/* 用来描述一个 tuple */struct ip_conntrack_tuple tuple;/* this = &ctrack-tuplehashDIRECTION(this). */struct ip_conntrack *ctrack;实际描述一个 tuple 的是 ip_conntrack_tuple 结构 ip_conntrack_tuple.h struct ip_conntrack_tuple/* 源 */struct ip_conntrack_manip src;/* These are the parts of the tuple w

14、hich 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 pr

15、otonum; dst;对于所有 IP 协议,协议类型、源地址、目的地址这三个参数是识别连接所必须 的,具体到各个协议,就要提取出各协议的唯一特征数据,如TCP UDP的源端口、目的端口,ICMP的ID、TYPE COD等值,这些值就是tuple结构要处理的 数据。各协议相关数据是以联合 ( u nion )形式定义在 tuple 结构中的, netfilter 缺省支持TCP UDP和ICMP协议,如果还要支持其他IP协议,如GRE ESP AH SCTP等,需要在联合中添加相应的协议参数值。ip_conntrack_manip 和 ip_conntrack_manip_proto ip_c

16、onntrack_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;Net

17、filter 将每一个数据包转换成 tuple ,再根据 tuple 计算出 hash 值,这样, 就可以使用 ip_conntrack_hashhash_id 找到 hash 表中链表的入口, 并组织链 表;找到 hash 表中链表入口后, 如果链表中不存在此“ tuple ”,则是一个新连 接,就把 tuple 插入到链表的合适位置;两个节点 tupleORIGINAL 和 tupleREPLY 虽然是分开的, 在两个链表当中, 但是如前所述, 它们同时又被封 装在 ip_conntrack 结构的 tuplehash 数组中3. 连接跟踪初始化初始化函数 init ()调用 init_o

18、r_cleanup(1) 函数 ip_conntrack_standalone.c static 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.22初始化 proc 文件系统节点3为连接跟踪注册 hookstatic int init_or

19、_cleanup(int init)struct proc_dir_entry *proc;int ret = 0;if (!init) goto cleanup;/* 初始化连接跟踪的一些变量和数据结构,如连接跟踪表的大小, Hash 表的大 小等 */ret = ip_conntrack_init() ;if (ret ow ner = THIS_MODULE;/*为连接跟踪注册hook, 共六个,所在的hook点、注册的hook函数和优先 级分别如下(和最开始的图是一致的):NF_IP_PRE_ROUTINGip conn track defrag NF IP PRI CONNTRACK

20、 DEFRAGip_co nn track_in NF_IP_PRI_CONNTRACKNF_IP_LOCAL_OUTip conntrack defrag NF IP PRI CONNTRACK DEFRAGip_co nn track_local NF_IP_PRI_CONNTRACKNF_IP_POST_ROUTINGip_refrag NF_IP_PRI_LASTNFP_LOCAL_INip_co nfirm NF_IP_PRI_LAST-1优先级的顺序为:NF_IP_PRI_FIRST (最高)NF_IP_PRI_CONNTRACK_DEFRAGNF_IP_PRI_CONNTRACK

21、NF_IP_PRI_MANGLENF_IP_PRI_NAT_DSTNF_IP_PRI_FILTERNF_IP_PRI_NAT_SRCNF_IP_PRI_LAST (最低)我们知道,LOCAL_OUT PRE_ROUTIN点可以看作是netfilter 的入口,而 POST_ROUTING LOCAL_IN可以看作是出口。也就是说,在每个数据包刚一进入 netfilter之后首先都会调用ip_conntrack_defrag 做分片处理,紧接着就是对收到的和发出的数据包分别进行 ip_conntrack_in 和ip_conntrack_loacl(ip_conntrack_loacl里还是调用

22、了 ip_conntrack_in )。而在数据包即将离开netfilter 之前,会对进入主机的数据包进行ip_confirm 处理,对发出的数据 包进行ip_refrag 处理(ip_refrag 里也会调用ip_confirm )。 这就是整个连 接跟踪模块在netfilter中的分布情况。另外,我们分析的是的内核,在linux2.6.12 中,这个地方还增加了两个 hook,分别是 ip_conntrack_helper_out_ops 和 ip_conntrack_helper_in_ops 将helper模块相关的处理提前了。*/ret = n f_register_hook(&i

23、p_conn track_defrag_ops);if (ret 0) prin tk(ip_c onn track: cant register pre-routi ng defraghook.n);goto clea nu p_proc; _ret = n f_register_hook(&ip_conn track_defrago cal_out_ops);if (ret 0) printk(ip_conntrack: cant register local_out defrag hook.n); goto cleanup_defragops;ret = nf_register_hook

24、(&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);

25、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_s

26、ize= (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) - %Z

27、d 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_h

28、ash = 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 (!i

29、p_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

30、_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_UN

31、LOCK(&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;/* 判断当前数据包是否已

32、被检查过了 */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;/* 在全局的连接表中,查找与当前包相匹配的连接结构,返回的是

33、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 (

34、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;

35、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.1 2 resolve_normal_ct() 函数 ip_conntrack_core.c 函数判断数据包在连接跟踪表是否存在, 如果不存在

36、, 则为数据包分配相应的连 接跟踪节点空间并初始化,然后设置连接状态。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

37、-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)

38、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_

39、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;/*

40、设置 skb 的对应成员,如使用计数器、数据包状态标记 */ skb-nfct = &h-ctrack-infos*ctinfo;return h-ctrack;5.1.3 获取 tuple 结构 ip_conntrack_core.c get_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_protoc

41、ol *protocol)/* Never happen */if (iph-frag_off & htons(IP_OFFSET) printk(ip_conntrack_core: Frag of proto %u.n, iph-protocol);return 0;/* 设置来源、目的地址和协议号 */tuple-src.ip = iph-saddr;tuple-dst.ip = iph-daddr;tonum = iph-protocol;tuple-src.u.all = tuple-dst.u.all = 0;/* 这里根据协议的不同调用各自的函数 */r

42、eturn protocol-pkt_to_tuple(skb, dataoff, tuple)以 tcp 协议为例, ip_conntrack_proto_tcp.c :static int tcp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, struct ip_conntrack_tuple *tuple) struct tcphdr hdr;/* Actually only need first 8 bytes. */ if (skb_copy_bits(skb, dataoff, &hdr, 8) != 0) return 0;/* 根据报头的端口信息,设置 tuple 对应成员 */ tuple-src.u.tcp.port = hdr.source; tuple-dst.u.tcp.port = hdr.dest;return 1;5.1.4 搜索 hash 表要对Hash表进行遍历,首要需要找到hash表的入口,然后来遍历该入口指向的 链表。每个链表的节点是 struct ip_conntrack_tuple_hash ,它封装了 tuple , 所谓封装,就是把待查找的 tu

温馨提示

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

评论

0/150

提交评论