netfilter nf - conntrack 模块实现分析

更新时间:2024-01-24 10:45:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

一.连接记录的存储

相关函数:

static inline struct nf_conn *nf_ct_tuplehash_to_ctrack(const struct nf_conntrack_tuple_hash *hash)

static inline struct nf_conn *nf_ct_get(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo)

nf_conntrack中tuple存储如上图所示。在struct nf_conntrack_tuple_hash中,成员hnode链接入ct->hash[]->first中的。实际的记录在保存在struct nf_conntrack_tuple中。

1.记录的访问:

hlist_nulls_for_each_entry_rcu

103 #define hlist_nulls_for_each_entry_rcu(tpos, pos, head, member) \\

104 for (pos = rcu_dereference((head)->first); \\

105 (!is_a_nulls(pos)) && \\

106 ({ tpos = hlist_nulls_entry(pos, typeof(*tpos), member); 1; }); \\ 107 pos = rcu_dereference(pos->next)) 108

参见 /net/netfilter/nf_conntrack_core.c __nf_conntrack_find函数;

struct nf_conntrack_tuple_hash *h; struct hlist_nulls_node *n;

hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[hash], hnnode)

2.记录的添加

84 static inline void hlist_nulls_add_head_rcu(struct hlist_nulls_node *n, 85 struct hlist_nulls_head *h) 86 {

87 struct hlist_nulls_node *first = h->first; 88

89 n->next = first;

90 n->pprev = &h->first;

91 rcu_assign_pointer(h->first, n); 92 if (!is_a_nulls(first))

93 first->pprev = &n->next; 94 }

参见/net/netfilter/nf_conntrack_core.c:

313 static void __nf_conntrack_hash_insert(struct nf_conn *ct, 314 unsigned int hash,

315 unsigned int repl_hash) 316 {

317 struct net *net = nf_ct_net(ct); 318

319 hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, 320 &net->ct.hash[hash]);

321 hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode, 322 &net->ct.hash[repl_hash]); 323 }

3.记录的删除

59 static inline void hlist_nulls_del_rcu(struct hlist_nulls_node *n) 60 {

61 __hlist_nulls_del(n); 62 n->pprev = LIST_POISON2; 63 }

59 static inline void __hlist_nulls_del(struct hlist_nulls_node *n) 60 {

61 struct hlist_nulls_node *next = n->next;

62 struct hlist_nulls_node **pprev = n->pprev; 63 *pprev = next;

64 if (!is_a_nulls(next))

65 next->pprev = pprev; 66 }

二.nf_conntrack模块的初始化

/net/netfilter/nf_conntrack_standalone.c

510 static int __init nf_conntrack_standalone_init(void) 511 {

512 return register_pernet_subsys(&nf_conntrack_net_ops); 513 }

473 static int nf_conntrack_net_init(struct net *net) 474 {

475 int ret; 476

477 ret = nf_conntrack_init(net); 478 if (ret < 0)

479 goto out_init;

480 ret = nf_conntrack_standalone_init_proc(net); 481 if (ret < 0)

482 goto out_proc;

483 net->ct.sysctl_checksum = 1; 484 net->ct.sysctl_log_invalid = 0;

485 ret = nf_conntrack_standalone_init_sysctl(net); 486 if (ret < 0)

487 goto out_sysctl; 488 return 0; 489

447-------:/net/netfilter/nf_conntrack_core.c#nf_conntrack_init

1275 int nf_conntrack_init(struct net *net) 1276 {

1277 int ret; 1278

1279 if (net_eq(net, &init_net)) {

1280 ret = nf_conntrack_init_init_net(); 1281 if (ret < 0)

1282 goto out_init_net; 1283 }

1284 ret = nf_conntrack_init_net(net); 1285 if (ret < 0)

1286 goto out_net; 1287

1288 if (net_eq(net, &init_net)) {

1289 /* For use by REJECT target */

1290 rcu_assign_pointer(ip_ct_attach, nf_conntrack_attach); 1291 rcu_assign_pointer(nf_ct_destroy, destroy_conntrack); 1292 }

1293 return 0; 1294

-----------------------/net/netfilter/nf_conntrack_core.c#nf_conntrack_init_net

1223 static int nf_conntrack_init_net(struct net *net) 1224 {

1225 int ret; 1226

1227 atomic_set(&net->ct.count, 0);

1228 INIT_HLIST_NULLS_HEAD(&net->ct.unconfirmed, 0);

1229 net->ct.stat = alloc_percpu(struct ip_conntrack_stat);

1234 ret = nf_conntrack_ecache_init(net);

1237 net->ct.hash = nf_ct_alloc_hashtable(&nf_conntrack_htable_size, 1238 &net->ct.hash_vmalloc, 1); 1244 ret = nf_conntrack_expect_init(net); 1247 ret = nf_conntrack_acct_init(net); 1253 #ifdef CONFIG_NET_NS

1254 nf_conntrack_untracked.ct_net = &init_net; 1255 #endif

1256 atomic_set(&nf_conntrack_untracked.ct_general.use, 1); 1257 /* - and look it like as a confirmed connection */

1258 set_bit(IPS_CONFIRMED_BIT, &nf_conntrack_untracked.status); 1259

1260 return 0;

这个函数所做的主要工作就是初始化net中的net->ct中的成员。初始化hash表大小,初始化expect链表大小,初始化acct扩展模块。

最后nf_conntrack_untracked.ct_net = &init_net,再设置它的status位为IPS_CONFIRMED_BIT;

三.conntrack的扩展

/include/net/netfilter/nf_conntrack_extend.h 66 struct nf_ct_ext_type 67 { 68 /* Destroys relationships (can be NULL). */ 69 void (*destroy)(struct nf_conn *ct); 70 /* Called when realloacted (can be NULL).

71 Contents has already been moved. */ 72 void (*move)(void *new, void *old); 73 74 enum nf_ct_ext_id id; 75 76 unsigned int flags; 77 78 /* Length and min alignment. */ 79 u8 len; 80 u8 align; 81 /* initial size of nf_ct_ext. */ 82 u8 alloc_size; 83 };

目前conntrack扩展有acct,helper,nat三种。它存储在*nf_ct_ext_types[NF_CT_EXT_NUM]全局数组中。

以nf_conntrack_acct为例:

/net/netfilter/nf_conntrack_acct.c 初始化

59 static struct nf_ct_ext_type acct_extend __read_mostly = { 60 .len = sizeof(struct nf_conn_counter[IP_CT_DIR_MAX]), 61 .align = __alignof__(struct nf_conn_counter[IP_CT_DIR_MAX]), 获得对齐方式 62 .id = NF_CT_EXT_ACCT, 63 };

struct nf_conn_counter为计数用: 17 struct nf_conn_counter { 18 u_int64_t packets; 19 u_int64_t bytes; 20 };

.len= sizeof(struct nf_conn_counter[IP_CT_DIR_MAX]),代表为连接的两个方向都要分配,进行计数。

1.扩展的注册

/net/netfilter/nf_conntrack_acct.c

110 int nf_conntrack_acct_init(struct net *net) 111 { 112 int ret; 113 114 net->ct.sysctl_acct = nf_ct_acct; 115 116 if (net_eq(net, &init_net)) { 123 ret = nf_ct_extend_register(&acct_extend); 124 if (ret < 0) { 125 printk(KERN_ERR \extension\\n\ 126 goto out_extend_register; 127 } 128 }

129 130 ret = nf_conntrack_acct_init_sysctl(net); 131 if (ret < 0) 132 goto out_sysctl; 133 134 return 0;

nf_conntrack_acct_init:114:

25 static int nf_ct_acct __read_mostly = NF_CT_ACCT_DEFAULT;

nf_conntrack_acct_init:123: nf_ct_extend_register /net/netfilter/nf_conntrack_extend.c

159 /* This MUST be called in process context. */

160 int nf_ct_extend_register(struct nf_ct_ext_type *type) 161 { 162 int ret = 0; 163 164 mutex_lock(&nf_ct_ext_type_mutex); 165 if (nf_ct_ext_types[type->id]) { 166 ret = -EBUSY; 167 goto out; 168 } 169 170 /* This ensures that nf_ct_ext_create() can allocate enough area 171 before updating alloc_size */ 172 type->alloc_size = ALIGN(sizeof(struct nf_ct_ext), type->align) 173 + type->len; 174 rcu_assign_pointer(nf_ct_ext_types[type->id], type); 175 update_alloc_size(type); 176 out: 177 mutex_unlock(&nf_ct_ext_type_mutex); 178 return ret; 179 }

第172行对nf_ct_ext的size进行对齐,再加上type->len再赋值给type->alloc_size.即它的大小为2*nf_conn_counter+1*nf_ct_ext;然后再将type保存到nf_ext_types数组中。

对照

可以发现,struct nf_ct_ext_type仅仅只是保存了struct nf_ct_ext所必要的一些信息而已。

至于为何要对齐,是因为扩展不只是一种,它们的数组结构也不一样,对齐方式都不一样,所以得保存自己的对齐方式,再对齐。

2.添加一个acct扩展

nf_ct_acct_ext_add:/include/net/netfilter/nf_conntrack_acct.h

28 static inline

29 struct nf_conn_counter *nf_ct_acct_ext_add(struct nf_conn *ct, gfp_t gfp) 30 { 31 struct net *net = nf_ct_net(ct); 32 struct nf_conn_counter *acct; 33 34 if (!net->ct.sysctl_acct) 35 return NULL; 36 37 acct = nf_ct_ext_add(ct, NF_CT_EXT_ACCT, gfp); 38 if (!acct) 39 pr_debug(\ 40 41 42 return acct; 43 };

/include/net/netfilter/nf_conntrack_extend.h#61 61 #define nf_ct_ext_add(ct, id, gfp) \\ 62 ((id##_TYPE *)__nf_ct_ext_add((ct), (id), (gfp)))

/net/netfilter/nf_conntrack_extend.c

函数会判断ct->ext是否为NULL,若为NULL则证明这是第一个,所以调用nf_ct_ext_create(), 它分配一个扩展的size并且返回它的起始地址;

对于扩展的管理是先来的放前面。所以nf_ct_ext::offset[id]会是添加id对应的扩展之前 nf_ct_ext的对齐后的连同以前添加的扩展数据在内的长度。

45 static void *

46 nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp) 47 { 48 unsigned int off, len; 49 struct nf_ct_ext_type *t;

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 }

rcu_read_lock();

t = rcu_dereference(nf_ct_ext_types[id]); BUG_ON(t == NULL);

off = ALIGN(sizeof(struct nf_ct_ext), t->align); len = off + t->len; rcu_read_unlock();

*ext = kzalloc(t->alloc_size, gfp); if (!*ext) return NULL; INIT_RCU_HEAD(&(*ext)->rcu); (*ext)->offset[id] = off; (*ext)->len = len;

return (void *)(*ext) + off;

3.查找一个扩展

/include/net/netfilter/nf_conntrack_extend.h#nf_ct_ext_find

38 #define nf_ct_ext_find(ext, id) \\ 39 ((id##_TYPE *)__nf_ct_ext_find((ext), (id))) 对于acct就强制转换成NF_CT_EXT_ACCT_TYPE 又:

14 #define NF_CT_EXT_HELPER_TYPE struct nf_conn_help 15 #define NF_CT_EXT_NAT_TYPE struct nf_conn_nat

16 #define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter 所以转换成了struct nf_conn_counter类型。

31 static inline void *__nf_ct_ext_find(const struct nf_conn *ct, u8 id) 32 { 33 if (!nf_ct_ext_exist(ct, id)) 34 return NULL; 35 36 return (void *)ct->ext + ct->ext->offset[id]; 37 }

根据offset取出值。

四.conntrack的具体实现

以PF_INET为例:

/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c

373 static int __init nf_conntrack_l3proto_ipv4_init(void) 374 {

375 int ret = 0; 376

377 need_conntrack();

378 nf_defrag_ipv4_enable();

379

380 ret = nf_register_sockopt(&so_getorigdst);

386 ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_tcp4); 392 ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udp4); 398 ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_icmp); 404 ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv4);

410 ret = nf_register_hooks(ipv4_conntrack_ops,

411 ARRAY_SIZE(ipv4_conntrack_ops));

416 #if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) 417 ret = nf_conntrack_ipv4_compat_init(); 418 if (ret < 0)

419 goto cleanup_hooks; 420 #endif

421 return ret;

函数先是注册socketopt,然后再注册协议,分别注册了tcp udp icmp ipv4这么PF_INET协议簇的协议类型。然后,便是注册了netfilter钩子。

1.协议注册

:/net/netfilter/nf_conntrack_proto.c#nf_conntrack_l4proto_register 协议是以数组的形式保存在net/netfilter/nf_conntrack_proto.c中: 四层为二维数组,三层为1维,因为四层协议会包含三层协议信息。

static struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX] __read_mostly; struct nf_conntrack_l3proto *nf_ct_l3protos[AF_MAX] __read_mostly;

四层协议的注册:

265 int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto) 266 {

267 int ret = 0; 268

269 if (l4proto->l3proto >= PF_MAX)

272 if ((l4proto->to_nlattr && !l4proto->nlattr_size)

273 || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size)) 274 return -EINVAL; 275

276 mutex_lock(&nf_ct_proto_mutex);

277 if (!nf_ct_protos[l4proto->l3proto]) {

278 /* l3proto may be loaded latter. */

279 struct nf_conntrack_l4proto **proto_array; 280 int i; 281

282 proto_array = kmalloc(MAX_NF_CT_PROTO *

283 sizeof(struct nf_conntrack_l4proto *), 284 GFP_KERNEL); 285 if (proto_array == NULL) { 286 ret = -ENOMEM; 287 goto out_unlock;

288 } 289

290 for (i = 0; i < MAX_NF_CT_PROTO; i++)

291 proto_array[i] = &nf_conntrack_l4proto_generic; 292 nf_ct_protos[l4proto->l3proto] = proto_array;

293 } else if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto] != 294 &nf_conntrack_l4proto_generic) { 295 ret = -EBUSY; 296 goto out_unlock; 297 } 298

299 ret = nf_ct_l4proto_register_sysctl(l4proto); 300 if (ret < 0)

301 goto out_unlock; 302

303 l4proto->nla_size = 0; 304 if (l4proto->nlattr_size)

305 l4proto->nla_size += l4proto->nlattr_size(); 306 if (l4proto->nlattr_tuple_size)

307 l4proto->nla_size += 3 * l4proto->nlattr_tuple_size(); 308

309 rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], 310 l4proto); 311

程序在277会检查nf_ct_protos数组中是否为NULL即是否已经有此三层协议注册过。如果没有,则分配相应的空间并且初始化成proto_arry,即把此三层协议对应的四层协议全部初始化成为nf_conntrack_l4proto_generic;

如果不为NULL,并且对应的四层协议不为nf_conntrack_l4proto_generic说明出错。

最后,调用rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],l4proto);赋值。

三层协议的注册:

163 int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto) 164 { 174 if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_l3proto_generic) 179 ret = nf_ct_l3proto_register_sysctl(proto); 183 if (proto->nlattr_tuple_size) 184 proto->nla_size = 3 * proto->nlattr_tuple_size(); 185 186 rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto); 190 return ret; 191 }

具体过程如四层协议的注册所说。进行检查,然后再赋值。

2.钩子的注册:

钩子注册函数就不多说;看注册的钩子类型:

157 static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {

158 { 159 .hook 160 .owner 161 .pf 162 .hooknum 163 .priority 164 }, 165 { 166 .hook 167 .owner 168 .pf 169 .hooknum 170 .priority 171 }, 172 { 173 .hook 174 .owner 175 .pf 176 .hooknum 177 .priority 178 }, 179 { 180 .hook 181 .owner 182 .pf 183 .hooknum 184 .priority 185 }, 186 };

附上netfilter钩子点的图

= ipv4_conntrack_in, = THIS_MODULE, = PF_INET,

= NF_INET_PRE_ROUTING, = NF_IP_PRI_CONNTRACK,

= ipv4_conntrack_local, = THIS_MODULE, = PF_INET,

= NF_INET_LOCAL_OUT, = NF_IP_PRI_CONNTRACK,

= ipv4_confirm, = THIS_MODULE, = PF_INET,

= NF_INET_POST_ROUTING,

= NF_IP_PRI_CONNTRACK_CONFIRM,

= ipv4_confirm, = THIS_MODULE, = PF_INET,

= NF_INET_LOCAL_IN,

= NF_IP_PRI_CONNTRACK_CONFIRM,

在4个钩子点分别注册了钩子函数。

在NF_INET_PRE_ROUTING, NF_INET_LOCAL_OUT,钩子点处,即数据包进入和刚刚出去的点上的有非常高的

优先级为NF_IP_PRI_CONNTRACK。

数据包在NF_INET_PRE_ROUTING时候会进入钩子ipv4_conntrack_in,之后在通向更加高层次NF_INET_LOCAL_IN时会进入ipv4_confirm钩子。

作为server如果有一个新的连接在PRE_ROUTING收到数据包,则为ORIG,然后server回复给client,此数据包会经过LOCAL_OUT出去那么这时的包就称为REPLY,再之后双方的数据交换就ESTABLISH。即ORIG和REPLY代表第一个和第二个数据包。

(1)ipv4_conntrack_in

133 static unsigned int ipv4_conntrack_in(unsigned int hooknum, 134 struct sk_buff *skb, 135 const struct net_device *in, 136 const struct net_device *out, 137 int (*okfn)(struct sk_buff *)) 138 {

139 return nf_conntrack_in(dev_net(in), PF_INET, hooknum, skb); 140 }

dev_net(in)取出net指针。由于是进入本机,所是参数为in;协议簇为PF_INET,hooknum为NF_INET_PRE_ROUTING;

686 nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, 687 struct sk_buff *skb) 688 { 689 struct nf_conn *ct; 690 enum ip_conntrack_info ctinfo; 691 struct nf_conntrack_l3proto *l3proto; 692 struct nf_conntrack_l4proto *l4proto; 693 unsigned int dataoff; 694 u_int8_t protonum; 695 int set_reply = 0; 696 int ret; 697 704 /* rcu_read_lock()ed by nf_hook_slow */ 705 l3proto = __nf_ct_l3proto_find(pf); 706 ret = l3proto->get_l4proto(skb, skb_network_offset(skb), 707 &dataoff, &protonum); 715 l4proto = __nf_ct_l4proto_find(pf, protonum); 728 729 ct = resolve_normal_ct(net, skb, dataoff, pf, protonum, 730 l3proto, l4proto, &set_reply, &ctinfo); 745 ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum); 757 758 if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status)) 759 nf_conntrack_event_cache(IPCT_STATUS, ct); 760 761 return ret; 762 }

----nf_conntrack_in程序会执行__nf_ct_l3proto_find(pf)从全局链表nf_conntrack_l3proto中取出

注册的l3协议;当然根据PF_INET取出的是nf_conntrack_l3proto_ipv4;于是执行

ipv4_get_l4proto,此函数的主要功能就是从skb数据包中取出ip数据包中的 承载的协议类型,并且保存在protonum中。

然后根据找到的protonum和传入的pf从nf_ct_protos[pf][protonum]来找到第四层协议。假设ip数据包承载的协议类型为IPPROTO_UDP;以后都以此假设;则取出的协议结构为:nf_conntrack_l4proto_udp4 。

之后调用了resolve_normal_ct函数;

/net/netfilter/nf_conntrack_core.c#resolve_normal_ct: 627 static inline struct nf_conn * 628 resolve_normal_ct(struct net *net, 629 630 631 632 633 634 635 636 637 { 638 639 640 641 642 643 644 649 650 651 652 653 } 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678

struct sk_buff *skb, unsigned int dataoff, u_int16_t l3num, u_int8_t protonum, struct nf_conntrack_l3proto *l3proto, struct nf_conntrack_l4proto *l4proto, int *set_reply, enum ip_conntrack_info *ctinfo) struct nf_conntrack_tuple tuple; struct nf_conntrack_tuple_hash *h; struct nf_conn *ct; if (!nf_ct_get_tuple(skb, skb_network_offset(skb), dataoff, l3num, protonum, &tuple, l3proto, l4proto)) /* look for tuple match */ h = nf_conntrack_find_get(net, &tuple); if (!h) { h = init_conntrack(net, &tuple, l3proto, l4proto, skb, dataoff); ct = nf_ct_tuplehash_to_ctrack(h); /* It exists; we have (non-exclusive) reference. */ if (NF_CT_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 we've had two way comms, always ESTABLISHED. */ if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { pr_debug(\ *ctinfo = IP_CT_ESTABLISHED; } else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) { pr_debug(\ ct); *ctinfo = IP_CT_RELATED; } else { pr_debug(\ *ctinfo = IP_CT_NEW; } *set_reply = 0;

679 } 680 skb->nfct = &ct->ct_general; 681 skb->nfctinfo = *ctinfo; 682 return ct; 683 }

-----resolve_normal_ct:642行调用nf_ct_get_tuple,此函数的主要作用就是从skb数据包中取出tuple连接记录结构需要的ip,port等等信息,并保存在tuple结构中。然后再调用

nf_conntrack_find_get从第一章所说的hash表中找到包含有这个tuple结构的节点。如果没有找到,则证明这是一个新的数据包,所以程序会调用了 init_conntrack函数来处理。 init_conntrack; /net/netfilter/nf_conntrack_core.c#init_conntrack: :

对一个连接来说它是双向的,发送方和接收方是相对的,所以先调用nf_ct_invert_tuple函数完成目的地和源反转。然后调用 nf_conntrack_alloc函数用于分配一个新的连接记录nf_conn;之后再把发送方,和接收方对应的tuple分别赋值给ct->tuplehash,再设置了计时器,用于进行超时后的处理。最后ct->ct_net = net。 在nf_conntrack_alloc完成了之后init_conntrack调用

nf_ct_acct_ext_add添加nf_conn的acct扩展,然后检查这个数据包是否为expectation连接,如果是,则会设置__set_bit(IPS_EXPECTED_BIT, &ct->status),并且进行expectation 相关的处理。之后再将ct->tuplehash[IP_CT_DIR_ORIGINAL]添加到net->ct.unconfirmed链表中。最后返回.

回到resolve_normal_ct之后检查方向: if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)

然后设置*ctinfo的值,ctinfo取值如下: 6 enum ip_conntrack_info 7 { 8 /* Part of an established connection (either direction). */ 9 IP_CT_ESTABLISHED, 10 11 /* Like NEW, but related to an existing connection, or ICMP error 12 (in either direction). */ 13 IP_CT_RELATED, 14 15 /* Started a new connection to track (only

16 IP_CT_DIR_ORIGINAL); may be a retransmission. */ 17 IP_CT_NEW, 18 19 /* >= this indicates reply direction */ 20 IP_CT_IS_REPLY, 21 22 /* Number of distinct IP_CT types (no NEW in reply dirn). */ 23 IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1 24 };

对于PRE_ROUTING钩子点上的数据包来说,都是IP_CT_DIR_ORIG;所以会直接执行else分支, 测试ct->status,前面在init_conntrack中,如果判断为expectation连接,则会设置IPS_EXPECTED位,于是*ctinfo = IP_CT_RELATED。对于一个新的连接来说 会执行 *ctinfo = IP_CT_NEW; 而IPS_SEEN_REPLY_BIT会在NF_INET_LOCAL_OUT,钩子点上设置。

同时设置*set_reply=0;然后skb->nfct=&ct->ct_general;skb->nfctinfo=*ctinfo使得通过skb可以取出ct的值。在后续钩子ipv4_comfirm会用到。然后函数返回ct;

回到nf_conntrack_in由于是IPPROTO_UDP,所以执行:udp_packet; /net/netfilter/nf_conntrack_proto_udp.c#udp_packet

* Returns verdict for packet, and may modify conntracktype */

67 static int udp_packet(struct nf_conn *ct, 68 const struct sk_buff *skb, 69 unsigned int dataoff, 70 enum ip_conntrack_info ctinfo, 71 u_int8_t pf, 72 unsigned int hooknum) 73 { 74 /* If we've seen traffic both ways, this is some kind of UDP 75 stream. Extend timeout. */ 76 if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { 77 nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout_stream); 78 /* Also, more likely to be important, and not a probe */ 79 if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status)) 80 nf_conntrack_event_cache(IPCT_STATUS, ct); 81 } else 82 nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout); 83 84 return NF_ACCEPT; 85 }

udp_packet:同样,由于是在NF_INET_PRE_ROUTING钩子点上,所以ct->status没有设置为

IPS_SEEN_REPLY_BIT。然后进入else分支,可以看到,这个函数一直在处理acct扩展相关的东西。 nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout_stream)::__nf_ct_refresh_acct(ct, ctinfo, skb, extra_jiffies, 1);:: /net/netfilter/nf_conntrack_core.c#804 函数重新设置了超时时间,之后进入 if (do_acct) { 841 struct nf_conn_counter *acct; 842 843 acct = nf_conn_acct_find(ct); 844 if (acct) { 845 acct[CTINFO2DIR(ctinfo)].packets++; 846 acct[CTINFO2DIR(ctinfo)].bytes += 847 skb->len - skb_network_offset(skb); 848 } 849 }

更新了acct计数器。

再回到nf_conntrack_in函数,

758 if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status)) 759 nf_conntrack_event_cache(IPCT_STATUS, ct); 如上所说,在NF_INET_PRE_ROUTING钩子点上,set_reply被resolve_normal_ct设置为0,所以不会执行。

(2)ipv4_confirm

/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c#ipv4_confirm ipv4_confirm?nf_conntrack_confirm

/include/net/netfilter/nf_conntrack_core.h#57

/* Confirm a connection: returns NF_DROP if packet must be dropped. */ 57 static inline int nf_conntrack_confirm(struct sk_buff *skb)

58 { 59 struct nf_conn *ct = (struct nf_conn *)skb->nfct; 60 int ret = NF_ACCEPT; 61 62 if (ct && ct != &nf_conntrack_untracked) { 63 if (!nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) 64 ret = __nf_conntrack_confirm(skb);

如果数据包没有被comfirm,并且连接没有dying,则进入__nf_conntrack_confirm进行下一步处理。 /net/netfilter/nf_conntrack_core.c#338

int__nf_conntrack_confirm(struct sk_buff *skb)

355 if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)

#define CTINFO2DIR(ctinfo)

((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL)

回顾enum ip_conntrack_info结构和resolve_normal_ct函数中,方向判断处: 661 if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) { 662 *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY; 663 /* Please set reply bit if this packet OK */ 664 *set_reply = 1;

665 }

可以看到 如果是IP_CT_DIR_REPLY方向的话,它的ctinfo值就会比IP_CT_IS_REPLY大。 所以这个宏就会返回IP_CT_DIR_REPLY.

对于一个IP_CT_DIR_REPLY数据包,则也没有comfirm的必要,直接接受。然后求出tuple,

reply_tuple的hash值,并且将自身从uncomfirmed链表删除。再调用__nf_conntrack_hash_insert(ct, hash, repl_hash);将自己插入到net->ct.hash中。然后设置定时器,并且将ct->status设置为IPS_CONFIRMED_BIT。

(3)ipv4_conntrack_local

/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c

142 static unsigned int ipv4_conntrack_local(unsigned int hooknum, 143 struct sk_buff *skb, 144 const struct net_device *in, 145 const struct net_device *out, 146 int (*okfn)(struct sk_buff *)) 147 { 148 /* root is playing with raw sockets. */ 149 if (skb->len < sizeof(struct iphdr) || 150 ip_hdrlen(skb) < sizeof(struct iphdr)) 151 return NF_ACCEPT; 152 return nf_conntrack_in(dev_net(out), PF_INET, hooknum, skb); 153 }

可以看到,ipv4_conntrack_local最终也调用了nf_conntrack_in;

同样,会取出注册的协议,然后进入resolve_normal_ct.由于是在NF_INET_LOCAL_OUT钩子点上,接着上面继续ipv4_conntrack_in中的流程,假设ipv4_conntrack_in中收到一个新的数据包,则在net->ct.hash就会有它的记录。由于是REPLY包,于是程序会执行到: 661 if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) {

662 *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY; 663 /* Please set reply bit if this packet OK */ 664 *set_reply = 1; 665 }

设置*ctinfo,set_reply=1;然后进入udp_packet;由于ct->status仍然不是IPS_SEEN_REPLY_BIT,所以,仅仅执行nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout);

然后, 758 if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status)) 会将ct->status设置为IPS_SEEN_REPLY_BIT,即看见了REPLY.

当此数据包再进入NF_INET_POST_ROUTING后,如(2)所说,基本不做处理。

3.establish

如(1)ipv4_conntrack_in->resolve_normal_ct

/* It exists; we have (non-exclusive) reference. */ 661 if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) { 662 *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY; 663 /* Please set reply bit if this packet OK */ 664 *set_reply = 1; 665 } else { 666 /* Once we've had two way comms, always ESTABLISHED. */ 667 if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { 668 pr_debug(\, ct); 669 *ctinfo = IP_CT_ESTABLISHED; 670 } else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) { 671 pr_debug(\, 672 ct); 673 *ctinfo = IP_CT_RELATED; 674 } else { 675 pr_debug(\, ct); 676 *ctinfo = IP_CT_NEW; 677 } 678 *set_reply = 0; 679 }

再次进入;方向IP_CT_DIR_ORIG所以直接进入else分支。然后由于在(3)中,status被设置为IPS_SEEN_REPLY_BIT,所以ctinfo=IP_CT_ESTABLISHED。并且*set_reply=0;

4.计时器

对于一个新到达的数据包:

在nf_conntrack_in->resolve_normal_ct->init_conntrack->nf_conntrack_alloc

530 setup_timer(&ct->timeout, death_by_timeout, (unsigned long)ct);

过程1. 然后在nf_conntrack_in中,调用 745 ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum);

对于IPPROTO_UDP:则调用了udp_packet: 76 if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { 77 nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout_stream); 78 /* Also, more likely to be important, and not a probe */ 79 if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status)) 80 nf_conntrack_event_cache(IPCT_STATUS, ct); 81 } else 82 nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout); 对于一个新的数据包,会直接进入到else中。 udp_packet->__nf_ct_refresh_acct

过程2. /* If not in hash table, timer will not be active yet */ 822 if (!nf_ct_is_confirmed(ct)) { 823 ct->timeout.expires = extra_jiffies; 824 event = IPCT_REFRESH; 825 } else { 826 unsigned long newtime = jiffies + extra_jiffies; 827 828 /* Only update the timeout if the new timeout is at least 829 HZ jiffies from the old timeout. Need del_timer for race 830 avoidance (may already be dying). */ 831 if (newtime - ct->timeout.expires >= HZ 832 && del_timer(&ct->timeout)) { 833 ct->timeout.expires = newtime; 834 add_timer(&ct->timeout); 835 event = IPCT_REFRESH; 836 } 837 }

会直接进入执行if分支。设置了超时时间。 然后;进入LOCAL_IN钩子点。

如以前所说,只有未comfirm的才能进入。

ipv4_confirm->nf_conntrack_confirm->__nf_conntrack_confirm 392 ct->timeout.expires += jiffies; 393 add_timer(&ct->timeout); 启动了timer;

在LOCAL_OUT钩子点上,又会进入nf_conntrack_in,由于为REPLY,则会在函数的最后设置IPS_SEEN_REPLY_BIT。

然后到了第二轮,数据包再次到达,进入过程1.进入if分支。过程2.中进入else分支。

更新了超时时间。如果数据没有被confirm,则timer不会启动,如果协议相关的数据在confirm以后,即下一次没有准时到达PRE_ROUTING,则会启超时函数death_by_timeout。 222 static void death_by_timeout(unsigned long ul_conntrack) 223 { 224 struct nf_conn *ct = (void *)ul_conntrack; 225 struct net *net = nf_ct_net(ct); 226 struct nf_conn_help *help = nfct_help(ct); 227 struct nf_conntrack_helper *helper; 228 229 if (help) {

230 rcu_read_lock(); 231 helper = rcu_dereference(help->helper); 232 if (helper && helper->destroy) 233 helper->destroy(ct); 234 rcu_read_unlock(); 235 } 236 237 spin_lock_bh(&nf_conntrack_lock); 238 /* Inside lock so preempt is disabled on module removal path. 239 * Otherwise we can get spurious warnings. */ 240 NF_CT_STAT_INC(net, delete_list); 241 clean_from_lists(ct); 242 spin_unlock_bh(&nf_conntrack_lock); 243 nf_ct_put(ct); 244 }

函数完成 了helper模块扩展的销毁,然后再执行clear_from_lists把ct从net->ct.hash上取出来。

5.helper扩展

实际的helper:

include/net/netfilter/nf_conntrack.h

/* nf_conn feature for connections that have a helper */ 79 struct nf_conn_help { 80 /* Helper. if any */ 81 struct nf_conntrack_helper *helper; 82 83 union nf_conntrack_help help; 84 85 struct hlist_head expectations; 86 87 /* Current number of expected connections */ 88 u8 expecting[NF_CT_MAX_EXPECT_CLASSES]; 89 };

/include/net/netfilter/nf_conntrack_helper.h struct nf_conntrack_helper 20 { 21 struct hlist_node hnode; /* Internal use. */ 22 23 const char *name; /* name of the module */ 24 struct module *me; /* pointer to self */ 25 const struct nf_conntrack_expect_policy *expect_policy; 26 27 /* Tuple of things we will help (compared against server response) */ 28 struct nf_conntrack_tuple tuple; 29 30 /* Function to call when data passes; return verdict, or -1 to 31 invalidate. */ 32 int (*help)(struct sk_buff *skb, 33 unsigned int protoff,

34 35 36 37 38 39 40 41 };

struct nf_conn *ct,

enum ip_conntrack_info conntrackinfo);

void (*destroy)(struct nf_conn *ct);

int (*to_nlattr)(struct sk_buff *skb, const struct nf_conn *ct); unsigned int expect_class_max;

(1) 初始化

nf_conntrack_init->nf_conntrack_init_init_net-> nf_conntrack_helper_init

/net/netfilter/nf_conntrack_helper.c

216 int nf_conntrack_helper_init(void) 217 { 218 int err; 219 220 nf_ct_helper_hsize = 1; /* gets rounded up to use one page */ 221 nf_ct_helper_hash = nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 222 &nf_ct_helper_vmalloc, 0); 223 if (!nf_ct_helper_hash) 224 return -ENOMEM; 225 226 err = nf_ct_extend_register(&helper_extend); 227 if (err < 0) 228 goto err1; 229

230 return 0;

先分配hash链表nf_ct_helper_hash

static struct hlist_head *nf_ct_helper_hash __read_mostly; 然后注册扩展helper_extend:

210 static struct nf_ct_ext_type helper_extend __read_mostly = { 211 .len = sizeof(struct nf_conn_help), 212 .align = __alignof__(struct nf_conn_help), 213 .id = NF_CT_EXT_HELPER, 214 };

(2)helper的获取

53 static inline struct nf_conn_help *nfct_help(const struct nf_conn *ct) 54 { 55 return nf_ct_ext_find(ct, NF_CT_EXT_HELPER); 56 }

然后就是对nf_ext_find的调用,见第三章3。

(3)helper的注册

/net/netfilter/nf_conntrack_helper.c#139

139 int nf_conntrack_helper_register(struct nf_conntrack_helper *me) 140 { 141 unsigned int h = helper_hash(&me->tuple); 142 143 BUG_ON(me->expect_policy == NULL); 144 BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES); 145 BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1); 146 147 mutex_lock(&nf_ct_helper_mutex); 148 hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]); 149 nf_ct_helper_count++; 150 mutex_unlock(&nf_ct_helper_mutex); 151 152 return 0; 153 }

先取得nf_conntrack_helper->tuple的hash值,然后根据此值将它添加到nf_ct_helper_hash表中。 此表在初始化的时候已经分配了大小。

static struct hlist_head *nf_ct_helper_hash __read_mostly; (4)helper如何工作

当新连接的数据包进入PRE_ROUTING时,函数init_conntrack用于分配并初始化一个新的conntrack. /net/netfilter/nf_conntrack_core.c#607

585 exp = nf_ct_find_expectation(net, tuple); 586 if (exp) { 587 pr_debug(\ 588 ct, exp); 589 /* Welcome, Mr. Bond. We've been expecting you... */ 590 __set_bit(IPS_EXPECTED_BIT, &ct->status); 591 ct->master = exp->master; 592 if (exp->helper) { 593 help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); 594 if (help) 595 rcu_assign_pointer(help->helper, exp->helper); 596 } 597

598 #ifdef CONFIG_NF_CONNTRACK_MARK 599 ct->mark = exp->master->mark; 600 #endif

601 #ifdef CONFIG_NF_CONNTRACK_SECMARK 602 ct->secmark = exp->master->secmark; 603 #endif 604 nf_conntrack_get(&ct->master->ct_general); 605 NF_CT_STAT_INC(net, expect_new); 606 } else { 607 __nf_ct_try_assign_helper(ct, GFP_ATOMIC); 608 NF_CT_STAT_INC(net, new);

609 } 610

关于expectation在ftp中有用,以后再看。

然后就是else中:__nf_ct_try_assign_helper(ct, GFP_ATOMIC); /net/netfilter/nf_conntrack_helper.c#

97 int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags) 98 { 99 int ret = 0; 100 struct nf_conntrack_helper *helper; 101 struct nf_conn_help *help = nfct_help(ct); 102 103 helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); 104 if (helper == NULL) { 105 if (help) 106 rcu_assign_pointer(help->helper, NULL); 107 goto out; 108 } 109 110 if (help == NULL) { 111 help = nf_ct_helper_ext_add(ct, flags); 112 if (help == NULL) { 113 ret = -ENOMEM; 114 goto out; 115 } 116 } else { 117 memset(&help->help, 0, sizeof(help->help)); 118 } 119 120 rcu_assign_pointer(help->helper, helper); 函数先从扩展数组中获取nf_conn_help扩展指针: 101 struct nf_conn_help *help = nfct_help(ct);

然后调用函数__nf_ct_helper_find根据tuple的hash值来找到对应的hlper,如ftp,tftp,等等。

48 static struct nf_conntrack_helper *

49 __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple) 50 { 51 struct nf_conntrack_helper *helper; 52 struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) }; 53 struct hlist_node *n; 54 unsigned int h; 55 56 if (!nf_ct_helper_count) 57 return NULL; 58 59 h = helper_hash(tuple); 60 hlist_for_each_entry_rcu(helper, n, &nf_ct_helper_hash[h], hnode) { 61 if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask)) 62 return helper; 63 } 64 return NULL; 65 }

最后如果都不为NULL:

120 rcu_assign_pointer(help->helper, helper);

这样,help->helper就指向了实际的协议对应的helper,如ftp,tftp.

(5) ftp的helper

i.初始化

/net/netfilter/nf_conntrack_ftp.c#nf_conntrack_ftp_init

542 static int __init nf_conntrack_ftp_init(void) 543 { 544 int i, j = -1, ret = 0; 545 char *tmpname; 546 547 ftp_buffer = kmalloc(65536, GFP_KERNEL); 548 if (!ftp_buffer) 549 return -ENOMEM; 550 551 if (ports_c == 0) 552 ports[ports_c++] = FTP_PORT; 553 554 /* FIXME should be configurable whether IPv4 and IPv6 FTP connections 555 are tracked or not - YK */ 556 for (i = 0; i < ports_c; i++) { 557 ftp[i][0].tuple.src.l3num = PF_INET; 558 ftp[i][1].tuple.src.l3num = PF_INET6; 559 for (j = 0; j < 2; j++) { 560 ftp[i][j].tuple.src.u.tcp.port = htons(ports[i]); 561 ftp[i][j].tuple.dst.protonum = IPPROTO_TCP; 562 ftp[i][j].expect_policy = &ftp_exp_policy; 563 ftp[i][j].me = THIS_MODULE; 564 ftp[i][j].help = help; 565 tmpname = &ftp_names[i][j][0]; 566 if (ports[i] == FTP_PORT) 567 sprintf(tmpname, \ 568 else 569 sprintf(tmpname, \ports[i]); 570 ftp[i][j].name = tmpname; 571 572 pr_debug(\ 573 \ 574 ftp[i][j].tuple.src.l3num, ports[i]); 575 ret = nf_conntrack_helper_register(&ftp[i][j]); 576 if (ret) { 577 printk(\ 578 \ 579 ftp[i][j].tuple.src.l3num, ports[i]); 580 nf_conntrack_ftp_fini(); 581 return ret; 582 }

583 584 585 586 587 }

}

}

return 0;

首先为ftp_buffer申请65536的空间大小,然后判断如果用户没有在加载模块时指定端口则使用默认。些时ports_c=1;表示只有一个端口。 然后进入ftp的初始化:

static struct nf_conntrack_helper ftp[MAX_PORTS][2] __read_mostly; 首先,第二维表示协议类型,INET4或者INET6; 之后,分别对两种协议的helper进行一系列的赋值。

再ret = nf_conntrack_helper_register(&ftp[i][j]);将helper注册到nf_ct_helper_hash中。

ii.helper的执行。

当数据包在确认时会执行相应的helper.

/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c#ipv4_confirm static unsigned int ipv4_confirm(unsigned int hooknum, 89 struct sk_buff *skb, 90 const struct net_device *in, 91 const struct net_device *out, 92 int (*okfn)(struct sk_buff *)) 93 { 94 struct nf_conn *ct; 95 enum ip_conntrack_info ctinfo; 96 const struct nf_conn_help *help; 97 const struct nf_conntrack_helper *helper; 98 unsigned int ret; 99 100 /* This is where we call the helper: as the packet goes out. */ 101 ct = nf_ct_get(skb, &ctinfo); 102 if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY) 103 goto out; 104 105 help = nfct_help(ct); 106 if (!help) 107 goto out; 108 109 /* rcu_read_lock()ed by nf_hook_slow */ 110 helper = rcu_dereference(help->helper); 111 if (!helper) 112 goto out; 113 114 ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb), 115 ct, ctinfo);

IP_CT_RELATED + IP_CT_IS_REPLY跟iptables的REJECT相关。 由i.可知,此处调用了/net/netfilter/nf_conntrack_ftp.c#help.

349 static int help(struct sk_buff *skb, 350 unsigned int protoff,

351 struct nf_conn *ct, 352 enum ip_conntrack_info ctinfo) 353 { 354 unsigned int dataoff, datalen; 355 const struct tcphdr *th; 356 struct tcphdr _tcph; 357 const char *fb_ptr; 358 int ret; 359 u32 seq; 360 int dir = CTINFO2DIR(ctinfo); 361 unsigned int uninitialized_var(matchlen), uninitialized_var(matchoff); 362 struct nf_ct_ftp_master *ct_ftp_info = &nfct_help(ct)->help.ct_ftp_info; 363 struct nf_conntrack_expect *exp; 364 union nf_inet_addr *daddr; 365 struct nf_conntrack_man cmd = {}; 366 unsigned int i; 367 int found = 0, ends_in_nl; 368 typeof(nf_nat_ftp_hook) nf_nat_ftp; 369 370 /* Until there's been traffic both ways, don't look in packets. */ 371 if (ctinfo != IP_CT_ESTABLISHED 372 && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { 373 pr_debug(\ctinfo); 374 return NF_ACCEPT; 375 }

判断连接是否ESTABLISH,若没,则返回。

377 th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); 378 if (th == NULL) 379 return NF_ACCEPT; 380 381 dataoff = protoff + th->doff * 4; 382 /* No data? */ 383 if (dataoff >= skb->len) { 384 pr_debug(\dataoff, 385 skb->len); 386 return NF_ACCEPT; 387 } 388 datalen = skb->len - dataoff; 求出数据长度。

390 spin_lock_bh(&nf_ftp_lock); 391 fb_ptr = skb_header_pointer(skb, dataoff, datalen, ftp_buffer); 取出ftp的数据指针。 411 cmd.l3num = nf_ct_l3num(ct); 412 memcpy(cmd.u3.all, &ct->tuplehash[dir].tuple.src.u3.all, 413 sizeof(cmd.u3.all)); 414 415 for (i = 0; i < ARRAY_SIZE(search[dir]); i++) { 416 found = find_pattern(fb_ptr, datalen, 417 search[dir][i].pattern, 418 search[dir][i].plen,

419 search[dir][i].skip, 420 search[dir][i].term, 421 &matchoff, &matchlen, 422 &cmd, 423 search[dir][i].getnum); 424 if (found) break; 425 }

根据num结构的内容,和dir,调用find_pattern()在数据中查找,看能否找到ftp的标志数据。 444 exp = nf_ct_expect_alloc(ct); 如果能够找到,然后分配了一个expectation;

484 nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, cmd.l3num, 485 &ct->tuplehash[!dir].tuple.src.u3, daddr, 486 IPPROTO_TCP, NULL, &cmd.u.tcp.port); 487

对expectation初始化。

nf_nat_ftp = rcu_dereference(nf_nat_ftp_hook); 491 if (nf_nat_ftp && ct->status & IPS_NAT_MASK) 492 ret = nf_nat_ftp(skb, ctinfo, search[dir][i].ftptype, 493 matchoff, matchlen, exp); 如果使用了nat,则调用nf_nat_ftp进一步处理。

由此看来,helper扩展的目的就是建立一个expectation.

419 search[dir][i].skip, 420 search[dir][i].term, 421 &matchoff, &matchlen, 422 &cmd, 423 search[dir][i].getnum); 424 if (found) break; 425 }

根据num结构的内容,和dir,调用find_pattern()在数据中查找,看能否找到ftp的标志数据。 444 exp = nf_ct_expect_alloc(ct); 如果能够找到,然后分配了一个expectation;

484 nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, cmd.l3num, 485 &ct->tuplehash[!dir].tuple.src.u3, daddr, 486 IPPROTO_TCP, NULL, &cmd.u.tcp.port); 487

对expectation初始化。

nf_nat_ftp = rcu_dereference(nf_nat_ftp_hook); 491 if (nf_nat_ftp && ct->status & IPS_NAT_MASK) 492 ret = nf_nat_ftp(skb, ctinfo, search[dir][i].ftptype, 493 matchoff, matchlen, exp); 如果使用了nat,则调用nf_nat_ftp进一步处理。

由此看来,helper扩展的目的就是建立一个expectation.

本文来源:https://www.bwwdw.com/article/uhuo.html

Top