/*
 * Copyright (C) 2000-2003 ASANO Masahiro
 */

#include "def.h"

#define __KERNEL__
#include <linux/netdevice.h>
#include <net/route.h>
#include <net/ip_fib.h>

/* begin linux/net/ipv4/route.c */
struct rt_hash_bucket {
	struct rtable	*chain;
	rwlock_t	lock;
};
/* end linux/net/ipv4/route.c */

/* begin linux/net/ipv4/fib_hash.c */
struct fn_zone
{
	struct fn_zone  *fz_next;	/* Next not empty zone  */
	struct fib_node **fz_hash;	/* Hash table pointer   */
	int		fz_nent;	/* Number of entries    */

	int		fz_divisor;	/* Hash divisor         */
	u32		fz_hashmask;	/* (1<<fz_divisor) - 1  */

	int		fz_order;	/* Zone order           */
	u32		fz_mask;
};

struct fn_hash
{
	struct fn_zone	*fn_zones[33];
	struct fn_zone	*fn_zone_list;
};
/* end linux/net/ipv4/fib_hsah.c */

void
prhead_rtable()
{
	mprintf(SPTR"      DST      SRC IIF  GATEWAY TYPE        "SPTR" "SPTR" "SPTR"\n", "ADDR", "DEV", "OPS", "NEIGHBOU");
}

const static char *
print_rtn(type)
{
	switch (type) {
	case RTN_UNSPEC:	return "unspec";
	case RTN_UNICAST:	return "unicast";
	case RTN_LOCAL:		return "local";
	case RTN_BROADCAST:	return "broadcast";
	case RTN_ANYCAST:	return "anycast";
	case RTN_MULTICAST:	return "multicast";
	case RTN_BLACKHOLE:	return "blackhole";
	case RTN_UNREACHABLE:	return "unreachable";
	case RTN_PROHIBIT:	return "prohibit";
	case RTN_THROW:		return "throw";
	case RTN_NAT:		return "nat";
	case RTN_XRESOLVE:	return "xresolve";
	}
	return "?";
}

addr_t
print_rtable(addr)
	addr_t addr;
{
	struct rtable rt;

	memread(addr, sizeof(rt), &rt, "rtable");
	mprintf(FPTR " ", addr);

	mprintf("%08x %08x %3x %08x ", m_ntohl(rt.rt_dst), m_ntohl(rt.rt_src), rt.rt_iif, m_ntohl(rt.rt_gateway));
	mprintf("%-11s ", print_rtn(rt.rt_type));
	mprintf(FPTR " " FPTR " " FPTR "\n", rt.u.dst.dev, rt.u.dst.ops, rt.u.dst.neighbour);
	return (addr_t)rt.u.rt_next;
}

addr_t
print_rt_hash_table(addr, size)
	addr_t addr;
	unsigned size;
{
#define	NBUCKET	32
	struct rt_hash_bucket hash[NBUCKET];
	int i, j;

	prhead_rtable();
	for (i = 0; i < size / NBUCKET; i++) {
		memread(addr + i * sizeof(hash), sizeof(hash), hash, "rt_hash_bucket");
		for (j = 0; j < NBUCKET; j++) {
			if (hash[j].chain) {
#ifdef DEBUG
				mprintf("(%d,%d) ", i, j);
#endif
				print_rtable(hash[j].chain);
			}
		}
	}
	return 0;
}

void
prhead_fib_table()
{
	mprintf(SPTR" ID STAMP "SPTR" "SPTR" "SPTR" "SPTR" "SPTR" "SPTR"\n", "ADDR", "LOOKUP", "INSERT", "DELETE", "DUMP", "FLUSH", "SELECT_D");
}

addr_t
print_fib_table(addr)
	addr_t addr;
{
	int i;
	struct fib_table fib;
	struct fn_hash hash;

	memread(addr, sizeof(fib), &fib, "fib_table");
	mprintf(FPTR " ", addr);

	mprintf("%2x %5x " FPTR " " FPTR " " FPTR " " FPTR " " FPTR " " FPTR "\n",
		fib.tb_id, fib.tb_stamp,
		fib.tb_lookup, fib.tb_insert, fib.tb_delete, fib.tb_dump,
		fib.tb_flush, fib.tb_select_default);

	memread(addr + OFFSET(struct fib_table, tb_data), sizeof(hash), &hash, "fn_hash");
	mprintf("  "SPTR"  O     MASK "SPTR" DV N\n", "ADDR", "HASH");
	for (i = 0; i < 33; i++) {
		struct fn_zone zone;
		if (hash.fn_zones[i] == NULL)
			continue;
		mprintf("  " FPTR " ", hash.fn_zones[i]);
		memread((addr_t)hash.fn_zones[i], sizeof(zone), &zone, "fn_zone");
		mprintf("%2x %08x " FPTR " %2x %x\n",
			zone.fz_order, m_ntohl(zone.fz_mask), zone.fz_hash, zone.fz_divisor, zone.fz_nent);
	}
	return 0;
}
