linux/net/netfilter/nf_conntrack_helper.c
<<
>>
Prefs
   1/* Helper handling for netfilter. */
   2
   3/* (C) 1999-2001 Paul `Rusty' Russell
   4 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
   5 * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
   6 * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/types.h>
  14#include <linux/netfilter.h>
  15#include <linux/module.h>
  16#include <linux/skbuff.h>
  17#include <linux/vmalloc.h>
  18#include <linux/stddef.h>
  19#include <linux/random.h>
  20#include <linux/err.h>
  21#include <linux/kernel.h>
  22#include <linux/netdevice.h>
  23#include <linux/rculist.h>
  24#include <linux/rtnetlink.h>
  25
  26#include <net/netfilter/nf_conntrack.h>
  27#include <net/netfilter/nf_conntrack_l3proto.h>
  28#include <net/netfilter/nf_conntrack_l4proto.h>
  29#include <net/netfilter/nf_conntrack_helper.h>
  30#include <net/netfilter/nf_conntrack_core.h>
  31#include <net/netfilter/nf_conntrack_extend.h>
  32#include <net/netfilter/nf_log.h>
  33
  34static DEFINE_MUTEX(nf_ct_helper_mutex);
  35struct hlist_head *nf_ct_helper_hash __read_mostly;
  36EXPORT_SYMBOL_GPL(nf_ct_helper_hash);
  37unsigned int nf_ct_helper_hsize __read_mostly;
  38EXPORT_SYMBOL_GPL(nf_ct_helper_hsize);
  39static unsigned int nf_ct_helper_count __read_mostly;
  40
  41static bool nf_ct_auto_assign_helper __read_mostly = false;
  42module_param_named(nf_conntrack_helper, nf_ct_auto_assign_helper, bool, 0644);
  43MODULE_PARM_DESC(nf_conntrack_helper,
  44                 "Enable automatic conntrack helper assignment (default 0)");
  45
  46#ifdef CONFIG_SYSCTL
  47static struct ctl_table helper_sysctl_table[] = {
  48        {
  49                .procname       = "nf_conntrack_helper",
  50                .data           = &init_net.ct.sysctl_auto_assign_helper,
  51                .maxlen         = sizeof(unsigned int),
  52                .mode           = 0644,
  53                .proc_handler   = proc_dointvec,
  54        },
  55        {}
  56};
  57
  58static int nf_conntrack_helper_init_sysctl(struct net *net)
  59{
  60        struct ctl_table *table;
  61
  62        table = kmemdup(helper_sysctl_table, sizeof(helper_sysctl_table),
  63                        GFP_KERNEL);
  64        if (!table)
  65                goto out;
  66
  67        table[0].data = &net->ct.sysctl_auto_assign_helper;
  68
  69        /* Don't export sysctls to unprivileged users */
  70        if (net->user_ns != &init_user_ns)
  71                table[0].procname = NULL;
  72
  73        net->ct.helper_sysctl_header =
  74                register_net_sysctl(net, "net/netfilter", table);
  75
  76        if (!net->ct.helper_sysctl_header) {
  77                pr_err("nf_conntrack_helper: can't register to sysctl.\n");
  78                goto out_register;
  79        }
  80        return 0;
  81
  82out_register:
  83        kfree(table);
  84out:
  85        return -ENOMEM;
  86}
  87
  88static void nf_conntrack_helper_fini_sysctl(struct net *net)
  89{
  90        struct ctl_table *table;
  91
  92        table = net->ct.helper_sysctl_header->ctl_table_arg;
  93        unregister_net_sysctl_table(net->ct.helper_sysctl_header);
  94        kfree(table);
  95}
  96#else
  97static int nf_conntrack_helper_init_sysctl(struct net *net)
  98{
  99        return 0;
 100}
 101
 102static void nf_conntrack_helper_fini_sysctl(struct net *net)
 103{
 104}
 105#endif /* CONFIG_SYSCTL */
 106
 107static DEFINE_MUTEX(nf_ct_nat_helpers_mutex);
 108static struct list_head nf_ct_nat_helpers __read_mostly;
 109
 110/* Stupid hash, but collision free for the default registrations of the
 111 * helpers currently in the kernel. */
 112static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple)
 113{
 114        return (((tuple->src.l3num << 8) | tuple->dst.protonum) ^
 115                (__force __u16)tuple->src.u.all) % nf_ct_helper_hsize;
 116}
 117
 118static struct nf_conntrack_helper *
 119__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
 120{
 121        struct nf_conntrack_helper *helper;
 122        struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
 123        unsigned int h;
 124
 125        if (!nf_ct_helper_count)
 126                return NULL;
 127
 128        h = helper_hash(tuple);
 129        hlist_for_each_entry_rcu(helper, &nf_ct_helper_hash[h], hnode) {
 130                if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask))
 131                        return helper;
 132        }
 133        return NULL;
 134}
 135
 136struct nf_conntrack_helper *
 137__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum)
 138{
 139        struct nf_conntrack_helper *h;
 140        unsigned int i;
 141
 142        for (i = 0; i < nf_ct_helper_hsize; i++) {
 143                hlist_for_each_entry_rcu(h, &nf_ct_helper_hash[i], hnode) {
 144                        if (strcmp(h->name, name))
 145                                continue;
 146
 147                        if (h->tuple.src.l3num != NFPROTO_UNSPEC &&
 148                            h->tuple.src.l3num != l3num)
 149                                continue;
 150
 151                        if (h->tuple.dst.protonum == protonum)
 152                                return h;
 153                }
 154        }
 155        return NULL;
 156}
 157EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find);
 158
 159struct nf_conntrack_helper *
 160nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum)
 161{
 162        struct nf_conntrack_helper *h;
 163
 164        rcu_read_lock();
 165
 166        h = __nf_conntrack_helper_find(name, l3num, protonum);
 167#ifdef CONFIG_MODULES
 168        if (h == NULL) {
 169                rcu_read_unlock();
 170                if (request_module("nfct-helper-%s", name) == 0) {
 171                        rcu_read_lock();
 172                        h = __nf_conntrack_helper_find(name, l3num, protonum);
 173                } else {
 174                        return h;
 175                }
 176        }
 177#endif
 178        if (h != NULL && !try_module_get(h->me))
 179                h = NULL;
 180        if (h != NULL && !refcount_inc_not_zero(&h->refcnt)) {
 181                module_put(h->me);
 182                h = NULL;
 183        }
 184
 185        rcu_read_unlock();
 186
 187        return h;
 188}
 189EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get);
 190
 191void nf_conntrack_helper_put(struct nf_conntrack_helper *helper)
 192{
 193        refcount_dec(&helper->refcnt);
 194        module_put(helper->me);
 195}
 196EXPORT_SYMBOL_GPL(nf_conntrack_helper_put);
 197
 198static struct nf_conntrack_nat_helper *
 199nf_conntrack_nat_helper_find(const char *mod_name)
 200{
 201        struct nf_conntrack_nat_helper *cur;
 202        bool found = false;
 203
 204        list_for_each_entry_rcu(cur, &nf_ct_nat_helpers, list) {
 205                if (!strcmp(cur->mod_name, mod_name)) {
 206                        found = true;
 207                        break;
 208                }
 209        }
 210        return found ? cur : NULL;
 211}
 212
 213int
 214nf_nat_helper_try_module_get(const char *name, u16 l3num, u8 protonum)
 215{
 216        struct nf_conntrack_helper *h;
 217        struct nf_conntrack_nat_helper *nat;
 218        char mod_name[NF_CT_HELPER_NAME_LEN];
 219        int ret = 0;
 220
 221        rcu_read_lock();
 222        h = __nf_conntrack_helper_find(name, l3num, protonum);
 223        if (!h) {
 224                rcu_read_unlock();
 225                return -ENOENT;
 226        }
 227
 228        nat = nf_conntrack_nat_helper_find(h->nat_mod_name);
 229        if (!nat) {
 230                snprintf(mod_name, sizeof(mod_name), "%s", h->nat_mod_name);
 231                rcu_read_unlock();
 232                request_module(mod_name);
 233
 234                rcu_read_lock();
 235                nat = nf_conntrack_nat_helper_find(mod_name);
 236                if (!nat) {
 237                        rcu_read_unlock();
 238                        return -ENOENT;
 239                }
 240        }
 241
 242        if (!try_module_get(nat->module))
 243                ret = -ENOENT;
 244
 245        rcu_read_unlock();
 246        return ret;
 247}
 248EXPORT_SYMBOL_GPL(nf_nat_helper_try_module_get);
 249
 250void nf_nat_helper_put(struct nf_conntrack_helper *helper)
 251{
 252        struct nf_conntrack_nat_helper *nat;
 253
 254        nat = nf_conntrack_nat_helper_find(helper->nat_mod_name);
 255        if (WARN_ON_ONCE(!nat))
 256                return;
 257
 258        module_put(nat->module);
 259}
 260EXPORT_SYMBOL_GPL(nf_nat_helper_put);
 261
 262struct nf_conn_help *
 263nf_ct_helper_ext_add(struct nf_conn *ct,
 264                     struct nf_conntrack_helper *helper, gfp_t gfp)
 265{
 266        struct nf_conn_help *help;
 267
 268        help = nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp);
 269        if (help)
 270                INIT_HLIST_HEAD(&help->expectations);
 271        else
 272                pr_debug("failed to add helper extension area");
 273        return help;
 274}
 275EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add);
 276
 277static struct nf_conntrack_helper *
 278nf_ct_lookup_helper(struct nf_conn *ct, struct net *net)
 279{
 280        if (!net->ct.sysctl_auto_assign_helper) {
 281                if (net->ct.auto_assign_helper_warned)
 282                        return NULL;
 283                if (!__nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple))
 284                        return NULL;
 285                pr_info("nf_conntrack: default automatic helper assignment "
 286                        "has been turned off for security reasons and CT-based "
 287                        " firewall rule not found. Use the iptables CT target "
 288                        "to attach helpers instead.\n");
 289                net->ct.auto_assign_helper_warned = 1;
 290                return NULL;
 291        }
 292
 293        return __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
 294}
 295
 296
 297int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
 298                              gfp_t flags)
 299{
 300        struct nf_conntrack_helper *helper = NULL;
 301        struct nf_conn_help *help;
 302        struct net *net = nf_ct_net(ct);
 303
 304        /* We already got a helper explicitly attached. The function
 305         * nf_conntrack_alter_reply - in case NAT is in use - asks for looking
 306         * the helper up again. Since now the user is in full control of
 307         * making consistent helper configurations, skip this automatic
 308         * re-lookup, otherwise we'll lose the helper.
 309         */
 310        if (test_bit(IPS_HELPER_BIT, &ct->status))
 311                return 0;
 312
 313        if (tmpl != NULL) {
 314                help = nfct_help(tmpl);
 315                if (help != NULL) {
 316                        helper = help->helper;
 317                        set_bit(IPS_HELPER_BIT, &ct->status);
 318                }
 319        }
 320
 321        help = nfct_help(ct);
 322
 323        if (helper == NULL) {
 324                helper = nf_ct_lookup_helper(ct, net);
 325                if (helper == NULL) {
 326                        if (help)
 327                                RCU_INIT_POINTER(help->helper, NULL);
 328                        return 0;
 329                }
 330        }
 331
 332        if (help == NULL) {
 333                help = nf_ct_helper_ext_add(ct, helper, flags);
 334                if (help == NULL)
 335                        return -ENOMEM;
 336        } else {
 337                /* We only allow helper re-assignment of the same sort since
 338                 * we cannot reallocate the helper extension area.
 339                 */
 340                struct nf_conntrack_helper *tmp = rcu_dereference(help->helper);
 341
 342                if (tmp && tmp->help != helper->help) {
 343                        RCU_INIT_POINTER(help->helper, NULL);
 344                        return 0;
 345                }
 346        }
 347
 348        rcu_assign_pointer(help->helper, helper);
 349
 350        return 0;
 351}
 352EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);
 353
 354/* appropriate ct lock protecting must be taken by caller */
 355static int unhelp(struct nf_conn *ct, void *me)
 356{
 357        struct nf_conn_help *help = nfct_help(ct);
 358
 359        if (help && rcu_dereference_raw(help->helper) == me) {
 360                nf_conntrack_event(IPCT_HELPER, ct);
 361                RCU_INIT_POINTER(help->helper, NULL);
 362        }
 363
 364        /* We are not intended to delete this conntrack. */
 365        return 0;
 366}
 367
 368void nf_ct_helper_destroy(struct nf_conn *ct)
 369{
 370        struct nf_conn_help *help = nfct_help(ct);
 371        struct nf_conntrack_helper *helper;
 372
 373        if (help) {
 374                rcu_read_lock();
 375                helper = rcu_dereference(help->helper);
 376                if (helper && helper->destroy)
 377                        helper->destroy(ct);
 378                rcu_read_unlock();
 379        }
 380}
 381
 382static LIST_HEAD(nf_ct_helper_expectfn_list);
 383
 384void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n)
 385{
 386        spin_lock_bh(&nf_conntrack_expect_lock);
 387        list_add_rcu(&n->head, &nf_ct_helper_expectfn_list);
 388        spin_unlock_bh(&nf_conntrack_expect_lock);
 389}
 390EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_register);
 391
 392void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n)
 393{
 394        spin_lock_bh(&nf_conntrack_expect_lock);
 395        list_del_rcu(&n->head);
 396        spin_unlock_bh(&nf_conntrack_expect_lock);
 397}
 398EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister);
 399
 400/* Caller should hold the rcu lock */
 401struct nf_ct_helper_expectfn *
 402nf_ct_helper_expectfn_find_by_name(const char *name)
 403{
 404        struct nf_ct_helper_expectfn *cur;
 405        bool found = false;
 406
 407        list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
 408                if (!strcmp(cur->name, name)) {
 409                        found = true;
 410                        break;
 411                }
 412        }
 413        return found ? cur : NULL;
 414}
 415EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_name);
 416
 417/* Caller should hold the rcu lock */
 418struct nf_ct_helper_expectfn *
 419nf_ct_helper_expectfn_find_by_symbol(const void *symbol)
 420{
 421        struct nf_ct_helper_expectfn *cur;
 422        bool found = false;
 423
 424        list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
 425                if (cur->expectfn == symbol) {
 426                        found = true;
 427                        break;
 428                }
 429        }
 430        return found ? cur : NULL;
 431}
 432EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol);
 433
 434__printf(3, 4)
 435void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
 436                      const char *fmt, ...)
 437{
 438        const struct nf_conn_help *help;
 439        const struct nf_conntrack_helper *helper;
 440        struct va_format vaf;
 441        va_list args;
 442
 443        va_start(args, fmt);
 444
 445        vaf.fmt = fmt;
 446        vaf.va = &args;
 447
 448        /* Called from the helper function, this call never fails */
 449        help = nfct_help(ct);
 450
 451        /* rcu_read_lock()ed by nf_hook_thresh */
 452        helper = rcu_dereference(help->helper);
 453
 454        nf_log_packet(nf_ct_net(ct), nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL,
 455                      "nf_ct_%s: dropping packet: %pV ", helper->name, &vaf);
 456
 457        va_end(args);
 458}
 459EXPORT_SYMBOL_GPL(nf_ct_helper_log);
 460
 461int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
 462{
 463        struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
 464        unsigned int h = helper_hash(&me->tuple);
 465        struct nf_conntrack_helper *cur;
 466        int ret = 0, i;
 467
 468        BUG_ON(me->expect_policy == NULL);
 469        BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
 470        BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
 471
 472        if (me->expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
 473                return -EINVAL;
 474
 475        mutex_lock(&nf_ct_helper_mutex);
 476        for (i = 0; i < nf_ct_helper_hsize; i++) {
 477                hlist_for_each_entry(cur, &nf_ct_helper_hash[i], hnode) {
 478                        if (!strcmp(cur->name, me->name) &&
 479                            (cur->tuple.src.l3num == NFPROTO_UNSPEC ||
 480                             cur->tuple.src.l3num == me->tuple.src.l3num) &&
 481                            cur->tuple.dst.protonum == me->tuple.dst.protonum) {
 482                                ret = -EEXIST;
 483                                goto out;
 484                        }
 485                }
 486        }
 487
 488        /* avoid unpredictable behaviour for auto_assign_helper */
 489        if (!(me->flags & NF_CT_HELPER_F_USERSPACE)) {
 490                hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
 491                        if (nf_ct_tuple_src_mask_cmp(&cur->tuple, &me->tuple,
 492                                                     &mask)) {
 493                                ret = -EEXIST;
 494                                goto out;
 495                        }
 496                }
 497        }
 498        refcount_set(&me->refcnt, 1);
 499        hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
 500        nf_ct_helper_count++;
 501out:
 502        mutex_unlock(&nf_ct_helper_mutex);
 503        return ret;
 504}
 505EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
 506
 507static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data)
 508{
 509        struct nf_conn_help *help = nfct_help(exp->master);
 510        const struct nf_conntrack_helper *me = data;
 511        const struct nf_conntrack_helper *this;
 512
 513        if (exp->helper == me)
 514                return true;
 515
 516        this = rcu_dereference_protected(help->helper,
 517                                         lockdep_is_held(&nf_conntrack_expect_lock));
 518        return this == me;
 519}
 520
 521void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
 522{
 523        mutex_lock(&nf_ct_helper_mutex);
 524        hlist_del_rcu(&me->hnode);
 525        nf_ct_helper_count--;
 526        mutex_unlock(&nf_ct_helper_mutex);
 527
 528        /* Make sure every nothing is still using the helper unless its a
 529         * connection in the hash.
 530         */
 531        synchronize_rcu();
 532
 533        nf_ct_expect_iterate_destroy(expect_iter_me, NULL);
 534        nf_ct_iterate_destroy(unhelp, me);
 535
 536        /* Maybe someone has gotten the helper already when unhelp above.
 537         * So need to wait it.
 538         */
 539        synchronize_rcu();
 540}
 541EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
 542
 543void nf_ct_helper_init(struct nf_conntrack_helper *helper,
 544                       u16 l3num, u16 protonum, const char *name,
 545                       u16 default_port, u16 spec_port, u32 id,
 546                       const struct nf_conntrack_expect_policy *exp_pol,
 547                       u32 expect_class_max,
 548                       int (*help)(struct sk_buff *skb, unsigned int protoff,
 549                                   struct nf_conn *ct,
 550                                   enum ip_conntrack_info ctinfo),
 551                       int (*from_nlattr)(struct nlattr *attr,
 552                                          struct nf_conn *ct),
 553                       struct module *module)
 554{
 555        helper->tuple.src.l3num = l3num;
 556        helper->tuple.dst.protonum = protonum;
 557        helper->tuple.src.u.all = htons(spec_port);
 558        helper->expect_policy = exp_pol;
 559        helper->expect_class_max = expect_class_max;
 560        helper->help = help;
 561        helper->from_nlattr = from_nlattr;
 562        helper->me = module;
 563        snprintf(helper->nat_mod_name, sizeof(helper->nat_mod_name),
 564                 NF_NAT_HELPER_PREFIX "%s", name);
 565
 566        if (spec_port == default_port)
 567                snprintf(helper->name, sizeof(helper->name), "%s", name);
 568        else
 569                snprintf(helper->name, sizeof(helper->name), "%s-%u", name, id);
 570}
 571EXPORT_SYMBOL_GPL(nf_ct_helper_init);
 572
 573int nf_conntrack_helpers_register(struct nf_conntrack_helper *helper,
 574                                  unsigned int n)
 575{
 576        unsigned int i;
 577        int err = 0;
 578
 579        for (i = 0; i < n; i++) {
 580                err = nf_conntrack_helper_register(&helper[i]);
 581                if (err < 0)
 582                        goto err;
 583        }
 584
 585        return err;
 586err:
 587        if (i > 0)
 588                nf_conntrack_helpers_unregister(helper, i);
 589        return err;
 590}
 591EXPORT_SYMBOL_GPL(nf_conntrack_helpers_register);
 592
 593void nf_conntrack_helpers_unregister(struct nf_conntrack_helper *helper,
 594                                unsigned int n)
 595{
 596        while (n-- > 0)
 597                nf_conntrack_helper_unregister(&helper[n]);
 598}
 599EXPORT_SYMBOL_GPL(nf_conntrack_helpers_unregister);
 600
 601void nf_nat_helper_register(struct nf_conntrack_nat_helper *nat)
 602{
 603        mutex_lock(&nf_ct_nat_helpers_mutex);
 604        list_add_rcu(&nat->list, &nf_ct_nat_helpers);
 605        mutex_unlock(&nf_ct_nat_helpers_mutex);
 606}
 607EXPORT_SYMBOL_GPL(nf_nat_helper_register);
 608
 609void nf_nat_helper_unregister(struct nf_conntrack_nat_helper *nat)
 610{
 611        mutex_lock(&nf_ct_nat_helpers_mutex);
 612        list_del_rcu(&nat->list);
 613        mutex_unlock(&nf_ct_nat_helpers_mutex);
 614}
 615EXPORT_SYMBOL_GPL(nf_nat_helper_unregister);
 616
 617static const struct nf_ct_ext_type helper_extend = {
 618        .len    = sizeof(struct nf_conn_help),
 619        .align  = __alignof__(struct nf_conn_help),
 620        .id     = NF_CT_EXT_HELPER,
 621};
 622
 623int nf_conntrack_helper_pernet_init(struct net *net)
 624{
 625        net->ct.auto_assign_helper_warned = false;
 626        net->ct.sysctl_auto_assign_helper = nf_ct_auto_assign_helper;
 627        return nf_conntrack_helper_init_sysctl(net);
 628}
 629
 630void nf_conntrack_helper_pernet_fini(struct net *net)
 631{
 632        nf_conntrack_helper_fini_sysctl(net);
 633}
 634
 635int nf_conntrack_helper_init(void)
 636{
 637        int ret;
 638        nf_ct_helper_hsize = 1; /* gets rounded up to use one page */
 639        nf_ct_helper_hash =
 640                nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0);
 641        if (!nf_ct_helper_hash)
 642                return -ENOMEM;
 643
 644        ret = nf_ct_extend_register(&helper_extend);
 645        if (ret < 0) {
 646                pr_err("nf_ct_helper: Unable to register helper extension.\n");
 647                goto out_extend;
 648        }
 649
 650        INIT_LIST_HEAD(&nf_ct_nat_helpers);
 651        return 0;
 652out_extend:
 653        nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
 654        return ret;
 655}
 656
 657void nf_conntrack_helper_fini(void)
 658{
 659        nf_ct_extend_unregister(&helper_extend);
 660        nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
 661}
 662