Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cb7b593c authored by Stephen Hemminger's avatar Stephen Hemminger Committed by David S. Miller
Browse files

[IPV4] fib_trie: fix proc interface

Create one iterator for walking over FIB trie, and use it
for all the /proc functions. Add a /proc/net/route
output for backwards compatibility with old applications.

Make initialization of fib_trie same as fib_hash so no #ifdef
is needed in af_inet.c

Fixes: http://bugzilla.kernel.org/show_bug.cgi?id=5209



Signed-off-by: default avatarStephen Hemminger <shemminger@osdl.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8259f162
Loading
Loading
Loading
Loading
+0 −13
Original line number Original line Diff line number Diff line
@@ -1248,11 +1248,6 @@ module_init(inet_init);
/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */


#ifdef CONFIG_PROC_FS
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_IP_FIB_TRIE
extern int  fib_stat_proc_init(void);
extern void fib_stat_proc_exit(void);
#endif

static int __init ipv4_proc_init(void)
static int __init ipv4_proc_init(void)
{
{
	int rc = 0;
	int rc = 0;
@@ -1265,19 +1260,11 @@ static int __init ipv4_proc_init(void)
		goto out_udp;
		goto out_udp;
	if (fib_proc_init())
	if (fib_proc_init())
		goto out_fib;
		goto out_fib;
#ifdef CONFIG_IP_FIB_TRIE
         if (fib_stat_proc_init())
                 goto out_fib_stat;
#endif
	if (ip_misc_proc_init())
	if (ip_misc_proc_init())
		goto out_misc;
		goto out_misc;
out:
out:
	return rc;
	return rc;
out_misc:
out_misc:
#ifdef CONFIG_IP_FIB_TRIE
 	fib_stat_proc_exit();
out_fib_stat:
#endif
	fib_proc_exit();
	fib_proc_exit();
out_fib:
out_fib:
	udp4_proc_exit();
	udp4_proc_exit();
+385 −419
Original line number Original line Diff line number Diff line
@@ -43,7 +43,7 @@
 *		2 of the License, or (at your option) any later version.
 *		2 of the License, or (at your option) any later version.
 */
 */


#define VERSION "0.402"
#define VERSION "0.403"


#include <linux/config.h>
#include <linux/config.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
@@ -164,7 +164,6 @@ static struct node *resize(struct trie *t, struct tnode *tn);
static struct tnode *inflate(struct trie *t, struct tnode *tn);
static struct tnode *inflate(struct trie *t, struct tnode *tn);
static struct tnode *halve(struct trie *t, struct tnode *tn);
static struct tnode *halve(struct trie *t, struct tnode *tn);
static void tnode_free(struct tnode *tn);
static void tnode_free(struct tnode *tn);
static void trie_dump_seq(struct seq_file *seq, struct trie *t);


static kmem_cache_t *fn_alias_kmem __read_mostly;
static kmem_cache_t *fn_alias_kmem __read_mostly;
static struct trie *trie_local = NULL, *trie_main = NULL;
static struct trie *trie_local = NULL, *trie_main = NULL;
@@ -1971,378 +1970,137 @@ struct fib_table * __init fib_hash_init(int id)
	return tb;
	return tb;
}
}


/* Trie dump functions */
#ifdef CONFIG_PROC_FS

/* Depth first Trie walk iterator */
static void putspace_seq(struct seq_file *seq, int n)
struct fib_trie_iter {
{
	struct tnode *tnode;
	while (n--)
	struct trie *trie;
		seq_printf(seq, " ");
	unsigned index;
}
	unsigned depth;
};


static void printbin_seq(struct seq_file *seq, unsigned int v, int bits)
static struct node *fib_trie_get_next(struct fib_trie_iter *iter)
{
{
	while (bits--)
	struct tnode *tn = iter->tnode;
		seq_printf(seq, "%s", (v & (1<<bits))?"1":"0");
	unsigned cindex = iter->index;
}
	struct tnode *p;


static void printnode_seq(struct seq_file *seq, int indent, struct node *n,
	pr_debug("get_next iter={node=%p index=%d depth=%d}\n",
		   int pend, int cindex, int bits)
		 iter->tnode, iter->index, iter->depth);
{
rescan:
	putspace_seq(seq, indent);
	while (cindex < (1<<tn->bits)) {
	if (IS_LEAF(n))
		struct node *n = tnode_get_child(tn, cindex);
		seq_printf(seq, "|");
	else
		seq_printf(seq, "+");
	if (bits) {
		seq_printf(seq, "%d/", cindex);
		printbin_seq(seq, cindex, bits);
		seq_printf(seq, ": ");
	} else
		seq_printf(seq, "<root>: ");
	seq_printf(seq, "%s:%p ", IS_LEAF(n)?"Leaf":"Internal node", n);


		if (n) {
			if (IS_LEAF(n)) {
			if (IS_LEAF(n)) {
		struct leaf *l = (struct leaf *)n;
				iter->tnode = tn;
		struct fib_alias *fa;
				iter->index = cindex + 1;
		int i;

		seq_printf(seq, "key=%d.%d.%d.%d\n",
			   n->key >> 24, (n->key >> 16) % 256, (n->key >> 8) % 256, n->key % 256);

		for (i = 32; i >= 0; i--)
			if (find_leaf_info(&l->list, i)) {
				struct list_head *fa_head = get_fa_head(l, i);

				if (!fa_head)
					continue;

				if (list_empty(fa_head))
					continue;

				putspace_seq(seq, indent+2);
				seq_printf(seq, "{/%d...dumping}\n", i);

				list_for_each_entry_rcu(fa, fa_head, fa_list) {
					putspace_seq(seq, indent+2);
					if (fa->fa_info == NULL) {
						seq_printf(seq, "Error fa_info=NULL\n");
						continue;
					}
					if (fa->fa_info->fib_nh == NULL) {
						seq_printf(seq, "Error _fib_nh=NULL\n");
						continue;
					}

					seq_printf(seq, "{type=%d scope=%d TOS=%d}\n",
					      fa->fa_type,
					      fa->fa_scope,
					      fa->fa_tos);
				}
			}
			} else {
			} else {
		struct tnode *tn = (struct tnode *)n;
				/* push down one level */
		int plen = ((struct tnode *)n)->pos;
				iter->tnode = (struct tnode *) n;
		t_key prf = MASK_PFX(n->key, plen);
				iter->index = 0;

				++iter->depth;
		seq_printf(seq, "key=%d.%d.%d.%d/%d\n",
			   prf >> 24, (prf >> 16) % 256, (prf >> 8) % 256, prf % 256, plen);

		putspace_seq(seq, indent); seq_printf(seq, "|    ");
		seq_printf(seq, "{key prefix=%08x/", tn->key & TKEY_GET_MASK(0, tn->pos));
		printbin_seq(seq, tkey_extract_bits(tn->key, 0, tn->pos), tn->pos);
		seq_printf(seq, "}\n");
		putspace_seq(seq, indent); seq_printf(seq, "|    ");
		seq_printf(seq, "{pos=%d", tn->pos);
		seq_printf(seq, " (skip=%d bits)", tn->pos - pend);
		seq_printf(seq, " bits=%d (%u children)}\n", tn->bits, (1 << tn->bits));
		putspace_seq(seq, indent); seq_printf(seq, "|    ");
		seq_printf(seq, "{empty=%d full=%d}\n", tn->empty_children, tn->full_children);
			}
			}
			return n;
		}
		}


static void trie_dump_seq(struct seq_file *seq, struct trie *t)
		++cindex;
{
	struct node *n;
	int cindex = 0;
	int indent = 1;
	int pend = 0;
	int depth = 0;
	struct tnode *tn;

	rcu_read_lock();
	n = rcu_dereference(t->trie);
	seq_printf(seq, "------ trie_dump of t=%p ------\n", t);

	if (!n) {
		seq_printf(seq, "------ trie is empty\n");

		rcu_read_unlock();
		return;
	}

	printnode_seq(seq, indent, n, pend, cindex, 0);

	if (!IS_TNODE(n)) {
		rcu_read_unlock();
		return;
	}

	tn = (struct tnode *)n;
	pend = tn->pos+tn->bits;
	putspace_seq(seq, indent); seq_printf(seq, "\\--\n");
	indent += 3;
	depth++;

	while (tn && cindex < (1 << tn->bits)) {
		struct node *child = rcu_dereference(tn->child[cindex]);
		if (!child)
			cindex++;
		else {
			/* Got a child */
			printnode_seq(seq, indent, child, pend,
				      cindex, tn->bits);

			if (IS_LEAF(child))
				cindex++;

			else {
				/*
				 * New tnode. Decend one level
				 */

				depth++;
				n = child;
				tn = (struct tnode *)n;
				pend = tn->pos+tn->bits;
				putspace_seq(seq, indent);
				seq_printf(seq, "\\--\n");
				indent += 3;
				cindex = 0;
	}
	}
		}

		/*
		 * Test if we are done
		 */

		while (cindex >= (1 << tn->bits)) {
			/*
			 * Move upwards and test for root
			 * pop off all traversed  nodes
			 */


			if (NODE_PARENT(tn) == NULL) {
	/* Current node exhausted, pop back up */
				tn = NULL;
	p = NODE_PARENT(tn);
				break;
	if (p) {
		cindex = tkey_extract_bits(tn->key, p->pos, p->bits)+1;
		tn = p;
		--iter->depth;
		goto rescan;
	}
	}


			cindex = tkey_extract_bits(tn->key, NODE_PARENT(tn)->pos, NODE_PARENT(tn)->bits);
	/* got root? */
			cindex++;
	return NULL;
			tn = NODE_PARENT(tn);
			pend = tn->pos + tn->bits;
			indent -= 3;
			depth--;
		}
	}
	rcu_read_unlock();
}
}


static struct trie_stat *trie_stat_new(void)
static struct node *fib_trie_get_first(struct fib_trie_iter *iter,
				       struct trie *t)
{
{
	struct trie_stat *s;
	struct node *n = rcu_dereference(t->trie);
	int i;


	s = kmalloc(sizeof(struct trie_stat), GFP_KERNEL);
	if (n && IS_TNODE(n)) {
	if (!s)
		iter->tnode = (struct tnode *) n;
		iter->trie = t;
		iter->index = 0;
		iter->depth = 0;
		return n;
	}
	return NULL;
	return NULL;

	s->totdepth = 0;
	s->maxdepth = 0;
	s->tnodes = 0;
	s->leaves = 0;
	s->nullpointers = 0;

	for (i = 0; i < MAX_CHILDS; i++)
		s->nodesizes[i] = 0;

	return s;
}
}


static struct trie_stat *trie_collect_stats(struct trie *t)
static void trie_collect_stats(struct trie *t, struct trie_stat *s)
{
{
	struct node *n;
	struct node *n;
	struct trie_stat *s = trie_stat_new();
	struct fib_trie_iter iter;
	int cindex = 0;
	int pend = 0;
	int depth = 0;


	if (!s)
	memset(s, 0, sizeof(*s));
		return NULL;


	rcu_read_lock();
	rcu_read_lock();
	n = rcu_dereference(t->trie);
	for (n = fib_trie_get_first(&iter, t); n;

	     n = fib_trie_get_next(&iter)) {
	if (!n)
		if (IS_LEAF(n)) {
		return s;

	if (IS_TNODE(n)) {
		struct tnode *tn = (struct tnode *)n;
		pend = tn->pos+tn->bits;
		s->nodesizes[tn->bits]++;
		depth++;

		while (tn && cindex < (1 << tn->bits)) {
			struct node *ch = rcu_dereference(tn->child[cindex]);
			if (ch) {

				/* Got a child */

				if (IS_LEAF(tn->child[cindex])) {
					cindex++;

					/* stats */
					if (depth > s->maxdepth)
						s->maxdepth = depth;
					s->totdepth += depth;
			s->leaves++;
			s->leaves++;
			s->totdepth += iter.depth;
			if (iter.depth > s->maxdepth)
				s->maxdepth = iter.depth;
		} else {
		} else {
					/*
			const struct tnode *tn = (const struct tnode *) n;
					 * New tnode. Decend one level
			int i;
					 */


			s->tnodes++;
			s->tnodes++;
			s->nodesizes[tn->bits]++;
			s->nodesizes[tn->bits]++;
					depth++;
			for (i = 0; i < (1<<tn->bits); i++)

				if (!tn->child[i])
					n = ch;
					tn = (struct tnode *)n;
					pend = tn->pos+tn->bits;

					cindex = 0;
				}
			} else {
				cindex++;
					s->nullpointers++;
					s->nullpointers++;
		}
		}

			/*
			 * Test if we are done
			 */

			while (cindex >= (1 << tn->bits)) {
				/*
				 * Move upwards and test for root
				 * pop off all traversed  nodes
				 */

				if (NODE_PARENT(tn) == NULL) {
					tn = NULL;
					n = NULL;
					break;
				}

				cindex = tkey_extract_bits(tn->key, NODE_PARENT(tn)->pos, NODE_PARENT(tn)->bits);
				tn = NODE_PARENT(tn);
				cindex++;
				n = (struct node *)tn;
				pend = tn->pos+tn->bits;
				depth--;
 			}
		}
	}
	}

	rcu_read_unlock();
	rcu_read_unlock();
	return s;
}

#ifdef CONFIG_PROC_FS

static struct fib_alias *fib_triestat_get_first(struct seq_file *seq)
{
	return NULL;
}

static struct fib_alias *fib_triestat_get_next(struct seq_file *seq)
{
	return NULL;
}

static void *fib_triestat_seq_start(struct seq_file *seq, loff_t *pos)
{
	if (!ip_fib_main_table)
		return NULL;

	if (*pos)
		return fib_triestat_get_next(seq);
	else
		return SEQ_START_TOKEN;
}

static void *fib_triestat_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
	++*pos;
	if (v == SEQ_START_TOKEN)
		return fib_triestat_get_first(seq);
	else
		return fib_triestat_get_next(seq);
}

static void fib_triestat_seq_stop(struct seq_file *seq, void *v)
{

}
}


/*
/*
 *	This outputs /proc/net/fib_triestats
 *	This outputs /proc/net/fib_triestats
 *
 *	It always works in backward compatibility mode.
 *	The format of the file is not supposed to be changed.
 */
 */

static void trie_show_stats(struct seq_file *seq, struct trie_stat *stat)
static void collect_and_show(struct trie *t, struct seq_file *seq)
{
{
	int bytes = 0; /* How many bytes are used, a ref is 4 bytes */
	unsigned i, max, pointers, bytes, avdepth;
	int i, max, pointers;
	struct trie_stat *stat;
	int avdepth;

	stat = trie_collect_stats(t);


	bytes = 0;
	seq_printf(seq, "trie=%p\n", t);

	if (stat) {
	if (stat->leaves)
	if (stat->leaves)
		avdepth = stat->totdepth*100 / stat->leaves;
		avdepth = stat->totdepth*100 / stat->leaves;
	else
	else
		avdepth = 0;
		avdepth = 0;
		seq_printf(seq, "Aver depth: %d.%02d\n", avdepth / 100, avdepth % 100);
		seq_printf(seq, "Max depth: %4d\n", stat->maxdepth);


		seq_printf(seq, "Leaves: %d\n", stat->leaves);
	seq_printf(seq, "\tAver depth:     %d.%02d\n", avdepth / 100, avdepth % 100 );
		bytes += sizeof(struct leaf) * stat->leaves;
	seq_printf(seq, "\tMax depth:      %u\n", stat->maxdepth);
		seq_printf(seq, "Internal nodes: %d\n", stat->tnodes);

	seq_printf(seq, "\tLeaves:         %u\n", stat->leaves);

	bytes = sizeof(struct leaf) * stat->leaves;
	seq_printf(seq, "\tInternal nodes: %d\n\t", stat->tnodes);
	bytes += sizeof(struct tnode) * stat->tnodes;
	bytes += sizeof(struct tnode) * stat->tnodes;


	max = MAX_CHILDS-1;
	max = MAX_CHILDS-1;

	while (max >= 0 && stat->nodesizes[max] == 0)
	while (max >= 0 && stat->nodesizes[max] == 0)
		max--;
		max--;
		pointers = 0;


	pointers = 0;
	for (i = 1; i <= max; i++)
	for (i = 1; i <= max; i++)
		if (stat->nodesizes[i] != 0) {
		if (stat->nodesizes[i] != 0) {
			seq_printf(seq, "  %d: %d",  i, stat->nodesizes[i]);
			seq_printf(seq, "  %d: %d",  i, stat->nodesizes[i]);
			pointers += (1<<i) * stat->nodesizes[i];
			pointers += (1<<i) * stat->nodesizes[i];
		}
		}
		seq_printf(seq, "\n");
	seq_putc(seq, '\n');
		seq_printf(seq, "Pointers: %d\n", pointers);
	seq_printf(seq, "\tPointers: %d\n", pointers);

	bytes += sizeof(struct node *) * pointers;
	bytes += sizeof(struct node *) * pointers;
	seq_printf(seq, "Null ptrs: %d\n", stat->nullpointers);
	seq_printf(seq, "Null ptrs: %d\n", stat->nullpointers);
		seq_printf(seq, "Total size: %d  kB\n", bytes / 1024);
	seq_printf(seq, "Total size: %d  kB\n", (bytes + 1023) / 1024);

		kfree(stat);
	}


#ifdef CONFIG_IP_FIB_TRIE_STATS
#ifdef CONFIG_IP_FIB_TRIE_STATS
	seq_printf(seq, "Counters:\n---------\n");
	seq_printf(seq, "Counters:\n---------\n");
@@ -2360,123 +2118,190 @@ static void collect_and_show(struct trie *t, struct seq_file *seq)


static int fib_triestat_seq_show(struct seq_file *seq, void *v)
static int fib_triestat_seq_show(struct seq_file *seq, void *v)
{
{
	char bf[128];
	struct trie_stat *stat;

	stat = kmalloc(sizeof(*stat), GFP_KERNEL);
	if (!stat)
		return -ENOMEM;


	if (v == SEQ_START_TOKEN) {
	seq_printf(seq, "Basic info: size of leaf: %Zd bytes, size of tnode: %Zd bytes.\n",
	seq_printf(seq, "Basic info: size of leaf: %Zd bytes, size of tnode: %Zd bytes.\n",
		   sizeof(struct leaf), sizeof(struct tnode));
		   sizeof(struct leaf), sizeof(struct tnode));
		if (trie_local)
			collect_and_show(trie_local, seq);


		if (trie_main)
	if (trie_local) {
			collect_and_show(trie_main, seq);
		seq_printf(seq, "Local:\n");
	} else {
		trie_collect_stats(trie_local, stat);
		snprintf(bf, sizeof(bf), "*\t%08X\t%08X", 200, 400);
		trie_show_stats(seq, stat);
	}


		seq_printf(seq, "%-127s\n", bf);
	if (trie_main) {
		seq_printf(seq, "Main:\n");
		trie_collect_stats(trie_main, stat);
		trie_show_stats(seq, stat);
	}
	}
	kfree(stat);

	return 0;
	return 0;
}
}


static struct seq_operations fib_triestat_seq_ops = {
	.start = fib_triestat_seq_start,
	.next  = fib_triestat_seq_next,
	.stop  = fib_triestat_seq_stop,
	.show  = fib_triestat_seq_show,
};

static int fib_triestat_seq_open(struct inode *inode, struct file *file)
static int fib_triestat_seq_open(struct inode *inode, struct file *file)
{
{
	struct seq_file *seq;
	return single_open(file, fib_triestat_seq_show, NULL);
	int rc = -ENOMEM;

	rc = seq_open(file, &fib_triestat_seq_ops);
	if (rc)
		goto out_kfree;

	seq = file->private_data;
out:
	return rc;
out_kfree:
	goto out;
}
}


static struct file_operations fib_triestat_seq_fops = {
static struct file_operations fib_triestat_fops = {
	.owner	= THIS_MODULE,
	.owner	= THIS_MODULE,
	.open	= fib_triestat_seq_open,
	.open	= fib_triestat_seq_open,
	.read	= seq_read,
	.read	= seq_read,
	.llseek	= seq_lseek,
	.llseek	= seq_lseek,
	.release = seq_release_private,
	.release = single_release,
};
};


int __init fib_stat_proc_init(void)
static struct node *fib_trie_get_idx(struct fib_trie_iter *iter,
				      loff_t pos)
{
{
	if (!proc_net_fops_create("fib_triestat", S_IRUGO, &fib_triestat_seq_fops))
	loff_t idx = 0;
		return -ENOMEM;
	struct node *n;
	return 0;
}


void __init fib_stat_proc_exit(void)
	for (n = fib_trie_get_first(iter, trie_local);
{
	     n; ++idx, n = fib_trie_get_next(iter)) {
	proc_net_remove("fib_triestat");
		if (pos == idx)
			return n;
	}
	}


static struct fib_alias *fib_trie_get_first(struct seq_file *seq)
	for (n = fib_trie_get_first(iter, trie_main);
{
	     n; ++idx, n = fib_trie_get_next(iter)) {
	return NULL;
		if (pos == idx)
			return n;
	}
	}

static struct fib_alias *fib_trie_get_next(struct seq_file *seq)
{
	return NULL;
	return NULL;
}
}


static void *fib_trie_seq_start(struct seq_file *seq, loff_t *pos)
static void *fib_trie_seq_start(struct seq_file *seq, loff_t *pos)
{
{
	if (!ip_fib_main_table)
	rcu_read_lock();
		return NULL;
	if (*pos == 0)

	if (*pos)
		return fib_trie_get_next(seq);
	else
		return SEQ_START_TOKEN;
		return SEQ_START_TOKEN;
	return fib_trie_get_idx(seq->private, *pos - 1);
}
}


static void *fib_trie_seq_next(struct seq_file *seq, void *v, loff_t *pos)
static void *fib_trie_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
{
	struct fib_trie_iter *iter = seq->private;
	void *l = v;

	++*pos;
	++*pos;
	if (v == SEQ_START_TOKEN)
	if (v == SEQ_START_TOKEN)
		return fib_trie_get_first(seq);
		return fib_trie_get_idx(iter, 0);
	else

		return fib_trie_get_next(seq);
	v = fib_trie_get_next(iter);
	BUG_ON(v == l);
	if (v)
		return v;


	/* continue scan in next trie */
	if (iter->trie == trie_local)
		return fib_trie_get_first(iter, trie_main);

	return NULL;
}
}


static void fib_trie_seq_stop(struct seq_file *seq, void *v)
static void fib_trie_seq_stop(struct seq_file *seq, void *v)
{
{
	rcu_read_unlock();
}
}


/*
static void seq_indent(struct seq_file *seq, int n)
 *	This outputs /proc/net/fib_trie.
{
 *
	while (n-- > 0) seq_puts(seq, "   ");
 *	It always works in backward compatibility mode.
}
 *	The format of the file is not supposed to be changed.

 */
static inline const char *rtn_scope(enum rt_scope_t s)
{
	static char buf[32];

	switch(s) {
	case RT_SCOPE_UNIVERSE: return "universe";
	case RT_SCOPE_SITE:	return "site";
	case RT_SCOPE_LINK:	return "link";
	case RT_SCOPE_HOST:	return "host";
	case RT_SCOPE_NOWHERE:	return "nowhere";
	default:
		snprintf(buf, sizeof(buf), "scope=%d", s);
		return buf;
	}
}

static const char *rtn_type_names[__RTN_MAX] = {
	[RTN_UNSPEC] = "UNSPEC",
	[RTN_UNICAST] = "UNICAST",
	[RTN_LOCAL] = "LOCAL",
	[RTN_BROADCAST] = "BROADCAST",
	[RTN_ANYCAST] = "ANYCAST",
	[RTN_MULTICAST] = "MULTICAST",
	[RTN_BLACKHOLE] = "BLACKHOLE",
	[RTN_UNREACHABLE] = "UNREACHABLE",
	[RTN_PROHIBIT] = "PROHIBIT",
	[RTN_THROW] = "THROW",
	[RTN_NAT] = "NAT",
	[RTN_XRESOLVE] = "XRESOLVE",
};

static inline const char *rtn_type(unsigned t)
{
	static char buf[32];


	if (t < __RTN_MAX && rtn_type_names[t])
		return rtn_type_names[t];
	snprintf(buf, sizeof(buf), "type %d", t);
	return buf;
}

/* Pretty print the trie */
static int fib_trie_seq_show(struct seq_file *seq, void *v)
static int fib_trie_seq_show(struct seq_file *seq, void *v)
{
{
	char bf[128];
	const struct fib_trie_iter *iter = seq->private;
	struct node *n = v;


	if (v == SEQ_START_TOKEN) {
	if (v == SEQ_START_TOKEN)
		if (trie_local)
		return 0;
			trie_dump_seq(seq, trie_local);


		if (trie_main)
	if (IS_TNODE(n)) {
			trie_dump_seq(seq, trie_main);
		struct tnode *tn = (struct tnode *) n;
		t_key prf = ntohl(MASK_PFX(tn->key, tn->pos));

		if (!NODE_PARENT(n)) {
			if (iter->trie == trie_local)
				seq_puts(seq, "<local>:\n");
			else
				seq_puts(seq, "<main>:\n");
		} else {
		} else {
		snprintf(bf, sizeof(bf),
			seq_indent(seq, iter->depth-1);
			 "*\t%08X\t%08X", 200, 400);
			seq_printf(seq, "  +-- %d.%d.%d.%d/%d\n",
		seq_printf(seq, "%-127s\n", bf);
				   NIPQUAD(prf), tn->pos);
		}
	} else {
		struct leaf *l = (struct leaf *) n;
		int i;
		u32 val = ntohl(l->key);

		seq_indent(seq, iter->depth);
		seq_printf(seq, "  |-- %d.%d.%d.%d\n", NIPQUAD(val));
		for (i = 32; i >= 0; i--) {
			struct leaf_info *li = find_leaf_info(&l->list, i);
			if (li) {
				struct fib_alias *fa;
				list_for_each_entry_rcu(fa, &li->falh, fa_list) {
					seq_indent(seq, iter->depth+1);
					seq_printf(seq, "  /%d %s %s", i,
						   rtn_scope(fa->fa_scope),
						   rtn_type(fa->fa_type));
					if (fa->fa_tos)
						seq_printf(seq, "tos =%d\n",
							   fa->fa_tos);
					seq_putc(seq, '\n');
				}
			}
		}
	}
	}


	return 0;
	return 0;
@@ -2493,19 +2318,26 @@ static int fib_trie_seq_open(struct inode *inode, struct file *file)
{
{
	struct seq_file *seq;
	struct seq_file *seq;
	int rc = -ENOMEM;
	int rc = -ENOMEM;
	struct fib_trie_iter *s = kmalloc(sizeof(*s), GFP_KERNEL);

	if (!s)
		goto out;


	rc = seq_open(file, &fib_trie_seq_ops);
	rc = seq_open(file, &fib_trie_seq_ops);
	if (rc)
	if (rc)
		goto out_kfree;
		goto out_kfree;


	seq	     = file->private_data;
	seq	     = file->private_data;
	seq->private = s;
	memset(s, 0, sizeof(*s));
out:
out:
	return rc;
	return rc;
out_kfree:
out_kfree:
	kfree(s);
	goto out;
	goto out;
}
}


static struct file_operations fib_trie_seq_fops = {
static struct file_operations fib_trie_fops = {
	.owner  = THIS_MODULE,
	.owner  = THIS_MODULE,
	.open   = fib_trie_seq_open,
	.open   = fib_trie_seq_open,
	.read   = seq_read,
	.read   = seq_read,
@@ -2513,16 +2345,150 @@ static struct file_operations fib_trie_seq_fops = {
	.release = seq_release_private,
	.release = seq_release_private,
};
};


static unsigned fib_flag_trans(int type, u32 mask, const struct fib_info *fi)
{
	static unsigned type2flags[RTN_MAX + 1] = {
		[7] = RTF_REJECT, [8] = RTF_REJECT,
	};
	unsigned flags = type2flags[type];

	if (fi && fi->fib_nh->nh_gw)
		flags |= RTF_GATEWAY;
	if (mask == 0xFFFFFFFF)
		flags |= RTF_HOST;
	flags |= RTF_UP;
	return flags;
}

/*
 *	This outputs /proc/net/route.
 *	The format of the file is not supposed to be changed
 * 	and needs to be same as fib_hash output to avoid breaking
 *	legacy utilities
 */
static int fib_route_seq_show(struct seq_file *seq, void *v)
{
	struct leaf *l = v;
	int i;
	char bf[128];

	if (v == SEQ_START_TOKEN) {
		seq_printf(seq, "%-127s\n", "Iface\tDestination\tGateway "
			   "\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU"
			   "\tWindow\tIRTT");
		return 0;
	}

	if (IS_TNODE(l))
		return 0;

	for (i=32; i>=0; i--) {
		struct leaf_info *li = find_leaf_info(&l->list, i);
		struct fib_alias *fa;
		u32 mask, prefix;

		if (!li)
			continue;

		mask = inet_make_mask(li->plen);
		prefix = htonl(l->key);

		list_for_each_entry_rcu(fa, &li->falh, fa_list) {
			const struct fib_info *fi = rcu_dereference(fa->fa_info);
			unsigned flags = fib_flag_trans(fa->fa_type, mask, fi);

			if (fa->fa_type == RTN_BROADCAST
			    || fa->fa_type == RTN_MULTICAST)
				continue;

			if (fi)
				snprintf(bf, sizeof(bf),
					 "%s\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
					 fi->fib_dev ? fi->fib_dev->name : "*",
					 prefix,
					 fi->fib_nh->nh_gw, flags, 0, 0,
					 fi->fib_priority,
					 mask,
					 (fi->fib_advmss ? fi->fib_advmss + 40 : 0),
					 fi->fib_window,
					 fi->fib_rtt >> 3);
			else
				snprintf(bf, sizeof(bf),
					 "*\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
					 prefix, 0, flags, 0, 0, 0,
					 mask, 0, 0, 0);

			seq_printf(seq, "%-127s\n", bf);
		}
	}

	return 0;
}

static struct seq_operations fib_route_seq_ops = {
	.start  = fib_trie_seq_start,
	.next   = fib_trie_seq_next,
	.stop   = fib_trie_seq_stop,
	.show   = fib_route_seq_show,
};

static int fib_route_seq_open(struct inode *inode, struct file *file)
{
	struct seq_file *seq;
	int rc = -ENOMEM;
	struct fib_trie_iter *s = kmalloc(sizeof(*s), GFP_KERNEL);

	if (!s)
		goto out;

	rc = seq_open(file, &fib_route_seq_ops);
	if (rc)
		goto out_kfree;

	seq	     = file->private_data;
	seq->private = s;
	memset(s, 0, sizeof(*s));
out:
	return rc;
out_kfree:
	kfree(s);
	goto out;
}

static struct file_operations fib_route_fops = {
	.owner  = THIS_MODULE,
	.open   = fib_route_seq_open,
	.read   = seq_read,
	.llseek = seq_lseek,
	.release = seq_release_private,
};

int __init fib_proc_init(void)
int __init fib_proc_init(void)
{
{
	if (!proc_net_fops_create("fib_trie", S_IRUGO, &fib_trie_seq_fops))
	if (!proc_net_fops_create("fib_trie", S_IRUGO, &fib_trie_fops))
		return -ENOMEM;
		goto out1;

	if (!proc_net_fops_create("fib_triestat", S_IRUGO, &fib_triestat_fops))
		goto out2;

	if (!proc_net_fops_create("route", S_IRUGO, &fib_route_fops))
		goto out3;

	return 0;
	return 0;

out3:
	proc_net_remove("fib_triestat");
out2:
	proc_net_remove("fib_trie");
out1:
	return -ENOMEM;
}
}


void __init fib_proc_exit(void)
void __init fib_proc_exit(void)
{
{
	proc_net_remove("fib_trie");
	proc_net_remove("fib_trie");
	proc_net_remove("fib_triestat");
	proc_net_remove("route");
}
}


#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_PROC_FS */