linux/net/netfilter/nfnetlink_cthelper.c
<<
>>
Prefs
   1/*
   2 * (C) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation (or any later at your option).
   7 *
   8 * This software has been sponsored by Vyatta Inc. <http://www.vyatta.com>
   9 */
  10#include <linux/init.h>
  11#include <linux/module.h>
  12#include <linux/kernel.h>
  13#include <linux/skbuff.h>
  14#include <linux/netlink.h>
  15#include <linux/rculist.h>
  16#include <linux/slab.h>
  17#include <linux/types.h>
  18#include <linux/list.h>
  19#include <linux/errno.h>
  20#include <net/netlink.h>
  21#include <net/sock.h>
  22
  23#include <net/netfilter/nf_conntrack_helper.h>
  24#include <net/netfilter/nf_conntrack_expect.h>
  25#include <net/netfilter/nf_conntrack_ecache.h>
  26
  27#include <linux/netfilter/nfnetlink.h>
  28#include <linux/netfilter/nfnetlink_conntrack.h>
  29#include <linux/netfilter/nfnetlink_cthelper.h>
  30
  31MODULE_LICENSE("GPL");
  32MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
  33MODULE_DESCRIPTION("nfnl_cthelper: User-space connection tracking helpers");
  34
  35static int
  36nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff,
  37                        struct nf_conn *ct, enum ip_conntrack_info ctinfo)
  38{
  39        const struct nf_conn_help *help;
  40        struct nf_conntrack_helper *helper;
  41
  42        help = nfct_help(ct);
  43        if (help == NULL)
  44                return NF_DROP;
  45
  46        /* rcu_read_lock()ed by nf_hook_slow */
  47        helper = rcu_dereference(help->helper);
  48        if (helper == NULL)
  49                return NF_DROP;
  50
  51        /* This is an user-space helper not yet configured, skip. */
  52        if ((helper->flags &
  53            (NF_CT_HELPER_F_USERSPACE | NF_CT_HELPER_F_CONFIGURED)) ==
  54             NF_CT_HELPER_F_USERSPACE)
  55                return NF_ACCEPT;
  56
  57        /* If the user-space helper is not available, don't block traffic. */
  58        return NF_QUEUE_NR(helper->queue_num) | NF_VERDICT_FLAG_QUEUE_BYPASS;
  59}
  60
  61static const struct nla_policy nfnl_cthelper_tuple_pol[NFCTH_TUPLE_MAX+1] = {
  62        [NFCTH_TUPLE_L3PROTONUM] = { .type = NLA_U16, },
  63        [NFCTH_TUPLE_L4PROTONUM] = { .type = NLA_U8, },
  64};
  65
  66static int
  67nfnl_cthelper_parse_tuple(struct nf_conntrack_tuple *tuple,
  68                          const struct nlattr *attr)
  69{
  70        int err;
  71        struct nlattr *tb[NFCTH_TUPLE_MAX+1];
  72
  73        err = nla_parse_nested(tb, NFCTH_TUPLE_MAX, attr, nfnl_cthelper_tuple_pol);
  74        if (err < 0)
  75                return err;
  76
  77        if (!tb[NFCTH_TUPLE_L3PROTONUM] || !tb[NFCTH_TUPLE_L4PROTONUM])
  78                return -EINVAL;
  79
  80        tuple->src.l3num = ntohs(nla_get_be16(tb[NFCTH_TUPLE_L3PROTONUM]));
  81        tuple->dst.protonum = nla_get_u8(tb[NFCTH_TUPLE_L4PROTONUM]);
  82
  83        return 0;
  84}
  85
  86static int
  87nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct)
  88{
  89        const struct nf_conn_help *help = nfct_help(ct);
  90
  91        if (attr == NULL)
  92                return -EINVAL;
  93
  94        if (help->helper->data_len == 0)
  95                return -EINVAL;
  96
  97        memcpy(&help->data, nla_data(attr), help->helper->data_len);
  98        return 0;
  99}
 100
 101static int
 102nfnl_cthelper_to_nlattr(struct sk_buff *skb, const struct nf_conn *ct)
 103{
 104        const struct nf_conn_help *help = nfct_help(ct);
 105
 106        if (help->helper->data_len &&
 107            nla_put(skb, CTA_HELP_INFO, help->helper->data_len, &help->data))
 108                goto nla_put_failure;
 109
 110        return 0;
 111
 112nla_put_failure:
 113        return -ENOSPC;
 114}
 115
 116static const struct nla_policy nfnl_cthelper_expect_pol[NFCTH_POLICY_MAX+1] = {
 117        [NFCTH_POLICY_NAME] = { .type = NLA_NUL_STRING,
 118                                .len = NF_CT_HELPER_NAME_LEN-1 },
 119        [NFCTH_POLICY_EXPECT_MAX] = { .type = NLA_U32, },
 120        [NFCTH_POLICY_EXPECT_TIMEOUT] = { .type = NLA_U32, },
 121};
 122
 123static int
 124nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy,
 125                            const struct nlattr *attr)
 126{
 127        int err;
 128        struct nlattr *tb[NFCTH_POLICY_MAX+1];
 129
 130        err = nla_parse_nested(tb, NFCTH_POLICY_MAX, attr, nfnl_cthelper_expect_pol);
 131        if (err < 0)
 132                return err;
 133
 134        if (!tb[NFCTH_POLICY_NAME] ||
 135            !tb[NFCTH_POLICY_EXPECT_MAX] ||
 136            !tb[NFCTH_POLICY_EXPECT_TIMEOUT])
 137                return -EINVAL;
 138
 139        strncpy(expect_policy->name,
 140                nla_data(tb[NFCTH_POLICY_NAME]), NF_CT_HELPER_NAME_LEN);
 141        expect_policy->max_expected =
 142                ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX]));
 143        expect_policy->timeout =
 144                ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_TIMEOUT]));
 145
 146        return 0;
 147}
 148
 149static const struct nla_policy
 150nfnl_cthelper_expect_policy_set[NFCTH_POLICY_SET_MAX+1] = {
 151        [NFCTH_POLICY_SET_NUM] = { .type = NLA_U32, },
 152};
 153
 154static int
 155nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper,
 156                                  const struct nlattr *attr)
 157{
 158        int i, ret;
 159        struct nf_conntrack_expect_policy *expect_policy;
 160        struct nlattr *tb[NFCTH_POLICY_SET_MAX+1];
 161
 162        ret = nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr,
 163                               nfnl_cthelper_expect_policy_set);
 164        if (ret < 0)
 165                return ret;
 166
 167        if (!tb[NFCTH_POLICY_SET_NUM])
 168                return -EINVAL;
 169
 170        helper->expect_class_max =
 171                ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM]));
 172
 173        if (helper->expect_class_max != 0 &&
 174            helper->expect_class_max > NF_CT_MAX_EXPECT_CLASSES)
 175                return -EOVERFLOW;
 176
 177        expect_policy = kzalloc(sizeof(struct nf_conntrack_expect_policy) *
 178                                helper->expect_class_max, GFP_KERNEL);
 179        if (expect_policy == NULL)
 180                return -ENOMEM;
 181
 182        for (i=0; i<helper->expect_class_max; i++) {
 183                if (!tb[NFCTH_POLICY_SET+i])
 184                        goto err;
 185
 186                ret = nfnl_cthelper_expect_policy(&expect_policy[i],
 187                                                  tb[NFCTH_POLICY_SET+i]);
 188                if (ret < 0)
 189                        goto err;
 190        }
 191        helper->expect_policy = expect_policy;
 192        return 0;
 193err:
 194        kfree(expect_policy);
 195        return -EINVAL;
 196}
 197
 198static int
 199nfnl_cthelper_create(const struct nlattr * const tb[],
 200                     struct nf_conntrack_tuple *tuple)
 201{
 202        struct nf_conntrack_helper *helper;
 203        int ret;
 204
 205        if (!tb[NFCTH_TUPLE] || !tb[NFCTH_POLICY] || !tb[NFCTH_PRIV_DATA_LEN])
 206                return -EINVAL;
 207
 208        helper = kzalloc(sizeof(struct nf_conntrack_helper), GFP_KERNEL);
 209        if (helper == NULL)
 210                return -ENOMEM;
 211
 212        ret = nfnl_cthelper_parse_expect_policy(helper, tb[NFCTH_POLICY]);
 213        if (ret < 0)
 214                goto err;
 215
 216        strncpy(helper->name, nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN);
 217        helper->data_len = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN]));
 218        helper->flags |= NF_CT_HELPER_F_USERSPACE;
 219        memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple));
 220
 221        helper->me = THIS_MODULE;
 222        helper->help = nfnl_userspace_cthelper;
 223        helper->from_nlattr = nfnl_cthelper_from_nlattr;
 224        helper->to_nlattr = nfnl_cthelper_to_nlattr;
 225
 226        /* Default to queue number zero, this can be updated at any time. */
 227        if (tb[NFCTH_QUEUE_NUM])
 228                helper->queue_num = ntohl(nla_get_be32(tb[NFCTH_QUEUE_NUM]));
 229
 230        if (tb[NFCTH_STATUS]) {
 231                int status = ntohl(nla_get_be32(tb[NFCTH_STATUS]));
 232
 233                switch(status) {
 234                case NFCT_HELPER_STATUS_ENABLED:
 235                        helper->flags |= NF_CT_HELPER_F_CONFIGURED;
 236                        break;
 237                case NFCT_HELPER_STATUS_DISABLED:
 238                        helper->flags &= ~NF_CT_HELPER_F_CONFIGURED;
 239                        break;
 240                }
 241        }
 242
 243        ret = nf_conntrack_helper_register(helper);
 244        if (ret < 0)
 245                goto err;
 246
 247        return 0;
 248err:
 249        kfree(helper);
 250        return ret;
 251}
 252
 253static int
 254nfnl_cthelper_update(const struct nlattr * const tb[],
 255                     struct nf_conntrack_helper *helper)
 256{
 257        int ret;
 258
 259        if (tb[NFCTH_PRIV_DATA_LEN])
 260                return -EBUSY;
 261
 262        if (tb[NFCTH_POLICY]) {
 263                ret = nfnl_cthelper_parse_expect_policy(helper,
 264                                                        tb[NFCTH_POLICY]);
 265                if (ret < 0)
 266                        return ret;
 267        }
 268        if (tb[NFCTH_QUEUE_NUM])
 269                helper->queue_num = ntohl(nla_get_be32(tb[NFCTH_QUEUE_NUM]));
 270
 271        if (tb[NFCTH_STATUS]) {
 272                int status = ntohl(nla_get_be32(tb[NFCTH_STATUS]));
 273
 274                switch(status) {
 275                case NFCT_HELPER_STATUS_ENABLED:
 276                        helper->flags |= NF_CT_HELPER_F_CONFIGURED;
 277                        break;
 278                case NFCT_HELPER_STATUS_DISABLED:
 279                        helper->flags &= ~NF_CT_HELPER_F_CONFIGURED;
 280                        break;
 281                }
 282        }
 283        return 0;
 284}
 285
 286static int
 287nfnl_cthelper_new(struct sock *nfnl, struct sk_buff *skb,
 288                  const struct nlmsghdr *nlh, const struct nlattr * const tb[])
 289{
 290        const char *helper_name;
 291        struct nf_conntrack_helper *cur, *helper = NULL;
 292        struct nf_conntrack_tuple tuple;
 293        int ret = 0, i;
 294
 295        if (!tb[NFCTH_NAME] || !tb[NFCTH_TUPLE])
 296                return -EINVAL;
 297
 298        helper_name = nla_data(tb[NFCTH_NAME]);
 299
 300        ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]);
 301        if (ret < 0)
 302                return ret;
 303
 304        rcu_read_lock();
 305        for (i = 0; i < nf_ct_helper_hsize && !helper; i++) {
 306                hlist_for_each_entry_rcu(cur, &nf_ct_helper_hash[i], hnode) {
 307
 308                        /* skip non-userspace conntrack helpers. */
 309                        if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
 310                                continue;
 311
 312                        if (strncmp(cur->name, helper_name,
 313                                        NF_CT_HELPER_NAME_LEN) != 0)
 314                                continue;
 315
 316                        if ((tuple.src.l3num != cur->tuple.src.l3num ||
 317                             tuple.dst.protonum != cur->tuple.dst.protonum))
 318                                continue;
 319
 320                        if (nlh->nlmsg_flags & NLM_F_EXCL) {
 321                                ret = -EEXIST;
 322                                goto err;
 323                        }
 324                        helper = cur;
 325                        break;
 326                }
 327        }
 328        rcu_read_unlock();
 329
 330        if (helper == NULL)
 331                ret = nfnl_cthelper_create(tb, &tuple);
 332        else
 333                ret = nfnl_cthelper_update(tb, helper);
 334
 335        return ret;
 336err:
 337        rcu_read_unlock();
 338        return ret;
 339}
 340
 341static int
 342nfnl_cthelper_dump_tuple(struct sk_buff *skb,
 343                         struct nf_conntrack_helper *helper)
 344{
 345        struct nlattr *nest_parms;
 346
 347        nest_parms = nla_nest_start(skb, NFCTH_TUPLE | NLA_F_NESTED);
 348        if (nest_parms == NULL)
 349                goto nla_put_failure;
 350
 351        if (nla_put_be16(skb, NFCTH_TUPLE_L3PROTONUM,
 352                         htons(helper->tuple.src.l3num)))
 353                goto nla_put_failure;
 354
 355        if (nla_put_u8(skb, NFCTH_TUPLE_L4PROTONUM, helper->tuple.dst.protonum))
 356                goto nla_put_failure;
 357
 358        nla_nest_end(skb, nest_parms);
 359        return 0;
 360
 361nla_put_failure:
 362        return -1;
 363}
 364
 365static int
 366nfnl_cthelper_dump_policy(struct sk_buff *skb,
 367                        struct nf_conntrack_helper *helper)
 368{
 369        int i;
 370        struct nlattr *nest_parms1, *nest_parms2;
 371
 372        nest_parms1 = nla_nest_start(skb, NFCTH_POLICY | NLA_F_NESTED);
 373        if (nest_parms1 == NULL)
 374                goto nla_put_failure;
 375
 376        if (nla_put_be32(skb, NFCTH_POLICY_SET_NUM,
 377                         htonl(helper->expect_class_max)))
 378                goto nla_put_failure;
 379
 380        for (i=0; i<helper->expect_class_max; i++) {
 381                nest_parms2 = nla_nest_start(skb,
 382                                (NFCTH_POLICY_SET+i) | NLA_F_NESTED);
 383                if (nest_parms2 == NULL)
 384                        goto nla_put_failure;
 385
 386                if (nla_put_string(skb, NFCTH_POLICY_NAME,
 387                                   helper->expect_policy[i].name))
 388                        goto nla_put_failure;
 389
 390                if (nla_put_be32(skb, NFCTH_POLICY_EXPECT_MAX,
 391                                 htonl(helper->expect_policy[i].max_expected)))
 392                        goto nla_put_failure;
 393
 394                if (nla_put_be32(skb, NFCTH_POLICY_EXPECT_TIMEOUT,
 395                                 htonl(helper->expect_policy[i].timeout)))
 396                        goto nla_put_failure;
 397
 398                nla_nest_end(skb, nest_parms2);
 399        }
 400        nla_nest_end(skb, nest_parms1);
 401        return 0;
 402
 403nla_put_failure:
 404        return -1;
 405}
 406
 407static int
 408nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
 409                        int event, struct nf_conntrack_helper *helper)
 410{
 411        struct nlmsghdr *nlh;
 412        struct nfgenmsg *nfmsg;
 413        unsigned int flags = portid ? NLM_F_MULTI : 0;
 414        int status;
 415
 416        event |= NFNL_SUBSYS_CTHELPER << 8;
 417        nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags);
 418        if (nlh == NULL)
 419                goto nlmsg_failure;
 420
 421        nfmsg = nlmsg_data(nlh);
 422        nfmsg->nfgen_family = AF_UNSPEC;
 423        nfmsg->version = NFNETLINK_V0;
 424        nfmsg->res_id = 0;
 425
 426        if (nla_put_string(skb, NFCTH_NAME, helper->name))
 427                goto nla_put_failure;
 428
 429        if (nla_put_be32(skb, NFCTH_QUEUE_NUM, htonl(helper->queue_num)))
 430                goto nla_put_failure;
 431
 432        if (nfnl_cthelper_dump_tuple(skb, helper) < 0)
 433                goto nla_put_failure;
 434
 435        if (nfnl_cthelper_dump_policy(skb, helper) < 0)
 436                goto nla_put_failure;
 437
 438        if (nla_put_be32(skb, NFCTH_PRIV_DATA_LEN, htonl(helper->data_len)))
 439                goto nla_put_failure;
 440
 441        if (helper->flags & NF_CT_HELPER_F_CONFIGURED)
 442                status = NFCT_HELPER_STATUS_ENABLED;
 443        else
 444                status = NFCT_HELPER_STATUS_DISABLED;
 445
 446        if (nla_put_be32(skb, NFCTH_STATUS, htonl(status)))
 447                goto nla_put_failure;
 448
 449        nlmsg_end(skb, nlh);
 450        return skb->len;
 451
 452nlmsg_failure:
 453nla_put_failure:
 454        nlmsg_cancel(skb, nlh);
 455        return -1;
 456}
 457
 458static int
 459nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
 460{
 461        struct nf_conntrack_helper *cur, *last;
 462
 463        rcu_read_lock();
 464        last = (struct nf_conntrack_helper *)cb->args[1];
 465        for (; cb->args[0] < nf_ct_helper_hsize; cb->args[0]++) {
 466restart:
 467                hlist_for_each_entry_rcu(cur,
 468                                &nf_ct_helper_hash[cb->args[0]], hnode) {
 469
 470                        /* skip non-userspace conntrack helpers. */
 471                        if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
 472                                continue;
 473
 474                        if (cb->args[1]) {
 475                                if (cur != last)
 476                                        continue;
 477                                cb->args[1] = 0;
 478                        }
 479                        if (nfnl_cthelper_fill_info(skb,
 480                                            NETLINK_CB(cb->skb).portid,
 481                                            cb->nlh->nlmsg_seq,
 482                                            NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
 483                                            NFNL_MSG_CTHELPER_NEW, cur) < 0) {
 484                                cb->args[1] = (unsigned long)cur;
 485                                goto out;
 486                        }
 487                }
 488        }
 489        if (cb->args[1]) {
 490                cb->args[1] = 0;
 491                goto restart;
 492        }
 493out:
 494        rcu_read_unlock();
 495        return skb->len;
 496}
 497
 498static int
 499nfnl_cthelper_get(struct sock *nfnl, struct sk_buff *skb,
 500                  const struct nlmsghdr *nlh, const struct nlattr * const tb[])
 501{
 502        int ret = -ENOENT, i;
 503        struct nf_conntrack_helper *cur;
 504        struct sk_buff *skb2;
 505        char *helper_name = NULL;
 506        struct nf_conntrack_tuple tuple;
 507        bool tuple_set = false;
 508
 509        if (nlh->nlmsg_flags & NLM_F_DUMP) {
 510                struct netlink_dump_control c = {
 511                        .dump = nfnl_cthelper_dump_table,
 512                };
 513                return netlink_dump_start(nfnl, skb, nlh, &c);
 514        }
 515
 516        if (tb[NFCTH_NAME])
 517                helper_name = nla_data(tb[NFCTH_NAME]);
 518
 519        if (tb[NFCTH_TUPLE]) {
 520                ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]);
 521                if (ret < 0)
 522                        return ret;
 523
 524                tuple_set = true;
 525        }
 526
 527        for (i = 0; i < nf_ct_helper_hsize; i++) {
 528                hlist_for_each_entry_rcu(cur, &nf_ct_helper_hash[i], hnode) {
 529
 530                        /* skip non-userspace conntrack helpers. */
 531                        if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
 532                                continue;
 533
 534                        if (helper_name && strncmp(cur->name, helper_name,
 535                                                NF_CT_HELPER_NAME_LEN) != 0) {
 536                                continue;
 537                        }
 538                        if (tuple_set &&
 539                            (tuple.src.l3num != cur->tuple.src.l3num ||
 540                             tuple.dst.protonum != cur->tuple.dst.protonum))
 541                                continue;
 542
 543                        skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 544                        if (skb2 == NULL) {
 545                                ret = -ENOMEM;
 546                                break;
 547                        }
 548
 549                        ret = nfnl_cthelper_fill_info(skb2, NETLINK_CB(skb).portid,
 550                                                nlh->nlmsg_seq,
 551                                                NFNL_MSG_TYPE(nlh->nlmsg_type),
 552                                                NFNL_MSG_CTHELPER_NEW, cur);
 553                        if (ret <= 0) {
 554                                kfree_skb(skb2);
 555                                break;
 556                        }
 557
 558                        ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid,
 559                                                MSG_DONTWAIT);
 560                        if (ret > 0)
 561                                ret = 0;
 562
 563                        /* this avoids a loop in nfnetlink. */
 564                        return ret == -EAGAIN ? -ENOBUFS : ret;
 565                }
 566        }
 567        return ret;
 568}
 569
 570static int
 571nfnl_cthelper_del(struct sock *nfnl, struct sk_buff *skb,
 572             const struct nlmsghdr *nlh, const struct nlattr * const tb[])
 573{
 574        char *helper_name = NULL;
 575        struct nf_conntrack_helper *cur;
 576        struct hlist_node *tmp;
 577        struct nf_conntrack_tuple tuple;
 578        bool tuple_set = false, found = false;
 579        int i, j = 0, ret;
 580
 581        if (tb[NFCTH_NAME])
 582                helper_name = nla_data(tb[NFCTH_NAME]);
 583
 584        if (tb[NFCTH_TUPLE]) {
 585                ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]);
 586                if (ret < 0)
 587                        return ret;
 588
 589                tuple_set = true;
 590        }
 591
 592        for (i = 0; i < nf_ct_helper_hsize; i++) {
 593                hlist_for_each_entry_safe(cur, tmp, &nf_ct_helper_hash[i],
 594                                                                hnode) {
 595                        /* skip non-userspace conntrack helpers. */
 596                        if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
 597                                continue;
 598
 599                        j++;
 600
 601                        if (helper_name && strncmp(cur->name, helper_name,
 602                                                NF_CT_HELPER_NAME_LEN) != 0) {
 603                                continue;
 604                        }
 605                        if (tuple_set &&
 606                            (tuple.src.l3num != cur->tuple.src.l3num ||
 607                             tuple.dst.protonum != cur->tuple.dst.protonum))
 608                                continue;
 609
 610                        found = true;
 611                        nf_conntrack_helper_unregister(cur);
 612                }
 613        }
 614        /* Make sure we return success if we flush and there is no helpers */
 615        return (found || j == 0) ? 0 : -ENOENT;
 616}
 617
 618static const struct nla_policy nfnl_cthelper_policy[NFCTH_MAX+1] = {
 619        [NFCTH_NAME] = { .type = NLA_NUL_STRING,
 620                         .len = NF_CT_HELPER_NAME_LEN-1 },
 621        [NFCTH_QUEUE_NUM] = { .type = NLA_U32, },
 622};
 623
 624static const struct nfnl_callback nfnl_cthelper_cb[NFNL_MSG_CTHELPER_MAX] = {
 625        [NFNL_MSG_CTHELPER_NEW]         = { .call = nfnl_cthelper_new,
 626                                            .attr_count = NFCTH_MAX,
 627                                            .policy = nfnl_cthelper_policy },
 628        [NFNL_MSG_CTHELPER_GET]         = { .call = nfnl_cthelper_get,
 629                                            .attr_count = NFCTH_MAX,
 630                                            .policy = nfnl_cthelper_policy },
 631        [NFNL_MSG_CTHELPER_DEL]         = { .call = nfnl_cthelper_del,
 632                                            .attr_count = NFCTH_MAX,
 633                                            .policy = nfnl_cthelper_policy },
 634};
 635
 636static const struct nfnetlink_subsystem nfnl_cthelper_subsys = {
 637        .name                           = "cthelper",
 638        .subsys_id                      = NFNL_SUBSYS_CTHELPER,
 639        .cb_count                       = NFNL_MSG_CTHELPER_MAX,
 640        .cb                             = nfnl_cthelper_cb,
 641};
 642
 643MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTHELPER);
 644
 645static int __init nfnl_cthelper_init(void)
 646{
 647        int ret;
 648
 649        ret = nfnetlink_subsys_register(&nfnl_cthelper_subsys);
 650        if (ret < 0) {
 651                pr_err("nfnl_cthelper: cannot register with nfnetlink.\n");
 652                goto err_out;
 653        }
 654        return 0;
 655err_out:
 656        return ret;
 657}
 658
 659static void __exit nfnl_cthelper_exit(void)
 660{
 661        struct nf_conntrack_helper *cur;
 662        struct hlist_node *tmp;
 663        int i;
 664
 665        nfnetlink_subsys_unregister(&nfnl_cthelper_subsys);
 666
 667        for (i=0; i<nf_ct_helper_hsize; i++) {
 668                hlist_for_each_entry_safe(cur, tmp, &nf_ct_helper_hash[i],
 669                                                                        hnode) {
 670                        /* skip non-userspace conntrack helpers. */
 671                        if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
 672                                continue;
 673
 674                        nf_conntrack_helper_unregister(cur);
 675                }
 676        }
 677}
 678
 679module_init(nfnl_cthelper_init);
 680module_exit(nfnl_cthelper_exit);
 681
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.