gpt4 book ai didi

c - nf_conntrack_helper 注册未注册端口返回错误

转载 作者:IT王子 更新时间:2023-10-28 23:55:39 25 4
gpt4 key购买 nike

我有以下代码从内核 3.18 注销和注册 sip conntrack

static void __nf_conntrack_sip_fini(void)
{
int i, j;

for (i = 0; i < ports_c; i++) {
for (j = 0; j < ARRAY_SIZE(sip[i]); j++) {
if (sip[i][j].me == NULL)
continue;
nf_conntrack_helper_unregister(&sip[i][j]);
}
}
memset(sip, 0, sizeof(sip));
}

static int __nf_conntrack_sip_init(void)
{
int i, j, ret;

if (ports_c == 0)
ports[ports_c++] = SIP_PORT;

for (i = 0; i < ports_c; i++) {
memset(&sip[i], 0, sizeof(sip[i]));

sip[i][0].tuple.src.l3num = AF_INET;
sip[i][0].tuple.dst.protonum = IPPROTO_UDP;
sip[i][0].help = sip_help_udp;
sip[i][1].tuple.src.l3num = AF_INET;
sip[i][1].tuple.dst.protonum = IPPROTO_TCP;
sip[i][1].help = sip_help_tcp;

sip[i][2].tuple.src.l3num = AF_INET6;
sip[i][2].tuple.dst.protonum = IPPROTO_UDP;
sip[i][2].help = sip_help_udp;
sip[i][3].tuple.src.l3num = AF_INET6;
sip[i][3].tuple.dst.protonum = IPPROTO_TCP;
sip[i][3].help = sip_help_tcp;

for (j = 0; j < ARRAY_SIZE(sip[i]); j++) {
sip[i][j].data_len = sizeof(struct nf_ct_sip_master);
sip[i][j].tuple.src.u.udp.port = htons(ports[i]);
sip[i][j].expect_policy = sip_exp_policy;
sip[i][j].expect_class_max = SIP_EXPECT_MAX;
sip[i][j].me = THIS_MODULE;

if (ports[i] == SIP_PORT)
sprintf(sip[i][j].name, "sip");
else
sprintf(sip[i][j].name, "sip-%u", i);

pr_debug("port #%u: %u\n", i, ports[i]);

ret = nf_conntrack_helper_register(&sip[i][j]);
if (ret) {
printk(KERN_ERR "nf_ct_sip: failed to register"
" helper for pf: %u port: %u i=%d\n",
sip[i][j].tuple.src.l3num, ports[i], i);
__nf_conntrack_sip_fini();
return ret;
}
}
}
return 0;
}

我开发了以下代码以使用相同的第一个端口重新启动已注册的 sip conntrack

static void nf_conntrack_sip_restart(void)
{
//here ports[] = {5060, 0}
__nf_conntrack_sip_fini();
memcpy(ports,newports,sizeof(ports));
//here ports[] = {5060, 5555}
__nf_conntrack_sip_init(); // <---- It fails
}

当我从用户空间触发这个重启函数时,新端口数组的注册失败

如果我使用其他端口,那么它可以工作:

static void nf_conntrack_sip_restart(void)
{
//here ports[] = {5060, 0}
__nf_conntrack_sip_fini();
memcpy(ports,newports,sizeof(ports));
//here ports[] = {5061, 5555}
__nf_conntrack_sip_init(); // <---- It works
}

我缺少什么?

在经过我修改的 nf_conntrack_sip.c 文件的整个源代码之后:http://vpaste.net/PgUVD

要查看我的修改,您可以与 linux 3.18 的原始源代码进行比较

最佳答案

您不能直接调用模块的 initexit 函数并期望它们在没有首先确保模块不再以任何方式使用的情况下工作。

What I'm missing?

initexit 函数以特定方式调用,即它们是安全的在锁定之前做出很多关于持有什么锁等的假设叫。您的代码绕过了所有这些并假设如果直接调用他们会工作的。事实并非如此。

如果您查看 delete_module,它很重要,它正准备卸载您的模块,这将检查您的模块是否正在使用。让我们假设你的代码是目前正在处理一个请求,你真的不想要你的模块,它是相关数据结构在运行时消失,即几乎未定义的行为肯定会导致内核 panic 或更糟糕的事情......

以下是内核在调用退出例程之前所做的事情的简短概要

  1. 获取module_lock,等待获取。
  2. 检查是否有依赖于此模块的模块。
  3. 检查是否有正在运行的initexit 例程。
  4. 确保有一个exit 函数。
  5. 停止模块即标记为MODULE_STATE_GOING
  6. 解锁mutex_lock

此时我们有以下评论

/* Final destruction now no one is using it. */

您的代码没有执行上面列出的 6 个步骤中的任何一个。这是 3.16 内核上删除模块的源代码,我怀疑它与 3.18 内核相同...

SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
unsigned int, flags)
{
struct module *mod;
char name[MODULE_NAME_LEN];
int ret, forced = 0;

if (!capable(CAP_SYS_MODULE) || modules_disabled)
return -EPERM;

if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
return -EFAULT;
name[MODULE_NAME_LEN-1] = '\0';

if (mutex_lock_interruptible(&module_mutex) != 0)
return -EINTR;

mod = find_module(name);
if (!mod) {
ret = -ENOENT;
goto out;
}

if (!list_empty(&mod->source_list)) {
/* Other modules depend on us: get rid of them first. */
ret = -EWOULDBLOCK;
goto out;
}

/* Doing init or already dying? */
if (mod->state != MODULE_STATE_LIVE) {
/* FIXME: if (force), slam module count damn the torpedoes */
pr_debug("%s already dying\n", mod->name);
ret = -EBUSY;
goto out;
}

/* If it has an init func, it must have an exit func to unload */
if (mod->init && !mod->exit) {
forced = try_force_unload(flags);
if (!forced) {
/* This module can't be removed */
ret = -EBUSY;
goto out;
}
}

/* Stop the machine so refcounts can't move and disable module. */
ret = try_stop_module(mod, flags, &forced);
if (ret != 0)
goto out;

mutex_unlock(&module_mutex);
/* Final destruction now no one is using it. */
if (mod->exit != NULL)
mod->exit();
blocking_notifier_call_chain(&module_notify_list,
MODULE_STATE_GOING, mod);
async_synchronize_full();

/* Store the name of the last unloaded module for diagnostic purposes */
strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));

free_module(mod);
return 0;
out:
mutex_unlock(&module_mutex);
return ret;
}

接近尾声时,它会运行您的退出函数,但只有在它确保没有人实际上是在使用它等等。为了能够按照你想要的方式调用函数需要了解加载和卸载模块。

我看到您已经编辑了问题,现在很清楚代码的来源

如果您提到您将从 nf_conntrack_sip 中剪切和粘贴 initexit 函数,那将会有所帮助。它可能会为很多人节省很多时间来思考为什么它不起作用。

关于c - nf_conntrack_helper 注册未注册端口返回错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36842386/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com