Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nf_conntrack_helper_register of unregistred port reutrn error

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

like image 698
MOHAMED Avatar asked Apr 25 '16 13:04

MOHAMED


1 Answers

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

  1. Acquire module_lock, wait until it can get it.
  2. Check if there are modules that depend on this one.
  3. Check if there is an already running init or exit routine.
  4. Make sure there is an exit function.
  5. Stop the module ie mark is as MODULE_STATE_GOING
  6. Unlock 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.

like image 52
Harry Avatar answered Oct 30 '22 16:10

Harry