前面说过很多次,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的位置。
这就需要定义一个新的枚举,定义蓝图:
然后定义一个数组来标识真正的索引:
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);