conntrack)中缓存私有数据省去每次查找

前面说过很多次,conntrack作为一中连接跟踪机制,如果它本身是可扩展的,那么将会是多么令人激动的一件事,当你看了N多文档代码之后,你发现它确实是可以扩展的,但是却没有感到激动,因为你可能发现:

1.它可以注册一个account扩展,但是计数机制却很原始;
2.我希望增加一个新型的扩展,却不得不重新编译内核;

怎么办?我曾经很生气地默默指责过当初实现这个的人,想当然的认为将扩展本身也做成可扩展的,而不是写死几个特定的扩展将是一个多么容易的事,我一直憋着没有去做这个实现,就是因为觉得它太简单,在工作中也确实需要一个新的扩展,然而既有的扩展类型中没有,为了不重新编译内核,我只好盗用了acct扩展。采用了一个OO中典型的封装方法:

struct my_ext {     struct orig_ext;     char info[0]; };

...
是时候改变一些事态了。基于下面几个原因,在周六的早上,我突然决定在周末完成它:
1.外部因素:好不容易感冒了,作为一个羸弱的人,我不希望得到别人的同情,只需要获得周末的安静,感冒发烧是最好的选择;
2.内部因素:年终总结完了,工作计划也确定了,后面是个收网的过程,稳为重,不需要太激进,因此也就没有什么技术上不可控的因素,心理安了,事就可以开始做了;
可能我又要笑话自己了,不就写个简单的模块么?怎么搞得跟诸葛孔明布阵一样...如此感性且主观一人怎么就...
      不管怎么讲,这个模块看起来确实是简单的。然而一旦做起来,发现有两个比较严重的问题:
1.反射内省问题
如果conntrack的extend有128个slot,每个slot里面放一个私有数据。问题是,程序怎么知道哪个slot里面有哪个数据。程序有能力存储,但是程序自己却不知道这一点...这就是一个怪圈,你必须让数据成为自描述的,或者就规定死第n号slot必须放路由项,第m个slot必须放 socket...现有的nf conntrack模块使用了后一种方法,即枚举nf_ct_ext_id做的事。
      可是我还是想随机选择slot,这样更加灵活。自描述的数据结构也看了不好,ASN.1太复杂,且内核数据更多的不是标识属性,而是定义一种行为,google的protocol buffer也不是很合适,需要定义太多的回调函数来完成反射自省..后来我想了一个办法,那就是定义个索引蓝图,标识“slot索引的索引”,而不是标识具体slot的位置。
      这就需要定义一个新的枚举,定义蓝图:

enum idx_idx{ ROUTE, SOCKET, AND_SO_ON, IDX_IDX_NUM };

然后定义一个数组来标识真正的索引:

int idx[IDX_IDX_NUM];

定义一个bitmap来表示slot的使用情况即可,具体的做法可以看代码,一目则了然。
2.内存寻址问题
内核内存是宝贵的,不是说物理内存用不起,而是它的虚拟地址空间也是有限的,因此建议使用64位系统,如果是32位系统,如果希望内核保存比较大的数据结构,请在编译的时候按照2G/2G或者1G/3G来拆分地址空间,前者情况用户和内核各自占据2G,后者的话内核占3G,用户仅仅占1G。
      也许就是因为存在这个内存问题,Linux的nf conntrack限制了extend的内存使用,其最大长度字段数据类型是u8。由于我知道我的系统,所以我将其改为了u16。你必须要知道的是,nf connrtack的extend内存使用时是连续的,你不能采用一个sizeof(char *)大小的空间保存一个指针,然后这个指针指向一个超级大的连续空间...但是为什么不能呢?还是因为代码的普适问题,我了解我的系统,所以我可以使用保存指针的做法。另外我还保留了数组的方式,总之,数组和指针分工是明确的,数组用于extend的寻址,而指针用于数据的获取。
      代码包括一个框架和一个测试程序,内核还是2.6.32 amd64,已经在github上了:https://github.com/marywangran/extension-of-nf_conntrack-ext
      还是在这里贴一份备份,怕哪天github被wall了...

修改include/net/netfilter/nf_conntrack_extend.h:

--- nf_conntrack_extend.h.orig  2014-03-29 12:55:26.000000000 +0800 +++ nf_conntrack_extend.h   2015-01-15 17:28:39.000000000 +0800 @@ -3,13 +3,17 @@  #include <net/netfilter/nf_conntrack.h> +#define NFCT_EXT_EXT +  enum nf_ct_ext_id  {     NF_CT_EXT_HELPER,     NF_CT_EXT_NAT,     NF_CT_EXT_ACCT,     NF_CT_EXT_ECACHE, -   NF_CT_EXT_NEW, +#ifdef NFCT_EXT_EXT +   NF_CT_EXT_EXT, +#endif     NF_CT_EXT_NUM,  }; @@ -17,13 +21,21 @@  #define NF_CT_EXT_NAT_TYPE struct nf_conn_nat  #define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter  #define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache -#define NF_CT_EXT_NEW_TYPE struct nf_conntrack_new +#ifdef NFCT_EXT_EXT +#define NF_CT_EXT_EXT_TYPE struct nf_conntrack_ext +#endif  /* Extensions: optional stuff which isn't permanently in struct. */  struct nf_ct_ext {     struct rcu_head rcu; +#ifdef NFCT_EXT_EXT +   /* 内存不再是个事儿 */ +   u16 offset[NF_CT_EXT_NUM]; +   u16 len; +#else     u8 offset[NF_CT_EXT_NUM];     u8 len; +#endif     char data[0];  }; @@ -80,10 +92,18 @@     unsigned int flags;     /* Length and min alignment. */ +#ifdef NFCT_EXT_EXT +   /* 内存不再是个事儿 */ +   u16 len; +   u16 align; +   /* initial size of nf_ct_ext. */ +   u16 alloc_size; +#else     u8 len;     u8 align;     /* initial size of nf_ct_ext. */     u8 alloc_size; +#endif  };  int nf_ct_extend_register(struct nf_ct_ext_type *type);

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/14f5351a21aca3864809c5f9dd727877.html