summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@coreworks.de>2005-03-04 01:17:17 +0100
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-03-04 01:17:17 +0100
commit788efd05e00cd5003328a7884a6dc3c6ec62bbce (patch)
treea4f2da788a6848ed63e4bbeef05eccd46d8a43df
parent7d2239ad476628440eeda5ae1b25e89c7fbbf5ea (diff)
[NETFILTER]: Fix /proc/net/ip_conntrack seq_file operations
ip_conntrack dumps an entire hash chain at a time. If dumping the first hash chain exceeds the available room nothing has been copied and seq_read() stops and returns the error. Change it to dump just a single entry at a time. Signed-off-by: Patrick McHardy <kaber@trash.net>
-rw-r--r--net/ipv4/netfilter/ip_conntrack_standalone.c116
1 files changed, 77 insertions, 39 deletions
diff --git a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c
index 3bbb47fc4eb9..a6ce4764fbf6 100644
--- a/net/ipv4/netfilter/ip_conntrack_standalone.c
+++ b/net/ipv4/netfilter/ip_conntrack_standalone.c
@@ -77,34 +77,70 @@ seq_print_counters(struct seq_file *s,
#define seq_print_counters(x, y) 0
#endif
-static void *ct_seq_start(struct seq_file *s, loff_t *pos)
+struct ct_iter_state {
+ unsigned int bucket;
+};
+
+static struct list_head *ct_get_first(struct seq_file *seq)
{
- if (*pos >= ip_conntrack_htable_size)
- return NULL;
- return &ip_conntrack_hash[*pos];
+ struct ct_iter_state *st = seq->private;
+
+ for (st->bucket = 0;
+ st->bucket < ip_conntrack_htable_size;
+ st->bucket++) {
+ if (!list_empty(&ip_conntrack_hash[st->bucket]))
+ return ip_conntrack_hash[st->bucket].next;
+ }
+ return NULL;
}
-
-static void ct_seq_stop(struct seq_file *s, void *v)
+
+static struct list_head *ct_get_next(struct seq_file *seq, struct list_head *head)
+{
+ struct ct_iter_state *st = seq->private;
+
+ head = head->next;
+ while (head == &ip_conntrack_hash[st->bucket]) {
+ if (++st->bucket >= ip_conntrack_htable_size)
+ return NULL;
+ head = ip_conntrack_hash[st->bucket].next;
+ }
+ return head;
+}
+
+static struct list_head *ct_get_idx(struct seq_file *seq, loff_t pos)
{
+ struct list_head *head = ct_get_first(seq);
+
+ if (head)
+ while (pos && (head = ct_get_next(seq, head)))
+ pos--;
+ return pos ? NULL : head;
+}
+
+static void *ct_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ READ_LOCK(&ip_conntrack_lock);
+ return ct_get_idx(seq, *pos);
}
static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
(*pos)++;
- if (*pos >= ip_conntrack_htable_size)
- return NULL;
- return &ip_conntrack_hash[*pos];
+ return ct_get_next(s, v);
}
-/* 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)
+static void ct_seq_stop(struct seq_file *s, void *v)
+{
+ READ_UNLOCK(&ip_conntrack_lock);
+}
+
+static int ct_seq_show(struct seq_file *s, void *v)
{
+ const struct ip_conntrack_tuple_hash *hash = v;
const struct ip_conntrack *conntrack = tuplehash_to_ctrack(hash);
struct ip_conntrack_protocol *proto;
MUST_BE_READ_LOCKED(&ip_conntrack_lock);
-
IP_NF_ASSERT(conntrack);
/* we only want to print DIR_ORIGINAL */
@@ -121,58 +157,44 @@ static int ct_seq_real_show(const struct ip_conntrack_tuple_hash *hash,
timer_pending(&conntrack->timeout)
? (long)(conntrack->timeout.expires - jiffies)/HZ
: 0) != 0)
- return 1;
+ return -ENOSPC;
if (proto->print_conntrack(s, conntrack))
- return 1;
+ return -ENOSPC;
if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
proto))
- return 1;
+ return -ENOSPC;
if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_ORIGINAL]))
- return 1;
+ return -ENOSPC;
if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)))
if (seq_printf(s, "[UNREPLIED] "))
- return 1;
+ return -ENOSPC;
if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
proto))
- return 1;
+ return -ENOSPC;
if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_REPLY]))
- return 1;
+ return -ENOSPC;
if (test_bit(IPS_ASSURED_BIT, &conntrack->status))
if (seq_printf(s, "[ASSURED] "))
- return 1;
+ return -ENOSPC;
#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
if (seq_printf(s, "mark=%lu ", conntrack->mark))
- return 1;
+ return -ENOSPC;
#endif
if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use)))
- return 1;
+ return -ENOSPC;
return 0;
}
-static int ct_seq_show(struct seq_file *s, void *v)
-{
- struct list_head *list = v;
- int ret = 0;
-
- /* FIXME: Simply truncates if hash chain too long. */
- READ_LOCK(&ip_conntrack_lock);
- if (LIST_FIND(list, ct_seq_real_show,
- struct ip_conntrack_tuple_hash *, s))
- ret = -ENOSPC;
- READ_UNLOCK(&ip_conntrack_lock);
- return ret;
-}
-
static struct seq_operations ct_seq_ops = {
.start = ct_seq_start,
.next = ct_seq_next,
@@ -182,7 +204,23 @@ static struct seq_operations ct_seq_ops = {
static int ct_open(struct inode *inode, struct file *file)
{
- return seq_open(file, &ct_seq_ops);
+ struct seq_file *seq;
+ struct ct_iter_state *st;
+ int ret;
+
+ st = kmalloc(sizeof(struct ct_iter_state), GFP_KERNEL);
+ if (st == NULL)
+ return -ENOMEM;
+ ret = seq_open(file, &ct_seq_ops);
+ if (ret)
+ goto out_free;
+ seq = file->private_data;
+ seq->private = st;
+ memset(st, 0, sizeof(struct ct_iter_state));
+ return ret;
+out_free:
+ kfree(st);
+ return ret;
}
static struct file_operations ct_file_ops = {
@@ -190,7 +228,7 @@ static struct file_operations ct_file_ops = {
.open = ct_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release
+ .release = seq_release_private,
};
/* expects */