zoukankan      html  css  js  c++  java
  • 至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不行。!


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

  • 相关阅读:
    2018/12/21 HDU-2077 汉诺塔IV(递归)
    2018-12-08 acm日常 HDU
    2018/12/12 acm日常 第二周 第六题
    git 添加远程分支,并可以code review.
    zookeeper数据迁移方法
    gem install nokogiri -v '1.6.6.2' 出错
    gem install json -v '1.8.2' error
    gem install bundle 安装失败
    全能型开源远程终端:MobaXterm
    如何写好 Git Commit 信息
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/4844601.html
Copyright © 2011-2022 走看看