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