Commit 331c9e9a authored by Harald Welte's avatar Harald Welte Committed by David S. Miller

[NETFILTER]: Move /proc/net/ip_conntrack to seq_file

This patch makes ip_conntrack use the seq_file API
Signed-off-by: default avatarHarald Welte <laforge@netfilter.org>
Signed-off-by: default avatarDavid S. Miller <davem@redhat.com>
parent fd8bcd00
......@@ -3,6 +3,11 @@
#define _IP_CONNTRACK_PROTOCOL_H
#include <linux/netfilter_ipv4/ip_conntrack.h>
/* length of buffer to which print_tuple/print_conntrack members are
* writing */
#define IP_CT_PRINT_BUFLEN 100
struct ip_conntrack_protocol
{
/* Next pointer. */
......
......@@ -20,6 +20,7 @@
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
#endif
......@@ -63,142 +64,224 @@ print_tuple(char *buffer, const struct ip_conntrack_tuple *tuple,
return len;
}
/* FIXME: Don't print source proto part. --RR */
static unsigned int
print_expect(char *buffer, const struct ip_conntrack_expect *expect)
{
unsigned int len;
if (expect->expectant->helper->timeout)
len = sprintf(buffer, "EXPECTING: %lu ",
timer_pending(&expect->timeout)
? (expect->timeout.expires - jiffies)/HZ : 0);
else
len = sprintf(buffer, "EXPECTING: - ");
len += sprintf(buffer + len, "use=%u proto=%u ",
atomic_read(&expect->use), expect->tuple.dst.protonum);
len += print_tuple(buffer + len, &expect->tuple,
__ip_ct_find_proto(expect->tuple.dst.protonum));
len += sprintf(buffer + len, "\n");
return len;
}
#ifdef CONFIG_IP_NF_CT_ACCT
static unsigned int
print_counters(char *buffer, struct ip_conntrack_counter *counter)
seq_print_counters(struct seq_file *s, struct ip_conntrack_counter *counter)
{
return sprintf(buffer, "packets=%llu bytes=%llu ",
counter->packets, counter->bytes);
return seq_printf(s, "packets=%llu bytes=%llu ",
counter->packets, counter->bytes);
}
#else
#define print_counters(x, y) 0
#define seq_print_counters(x, y) 0
#endif
static unsigned int
print_conntrack(char *buffer, struct ip_conntrack *conntrack)
static void *ct_seq_start(struct seq_file *s, loff_t *pos)
{
unsigned int len;
struct ip_conntrack_protocol *proto
= __ip_ct_find_proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
.tuple.dst.protonum);
unsigned int *bucket;
len = sprintf(buffer, "%-8s %u %lu ",
proto->name,
conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
.tuple.dst.protonum,
timer_pending(&conntrack->timeout)
? (conntrack->timeout.expires - jiffies)/HZ : 0);
len += proto->print_conntrack(buffer + len, conntrack);
len += print_tuple(buffer + len,
&conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
proto);
len += print_counters(buffer + len,
&conntrack->counters[IP_CT_DIR_ORIGINAL]);
if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)))
len += sprintf(buffer + len, "[UNREPLIED] ");
len += print_tuple(buffer + len,
&conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
proto);
len += print_counters(buffer + len,
&conntrack->counters[IP_CT_DIR_REPLY]);
if (test_bit(IPS_ASSURED_BIT, &conntrack->status))
len += sprintf(buffer + len, "[ASSURED] ");
len += sprintf(buffer + len, "use=%u ",
atomic_read(&conntrack->ct_general.use));
len += sprintf(buffer + len, "\n");
/* strange seq_file api calls stop even if we fail,
* thus we need to grab lock since stop unlocks */
READ_LOCK(&ip_conntrack_lock);
if (*pos >= ip_conntrack_htable_size)
return NULL;
return len;
bucket = kmalloc(sizeof(unsigned int), GFP_KERNEL);
if (!bucket) {
return ERR_PTR(-ENOMEM);
}
*bucket = *pos;
return bucket;
}
static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
unsigned int *bucket = (unsigned int *) v;
/* Returns true when finished. */
static inline int
conntrack_iterate(const struct ip_conntrack_tuple_hash *hash,
char *buffer, off_t offset, off_t *upto,
unsigned int *len, unsigned int maxlen)
*pos = ++(*bucket);
if (*pos >= ip_conntrack_htable_size) {
kfree(v);
return NULL;
}
return bucket;
}
static void ct_seq_stop(struct seq_file *s, void *v)
{
unsigned int newlen;
IP_NF_ASSERT(hash->ctrack);
READ_UNLOCK(&ip_conntrack_lock);
}
/* return 0 on success, 1 in case of error */
static int ct_seq_real_show(const struct ip_conntrack_tuple_hash *hash,
struct seq_file *s)
{
struct ip_conntrack *conntrack = hash->ctrack;
struct ip_conntrack_protocol *proto;
char buffer[IP_CT_PRINT_BUFLEN];
MUST_BE_READ_LOCKED(&ip_conntrack_lock);
/* Only count originals */
IP_NF_ASSERT(conntrack);
/* we only want to print DIR_ORIGINAL */
if (DIRECTION(hash))
return 0;
if ((*upto)++ < offset)
return 0;
proto = __ip_ct_find_proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
.tuple.dst.protonum);
IP_NF_ASSERT(proto);
if (seq_printf(s, "%-8s %u %lu ",
proto->name,
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum,
timer_pending(&conntrack->timeout)
? (conntrack->timeout.expires - jiffies)/HZ : 0) != 0)
return 1;
proto->print_conntrack(buffer, conntrack);
if (seq_puts(s, buffer))
return 1;
print_tuple(buffer, &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
proto);
if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_ORIGINAL]))
return 1;
if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)))
if (seq_printf(s, "[UNREPLIED] "))
return 1;
print_tuple(buffer, &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
proto);
if (seq_puts(s, buffer))
return 1;
if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_REPLY]))
return 1;
newlen = print_conntrack(buffer + *len, hash->ctrack);
if (*len + newlen > maxlen)
if (test_bit(IPS_ASSURED_BIT, &conntrack->status))
if (seq_printf(s, "[ASSURED] "))
return 1;
if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use)))
return 1;
else *len += newlen;
return 0;
}
static int
list_conntracks(char *buffer, char **start, off_t offset, int length)
static int ct_seq_show(struct seq_file *s, void *v)
{
unsigned int i;
unsigned int len = 0;
off_t upto = 0;
struct list_head *e;
unsigned int *bucket = (unsigned int *) v;
READ_LOCK(&ip_conntrack_lock);
/* Traverse hash; print originals then reply. */
for (i = 0; i < ip_conntrack_htable_size; i++) {
if (LIST_FIND(&ip_conntrack_hash[i], conntrack_iterate,
struct ip_conntrack_tuple_hash *,
buffer, offset, &upto, &len, length))
goto finished;
if (LIST_FIND(&ip_conntrack_hash[*bucket], ct_seq_real_show,
struct ip_conntrack_tuple_hash *, s)) {
/* buffer was filled and unable to print that tuple */
return 1;
}
return 0;
}
static struct seq_operations ct_seq_ops = {
.start = ct_seq_start,
.next = ct_seq_next,
.stop = ct_seq_stop,
.show = ct_seq_show
};
static int ct_open(struct inode *inode, struct file *file)
{
return seq_open(file, &ct_seq_ops);
}
/* Now iterate through expecteds. */
static struct file_operations ct_file_ops = {
.owner = THIS_MODULE,
.open = ct_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
/* expects */
static void *exp_seq_start(struct seq_file *s, loff_t *pos)
{
struct list_head *e = &ip_conntrack_expect_list;
loff_t i;
/* strange seq_file api calls stop even if we fail,
* thus we need to grab lock since stop unlocks */
READ_LOCK(&ip_conntrack_lock);
READ_LOCK(&ip_conntrack_expect_tuple_lock);
list_for_each(e, &ip_conntrack_expect_list) {
unsigned int last_len;
struct ip_conntrack_expect *expect
= (struct ip_conntrack_expect *)e;
if (upto++ < offset) continue;
last_len = len;
len += print_expect(buffer + len, expect);
if (len > length) {
len = last_len;
goto finished_expects;
}
if (list_empty(e))
return NULL;
for (i = 0; i <= *pos; i++) {
e = e->next;
if (e == &ip_conntrack_expect_list)
return NULL;
}
return e;
}
static void *exp_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
struct list_head *e = v;
finished_expects:
e = e->next;
if (e == &ip_conntrack_expect_list)
return NULL;
return e;
}
static void exp_seq_stop(struct seq_file *s, void *v)
{
READ_UNLOCK(&ip_conntrack_expect_tuple_lock);
finished:
READ_UNLOCK(&ip_conntrack_lock);
}
/* `start' hack - see fs/proc/generic.c line ~165 */
*start = (char *)((unsigned int)upto - offset);
return len;
static int exp_seq_show(struct seq_file *s, void *v)
{
struct ip_conntrack_expect *expect = v;
char buffer[IP_CT_PRINT_BUFLEN];
if (expect->expectant->helper->timeout)
seq_printf(s, "%lu ", timer_pending(&expect->timeout)
? (expect->timeout.expires - jiffies)/HZ : 0);
else
seq_printf(s, "- ");
seq_printf(s, "use=%u proto=%u ", atomic_read(&expect->use),
expect->tuple.dst.protonum);
print_tuple(buffer, &expect->tuple,
__ip_ct_find_proto(expect->tuple.dst.protonum));
return seq_printf(s, "%s\n", buffer);
}
static struct seq_operations exp_seq_ops = {
.start = exp_seq_start,
.next = exp_seq_next,
.stop = exp_seq_stop,
.show = exp_seq_show
};
static int exp_open(struct inode *inode, struct file *file)
{
return seq_open(file, &exp_seq_ops);
}
static struct file_operations exp_file_ops = {
.owner = THIS_MODULE,
.open = exp_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
static unsigned int ip_confirm(unsigned int hooknum,
struct sk_buff **pskb,
......@@ -509,7 +592,7 @@ static ctl_table ip_ct_net_table[] = {
#endif
static int init_or_cleanup(int init)
{
struct proc_dir_entry *proc;
struct proc_dir_entry *proc, *proc_exp;
int ret = 0;
if (!init) goto cleanup;
......@@ -518,14 +601,18 @@ static int init_or_cleanup(int init)
if (ret < 0)
goto cleanup_nothing;
proc = proc_net_create("ip_conntrack", 0440, list_conntracks);
proc = proc_net_create("ip_conntrack", 0440, NULL);
if (!proc) goto cleanup_init;
proc->owner = THIS_MODULE;
proc->proc_fops = &ct_file_ops;
proc_exp = proc_net_create("ip_conntrack_expect", 0440, NULL);
if (!proc_exp) goto cleanup_proc;
proc_exp->proc_fops = &exp_file_ops;
ret = nf_register_hook(&ip_conntrack_defrag_ops);
if (ret < 0) {
printk("ip_conntrack: can't register pre-routing defrag hook.\n");
goto cleanup_proc;
goto cleanup_proc_exp;
}
ret = nf_register_hook(&ip_conntrack_defrag_local_out_ops);
if (ret < 0) {
......@@ -577,6 +664,8 @@ static int init_or_cleanup(int init)
nf_unregister_hook(&ip_conntrack_defrag_local_out_ops);
cleanup_defragops:
nf_unregister_hook(&ip_conntrack_defrag_ops);
cleanup_proc_exp:
proc_net_remove("ip_conntrack_exp");
cleanup_proc:
proc_net_remove("ip_conntrack");
cleanup_init:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment