/* 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/nettcpTCP.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: nettcpTCP.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 */ // This module handles the whole tcp protocol standard. // It's derived from the implementation used by Xinu, written // by Douglas E. Comer and David L. Stevens. // ftp://ftp.cs.purdue.edu/pub/comer/ #define MAX_ETHER_IP_HEADER_LEN 40 #include "nettcpTimer.h" #include "nettcpTCP.h" #include "nettcpMain.h" #include "nettcpSocket.h" #include #include //************************************************************************************************** // type definitions //************************************************************************************************** typedef struct { NetBufHdr netbuf; IPAddressUon uonSrcAddr; IPAddressUon uonDstAddr; unsigned char u8Zero; unsigned char u8Protocol; unsigned short u16Len; } TCPPseudoHdrRec; //************************************************************************************************** // Prototypes //************************************************************************************************** void tcpPacketDump(TCPSegPtr pTCPSeg,char *pn8Info,int nNum); static void tcpHeaderNet2Host(TCPSegPtr pTCPSeg); static void tcpHeaderHost2Net(TCPSegPtr pTCPSeg); static TCBPtr tcpDemux(PacketPtr pPacket); static unsigned short tcpCheckSum(NetBuf buf,NetAttrs attrs,int len); static int tcpRoundTripTime(TCBPtr pTCB); static int tcpFragmentInsert(TCBPtr pTCB,long nSeq,unsigned uDataLen); static int tcpFragmentJoin(TCBPtr pTCB,PacketPtr pPacket,unsigned uDataLen); static Boolean tcpIsOK(TCBPtr pTCB,PacketPtr pPacket); static int tcpSndMaxSegSize(TCBPtr pTCB,PacketPtr pPacket,unsigned char *pu8Option); static int tcpOptions(TCBPtr pTCB,PacketPtr pPacket); static int tcpReset(PacketPtr pPacket); static int tcpSndWindow(TCBPtr pTCB,PacketPtr pPacket); static int tcpAbort(TCBPtr pTCB,int nError); static int tcpHowMuch(TCBPtr pTCB); static int tcpSendLen(TCBPtr pTCB,Boolean bRetransmit,unsigned int *puOffset); static void tcpRcvMaxSegSize(TCBPtr pTCB,TCPSegPtr pTCPSeg); static int tcpSend(TCBPtr pTCB,Boolean bRetransmit); static int tcpReTransmit(TCBPtr pTCB,unsigned long u32Event); static int tcpTransmit(TCBPtr pTCB,unsigned long u32Event); static int tcpIdle(TCBPtr pTCB,unsigned long u32Event); static int tcpPersist(TCBPtr pTCB,unsigned long u32Event); static void tcpOutDispatcher(TCBPtr pTCB,unsigned long u32Event); static int tcpOutputState(TCBPtr pTCB,int nAcked); static int tcpAcked(TCBPtr pTCB,PacketPtr pPacket); static int tcpAckIt(TCBPtr pTCB,PacketPtr pPacket); static int tcpClosed(TCBPtr pTCB,PacketPtr pPacket); static int tcpWindowInit(TCBPtr pTCB,TCBPtr pNewTCB,PacketPtr pPacket); static int tcpDoData(TCBPtr pTCB,PacketPtr pPacket,long n32First,unsigned uDataLen); static int tcpData(TCBPtr pTCB,PacketPtr pPacket); static int tcpDoListen(TCBPtr pTCB,PacketPtr pPacket); static int tcpKillTimers(TCBPtr pTCB); static int tcpSynSent(TCBPtr pTCB,PacketPtr pPacket); static int tcpSynReceived(TCBPtr pTCB,PacketPtr pPacket); static int tcpEstablished(TCBPtr pTCB,PacketPtr pPacket); static int tcpWait(TCBPtr pTCB); static int tcpFin1(TCBPtr pTCB,PacketPtr pPacket); static int tcpFin2(TCBPtr pTCB,PacketPtr pPacket); static int tcpCloseWait(TCBPtr pTCB,PacketPtr pPacket); static int tcpLastAck(TCBPtr pTCB,PacketPtr pPacket); static int tcpClosing(TCBPtr pTCB,PacketPtr pPacket); static int tcpTimeWait(TCBPtr pTCB,PacketPtr pPacket); static void tcpInputStateDispatcher(TCBPtr pTCB,PacketPtr pPacket); static long tcpInitialSequence(void); //************************************************************************************************** // tcp globals //************************************************************************************************** extern SemaphoreRec gTCBLinkedListMutex; extern PortRec gOutputPort; TCBPtr gTCPTCBList = NULL; // head of TCB linked list // statistics unsigned long gu32TcpInErrors = 0; unsigned long gu32TcpPassiveOpens = 0; unsigned long gu32TcpAttemptFails = 0; unsigned long gu32TcpCurrEstab = 0; unsigned long gu32TcpEstabResets = 0; unsigned long gu32TcpOutSegs = 0; unsigned long gu32TcpOutRsts = 0; unsigned long gu32TcpRetransSegs = 0; unsigned long gu32TcpActiveOpens = 0; //************************************************************************************************** // tcpAlloc //************************************************************************************************** unsigned char *tcpAlloc(unsigned int nSize) { int nRes; NetBuf pBuf; nRes=netbufAlloc(&pBuf,nSize); if (nRes>0) { if (pBuf->next!=NULL) { netbufFree(pBuf); return NULL; } return ((unsigned char *)pBuf)+sizeof(NetBufHdr); } return NULL; } //************************************************************************************************** // tcpFree //************************************************************************************************** void tcpFree(unsigned char *pBuf) { netbufFree((NetBuf)(pBuf-sizeof(NetBufHdr))); } //************************************************************************************************** // tcpGetIPAddr // retrieves either the source or destination ip addr from the net attrs //************************************************************************************************** void tcpPacketDump( TCPSegPtr pTCPSeg, // pointer to tcp segmen char *pn8Info, // additional info as c string int nNum) // additional info as number { if (((*(short*)pn8Info==*(short*)"SR") && (TCP_DUMP_MASK & TCP_DUMP_SEND_RESET)) || ((*(short*)pn8Info==*(short*)"SN") && (TCP_DUMP_MASK & TCP_DUMP_SEND_DATA)) || ((*(short*)pn8Info==*(short*)"SA") && (TCP_DUMP_MASK & TCP_DUMP_SEND_ACK)) || ((*(short*)pn8Info==*(short*)"RN") && (TCP_DUMP_MASK & TCP_DUMP_RECEIVE))) { ThreadId myThread,parentThread; tmGetInfo(SELF,&myThread,&parentThread); netdbgPrintf(NETDEBUG_TCP,(char*)pn8Info); netdbgPrintf(NETDEBUG_TCP," id:%d",myThread); netdbgPrintf(NETDEBUG_TCP," s:%d",pTCPSeg->u16SrcPort); netdbgPrintf(NETDEBUG_TCP," d:%d",pTCPSeg->u16DstPort); netdbgPrintf(NETDEBUG_TCP," seq:%d",pTCPSeg->n32Seq); netdbgPrintf(NETDEBUG_TCP," ack:%d",pTCPSeg->n32AckSeq); netdbgPrintf(NETDEBUG_TCP," off:%d",pTCPSeg->u8Offset); if (pTCPSeg->u8Code & TCP_CODE_URG) netdbgPrintf(NETDEBUG_TCP," URG"); if (pTCPSeg->u8Code & TCP_CODE_ACK) netdbgPrintf(NETDEBUG_TCP," ACK"); if (pTCPSeg->u8Code & TCP_CODE_PSH) netdbgPrintf(NETDEBUG_TCP," PSH"); if (pTCPSeg->u8Code & TCP_CODE_RST) netdbgPrintf(NETDEBUG_TCP," RST"); if (pTCPSeg->u8Code & TCP_CODE_SYN) netdbgPrintf(NETDEBUG_TCP," SYN"); if (pTCPSeg->u8Code & TCP_CODE_FIN) netdbgPrintf(NETDEBUG_TCP," FIN"); netdbgPrintf(NETDEBUG_TCP," win:%d",pTCPSeg->u16Window); netdbgPrintf(NETDEBUG_TCP," chk:%d",pTCPSeg->u16CheckSum); netdbgPrintf(NETDEBUG_TCP," urp:%d",pTCPSeg->u16Urgent); netdbgPrintf(NETDEBUG_TCP," n:%d",nNum); netdbgPrintf(NETDEBUG_TCP,"\n"); } } //************************************************************************************************** // tcpGetIPAddr // retrieves either the source or destination ip addr from the net attrs //************************************************************************************************** int // result code tcpGetIPAddr( NetAttrs attrs, // net attributes IPAddrSet setType, // source or dest address IPAddressPtr pIpAddr) // -> returns ip address { unsigned short tmp; switch(setType) { case TCP_IP_SRC_ADDR: { if(!netattrGet(attrs,NETATTR_IP_FROM_0,&tmp)) return TCP_GENERAL_ERROR; pIpAddr->recAddr.u8Addr0 = tmp; if(!netattrGet(attrs,NETATTR_IP_FROM_1,&tmp)) return TCP_GENERAL_ERROR; pIpAddr->recAddr.u8Addr1 = tmp; if(!netattrGet(attrs,NETATTR_IP_FROM_2,&tmp)) return TCP_GENERAL_ERROR; pIpAddr->recAddr.u8Addr2 = tmp; if(!netattrGet(attrs,NETATTR_IP_FROM_3,&tmp)) return TCP_GENERAL_ERROR; pIpAddr->recAddr.u8Addr3 = tmp; } break; case TCP_IP_DST_ADDR: { if(!netattrGet(attrs,NETATTR_IP_TO_0,&tmp)) return TCP_GENERAL_ERROR; pIpAddr->recAddr.u8Addr0 = tmp; if(!netattrGet(attrs,NETATTR_IP_TO_1,&tmp)) return TCP_GENERAL_ERROR; pIpAddr->recAddr.u8Addr1 = tmp; if(!netattrGet(attrs,NETATTR_IP_TO_2,&tmp)) return TCP_GENERAL_ERROR; pIpAddr->recAddr.u8Addr2 = tmp; if(!netattrGet(attrs,NETATTR_IP_TO_3,&tmp)) return TCP_GENERAL_ERROR; pIpAddr->recAddr.u8Addr3 = tmp; } break; default: break; } return TCP_OK; } //************************************************************************************************** // tcpSetIPAddr // puts either the source or destination ip addr into the net attrs //************************************************************************************************** int // result code tcpSetIPAddr( NetAttrs attrs, IPAddrSet setType, // source or dest address IPAddressPtr pIpAddr) // ip address { switch(setType) { case TCP_IP_SRC_ADDR: { if(!netattrSet(attrs,NETATTR_IP_FROM_0,pIpAddr->recAddr.u8Addr0)) return TCP_GENERAL_ERROR; if(!netattrSet(attrs,NETATTR_IP_FROM_1,pIpAddr->recAddr.u8Addr1)) return TCP_GENERAL_ERROR; if(!netattrSet(attrs,NETATTR_IP_FROM_2,pIpAddr->recAddr.u8Addr2)) return TCP_GENERAL_ERROR; if(!netattrSet(attrs,NETATTR_IP_FROM_3,pIpAddr->recAddr.u8Addr3)) return TCP_GENERAL_ERROR; } break; case TCP_IP_DST_ADDR: { if(!netattrSet(attrs,NETATTR_IP_TO_0,pIpAddr->recAddr.u8Addr0)) return TCP_GENERAL_ERROR; if(!netattrSet(attrs,NETATTR_IP_TO_1,pIpAddr->recAddr.u8Addr1)) return TCP_GENERAL_ERROR; if(!netattrSet(attrs,NETATTR_IP_TO_2,pIpAddr->recAddr.u8Addr2)) return TCP_GENERAL_ERROR; if(!netattrSet(attrs,NETATTR_IP_TO_3,pIpAddr->recAddr.u8Addr3)) return TCP_GENERAL_ERROR; } break; default: break; } return TCP_OK; } //************************************************************************************************** // tcpHeaderNet2Host // transfers byte order of header from host to network //************************************************************************************************** static void tcpHeaderNet2Host( TCPSegPtr pTCPSeg) // pointer to tcp segment { pTCPSeg->u16SrcPort = ntohs(pTCPSeg->u16SrcPort); pTCPSeg->u16DstPort = ntohs(pTCPSeg->u16DstPort); pTCPSeg->n32Seq = ntohl(pTCPSeg->n32Seq); pTCPSeg->n32AckSeq = ntohl(pTCPSeg->n32AckSeq); pTCPSeg->u16Window = ntohs(pTCPSeg->u16Window); pTCPSeg->u16Urgent = ntohs(pTCPSeg->u16Urgent); } //************************************************************************************************** // tcpHeaderHost2Net // transfers byte order of header from network to host //************************************************************************************************** static void tcpHeaderHost2Net( TCPSegPtr pTCPSeg) // pointer to tcp segment { pTCPSeg->u16SrcPort = htons(pTCPSeg->u16SrcPort); pTCPSeg->u16DstPort = htons(pTCPSeg->u16DstPort); pTCPSeg->n32Seq = htonl(pTCPSeg->n32Seq); pTCPSeg->n32AckSeq = htonl(pTCPSeg->n32AckSeq); pTCPSeg->u16Window = htons(pTCPSeg->u16Window); pTCPSeg->u16Urgent = htons(pTCPSeg->u16Urgent); } //************************************************************************************************** // tcpDemux // does TCP port demultiplexing //************************************************************************************************** static TCBPtr // returns tcb which belongs to the given tcp packet or NULL tcpDemux( PacketPtr pPacket) // tcp packet { TCBPtr pCurrentTCB; TCBPtr pListenTCB; IPAddressUon uonSegSrcIpAddr,uonSegDstIpAddr; // gets source and destination ip address of segment if (tcpGetIPAddr(pPacket->netAttrs,TCP_IP_SRC_ADDR,&uonSegSrcIpAddr) != TCP_OK) return NULL; if (tcpGetIPAddr(pPacket->netAttrs,TCP_IP_DST_ADDR,&uonSegDstIpAddr) != TCP_OK) return NULL; semWait(&gTCBLinkedListMutex); pCurrentTCB = gTCPTCBList; pListenTCB = NULL; while (pCurrentTCB != NULL) { unsigned short u16TCBSrcPort,u16TCBDstPort; IPAddressUon uonTCBSrcIpAddr,uonTCBDstIpAddr; // gets source and destination port of tcb if (!netattrGet(pCurrentTCB->netParams,NETATTR_TCP_FROM,&u16TCBSrcPort)) return NULL; if (!netattrGet(pCurrentTCB->netParams,NETATTR_TCP_TO,&u16TCBDstPort)) return NULL; // gets source and destination ip address of tcb if (tcpGetIPAddr(pCurrentTCB->netParams,TCP_IP_SRC_ADDR,&uonTCBSrcIpAddr) != TCP_OK) return NULL; if (tcpGetIPAddr(pCurrentTCB->netParams,TCP_IP_DST_ADDR,&uonTCBDstIpAddr) != TCP_OK) return NULL; if ((pPacket->pTCPSeg->u16DstPort == u16TCBSrcPort) && (pPacket->pTCPSeg->u16SrcPort == u16TCBDstPort) && (uonSegSrcIpAddr.u32Addr == uonTCBDstIpAddr.u32Addr) && (uonSegDstIpAddr.u32Addr == uonTCBSrcIpAddr.u32Addr) ) break; if ((pCurrentTCB->setState == TCP_STATE_LISTEN) && (pPacket->pTCPSeg->u16DstPort == u16TCBSrcPort) ) pListenTCB = pCurrentTCB; pCurrentTCB = (TCBPtr)pCurrentTCB->pNext; } if ((pCurrentTCB == NULL) && (pPacket->pTCPSeg->u8Code & TCP_CODE_SYN)) pCurrentTCB = pListenTCB; semSignal(&gTCBLinkedListMutex); if (pCurrentTCB == NULL) return NULL; semWait(&pCurrentTCB->recMutex); return pCurrentTCB; } //************************************************************************************************** // tcpCheckSum // calculates tcp checksum. //************************************************************************************************** static unsigned short tcpCheckSum( NetBuf buf, NetAttrs attrs, int len) // data length { TCPPseudoHdrRec recTcpPseudoHdr; // Constructs the Pseudo-header by adding a FALSE-NetBuf recTcpPseudoHdr.netbuf.start = NETBUF_HEADERSIZE; recTcpPseudoHdr.netbuf.end = recTcpPseudoHdr.netbuf.start + sizeof(TCPPseudoHdrRec) - NETBUF_HEADERSIZE; recTcpPseudoHdr.netbuf.next = buf; // source address if (tcpGetIPAddr(attrs,TCP_IP_SRC_ADDR,&recTcpPseudoHdr.uonSrcAddr) != TCP_OK) return TCP_GENERAL_ERROR; // dest address if (tcpGetIPAddr(attrs,TCP_IP_DST_ADDR,&recTcpPseudoHdr.uonDstAddr) != TCP_OK) return TCP_GENERAL_ERROR; recTcpPseudoHdr.u8Zero = 0; recTcpPseudoHdr.u8Protocol = NETIP_PROTOCOL_TCP; recTcpPseudoHdr.u16Len = htons(len); // calculate checksum return netipChecksum(&recTcpPseudoHdr.netbuf,len + sizeof(TCPPseudoHdrRec)-NETBUF_HEADERSIZE); } //************************************************************************************************** // tcpRoundTripTime //************************************************************************************************** static int tcpRoundTripTime( TCBPtr pTCB) { int nRoundTripTime; int nDelta; nRoundTripTime = tcp_tmClear(&gOutputPort,pTCB,RETRANSMIT); if (nRoundTripTime != TIMER_FAILED && pTCB->setOutState != TCP_OUT_STATE_RETRANSMIT) { if (pTCB->nSmoothedRoundTripTime == 0) pTCB->nSmoothedRoundTripTime = nRoundTripTime << 3; nDelta = nRoundTripTime - (pTCB->nSmoothedRoundTripTime >> 3); pTCB->nSmoothedRoundTripTime += nDelta; if (nDelta < 0) nDelta -=nDelta; pTCB->nRoundTripDeviationEst += nDelta - (pTCB->nRoundTripDeviationEst >> 2); pTCB->nRetransmitTimeout = ((pTCB->nSmoothedRoundTripTime >> 2) + pTCB->nRoundTripDeviationEst) >> 1; if (pTCB->nRetransmitTimeout < TCP_MINRXT) pTCB->nRetransmitTimeout = TCP_MINRXT; } if (pTCB->u32CongestionWinSize < pTCB->u32SlowStartThreshold) pTCB->u32CongestionWinSize += pTCB->u32SndMaxSegSize; else pTCB->u32CongestionWinSize += (pTCB->u32SndMaxSegSize * pTCB->u32SndMaxSegSize) / pTCB->u32CongestionWinSize; return TCP_OK; } //************************************************************************************************** // tcpFragmentInsert // add a new TCP segment fragment to a TCB sequence queue //************************************************************************************************** static int tcpFragmentInsert( TCBPtr pTCB, long nSeq, unsigned uDataLen) { TCPFragmentPtr pFragment; if (uDataLen == 0) return TCP_OK; pFragment=(TCPFragmentPtr)tcpAlloc(sizeof(TCPFragmentRec)); if (pFragment == NULL) return TCP_GENERAL_ERROR; pFragment->n32Seq = nSeq; pFragment->nLen = uDataLen; if (pTCB->nRcvSegFragQueue < 0) pTCB->nRcvSegFragQueue = pqNewq(TCP_FRAGMENT_QUEUE_SIZE); if (pqEnq(pTCB->nRcvSegFragQueue,pFragment,-pFragment->n32Seq) < 0) tcpFree((unsigned char *)pFragment); return TCP_OK; } //************************************************************************************************** // tcpFragmentJoin // joins TCP fragments //************************************************************************************************** static int tcpFragmentJoin( TCBPtr pTCB, PacketPtr pPacket, unsigned uDataLen) { TCPFragmentPtr pFragment = NULL; int nNewCount; pTCB->n32RcvNextSeq += uDataLen; pTCB->u32RcvBufCount += uDataLen; if (pTCB->n32RcvNextSeq == pTCB->n32FINSeqNum) goto alldone; if ((pTCB->n32RcvNextSeq - pTCB->n32PUSHSeqNum) >= 0) { pPacket->pTCPSeg->u8Code |= TCP_CODE_PSH; pTCB->n32PUSHSeqNum = 0; } if (pTCB->nRcvSegFragQueue < 0) return TCP_OK; pFragment = (TCPFragmentPtr)pqDeq(pTCB->nRcvSegFragQueue); if (pFragment == NULL) return TCP_OK; while ((pFragment->n32Seq - pTCB->n32RcvNextSeq) <= 0) { nNewCount = pFragment->nLen - (pTCB->n32RcvNextSeq - pFragment->n32Seq); if (nNewCount > 0) { pTCB->n32RcvNextSeq += nNewCount; pTCB->u32RcvBufCount += nNewCount ; } if (pTCB->n32RcvNextSeq == pTCB->n32FINSeqNum) goto alldone; if ((pTCB->n32RcvNextSeq - pTCB->n32PUSHSeqNum) >= 0) { pPacket->pTCPSeg->u8Code |= TCP_CODE_PSH; pTCB->n32PUSHSeqNum = 0; } tcpFree((unsigned char *)pFragment); pFragment = (TCPFragmentPtr)pqDeq(pTCB->nRcvSegFragQueue); if (pFragment == NULL) { pqFreeq(pTCB->nRcvSegFragQueue); pTCB->nRcvSegFragQueue = EMPTY; return TCP_OK; } } pqEnq(pTCB->nRcvSegFragQueue,pFragment,-pFragment->n32Seq); /* got one too many */ return TCP_OK; alldone: if (pFragment != NULL) tcpFree((unsigned char *)pFragment); pFragment = (TCPFragmentPtr)pqDeq(pTCB->nRcvSegFragQueue); while (pFragment != NULL) { tcpFree((unsigned char *)pFragment); pFragment = (TCPFragmentPtr)pqDeq(pTCB->nRcvSegFragQueue); } pqFreeq(pTCB->nRcvSegFragQueue); pTCB->nRcvSegFragQueue = EMPTY; pPacket->pTCPSeg->u8Code |= TCP_CODE_FIN; return TCP_OK; } //************************************************************************************************** // tcpIsOK // determines, whether a received packet is acceptable //************************************************************************************************** static Boolean // returns TRUE if this is a "good" packet tcpIsOK( TCBPtr pTCB, PacketPtr pPacket) { long n32WindowLast; long n32SegLast; int nDataLen; int nRcvWindow; Boolean bRv; if (pTCB->setState < TCP_STATE_SYNRCVD) return TRUE; nDataLen = netbufLen(pPacket->segBuf) - TCP_HEADER_LEN(pPacket->pTCPSeg); // adds SYN and FIN if (pPacket->pTCPSeg->u8Code & TCP_CODE_SYN) ++nDataLen; if (pPacket->pTCPSeg->u8Code & TCP_CODE_FIN) ++nDataLen; nRcvWindow = pTCB->u32RcvBufSize - pTCB->u32RcvBufCount; if ((nRcvWindow == 0) && (nDataLen == 0)) return (pPacket->pTCPSeg->n32Seq == pTCB->n32RcvNextSeq); n32WindowLast = pTCB->n32RcvNextSeq + nRcvWindow - 1; bRv = (pPacket->pTCPSeg->n32Seq - pTCB->n32RcvNextSeq) >= 0 && (pPacket->pTCPSeg->n32Seq - n32WindowLast) <= 0; if (nDataLen == 0) return bRv; n32SegLast = pPacket->pTCPSeg->n32Seq + nDataLen - 1; bRv |= (n32SegLast - pTCB->n32RcvNextSeq) >= 0 && (n32SegLast - n32WindowLast) <= 0; // If no window, strip data but keep ACK, RST and URG if (n32WindowLast == 0) NETBUF_SETLEN(pPacket->segBuf,TCP_HEADER_LEN(pPacket->pTCPSeg)); return bRv; } //************************************************************************************************** // tcpSndMaxSegSize // sets sender MSS from option in incoming segment //************************************************************************************************** static int tcpSndMaxSegSize( TCBPtr pTCB, PacketPtr pPacket, unsigned char *pu8Option) { unsigned uLen,uMSS; uLen = *++pu8Option; ++pu8Option; // skips length field if ((pPacket->pTCPSeg->u8Code & TCP_CODE_SYN) == 0) return uLen; switch (uLen-2) // subtracts kind & len { case sizeof(char): uMSS = *pu8Option; break; case sizeof(short): uMSS = ntohs(*(unsigned short*)pu8Option); break; case sizeof(long): uMSS = ntohl(*(unsigned long*)pu8Option); break; default: uMSS = pTCB->u32SndMaxSegSize; break; } uMSS -= TCP_MIN_HDR_LEN; // save just the data buffer size if (pTCB->u32SndMaxSegSize != 0) pTCB->u32SndMaxSegSize = min(uMSS,pTCB->u32SndMaxSegSize); else pTCB->u32SndMaxSegSize = uMSS; return uLen; } //************************************************************************************************** // tcpOptions // handles TCP options for an inbound segment //************************************************************************************************** static int tcpOptions( TCBPtr pTCB, PacketPtr pPacket) { unsigned char *pu8Options; unsigned char *pu8OptionEnd; unsigned uLen; unsigned uToCopy; int nSrcOffset; int nDstPos; if (TCP_HEADER_LEN(pPacket->pTCPSeg) == TCP_MIN_HDR_LEN) return TCP_OK; pu8Options = pPacket->pTCPSeg->aryData; pu8OptionEnd = (unsigned char*)pPacket->pTCPSeg + TCP_HEADER_LEN(pPacket->pTCPSeg); do { switch(*pu8Options) { case TCP_OPTION_NOOP: pu8Options++; // falls through case TCP_OPTION_EOL: break; case TCP_OPTION_MAXSEGSIZE: pu8Options += tcpSndMaxSegSize(pTCB,pPacket,pu8Options); break; default: pu8Options++; // skips option code if (*pu8Options > 0 && *pu8Options <= pu8OptionEnd - pu8Options - 1) pu8Options += *pu8Options - 1; else pu8Options = pu8OptionEnd; break; } } while (*pu8Options != TCP_OPTION_EOL && pu8Options < pu8OptionEnd); // deletes the options uLen = netbufLen(pPacket->segBuf) - TCP_HEADER_LEN(pPacket->pTCPSeg); nSrcOffset = TCP_HEADER_LEN(pPacket->pTCPSeg) - TCP_MIN_HDR_LEN; nDstPos = 0; uToCopy = uLen; while (uToCopy-- > 0) { pPacket->pTCPSeg->aryData[nDstPos++] = pPacket->pTCPSeg->aryData[nSrcOffset++]; } NETBUF_SETLEN(pPacket->segBuf,TCP_MIN_HDR_LEN + uLen); pPacket->pTCPSeg->u8Offset = TCP_HDR_OFFSET; return TCP_OK; } //************************************************************************************************** // tcpreset // generates a reset in response to a bad packet //************************************************************************************************** static int tcpReset( PacketPtr pPacket) { TCPSegPtr pTCPOutSeg; NetBuf buf; NetAttrs attrs; unsigned short u16Port; IPAddressUon uonSrcIpAddr; IPAddressUon uonDstIpAddr; int nDataLen; if (pPacket->pTCPSeg->u8Code & TCP_CODE_RST) return TCP_OK; if (netbufAlloc(&buf,TCP_MIN_HDR_LEN+MAX_ETHER_IP_HEADER_LEN) == 0) return TCP_GENERAL_ERROR; buf->start += MAX_ETHER_IP_HEADER_LEN; if (netattrNew(&attrs) == 0) return TCP_GENERAL_ERROR; if (tcpGetIPAddr(pPacket->netAttrs,TCP_IP_SRC_ADDR,&uonSrcIpAddr) != TCP_OK) return TCP_GENERAL_ERROR; if (tcpGetIPAddr(pPacket->netAttrs,TCP_IP_DST_ADDR,&uonDstIpAddr) != TCP_OK) return TCP_GENERAL_ERROR; if (tcpSetIPAddr(attrs,TCP_IP_SRC_ADDR,&uonDstIpAddr) != TCP_OK) return TCP_GENERAL_ERROR; if (tcpSetIPAddr(attrs,TCP_IP_DST_ADDR,&uonSrcIpAddr) != TCP_OK) return TCP_GENERAL_ERROR; pTCPOutSeg = (TCPSegPtr)NETBUF_DATA(buf); if (!netattrGet(pPacket->netAttrs,NETATTR_TCP_TO,&u16Port)) return NULL; if (!netattrSet(attrs,NETATTR_TCP_FROM,u16Port)) return NULL; pTCPOutSeg->u16SrcPort = pPacket->pTCPSeg->u16DstPort; if (!netattrGet(pPacket->netAttrs,NETATTR_TCP_FROM,&u16Port)) return NULL; if (!netattrSet(attrs,NETATTR_TCP_TO,u16Port)) return NULL; pTCPOutSeg->u16DstPort = pPacket->pTCPSeg->u16SrcPort; if (pPacket->pTCPSeg->u8Code & TCP_CODE_ACK) { pTCPOutSeg->n32Seq = pPacket->pTCPSeg->n32AckSeq; pTCPOutSeg->u8Code = TCP_CODE_RST; } else { pTCPOutSeg->n32Seq = 0; pTCPOutSeg->u8Code = TCP_CODE_RST | TCP_CODE_ACK; } nDataLen = netbufLen(pPacket->segBuf) - TCP_HEADER_LEN(pPacket->pTCPSeg); if (pPacket->pTCPSeg->u8Code & TCP_CODE_SYN) nDataLen++; if (pPacket->pTCPSeg->u8Code & TCP_CODE_FIN) nDataLen++; pTCPOutSeg->n32AckSeq = pPacket->pTCPSeg->n32Seq + nDataLen; pTCPOutSeg->u8Offset = TCP_HDR_OFFSET; pTCPOutSeg->u16Window = 0; pTCPOutSeg->u16Urgent = 0; tcpHeaderHost2Net(pTCPOutSeg); pTCPOutSeg->u16CheckSum = 0; NETBUF_SETLEN(buf,TCP_MIN_HDR_LEN); pTCPOutSeg->u16CheckSum = tcpCheckSum(buf,attrs,TCP_MIN_HDR_LEN); gu32TcpOutSegs++; gu32TcpOutRsts++; if (!netattrSet(attrs,NETATTR_IP_PROTOCOL,NETIP_PROTOCOL_TCP)) return NULL; tcpPacketDump(pTCPOutSeg,"SR",TCP_MIN_HDR_LEN); netcfgSendNextDown(NETMODULE_TCP_OUT,buf,attrs); return TCP_OK; } //************************************************************************************************** // tcpkick // makes sure we send a packet soon //************************************************************************************************** int tcpKick( TCBPtr pTCB) { if (pTCB->u16Flags & TCP_TCB_DELACK && !tcp_tmLeft(&gOutputPort,pTCB,SEND)) { tcp_tmSet(&gOutputPort,TCP_QUEUE_LEN,pTCB,SEND,TCP_ACKDELAY); } else if (ptCount(&gOutputPort) < TCP_QUEUE_LEN) { ptSend(&gOutputPort,pTCB,SEND); } return TCP_OK; } //************************************************************************************************** // tcpSndWindow // handlea send window updates from remote //************************************************************************************************** static int tcpSndWindow( TCBPtr pTCB, PacketPtr pPacket) { long n32WinLast; long n32OutWinLast; if (COMPARE_SEQUENCE(pPacket->pTCPSeg->n32Seq,pTCB->n32SeqOfLastWinUpdt) < 0) return TCP_OK; if (COMPARE_SEQUENCE(pPacket->pTCPSeg->n32Seq,pTCB->n32SeqOfLastWinUpdt) == 0 && COMPARE_SEQUENCE(pPacket->pTCPSeg->n32AckSeq,pTCB->n32AckSeqOfLastWinUpdt) < 0) return TCP_OK; // else, we have a send window updat // computes the last sequences of the new and old windows n32OutWinLast = pTCB->n32AckSeqOfLastWinUpdt + pTCB->u32SndWinSize; n32WinLast = pPacket->pTCPSeg->n32AckSeq + pPacket->pTCPSeg->u16Window; pTCB->u32SndWinSize = pPacket->pTCPSeg->u16Window; pTCB->n32SeqOfLastWinUpdt = pPacket->pTCPSeg->n32Seq; pTCB->n32AckSeqOfLastWinUpdt = pPacket->pTCPSeg->n32AckSeq; if (COMPARE_SEQUENCE(n32WinLast,n32OutWinLast) <= 0) return TCP_OK; // else, window increased if (pTCB->setOutState == TCP_OUT_STATE_PERSISTS) { tcp_tmClear(&gOutputPort,pTCB,PERSIST); pTCB->setOutState = TCP_OUT_STATE_TRANSMIT; } tcpKick(pTCB); return TCP_OK; } //************************************************************************************************** // tcpRcvWindow // does receive window processing for a TCB //************************************************************************************************** int tcpRcvWindow( TCBPtr pTCB) { int nWindow; nWindow = pTCB->u32RcvBufSize - pTCB->u32RcvBufCount; if (pTCB->setState < TCP_STATE_ESTABLISHED) return nWindow; // Receiver-Side Silly Window Syndrome Avoidance: Never shrink an already-advertised window, but // semWait for at least 1/4 receiver buffer and 1 max-sized segment before opening a zero window. if (nWindow*4 < (int)pTCB->u32RcvBufSize || nWindow < (int)pTCB->u32RcvMaxSegSize) nWindow = 0; nWindow = max(nWindow,pTCB->n32CurAdvWinSeq - pTCB->n32RcvNextSeq); pTCB->n32CurAdvWinSeq = pTCB->n32RcvNextSeq + nWindow; return nWindow; } //************************************************************************************************** // tcpwakeup // wakes up processes sleeping for TCP //************************************************************************************************** int tcpWakeup( TCBPtr pTCB, int nType) { int nFreeLen; if (nType & TCP_READERS) { if (((pTCB->u16Flags & TCP_TCB_RCV_DONE) || pTCB->u32RcvBufCount > 0 || (pTCB->u16Flags & TCP_TCB_RCV_URGENT_ISOK))) { semSignalIfLess(&pTCB->recRcvSemaphore,1); } } if (nType & TCP_WRITERS) { nFreeLen = pTCB->u32SndBufSize - pTCB->u32SndBufCount; if ((pTCB->u16Flags & TCP_TCB_SND_DONE) || nFreeLen > 0) { semSignalIfLess(&pTCB->recSndSemaphore,1); } // special for abort if (pTCB->nError && pTCB->recOpenCloseSemaphore.nsCount > 0) semSignal(&pTCB->recOpenCloseSemaphore); } return TCP_OK; } //************************************************************************************************** // tcpAbort // aborts an active TCP connection //************************************************************************************************** static int tcpAbort( TCBPtr pTCB, int nError) { tcpKillTimers(pTCB); pTCB->u16Flags |= TCP_TCB_RCV_DONE|TCP_TCB_SND_DONE; pTCB->nError = nError; tcpWakeup(pTCB,TCP_READERS|TCP_WRITERS); return TCP_OK; } //************************************************************************************************** // tcpHowMuch // computes how much data is available to send //************************************************************************************************** static int tcpHowMuch( TCBPtr pTCB) { int nToSend; nToSend = pTCB->n32SndUnacked + pTCB->u32SndBufCount - pTCB->n32SndNext; if (pTCB->u8NextPacketCode & TCP_CODE_SYN) ++nToSend; if (pTCB->u16Flags & TCP_TCB_SEND_FIN) ++nToSend; return nToSend; } //************************************************************************************************** // tcpSendLen // computes the packet length and offset in sndbuf //************************************************************************************************** static int tcpSendLen( TCBPtr pTCB, Boolean bRetransmit, unsigned int *puOffset) { unsigned uDataLen; if (bRetransmit || (pTCB->u8NextPacketCode & TCP_CODE_SYN)) *puOffset = 0; else *puOffset = pTCB->n32SndNext - pTCB->n32SndUnacked; uDataLen = pTCB->u32SndBufCount - *puOffset; uDataLen = min(uDataLen,pTCB->u32SndWinSize); return min(uDataLen,pTCB->u32SndMaxSegSize); } //************************************************************************************************** // tcpRcvMaxSegSize // sets receive MSS option (MSS = Max Segment Size) //************************************************************************************************** static void tcpRcvMaxSegSize( TCBPtr pTCB, TCPSegPtr pTCPSeg) { int nHdrLen; int nOptionLen; int nMSS; int n; nHdrLen = TCP_HEADER_LEN(pTCPSeg); nOptionLen = 2 + sizeof(short); pTCPSeg->aryData[nHdrLen - TCP_MIN_HDR_LEN] = TCP_OPTION_MAXSEGSIZE; // option kind pTCPSeg->aryData[nHdrLen - TCP_MIN_HDR_LEN + 1] = nOptionLen; // option length nMSS = pTCB->u32RcvMaxSegSize; for (n = nOptionLen-1;n > 1;n--) { pTCPSeg->aryData[nHdrLen - TCP_MIN_HDR_LEN + n] = nMSS & 0xFF; nMSS >>= 8; } nHdrLen += nOptionLen + 3; // +3 for proper rounding below // header length is high 4 bits of tcp_offset, in longs pTCPSeg->u8Offset = ((nHdrLen << 2) & 0xF0) | pTCPSeg->u8Offset & 0x0F; } //************************************************************************************************** // tcpSend // computes and sends a TCP segment for the given TCB //************************************************************************************************** static int tcpSend( TCBPtr pTCB, Boolean bRetransmit) { long n32Left,n32Copied; unsigned uDataLen; unsigned uOffset; int nNewData; int nCurPos; NetBuf buf; NetAttrs attrs; unsigned short u16Port; TCPSegPtr pTCPSeg; short n16Urgent; IPAddressUon uonIpAddress; if (!netattrNew(&attrs)) return TCP_GENERAL_ERROR; if (tcpGetIPAddr(pTCB->netParams,TCP_IP_SRC_ADDR,&uonIpAddress) != TCP_OK) return TCP_GENERAL_ERROR; if (tcpSetIPAddr(attrs,TCP_IP_SRC_ADDR,&uonIpAddress) != TCP_OK) return TCP_GENERAL_ERROR; if (tcpGetIPAddr(pTCB->netParams,TCP_IP_DST_ADDR,&uonIpAddress) != TCP_OK) return TCP_GENERAL_ERROR; if (tcpSetIPAddr(attrs,TCP_IP_DST_ADDR,&uonIpAddress) != TCP_OK) return TCP_GENERAL_ERROR; uDataLen = tcpSendLen(pTCB,bRetransmit,&uOffset); if (!netbufAlloc(&buf,uDataLen + TCP_MAX_HDR_LEN + MAX_ETHER_IP_HEADER_LEN)) return TCP_GENERAL_ERROR; buf->start += MAX_ETHER_IP_HEADER_LEN; pTCPSeg = (TCPSegPtr)NETBUF_DATA(buf); if (!netattrGet(pTCB->netParams,NETATTR_TCP_FROM,&u16Port)) return NULL; if (!netattrSet(attrs,NETATTR_TCP_FROM,u16Port)) return NULL; pTCPSeg->u16SrcPort = u16Port; if (!netattrGet(pTCB->netParams,NETATTR_TCP_TO,&u16Port)) return NULL; if (!netattrSet(attrs,NETATTR_TCP_TO,u16Port)) return NULL; pTCPSeg->u16DstPort = u16Port; if (bRetransmit) pTCPSeg->n32Seq = pTCB->n32SndUnacked; else pTCPSeg->n32Seq = pTCB->n32SndNext; pTCPSeg->n32AckSeq = pTCB->n32RcvNextSeq; if ((pTCB->u16Flags & TCP_TCB_SEND_FIN) && COMPARE_SEQUENCE(pTCPSeg->n32Seq + uDataLen,pTCB->n32SndLast) == 0) pTCB->u8NextPacketCode |= TCP_CODE_FIN; pTCPSeg->u8Code = pTCB->u8NextPacketCode; pTCPSeg->u8Offset = TCP_HDR_OFFSET; if ((pTCB->u16Flags & TCP_TCB_FIRST_SEND) == 0) pTCPSeg->u8Code |= TCP_CODE_ACK; if (pTCPSeg->u8Code & TCP_CODE_SYN) tcpRcvMaxSegSize(pTCB,pTCPSeg); if (uDataLen > 0) pTCPSeg->u8Code |= TCP_CODE_PSH; pTCPSeg->u16Window = tcpRcvWindow(pTCB); if (pTCB->u16Flags & TCP_TCB_SND_URGENT_ISOK) { n16Urgent = pTCB->n32SndUrgentSeq - pTCPSeg->n32Seq; if (n16Urgent >= 0) { #ifdef BSDURG pTCPSeg->u16Urgent = n16Urgent + 1; #else pTCPSeg->u16Urgent = n16Urgent; #endif pTCPSeg->u8Code |= TCP_CODE_URG; } else pTCPSeg->u16Urgent = 0; } else pTCPSeg->u16Urgent = 0; // copies data from snd buffer to the tcp segment nCurPos = (pTCB->u32SndBufStart + uOffset) % pTCB->u32SndBufSize; if (uDataLen > 0) { n32Left = pTCB->u32SndBufSize - nCurPos; if (uDataLen > n32Left) { n32Copied = netbufWrite(buf,&pTCB->pu8sndBuf[nCurPos],n32Left,TCP_HEADER_LEN(pTCPSeg)); if (n32Copied != n32Left) return TCP_GENERAL_ERROR; n32Copied = netbufWrite(buf,&pTCB->pu8sndBuf[0],uDataLen - n32Left, TCP_HEADER_LEN(pTCPSeg) + n32Left); if (n32Copied != (uDataLen - n32Left)) return TCP_GENERAL_ERROR; } else { n32Copied = netbufWrite(buf,&pTCB->pu8sndBuf[nCurPos],uDataLen,TCP_HEADER_LEN(pTCPSeg)); if (n32Copied != uDataLen) return TCP_GENERAL_ERROR; } } pTCB->u16Flags &= ~TCP_TCB_NEED_OUTPUT; // we're doing it if (bRetransmit) { nNewData = pTCB->n32SndUnacked + uDataLen - pTCB->n32SndNext; if (nNewData < 0) nNewData = 0; gu32TcpRetransSegs++; } else { nNewData = uDataLen; if (pTCB->u8NextPacketCode & TCP_CODE_SYN) nNewData++; // SYN is part of the sequence if (pTCB->u8NextPacketCode & TCP_CODE_FIN) nNewData++; // FIN is part of the sequence } pTCB->n32SndNext += nNewData; if (nNewData >= 0) gu32TcpOutSegs++; if (pTCB->setState == TCP_STATE_TIMEWAIT) tcpWait(pTCB); // final ACK uDataLen += TCP_HEADER_LEN(pTCPSeg); tcpHeaderHost2Net(pTCPSeg); NETBUF_SETLEN(buf,uDataLen); pTCPSeg->u16CheckSum = 0; pTCPSeg->u16CheckSum = tcpCheckSum(buf,attrs,uDataLen); if (!netattrSet(attrs,NETATTR_IP_PROTOCOL,NETIP_PROTOCOL_TCP)) return NULL; tcpPacketDump(pTCPSeg,"SN",uDataLen); netcfgSendNextDown(NETMODULE_TCP_OUT,buf,attrs); return TCP_OK; } //************************************************************************************************** // tcpReTransmit // handles TCP output events while we are retransmitting //************************************************************************************************** static int tcpReTransmit( TCBPtr pTCB, unsigned long u32Event) { int nTimeout; if (u32Event != RETRANSMIT) return TCP_OK; // ignore others while retransmitting if (++pTCB->nNumOfRetransmits > TCP_MAXRETRIES) { tcpAbort(pTCB,TCP_TIMEDOUT); return TCP_OK; } tcpSend(pTCB,TCP_REXMT); nTimeout = min(pTCB->nRetransmitTimeout << pTCB->nNumOfRetransmits,TCP_MAXRXT); tcp_tmSet(&gOutputPort,TCP_QUEUE_LEN,pTCB,RETRANSMIT,nTimeout); if (pTCB->setOutState != TCP_OUT_STATE_RETRANSMIT) pTCB->u32SlowStartThreshold = pTCB->u32CongestionWinSize; pTCB->u32SlowStartThreshold = min(pTCB->u32SndWinSize,pTCB->u32SlowStartThreshold)/2; if (pTCB->u32SlowStartThreshold < pTCB->u32SndMaxSegSize) pTCB->u32SlowStartThreshold = pTCB->u32SndMaxSegSize; pTCB->u32CongestionWinSize = pTCB->u32SndMaxSegSize; return TCP_OK; } //************************************************************************************************** // tcpTransmit // handles TCP output events while we are transmitting //************************************************************************************************** static int tcpTransmit( TCBPtr pTCB, unsigned long u32Event) { int nToSend; int nWindow; int nPending; if (u32Event == RETRANSMIT) { tcp_tmClear(&gOutputPort,pTCB,SEND); tcpReTransmit(pTCB,u32Event); pTCB->setOutState = TCP_OUT_STATE_RETRANSMIT; return TCP_OK; } // else SEND nToSend = tcpHowMuch(pTCB); if (nToSend == 0) { if (pTCB->u16Flags & TCP_TCB_NEED_OUTPUT) tcpSend(pTCB,TCP_NEWDATA); if (pTCB->n32SndNext == pTCB->n32SndUnacked) return TCP_OK; // still unacked data; restarts transmit timer if (!tcp_tmLeft(&gOutputPort,pTCB,RETRANSMIT)) tcp_tmSet(&gOutputPort,TCP_QUEUE_LEN,pTCB,RETRANSMIT,pTCB->nRetransmitTimeout); return TCP_OK; } else if (pTCB->u32SndWinSize == 0) { pTCB->setOutState = TCP_OUT_STATE_PERSISTS; pTCB->nPersist = pTCB->nRetransmitTimeout; tcpSend(pTCB,TCP_NEWDATA); tcp_tmSet(&gOutputPort,TCP_QUEUE_LEN,pTCB,PERSIST,pTCB->nPersist); return TCP_OK; } // else, we have data and window pTCB->setOutState = TCP_OUT_STATE_TRANSMIT; nWindow = min(pTCB->u32SndWinSize,pTCB->u32CongestionWinSize); nPending = pTCB->n32SndNext - pTCB->n32SndUnacked; while ((tcpHowMuch(pTCB) > 0) && (nPending < nWindow)) { tcpSend(pTCB,TCP_NEWDATA); nPending = pTCB->n32SndNext - pTCB->n32SndUnacked; } if (!tcp_tmLeft(&gOutputPort,pTCB,RETRANSMIT)) { tcp_tmSet(&gOutputPort,TCP_QUEUE_LEN,pTCB,RETRANSMIT,pTCB->nRetransmitTimeout); } return TCP_OK; } //************************************************************************************************** // tcpIdle // handles events while a connection is idle //************************************************************************************************** static int tcpIdle( TCBPtr pTCB, unsigned long u32Event) { if (u32Event == SEND) tcpTransmit(pTCB,u32Event); return TCP_OK; } //************************************************************************************************** // tcpPersist // handles events while the send window is closed //************************************************************************************************** static int tcpPersist( TCBPtr pTCB, unsigned long u32Event) { if (u32Event != PERSIST && u32Event != SEND) return TCP_OK; tcpSend(pTCB,TCP_REXMT); pTCB->nPersist = min(pTCB->nPersist << 1,TCP_MAXPRS); tcp_tmSet(&gOutputPort,TCP_QUEUE_LEN,pTCB,PERSIST,pTCB->nPersist); return TCP_OK; } //************************************************************************************************** // tcpOutDispatcher // handles events affecting TCP output processing //************************************************************************************************** static void tcpOutDispatcher( TCBPtr pTCB, unsigned long u32Event) { switch(pTCB->setOutState) { case TCP_OUT_STATE_IDLE: tcpIdle(pTCB,u32Event); break; case TCP_OUT_STATE_PERSISTS: tcpPersist(pTCB,u32Event); break; case TCP_OUT_STATE_TRANSMIT: tcpTransmit(pTCB,u32Event); break; case TCP_OUT_STATE_RETRANSMIT: tcpReTransmit(pTCB,u32Event); break; default: break; } } //************************************************************************************************** // tcpOut // handles events affecting TCP output processing //************************************************************************************************** #ifdef MacOS pascal void *tcpOut(ThreadArg arg) #else void tcpOut(ThreadArg arg) #endif { ThreadId myThreadId,parentThreadId; TCBPtr pTCB; unsigned long u32Event; tmGetInfo(SELF,&myThreadId,&parentThreadId); // Add to list of modules netmodAdd(NETMODULE_TCP_OUT, myThreadId); netdbgPrintf(NETDEBUG_MODULES,"tcp module started (moduleId=%d, threadId=%d).\n", NETMODULE_TCP_OUT, myThreadId); ptInit(&gOutputPort,TCP_QUEUE_LEN); while (TRUE) { ptReceive(&gOutputPort,&pTCB,&u32Event); if (pTCB->setState <= TCP_STATE_CLOSED) continue; semWait(&pTCB->recMutex); if (pTCB->setState <= TCP_STATE_CLOSED) continue; if (u32Event == DELETE) tcpTCBDeallocate(pTCB); else { tcpOutDispatcher(pTCB,u32Event); semSignal(&pTCB->recMutex); } } } //************************************************************************************************** // tcpOutputState // does TCP output state processing after an ACK //************************************************************************************************** static int tcpOutputState( TCBPtr pTCB, int nAcked) { if (nAcked <= 0) return TCP_OK; // no state change if (pTCB->setOutState == TCP_OUT_STATE_RETRANSMIT) { pTCB->nNumOfRetransmits = 0; pTCB->setOutState = TCP_OUT_STATE_TRANSMIT; } if (pTCB->u32SndBufCount == 0) { pTCB->setOutState = TCP_OUT_STATE_IDLE; return TCP_OK; } tcpKick(pTCB); return TCP_OK; } //************************************************************************************************** // tcpAcked // handles in-bound ACKs and do round trip estimates //************************************************************************************************** static int tcpAcked( TCBPtr pTCB, PacketPtr pPacket) { long n32Acked; long n32NumOfAcked; if (!(pPacket->pTCPSeg->u8Code & TCP_CODE_ACK)) return TCP_GENERAL_ERROR; n32Acked = pPacket->pTCPSeg->n32AckSeq - pTCB->n32SndUnacked; n32NumOfAcked = 0; if (n32Acked <= 0) return 0; if (COMPARE_SEQUENCE(pPacket->pTCPSeg->n32AckSeq,pTCB->n32SndNext) > 0) if (pTCB->setState == TCP_STATE_SYNRCVD) return tcpReset(pPacket); else return tcpAckIt(pTCB,pPacket); tcpRoundTripTime(pTCB); pTCB->n32SndUnacked = pPacket->pTCPSeg->n32AckSeq; if (n32Acked && pTCB->u8NextPacketCode & TCP_CODE_SYN) { n32Acked--; n32NumOfAcked++; pTCB->u8NextPacketCode &= ~TCP_CODE_SYN; pTCB->u16Flags &= ~TCP_TCB_FIRST_SEND; } if ( (pTCB->u8NextPacketCode & TCP_CODE_FIN) && COMPARE_SEQUENCE(pPacket->pTCPSeg->n32AckSeq,pTCB->n32SndNext) == 0) { n32Acked--; n32NumOfAcked++; pTCB->u8NextPacketCode &= ~TCP_CODE_FIN; pTCB->u16Flags &= ~TCP_TCB_SEND_FIN; } if ( (pTCB->u16Flags & TCP_TCB_SND_URGENT_ISOK) && COMPARE_SEQUENCE(pPacket->pTCPSeg->n32AckSeq,pTCB->n32SndUrgentSeq) >= 0) pTCB->u16Flags &= ~TCP_TCB_SND_URGENT_ISOK; pTCB->u32SndBufStart = (pTCB->u32SndBufStart+n32Acked) % pTCB->u32SndBufSize; pTCB->u32SndBufCount -= n32Acked; if (n32Acked) semSignalIfLess(&pTCB->recSndSemaphore,1); tcpOutputState(pTCB,n32Acked+n32NumOfAcked); return n32Acked; } //************************************************************************************************** // tcpAckIt //************************************************************************************************** static int tcpAckIt( TCBPtr pTCB, PacketPtr pPacket) { TCPSegPtr pTCPOutSeg; NetBuf buf; NetAttrs attrs; unsigned short u16Port; IPAddressUon uonSrcIpAddr; IPAddressUon uonDstIpAddr; if (pPacket->pTCPSeg->u8Code & TCP_CODE_RST) return TCP_OK; if ( (netbufLen(pPacket->segBuf) <= TCP_HEADER_LEN(pPacket->pTCPSeg)) && !(pPacket->pTCPSeg->u8Code & (TCP_CODE_SYN | TCP_CODE_FIN))) return TCP_OK; if (!netbufAlloc(&buf,TCP_MIN_HDR_LEN + MAX_ETHER_IP_HEADER_LEN)) return TCP_GENERAL_ERROR; buf->start += MAX_ETHER_IP_HEADER_LEN; if (!netattrNew(&attrs)) return TCP_GENERAL_ERROR; if (tcpGetIPAddr(pPacket->netAttrs,TCP_IP_SRC_ADDR,&uonSrcIpAddr) != TCP_OK) return TCP_GENERAL_ERROR; if (tcpGetIPAddr(pPacket->netAttrs,TCP_IP_DST_ADDR,&uonDstIpAddr) != TCP_OK) return TCP_GENERAL_ERROR; if (tcpSetIPAddr(attrs,TCP_IP_SRC_ADDR,&uonDstIpAddr) != TCP_OK) return TCP_GENERAL_ERROR; if (tcpSetIPAddr(attrs,TCP_IP_DST_ADDR,&uonSrcIpAddr) != TCP_OK) return TCP_GENERAL_ERROR; pTCPOutSeg = (TCPSegPtr)NETBUF_DATA(buf); if (!netattrGet(pPacket->netAttrs,NETATTR_TCP_TO,&u16Port)) return TCP_GENERAL_ERROR; if (!netattrSet(attrs,NETATTR_TCP_FROM,u16Port)) return TCP_GENERAL_ERROR; pTCPOutSeg->u16SrcPort = pPacket->pTCPSeg->u16DstPort; if (!netattrGet(pPacket->netAttrs,NETATTR_TCP_FROM,&u16Port)) return NULL; if (!netattrSet(attrs,NETATTR_TCP_TO,u16Port)) return NULL; pTCPOutSeg->u16DstPort = pPacket->pTCPSeg->u16SrcPort; pTCPOutSeg->n32Seq = pTCB->n32SndNext; pTCPOutSeg->n32AckSeq = pTCB->n32RcvNextSeq; pTCPOutSeg->u8Code = TCP_CODE_ACK; pTCPOutSeg->u8Offset = TCP_HDR_OFFSET; pTCPOutSeg->u16Window = tcpRcvWindow(pTCB); pTCPOutSeg->u16Urgent = 0; tcpHeaderHost2Net(pTCPOutSeg); NETBUF_SETLEN(buf,TCP_MIN_HDR_LEN); pTCPOutSeg->u16CheckSum = 0; pTCPOutSeg->u16CheckSum = tcpCheckSum(buf,attrs,TCP_MIN_HDR_LEN); gu32TcpOutSegs++; if (!netattrSet(attrs,NETATTR_IP_PROTOCOL,NETIP_PROTOCOL_TCP)) return NULL; tcpPacketDump(pTCPOutSeg,"SA",TCP_MIN_HDR_LEN); netcfgSendNextDown(NETMODULE_TCP_OUT,buf,attrs); return TCP_OK; } //************************************************************************************************** // tcpClosed //************************************************************************************************** static int tcpClosed( TCBPtr pTCB, PacketPtr pPacket) { tcpReset(pPacket); return TCP_GENERAL_ERROR; } //************************************************************************************************** // tcpWindowInit // €€€ //************************************************************************************************** static int tcpWindowInit( TCBPtr pTCB, TCBPtr pNewTCB, PacketPtr pPacket) { unsigned long u32MaxSegSize; pNewTCB->u32SndWinSize = pPacket->pTCPSeg->u16Window; pNewTCB->n32SeqOfLastWinUpdt = pPacket->pTCPSeg->n32Seq; pNewTCB->n32AckSeqOfLastWinUpdt = pNewTCB->n32InitSndSeq; u32MaxSegSize = 536; pNewTCB->u32SndMaxSegSize = u32MaxSegSize; pNewTCB->u32RcvMaxSegSize = u32MaxSegSize; pNewTCB->u32CongestionWinSize = pNewTCB->u32SndMaxSegSize; pNewTCB->u32SlowStartThreshold = 65535; // ip max window pNewTCB->n32RcvNextSeq = pPacket->pTCPSeg->n32Seq; pNewTCB->n32CurAdvWinSeq = pNewTCB->n32RcvNextSeq + pNewTCB->u32RcvBufSize; return 1; } //************************************************************************************************** // tcpdodat // does input data processing //************************************************************************************************** static int tcpDoData( TCBPtr pTCB, PacketPtr pPacket, long n32First, unsigned uDataLen) { int nWakeup = 0; if (pTCB->n32RcvNextSeq == n32First) { if (uDataLen > 0) { tcpFragmentJoin(pTCB,pPacket,uDataLen); pTCB->u16Flags |= TCP_TCB_NEED_OUTPUT; nWakeup++; } if (pPacket->pTCPSeg->u8Code & TCP_CODE_FIN) { pTCB->u16Flags |= TCP_TCB_RCV_DONE | TCP_TCB_NEED_OUTPUT; pTCB->n32RcvNextSeq++; nWakeup++; } if (pPacket->pTCPSeg->u8Code & (TCP_CODE_PSH | TCP_CODE_URG)) { pTCB->u16Flags |= TCP_TCB_PUSH; nWakeup++; } if (nWakeup != 0) tcpWakeup(pTCB,TCP_READERS); } else { // process delayed controls if (pPacket->pTCPSeg->u8Code & TCP_CODE_FIN) pTCB->n32FINSeqNum = pPacket->pTCPSeg->n32Seq + uDataLen; if (pPacket->pTCPSeg->u8Code & (TCP_CODE_PSH | TCP_CODE_URG)) pTCB->n32PUSHSeqNum = pPacket->pTCPSeg->n32Seq + uDataLen; pPacket->pTCPSeg->u8Code &= ~(TCP_CODE_FIN | TCP_CODE_PSH); tcpFragmentInsert(pTCB,n32First,uDataLen); } return TCP_OK; } //************************************************************************************************** // tcpData // process an input segment's data section //************************************************************************************************** static int tcpData( TCBPtr pTCB, PacketPtr pPacket) { int nDataLen; int nRcvWin; long n32WinLast; long n32First,n32Last; unsigned uRcvBufPos; int nSegBufPos; long n32Left,n32Copied; if (pPacket->pTCPSeg->u8Code & TCP_CODE_URG) { int nRcvUrgent = pPacket->pTCPSeg->n32Seq + pPacket->pTCPSeg->u16Urgent; #ifdef BSDURG nRcvUrgent--; #endif if (!(pTCB->u16Flags & TCP_TCB_RCV_URGENT_ISOK) || COMPARE_SEQUENCE(nRcvUrgent,pTCB->n32RcvUrgentSeq) > 0) { pTCB->n32RcvUrgentSeq = nRcvUrgent; pTCB->u16Flags |= TCP_TCB_RCV_URGENT_ISOK; } } if (pPacket->pTCPSeg->u8Code & TCP_CODE_SYN) { pTCB->n32RcvNextSeq++; pTCB->u16Flags |= TCP_TCB_NEED_OUTPUT; ++pPacket->pTCPSeg->n32Seq; } nDataLen = netbufLen(pPacket->segBuf) - TCP_HEADER_LEN(pPacket->pTCPSeg); nRcvWin = pTCB->u32RcvBufSize - pTCB->u32RcvBufCount; n32WinLast = pTCB->n32RcvNextSeq + nRcvWin - 1; n32First = pPacket->pTCPSeg->n32Seq; n32Last = n32First + nDataLen - 1; if (COMPARE_SEQUENCE(pTCB->n32RcvNextSeq,n32First) > 0) { nDataLen -= pTCB->n32RcvNextSeq - n32First; n32First = pTCB->n32RcvNextSeq; } if (COMPARE_SEQUENCE(n32Last,n32WinLast) > 0) { nDataLen -= n32Last - n32WinLast; pPacket->pTCPSeg->u8Code &= ~TCP_CODE_FIN; // cutting it off } uRcvBufPos = pTCB->u32RcvBufStart + pTCB->u32RcvBufCount; // == rnext, in buf uRcvBufPos += n32First - pTCB->n32RcvNextSeq; // distance in buf uRcvBufPos %= pTCB->u32RcvBufSize; // may wrap nSegBufPos = n32First - pPacket->pTCPSeg->n32Seq; // copies input data into rcv buffer if (nDataLen > 0) { n32Left = pTCB->u32RcvBufSize - uRcvBufPos; if (nDataLen > n32Left) { // need to copy data in two pieces (buffer wraps) n32Copied = netbufRead(&pTCB->pu8rcvBuf[uRcvBufPos],pPacket->segBuf, n32Left,TCP_MIN_HDR_LEN + nSegBufPos); if (n32Copied != n32Left) return TCP_GENERAL_ERROR; n32Copied = netbufRead(&pTCB->pu8rcvBuf[0],pPacket->segBuf, nDataLen - n32Left,TCP_MIN_HDR_LEN + nSegBufPos + n32Left); if (n32Copied != (nDataLen - n32Left)) return TCP_GENERAL_ERROR; } else { n32Copied = netbufRead(&pTCB->pu8rcvBuf[uRcvBufPos],pPacket->segBuf, nDataLen,TCP_MIN_HDR_LEN+nSegBufPos); if (n32Copied != nDataLen) return TCP_GENERAL_ERROR; } } tcpDoData(pTCB,pPacket,n32First,nDataLen); if (pTCB->u16Flags & TCP_TCB_NEED_OUTPUT) tcpKick(pTCB); return TCP_OK; } //************************************************************************************************** // tcpDoListen //************************************************************************************************** static int tcpDoListen( TCBPtr pTCB, PacketPtr pPacket) { TCBPtr pNewTCB; IPAddressUon uonIpAddress; SocketPtr pSocket; if (pPacket->pTCPSeg->u8Code & TCP_CODE_RST) return TCP_OK; // "parent" TCB still in LISTEN if ((pPacket->pTCPSeg->u8Code & TCP_CODE_ACK) || (pPacket->pTCPSeg->u8Code & TCP_CODE_SYN) == 0) return tcpReset(pPacket); if (tcpTCBAllocate(&pNewTCB) != TCP_OK) return TCP_GENERAL_ERROR; if (tcpSync(pNewTCB) == TCP_GENERAL_ERROR) return TCP_GENERAL_ERROR; if (tcpNewSocket(&pSocket) != TCP_OK) return TCP_GENERAL_ERROR; pNewTCB->pDevice = pSocket; pSocket->pTCB = pNewTCB; pNewTCB->setState = TCP_STATE_SYNRCVD; pNewTCB->setOutState = TCP_OUT_STATE_IDLE; pNewTCB->nError = 0; pNewTCB->pParentTCB = (struct TCBRec*)pTCB; // for ACCEPT if (tcpGetIPAddr(pPacket->netAttrs,TCP_IP_SRC_ADDR,&uonIpAddress) != TCP_OK) return TCP_GENERAL_ERROR; if (tcpSetIPAddr(pNewTCB->netParams,TCP_IP_DST_ADDR,&uonIpAddress) != TCP_OK) return TCP_GENERAL_ERROR; if (tcpGetIPAddr(pPacket->netAttrs,TCP_IP_DST_ADDR,&uonIpAddress) != TCP_OK) return TCP_GENERAL_ERROR; if (tcpSetIPAddr(pNewTCB->netParams,TCP_IP_SRC_ADDR,&uonIpAddress) != TCP_OK) return TCP_GENERAL_ERROR; //if (!netattrGet(pPacket->netAttrs,NETATTR_TCP_FROM,&u16Port)) return NULL; if (!netattrSet(pNewTCB->netParams,NETATTR_TCP_TO,pPacket->pTCPSeg->u16SrcPort)) return NULL; //if (!netattrGet(pPacket->netAttrs,NETATTR_TCP_TO,&u16Port)) return NULL; if (!netattrSet(pNewTCB->netParams,NETATTR_TCP_FROM,pPacket->pTCPSeg->u16DstPort)) return NULL; tcpWindowInit(pTCB,pNewTCB,pPacket); pNewTCB->n32FINSeqNum = pNewTCB->n32PUSHSeqNum = 0; pNewTCB->u16Flags = TCP_TCB_NEED_OUTPUT; gu32TcpPassiveOpens++; tcpData(pNewTCB,pPacket); semSignal(&pNewTCB->recMutex); return TCP_OK; } //************************************************************************************************** // tcpKillTimers // kills all outstanding timers for a TCB //************************************************************************************************** static int tcpKillTimers( TCBPtr pTCB) { tcp_tmClear(&gOutputPort,pTCB,SEND); tcp_tmClear(&gOutputPort,pTCB,RETRANSMIT); tcp_tmClear(&gOutputPort,pTCB,PERSIST); return TCP_OK; } //************************************************************************************************** // tcpInput // does SYN_SENT state processing //************************************************************************************************** static int tcpSynSent( TCBPtr pTCB, PacketPtr pPacket) { if ((pPacket->pTCPSeg->u8Code & TCP_CODE_ACK) && ((pPacket->pTCPSeg->n32AckSeq - pTCB->n32InitSndSeq <= 0) || (pPacket->pTCPSeg->n32AckSeq - pTCB->n32SndNext) > 0)) return tcpReset(pPacket); if (pPacket->pTCPSeg->u8Code & TCP_CODE_RST) { pTCB->setState = TCP_STATE_CLOSED; pTCB->nError = TCP_RESET; gu32TcpAttemptFails++; tcpKillTimers(pTCB); semSignal(&pTCB->recOpenCloseSemaphore); return TCP_OK; } if ((pPacket->pTCPSeg->u8Code & TCP_CODE_SYN) == 0) return TCP_OK; pTCB->u32SndWinSize = pPacket->pTCPSeg->u16Window; pTCB->n32SeqOfLastWinUpdt = pPacket->pTCPSeg->n32Seq; pTCB->n32RcvNextSeq = pPacket->pTCPSeg->n32Seq; pTCB->n32CurAdvWinSeq = pTCB->n32RcvNextSeq + pTCB->u32RcvBufSize; tcpAcked(pTCB,pPacket); tcpData(pTCB,pPacket); pPacket->pTCPSeg->u8Code &= ~TCP_CODE_FIN; if (pTCB->u8NextPacketCode & TCP_CODE_SYN) // our SYN not ACKed pTCB->setState = TCP_STATE_SYNRCVD; else { gu32TcpCurrEstab++; pTCB->setState = TCP_STATE_ESTABLISHED; semSignal(&pTCB->recOpenCloseSemaphore); } return TCP_OK; } //************************************************************************************************** // tcpSynReceived // do SYN_RCVD state input processing //************************************************************************************************** static int tcpSynReceived( TCBPtr pTCB, PacketPtr pPacket) { TCBPtr pParentTCB; if (pPacket->pTCPSeg->u8Code & TCP_CODE_RST) { gu32TcpAttemptFails++; if (pTCB->pParentTCB != NULL) return tcpTCBDeallocate(pTCB); else return tcpAbort(pTCB,TCP_REFUSED); } if (pPacket->pTCPSeg->u8Code & TCP_CODE_SYN) { gu32TcpAttemptFails++; tcpReset(pPacket); return tcpAbort(pTCB,TCP_RESET); } if (tcpAcked(pTCB,pPacket) == TCP_GENERAL_ERROR) return TCP_OK; if (pTCB->pParentTCB != NULL) // from a passive open { pParentTCB = (TCBPtr)pTCB->pParentTCB; if (semWait(&pParentTCB->recMutex) != TCP_OK) { gu32TcpAttemptFails++; tcpReset(pPacket); return tcpTCBDeallocate(pTCB); } if (pParentTCB->setState != TCP_STATE_LISTEN) { gu32TcpAttemptFails++; tcpReset(pPacket); semSignal(&pParentTCB->recMutex); return tcpTCBDeallocate(pTCB); } if (ptCount(&pParentTCB->recListenQueue) >= pParentTCB->nListenQueueSize) { gu32TcpAttemptFails++; tcpReset(pPacket); semSignal(&pParentTCB->recMutex); return tcpTCBDeallocate(pTCB); } ptSend(&pParentTCB->recListenQueue,pTCB,0); semSignal(&pParentTCB->recMutex); } else // from an active open semSignal(&pTCB->recOpenCloseSemaphore); gu32TcpCurrEstab++; pTCB->setState = TCP_STATE_ESTABLISHED; tcpData(pTCB,pPacket); if (pTCB->u16Flags & TCP_TCB_RCV_DONE) pTCB->setState = TCP_STATE_CLOSEWAIT; return TCP_OK; } //************************************************************************************************** // tcpEstablished // does ESTABLISHED state input processing //************************************************************************************************** static int tcpEstablished( TCBPtr pTCB, PacketPtr pPacket) { if (pPacket->pTCPSeg->u8Code & TCP_CODE_RST) { gu32TcpEstabResets++; gu32TcpCurrEstab--; return tcpAbort(pTCB,TCP_RESET); } if (pPacket->pTCPSeg->u8Code & TCP_CODE_SYN) { gu32TcpEstabResets++; gu32TcpCurrEstab--; tcpReset(pPacket); return tcpAbort(pTCB,TCP_RESET); } if (tcpAcked(pTCB,pPacket) == TCP_GENERAL_ERROR) return TCP_OK; tcpData(pTCB,pPacket); tcpSndWindow(pTCB,pPacket); if (pTCB->u16Flags & TCP_TCB_RCV_DONE) pTCB->setState = TCP_STATE_CLOSEWAIT; return TCP_OK; } //************************************************************************************************** // (re)schedules a DELETE event for 2MSL from now //************************************************************************************************** static int tcpWait( TCBPtr pTCB) { tcpKillTimers(pTCB); tcp_tmSet(&gOutputPort,TCP_QUEUE_LEN,pTCB,DELETE,TCP_TWOMSL); return TCP_OK; } //************************************************************************************************** // tcpFin1 // does FIN_WAIT_1 state input processing //************************************************************************************************** static int tcpFin1( TCBPtr pTCB, PacketPtr pPacket) { if (pPacket->pTCPSeg->u8Code & TCP_CODE_RST) return tcpAbort(pTCB,TCP_RESET); if (pPacket->pTCPSeg->u8Code & TCP_CODE_SYN) { tcpReset(pPacket); return tcpAbort(pTCB,TCP_RESET); } if (tcpAcked(pTCB,pPacket) == TCP_GENERAL_ERROR) return TCP_OK; tcpData(pTCB,pPacket); tcpSndWindow(pTCB,pPacket); if (pTCB->u16Flags & TCP_TCB_RCV_DONE) { if (pTCB->u8NextPacketCode & TCP_CODE_FIN) // FIN not ACKed pTCB->setState = TCP_STATE_CLOSING; else { pTCB->setState = TCP_STATE_TIMEWAIT; semSignal(&pTCB->recOpenCloseSemaphore); // wake closer tcpWait(pTCB); } } else if ((pTCB->u8NextPacketCode & TCP_CODE_FIN) == 0) { semSignal(&pTCB->recOpenCloseSemaphore); pTCB->setState = TCP_STATE_FINWAIT2; } return TCP_OK; } //************************************************************************************************** // tcpFin2 // does FIN_WAIT_2 state input processing //************************************************************************************************** static int tcpFin2( TCBPtr pTCB, PacketPtr pPacket) { if (pPacket->pTCPSeg->u8Code & TCP_CODE_RST) return tcpAbort(pTCB,TCP_RESET); if (pPacket->pTCPSeg->u8Code & TCP_CODE_SYN) { tcpReset(pPacket); return tcpAbort(pTCB,TCP_RESET); } if (tcpAcked(pTCB,pPacket) == TCP_GENERAL_ERROR) return TCP_OK; tcpData(pTCB,pPacket); // for data + FIN ACKing if (pTCB->u16Flags & TCP_TCB_RCV_DONE) { pTCB->setState = TCP_STATE_TIMEWAIT; tcpWait(pTCB); } return TCP_OK; } //************************************************************************************************** // tcpCloseWait // does CLOSE_WAIT state input processing //************************************************************************************************** static int tcpCloseWait( TCBPtr pTCB, PacketPtr pPacket) { if (pPacket->pTCPSeg->u8Code & TCP_CODE_RST) { gu32TcpEstabResets++; gu32TcpCurrEstab--; return tcpAbort(pTCB,TCP_RESET); } if (pPacket->pTCPSeg->u8Code & TCP_CODE_SYN) { gu32TcpEstabResets++; gu32TcpCurrEstab--; tcpReset(pPacket); return tcpAbort(pTCB,TCP_RESET); } tcpAcked(pTCB,pPacket); tcpSndWindow(pTCB,pPacket); return TCP_OK; } //************************************************************************************************** // tcpLastAck // does LAST_ACK state input processing //************************************************************************************************** static int tcpLastAck( TCBPtr pTCB, PacketPtr pPacket) { if (pPacket->pTCPSeg->u8Code & TCP_CODE_RST) return tcpAbort(pTCB,TCP_RESET); if (pPacket->pTCPSeg->u8Code & TCP_CODE_SYN) { tcpReset(pPacket); return tcpAbort(pTCB,TCP_RESET); } tcpAcked(pTCB,pPacket); if ((pTCB->u8NextPacketCode & TCP_CODE_FIN) == 0) semSignal(&pTCB->recOpenCloseSemaphore); return TCP_OK; } //************************************************************************************************** // tcpClosing // does CLOSING state input processing //************************************************************************************************** static int tcpClosing( TCBPtr pTCB, PacketPtr pPacket) { if (pPacket->pTCPSeg->u8Code & TCP_CODE_RST) return tcpTCBDeallocate(pTCB); if (pPacket->pTCPSeg->u8Code & TCP_CODE_SYN) { tcpReset(pPacket); return tcpTCBDeallocate(pTCB); } tcpAcked(pTCB,pPacket); if ((pTCB->u8NextPacketCode & TCP_CODE_FIN) == 0) { pTCB->setState = TCP_STATE_TIMEWAIT; semSignal(&pTCB->recOpenCloseSemaphore); tcpWait(pTCB); } return TCP_OK; } //************************************************************************************************** // tcpTimeWait // does TIME_WAIT state input processing //************************************************************************************************** static int tcpTimeWait( TCBPtr pTCB, PacketPtr pPacket) { if (pPacket->pTCPSeg->u8Code & TCP_CODE_RST) return tcpTCBDeallocate(pTCB); if (pPacket->pTCPSeg->u8Code & TCP_CODE_SYN) { tcpReset(pPacket); return tcpTCBDeallocate(pTCB); } tcpAcked(pTCB,pPacket); tcpData(pTCB,pPacket); // just ACK any packets tcpWait(pTCB); return TCP_OK; } //************************************************************************************************** // tcpInput //************************************************************************************************** static void tcpInputStateDispatcher( TCBPtr pTCB, PacketPtr pPacket) { switch(pTCB->setState) { case TCP_STATE_CLOSED: tcpClosed(pTCB,pPacket); break; case TCP_STATE_LISTEN: tcpDoListen(pTCB,pPacket); break; case TCP_STATE_SYNSENT: tcpSynSent(pTCB,pPacket); break; case TCP_STATE_SYNRCVD: tcpSynReceived(pTCB,pPacket); break; case TCP_STATE_ESTABLISHED: tcpEstablished(pTCB,pPacket); break; case TCP_STATE_FINWAIT1: tcpFin1(pTCB,pPacket); break; case TCP_STATE_FINWAIT2: tcpFin2(pTCB,pPacket); break; case TCP_STATE_CLOSEWAIT: tcpCloseWait(pTCB,pPacket); break; case TCP_STATE_LASTACK: tcpLastAck(pTCB,pPacket); break; case TCP_STATE_CLOSING: tcpClosing(pTCB,pPacket); break; case TCP_STATE_TIMEWAIT: tcpTimeWait(pTCB,pPacket); break; default: break; } } //************************************************************************************************** // tcpInput //************************************************************************************************** void tcpInput( NetBuf buf, NetAttrs attrs) { TCBPtr pTCB; PacketRec recPacket; int err; recPacket.pTCPSeg = (TCPSegPtr)NETBUF_DATA(buf); recPacket.segBuf = buf; recPacket.netAttrs = attrs; err = TCP_NO_ERR; if (netbufLen(recPacket.segBuf) < sizeof(TCPSegRec)) err = TCP_HEADER_TOO_SHORT_ERR; else if (netbufLen(recPacket.segBuf) < ntohs(TCP_HEADER_LEN(recPacket.pTCPSeg))) err = TCP_HEADER_TOO_SHORT_ERR; else { tcpPacketDump(recPacket.pTCPSeg,"RN",netbufLen(buf)); if (recPacket.pTCPSeg->u16CheckSum != 0) { if (tcpCheckSum(buf,attrs,netbufLen(buf)) != 0) err = TCP_CHECKSUM_ERR; else { // converts to local host byte order tcpHeaderNet2Host(recPacket.pTCPSeg); // searches the TCB block which maches current tcp port pTCB = tcpDemux(&recPacket); if (pTCB == NULL) { // unexpected segement has arrived. generates and sends a reset. tcpReset(&recPacket); err = TCP_NO_LISTENER_ERR; } else { if (tcpIsOK(pTCB,&recPacket)) { tcpOptions(pTCB,&recPacket); tcpInputStateDispatcher(pTCB,&recPacket); } else { tcpAckIt(pTCB,&recPacket); } semSignal(&pTCB->recMutex); } } } } netbufFree(buf); netattrFree(attrs); // error occured if (err != TCP_NO_ERR) { netdbgPrintf(NETDEBUG_TCP,"tcp: Discarding upcoming packet. (%d)\n",err); ++gu32TcpInErrors; } } //************************************************************************************************** // tcpTCBInit //************************************************************************************************** static long tcpInitialSequence(void) { static long seq = 0; if (seq == 0) seq = tmUserTimerGetTicks(); seq += TCPINCR; return seq; } //************************************************************************************************** // tcpSync // initializes TCB for a new connection request //************************************************************************************************** int tcpSync( TCBPtr pTCB) // Uninitialized TCB data structure { long n32Seq; pTCB->setState = TCP_STATE_CLOSED; pTCB->setType = TCP_TYPE_CONNECTION; n32Seq = tcpInitialSequence(); pTCB->n32InitSndSeq = n32Seq; pTCB->n32SndUnacked = n32Seq; pTCB->n32SndNext = n32Seq; pTCB->n32AckSeqOfLastWinUpdt = n32Seq; pTCB->pu8sndBuf=tcpAlloc(TCP_SND_BUF_SIZE); if (pTCB->pu8sndBuf == NULL) return TCP_GENERAL_ERROR; pTCB->u32SndBufSize = TCP_SND_BUF_SIZE; pTCB->u32SndBufStart = 0; pTCB->u32SndBufCount = 0; semInit(&pTCB->recSndSemaphore,1); pTCB->pu8rcvBuf=tcpAlloc(TCP_RCV_BUF_SIZE); if (pTCB->pu8rcvBuf == NULL) return TCP_GENERAL_ERROR; pTCB->u32RcvBufSize = TCP_RCV_BUF_SIZE; pTCB->u32RcvBufStart = 0; pTCB->u32RcvBufCount = 0; pTCB->nRcvSegFragQueue = EMPTY; semInit(&pTCB->recRcvSemaphore,0); semInit(&pTCB->recOpenCloseSemaphore,0); // timer stuff pTCB->nSmoothedRoundTripTime = 0; // in sec/100 pTCB->nRoundTripDeviationEst = 0; // in sec/100 pTCB->nRetransmitTimeout = 50; // in sec/100 pTCB->nNumOfRetransmits = 0; pTCB->u8NextPacketCode = TCP_CODE_SYN; pTCB->u16Flags = 0; return TCP_OK; } //************************************************************************************************** // tcpTCBAllocate //************************************************************************************************** int tcpTCBAllocate( TCBPtr *ppTCB) /* allocated tcb data structure */ { *ppTCB = (TCBPtr)tcpAlloc(sizeof(TCBRec)); if (*ppTCB == NULL) return TCP_GENERAL_ERROR; semWait(&gTCBLinkedListMutex); (**ppTCB).pNext = (struct TCBRec*)gTCPTCBList; gTCPTCBList = *ppTCB; semSignal(&gTCBLinkedListMutex); // allocates tcb internal netattrs if (netattrNew(&(**ppTCB).netParams) == 0) { tcpFree((unsigned char *)*ppTCB); return TCP_GENERAL_ERROR; } // allocates tcb mutex semaphore semInit(&(**ppTCB).recMutex,0); // basic initialisation (**ppTCB).setState = TCP_STATE_CLOSED; (**ppTCB).pDevice = NULL; (**ppTCB).pParentTCB = NULL; return TCP_OK; } //************************************************************************************************** // tcpTCBDeallocate // deallocate a TCB and free its resources ASSUMES ptcb->tcb_mutex HELD //************************************************************************************************** int tcpTCBDeallocate(TCBPtr pTCB) { TCBPtr pCurrentTCB,pOneBeforeCurrentTCB; switch(pTCB->setType) { case TCP_TYPE_CONNECTION: tcpKillTimers(pTCB); tcpFree(pTCB->pu8sndBuf); tcpFree(pTCB->pu8rcvBuf); if (pTCB->nRcvSegFragQueue >= 0) pqFreeq(pTCB->nRcvSegFragQueue); if (pTCB->netParams != NULL) netattrFree(pTCB->netParams); break; case TCP_TYPE_SERVER: break; default: semSignal(&pTCB->recMutex); return TCP_GENERAL_ERROR; } // deallocates memory semWait(&gTCBLinkedListMutex); pCurrentTCB = gTCPTCBList; pOneBeforeCurrentTCB = gTCPTCBList; while ((pCurrentTCB != NULL) && (pCurrentTCB != pTCB)) { pOneBeforeCurrentTCB = pCurrentTCB; pCurrentTCB = (TCBPtr)pCurrentTCB->pNext; } if (pOneBeforeCurrentTCB == gTCPTCBList) gTCPTCBList = (TCBPtr)pTCB->pNext; else pOneBeforeCurrentTCB->pNext = pTCB->pNext; semSignal(&gTCBLinkedListMutex); tcpFree((unsigned char *)pTCB); return TCP_OK; }