/*
       * 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:	$Id: proc.c,v 1.44 2000/08/09 11:59:04 davem Exp $
       *
       * 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>
       *		Erik Schoenfelder, <schoenfr@ibr.cs.tu-bs.de>
       *
       * Fixes:
       *		Alan Cox	:	UDP sockets show the rxqueue/txqueue
       *					using hint flag for the netinfo.
       *	Pauline Middelink	:	identd support
       *		Alan Cox	:	Make /proc safer.
       *	Erik Schoenfelder	:	/proc/net/snmp
       *		Alan Cox	:	Handle dead sockets properly.
       *	Gerhard Koerting	:	Show both timers
       *		Alan Cox	:	Allow inode to be NULL (kernel socket)
       *	Andi Kleen		:	Add support for open_requests and 
       *					split functions for more readibility.
       *	Andi Kleen		:	Add support for /proc/net/netstat
       *
       *		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/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 <net/ip.h>
      #include <net/icmp.h>
      #include <net/protocol.h>
      #include <net/tcp.h>
      #include <net/udp.h>
      #include <linux/skbuff.h>
      #include <net/sock.h>
      #include <net/raw.h>
      
  53  static int fold_prot_inuse(struct proto *proto)
      {
      	int res = 0;
      	int cpu;
      
  58  	for (cpu=0; cpu<smp_num_cpus; cpu++)
      		res += proto->stats[cpu_logical_map(cpu)].inuse;
      
  61  	return res;
      }
      
      /*
       *	Report socket allocation statistics [mea@utu.fi]
       */
  67  int afinet_get_info(char *buffer, char **start, off_t offset, int length)
      {
      	/* From  net/socket.c  */
      	extern int socket_get_info(char *, char **, off_t, int);
      
      	int len  = socket_get_info(buffer,start,offset,length);
      
      	len += sprintf(buffer+len,"TCP: inuse %d orphan %d tw %d alloc %d mem %d\n",
      		       fold_prot_inuse(&tcp_prot),
      		       atomic_read(&tcp_orphan_count), tcp_tw_count,
      		       atomic_read(&tcp_sockets_allocated),
      		       atomic_read(&tcp_memory_allocated));
      	len += sprintf(buffer+len,"UDP: inuse %d\n",
      		       fold_prot_inuse(&udp_prot));
      	len += sprintf(buffer+len,"RAW: inuse %d\n",
      		       fold_prot_inuse(&raw_prot));
      	len += sprintf(buffer+len, "FRAG: inuse %d memory %d\n",
      		       ip_frag_nqueues, atomic_read(&ip_frag_mem));
  85  	if (offset >= len)
      	{
      		*start = buffer;
  88  		return 0;
      	}
      	*start = buffer + offset;
      	len -= offset;
  92  	if (len > length)
      		len = length;
  94  	if (len < 0)
      		len = 0;
  96  	return len;
      }
      
  99  static unsigned long fold_field(unsigned long *begin, int sz, int nr)
      {
      	unsigned long res = 0;
      	int i;
      
      	sz /= sizeof(unsigned long);
      
 106  	for (i=0; i<smp_num_cpus; i++) {
      		res += begin[2*cpu_logical_map(i)*sz + nr];
      		res += begin[(2*cpu_logical_map(i)+1)*sz + nr];
      	}
 110  	return res;
      }
      
      /* 
       *	Called from the PROCfs module. This outputs /proc/net/snmp.
       */
       
 117  int snmp_get_info(char *buffer, char **start, off_t offset, int length)
      {
      	extern int sysctl_ip_default_ttl;
      	int len, i;
      
      	len = sprintf (buffer,
      		"Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates\n"
      		"Ip: %d %d", ipv4_devconf.forwarding ? 1 : 2, sysctl_ip_default_ttl);
 125  	for (i=0; i<offsetof(struct ip_mib, __pad)/sizeof(unsigned long); i++)
      		len += sprintf(buffer+len, " %lu", fold_field((unsigned long*)ip_statistics, sizeof(struct ip_mib), i));
      
      	len += sprintf (buffer + len,
      		"\nIcmp: InMsgs InErrors InDestUnreachs InTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoReps InTimestamps InTimestampReps InAddrMasks InAddrMaskReps OutMsgs OutErrors OutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchos OutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutAddrMaskReps\n"
      		  "Icmp:");
 131  	for (i=0; i<offsetof(struct icmp_mib, __pad)/sizeof(unsigned long); i++)
      		len += sprintf(buffer+len, " %lu", fold_field((unsigned long*)icmp_statistics, sizeof(struct icmp_mib), i));
      
      	len += sprintf (buffer + len,
      		"\nTcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs InErrs OutRsts\n"
      		  "Tcp:");
 137  	for (i=0; i<offsetof(struct tcp_mib, __pad)/sizeof(unsigned long); i++)
      		len += sprintf(buffer+len, " %lu", fold_field((unsigned long*)tcp_statistics, sizeof(struct tcp_mib), i));
      
      	len += sprintf (buffer + len,
      		"\nUdp: InDatagrams NoPorts InErrors OutDatagrams\n"
      		  "Udp:");
 143  	for (i=0; i<offsetof(struct udp_mib, __pad)/sizeof(unsigned long); i++)
      		len += sprintf(buffer+len, " %lu", fold_field((unsigned long*)udp_statistics, sizeof(struct udp_mib), i));
      
      	len += sprintf (buffer + len, "\n");
      
 148  	if (offset >= len)
      	{
      		*start = buffer;
 151  		return 0;
      	}
      	*start = buffer + offset;
      	len -= offset;
 155  	if (len > length)
      		len = length;
 157  	if (len < 0)
      		len = 0; 
 159  	return len;
      }
      
      /* 
       *	Output /proc/net/netstat
       */
       
 166  int netstat_get_info(char *buffer, char **start, off_t offset, int length)
      {
      	int len, i;
      
      	len = sprintf(buffer,
      		      "TcpExt: SyncookiesSent SyncookiesRecv SyncookiesFailed"
      		      " EmbryonicRsts PruneCalled RcvPruned OfoPruned"
      		      " OutOfWindowIcmps LockDroppedIcmps"
      		      " TW TWRecycled TWKilled"
      		      " PAWSPassive PAWSActive PAWSEstab"
      		      " DelayedACKs DelayedACKLocked DelayedACKLost"
      		      " ListenOverflows ListenDrops"
      		      " TCPPrequeued TCPDirectCopyFromBacklog"
      		      " TCPDirectCopyFromPrequeue TCPPrequeueDropped"
      		      " TCPHPHits TCPHPHitsToUser"
      		      " TCPPureAcks TCPHPAcks"
      		      " TCPRenoRecovery TCPSackRecovery"
      		      " TCPSACKReneging"
      		      " TCPFACKReorder TCPSACKReorder TCPRenoReorder TCPTSReorder"
      		      " TCPFullUndo TCPPartialUndo TCPDSACKUndo TCPLossUndo"
      		      " TCPLoss TCPLostRetransmit"
      		      " TCPRenoFailures TCPSackFailures TCPLossFailures"
      		      " TCPFastRetrans TCPForwardRetrans TCPSlowStartRetrans"
      		      " TCPTimeouts"
      		      " TCPRenoRecoveryFail TCPSackRecoveryFail"
      		      " TCPSchedulerFailed TCPRcvCollapsed"
      		      " TCPDSACKOldSent TCPDSACKOfoSent TCPDSACKRecv TCPDSACKOfoRecv"
      		      " TCPAbortOnSyn TCPAbortOnData TCPAbortOnClose"
      		      " TCPAbortOnMemory TCPAbortOnTimeout TCPAbortOnLinger"
      		      " TCPAbortFailed TCPMemoryPressures\n"
      		      "TcpExt:");
 197  	for (i=0; i<offsetof(struct linux_mib, __pad)/sizeof(unsigned long); i++)
      		len += sprintf(buffer+len, " %lu", fold_field((unsigned long*)net_statistics, sizeof(struct linux_mib), i));
      
      	len += sprintf (buffer + len, "\n");
      
 202  	if (offset >= len)
      	{
      		*start = buffer;
 205  		return 0;
      	}
      	*start = buffer + offset;
      	len -= offset;
 209  	if (len > length)
      		len = length;
 211  	if (len < 0)
      		len = 0; 
 213  	return len;
      }