linux/net/netfilter/nf_conntrack_standalone.c
<<
>>
Prefs
   1/* (C) 1999-2001 Paul `Rusty' Russell
   2 * (C) 2002-2004 Netfilter Core Team <coreteam@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.
   7 */
   8
   9#include <linux/types.h>
  10#include <linux/netfilter.h>
  11#include <linux/module.h>
  12#include <linux/skbuff.h>
  13#include <linux/proc_fs.h>
  14#include <linux/seq_file.h>
  15#include <linux/percpu.h>
  16#include <linux/netdevice.h>
  17#include <net/net_namespace.h>
  18#ifdef CONFIG_SYSCTL
  19#include <linux/sysctl.h>
  20#endif
  21
  22#include <net/netfilter/nf_conntrack.h>
  23#include <net/netfilter/nf_conntrack_core.h>
  24#include <net/netfilter/nf_conntrack_l3proto.h>
  25#include <net/netfilter/nf_conntrack_l4proto.h>
  26#include <net/netfilter/nf_conntrack_expect.h>
  27#include <net/netfilter/nf_conntrack_helper.h>
  28#include <net/netfilter/nf_conntrack_acct.h>
  29
  30MODULE_LICENSE("GPL");
  31
  32#ifdef CONFIG_PROC_FS
  33int
  34print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple,
  35            const struct nf_conntrack_l3proto *l3proto,
  36            const struct nf_conntrack_l4proto *l4proto)
  37{
  38        return l3proto->print_tuple(s, tuple) || l4proto->print_tuple(s, tuple);
  39}
  40EXPORT_SYMBOL_GPL(print_tuple);
  41
  42struct ct_iter_state {
  43        struct seq_net_private p;
  44        unsigned int bucket;
  45};
  46
  47static struct hlist_node *ct_get_first(struct seq_file *seq)
  48{
  49        struct net *net = seq_file_net(seq);
  50        struct ct_iter_state *st = seq->private;
  51        struct hlist_node *n;
  52
  53        for (st->bucket = 0;
  54             st->bucket < nf_conntrack_htable_size;
  55             st->bucket++) {
  56                n = rcu_dereference(net->ct.hash[st->bucket].first);
  57                if (n)
  58                        return n;
  59        }
  60        return NULL;
  61}
  62
  63static struct hlist_node *ct_get_next(struct seq_file *seq,
  64                                      struct hlist_node *head)
  65{
  66        struct net *net = seq_file_net(seq);
  67        struct ct_iter_state *st = seq->private;
  68
  69        head = rcu_dereference(head->next);
  70        while (head == NULL) {
  71                if (++st->bucket >= nf_conntrack_htable_size)
  72                        return NULL;
  73                head = rcu_dereference(net->ct.hash[st->bucket].first);
  74        }
  75        return head;
  76}
  77
  78static struct hlist_node *ct_get_idx(struct seq_file *seq, loff_t pos)
  79{
  80        struct hlist_node *head = ct_get_first(seq);
  81
  82        if (head)
  83                while (pos && (head = ct_get_next(seq, head)))
  84                        pos--;
  85        return pos ? NULL : head;
  86}
  87
  88static void *ct_seq_start(struct seq_file *seq, loff_t *pos)
  89        __acquires(RCU)
  90{
  91        rcu_read_lock();
  92        return ct_get_idx(seq, *pos);
  93}
  94
  95static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
  96{
  97        (*pos)++;
  98        return ct_get_next(s, v);
  99}
 100
 101static void ct_seq_stop(struct seq_file *s, void *v)
 102        __releases(RCU)
 103{
 104        rcu_read_unlock();
 105}
 106
 107/* return 0 on success, 1 in case of error */
 108static int ct_seq_show(struct seq_file *s, void *v)
 109{
 110        const struct nf_conntrack_tuple_hash *hash = v;
 111        const struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(hash);
 112        const struct nf_conntrack_l3proto *l3proto;
 113        const struct nf_conntrack_l4proto *l4proto;
 114
 115        NF_CT_ASSERT(ct);
 116
 117        /* we only want to print DIR_ORIGINAL */
 118        if (NF_CT_DIRECTION(hash))
 119                return 0;
 120
 121        l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct));
 122        NF_CT_ASSERT(l3proto);
 123        l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
 124        NF_CT_ASSERT(l4proto);
 125
 126        if (seq_printf(s, "%-8s %u %-8s %u %ld ",
 127                       l3proto->name, nf_ct_l3num(ct),
 128                       l4proto->name, nf_ct_protonum(ct),
 129                       timer_pending(&ct->timeout)
 130                       ? (long)(ct->timeout.expires - jiffies)/HZ : 0) != 0)
 131                return -ENOSPC;
 132
 133        if (l4proto->print_conntrack && l4proto->print_conntrack(s, ct))
 134                return -ENOSPC;
 135
 136        if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
 137                        l3proto, l4proto))
 138                return -ENOSPC;
 139
 140        if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL))
 141                return -ENOSPC;
 142
 143        if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status)))
 144                if (seq_printf(s, "[UNREPLIED] "))
 145                        return -ENOSPC;
 146
 147        if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple,
 148                        l3proto, l4proto))
 149                return -ENOSPC;
 150
 151        if (seq_print_acct(s, ct, IP_CT_DIR_REPLY))
 152                return -ENOSPC;
 153
 154        if (test_bit(IPS_ASSURED_BIT, &ct->status))
 155                if (seq_printf(s, "[ASSURED] "))
 156                        return -ENOSPC;
 157
 158#if defined(CONFIG_NF_CONNTRACK_MARK)
 159        if (seq_printf(s, "mark=%u ", ct->mark))
 160                return -ENOSPC;
 161#endif
 162
 163#ifdef CONFIG_NF_CONNTRACK_SECMARK
 164        if (seq_printf(s, "secmark=%u ", ct->secmark))
 165                return -ENOSPC;
 166#endif
 167
 168        if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use)))
 169                return -ENOSPC;
 170
 171        return 0;
 172}
 173
 174static const struct seq_operations ct_seq_ops = {
 175        .start = ct_seq_start,
 176        .next  = ct_seq_next,
 177        .stop  = ct_seq_stop,
 178        .show  = ct_seq_show
 179};
 180
 181static int ct_open(struct inode *inode, struct file *file)
 182{
 183        return seq_open_net(inode, file, &ct_seq_ops,
 184                        sizeof(struct ct_iter_state));
 185}
 186
 187static const struct file_operations ct_file_ops = {
 188        .owner   = THIS_MODULE,
 189        .open    = ct_open,
 190        .read    = seq_read,
 191        .llseek  = seq_lseek,
 192        .release = seq_release_net,
 193};
 194
 195static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos)
 196{
 197        struct net *net = seq_file_net(seq);
 198        int cpu;
 199
 200        if (*pos == 0)
 201                return SEQ_START_TOKEN;
 202
 203        for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) {
 204                if (!cpu_possible(cpu))
 205                        continue;
 206                *pos = cpu + 1;
 207                return per_cpu_ptr(net->ct.stat, cpu);
 208        }
 209
 210        return NULL;
 211}
 212
 213static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 214{
 215        struct net *net = seq_file_net(seq);
 216        int cpu;
 217
 218        for (cpu = *pos; cpu < NR_CPUS; ++cpu) {
 219                if (!cpu_possible(cpu))
 220                        continue;
 221                *pos = cpu + 1;
 222                return per_cpu_ptr(net->ct.stat, cpu);
 223        }
 224
 225        return NULL;
 226}
 227
 228static void ct_cpu_seq_stop(struct seq_file *seq, void *v)
 229{
 230}
 231
 232static int ct_cpu_seq_show(struct seq_file *seq, void *v)
 233{
 234        struct net *net = seq_file_net(seq);
 235        unsigned int nr_conntracks = atomic_read(&net->ct.count);
 236        const struct ip_conntrack_stat *st = v;
 237
 238        if (v == SEQ_START_TOKEN) {
 239                seq_printf(seq, "entries  searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error  expect_new expect_create expect_delete\n");
 240                return 0;
 241        }
 242
 243        seq_printf(seq, "%08x  %08x %08x %08x %08x %08x %08x %08x "
 244                        "%08x %08x %08x %08x %08x  %08x %08x %08x \n",
 245                   nr_conntracks,
 246                   st->searched,
 247                   st->found,
 248                   st->new,
 249                   st->invalid,
 250                   st->ignore,
 251                   st->delete,
 252                   st->delete_list,
 253                   st->insert,
 254                   st->insert_failed,
 255                   st->drop,
 256                   st->early_drop,
 257                   st->error,
 258
 259                   st->expect_new,
 260                   st->expect_create,
 261                   st->expect_delete
 262                );
 263        return 0;
 264}
 265
 266static const struct seq_operations ct_cpu_seq_ops = {
 267        .start  = ct_cpu_seq_start,
 268        .next   = ct_cpu_seq_next,
 269        .stop   = ct_cpu_seq_stop,
 270        .show   = ct_cpu_seq_show,
 271};
 272
 273static int ct_cpu_seq_open(struct inode *inode, struct file *file)
 274{
 275        return seq_open_net(inode, file, &ct_cpu_seq_ops,
 276                            sizeof(struct seq_net_private));
 277}
 278
 279static const struct file_operations ct_cpu_seq_fops = {
 280        .owner   = THIS_MODULE,
 281        .open    = ct_cpu_seq_open,
 282        .read    = seq_read,
 283        .llseek  = seq_lseek,
 284        .release = seq_release_net,
 285};
 286
 287static int nf_conntrack_standalone_init_proc(struct net *net)
 288{
 289        struct proc_dir_entry *pde;
 290
 291        pde = proc_net_fops_create(net, "nf_conntrack", 0440, &ct_file_ops);
 292        if (!pde)
 293                goto out_nf_conntrack;
 294
 295        pde = proc_create("nf_conntrack", S_IRUGO, net->proc_net_stat,
 296                          &ct_cpu_seq_fops);
 297        if (!pde)
 298                goto out_stat_nf_conntrack;
 299        return 0;
 300
 301out_stat_nf_conntrack:
 302        proc_net_remove(net, "nf_conntrack");
 303out_nf_conntrack:
 304        return -ENOMEM;
 305}
 306
 307static void nf_conntrack_standalone_fini_proc(struct net *net)
 308{
 309        remove_proc_entry("nf_conntrack", net->proc_net_stat);
 310        proc_net_remove(net, "nf_conntrack");
 311}
 312#else
 313static int nf_conntrack_standalone_init_proc(struct net *net)
 314{
 315        return 0;
 316}
 317
 318static void nf_conntrack_standalone_fini_proc(struct net *net)
 319{
 320}
 321#endif /* CONFIG_PROC_FS */
 322
 323/* Sysctl support */
 324
 325#ifdef CONFIG_SYSCTL
 326/* Log invalid packets of a given protocol */
 327static int log_invalid_proto_min = 0;
 328static int log_invalid_proto_max = 255;
 329
 330static struct ctl_table_header *nf_ct_netfilter_header;
 331
 332static ctl_table nf_ct_sysctl_table[] = {
 333        {
 334                .ctl_name       = NET_NF_CONNTRACK_MAX,
 335                .procname       = "nf_conntrack_max",
 336                .data           = &nf_conntrack_max,
 337                .maxlen         = sizeof(int),
 338                .mode           = 0644,
 339                .proc_handler   = &proc_dointvec,
 340        },
 341        {
 342                .ctl_name       = NET_NF_CONNTRACK_COUNT,
 343                .procname       = "nf_conntrack_count",
 344                .data           = &init_net.ct.count,
 345                .maxlen         = sizeof(int),
 346                .mode           = 0444,
 347                .proc_handler   = &proc_dointvec,
 348        },
 349        {
 350                .ctl_name       = NET_NF_CONNTRACK_BUCKETS,
 351                .procname       = "nf_conntrack_buckets",
 352                .data           = &nf_conntrack_htable_size,
 353                .maxlen         = sizeof(unsigned int),
 354                .mode           = 0444,
 355                .proc_handler   = &proc_dointvec,
 356        },
 357        {
 358                .ctl_name       = NET_NF_CONNTRACK_CHECKSUM,
 359                .procname       = "nf_conntrack_checksum",
 360                .data           = &init_net.ct.sysctl_checksum,
 361                .maxlen         = sizeof(unsigned int),
 362                .mode           = 0644,
 363                .proc_handler   = &proc_dointvec,
 364        },
 365        {
 366                .ctl_name       = NET_NF_CONNTRACK_LOG_INVALID,
 367                .procname       = "nf_conntrack_log_invalid",
 368                .data           = &init_net.ct.sysctl_log_invalid,
 369                .maxlen         = sizeof(unsigned int),
 370                .mode           = 0644,
 371                .proc_handler   = &proc_dointvec_minmax,
 372                .strategy       = &sysctl_intvec,
 373                .extra1         = &log_invalid_proto_min,
 374                .extra2         = &log_invalid_proto_max,
 375        },
 376        {
 377                .ctl_name       = CTL_UNNUMBERED,
 378                .procname       = "nf_conntrack_expect_max",
 379                .data           = &nf_ct_expect_max,
 380                .maxlen         = sizeof(int),
 381                .mode           = 0644,
 382                .proc_handler   = &proc_dointvec,
 383        },
 384        { .ctl_name = 0 }
 385};
 386
 387#define NET_NF_CONNTRACK_MAX 2089
 388
 389static ctl_table nf_ct_netfilter_table[] = {
 390        {
 391                .ctl_name       = NET_NF_CONNTRACK_MAX,
 392                .procname       = "nf_conntrack_max",
 393                .data           = &nf_conntrack_max,
 394                .maxlen         = sizeof(int),
 395                .mode           = 0644,
 396                .proc_handler   = &proc_dointvec,
 397        },
 398        { .ctl_name = 0 }
 399};
 400
 401static struct ctl_path nf_ct_path[] = {
 402        { .procname = "net", .ctl_name = CTL_NET, },
 403        { }
 404};
 405
 406static int nf_conntrack_standalone_init_sysctl(struct net *net)
 407{
 408        struct ctl_table *table;
 409
 410        if (net_eq(net, &init_net)) {
 411                nf_ct_netfilter_header =
 412                       register_sysctl_paths(nf_ct_path, nf_ct_netfilter_table);
 413                if (!nf_ct_netfilter_header)
 414                        goto out;
 415        }
 416
 417        table = kmemdup(nf_ct_sysctl_table, sizeof(nf_ct_sysctl_table),
 418                        GFP_KERNEL);
 419        if (!table)
 420                goto out_kmemdup;
 421
 422        table[1].data = &net->ct.count;
 423        table[3].data = &net->ct.sysctl_checksum;
 424        table[4].data = &net->ct.sysctl_log_invalid;
 425
 426        net->ct.sysctl_header = register_net_sysctl_table(net,
 427                                        nf_net_netfilter_sysctl_path, table);
 428        if (!net->ct.sysctl_header)
 429                goto out_unregister_netfilter;
 430
 431        return 0;
 432
 433out_unregister_netfilter:
 434        kfree(table);
 435out_kmemdup:
 436        if (net_eq(net, &init_net))
 437                unregister_sysctl_table(nf_ct_netfilter_header);
 438out:
 439        printk("nf_conntrack: can't register to sysctl.\n");
 440        return -ENOMEM;
 441}
 442
 443static void nf_conntrack_standalone_fini_sysctl(struct net *net)
 444{
 445        struct ctl_table *table;
 446
 447        if (net_eq(net, &init_net))
 448                unregister_sysctl_table(nf_ct_netfilter_header);
 449        table = net->ct.sysctl_header->ctl_table_arg;
 450        unregister_net_sysctl_table(net->ct.sysctl_header);
 451        kfree(table);
 452}
 453#else
 454static int nf_conntrack_standalone_init_sysctl(struct net *net)
 455{
 456        return 0;
 457}
 458
 459static void nf_conntrack_standalone_fini_sysctl(struct net *net)
 460{
 461}
 462#endif /* CONFIG_SYSCTL */
 463
 464static int nf_conntrack_net_init(struct net *net)
 465{
 466        int ret;
 467
 468        ret = nf_conntrack_init(net);
 469        if (ret < 0)
 470                goto out_init;
 471        ret = nf_conntrack_standalone_init_proc(net);
 472        if (ret < 0)
 473                goto out_proc;
 474        net->ct.sysctl_checksum = 1;
 475        net->ct.sysctl_log_invalid = 0;
 476        ret = nf_conntrack_standalone_init_sysctl(net);
 477        if (ret < 0)
 478                goto out_sysctl;
 479        return 0;
 480
 481out_sysctl:
 482        nf_conntrack_standalone_fini_proc(net);
 483out_proc:
 484        nf_conntrack_cleanup(net);
 485out_init:
 486        return ret;
 487}
 488
 489static void nf_conntrack_net_exit(struct net *net)
 490{
 491        nf_conntrack_standalone_fini_sysctl(net);
 492        nf_conntrack_standalone_fini_proc(net);
 493        nf_conntrack_cleanup(net);
 494}
 495
 496static struct pernet_operations nf_conntrack_net_ops = {
 497        .init = nf_conntrack_net_init,
 498        .exit = nf_conntrack_net_exit,
 499};
 500
 501static int __init nf_conntrack_standalone_init(void)
 502{
 503        return register_pernet_subsys(&nf_conntrack_net_ops);
 504}
 505
 506static void __exit nf_conntrack_standalone_fini(void)
 507{
 508        unregister_pernet_subsys(&nf_conntrack_net_ops);
 509}
 510
 511module_init(nf_conntrack_standalone_init);
 512module_exit(nf_conntrack_standalone_fini);
 513
 514/* Some modules need us, but don't depend directly on any symbol.
 515   They should call this. */
 516void need_conntrack(void)
 517{
 518}
 519EXPORT_SYMBOL_GPL(need_conntrack);
 520