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

新的版本号ipset

上周,一名医生在儿童医院等待一段差距叫做数量。接受NetfilterPush信息的邮件列表,列表ipset最新6.23版本号的新功能,非常喜欢我现在需要的是,特别是timeout和skbinfo支持参数,欲了解更多详情,请参阅自己manual,假设不想看那么多,我这里简单的贴一下:

   timeout
       All  set  types  supports the optional timeout parameter when creating a set and adding entries. The value of the timeout
       parameter for the create command means the default timeout value (in seconds) for new entries. If a set is  created  with
       timeout support, then the same timeout option can be used to specify non-default timeout values when adding entries. Zero
       timeout value means the entry is added permanent to the set.  The timeout value of already added elements can be  changed
       by readding the element using the -exist option. Example:

ipset create test hash:ip timeout 300
              ipset add test 192.168.0.1 timeout 60
              ipset -exist add test 192.168.0.1 timeout 600
...
   skbinfo, skbmark, skbprio, skbqueue
       All set types support the optional skbinfo extension. This extension allow to store the metainfo (firewall mark, tc class
       and  hardware queue) with every entry and map it to packets by usage of SET netfilter target with --map-set option.  skb┅\
       mark option format: MARK or MARK/MASK, where MARK and MASK are 32bit hex numbers with 0x prefix. If only mark  is  speci‐
       fied  mask  0xffffffff  are used.  skbprio option has tc class format: MAJOR:MINOR, where major and minor numbers are hex
       without 0x prefix.  skbqueue option is just decimal number.

ipset create foo hash:ip skbinfo
              ipset add foo skbmark 0x1111/0xff00ffff skbprio 1:10 skbqueue 10
...
   nomatch
       The  hash  set  types  which can store net type of data (i.e. hash:*net*) support the optional nomatch option when adding
       entries. When matching elements in the set, entries marked as nomatch are skipped as if those were not added to the  set,
       which makes possible to build up sets with exceptions. See the example at hash type hash:net below.

When  elements  are  tested  by ipset, the nomatch flags are taken into account. If one wants to test the existence of an
       element marked with nomatch in a set, then the flag must be specified too.
...

编译

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

0. You need the source tree of your kernel (version >= 2.6.32)
   and it have to be configured with ip6tables support enabled,
   modules compiled. For kernel versions < 2.6.39 please apply
   the netlink.patch against your kernel tree, which adds the
   new subsystem identifier for ipset.
而我的内核就是2.6.32版本号的,尽管比較老了。但没有办法。只是既然明说了支持2.6.32。那就放心了。除了README之外,其站点上也明白说明支持2.6.32内核:
For the new branch
    linux kernel source code (version >= 2.6.32)
    source of ipset: ipset-6.23.tar.bz2 (md5sum)
于是開始例行的工作:
tar xjvf ipset-6.23.tar.bz2
cd ipset-6.23
./configure
报告说没有为内核打netlink.patch。只是这不是什么事儿。可是在这个点上,我提出了质疑:
质疑:明明仅仅是更新了头文件,为何非要给内核源代码树打patch啊?
我感到这个动作不太合乎常规,为了不依赖源代码树,我将这个补丁打到了编译所需的2.6.32的内核头文件里,于是configure顺利通过,可是遇到了另一个问题。提示我没有安装libmnl,因为我这是一个干净的环境,所以在Netfilter站点下载了次新版的libmnl源代码。例行安装libmnl。这一步对于我而言并不是必须的,像我这样天天折腾Netfilter的,怎么可能不安装libmnl啊...
       接来下就是make了,顺利通过,然后make modules,报错一大堆。然后我感到。这个ipset-6.23根本TMD就不支持2.6.32,全部的文档说明都是TMD在胡扯!报错的是以下这个文件:
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‘:
/usr/src/ipset-6.23/kernel/net/netfilter/xt_set.c:99: error: ‘const struct xt_mtchk_param‘ has no member named ‘net‘
/usr/src/ipset-6.23/kernel/net/netfilter/xt_set.c:108: error: ‘const struct xt_mtchk_param‘ has no member named ‘net‘
/usr/src/ipset-6.23/kernel/net/netfilter/xt_set.c: In function ‘set_match_v0_destroy‘:
/usr/src/ipset-6.23/kernel/net/netfilter/xt_set.c:123: error: ‘const struct xt_mtdtor_param‘ has no member named ‘net‘
/usr/src/ipset-6.23/kernel/net/netfilter/xt_set.c: In function ‘set_match_v1_checkentry‘:
/usr/src/ipset-6.23/kernel/net/netfilter/xt_set.c:148: error: ‘const struct xt_mtchk_param‘ has no member named ‘net‘
/usr/src/ipset-6.23/kernel/net/netfilter/xt_set.c:157: error: ‘const struct xt_mtchk_param‘ has no member named ‘net‘
/usr/src/ipset-6.23/kernel/net/netfilter/xt_set.c: In function ‘set_match_v1_destroy‘:
/usr/src/ipset-6.23/kernel/net/netfilter/xt_set.c:169: error: ‘const struct xt_mtdtor_param‘ has no member named ‘net‘
/usr/src/ipset-6.23/kernel/net/netfilter/xt_set.c: In function ‘set_target_v0‘:
/usr/src/ipset-6.23/kernel/net/netfilter/xt_set.c:231: error: ‘const struct xt_match_param‘ has no member named ‘targinfo‘
/usr/src/ipset-6.23/kernel/net/netfilter/xt_set.c: In function ‘set_target_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
/usr/src/ipset-6.23/kernel/net/netfilter/ipset/ip_set_core.c: In function ‘call_ad‘:
/usr/src/ipset-6.23/kernel/net/netfilter/ipset/ip_set_core.c:1444: error: ‘SIZE_MAX‘ undeclared (first use in this function)
/usr/src/ipset-6.23/kernel/net/netfilter/ipset/ip_set_core.c:1444: error: (Each undeclared identifier is reported only once
/usr/src/ipset-6.23/kernel/net/netfilter/ipset/ip_set_core.c:1444: error: for each function it appears in.)
/usr/src/ipset-6.23/kernel/net/netfilter/ipset/ip_set_core.c:1444: warning: type defaults to ‘int‘ in declaration of ‘_min1‘
/usr/src/ipset-6.23/kernel/net/netfilter/ipset/ip_set_core.c:1444: warning: comparison of distinct pointer types lacks a cast
make[4]: *** [/usr/src/ipset-6.23/kernel/net/netfilter/ipset/ip_set_core.o] 错误 1
说的是SIZE_MAX未定义。这个常量在3.5以上的高版本号内核上才有,2.6.32假设不手工定义的话依据不能编译通过,尽管手工定义一下并不难,可是越发认为ipset-6.23的编译文档和支持版本号的说明文档就是在不负责任地胡说八道。简直就是在扯淡!。我找到了另外一篇文档。上面明白了一个多少还能说得过去的事实:
2 Supported Configurations
* iptables >= 1.4.3
* kernel-source >= 2.6.29
For ipset-6 you need:
* libmnl
* Linux kernel >= 2.6.35
起码和ipset-6.23源代码中的凝视能对得上。除此之外也是在胡扯。
       这个事实太恶心人了啊!我定义了SIZE_MAX之后,编译就能够顺利通过了,尽管有一些类型转换的警告,可是忽略它们并不会有什么大不了的后果。于是我认为为了让ipset-6.23自带的文档更好地服务大众,有必要做一个真正的patch,其实我确实这么做了,我想做的事情有以下的目标:
1.编译ipset-6.23仅仅依赖内核头文件而不再依赖源代码。
2.依赖内核版本号而适配数据结构和宏定义而不是在内核源代码中找特征值。

因为我的目标仅仅是在2.6.32内核上编译成功,因为我并没有測试2.6.33/34/35/36以及3.0/1/2/3/4/5/6/7/8...但我相信,3.5以上的内核版本号上是一定能够成功编译的。

ipset-6.23的打包者可能根本就没有在低版本号比方2.6.32内核上进行測试。这个工作以及紧随其后的修正工作或许本来就是留给我这种人的,再者说。2.6.32这个版本号或许使用的人本来就不多,不提供全訪问的支持也是理所当然。我并没有怪作者和打包者的意思,或许在修正过程中确实有些冲动。所以在此澄清。

这让人想起了时尚这个名词,这是在19世纪末粗放的工业化达到顶峰时诞生的一个词。人们普遍认为,进步是必定的,最新的就是最好的,变化的向前的,速度是加快的,保持时尚的方式就是站在潮流最前端。这个理念被普遍信奉和传承,一直到Linux 3.X内核时代...假设你还在用2.6.9内核,并且发现了它的一个大bug,没人会理你的。站在潮流最前端的冲浪者会说:世界在进步,为何不试试3.17版本号的内核呢?。
       直接进入ipset-6.23文件夹,运行patch -p1 < ../ipset-6.23.patch就可以,而ipset-6.23.patch的内容例如以下:

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 *)

打上这个patch,在2.6.32内核上便能够直接编译ipset-6.23软件包了。

编译后记:

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.
 */

ipset就是将自己的数据放在了gen字段里面。假设net因为HAVE_CHECKENTRY_BOOL宏置为NULL了,那么皮之不存,毛将焉附?即便编译通过,能用吗?其实。因为在取值的时候,并没有推断net是否为空:

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.6.32内核可能对net命名空间支持的还不够完好。可是无论如何,init_net是存在的,因此我将XT_PAR_NET定义成了&init_net。

2.我是不是该提交一个patch

我怕被骂。并且我也不想骂人。所以这件事交给远方的朋友去做了。在我看来,非常多犯错误的人都是执迷不悟的。轻轻说一句就能够展开骂战。

仅仅会编程的人是惹不起的。

别的不说。反正我是看着README操作的。上面写了>=2.6.32的都能够,然而我就是没法编译,confiure里面的办法真的非常恶心。

反正不正确写入!。。跟风TMD不行。!

版权声明:本文博主原创文章,博客,未经同意不得转载。

时间: 2024-10-13 05:17:57

至Linux-2.6.32编译内核ipset-6.23坎坷的经历的相关文章

linux源码升级编译内核

内核源码更新内核(以linux内核4.11.7为例子) 1. 内核更新前的工作准备 a. 系统:ubuntu16.04的Desktop版本,内核为linux-4.8.0: b. 虚拟机VMware Workstation Pro 12,并安装上述的ubuntu版本系统: c. 配置好相应的ubuntu16.04的基本操作,包括安装vim,nfs等必要包: d. 在http://www.kernel.org上下载最新的内核源码包,本文使用的为当时最新的内核源码包linux-4.11.7.tar.x

linux编译内核2.6.32.67

上传linux内核至主机,linux内核文件格式为tar.xz. [[email protected] home]# xz -d linux-2.6.32.67.tar.xz    [[email protected] home]# ls     linux-2.6.32.67.tar xz -d解压后,会删除tar.xz文件,仅留下tar包 [[email protected] home]# tar xvf linux-2.6.32.67.tar -C /usr/src 展开归档文件至/usr

启动流程排错,自制linux内核,编译内核

CentOS 6启动流程: POST --> Boot Sequence(BIOS) --> Boot Loader --> Kernel(ramdisk) --> rootfs--> switchroot--> /sbin/init-->(/etc/inittab, /etc/init/*.conf) --> 设定默认运行级别--> 系统初始化脚本rc.sysinit--> 关闭或启动对应级别的服务--> 启动终端 grub legacy

[Mini2440] 编译 linux-2.6.32.2 内核

一 准备好开发环境 需要创建一个 Ubuntu 虚拟机,并且安装好 VMwareTools 方便和 Windows 之间传文件. 创建好一个 mini2440 的工作目录,后续所有 mini2440 相关的文件均存放在此目录下. [email protected]:~$ mkdir -p workspace/mini2440 二 安装交叉编译器 在 mini2440 工作目录下创建 package 目录,存放软件包. [email protected]:~$ mkdir workspace/mi

Linux重新编译内核指南

Linux的一个重要的特点就是其源代码的公开性,全世界任何一个软件工程师都可以将自己认为优秀的代码加入到其中,由此引发的一个明显的好处就是Linux修补漏洞的快速以及对最新软件技术的利用.而Linux的内核则是这些特点的最直接的代表. 为什么要升级内核 正如它的名字的字面意义,Linux的内核承担着Linux操作系统的最为核心的任务,是其它程序和硬件等运行过程中的仲裁者:它要管理所有进程的内存,保证它们都能平等得到处理器的时隙.此外,它还提供程序和硬件之间的接口等功能. 通常,更新的内核会支持更

Linux 编译内核

编译内核步骤: 1.先查看自己OS使用的内核版本 [email protected]:~$ uname -r4.4.0-51-generic[email protected]:~$ 2.如果安装系统时,自动安装了源码.在 /usr/src 目录下有对应的使用的版本目录(ubuntu安装完一般没有源码需要自己下载).如下: 后缀generic表示通用版. [email protected]:/usr/src$ lslinux-headers-4.4.0-21  linux-headers-4.4.

linux内核编译,内核参数修改

核心(kernel):/boot/vmlinuz-version version 带发行包版本,本地版本内核模块(kernel object): /lib/modules/version/ 内核设计:    单内核        模块化设计--内核小,功能做成模块    微内核装载模块:    insmod    modprobe 内核下载:www.kernel.org 2(主).6(次).17(修正)-->2.6.17.1修复bug2.6.18.rc1(候补版) 新增功能 用户空间访问,监控内

linux学习之路之手动编译内核

手动编译内核 一般来说,内核的核心功是直接在内核里,其他大多数功能具有以下三种选择: 1.不使用该功能 2.将这些功能编译成模块的形式 3.将这些功能直接编译进内核当中 那么该如何手动编译内核呢? 手动编译的方式: 使用make gconfig命令,不过使用该命令适用于Gnome桌面环境,且需要安装图形开发库 使用make kconfig命令,该命令适用于KDE桌面环境,且需要安装图形开发库 不过最常见的方式是使用make menuconfig命令来编译,使用该命令会产生一个窗口界面 在该窗口下

【Linux学习】Ubuntu下内核编译(一)

(1)当要执行内核配置时,输入make menuconfig时出现错误 遇到这个问题,主要是以为没有ncurses库,而make menconfig 需要这个库,因此需要安装ncurses或者ncurses-devel即可.输入命令:sudo  apt-get  install ncurses或者sudo apt-get  install ncurses-devel. (2)错误 drivers/input/touchscreen/eeti_ts.c: In function 'eeti_ts_