Подтвердить что ты не робот

Nf_conntrack_helper_register ошибки нерегрессированного порта reutrn

У меня есть следующий код для отмены регистрации и регистрации 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: %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

Чтобы увидеть мои изменения, вы можете сделать diff с исходным кодом источника linux 3.18

4b9b3361

Ответ 1

Вы не можете напрямую обращаться к функциям модулей init и exit и ожидать, что они будут работать без предварительного удостоверения того, что модуль больше не используется.

Что мне не хватает?

Функции init и exit вызываются определенным образом, то есть они безопасны чтобы сделать много предположений о том, какие блокировки хранятся и т.д., прежде чем они называется. Ваш код обходит все это и предполагает, что если он вызван напрямую они будут работать. Это не так.

Если вы посмотрите на delete_module, это не является тривиальным, он готов к выгрузке модуль, и это проверяет, используется ли ваш модуль. Давайте предположим, что ваш код в настоящее время обслуживающий запрос, вам действительно не нужен ваш модуль, и он связанные структуры данных исчезают во время работы, т.е. поведение undefined почти безусловно, приводит к панике ядра или чем-то значительно худшему...

Ниже приведен краткий обзор того, что делает ядро, прежде чем он вызывает процедуру выхода

  • Приобретите module_lock, подождите, пока он не сможет его получить.
  • Проверьте, есть ли модули, которые зависят от этого.
  • Проверьте, существует ли уже запущенная процедура init или exit.
  • Убедитесь, что есть функция exit.
  • Остановите модуль, т.е. отметка будет как MODULE_STATE_GOING
  • Разблокировать 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;
}

Ближе к концу он запускает вашу функцию выхода, но только после того, как он убедится, что никто фактически использует его и т.д. Чтобы иметь возможность вызывать функции так, как вы хотите, необходимо понимать модули погрузки и разгрузки.

Я вижу, вы отредактировали вопрос, и теперь ясно, где код появился из

Это помогло бы, если бы вы упоминали, что вы вырезали и вставляли функции init и exit из nf_conntrack_sip. Скорее всего, это сэкономило бы много людей много времени, задаваясь вопросом, почему это не сработало.

Ответ 2

Функция nf_conntrack_helper_register не работает из-за дублирования записи в таблице отслеживания соединений, а ее код возврата может быть 0 или -EEXIST в соответствии с его реализацией в 3.18. Однако я не видел никаких проблем с вашим кодом, вы могли бы проверить другие места в своем коде, где вы можете вставить дубликат nf_conntrack_helper?