/*
 * INET		An implementation of the TCP/IP protocol suite for the LINUX
 *		operating system.  INET is implemented using the  BSD Socket
 *		interface as the means of communication with the user level.
 *
 *		This file implements the various access functions for the
 *		PROC file system.  It is mainly used for debugging and
 *		statistics.
 *
 * Version:	@(#)proc.c	1.0.5	05/27/93
 *
 * Authors:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
 *		Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de>
 *		Fred Baumgarten, <dc6iq@insu1.etec.uni-karlsruhe.de>
 *
 * Fixes:
 *		Alan Cox	:	UDP sockets show the rxqueue/txqueue
 *					using hint flag for the netinfo.
 *	Pauline Middelink	:	Pidentd support
 *		Alan Cox	:	Make /proc safer.
 *
 * To Do:
 *		Put the creating userid in the proc/net/... files. This will
 *		allow us to write an RFC931 daemon for Linux
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 */
#include <asm/system.h>
#include <linux/autoconf.h>
#include <linux/sched.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/un.h>
#include <linux/in.h>
#include <linux/param.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include "ip.h"
#include "protocol.h"
#include "tcp.h"
#include "udp.h"
#include <linux/skbuff.h>
#include "sock.h"
#include "raw.h"

/*
 * Get__netinfo returns the length of that string.
 *
 * KNOWN BUGS
 *  As in get_unix_netinfo, the buffer might be too small. If this
 *  happens, get__netinfo returns only part of the available infos.
 */
static int
get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t offset, int length)
{
  struct sock **s_array;
  struct sock *sp;
  int i;
  int timer_active;
  unsigned long  dest, src;
  unsigned short destp, srcp;
  int len=0;
  off_t pos=0;
  off_t begin=0;
  

  s_array = pro->sock_array;
  len+=sprintf(buffer, "sl  local_address rem_address   st tx_queue rx_queue tr tm->when uid\n");
/*
 *	This was very pretty but didn't work when a socket is destroyed at the wrong moment
 *	(eg a syn recv socket getting a reset), or a memory timer destroy. Instead of playing
 *	with timers we just concede defeat and cli().
 */
  for(i = 0; i < SOCK_ARRAY_SIZE; i++) {
  	cli();
	sp = s_array[i];
	while(sp != NULL) {
		dest  = sp->daddr;
		src   = sp->saddr;
		destp = sp->dummy_th.dest;
		srcp  = sp->dummy_th.source;

		/* Since we are Little Endian we need to swap the bytes :-( */
		destp = ntohs(destp);
		srcp  = ntohs(srcp);
		timer_active = del_timer(&sp->timer);
		if (!timer_active)
			sp->timer.expires = 0;
		len+=sprintf(buffer+len, "%2d: %08lX:%04X %08lX:%04X %02X %08lX:%08lX %02X:%08lX %08X %d\n",
			i, src, srcp, dest, destp, sp->state, 
			format==0?sp->write_seq-sp->rcv_ack_seq:sp->rmem_alloc, 
			format==0?sp->acked_seq-sp->copied_seq:sp->wmem_alloc,
			timer_active, sp->timer.expires, (unsigned) sp->retransmits,
			SOCK_INODE(sp->socket)->i_uid);
		if (timer_active)
			add_timer(&sp->timer);
		/*
		 * All sockets with (port mod SOCK_ARRAY_SIZE) = i
		 * are kept in sock_array[i], so we must follow the
		 * 'next' link to get them all.
		 */
		sp = sp->next;
		pos=begin+len;
		if(pos<offset)
		{
			len=0;
			begin=pos;
		}
		if(pos>offset+length)
			break;
	}
	sti();	/* We only turn interrupts back on for a moment, but because the interrupt queues anything built up
		   before this will clear before we jump back and cli, so its not as bad as it looks */
	if(pos>offset+length)
		break;
  }
  *start=buffer+(offset-begin);
  len-=(offset-begin);
  if(len>length)
  	len=length;
  return len;
} 


int tcp_get_info(char *buffer, char **start, off_t offset, int length)
{
  return get__netinfo(&tcp_prot, buffer,0, start, offset, length);
}


int udp_get_info(char *buffer, char **start, off_t offset, int length)
{
  return get__netinfo(&udp_prot, buffer,1, start, offset, length);
}


int raw_get_info(char *buffer, char **start, off_t offset, int length)
{
  return get__netinfo(&raw_prot, buffer,1, start, offset, length);
}
