未注册端口重组错误的nf_conntrack_helper_register

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

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: %ui=%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 } 

当我从用户空间触发此重新启动function时,新端口arrays的寄存器失败

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

 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的源代码进行比较

Solutions Collecting From Web of "未注册端口重组错误的nf_conntrack_helper_register"

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

我错过了什么?

initexit函数是以一种特定的方式调用的,也就是说,在调用之前,可以很安全地进行关于哪些锁等的假设。 你的代码绕过所有这一切,并假设如果直接调用,他们将工作。 不是这种情况。

如果你看看delete_module这不是微不足道的,它准备卸载你的模块,这将检查你的模块是否被使用。 让我们假设你的代码当前正在处理一个请求,你真的不希望你的模块,它的相关数据结构在运行时消失,即未定义的行为几乎可以肯定导致内核恐慌或更糟的事情…

以下是内核在调用出口例程之前所做的简要说明

  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函数,这将会有所帮助。 这很可能会让很多人不知道为什么它不起作用。

nf_conntrack_helper_register函数因连接跟踪表中的重复条目而失败,根据3.18中的实现,其返回码可以是0或者-EEXIST。 然而,我没有看到你的代码片断有任何问题,你可以检查你的代码中的其他地方,你可能会插入一个重复的nf_conntrack_helper