I have the following code to unregister and register sip conntrack from kernel 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: %u i=%d\n",
sip[i][j].tuple.src.l3num, ports[i], i);
__nf_conntrack_sip_fini();
return ret;
}
}
}
return 0;
}
I developed the following code to restart the registred sip conntrack with the same first port
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
}
when I trigger this restart function from the user space the register of the new ports array fails
If I use other ports, then it works:
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
}
What I m missing?
Here after the whole source code of the nf_conntrack_sip.c file with my modifications: http://vpaste.net/PgUVD
To see my modifications you can make diff with origin source code of linux 3.18
You cannot call a modules init
and exit
functions directly and expect them to work without first making sure the module is no longer used in any way.
What I'm missing?
The init
and exit
functions are called in a particular way ie they're safe
to make a lot of assumptions about what locks are held etc before they're
called. Your code is bypassing all of this and assuming that if called directly
they'll work. This is not the case.
If you look at delete_module
it's non trivial, it's preparing to unload your
module and this checks if your module is being used. Lets assume your code is
currently servicing a request, you really don't want your module and it's
associated data structures to vanish while running ie undefined behavior almost
certainly resulting in a kernel panic or something much worse...
The following is a short synopsis of what the kernel does before it calls the exit routine
module_lock
, wait until it can get it.init
or exit
routine.exit
function.mutex_lock
At this point we have the following comment
/* Final destruction now no one is using it. */
Your code is not doing any of the 6 steps listed above. This is the source of delete module on a 3.16 kernel which I suspect is identical to 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;
}
Near the end it runs your exit function but only after it makes sure that no one is actually using it etc. To be able to call the functions the way you want you need to understand loading and unloading modules.
I see you've edited the question and it's now clear where the code came from
It would have helped if you'd mentioned that that you'd cut and paste the init
and exit
functions from nf_conntrack_sip. It would likely have saved a lot of people a lot of time wondering why it didn't work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With