You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何获取指定网络命名空间的struct net指针以挂载Netfilter钩子函数

获取特定网络命名空间的struct net *指针用于Netfilter钩子注册

我来帮你搞定这个问题——要把Netfilter钩子挂载到你创建的自定义网络命名空间(比如ns1),核心是获取对应命名空间的struct net *指针,替换代码里的&init_net。下面是具体的实现方案和修改后的代码:

核心思路

内核中每个网络命名空间对应一个struct net实例,我们可以通过命名空间的名称直接查找获取指针,同时必须遵循内核的引用计数规则:用get_net系列函数获取指针时会自动增加引用计数,模块退出时必须调用put_net释放,避免内存泄漏。

具体实现步骤

1. 添加必要的头文件和模块参数

首先,我们需要添加网络命名空间相关的头文件,同时定义一个模块参数,方便你在加载模块时指定目标命名空间的名字(比如ns1):

#include <net/net_namespace.h>
#include <linux/moduleparam.h>

// 模块参数:指定目标网络命名空间的名字,默认是ns1
static char *netns_name = "ns1";
module_param(netns_name, charp, 0644);
MODULE_PARM_DESC(netns_name, "Name of the target network namespace (e.g., ns1)");

// 全局变量存储目标网络命名空间的指针
static struct net *target_net;
// 全局声明nf_hook_ops,避免函数间作用域问题
static struct nf_hook_ops nfho;

2. 修改初始化函数,获取目标netns指针并注册钩子

在模块初始化函数中,用get_net_by_name函数获取指定名称的网络命名空间指针,然后用这个指针注册Netfilter钩子:

static unsigned int verify_hookfunc(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) {
    char srcIP[16];
    struct iphdr *ip_header = (struct iphdr *)skb_network_header(skb);
    if(!skb)
        return NF_ACCEPT;
    memset(srcIP, '\0', sizeof(srcIP));
    sprintf(srcIP, "%pI4", &ip_header->saddr);
    if(strcmp(srcIP, "1.2.3.4") == 0){
        printk("see sent modified packet\n");
    }
    return NF_ACCEPT;
}

static int __init init_main(void) {
    // 获取目标网络命名空间指针
    target_net = get_net_by_name(&init_net, netns_name);
    if (!target_net) {
        printk(KERN_ERR "Failed to locate network namespace: %s\n", netns_name);
        return -ENOENT;
    }

    // 配置Netfilter钩子信息
    memset(&nfho, 0, sizeof(nfho)); // 初始化结构体避免未定义行为
    nfho.hook = verify_hookfunc;
    nfho.hooknum = NF_BR_PRE_ROUTING;
    nfho.pf = PF_BRIDGE;
    nfho.priority = INT_MIN;

    // 注册钩子到目标网络命名空间
    nf_register_net_hook(target_net, &nfho);
    printk(KERN_INFO "Successfully registered hook in netns: %s\n", netns_name);
    return 0;
}

3. 修改清理函数,释放资源

模块退出时,要先注销钩子,再释放struct net的引用计数:

static void __exit cleanup_main(void) {
    if (target_net) {
        nf_unregister_net_hook(target_net, &nfho);
        put_net(target_net); // 释放引用计数,避免内存泄漏
        printk(KERN_INFO "Successfully unregistered hook from netns: %s\n", netns_name);
    }
}

module_init(init_main);
module_exit(cleanup_main);
MODULE_LICENSE("GPLv3");

额外注意事项

  • 内核配置要求:确保你的内核开启了CONFIG_NET_NS(网络命名空间支持),这是ip netns命令和网络命名空间API工作的前提。
  • 替代方案(通过路径获取):如果你想通过/var/run/netns/ns1路径直接获取,也可以用kern_pathget_net_from_inode组合实现,但用名称查找更简洁:
    struct path path;
    int ret = kern_path("/var/run/netns/ns1", LOOKUP_FOLLOW, &path);
    if (ret) {
        printk(KERN_ERR "Failed to resolve netns path\n");
        return ret;
    }
    target_net = get_net_from_inode(path.dentry->d_inode);
    if (!target_net) {
        printk(KERN_ERR "Failed to get net from inode\n");
        path_put(&path);
        return -ENOENT;
    }
    path_put(&path);
    
  • 内核版本兼容性get_net_by_name函数在Linux 3.10及以上内核版本可用,如果你的内核版本更旧,可以使用netns_get_by_name替代(注意函数参数略有不同)。

内容的提问来源于stack exchange,提问作者CE KUAN

火山引擎 最新活动