/*
    Copyright 2000 (c) by Reto Gaehler, Toni Kaufmann,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public 
    Licence. A full copy of the GNU GPL is provided in the file COPYING 
    found in the development root of Topsy.
*/
/*
    File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/Net/old/TCP/nettcpSocket.c,v $
    Author(s):             
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2936 $
    Creation Date:         
    Last Date of Change:   $Date: 2007-03-21 15:23:42 +0100 (Mit, 21 Mär 2007) $      by: $Author: baderma $
 
 
    $Log: nettcpSocket.c,v $
    Revision 1.3  2000/11/15 10:12:15  ruf
    Repository Reorganisation
 
    Revision 1.2  2000/04/03 17:45:30  gfa
    *** empty log message ***
 
    Revision 1.1  2000/03/31 17:50:39  gfa
    Merged with /Net from several term projects
 
*/

#include "nettcpConfig.h"
#include "nettcpSocket.h"

//**************************************************************************************************
//	prototypes
//**************************************************************************************************

static int tcpCon(TCBPtr pTCB);
static int tcpServer(SocketPtr pSocket,unsigned short u16LocalPort,SocketPtr *ppNewSocket);
static unsigned short tcpNextPort();
static int tcpBind(TCBPtr pTCB,unsigned short u16RemotePort,unsigned short	u16LocalPort);
static int tcpGetSpace(TCBPtr pTCB,unsigned uLen);
static int tcpGetData(TCBPtr pTCB,unsigned char *pu8SrcBuf,unsigned uLen);
static void tcpListenQueue(TCBPtr pTCB,int nListenQueueSize);
static int tcpUserOption(TCBPtr pTCB,int nOperation,unsigned short u16Flags);
static int tcpWriteBlock(SocketPtr pSocket,unsigned char *pu8SrcBuf,unsigned uLen,Boolean bIsUrgent);
static int tcpReadBlock(SocketPtr pSocket,unsigned char *pu8SrcBuf,unsigned uLen);

//**************************************************************************************************
//	tcp globals
//**************************************************************************************************

// statistics
extern unsigned long	gu32TcpInErrors;
extern unsigned long	gu32TcpPassiveOpens;
extern unsigned long	gu32TcpAttemptFails;
extern unsigned long	gu32TcpCurrEstab;
extern unsigned long	gu32TcpEstabResets;
extern unsigned long	gu32TcpOutSegs;
extern unsigned long	gu32TcpOutRsts;
extern unsigned long	gu32TcpRetransSegs;
extern unsigned long	gu32TcpActiveOpens;

extern int				gnListenQueueSize;
extern SemaphoreRec		gTCBLinkedListMutex;
extern TCBPtr			gTCPTCBList;

//**************************************************************************************************
//	tcpNewSocket
//	initializes a socket
//**************************************************************************************************
int										// result code. returns TCP_OK if success
tcpNewSocket(
  SocketPtr *ppSocket)					// -> allocated socket data structure
{
  *ppSocket = (SocketPtr)tcpAlloc(sizeof(SocketRec));
  if (*ppSocket == NULL)
    return TCP_GENERAL_ERROR;
  (**ppSocket).pTCB = NULL;
  (**ppSocket).bUrgentMode = FALSE;
  return TCP_OK;
}

//**************************************************************************************************
//	tcpCon
//	initiates a connection
//	���
//**************************************************************************************************
static int
tcpCon(
  TCBPtr	pTCB)
{
  int	nError = 0;

  pTCB->u32SndMaxSegSize = 536;							// RFC 1122
  pTCB->u32RcvMaxSegSize = pTCB->u32SndMaxSegSize;
  pTCB->u32SndWinSize = pTCB->u32SndMaxSegSize;
  pTCB->u32CongestionWinSize = pTCB->u32SndMaxSegSize;	/* 1 segment */
  pTCB->u32SlowStartThreshold = 65535;					/* IP Max window size */
  pTCB->n32RcvNextSeq = 0;
  pTCB->n32FINSeqNum = 0;
  pTCB->n32PUSHSeqNum = 0;
  pTCB->u16Flags = TCP_TCB_NEED_OUTPUT | TCP_TCB_FIRST_SEND;
  pTCB->setOutState = TCP_OUT_STATE_IDLE;
  pTCB->setState = TCP_STATE_SYNSENT;
  tcpKick(pTCB);
  gu32TcpActiveOpens++;
  semSignal(&pTCB->recMutex);
  semWait(&pTCB->recOpenCloseSemaphore);
  nError = pTCB->nError;
  if (nError != 0)
    tcpTCBDeallocate(pTCB);

  return nError;
}

//**************************************************************************************************
//	tcpServer
//	does a TCP passive open
//**************************************************************************************************
static int
tcpServer(
  SocketPtr		pSocket,
  unsigned short	u16LocalPort,
  SocketPtr		*ppNewSocket)
{
  TCBPtr	pTCB = pSocket->pTCB;

  if (u16LocalPort == TCP_ANY_LOCAL_PORT)
  {
    return TCP_GENERAL_ERROR;
  }

  pTCB->setType = TCP_TYPE_SERVER;

  if (!netattrSet(pTCB->netParams,NETATTR_TCP_FROM,u16LocalPort))
    return TCP_GENERAL_ERROR;
  if (!netattrSet(pTCB->netParams,NETATTR_TCP_TO,0))
    return TCP_GENERAL_ERROR;

  pTCB->setState = TCP_STATE_LISTEN;
  pTCB->nListenQueueSize = gnListenQueueSize;
  ptInit(&pTCB->recListenQueue,gnListenQueueSize);
  pTCB->u32SndMaxSegSize = 0;
  semSignal(&pTCB->recMutex);
  *ppNewSocket = (SocketPtr)pTCB->pDevice;
  return TCP_NO_ERR;
}

//**************************************************************************************************
//	returns the next available TCP local "port" number
//**************************************************************************************************
static unsigned short
tcpNextPort()
{
  static unsigned short	gu16LastPort = 1;	// #'s 1-1023
  unsigned short			u16Start;
  TCBPtr					pCurrentTCB;
  unsigned short			u16CurrentPort;

  semWait(&gTCBLinkedListMutex);

  for (u16Start = gu16LastPort++;u16Start != gu16LastPort;++gu16LastPort)
  {
    if (gu16LastPort == TCP_PPORT_RESERVED)
      gu16LastPort = 1;

    pCurrentTCB = gTCPTCBList;
    while (pCurrentTCB != NULL)
    {
      if (!netattrGet(pCurrentTCB->netParams,NETATTR_TCP_FROM,&u16CurrentPort))
        return TCP_GENERAL_ERROR;
      if (u16CurrentPort == gu16LastPort)
        break;
      pCurrentTCB = (TCBPtr)pCurrentTCB->pNext;
    }

    if (pCurrentTCB == NULL)
      break;
  }

  if (gu16LastPort == u16Start)
    netdbgDisplay(NETDEBUG_TCP, "tcp: out of TCP ports\n");

  semSignal(&gTCBLinkedListMutex);

  return gu16LastPort;
}

//**************************************************************************************************
//	tcpBind
//	binds a TCP pseudo device to its addresses and port
//	���
//**************************************************************************************************
static int
tcpBind(
  TCBPtr			pTCB,
  unsigned short	u16RemotePort,
  unsigned short	u16LocalPort)
{
  unsigned short	u16NextPort;

  if (!netattrSet(pTCB->netParams,NETATTR_TCP_TO,u16RemotePort))
    return TCP_GENERAL_ERROR;

  if (u16LocalPort == TCP_ANY_LOCAL_PORT)
  {
    u16NextPort = tcpNextPort();
    if (netattrSet(pTCB->netParams,NETATTR_TCP_FROM,u16NextPort))
      return TCP_OK;
    else
      return TCP_GENERAL_ERROR;
  }

  if (!netattrSet(pTCB->netParams,NETATTR_TCP_FROM,u16LocalPort))
    return TCP_GENERAL_ERROR;

  return TCP_OK;
}

//**************************************************************************************************
//	tcpOpen
//  opens a fresh TCP pseudo device and returns a socket
//**************************************************************************************************
int										// returns TCP_NO_ERR if successful
tcpOpen(
  IPAddressPtr	pIPSrcAddr,				// source ip addrs
  IPAddressPtr	pIPDstAddr,				// dest ip addrs
  unsigned short	u16SrcPort,				// local tcp port or TCP_ANY_LOCAL_PORT
  unsigned short	u16DstPort,				// TCP_ANY_FOREIGN_PORT = Server
  SocketPtr		*ppSocket)				// returns allocated Socket or NULL
{
  int			nError = TCP_NO_ERR;
  TCBPtr		pTCB;
  SocketPtr 	pNewSocket;

  if (tcpTCBAllocate(&pTCB) != TCP_OK)
    return TCP_GENERAL_ERROR;

  if (tcpNewSocket(&pNewSocket) != TCP_OK)
    return TCP_GENERAL_ERROR;
  pTCB->pDevice = pNewSocket;
  pNewSocket->pTCB = pTCB;

  if (tcpSetIPAddr(pTCB->netParams,TCP_IP_SRC_ADDR,pIPSrcAddr) != TCP_OK)
    return TCP_GENERAL_ERROR;
  if (tcpSetIPAddr(pTCB->netParams,TCP_IP_DST_ADDR,pIPDstAddr) != TCP_OK)
    return TCP_GENERAL_ERROR;
  if (!netattrSet(pTCB->netParams,NETATTR_TCP_FROM,0))
    return TCP_GENERAL_ERROR;

  pTCB->nError = TCP_NO_ERR;

  if (u16DstPort == TCP_ANY_FOREIGN_PORT)
    return tcpServer(pNewSocket,u16SrcPort,ppSocket);

  if (tcpBind(pTCB,u16DstPort,u16SrcPort) != TCP_OK || tcpSync(pTCB) != TCP_OK)
  {
    return TCP_GENERAL_ERROR;
  }

  nError = tcpCon(pTCB);
  *ppSocket = pNewSocket;

  return nError;
}

//**************************************************************************************************
//	tcpClose
//**************************************************************************************************
int
tcpClose(
  SocketPtr pSocket)
{
  int nError;

  TCBPtr pTCB = pSocket->pTCB;
  semWait(&pTCB->recMutex);
  switch(pTCB->setState)
  {
  case TCP_STATE_ESTABLISHED:
  case TCP_STATE_CLOSEWAIT:
    break;
  case TCP_STATE_LISTEN:
    return tcpTCBDeallocate(pTCB);
    break;
  default:
    semSignal(&pTCB->recMutex);
    return TCP_GENERAL_ERROR;
    break;
  }
  if (pTCB->nError != 0)
    return tcpTCBDeallocate(pTCB);

  gu32TcpCurrEstab--;
  pTCB->u16Flags |= TCP_TCB_SEND_FIN;

  pTCB->n32SndLast = pTCB->n32SndUnacked + pTCB->u32SndBufCount;

  if (pTCB->setState == TCP_STATE_ESTABLISHED)
    pTCB->setState = TCP_STATE_FINWAIT1;
  else
    pTCB->setState = TCP_STATE_LASTACK;
  pTCB->u16Flags |= TCP_TCB_NEED_OUTPUT;

  tcpKick(pTCB);

  semSignal(&pTCB->recMutex);
  semWait(&pTCB->recOpenCloseSemaphore);	// tmWait for FIN to be ACKed

  nError = pTCB->nError;

  if (pTCB->setState == TCP_STATE_LASTACK)
    tcpTCBDeallocate(pTCB);
  tcpFree((unsigned char *)pSocket);
  return nError;
}

//**************************************************************************************************
//	tcpGetSpace
//	waits for space in the send buffer. returns with tcb_mutex HELD
//**************************************************************************************************
static int
tcpGetSpace(
  TCBPtr		pTCB,
  unsigned	uLen)
{
  if (uLen > pTCB->u32SndBufSize)
    return TCP_TOOBIG;		// we'll never have this much

  while(TRUE)
  {
    semWait(&pTCB->recSndSemaphore);
    semWait(&pTCB->recMutex);

    if (pTCB->nError != 0)
    {
      tcpWakeup(pTCB,TCP_WRITERS);
      semSignal(&pTCB->recMutex);
      return pTCB->nError;
    }

    if (uLen <= (pTCB->u32SndBufSize - pTCB->u32SndBufCount))
      return uLen;
    semSignal(&pTCB->recMutex);
  }
}

//**************************************************************************************************
//	tcpWriteBlock
//	writes urgent and normal data to TCP buffers. The max block size which can be sent is
//	TCP_SND_BUF_SIZE
//**************************************************************************************************
static	int									// returns number of sent bytes
tcpWriteBlock(								// transmits data
  SocketPtr 		pSocket,				// socket ptr
  unsigned char	*pu8SrcBuf,				// source data buffer
  unsigned 		uLen,					// length of source data buffer
  Boolean			bIsUrgent)				// if TRUE, urgent data processing will be used
{
  TCBPtr			pTCB = pSocket->pTCB;
  int 			nToCopy;
  unsigned		uSndBufOffset;

  if ((pTCB->setState != TCP_STATE_ESTABLISHED) && (pTCB->setState != TCP_STATE_CLOSEWAIT))
    return TCP_GENERAL_ERROR;

  nToCopy = tcpGetSpace(pTCB,uLen);

  if (nToCopy <= 0)
    return nToCopy;

  uSndBufOffset = (pTCB->u32SndBufStart + pTCB->u32SndBufCount) % pTCB->u32SndBufSize;

  if (bIsUrgent)
  {
    pTCB->n32SndUrgentSeq = pTCB->n32SndNext + uLen -1;
    pTCB->u16Flags |= TCP_TCB_SND_URGENT_ISOK;
  }

  while (nToCopy--)
  {
    pTCB->pu8sndBuf[uSndBufOffset] = *pu8SrcBuf++;
    ++pTCB->u32SndBufCount;
    if (++uSndBufOffset >= pTCB->u32SndBufSize)
      uSndBufOffset = 0;
  }

  pTCB->u16Flags |= TCP_TCB_NEED_OUTPUT;
  tcpWakeup(pTCB,TCP_WRITERS);
  semSignal(&pTCB->recMutex);
  if (bIsUrgent || (pTCB->n32SndNext == pTCB->n32SndUnacked))
    tcpKick(pTCB);

  return uLen;
}

//**************************************************************************************************
//	tcpWrite
//	writes urgent and normal data to TCP buffers.
//**************************************************************************************************
int										// returns number of sent bytes or error code (<0)
tcpWrite(									// transmits data
  SocketPtr 		pSocket,				// socket ptr
  unsigned char	*pu8SrcBuf,				// source data buffer
  unsigned long	u32Len,					// length of source data buffer
  Boolean			bIsUrgent)				// if TRUE, urgent data processing will be used
{
  long int nResult=0L;
  long int nBytesWritten=0;

  while (nBytesWritten<u32Len)
  {
    // are remaining data > TCP_SND_BUF_SIZE
    if (u32Len-nBytesWritten>TCP_SND_BUF_SIZE)
      nResult = tcpWriteBlock(pSocket,pu8SrcBuf+nBytesWritten,TCP_SND_BUF_SIZE,bIsUrgent);
    else
      nResult = tcpWriteBlock(pSocket,pu8SrcBuf+nBytesWritten,u32Len-nBytesWritten,bIsUrgent);
    if (nResult<0L)
      return nResult;
    else
      nBytesWritten+=nResult;

    if ((pSocket->pTCB->u16Flags & TCP_TCB_BUFFER) == 0)
      break;
    else if (nBytesWritten<u32Len)
      tmYield();
  };

  return nBytesWritten;
}

//**************************************************************************************************
//	tcpGetData
//	copies data from a TCP receive buffer to a user buffer
//**************************************************************************************************
static int
tcpGetData(
  TCBPtr			pTCB,
  unsigned char	*pu8SrcBuf,
  unsigned		uLen)
{
  long		n32Seq;
  unsigned	uByteCount;

  n32Seq = pTCB->n32RcvNextSeq - pTCB->u32RcvBufCount; // start sequence

  if (pTCB->u16Flags & TCP_TCB_RCV_URGENT_ISOK)
  {
    int nNormalByteCount,nUrgentByteCount;
    nNormalByteCount = pTCB->n32RcvNextSeq - pTCB->n32RcvUrgentSeq - 1;

    if (nNormalByteCount >= 0)			// urgent boundary in buffer
    {
      nUrgentByteCount = pTCB->u32RcvBufCount - nNormalByteCount;
      if ((int)uLen >= nUrgentByteCount)
      {
        uLen = nUrgentByteCount;
        pTCB->u16Flags &= ~TCP_TCB_RCV_URGENT_ISOK;
      }
    }
  }

  uByteCount = 0;
  while (pTCB->u32RcvBufCount > 0 && uByteCount < uLen)
  {
    *pu8SrcBuf++ = pTCB->pu8rcvBuf[pTCB->u32RcvBufStart];
    --pTCB->u32RcvBufCount;
    if (++pTCB->u32RcvBufStart >= pTCB->u32RcvBufSize)
      pTCB->u32RcvBufStart = 0;
    ++uByteCount;
  }

  if (pTCB->u32RcvBufCount == 0)
    pTCB->u16Flags &= ~TCP_TCB_PUSH;

  // open the receive window, if it's closed and we've made enough space to fit a segment.
  if (COMPARE_SEQUENCE(pTCB->n32CurAdvWinSeq,pTCB->n32RcvNextSeq) <= 0 && tcpRcvWindow(pTCB))
  {
    pTCB->u16Flags |= TCP_TCB_NEED_OUTPUT;
    tcpKick(pTCB);
  }

  return uByteCount;
}

//**************************************************************************************************
//	tcpReadBlock
//	Reads max u32Len bytes from tcp stream or less and places it into the given buffer.
//	The function does not block. If urgent data arrives, it returns a result code whichs tells
//	the application that urgent data is waiting to be consumed. The application shouls therefore
//	call this function until it returns a resul code which tells the application that the urgent
//	all urgent data has arrived. After this, the application needs to call this function once again.
//	This time, the function returns the amount of data right to the end of the urgent data.
//**************************************************************************************************
static	int									// num of bytes read or TCPE_URGENTMODE/TCPE_NORMALMODE
tcpReadBlock(
  SocketPtr 		pSocket,				// socket
  unsigned char	*pu8SrcBuf,				// user buffer
  unsigned		uLen)					// size of buffer
{
  TCBPtr	pTCB = pSocket->pTCB;
  int		nCount;

  if (pTCB->setState != TCP_STATE_ESTABLISHED &&  pTCB->setState != TCP_STATE_CLOSEWAIT)
    return TCP_GENERAL_ERROR;

retry:
  semWait(&pTCB->recRcvSemaphore);
  semWait(&pTCB->recMutex);
  if (pTCB->nError != 0)
  {
    tcpWakeup(pTCB,TCP_READERS);
    semSignal(&pTCB->recMutex);
    return pTCB->nError;
  }

  if (pTCB->u16Flags & TCP_TCB_RCV_URGENT_ISOK)
  {
    if (!pSocket->bUrgentMode)
    {
      pSocket->bUrgentMode = TRUE;
      nCount = TCP_URGENTMODE;
    }
    else
      nCount = tcpGetData(pTCB,pu8SrcBuf,uLen);
  }
  else
  {
    if (pSocket->bUrgentMode)
    {
      pSocket->bUrgentMode = FALSE;
      nCount = TCP_NORMALMODE;
    }
    else if (uLen > pTCB->u32RcvBufCount && pTCB->u16Flags & TCP_TCB_BUFFER &&
             (pTCB->u16Flags & (TCP_TCB_PUSH | TCP_TCB_RCV_DONE)) == 0)
    {
      semSignal(&pTCB->recMutex);
      goto retry;
    }
    else
      nCount = tcpGetData(pTCB,pu8SrcBuf,uLen);
  }
  tcpWakeup(pTCB,TCP_READERS);
  semSignal(&pTCB->recMutex);

  return nCount;
}
//**************************************************************************************************
//	tcpRead
//	reads urgent and normal data to TCP buffers.
//**************************************************************************************************
int										// num of bytes read or TCPE_URGENTMODE/TCPE_NORMALMODE
tcpRead(									// reads data
  SocketPtr 		pSocket,				// socket ptr
  unsigned char	*pu8SrcBuf,				// source data buffer
  unsigned long	u32Len)					// length of source data buffer
{
  long int nResult=0L;
  long int nBytesRead=0;

  while (nBytesRead<u32Len)
  {
    // are remaining data to read > TCP_RCV_BUF_SIZE
    if (u32Len-nBytesRead>TCP_RCV_BUF_SIZE)
      nResult = tcpReadBlock(pSocket,pu8SrcBuf+nBytesRead,TCP_RCV_BUF_SIZE);
    else
      nResult = tcpReadBlock(pSocket,pu8SrcBuf+nBytesRead,u32Len-nBytesRead);
    if (nResult<0L)
      return nResult;
    else
      nBytesRead+=nResult;

    if ((pSocket->pTCB->u16Flags & TCP_TCB_BUFFER) == 0)
      break;
    else if (nBytesRead<u32Len)
      tmYield();
  };

  return nBytesRead;
}
//**************************************************************************************************
//	tcpListenQueue
//	sets the listen queue size for a TCP pseudo device
//**************************************************************************************************
static void
tcpListenQueue(
  TCBPtr	pTCB,
  int		nListenQueueSize)
{
  pTCB->nListenQueueSize = nListenQueueSize;
  if (pTCB->setType == TCP_TYPE_SERVER)
  {
    ptInit(&pTCB->recListenQueue,pTCB->nListenQueueSize);
  }
}

//**************************************************************************************************
//	tcpUserOption
//	set/clears TCP user option flags
//**************************************************************************************************
static int
tcpUserOption(
  TCBPtr			pTCB,
  int				nOperation,
  unsigned short 	u16Flags)
{
  if (u16Flags & ~(TCP_TCB_DELACK | TCP_TCB_BUFFER))
    return TCP_GENERAL_ERROR;
  if (nOperation == TCP_CONTROL_SET_USER_OPTION)
    pTCB->u16Flags |= u16Flags;
  else
    pTCB->u16Flags &= ~u16Flags;
  return TCP_OK;
}

//**************************************************************************************************
//	tcpControl
//	controls function for TCP pseudo-devices
//**************************************************************************************************
int										// Error code, or TCP_NO_ERR
tcpControl(
  SocketPtr 	pSocket,					// socket
  int			nOperation,					// control operation
  void		*arg,						// argument 1 of operation
  void 		*arg2,						// argument 2 of operation
  void		**pResult)					// returns a new socket
{
  TCBPtr			pQueuedTCB;
  TCBPtr			pTCB = pSocket->pTCB;
  int				nError = TCP_NO_ERR;
  unsigned long	u32;

  if (pTCB == NULL)
    return TCP_GENERAL_ERROR;
  semWait(&pTCB->recMutex);
  switch(nOperation)
  {
  case TCP_CONTROL_ACCEPT:
    if (pTCB->setType != TCP_TYPE_SERVER)
    {
      nError = TCP_GENERAL_ERROR;
      break;
    }

    semSignal(&pTCB->recMutex);
    ptReceive(&pTCB->recListenQueue,&pQueuedTCB,&u32);
    *pResult = pQueuedTCB->pDevice;

    return TCP_NO_ERR;
  case TCP_CONTROL_LISTEN_QUEUE:
    tcpListenQueue(pTCB,(int)arg);
    break;
  case TCP_CONTROL_STATUS:
    tcpStatistics(pTCB,(TCPStatisticsPtr)arg);
    break;
  case TCP_CONTROL_SET_USER_OPTION:
  case TCP_CONTROL_CLR_USER_OPTION:
    nError = tcpUserOption(pTCB,nOperation,(unsigned short)arg);
    break;
  case TCP_CONTROL_SEND_URGENT:
    semSignal(&pTCB->recMutex);
    return tcpWrite(pSocket,(unsigned char *)arg,(unsigned)arg,TRUE);
  default:
    nError = TCP_GENERAL_ERROR;
    break;
  }

  semSignal(&pTCB->recMutex);
  return nError;
}

//**************************************************************************************************
//	tcpStatistics
//	return status information for a TCP device
//**************************************************************************************************
void
tcpStatistics(
  TCBPtr				pTCB,				// tcb, for which the statistic is requested
  TCPStatisticsPtr 	pStatistics)		// pointer to statistic data structure
{
  pStatistics->setType = pTCB->setType;
  pStatistics->u32InputError = gu32TcpInErrors;
  pStatistics->u32AttempFailed = gu32TcpAttemptFails;
  pStatistics->u32TcpCurrEstab = gu32TcpCurrEstab;
  pStatistics->u32TcpOutSegs = gu32TcpOutSegs;
  pStatistics->u32TcpOutRsts = gu32TcpOutRsts;
  switch (pTCB->setType)
  {
  case TCP_TYPE_SERVER:
    pStatistics->uon.recServer.u32PassiveOpens = gu32TcpPassiveOpens;
    pStatistics->uon.recServer.u32Connections = gu32TcpActiveOpens;
    pStatistics->uon.recServer.u32Resets = gu32TcpEstabResets;
    pStatistics->uon.recServer.u32Retransmissions = gu32TcpRetransSegs;
    break;
  case TCP_TYPE_CONNECTION:
    pStatistics->uon.recConnection.netParams = pTCB->netParams;
    pStatistics->uon.recConnection.u32ReceiveWindow = pTCB->u32RcvBufSize - pTCB->u32RcvBufCount;
    pStatistics->uon.recConnection.u32SendWindow = pTCB->u32SndWinSize;
    pStatistics->uon.recConnection.setState = pTCB->setState;
    pStatistics->uon.recConnection.n32SndUnacked = pTCB->n32SndUnacked;
    break;
  default:
    break;
  }
}
