From fa61987c5519fd4ad7032ed7ff7e467222b16fb7 Mon Sep 17 00:00:00 2001
From: Masahide NAKAMURA <nakam@linux-ipv6.org>
Date: Mon, 5 Feb 2007 13:28:05 +0900
Subject: [PATCH] [XFRM] IPV6: Show bundle cache information.

Outbound transformation makes bundle cache as stackable
destination and binds it to policy. This interface shows
it for developers.

TODO:
- IPv4 information has not been written completely yet then should be done.
- Currently both IPv4 and IPv6 parts is written at xfrm_proc.c
but it should be protocol-independent and splitted it to ipv4 or ipv6
directory respectively.

Signed-off-by: Masahide NAKAMURA <nakam@linux-ipv6.org>
---
 net/xfrm/xfrm_proc.c |  279 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 279 insertions(+), 0 deletions(-)

diff --git a/net/xfrm/xfrm_proc.c b/net/xfrm/xfrm_proc.c
index 7b55334..63a0b75 100644
--- a/net/xfrm/xfrm_proc.c
+++ b/net/xfrm/xfrm_proc.c
@@ -6,12 +6,281 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  */
+#include <stdarg.h>
+#include <linux/kernel.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
+#include <net/xfrm.h>
+#include <linux/in.h>
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#include <linux/in6.h>
+#endif
+
+#define XFRM_SEQ_NUM_BUF 32
+#define XFRM_PROC_DEBUG
+
+#ifdef XFRM_PROC_DEBUG
+#include <net/neighbour.h>
+#include <linux/if_arp.h>
+#endif
 
 #ifdef CONFIG_PROC_FS
 
+static char* xfrm_seq_sprintf(char *buf, const char *fmt, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, fmt);
+	i = vsnprintf(buf, INT_MAX, fmt, args);
+	va_end(args);
+	return buf;
+}
+
+#ifdef XFRM_PROC_DEBUG
+static int __xfrm_bundle_neigh_seq_print(struct seq_file *seq,
+					 struct neighbour *n)
+{
+	if (n->tbl) {
+		switch (n->tbl->family) {
+		case AF_INET:
+		{
+			struct in_addr *addr;
+			if (n->tbl->key_len >= sizeof(*addr)) {
+				addr = (struct in_addr *)n->primary_key;
+				seq_printf(seq, NIPQUAD_FMT, NIPQUAD(*addr));
+			}
+			break;
+		}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case AF_INET6:
+		{
+			struct in6_addr *addr;
+			if (n->tbl->key_len >= sizeof(*addr)) {
+				addr = (struct in6_addr *)n->primary_key;
+				seq_printf(seq, NIP6_FMT, NIP6(*addr));
+			}
+			break;
+		}
+#endif
+		default:
+			break;
+		}
+	}
+
+	if (n->dev) {
+		int i;
+
+		seq_printf(seq, " dev %s", n->dev->name);
+
+		switch (n->type) {
+		case ARPHRD_ETHER:
+			seq_printf(seq, " lladdr ");
+			for (i = 0; i < n->dev->addr_len; i++) {
+				if (i > 0)
+					seq_printf(seq, ":");
+				seq_printf(seq, "%02x", n->ha[i]);
+			}
+			break;
+		/* XXX: other types should be written */
+		default:
+			seq_printf(seq, " %u", n->type);
+			break;
+		}
+	}
+
+	return 0;
+}
+#endif
+
+static int __xfrm_bundle_state_seq_print(struct seq_file *seq,
+					 struct xfrm_state *x)
+{
+	char buf[XFRM_SEQ_NUM_BUF];
+
+	seq_printf(seq, "%-3u %s", x->id.proto,
+		   ((x->props.mode == XFRM_MODE_TRANSPORT) ? "transport" :
+		    (x->props.mode == XFRM_MODE_TUNNEL) ? "tunnel" :
+		    (x->props.mode == XFRM_MODE_ROUTEOPTIMIZATION) ? "ro" :
+		    (x->props.mode == XFRM_MODE_IN_TRIGGER) ? "in_trigger" :
+		    (x->props.mode == XFRM_MODE_BEET) ? "beet" :
+		    xfrm_seq_sprintf(buf, "%u", x->props.mode)));
+
+	switch (x->props.family) {
+	case AF_INET:
+		seq_printf(seq, " " NIPQUAD_FMT " " NIPQUAD_FMT,
+			   NIPQUAD(*(struct in_addr *)&x->props.saddr),
+			   NIPQUAD(*(struct in_addr *)&x->id.daddr));
+		if (x->coaddr)
+			seq_printf(seq, " coa " NIPQUAD_FMT,
+				   NIPQUAD(*(struct in_addr *)x->coaddr));
+		break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	case AF_INET6:
+		seq_printf(seq, " " NIP6_FMT " " NIP6_FMT,
+			   NIP6(*(struct in6_addr *)&x->props.saddr),
+			   NIP6(*(struct in6_addr *)&x->id.daddr));
+		if (x->coaddr)
+			seq_printf(seq, " coa " NIP6_FMT,
+				   NIP6(*(struct in6_addr *)x->coaddr));
+		break;
+#endif
+	default:
+		break;
+	}
+
+	if (xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY))
+		seq_printf(seq, " spi 0x%08x", ntohl(x->id.spi));
+
+	seq_printf(seq, " ref %u", atomic_read(&x->refcnt));
+
+	return 0;
+}
+
+static int __xfrm_bundle_xdst_seq_print(struct seq_file *seq,
+					struct xfrm_dst *xdst)
+{
+	if (!xdst->u.dst.ops)
+		return 0;
+
+	switch (xdst->u.dst.ops->family) {
+	case AF_INET:
+		seq_printf(seq, " rt");
+		/* XXX: should be written */
+		break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	case AF_INET6:
+	{
+		struct rt6_info *rt6i = &xdst->u.rt6;
+
+		seq_printf(seq, " rt6");
+
+		seq_printf(seq, " " NIP6_FMT "/%d", NIP6(rt6i->rt6i_dst.addr),
+			   rt6i->rt6i_dst.plen);
+		if (rt6i->rt6i_src.plen) {
+			seq_printf(seq, " src " NIP6_FMT "/%d",
+				   NIP6(rt6i->rt6i_src.addr),
+				   rt6i->rt6i_src.plen);
+		}
+		seq_printf(seq, " via " NIP6_FMT,
+			   NIP6(rt6i->rt6i_gateway));
+		seq_printf(seq, " dev %s %s",
+			   ((rt6i->rt6i_idev && rt6i->rt6i_idev->dev) ?
+			    rt6i->rt6i_idev->dev->name : "null"),
+			   ((rt6i->rt6i_flags&RTF_CACHE)?"cache":""));
+		seq_printf(seq, " sernum %u",
+			   (rt6i->rt6i_node ? rt6i->rt6i_node->fn_sernum : 0));
+		seq_printf(seq, " ref %u", atomic_read(&rt6i->rt6i_ref));
+		break;
+	}
+#endif
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int __xfrm_bundle_seq_show_dst(struct seq_file *seq,
+				      struct xfrm_dst *xdst, int gen)
+{
+	seq_printf(seq, "  gen %d", gen);
+	seq_printf(seq, " ref %u", atomic_read(&xdst->u.dst.__refcnt));
+	if (!gen) {
+		seq_printf(seq, " rtcook %u", xdst->route_cookie);
+		seq_printf(seq, " ptcook %u", xdst->path_cookie);
+	}
+	seq_printf(seq, "\n");
+
+	if (xdst->u.dst.ops) {
+		seq_printf(seq, "    dst ");
+		__xfrm_bundle_xdst_seq_print(seq, xdst);
+		seq_printf(seq, "\n");
+
+#ifdef XFRM_PROC_DEBUG
+		if (xdst->u.dst.neighbour) {
+			struct neighbour *n = xdst->u.dst.neighbour;
+			seq_printf(seq, "    neigh ");
+			read_lock(&n->lock);
+			__xfrm_bundle_neigh_seq_print(seq, n);
+			read_unlock(&n->lock);
+			seq_printf(seq, "\n");
+		}
+#endif
+	}
+	if (xdst->u.dst.xfrm) {
+		struct xfrm_state *x = xdst->u.dst.xfrm;
+		seq_printf(seq, "    xfrm ");
+		spin_lock(&x->lock);
+		__xfrm_bundle_state_seq_print(seq, x);
+		spin_unlock(&x->lock);
+		seq_printf(seq, "\n");
+	}
+	return 0;
+}
+
+static int xfrm_bundle_seq_show_one(struct xfrm_policy *xp, int dir, int count,
+				    void *data)
+{
+	struct seq_file *seq = (struct seq_file *)data;
+	char buf[XFRM_SEQ_NUM_BUF];
+	struct dst_entry *dst;
+
+	read_lock_bh(&xp->lock);
+
+	if (!xp->bundles)
+		goto out;
+
+	seq_printf(seq, "%-4s %011u %-3s\n",
+		   ((xp->type == XFRM_POLICY_TYPE_MAIN) ? "main" :
+		    (xp->type == XFRM_POLICY_TYPE_SUB) ? "sub" :
+		    xfrm_seq_sprintf(buf, "%u", xp->type)),
+		   xp->index,
+		   ((dir == XFRM_POLICY_IN) ? "in" :
+		    (dir == XFRM_POLICY_OUT) ? "out" :
+		    (dir == XFRM_POLICY_FWD) ? "fwd" :
+		    xfrm_seq_sprintf(buf, "%u", dir)));
+
+	for (dst = xp->bundles; dst; dst = dst->next) {
+		struct dst_entry *d = dst;
+		int gen = 0;
+
+		for (d = dst; d; d = d->child) {
+			struct xfrm_dst *xdst = (struct xfrm_dst*)d;
+			__xfrm_bundle_seq_show_dst(seq, xdst, gen++);
+		}
+		seq_printf(seq, "\n"); /* for delimiter */
+	}
+
+ out:
+	read_unlock_bh(&xp->lock);
+	return 0;
+}
+
+static int xfrm_bundle_seq_show(struct seq_file *seq, void *v)
+{
+	xfrm_policy_walk(XFRM_POLICY_TYPE_MAIN, xfrm_bundle_seq_show_one, seq);
+#ifdef CONFIG_XFRM_SUB_POLICY
+	xfrm_policy_walk(XFRM_POLICY_TYPE_SUB,  xfrm_bundle_seq_show_one, seq);
+#endif
+	return 0;
+}
+
+static int xfrm_bundle_seq_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, xfrm_bundle_seq_show, NULL);
+}
+
+static struct file_operations proc_net_xfrm_bundle_seq_fops = {
+	.owner	 = THIS_MODULE,
+	.open	 = xfrm_bundle_seq_open,
+	.read	 = seq_read,
+	.llseek	 = seq_lseek,
+	.release = single_release,
+};
+
 static struct proc_dir_entry *proc_net_xfrm;
+static struct proc_dir_entry *proc_net_xfrm_bundle;
 
 int __init xfrm_proc_init(void)
 {
@@ -21,9 +290,18 @@ int __init xfrm_proc_init(void)
 	if (!proc_net_xfrm)
 		goto proc_net_xfrm_fail;
 
+	proc_net_xfrm_bundle = create_proc_entry("bundle", S_IRUGO,
+						 proc_net_xfrm);
+	if (!proc_net_xfrm_bundle)
+		goto proc_net_xfrm_bundle_fail;
+
+	proc_net_xfrm_bundle->proc_fops = &proc_net_xfrm_bundle_seq_fops;
+
  out:
 	return rc;
 
+ proc_net_xfrm_bundle_fail:
+	remove_proc_entry("xfrm", proc_net);
  proc_net_xfrm_fail:
 	rc = -ENOMEM;
 	goto out;
@@ -32,6 +310,7 @@ int __init xfrm_proc_init(void)
 #if 0
 void xfrm_proc_exit(void)
 {
+	remove_proc_entry("bundle", proc_net_xfrm);
 	remove_proc_entry("xfrm", proc_net);
 }
 #endif
-- 
1.5.0.3

