linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c
<<
>>
Prefs
   1/* ip_conntrack proc compat - based on ip_conntrack_standalone.c
   2 *
   3 * (C) 1999-2001 Paul `Rusty' Russell
   4 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 */
  10#include <linux/types.h>
  11#include <linux/proc_fs.h>
  12#include <linux/seq_file.h>
  13#include <linux/percpu.h>
  14#include <net/net_namespace.h>
  15
  16#include <linux/netfilter.h>
  17#include <net/netfilter/nf_conntrack_core.h>
  18#include <net/netfilter/nf_conntrack_l3proto.h>
  19#include <net/netfilter/nf_conntrack_l4proto.h>
  20#include <net/netfilter/nf_conntrack_expect.h>
  21#include <net/netfilter/nf_conntrack_acct.h>
  22
  23struct ct_iter_state {
  24        struct seq_net_private p;
  25        unsigned int bucket;
  26};
  27
  28static struct hlist_nulls_node *ct_get_first(struct seq_file *seq)
  29{
  30        struct net *net = seq_file_net(seq);
  31        struct ct_iter_state *st = seq->private;
  32        struct hlist_nulls_node *n;
  33
  34        for (st->bucket = 0;
  35             st->bucket < nf_conntrack_htable_size;
  36             st->bucket++) {
  37                n = rcu_dereference(net->ct.hash[st->bucket].first);
  38                if (!is_a_nulls(n))
  39                        return n;
  40        }
  41        return NULL;
  42}
  43
  44static struct hlist_nulls_node *ct_get_next(struct seq_file *seq,
  45                                      struct hlist_nulls_node *head)
  46{
  47        struct net *net = seq_file_net(seq);
  48        struct ct_iter_state *st = seq->private;
  49
  50        head = rcu_dereference(head->next);
  51        while (is_a_nulls(head)) {
  52                if (likely(get_nulls_value(head) == st->bucket)) {
  53                        if (++st->bucket >= nf_conntrack_htable_size)
  54                                return NULL;
  55                }
  56                head = rcu_dereference(net->ct.hash[st->bucket].first);
  57        }
  58        return head;
  59}
  60
  61static struct hlist_nulls_node *ct_get_idx(struct seq_file *seq, loff_t pos)
  62{
  63        struct hlist_nulls_node *head = ct_get_first(seq);
  64
  65        if (head)
  66                while (pos && (head = ct_get_next(seq, head)))
  67                        pos--;
  68        return pos ? NULL : head;
  69}
  70
  71static void *ct_seq_start(struct seq_file *seq, loff_t *pos)
  72        __acquires(RCU)
  73{
  74        rcu_read_lock();
  75        return ct_get_idx(seq, *pos);
  76}
  77
  78static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
  79{
  80        (*pos)++;
  81        return ct_get_next(s, v);
  82}
  83
  84static void ct_seq_stop(struct seq_file *s, void *v)
  85        __releases(RCU)
  86{
  87        rcu_read_unlock();
  88}
  89
  90static int ct_seq_show(struct seq_file *s, void *v)
  91{
  92        struct nf_conntrack_tuple_hash *hash = v;
  93        struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(hash);
  94        const struct nf_conntrack_l3proto *l3proto;
  95        const struct nf_conntrack_l4proto *l4proto;
  96        int ret = 0;
  97
  98        NF_CT_ASSERT(ct);
  99        if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use)))
 100                return 0;
 101
 102
 103        /* we only want to print DIR_ORIGINAL */
 104        if (NF_CT_DIRECTION(hash))
 105                goto release;
 106        if (nf_ct_l3num(ct) != AF_INET)
 107                goto release;
 108
 109        l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct));
 110        NF_CT_ASSERT(l3proto);
 111        l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
 112        NF_CT_ASSERT(l4proto);
 113
 114        ret = -ENOSPC;
 115        if (seq_printf(s, "%-8s %u %ld ",
 116                      l4proto->name, nf_ct_protonum(ct),
 117                      timer_pending(&ct->timeout)
 118                      ? (long)(ct->timeout.expires - jiffies)/HZ : 0) != 0)
 119                goto release;
 120
 121        if (l4proto->print_conntrack && l4proto->print_conntrack(s, ct))
 122                goto release;
 123
 124        if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
 125                        l3proto, l4proto))
 126                goto release;
 127
 128        if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL))
 129                goto release;
 130
 131        if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status)))
 132                if (seq_printf(s, "[UNREPLIED] "))
 133                        goto release;
 134
 135        if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple,
 136                        l3proto, l4proto))
 137                goto release;
 138
 139        if (seq_print_acct(s, ct, IP_CT_DIR_REPLY))
 140                goto release;
 141
 142        if (test_bit(IPS_ASSURED_BIT, &ct->status))
 143                if (seq_printf(s, "[ASSURED] "))
 144                        goto release;
 145
 146#ifdef CONFIG_NF_CONNTRACK_MARK
 147        if (seq_printf(s, "mark=%u ", ct->mark))
 148                goto release;
 149#endif
 150
 151#ifdef CONFIG_NF_CONNTRACK_SECMARK
 152        if (seq_printf(s, "secmark=%u ", ct->secmark))
 153                goto release;
 154#endif
 155
 156        if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use)))
 157                goto release;
 158        ret = 0;
 159release:
 160        nf_ct_put(ct);
 161        return ret;
 162}
 163
 164static const struct seq_operations ct_seq_ops = {
 165        .start = ct_seq_start,
 166        .next  = ct_seq_next,
 167        .stop  = ct_seq_stop,
 168        .show  = ct_seq_show
 169};
 170
 171static int ct_open(struct inode *inode, struct file *file)
 172{
 173        return seq_open_net(inode, file, &ct_seq_ops,
 174                            sizeof(struct ct_iter_state));
 175}
 176
 177static const struct file_operations ct_file_ops = {
 178        .owner   = THIS_MODULE,
 179        .open    = ct_open,
 180        .read    = seq_read,
 181        .llseek  = seq_lseek,
 182        .release = seq_release_net,
 183};
 184
 185/* expects */
 186struct ct_expect_iter_state {
 187        struct seq_net_private p;
 188        unsigned int bucket;
 189};
 190
 191static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
 192{
 193        struct net *net = seq_file_net(seq);
 194        struct ct_expect_iter_state *st = seq->private;
 195        struct hlist_node *n;
 196
 197        for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
 198                n = rcu_dereference(net->ct.expect_hash[st->bucket].first);
 199                if (n)
 200                        return n;
 201        }
 202        return NULL;
 203}
 204
 205static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
 206                                             struct hlist_node *head)
 207{
 208        struct net *net = seq_file_net(seq);
 209        struct ct_expect_iter_state *st = seq->private;
 210
 211        head = rcu_dereference(head->next);
 212        while (head == NULL) {
 213                if (++st->bucket >= nf_ct_expect_hsize)
 214                        return NULL;
 215                head = rcu_dereference(net->ct.expect_hash[st->bucket].first);
 216        }
 217        return head;
 218}
 219
 220static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos)
 221{
 222        struct hlist_node *head = ct_expect_get_first(seq);
 223
 224        if (head)
 225                while (pos && (head = ct_expect_get_next(seq, head)))
 226                        pos--;
 227        return pos ? NULL : head;
 228}
 229
 230static void *exp_seq_start(struct seq_file *seq, loff_t *pos)
 231        __acquires(RCU)
 232{
 233        rcu_read_lock();
 234        return ct_expect_get_idx(seq, *pos);
 235}
 236
 237static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 238{
 239        (*pos)++;
 240        return ct_expect_get_next(seq, v);
 241}
 242
 243static void exp_seq_stop(struct seq_file *seq, void *v)
 244        __releases(RCU)
 245{
 246        rcu_read_unlock();
 247}
 248
 249static int exp_seq_show(struct seq_file *s, void *v)
 250{
 251        struct nf_conntrack_expect *exp;
 252        const struct hlist_node *n = v;
 253
 254        exp = hlist_entry(n, struct nf_conntrack_expect, hnode);
 255
 256        if (exp->tuple.src.l3num != AF_INET)
 257                return 0;
 258
 259        if (exp->timeout.function)
 260                seq_printf(s, "%ld ", timer_pending(&exp->timeout)
 261                           ? (long)(exp->timeout.expires - jiffies)/HZ : 0);
 262        else
 263                seq_printf(s, "- ");
 264
 265        seq_printf(s, "proto=%u ", exp->tuple.dst.protonum);
 266
 267        print_tuple(s, &exp->tuple,
 268                    __nf_ct_l3proto_find(exp->tuple.src.l3num),
 269                    __nf_ct_l4proto_find(exp->tuple.src.l3num,
 270                                         exp->tuple.dst.protonum));
 271        return seq_putc(s, '\n');
 272}
 273
 274static const struct seq_operations exp_seq_ops = {
 275        .start = exp_seq_start,
 276        .next = exp_seq_next,
 277        .stop = exp_seq_stop,
 278        .show = exp_seq_show
 279};
 280
 281static int exp_open(struct inode *inode, struct file *file)
 282{
 283        return seq_open_net(inode, file, &exp_seq_ops,
 284                            sizeof(struct ct_expect_iter_state));
 285}
 286
 287static const struct file_operations ip_exp_file_ops = {
 288        .owner   = THIS_MODULE,
 289        .open    = exp_open,
 290        .read    = seq_read,
 291        .llseek  = seq_lseek,
 292        .release = seq_release_net,
 293};
 294
 295static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos)
 296{
 297        struct net *net = seq_file_net(seq);
 298        int cpu;
 299
 300        if (*pos == 0)
 301                return SEQ_START_TOKEN;
 302
 303        for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) {
 304                if (!cpu_possible(cpu))
 305                        continue;
 306                *pos = cpu+1;
 307                return per_cpu_ptr(net->ct.stat, cpu);
 308        }
 309
 310        return NULL;
 311}
 312
 313static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 314{
 315        struct net *net = seq_file_net(seq);
 316        int cpu;
 317
 318        for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) {
 319                if (!cpu_possible(cpu))
 320                        continue;
 321                *pos = cpu+1;
 322                return per_cpu_ptr(net->ct.stat, cpu);
 323        }
 324
 325        return NULL;
 326}
 327
 328static void ct_cpu_seq_stop(struct seq_file *seq, void *v)
 329{
 330}
 331
 332static int ct_cpu_seq_show(struct seq_file *seq, void *v)
 333{
 334        struct net *net = seq_file_net(seq);
 335        unsigned int nr_conntracks = atomic_read(&net->ct.count);
 336        const struct ip_conntrack_stat *st = v;
 337
 338        if (v == SEQ_START_TOKEN) {
 339                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");
 340                return 0;
 341        }
 342
 343        seq_printf(seq, "%08x  %08x %08x %08x %08x %08x %08x %08x "
 344                        "%08x %08x %08x %08x %08x  %08x %08x %08x \n",
 345                   nr_conntracks,
 346                   st->searched,
 347                   st->found,
 348                   st->new,
 349                   st->invalid,
 350                   st->ignore,
 351                   st->delete,
 352                   st->delete_list,
 353                   st->insert,
 354                   st->insert_failed,
 355                   st->drop,
 356                   st->early_drop,
 357                   st->error,
 358
 359                   st->expect_new,
 360                   st->expect_create,
 361                   st->expect_delete
 362                );
 363        return 0;
 364}
 365
 366static const struct seq_operations ct_cpu_seq_ops = {
 367        .start  = ct_cpu_seq_start,
 368        .next   = ct_cpu_seq_next,
 369        .stop   = ct_cpu_seq_stop,
 370        .show   = ct_cpu_seq_show,
 371};
 372
 373static int ct_cpu_seq_open(struct inode *inode, struct file *file)
 374{
 375        return seq_open_net(inode, file, &ct_cpu_seq_ops,
 376                            sizeof(struct seq_net_private));
 377}
 378
 379static const struct file_operations ct_cpu_seq_fops = {
 380        .owner   = THIS_MODULE,
 381        .open    = ct_cpu_seq_open,
 382        .read    = seq_read,
 383        .llseek  = seq_lseek,
 384        .release = seq_release_net,
 385};
 386
 387static int __net_init ip_conntrack_net_init(struct net *net)
 388{
 389        struct proc_dir_entry *proc, *proc_exp, *proc_stat;
 390
 391        proc = proc_net_fops_create(net, "ip_conntrack", 0440, &ct_file_ops);
 392        if (!proc)
 393                goto err1;
 394
 395        proc_exp = proc_net_fops_create(net, "ip_conntrack_expect", 0440,
 396                                        &ip_exp_file_ops);
 397        if (!proc_exp)
 398                goto err2;
 399
 400        proc_stat = proc_create("ip_conntrack", S_IRUGO,
 401                                net->proc_net_stat, &ct_cpu_seq_fops);
 402        if (!proc_stat)
 403                goto err3;
 404        return 0;
 405
 406err3:
 407        proc_net_remove(net, "ip_conntrack_expect");
 408err2:
 409        proc_net_remove(net, "ip_conntrack");
 410err1:
 411        return -ENOMEM;
 412}
 413
 414static void __net_exit ip_conntrack_net_exit(struct net *net)
 415{
 416        remove_proc_entry("ip_conntrack", net->proc_net_stat);
 417        proc_net_remove(net, "ip_conntrack_expect");
 418        proc_net_remove(net, "ip_conntrack");
 419}
 420
 421static struct pernet_operations ip_conntrack_net_ops = {
 422        .init = ip_conntrack_net_init,
 423        .exit = ip_conntrack_net_exit,
 424};
 425
 426int __init nf_conntrack_ipv4_compat_init(void)
 427{
 428        return register_pernet_subsys(&ip_conntrack_net_ops);
 429}
 430
 431void __exit nf_conntrack_ipv4_compat_fini(void)
 432{
 433        unregister_pernet_subsys(&ip_conntrack_net_ops);
 434}
 435