于Linux-2.6.32内核上编译ipset-6.23的坎坷经历


新版本的ipset

上周在儿童医院给小小看病等待叫号的间隙,收到了Netfilter邮件列表的推送消息,一览了ipset最新的6.23版本的新特性,很多正是我目前所需要的,特别是timeout和skbinfo参数的支持,具体的详情请自行查看manual,如果不想看那么多,我这里简单的贴一下:

timeout

编译

总之,相比较老版本的4.5,确实增加了不少新的东西,于是就迫不及待地下载,编译,试用,一般而言,这些步骤都是例行的,都不会遇到什么特别大的困难,特别是看了其README之后:

0. You need the source tree of your kernel (version >= 2.6.32)
而我的内核就是2.6.32版本的,虽然比较老了,但没有办法。不过既然明说了支持2.6.32,那就放心了,除了README之外,其网站上也明确说明支持2.6.32内核:
For the new branch
于是开始例行的工作:
tar xjvf ipset-6.23.tar.bz2
报告说没有为内核打netlink.patch,不过这不是什么事儿,但是在这个点上,我提出了质疑:
ipset-6.23/kernel/net/netfilter/xt_set.c
/usr/src/ipset-6.23/kernel/net/netfilter/xt_set.c: In function 'set_match_v0_checkentry':
我在这个源文件中发现了下面的宏定义:
#ifdef HAVE_CHECKENTRY_BOOL #define CHECK_OK 1 #define CHECK_FAIL(err) 0 #define CONST const #define FTYPE bool #define XT_PAR_NET(par) NULL #else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ #define CHECK_OK 0 #define CHECK_FAIL(err) (err) #define CONST #define FTYPE int #define XT_PAR_NET(par) (par)->net #endif 很显然,根据注释,我应该定义HAVE_CHECKENTRY_BOOL,但是这个宏的定义应该自动化才合理,完全不应该去手工干预,在configure文件中,发现了下面的定义语句:
if test -f $ksourcedir/net/netfilter/xt_state.c && \ $GREP -q 'bool state_mt_check' $ksourcedir/net/netfilter/xt_state.c; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } HAVE_CHECKENTRY_BOOL=define else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } HAVE_CHECKENTRY_BOOL=undef fi 注意$ksourcedir/net/netfilter/xt_state.c这个万恶的语句,我顿时火冒三丈,指定源码文件就是为了在这个文件中寻找match check回调函数的返回值类型,作者难道不能通过内核的版本号还区分吗?难道对于一个固定的内核版本,match check的返回值规范不是固定的吗?对于2.6.32内核,下面的调用难道可以改变吗?:
if (par->target->checkentry != NULL && !par->target->checkentry(par)) return -EINVAL; 我很生气,但我不能强求开源软件一定怎么怎么样,就像比尔.盖茨曾经说的那样,得不到报酬的程序员是写不出一流的软件的,这一点我稍微有点信了。好吧,我指定一定源码文件,让configure的过程去定义那个万恶的HAVE_CHECKENTRY_BOOL宏,值得注意的是,除了那个宏之外,起到相同的旨在不同内核版本间适配作用的还有一个宏:HAVE_XT_TARGET_PARAM,它的作用是在没有定义xt_action_param结构体的低版本内核中将其定义为xt_target_param,在必要的时候强转成xt_match_param。定义了这两个宏之后,xt_set编译通过,但是ipset内核模块本身却报错了,而这个ipset内核模块本身是要比xt_set更重要的,要知道xt_set只是一个和iptables联动时所用的模块,即便真的无法适配,自己写一个应该也不难,然而对于ipset本身的内核模块,如果要自己写,那就相当于自己实现ipset-6.23本身了...还好,这次的新错误不多:
CC [M] /usr/src/ipset-6.23/kernel/net/netfilter/ipset/ip_set_core.o
说的是SIZE_MAX没有定义,这个常量在3.5以上的高版本内核上才有,2.6.32如果不手工定义的话根据不能编译通过,虽然手工定义一下并不难,但是越发觉得ipset-6.23的编译文档和支持版本的说明文档就是在不负责任地胡说八道,简直就是在扯淡!!我找到了另外一篇文档,上面明确了一个多少还能说得过去的事实:
2 Supported Configurations
起码和ipset-6.23源码中的注释能对得上,除此之外也是在胡扯。
1.编译ipset-6.23仅仅依赖内核头文件而不再依赖源码;

diff -Nur ipset-6.23/kernel/include/linux/netfilter/ipset/ip_set.h ipset-6.23.new/kernel/include/linux/netfilter/ipset/ip_set.h --- ipset-6.23/kernel/include/linux/netfilter/ipset/ip_set.h 2014-09-23 19:18:34.000000000 +0800 +++ ipset-6.23.new/kernel/include/linux/netfilter/ipset/ip_set.h 2014-11-13 16:27:15.000000000 +0800 @@ -26,6 +26,9 @@ #define IP_SET_MODULE_DESC(a, b, c) \ _IP_SET_MODULE_DESC(a, __stringify(b), __stringify(c)) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,35) +#define SIZE_MAX (~(size_t)0) +#endif /* Set features */ enum ip_set_feature { IPSET_TYPE_IP_FLAG = 0, diff -Nur ipset-6.23/kernel/net/netfilter/xt_set.c ipset-6.23.new/kernel/net/netfilter/xt_set.c --- ipset-6.23/kernel/net/netfilter/xt_set.c 2014-09-23 19:18:34.000000000 +0800 +++ ipset-6.23.new/kernel/net/netfilter/xt_set.c 2014-11-13 16:26:50.000000000 +0800 @@ -28,12 +28,18 @@ MODULE_ALIAS("ipt_SET"); MODULE_ALIAS("ip6t_SET"); -#ifdef HAVE_CHECKENTRY_BOOL +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,35) #define CHECK_OK 1 #define CHECK_FAIL(err) 0 #define CONST const #define FTYPE bool +/* Only confirm version 2.6.32 :) */ +#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,32) +/* netns is not supported completly */ +#define XT_PAR_NET(par) (&init_net) +#else #define XT_PAR_NET(par) NULL +#endif #else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */ #define CHECK_OK 0 #define CHECK_FAIL(err) (err) @@ -217,7 +223,7 @@ /* Revision 0 interface: backward compatible with netfilter/iptables */ -#ifdef HAVE_XT_TARGET_PARAM +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,35) #undef xt_action_param #define xt_action_param xt_target_param #define CAST_TO_MATCH (const struct xt_match_param *)
编译后记:

1.关于命名空间

ipset-6.23的HAVE_CHECKENTRY_BOOL宏将XT_PAR_NET定义成了NULL,而XT_PAR_NET这个宏取出的是net结构体:
struct net { ... struct net_generic *gen; } 值得注意的是gen字段:
struct net_generic { unsigned int len; struct rcu_head rcu; void *ptr[0]; }; 我们看一下它的注释:
/* * Generic net pointers are to be used by modules to put some private * stuff on the struct net without explicit struct net modification * * The rules are simple: * 1. register the ops with register_pernet_gen_device to get the id * of your private pointer; * 2. call net_assign_generic() to put the private data on the struct * net (most preferably this should be done in the ->init callback * of the ops registered); * 3. do not change this pointer while the net is alive; * 4. do not try to have any private reference on the net_generic object. * * After accomplishing all of the above, the private pointer can be * accessed with the net_generic() call. */
static inline struct ip_set_net *ip_set_pernet(struct net *net) { return net_generic(net, ip_set_net_id); }这样就会导致panic崩溃。因此这个原始的代码根本就不可能在HAVE_CHECKENTRY_BOOL宏被定义了的时候使用。因为net为空,如果判断了,内核不会崩溃,但是却取不到任何数据,如果没有判断,内核就会崩溃。因此这个代码本身可以说是错误的!
2.我是不是该提交一个patch我怕被骂,而且我也不想骂人,所以这件事交给远方的朋友去做了。在我看来,很多犯错误的人都是执迷不悟的,轻轻说一句就可以展开骂战。只会编程的人是惹不起的。别的不说,反正我是看着README操作的,上面写了>=2.6.32的都可以,然而我就是没法编译,confiure里面的办法真的很恶心。反正就是写的不对!!!照着做就是TMD不行!!!

相关内容