概述
基于linux2.6.21
在上一节中分析了连接跟踪模块相关的数据结构,本节就开始分析连接跟踪模块相关的初始化,下一节理解连接跟踪模块的hook机制。
在分析连接跟踪模块代码之前,先说明几点:
1.连接跟踪模块的helper结构能够实现期望连接的建立以及相关协议的ALG功能。
2.连接跟踪为NAT或者状态防火墙的实现提供了依据
连接跟踪模块的初始化过程分别在三个地方进行,一个用于注册连接跟踪模块相关的hook回调函数;一个用于创建连接跟踪项与期望连接项相关的slab缓存;一个用于注册连接跟踪中协议相关的变量。
1.全局初始化
1.1 nf_conntrack_init
主要是 nf_conntrack_init进行初始化操作,该函数定义在nf_conntrack_core.c中。
主要完成以下功能:
a.设置nf_conntrack_htable_size、nf_conntrack_max的值
b.为nf_conntrack_hash申请内存并初始化
c.为连接跟踪项与期望连接跟踪项创建slab缓存。
- int __init nf_conntrack_init(void)
- {
- unsigned int i;
- int ret;
- 当nf_conntrack_htable_size的值没有设置时,则在内存小于1GB时,
- 使用内存的1/16384作为hash数组的最大值;在内存大于1GB时
- 则最大hash数组的值为8192
- */
- if (!nf_conntrack_htable_size) {
- nf_conntrack_htable_size
- = (((num_physpages << PAGE_SHIFT) / 16384)
- / sizeof(struct list_head));
- if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE))
- nf_conntrack_htable_size = 8192;
- if (nf_conntrack_htable_size < 16)
- nf_conntrack_htable_size = 16;
- }
- /*
- 设置nf_conntrack_max的值,即连接跟踪项的最大值。
- 该值取决于nf_conntrack_hash[]数组的最大值,即为
- nf_conntrack_htable_size的8倍。
- */
- nf_conntrack_max = 8 * nf_conntrack_htable_size;
- printk("nf_conntrack version %s (%u buckets, %d max)n",
- NF_CONNTRACK_VERSION, nf_conntrack_htable_size,
- nf_conntrack_max);
- /*
- hash链表的初始化,申请nf_conntrack_htable_size个hash链表
- */
- nf_conntrack_hash = alloc_hashtable(nf_conntrack_htable_size,
- &nf_conntrack_vmalloc);
- if (!nf_conntrack_hash) {
- printk(KERN_ERR "Unable to create nf_conntrack_hashn");
- goto err_out;
- }
- /*调用函数nf_conntrack_register_cache创建连接跟踪项相关的slab缓存*/
- ret = nf_conntrack_register_cache(NF_CT_F_BASIC, "nf_conntrack:basic",
- sizeof(struct nf_conn), NULL);
- if (ret < 0) {
- printk(KERN_ERR "Unable to create nf_conn slab cachen");
- goto err_free_hash;
- }
- /*创建期望连接跟踪项相关的slab缓存*/
- nf_conntrack_expect_cachep = kmem_cache_create("nf_conntrack_expect",
- sizeof(struct nf_conntrack_expect),
- 0, 0, NULL, NULL);
- if (!nf_conntrack_expect_cachep) {
- printk(KERN_ERR "Unable to create nf_expect slab cachen");
- goto err_free_conntrack_slab;
- }
- /* 将nf_ct_l3protos中每一个成员的变量都设置成nf_conntrack_generic_l3proto
- ,然后不同的连接跟踪的三层协议初始化时,即会将相应的
- 数组成员的值替换掉*/
- write_lock_bh(&nf_conntrack_lock);
- for (i = 0; i < PF_MAX; i++)
- nf_ct_l3protos[i] = &nf_conntrack_generic_l3proto;
- write_unlock_bh(&nf_conntrack_lock);
- /* For use by REJECT target */
- ip_ct_attach = __nf_conntrack_attach;
- /*将nf_conntrack_untracked的使用计数设置为1,使该数据连接项能不被删除掉*/
- atomic_set(&nf_conntrack_untracked.ct_general.use, 1);
- /*设置变量nf_conntrack_untracked的状态为confirmed,当对一类数据包
- 不想进行连接跟踪时,就会添加如下命令,
- iptables -t raw -A PREROUTING -d x.x.x.x-j NOTRACK,这样在数据包首先进入PRE_ROUTING
- 、OUTPUT链时,就会首先进入raw模块对应注册的hook函数,并将
- 数据包的nfct指针指向存储nf_conntrack_untracked的内存地址*/
- set_bit(IPS_CONFIRMED_BIT, &nf_conntrack_untracked.status);
- return ret;
- err_free_conntrack_slab:
- nf_conntrack_unregister_cache(NF_CT_F_BASIC);
- err_free_hash:
- free_conntrack_hash(nf_conntrack_hash, nf_conntrack_vmalloc,
- nf_conntrack_htable_size);
- err_out:
- return -ENOMEM;
- }
1.2init_or_cleanup
在nf_conntrack_standalone.c中定义的函数,主要是调用上1.1中介绍的函数进行初始化,然后在/proc/net文件系统中创建相应的文件以及在/proc/sys/net中创建连接跟踪模块相关的文件
/*
nf_conntrack_standalone的初始化与销毁函数
对于初始化:
1.调用nf_conntrack_init进行连接模块相关的初始化,主要是
设置连接跟踪数的最大值、为连接跟踪项或者期望连接跟踪
创建slab缓存等操作。
2.在proc/net目录下创建连接跟踪相关的文件,主要nf_conntrack、nf_conntrack_expect
查看如下:
- # cat /proc/net/stat/nf_conntrack
- entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete
- 00000001 00000027 00000b0b 000035a4 000053e5 000049fb 000034d8 000003c3 000003ea 00000000 00000000 00000000 00000000 00000000 00000000 00000000
- 00000001 00000000 000008e9 00000067 00007eec 00007d2b 00000132 0000008d 00000067 00000000 00000000 00000000 00000000 00000000 00000000 00000000
- #
- # cat /proc/net/nf_conntrack
- /proc/net/nf_conntrack /proc/net/nf_conntrack_expect
- # cat /proc/net/nf_conntrack
- ipv4 2 unknown 2 492 src=192.168.1.1 dst=224.0.0.1 [UNREPLIED] src=224.0.0.1 dst=192.168.1.1 use=2
- #
- #
- # cat /proc/net/nf_conntrack_expect
- #
- #
3.在/proc/sys文件系统中创建连接跟踪相关的内容。
*/
- static int init_or_cleanup(int init)
- {
- #ifdef CONFIG_PROC_FS
- struct proc_dir_entry *proc, *proc_exp, *proc_stat;
- #endif
- int ret = 0;
- if (!init) goto cleanup;
- ret = nf_conntrack_init();
- if (ret < 0)
- goto cleanup_nothing;
- #ifdef CONFIG_PROC_FS
- proc = proc_net_fops_create("nf_conntrack", 0440, &ct_file_ops);
- if (!proc) goto cleanup_init;
- proc_exp = proc_net_fops_create("nf_conntrack_expect", 0440,
- &exp_file_ops);
- if (!proc_exp) goto cleanup_proc;
- proc_stat = create_proc_entry("nf_conntrack", S_IRUGO, proc_net_stat);
- if (!proc_stat)
- goto cleanup_proc_exp;
- proc_stat->proc_fops = &ct_cpu_seq_fops;
- proc_stat->owner = THIS_MODULE;
- #endif
- #ifdef CONFIG_SYSCTL
- nf_ct_sysctl_header = register_sysctl_table(nf_ct_net_table, 0);
- if (nf_ct_sysctl_header == NULL) {
- printk("nf_conntrack: can't register to sysctl.n");
- ret = -ENOMEM;
- goto cleanup_proc_stat;
- }
- #endif
- return ret;
- cleanup:
- #ifdef CONFIG_SYSCTL
- unregister_sysctl_table(nf_ct_sysctl_header);
- cleanup_proc_stat:
- #endif
- #ifdef CONFIG_PROC_FS
- remove_proc_entry("nf_conntrack", proc_net_stat);
- cleanup_proc_exp:
- proc_net_remove("nf_conntrack_expect");
- cleanup_proc:
- proc_net_remove("nf_conntrack");
- cleanup_init:
- #endif /* CNFIG_PROC_FS */
- nf_conntrack_cleanup();
- cleanup_nothing:
- return ret;
- }
2 Ipv4 连接跟踪模块Hook回调函数的注册
Ipv4协议中,注册的连接跟踪模块相关的回调函数有如下几个:
/*
在连接跟踪PRE_ROUTING链上注册的hook回调函数,主要
是分段数据包进行重组。优先级高于ipv4_conntrack_in
*/
- static struct nf_hook_ops ipv4_conntrack_defrag_ops = {
- .hook = ipv4_conntrack_defrag,
- .owner = THIS_MODULE,
- .pf = PF_INET,
- .hooknum = NF_IP_PRE_ROUTING,
- .priority = NF_IP_PRI_CONNTRACK_DEFRAG,
- };
- /*PREROUTING链上注册ipv4_conntrack_in,主要是实现为一条数据流
- 创建连接跟踪项等操作*/
- static struct nf_hook_ops ipv4_conntrack_in_ops = {
- .hook = ipv4_conntrack_in,
- .owner = THIS_MODULE,
- .pf = PF_INET,
- .hooknum = NF_IP_PRE_ROUTING,
- .priority = NF_IP_PRI_CONNTRACK,
- };
- /*
- 在连接跟踪LOCAL_OUT链上注册的hook回调函数,主要
- 是分段数据包进行重组。优先级高于ipv4_conntrack_in
- */
- static struct nf_hook_ops ipv4_conntrack_defrag_local_out_ops = {
- .hook = ipv4_conntrack_defrag,
- .owner = THIS_MODULE,
- .pf = PF_INET,
- .hooknum = NF_IP_LOCAL_OUT,
- .priority = NF_IP_PRI_CONNTRACK_DEFRAG,
- };
- /*LOCALOUT链上注册ipv4_conntrack_in*/
- /*LOCAL_OUT链上注册ipv4_conntrack_local,主要是实现为本机发出的
- 一条数据流创建连接跟踪项等操作*/
- static struct nf_hook_ops ipv4_conntrack_local_out_ops = {
- .hook = ipv4_conntrack_local,
- .owner = THIS_MODULE,
- .pf = PF_INET,
- .hooknum = NF_IP_LOCAL_OUT,
- .priority = NF_IP_PRI_CONNTRACK,
- };
- /* helpers */
- /*
- 在POST_ROUTING链上注册ipv4_conntrack_help,根据传递的数据包,
- 找到该数据包关联的连接跟踪项,然后执行该连接跟踪
- 项关联的helper函数,实现期望连接的创建以及ALG等功能。
- */
- static struct nf_hook_ops ipv4_conntrack_helper_out_ops = {
- .hook = ipv4_conntrack_help,
- .owner = THIS_MODULE,
- .pf = PF_INET,
- .hooknum = NF_IP_POST_ROUTING,
- .priority = NF_IP_PRI_CONNTRACK_HELPER,
- };
- /*
- 在LOCAL_IN链上注册ipv4_conntrack_help,根据传递的数据包,
- 找到该数据包关联的连接跟踪项,然后执行该连接跟踪
- 项关联的helper函数,实现期望连接的创建以及ALG等功能。
- */
- static struct nf_hook_ops ipv4_conntrack_helper_in_ops = {
- .hook = ipv4_conntrack_help,
- .owner = THIS_MODULE,
- .pf = PF_INET,
- .hooknum = NF_IP_LOCAL_IN,
- .priority = NF_IP_PRI_CONNTRACK_HELPER,
- };
- /* Refragmenter; last chance. */
- /*
- 在POST_ROUTING链上注册ipv4_confirm。若传递的数据包关联
- 的连接跟踪项还没有被确认,则执行确认操作
- */
- static struct nf_hook_ops ipv4_conntrack_out_ops = {
- .hook = ipv4_confirm,
- .owner = THIS_MODULE,
- .pf = PF_INET,
- .hooknum = NF_IP_POST_ROUTING,
- .priority = NF_IP_PRI_CONNTRACK_CONFIRM,
- };
- /*
- 在PRE_ROUTING链上注册ipv4_confirm。若传递的数据包关联
- 的连接跟踪项还没有被确认,则执行确认操作
- */
- static struct nf_hook_ops ipv4_conntrack_local_in_ops = {
- .hook = ipv4_confirm,
- .owner = THIS_MODULE,
- .pf = PF_INET,
- .hooknum = NF_IP_LOCAL_IN,
- .priority = NF_IP_PRI_CONNTRACK_CONFIRM,
- };
然后在nf_conntrack_l3proto_ipv4.c的init_or_cleanup里,通过nf_register_hook将以上的nf_hook_ops添加到nf_hooks[][]数组的相应的链表中。
3.三层协议、四层协议中连接模块相关的全局变量定义
3.1 nf_conntrack_l3proto_ipv4
定义ipv4协议相关的nf_conntrack_l3proto变量,其中比较重要的是ipv4_pkt_to_tuple、
ipv4_invert_tuple、ipv4_tuple_to_nfattr、 ipv4_nfattr_to_tuple。下面一一分析之。
- struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 = {
- .l3proto = PF_INET,
- .name = "ipv4",
- .pkt_to_tuple = ipv4_pkt_to_tuple,
- .invert_tuple = ipv4_invert_tuple,
- .print_tuple = ipv4_print_tuple,
- .print_conntrack = ipv4_print_conntrack,
- .prepare = ipv4_prepare,
- .get_features = ipv4_get_features,
- #if defined(CONFIG_NF_CT_NETLINK) ||
- defined(CONFIG_NF_CT_NETLINK_MODULE)
- .tuple_to_nfattr = ipv4_tuple_to_nfattr,
- .nfattr_to_tuple = ipv4_nfattr_to_tuple,
- #endif
- .me = THIS_MODULE,
- };
3.1.1 ipv4_pkt_to_tuple
功能:根据ip头部获取源ip地址与目的ip地址,并写入tuple变量中。
- static int ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
- struct nf_conntrack_tuple *tuple)
- {
- u_int32_t _addrs[2], *ap;
- ap = skb_header_pointer(skb, nhoff + offsetof(struct iphdr, saddr),
- sizeof(u_int32_t) * 2, _addrs);
- if (ap == NULL)
- return 0;
- tuple->src.u3.ip = ap[0];
- tuple->dst.u3.ip = ap[1];
- return 1;
- }
3.1.2 ipv4_invert_tuple
功能:根据原始的tuple的源ip、目的ip值,设置reply的tuple值,新的tuple值的源、目的ip值与原始的tuple的源ip、目的ip值是反过来的。
- static int ipv4_invert_tuple(struct nf_conntrack_tuple *tuple,
- const struct nf_conntrack_tuple *orig)
- {
- tuple->src.u3.ip = orig->dst.u3.ip;
- tuple->dst.u3.ip = orig->src.u3.ip;
- return 1;
- }
3.1.3 ipv4_prepare
/*
1. 计算数据包的三层数据部分相对于skb->data的偏移量。
2.获取四层协议的协议号
*/
- static int
- ipv4_prepare(struct sk_buff **pskb, unsigned int hooknum, unsigned int *dataoff,
- u_int8_t *protonum)
- {
- /* Never happen */
- if ((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) {
- if (net_ratelimit()) {
- printk(KERN_ERR "ipv4_prepare: Frag of proto %u (hook=%u)n",
- (*pskb)->nh.iph->protocol, hooknum);
- }
- return -NF_DROP;
- }
- *dataoff = (*pskb)->nh.raw - (*pskb)->data + (*pskb)->nh.iph->ihl*4;
- /*获取四层协议号*/
- *protonum = (*pskb)->nh.iph->protocol;
- return NF_ACCEPT;
- }
3.1.4 ipv4_tuple_to_nfattr
功能:tuple结构中的三层源ip、目的ip地址按照nfnetlink规定的形式进行填充
- static int ipv4_tuple_to_nfattr(struct sk_buff *skb,
- const struct nf_conntrack_tuple *tuple)
- {
- NFA_PUT(skb, CTA_IP_V4_SRC, sizeof(u_int32_t),
- &tuple->src.u3.ip);
- NFA_PUT(skb, CTA_IP_V4_DST, sizeof(u_int32_t),
- &tuple->dst.u3.ip);
- return 0;
- nfattr_failure:
- return -1;
- }
3.1.5 ipv4_nfattr_to_tuple
功能:将nfnetlink消息传递过来的变量,转换成tuple结构中的三层源ip、目的ip地址,
- static int ipv4_nfattr_to_tuple(struct nfattr *tb[],
- struct nf_conntrack_tuple *t)
- {
- if (!tb[CTA_IP_V4_SRC-1] || !tb[CTA_IP_V4_DST-1])
- return -EINVAL;
- if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip))
- return -EINVAL;
- t->src.u3.ip =
- *(u_int32_t *)NFA_DATA(tb[CTA_IP_V4_SRC-1]);
- t->dst.u3.ip =
- *(u_int32_t *)NFA_DATA(tb[CTA_IP_V4_DST-1]);
- return 0;
- }
然后在nf_conntrack_l3proto_ipv4.c的init_or_cleanup里,通过nf_conntrack_l3proto_register将nf_conntrack_l3proto_ipv4的地址存放在nf_ct_l3protos[PF_INET]。
3.2 nf_conntrack_protocol_tcp4
定义tcp协议相关的nf_conntrack_protocol 变量,其中比较重要的是tcp_pkt_to_tuple、
tcp_invert_tuple、nf_ct_port_tuple_to_nfattr、 nf_ct_port_nfattr_to_tuple。这几个函数实现的功能与nf_conntrack_l3proto_ipv4中相应的函数相似,只不过ipv4中针对的是ip地址,此处针对的是端口号而已。
而函数tcp_packet主要是针对tcp协议的状态变化而定义的,主要用于状态防火墙的。
- struct nf_conntrack_protocol nf_conntrack_protocol_tcp4 =
- {
- .l3proto = PF_INET,
- .proto = IPPROTO_TCP,
- .name = "tcp",
- .pkt_to_tuple = tcp_pkt_to_tuple,
- .invert_tuple = tcp_invert_tuple,
- .print_tuple = tcp_print_tuple,
- .print_conntrack = tcp_print_conntrack,
- .packet = tcp_packet,
- .new = tcp_new,
- .error = tcp_error4,
- #if defined(CONFIG_NF_CT_NETLINK) ||
- defined(CONFIG_NF_CT_NETLINK_MODULE)
- .to_nfattr = tcp_to_nfattr,
- .from_nfattr = nfattr_to_tcp,
- .tuple_to_nfattr = nf_ct_port_tuple_to_nfattr,
- .nfattr_to_tuple = nf_ct_port_nfattr_to_tuple,
- #endif
- };
然后在nf_conntrack_l3proto_ipv4.c的init_or_cleanup里,通过nf_conntrack_protocol_register将nf_conntrack_protocol_tcp4的地址存放在nf_ct_protos[PF_INET][TCP]。
至此,分析完了连接跟踪模块的初始化代码部分。
最后
以上就是结实裙子为你收集整理的Linux netfilter 学习笔记 之八 ip层netfilter的连接跟踪模块初始化 1.全局初始化 2 Ipv4 连接跟踪模块Hook回调函数的注册 3.三层协议、四层协议中连接模块相关的全局变量定义的全部内容,希望文章能够帮你解决Linux netfilter 学习笔记 之八 ip层netfilter的连接跟踪模块初始化 1.全局初始化 2 Ipv4 连接跟踪模块Hook回调函数的注册 3.三层协议、四层协议中连接模块相关的全局变量定义所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复