/* 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: /usr/drwho/vault/cvs/topsy/Topsy/Net/TCP/nettcpSocket.c,v $ Author(s): Affiliation: ETH Zuerich, TIK Version: $Revision: 1.2 $ Creation Date: Last Date of Change: $Date: 2000/04/03 17:45:30 $ by: $Author: gfa $ $Log: nettcpSocket.c,v $ 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; 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 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 (nBytesWrittenn32RcvNextSeq - 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 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 (nBytesReadnListenQueueSize = 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; } }