[PATCH 2.6] NETFILTER (7/x): move /proc/net/ip_conntrack to seq_file

Harald Welte laforge@netfilter.org
Sat, 24 Jul 2004 10:41:59 -0400


--qXPE+6hUsFlDAc0e
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

Hi Dave!

This patch makes ip_conntrack use the seq_file API

Signed-off-by: Harald Welte <laforge@netfilter.org>

diff -Nru --exclude-from /space/home/laforge/scripts/dontdiff --exclude .de=
pend --exclude '*.o' --exclude '*.ko' --exclude '*.ver' --exclude '.*.flags=
' --exclude '*.orig' --exclude '*.rej' --exclude '*.cmd' --exclude '*.mod.c=
' --exclude '*~' linux-2.6.8-rc2-nfpending/include/linux/netfilter_ipv4/ip_=
conntrack_protocol.h linux-2.6.8-rc2-nfpending-seqfile/include/linux/netfil=
ter_ipv4/ip_conntrack_protocol.h
--- linux-2.6.8-rc2-nfpending/include/linux/netfilter_ipv4/ip_conntrack_pro=
tocol.h	2004-07-22 14:05:49.732256392 -0400
+++ linux-2.6.8-rc2-nfpending-seqfile/include/linux/netfilter_ipv4/ip_connt=
rack_protocol.h	2004-07-22 14:08:47.483234136 -0400
@@ -3,6 +3,11 @@
 #define _IP_CONNTRACK_PROTOCOL_H
 #include <linux/netfilter_ipv4/ip_conntrack.h>
=20
+/* length of buffer to which print_tuple/print_conntrack members are
+ * writing */
+
+#define IP_CT_PRINT_BUFLEN 100
+
 struct ip_conntrack_protocol
 {
 	/* Next pointer. */
diff -Nru --exclude-from /space/home/laforge/scripts/dontdiff --exclude .de=
pend --exclude '*.o' --exclude '*.ko' --exclude '*.ver' --exclude '.*.flags=
' --exclude '*.orig' --exclude '*.rej' --exclude '*.cmd' --exclude '*.mod.c=
' --exclude '*~' linux-2.6.8-rc2-nfpending/net/ipv4/netfilter/ip_conntrack_=
standalone.c linux-2.6.8-rc2-nfpending-seqfile/net/ipv4/netfilter/ip_conntr=
ack_standalone.c
--- linux-2.6.8-rc2-nfpending/net/ipv4/netfilter/ip_conntrack_standalone.c	=
2004-07-22 14:06:34.138505616 -0400
+++ linux-2.6.8-rc2-nfpending-seqfile/net/ipv4/netfilter/ip_conntrack_stand=
alone.c	2004-07-22 14:30:44.548009856 -0400
@@ -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 @@
 	return len;
 }
=20
-/* 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 =3D sprintf(buffer, "EXPECTING: %lu ",
-			      timer_pending(&expect->timeout)
-			      ? (expect->timeout.expires - jiffies)/HZ : 0);
-	else
-		len =3D sprintf(buffer, "EXPECTING: - ");
-	len +=3D sprintf(buffer + len, "use=3D%u proto=3D%u ",
-		      atomic_read(&expect->use), expect->tuple.dst.protonum);
-	len +=3D print_tuple(buffer + len, &expect->tuple,
-			   __ip_ct_find_proto(expect->tuple.dst.protonum));
-	len +=3D 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 *counte=
r)
 {
-	return sprintf(buffer, "packets=3D%llu bytes=3D%llu ",=20
-			counter->packets, counter->bytes);
+	return seq_printf(s, "packets=3D%llu bytes=3D%llu ",
+			  counter->packets, counter->bytes);
 }
 #else
 #define seq_print_counters(x, y)	0
 #endif
=20
-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
-		=3D __ip_ct_find_proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
-			       .tuple.dst.protonum);
+	unsigned int *bucket;
=20
-	len =3D 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 +=3D proto->print_conntrack(buffer + len, conntrack);
-	len +=3D print_tuple(buffer + len,
-			   &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
-			   proto);
-	len +=3D print_counters(buffer + len,=20
-			      &conntrack->counters[IP_CT_DIR_ORIGINAL]);
-	if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)))
-		len +=3D sprintf(buffer + len, "[UNREPLIED] ");
-	len +=3D print_tuple(buffer + len,
-			   &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
-			   proto);
-	len +=3D print_counters(buffer + len,=20
-			      &conntrack->counters[IP_CT_DIR_REPLY]);
-	if (test_bit(IPS_ASSURED_BIT, &conntrack->status))
-		len +=3D sprintf(buffer + len, "[ASSURED] ");
-	len +=3D sprintf(buffer + len, "use=3D%u ",
-		       atomic_read(&conntrack->ct_general.use));
-	len +=3D 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);
+ =20
+	if (*pos >=3D ip_conntrack_htable_size)
+		return NULL;
=20
-	return len;
+	bucket =3D kmalloc(sizeof(unsigned int), GFP_KERNEL);
+	if (!bucket) {
+		return ERR_PTR(-ENOMEM);
+	}
+ =20
+	*bucket =3D *pos;
+	return bucket;
 }
+ =20
+static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+	unsigned int *bucket =3D (unsigned int *) v;
=20
-/* 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 =3D ++(*bucket);
+	if (*pos >=3D ip_conntrack_htable_size) {
+		kfree(v);
+		return NULL;
+	}
+	return bucket;
+}
+ =20
+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 =3D hash->ctrack;
+	struct ip_conntrack_protocol *proto;
+	char buffer[IP_CT_PRINT_BUFLEN];
=20
 	MUST_BE_READ_LOCKED(&ip_conntrack_lock);
=20
-	/* Only count originals */
+	IP_NF_ASSERT(conntrack);
+
+	/* we only want to print DIR_ORIGINAL */
 	if (DIRECTION(hash))
 		return 0;
=20
-	if ((*upto)++ < offset)
-		return 0;
+	proto =3D __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) !=3D 0)
+		return 1;
+
+	proto->print_conntrack(buffer, conntrack);
+	if (seq_puts(s, buffer))
+		return 1;
+ =20
+	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;
=20
-	newlen =3D 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=3D%u\n", atomic_read(&conntrack->ct_general.use)))
 		return 1;
-	else *len +=3D newlen;
=20
 	return 0;
 }
=20
-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 =3D 0;
-	off_t upto =3D 0;
-	struct list_head *e;
+	unsigned int *bucket =3D (unsigned int *) v;
=20
-	READ_LOCK(&ip_conntrack_lock);
-	/* Traverse hash; print originals then reply. */
-	for (i =3D 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;
+}
+=09
+static struct seq_operations ct_seq_ops =3D {
+	.start =3D ct_seq_start,
+	.next  =3D ct_seq_next,
+	.stop  =3D ct_seq_stop,
+	.show  =3D ct_seq_show
+};
+ =20
+static int ct_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &ct_seq_ops);
+}
=20
-	/* Now iterate through expecteds. */
+static struct file_operations ct_file_ops =3D {
+	.owner   =3D THIS_MODULE,
+	.open    =3D ct_open,
+	.read    =3D seq_read,
+	.llseek  =3D seq_lseek,
+	.release =3D seq_release
+};
+ =20
+/* expects */
+static void *exp_seq_start(struct seq_file *s, loff_t *pos)
+{
+	struct list_head *e =3D &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
-			=3D (struct ip_conntrack_expect *)e;
-		if (upto++ < offset) continue;
-
-		last_len =3D len;
-		len +=3D print_expect(buffer + len, expect);
-		if (len > length) {
-			len =3D last_len;
-			goto finished_expects;
-		}
+
+	if (list_empty(e))
+		return NULL;
+
+	for (i =3D 0; i <=3D *pos; i++) {
+		e =3D e->next;
+		if (e =3D=3D &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 =3D v;
=20
- finished_expects:
+	e =3D e->next;
+
+	if (e =3D=3D &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);
+}
=20
-	/* `start' hack - see fs/proc/generic.c line ~165 */
-	*start =3D (char *)((unsigned int)upto - offset);
-	return len;
+static int exp_seq_show(struct seq_file *s, void *v)
+{
+	struct ip_conntrack_expect *expect =3D 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=3D%u proto=3D%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 =3D {
+	.start =3D exp_seq_start,
+	.next =3D exp_seq_next,
+	.stop =3D exp_seq_stop,
+	.show =3D exp_seq_show
+};
+
+static int exp_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &exp_seq_ops);
 }
+ =20
+static struct file_operations exp_file_ops =3D {
+	.owner   =3D THIS_MODULE,
+	.open    =3D exp_open,
+	.read    =3D seq_read,
+	.llseek  =3D seq_lseek,
+	.release =3D seq_release
+};
=20
 static unsigned int ip_confirm(unsigned int hooknum,
 			       struct sk_buff **pskb,
@@ -525,7 +608,7 @@
 #endif
 static int init_or_cleanup(int init)
 {
-	struct proc_dir_entry *proc;
+	struct proc_dir_entry *proc, *proc_exp;
 	int ret =3D 0;
=20
 	if (!init) goto cleanup;
@@ -534,14 +617,18 @@
 	if (ret < 0)
 		goto cleanup_nothing;
=20
-	proc =3D proc_net_create("ip_conntrack", 0440, list_conntracks);
+	proc =3D proc_net_create("ip_conntrack", 0440, NULL);
 	if (!proc) goto cleanup_init;
-	proc->owner =3D THIS_MODULE;
+	proc->proc_fops =3D &ct_file_ops;
+
+	proc_exp =3D proc_net_create("ip_conntrack_expect", 0440, NULL);
+	if (!proc_exp) goto cleanup_proc;
+	proc_exp->proc_fops =3D &exp_file_ops;
=20
 	ret =3D 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 =3D nf_register_hook(&ip_conntrack_defrag_local_out_ops);
 	if (ret < 0) {
@@ -593,6 +680,8 @@
 	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:
--=20
- Harald Welte <laforge@netfilter.org>             http://www.netfilter.org/
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D
  "Fragmentation is like classful addressing -- an interesting early
   architectural error that shows how much experimentation was going
   on while IP was being designed."                    -- Paul Vixie

--qXPE+6hUsFlDAc0e
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: Digital signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (GNU/Linux)

iD8DBQFBAnU3XaXGVTD0i/8RAtQKAKCwe4rf0s6eHkYEZ72C31C/qtPGJwCdEMiz
4GDeGlP/SY37TXVKI2I/eHE=
=9wNR
-----END PGP SIGNATURE-----

--qXPE+6hUsFlDAc0e--