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