【问题标题】:What is the correct way to define a Netfilter hook function?定义 Netfilter 钩子函数的正确方法是什么?
【发布时间】:2017-05-26 01:17:24
【问题描述】:

我正在为 Linux 编写一个内核模块(更具体地说,是一个 Netfilter 模块)。我正在尝试使其与各种内核兼容,但是入口功能给我带来了麻烦。

从 LXR,我可以看到 nf_hookfn typedef 在内核 3.13 中发生了变化。

Linux 3.12 and before:

typedef unsigned int nf_hookfn(unsigned int hooknum, (...));

3.13 onwards:

typedef unsigned int nf_hookfn(const struct nf_hook_ops *ops, (...));

但是,我们有一台声称使用内核 3.10.0-123.4.4.el7.x86_64 的 Red Hat 机器,但它的 netlink.h 读取

typedef unsigned int nf_hookfn(const struct nf_hook_ops *ops, (...));

好像是 3.13+ 代码。

它在我的模块上引起警告,因为它完全破坏了我根据内核版本以不同方式定义函数的尝试:

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
#define HOOK_ARG_TYPE const struct nf_hook_ops *
#else
#define HOOK_ARG_TYPE unsigned int
#endif

我错过了哪些文档?从来没有人向我建议过内核 API 依赖于内核版本和发行版,这是没有意义的。

更重要的是,我该如何解决这个问题? nf_hookfn 是 typedef,而不是宏,所以我不能把它放在我的函数定义中。 可能使事情变得更容易的一件事是我从不使用那个论点。

我肯定不是第一个遇到这种情况的人吗?我的意思是nf_hookfn 是任何Netfilter 模块的入口点;你会认为他们通过更改它破坏了成千上万的驱动程序。

【问题讨论】:

    标签: linux linux-kernel netfilter


    【解决方案1】:

    最后我只写了一个entire module 专用于此:

    /**
     * The kernel API is far from static. In particular, the Netfilter packet entry
     * function keeps changing. nf_hook.c, the file where we declare our packet
     * entry function, has been quite difficult to read for a while now. It's pretty
     * amusing, because we don't even use any of the noisy arguments.
     *
     * This file declares a usable function header that abstracts away all those
     * useless arguments.
     */
    
    #include <linux/version.h>
    
    /* If this is a Red Hat-based kernel (Red Hat, CentOS, Fedora, etc)... */
    #ifdef RHEL_RELEASE_CODE
    
    #if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 2)
    #define NF_CALLBACK(name, skb) unsigned int name( \
            const struct nf_hook_ops *ops, \
            struct sk_buff *skb, \
            const struct net_device *in, \
            const struct net_device *out, \
            const struct nf_hook_state *state) \
    
    #elif RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 0)
    #define NF_CALLBACK(name, skb) unsigned int name( \
            const struct nf_hook_ops *ops, \
            struct sk_buff *skb, \
            const struct net_device *in, \
            const struct net_device *out, \
            int (*okfn)(struct sk_buff *))
    
    #else
    
    /*
     * Sorry, I don't have headers for RHEL 6 and below because I'm in a bit of a
     * deadline right now.
     * If this is causing you trouble, find `nf_hookfn` in your kernel headers
     * (typically in include/linux/netfilter.h) and add your version of the
     * NF_CALLBACK macro here.
     * Also, kernel headers per version can be found here: http://vault.centos.org/
     */
    #error "Sorry; this version of RHEL is not supported because it's kind of old."
    
    #endif /* RHEL_RELEASE_CODE >= x */
    
    
    /* If this NOT a RedHat-based kernel (Ubuntu, Debian, SuSE, etc)... */
    #else
    
    #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
    #define NF_CALLBACK(name, skb) unsigned int name( \
            void *priv, \
            struct sk_buff *skb, \
            const struct nf_hook_state *state)
    
    #elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
    #define NF_CALLBACK(name, skb) unsigned int name( \
            const struct nf_hook_ops *ops, \
            struct sk_buff *skb, \
            const struct nf_hook_state *state)
    
    #elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
    #define NF_CALLBACK(name, skb) unsigned int name( \
            const struct nf_hook_ops *ops, \
            struct sk_buff *skb, \
            const struct net_device *in, \
            const struct net_device *out, \
            int (*okfn)(struct sk_buff *))
    
    #elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
    #define NF_CALLBACK(name, skb) unsigned int name( \
            unsigned int hooknum, \
            struct sk_buff *skb, \
            const struct net_device *in, \
            const struct net_device *out, \
            int (*okfn)(struct sk_buff *))
    
    #else
    #error "Linux < 3.0 isn't supported at all."
    
    #endif /* LINUX_VERSION_CODE > n */
    
    #endif /* RHEL or not RHEL */
    

    所以不要这样:

    static unsigned int function_name((...), struct sk_buff *skb, (...))
    {
        return do_something_with_skb(skb);
    }
    

    这样做:

    static NF_CALLBACK(function_name, skb)
    {
        return do_something_with_skb(skb);
    }
    

    【讨论】:

      【解决方案2】:

      您可能正在使用 ko,它是为较新版本的内核编译的。如果模块不是作为 RH 提供的,那么您可能需要与供应商合作才能解决此问题。

      【讨论】:

      • 我实际上是在尝试编码一个内核模块,并使其与各种内核兼容。呃......我想我的问题不清楚。我会编辑它。
      猜你喜欢
      • 2019-09-30
      • 2012-06-18
      • 2019-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-12
      • 2022-01-20
      相关资源
      最近更新 更多