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

#include "def.h"

#define __KERNEL__
#include <linux/net.h>
#include <net/sock.h>
#include <net/tcp.h>
#include <net/udp.h>

const char *
get_address_family(af)
	int af;
{
	switch (af) {
	case AF_UNSPEC:		return "UNSPEC";
	case AF_UNIX:		return "UNIX";
	case AF_INET:		return "INET";
	case AF_AX25:		return "AX25";
	case AF_IPX:		return "IPX";
	case AF_APPLETALK:	return "APPLETALK";
	case AF_NETROM:		return "NETROM";
	case AF_BRIDGE:		return "BRIDGE";
	case AF_ATMPVC:		return "ATMPVC";
	case AF_X25:		return "X25";
	case AF_INET6:		return "INET6";
	case AF_ROSE:		return "ROSE";
	case AF_DECnet:		return "DECnet";
	case AF_NETBEUI:	return "NETBEUI";
	case AF_SECURITY:	return "SECURITY";
	case AF_KEY:		return "KEY";
	case AF_NETLINK:	return "NETLINK";
	case AF_PACKET:		return "PACKET";
	case AF_ASH:		return "ASH";
	case AF_ECONET:		return "ECONET";
	case AF_ATMSVC:		return "ATMSVC";
	case AF_SNA:		return "SNA";
	case AF_IRDA:		return "IRDA";
	case AF_PPPOX:		return "PPPOX";
	case AF_WANPIPE:	return "WANPIPE";
#ifdef AF_LLC
	case AF_LLC:		return "LLC";
#endif
#ifdef AF_BLUETOOTH
	case AF_BLUETOOTH:	return "BLUETOOTH";
#endif
	default:		return "?";
	}
}

const char *
get_socket_type(typ)
	int typ;
{
	switch (typ) {
	case SOCK_STREAM:	return "STREAM";
	case SOCK_DGRAM:	return "DGRAM";
	case SOCK_RAW:		return "RAW";
	case SOCK_RDM:		return "RDM";
	case SOCK_SEQPACKET:	return "SEQPACKET";
	case SOCK_PACKET:	return "PACKET";
	default:		return "?";
	}
}

addr_t
print_socket(addr)
	addr_t addr;
{
	addr_t waddr;
	struct socket sock;
	static const struct bitname sflags[] = {
		{ 1<<SOCK_ASYNC_NOSPACE,	"async_nospace" },
		{ 1<<SOCK_ASYNC_WAITDATA,	"async_waitdata" },
		{ 1<<SOCK_NOSPACE,		"nospace" },
		{ 0, NULL }
	};

	memread(addr, sizeof(sock), &sock, "socket");
	mprintf("addr:        " FPTR "\n", addr);

	mprintf("state:       ");
	switch (sock.state) {
	case SS_FREE:		mprintf("FREE");	break;
	case SS_UNCONNECTED:	mprintf("UNCONNECTED");	break;
	case SS_CONNECTING:	mprintf("CONNECTING");	break;
	case SS_CONNECTED:	mprintf("CONNECTED");	break;
	case SS_DISCONNECTING:	mprintf("DISCONNECTING"); break;
	default:		mprintf("?");		break;
	}
	mprintf("\n");
	mprintf("flags:      "); mprintbit(sflags, sock.flags); mprintf("\n");
	mprintf("ops:         " FPTR "\n", sock.ops);
	mprintf("inode:       " FPTR "\n", sock.inode);
	mprintf("fasync_list: " FPTR "\n", sock.fasync_list);
	mprintf("file:        " FPTR "\n", sock.file);
	mprintf("sk:          " FPTR "\n", sock.sk);
	mprintf("wait:       ");
	waddr = (addr_t)sock.wait.task_list.next;
	while (waddr && waddr != addr + OFFSET(struct socket, wait.task_list)) {
		mprintf(" " FPTR, waddr);
		memread(waddr + OFFSET(struct list_head, next), sizeof(waddr), &waddr, "wait_queue_head");
	}
	mprintf("\n");
	mprintf("type:        %s\n", get_socket_type(sock.type));
	mprintf("passcread:   %x\n", sock.passcred);

	return 0;
}

void
prhead_sock()
{
	mprintf(SPTR" FAMILY CNT   TYPE   DESTINATION         LOCAL   R Q   W Q "SPTR"\n", "ADDR", "SOCKET");
}

void
prhead_sk_buff()
{
	mprintf("  "SPTR" "SPTR" "SPTR" "SPTR" "SPTR" "SPTR"  LEN DLEN "SPTR"\n",
		"ADDR", "DEV", "TRANSPOR", "NETWORK", "LINK", "DST", "HEAD");
}

addr_t
print_sk_buff(addr)
	addr_t addr;
{
	struct sk_buff skb;

	memread(addr, sizeof(skb), &skb, "sk_buff");
	mprintf("  " FPTR, addr);
	mprintf(" " FPTR, skb.dev);
	mprintf(" " FPTR, skb.h.th);
	mprintf(" " FPTR, skb.nh.iph);
	mprintf(" " FPTR, skb.mac.ethernet);
	mprintf(" " FPTR, skb.dst);
	mprintf(" %4x %4x", skb.len, skb.data_len);
	mprintf(" " FPTR, skb.head);
	mprintf("\n");
	return (addr_t) skb.next;
}

addr_t
print_sock(addr, fflag)
	addr_t addr;
	int fflag;
{
	struct sock sk;

	memread(addr, sizeof(sk), &sk, "sock");
	mprintf(FPTR, addr);
	mprintf(" %6s", get_address_family(sk.family));
	mprintf(" %3x", ATOMIC_READ(sk.refcnt));
	mprintf(" %6s", get_socket_type(sk.type));
	mprintf(" %08x:%04x %08x:%04x", m_ntohl(sk.daddr), m_ntohs(sk.dport), m_ntohl(sk.rcv_saddr), sk.num);
	mprintf(" %3x %1x", ATOMIC_READ(sk.rmem_alloc), sk.receive_queue.qlen);
	mprintf(" %3x %1x", ATOMIC_READ(sk.wmem_alloc), sk.write_queue.qlen);
#if 0
	mprintf(" " FPTR, sk.pair);
#endif
	mprintf(" " FPTR, sk.socket);
	mprintf("\n");

	if (fflag) {
		addr_t skb;

		if (sk.receive_queue.qlen) {
			prhead_sk_buff();
		}
		skb = (addr_t)sk.receive_queue.next;
		while (skb && skb != addr + OFFSET(struct sock, receive_queue)) {
			skb = print_sk_buff(skb);
		}

		if (sk.write_queue.qlen) {
			prhead_sk_buff();
		}
		skb = (addr_t)sk.write_queue.next;
		while (skb && skb != addr + OFFSET(struct sock, write_queue)) {
			skb = print_sk_buff(skb);
		}
	}
	return (addr_t) sk.next;
}

addr_t
print_listed_sock(fflag, addr, udp_hash_addr, unix_socket_table_addr)
	int fflag;
	addr_t addr;
	addr_t udp_hash_addr;
	addr_t unix_socket_table_addr;
{
	struct tcp_hashinfo tcp;
	int i;

	memread(addr, sizeof(tcp), &tcp, "tcp_hashinfo");

	mprintf("TCP ESTABLISHED\n");
	prhead_sock();
	for (i = 0; i < tcp.__tcp_ehash_size; i++) {
		struct tcp_ehash_bucket ebucket;

		memread((addr_t)(tcp.__tcp_ehash + i), sizeof(ebucket), &ebucket, "tcp_ehash_bucket");
		addr = (addr_t)ebucket.chain;
		while (addr) {
			addr = print_sock(addr, fflag);
		}
	}

	mprintf("TCP LISTENING\n");
	prhead_sock();
	for (i = 0; i < TCP_LHTABLE_SIZE; i++) {
		addr = (addr_t) tcp.__tcp_listening_hash[i];
		while (addr) {
			addr = print_sock(addr, fflag);
		}
	}

	mprintf("UDP\n");
	prhead_sock();
	for (i = 0; i < UDP_HTABLE_SIZE; i++) {
		memread(udp_hash_addr + sizeof(addr_t) * i, sizeof(addr), &addr, "udp_hash");
		while (addr) {
			addr = print_sock(addr, fflag);
		}
	}

	mprintf("UNIX\n");
	prhead_sock();
	for (i = 0; i < 256 + 1; i++) {
		memread(unix_socket_table_addr + sizeof(addr_t) * i, sizeof(addr), &addr, "unix_socket_table");
		while (addr) {
			addr = print_sock(addr, fflag);
		}
	}

	return 0;
}
