Netfilter 钩子状态连接包过滤

问题描述

我正在编写一个 Netfilter 钩子,想要对传入的 TCP 数据包进行状态分析,无论它们是属于现有连接还是正在启动的新连接。 这是我第一次尝试使用 Netfilter 编写代码,在阅读 https://people.netfilter.org/pablo/docs/login.pdf 后,我明白我需要检查数据包是否被归类为 NEW 或 ESTABLISHED 状态。但是我找不到关于如何为此编写代码的任何文档。

static unsigned int hfunc(void *priv,struct sk_buff *skb,const struct nf_hook_state *state) {
    struct iphdr *iph;
    struct udphdr *udph;
    if (!skb)
        return NF_ACCEPT;

    iph = ip_hdr(skb);
    if (iph->protocol == IPPROTO_TCP) {
        /*
        if packet SYN flag is enabled and state==NEW:
            return NF_ACCEPT
        else if SYN flag is disabled and state==NEW:
            return NF_DROP
        */      
    }
    return NF_ACCEPT
}

static int __init my_net_module_init(void) {
    printk(KERN_INFO "Initializing my netfilter module\n");

    // Allocating memory for hook structure.
    my_nf_hook = (struct nf_hook_ops*) kzalloc(sizeof(struct nf_hook_ops),GFP_KERNEL);

    // Constructing the structure
    my_nf_hook->hook    = (nf_hookfn*)hfunc;        /* hook function */
    my_nf_hook->hooknum     = NF_INET_PRE_ROUTING;      /* received packets */
    my_nf_hook->pf  = PF_INET;                      /* IPv4 */
    my_nf_hook->priority    = NF_IP_PRI_FirsT;          /* max hook priority */

    nf_register_net_hook(&init_net,my_nf_hook);
    return 0;
}

static void __exit my_net_module_exit(void) {

    nf_unregister_net_hook(&init_net,my_nf_hook);
    kfree(my_nf_hook);
    printk(KERN_INFO "Exiting my netfilter module\n");
}

module_init(my_net_module_init);
module_exit(my_net_module_exit);

编辑: 添加了用于在路由前注册钩子的代码片段。

解决方法

似乎在您的钩子中,您想根据有关连接状态的 conntrack(CT) 信息对数据包做出决定 - 阻止(丢弃)所有处于连接中间的 TCP 数据包,即没有 SYN 的数据包标志并且在 CT 中没有连接条目。

所以如果你想获得CT的好处,你必须让他工作一点。
现在您的钩子在 NF_INET_PRE_ROUTING 中,优先级为 NF_IP_PRI_FIRST。看看Linux内核数据包流的picture就知道了。如果我们谈论路由前链,则 CT 处理位于 RAW 表之后(即具有较低优先级)。
您可以看到的优先级列表here

enum nf_ip_hook_priorities {
    NF_IP_PRI_FIRST = INT_MIN,NF_IP_PRI_CONNTRACK_DEFRAG = -400,NF_IP_PRI_RAW = -300,NF_IP_PRI_SELINUX_FIRST = -225,NF_IP_PRI_CONNTRACK = -200,NF_IP_PRI_MANGLE = -150,NF_IP_PRI_NAT_DST = -100,NF_IP_PRI_FILTER = 0,NF_IP_PRI_SECURITY = 50,NF_IP_PRI_NAT_SRC = 100,NF_IP_PRI_SELINUX_LAST = 225,NF_IP_PRI_CONNTRACK_HELPER = 300,NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,NF_IP_PRI_LAST = INT_MAX,};

因此要在 CT 之后(在 nf_conntrack_in() 之后),您必须以低于 NF_IP_PRI_CONNTRACK 的优先级(即具有更大的数量,例如 -50)注册您的钩子。
所以你这样做:

static struct nf_hook_ops hooks[] __read_mostly = {
     {    
          .hook = hfunc,.pf = PF_INET,.hooknum = NF_INET_PRE_ROUTING,.priority = NF_IP_PRI_CONNTRACK + 150
     },// ...
};
// ...
int ret;
ret = nf_register_hooks(hooks,ARRAY_SIZE(hooks));
if (ret < 0)
    // error

然后你应该从你的钩子中访问 CT 信息:

static unsigned int hfunc(void *priv,struct sk_buff *skb,const struct nf_hook_state *state) {
    struct iphdr *iph;

    iph = ip_hdr(skb);
    if (iph->protocol == IPPROTO_TCP) {
        struct nf_conn *ct;
        enum ip_conntrack_info ctinfo;
        struct tcphdr *tcph;

        ct = nf_ct_get(skb,&ctinfo);
        if (!ct)
            return NF_ACCEPT;

        tcph = tcp_hdr(skb)
        if (tcph->syn) { // && !tcph->ack ???
            if (ctinfo == IP_CT_NEW)
                return NF_ACCEPT;
        } else {
            if (ctinfo == IP_CT_NEW)
                return NF_DROP;
        }      
    }
    return NF_ACCEPT
}

还要记住,CT 必须参与你的 Linux 内核网络处理。应该在内核中插入 CT 模块并添加适当的 iptables 规则。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...