Netfilter 之 连接跟踪初始化 您所在的位置:网站首页 nf_defrag_ipv4 Netfilter 之 连接跟踪初始化

Netfilter 之 连接跟踪初始化

2024-07-06 05:38| 来源: 网络整理| 查看: 265

基础参数初始化

nf_conntrack_init_start函数完成连接跟踪基础参数的初始化,包括了hash,slab,扩展项,GC任务等;

1 int nf_conntrack_init_start(void) 2 { 3 int max_factor = 8; 4 int ret = -ENOMEM; 5 int i; 6 7 /* struct nf_ct_ext uses u8 to store offsets/size */ 8 BUILD_BUG_ON(total_extension_size() > 255u); 9 10 seqcount_init(&nf_conntrack_generation); 11 12 for (i = 0; i < CONNTRACK_LOCKS; i++) 13 spin_lock_init(&nf_conntrack_locks[i]); 14 15 /* 根据内存大小,初始化htable_size */ 16 if (!nf_conntrack_htable_size) { 17 /* Idea from tcp.c: use 1/16384 of memory. 18 * On i386: 32MB machine has 512 buckets. 19 * >= 1GB machines have 16384 buckets. 20 * >= 4GB machines have 65536 buckets. 21 */ 22 nf_conntrack_htable_size 23 = (((totalram_pages (4 * (1024 * 1024 * 1024 / PAGE_SIZE))) 26 nf_conntrack_htable_size = 65536; 27 else if (totalram_pages > (1024 * 1024 * 1024 / PAGE_SIZE)) 28 nf_conntrack_htable_size = 16384; 29 if (nf_conntrack_htable_size < 32) 30 nf_conntrack_htable_size = 32; 31 32 /* Use a max. factor of four by default to get the same max as 33 * with the old struct list_heads. When a table size is given 34 * we use the old value of 8 to avoid reducing the max. 35 * entries. */ 36 max_factor = 4; 37 } 38 39 /* 分配hash */ 40 nf_conntrack_hash = nf_ct_alloc_hashtable(&nf_conntrack_htable_size, 1); 41 if (!nf_conntrack_hash) 42 return -ENOMEM; 43 44 /* 设置连接跟踪项的最大值 */ 45 nf_conntrack_max = max_factor * nf_conntrack_htable_size; 46 47 /* 创建nf_conn连接跟踪slab */ 48 nf_conntrack_cachep = kmem_cache_create("nf_conntrack", 49 sizeof(struct nf_conn), 50 NFCT_INFOMASK + 1, 51 SLAB_TYPESAFE_BY_RCU | SLAB_HWCACHE_ALIGN, NULL); 52 if (!nf_conntrack_cachep) 53 goto err_cachep; 54 55 printk(KERN_INFO "nf_conntrack version %s (%u buckets, %d max)\n", 56 NF_CONNTRACK_VERSION, nf_conntrack_htable_size, 57 nf_conntrack_max); 58 59 /* 各个扩展项的初始化 */ 60 ret = nf_conntrack_expect_init(); 61 if (ret < 0) 62 goto err_expect; 63 64 ret = nf_conntrack_acct_init(); 65 if (ret < 0) 66 goto err_acct; 67 68 ret = nf_conntrack_tstamp_init(); 69 if (ret < 0) 70 goto err_tstamp; 71 72 ret = nf_conntrack_ecache_init(); 73 if (ret < 0) 74 goto err_ecache; 75 76 ret = nf_conntrack_timeout_init(); 77 if (ret < 0) 78 goto err_timeout; 79 80 ret = nf_conntrack_helper_init(); 81 if (ret < 0) 82 goto err_helper; 83 84 ret = nf_conntrack_labels_init(); 85 if (ret < 0) 86 goto err_labels; 87 88 ret = nf_conntrack_seqadj_init(); 89 if (ret < 0) 90 goto err_seqadj; 91 92 /* 初始化nf_ct_l3protos[]初始化为nf_conntrack_l3proto_generic */ 93 ret = nf_conntrack_proto_init(); 94 if (ret < 0) 95 goto err_proto; 96 97 /* 初始化连接跟踪gc任务 */ 98 conntrack_gc_work_init(&conntrack_gc_work); 99 queue_delayed_work(system_long_wq, &conntrack_gc_work.dwork, HZ); 100 101 return 0; 102 103 err_proto: 104 nf_conntrack_seqadj_fini(); 105 err_seqadj: 106 nf_conntrack_labels_fini(); 107 err_labels: 108 nf_conntrack_helper_fini(); 109 err_helper: 110 nf_conntrack_timeout_fini(); 111 err_timeout: 112 nf_conntrack_ecache_fini(); 113 err_ecache: 114 nf_conntrack_tstamp_fini(); 115 err_tstamp: 116 nf_conntrack_acct_fini(); 117 err_acct: 118 nf_conntrack_expect_fini(); 119 err_expect: 120 kmem_cache_destroy(nf_conntrack_cachep); 121 err_cachep: 122 nf_ct_free_hashtable(nf_conntrack_hash, nf_conntrack_htable_size); 123 return ret; 124 }

 

协议与tuple操作初始化

nf_conntrack_l3proto_ipv4_init函数完成了协议和tuple操作函数相关的初始化;

1 static int __init nf_conntrack_l3proto_ipv4_init(void) 2 { 3 int ret = 0; 4 5 need_conntrack(); 6 7 ret = nf_register_sockopt(&so_getorigdst); 8 if (ret < 0) { 9 pr_err("Unable to register netfilter socket option\n"); 10 return ret; 11 } 12 13 ret = register_pernet_subsys(&ipv4_net_ops); 14 if (ret < 0) { 15 pr_err("nf_conntrack_ipv4: can't register pernet ops\n"); 16 goto cleanup_sockopt; 17 } 18 19 /* nf_conntrack_l4proto相关初始化 */ 20 ret = nf_ct_l4proto_register(builtin_l4proto4, 21 ARRAY_SIZE(builtin_l4proto4)); 22 if (ret < 0) 23 goto cleanup_pernet; 24 25 /* nf_conntrack_l3proto相关初始化 */ 26 ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4); 27 if (ret < 0) { 28 pr_err("nf_conntrack_ipv4: can't register ipv4 proto.\n"); 29 goto cleanup_l4proto; 30 } 31 32 return ret; 33 cleanup_l4proto: 34 nf_ct_l4proto_unregister(builtin_l4proto4, 35 ARRAY_SIZE(builtin_l4proto4)); 36 cleanup_pernet: 37 unregister_pernet_subsys(&ipv4_net_ops); 38 cleanup_sockopt: 39 nf_unregister_sockopt(&so_getorigdst); 40 return ret; 41 }

 

nf_conntrack_l3proto_ipv4

nf_conntrack_l3proto_ipv4 结构成员初始化包括了基础信息,tuple的相关操作,钩子函数的注册,注销等,每个函数的作用如下;

1 struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = { 2 .l3proto = PF_INET, 3 .name = "ipv4", 4 .pkt_to_tuple = ipv4_pkt_to_tuple, 5 .invert_tuple = ipv4_invert_tuple, 6 .print_tuple = ipv4_print_tuple, 7 .get_l4proto = ipv4_get_l4proto, 8 #if IS_ENABLED(CONFIG_NF_CT_NETLINK) 9 .tuple_to_nlattr = ipv4_tuple_to_nlattr, 10 .nlattr_tuple_size = ipv4_nlattr_tuple_size, 11 .nlattr_to_tuple = ipv4_nlattr_to_tuple, 12 .nla_policy = ipv4_nla_policy, 13 #endif 14 .net_ns_get = ipv4_hooks_register, 15 .net_ns_put = ipv4_hooks_unregister, 16 .me = THIS_MODULE, 17 };

 

tuple相关操作 1 /* 从ip头中获取源目的地址,存入tuple */ 2 static bool ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, 3 struct nf_conntrack_tuple *tuple) 4 { 5 const __be32 *ap; 6 __be32 _addrs[2]; 7 ap = skb_header_pointer(skb, nhoff + offsetof(struct iphdr, saddr), 8 sizeof(u_int32_t) * 2, _addrs); 9 if (ap == NULL) 10 return false; 11 12 tuple->src.u3.ip = ap[0]; 13 tuple->dst.u3.ip = ap[1]; 14 15 return true; 16 } 17 18 /* 根据原tuple地址设置新tuple,源目的地址均相反 */ 19 static bool ipv4_invert_tuple(struct nf_conntrack_tuple *tuple, 20 const struct nf_conntrack_tuple *orig) 21 { 22 tuple->src.u3.ip = orig->dst.u3.ip; 23 tuple->dst.u3.ip = orig->src.u3.ip; 24 25 return true; 26 } 27 28 /* 打印tuple的源目的地址 */ 29 static void ipv4_print_tuple(struct seq_file *s, 30 const struct nf_conntrack_tuple *tuple) 31 { 32 seq_printf(s, "src=%pI4 dst=%pI4 ", 33 &tuple->src.u3.ip, &tuple->dst.u3.ip); 34 } 35 36 /* 获取ip头中的协议 */ 37 static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, 38 unsigned int *dataoff, u_int8_t *protonum) 39 { 40 const struct iphdr *iph; 41 struct iphdr _iph; 42 43 44 iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); 45 if (iph == NULL) 46 return -NF_ACCEPT; 47 48 /* Conntrack defragments packets, we might still see fragments 49 * inside ICMP packets though. */ 50 if (iph->frag_off & htons(IP_OFFSET)) 51 return -NF_ACCEPT; 52 53 *dataoff = nhoff + (iph->ihl protocol; 55 56 /* Check bogus IP headers */ 57 if (*dataoff > skb->len) { 58 pr_debug("nf_conntrack_ipv4: bogus IPv4 packet: " 59 "nhoff %u, ihl %u, skblen %u\n", 60 nhoff, iph->ihl len); 61 return -NF_ACCEPT; 62 } 63 64 return NF_ACCEPT; 65 }

 

netlink与tuple之间的操作 1 /* 填充tuple的源目的地址到netlink */ 2 static int ipv4_tuple_to_nlattr(struct sk_buff *skb, 3 const struct nf_conntrack_tuple *tuple) 4 { 5 if (nla_put_in_addr(skb, CTA_IP_V4_SRC, tuple->src.u3.ip) || 6 nla_put_in_addr(skb, CTA_IP_V4_DST, tuple->dst.u3.ip)) 7 goto nla_put_failure; 8 return 0; 9 10 nla_put_failure: 11 return -1; 12 } 13 14 static const struct nla_policy ipv4_nla_policy[CTA_IP_MAX+1] = { 15 [CTA_IP_V4_SRC] = { .type = NLA_U32 }, 16 [CTA_IP_V4_DST] = { .type = NLA_U32 }, 17 }; 18 19 /* 将netlink中的源目的地址填充到tuple */ 20 static int ipv4_nlattr_to_tuple(struct nlattr *tb[], 21 struct nf_conntrack_tuple *t) 22 { 23 if (!tb[CTA_IP_V4_SRC] || !tb[CTA_IP_V4_DST]) 24 return -EINVAL; 25 26 t->src.u3.ip = nla_get_in_addr(tb[CTA_IP_V4_SRC]); 27 t->dst.u3.ip = nla_get_in_addr(tb[CTA_IP_V4_DST]); 28 29 return 0; 30 } 31 32 /* 获取netlink中的tuple大小 */ 33 static int ipv4_nlattr_tuple_size(void) 34 { 35 return nla_policy_len(ipv4_nla_policy, CTA_IP_MAX + 1); 36 }

 

钩子函数的注册

ipv4_hooks_register钩子函数注册回调,其中分为两步,defrag钩子注册和其他钩子注册;

1 static int ipv4_hooks_register(struct net *net) 2 { 3 struct conntrack4_net *cnet = net_generic(net, conntrack4_net_id); 4 int err = 0; 5 6 mutex_lock(®ister_ipv4_hooks); 7 8 cnet->users++; 9 if (cnet->users > 1) 10 goto out_unlock; 11 12 /* defrag钩子函数注册 */ 13 err = nf_defrag_ipv4_enable(net); 14 if (err) { 15 cnet->users = 0; 16 goto out_unlock; 17 } 18 19 /* 注册netfilter钩子函数 */ 20 err = nf_register_net_hooks(net, ipv4_conntrack_ops, 21 ARRAY_SIZE(ipv4_conntrack_ops)); 22 23 if (err) 24 cnet->users = 0; 25 out_unlock: 26 mutex_unlock(®ister_ipv4_hooks); 27 return err; 28 }

 

nf_defrag_ipv4_enable为defrag钩子注册流程;

1 int nf_defrag_ipv4_enable(struct net *net) 2 { 3 int err = 0; 4 5 might_sleep(); 6 7 if (net->nf.defrag_ipv4) 8 return 0; 9 10 mutex_lock(&defrag4_mutex); 11 if (net->nf.defrag_ipv4) 12 goto out_unlock; 13 14 err = nf_register_net_hooks(net, ipv4_defrag_ops, 15 ARRAY_SIZE(ipv4_defrag_ops)); 16 if (err == 0) 17 net->nf.defrag_ipv4 = true; 18 19 out_unlock: 20 mutex_unlock(&defrag4_mutex); 21 return err; 22 }

 

defrag钩子函数;

1 static struct nf_hook_ops ipv4_defrag_ops[] = { 2 { 3 .hook = ipv4_conntrack_defrag, 4 .pf = NFPROTO_IPV4, 5 .hooknum = NF_INET_PRE_ROUTING, 6 .priority = NF_IP_PRI_CONNTRACK_DEFRAG, 7 }, 8 { 9 .hook = ipv4_conntrack_defrag, 10 .pf = NFPROTO_IPV4, 11 .hooknum = NF_INET_LOCAL_OUT, 12 .priority = NF_IP_PRI_CONNTRACK_DEFRAG, 13 }, 14 };

 

conntrack_in,helper,confirm钩子函数;

1 static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = { 2 { 3 .hook = ipv4_conntrack_in, 4 .pf = NFPROTO_IPV4, 5 .hooknum = NF_INET_PRE_ROUTING, 6 .priority = NF_IP_PRI_CONNTRACK, 7 }, 8 { 9 .hook = ipv4_conntrack_local, 10 .pf = NFPROTO_IPV4, 11 .hooknum = NF_INET_LOCAL_OUT, 12 .priority = NF_IP_PRI_CONNTRACK, 13 }, 14 { 15 .hook = ipv4_helper, 16 .pf = NFPROTO_IPV4, 17 .hooknum = NF_INET_POST_ROUTING, 18 .priority = NF_IP_PRI_CONNTRACK_HELPER, 19 }, 20 { 21 .hook = ipv4_confirm, 22 .pf = NFPROTO_IPV4, 23 .hooknum = NF_INET_POST_ROUTING, 24 .priority = NF_IP_PRI_CONNTRACK_CONFIRM, 25 }, 26 { 27 .hook = ipv4_helper, 28 .pf = NFPROTO_IPV4, 29 .hooknum = NF_INET_LOCAL_IN, 30 .priority = NF_IP_PRI_CONNTRACK_HELPER, 31 }, 32 { 33 .hook = ipv4_confirm, 34 .pf = NFPROTO_IPV4, 35 .hooknum = NF_INET_LOCAL_IN, 36 .priority = NF_IP_PRI_CONNTRACK_CONFIRM, 37 }, 38 };

 

nf_conntrack_l4proto_tcp4

多种协议会实现自己的nf_conntrack_l4proto,这里只列出tcp的实现,简要看下;

nf_conntrack_l4proto_tcp4结构中的函数与nf_conntrack_l3proto_ipv4 结构类似,其中多数函数是将源目的ip的操作换成了源目的端口的操作,此处不重复分析,另外增加了几个连接相关的操作函数packet,new等,遇到的时候在展开分析;

1 struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly = 2 { 3 .l3proto = PF_INET, 4 .l4proto = IPPROTO_TCP, 5 .name = "tcp", 6 .pkt_to_tuple = tcp_pkt_to_tuple, 7 .invert_tuple = tcp_invert_tuple, 8 .print_tuple = tcp_print_tuple, 9 .print_conntrack = tcp_print_conntrack, 10 .packet = tcp_packet, 11 .get_timeouts = tcp_get_timeouts, 12 .new = tcp_new, 13 .error = tcp_error, 14 .can_early_drop = tcp_can_early_drop, 15 #if IS_ENABLED(CONFIG_NF_CT_NETLINK) 16 .to_nlattr = tcp_to_nlattr, 17 .nlattr_size = tcp_nlattr_size, 18 .from_nlattr = nlattr_to_tcp, 19 .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, 20 .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, 21 .nlattr_tuple_size = tcp_nlattr_tuple_size, 22 .nla_policy = nf_ct_port_nla_policy, 23 #endif 24 #if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) 25 .ctnl_timeout = { 26 .nlattr_to_obj = tcp_timeout_nlattr_to_obj, 27 .obj_to_nlattr = tcp_timeout_obj_to_nlattr, 28 .nlattr_max = CTA_TIMEOUT_TCP_MAX, 29 .obj_size = sizeof(unsigned int) * 30 TCP_CONNTRACK_TIMEOUT_MAX, 31 .nla_policy = tcp_timeout_nla_policy, 32 }, 33 #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ 34 .init_net = tcp_init_net, 35 .get_net_proto = tcp_get_net_proto, 36 };

 

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有