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