// DSWifi Project - sgIP Internet Protocol Stack Implementation // Copyright (C) 2005-2006 Stephen Stair - sgstair@akkit.org - http://www.akkit.org /****************************************************************************** DSWifi Lib and test materials are licenced under the MIT open source licence: Copyright (c) 2005-2006 Stephen Stair Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************************/ #include "sgIP_TCP.h" #include "sgIP_IP.h" #include "sgIP_Hub.h" #include "sys/socket.h" sgIP_Record_TCP * tcprecords; int port_counter; unsigned long lasttime; extern unsigned long volatile sgIP_timems; sgIP_TCP_SYNCookie synlist[SGIP_TCP_MAXSYNS]; int numsynlist; // number of active entries in synlist (earliest first) void sgIP_TCP_Init() { tcprecords=0; numsynlist=0; port_counter=SGIP_TCP_FIRSTOUTGOINGPORT; lasttime=sgIP_timems; } void sgIP_TCP_Timer() { // scan through tcp records and resend anything necessary sgIP_Record_TCP * rec; int time,i,j; time=sgIP_timems-lasttime; lasttime=sgIP_timems; for(i=0;iSGIP_TCP_BACKOFFMAX) synlist[i].timebackoff=SGIP_TCP_BACKOFFMAX; if(j>synlist[i].timebackoff) synlist[i].timenext=0; else synlist[i].timenext=synlist[i].timebackoff-j; // resend SYN sgIP_TCP_SendSynReply(SGIP_TCP_FLAG_SYN|SGIP_TCP_FLAG_ACK,synlist[i].localseq,synlist[i].remoteseq,synlist[i].localip,synlist[i].remoteip,synlist[i].localport,synlist[i].remoteport,-1); } else { synlist[i].timenext-=time; } } rec=tcprecords; while(rec) { time=sgIP_timems-rec->time_last_action; switch(rec->tcpstate) { case SGIP_TCP_STATE_NODATA: // newly allocated [do nothing] case SGIP_TCP_STATE_UNUSED: // allocated & BINDed [do nothing] case SGIP_TCP_STATE_CLOSED: // Block is unused. [do nothing] case SGIP_TCP_STATE_LISTEN: // listening [do nothing] case SGIP_TCP_STATE_FIN_WAIT_2: // got ACK for our FIN, haven't got FIN yet. [do nothing] break; case SGIP_TCP_STATE_SYN_SENT: // connect initiated [resend syn] if(time>rec->time_backoff) { rec->retrycount++; if(rec->retrycount>=SGIP_TCP_MAXRETRY) { //error rec->errorcode=ECONNABORTED; rec->tcpstate=SGIP_TCP_STATE_CLOSED; break; } j=rec->time_backoff; j*=2; if(j>SGIP_TCP_BACKOFFMAX) j=SGIP_TCP_BACKOFFMAX; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_SYN,0); rec->time_backoff=j; // preserve backoff } break; case SGIP_TCP_STATE_CLOSE_WAIT: // got FIN, wait for user code to close socket & send FIN [do nothing] case SGIP_TCP_STATE_ESTABLISHED: // syns have been exchanged [check for data in buffer, send] if(rec->want_shutdown==1 && rec->buf_tx_out==rec->buf_tx_in) { // oblige & shutdown sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_FIN | SGIP_TCP_FLAG_ACK,0); if(rec->tcpstate==SGIP_TCP_STATE_CLOSE_WAIT) { rec->tcpstate=SGIP_TCP_STATE_CLOSING; } else { rec->tcpstate=SGIP_TCP_STATE_FIN_WAIT_1; } rec->want_shutdown=2; break; } j=rec->buf_tx_out-rec->buf_tx_in; if(j<0) j+=SGIP_TCP_TRANSMITBUFFERLENGTH; j+=(int)(rec->sequence-rec->sequence_next); if(j>0) {// never-sent bytes if(time>SGIP_TCP_TRANSMIT_DELAY) { // 1000 is an arbitrary constant. j=rec->buf_tx_out-rec->buf_tx_in; if(j<0) j+=SGIP_TCP_TRANSMITBUFFERLENGTH; i=(int)(rec->txwindow-rec->sequence); if(j>i) j=i; i=sgIP_IP_MaxContentsSize(rec->destip)-20; // max tcp data size if(j>i) j=i; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,i); break; } } if(time>rec->time_backoff && rec->buf_tx_out!=rec->buf_tx_in) { // resend last packet j=rec->buf_tx_out-rec->buf_tx_in; if(j<0) j+=SGIP_TCP_TRANSMITBUFFERLENGTH; i=(int)(rec->txwindow-rec->sequence); if(j>i) j=i; i=sgIP_IP_MaxContentsSize(rec->destip)-20; // max tcp data size if(j>i) j=i; j=rec->time_backoff; j*=2; if(j>SGIP_TCP_BACKOFFMAX) j=SGIP_TCP_BACKOFFMAX; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,i); rec->time_backoff=j; // preserve backoff break; } break; case SGIP_TCP_STATE_FIN_WAIT_1: // sent a FIN, haven't got FIN or ACK yet. [resend fin] if(time>rec->time_backoff) { rec->retrycount++; if(rec->retrycount>=SGIP_TCP_MAXRETRY) { //error rec->errorcode=ETIMEDOUT; rec->tcpstate=SGIP_TCP_STATE_CLOSED; break; } j=rec->time_backoff; j*=2; if(j>SGIP_TCP_BACKOFFMAX) j=SGIP_TCP_BACKOFFMAX; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_FIN,0); rec->time_backoff=j; // preserve backoff } break; case SGIP_TCP_STATE_CLOSING: // got FIN, waiting for ACK of our FIN [resend FINACK] if(time>rec->time_backoff) { rec->retrycount++; if(rec->retrycount>=SGIP_TCP_MAXRETRY) { //error rec->errorcode=ETIMEDOUT; rec->tcpstate=SGIP_TCP_STATE_CLOSED; break; } j=rec->time_backoff; j*=2; if(j>SGIP_TCP_BACKOFFMAX) j=SGIP_TCP_BACKOFFMAX; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_FIN | SGIP_TCP_FLAG_ACK,0); rec->time_backoff=j; // preserve backoff } break; case SGIP_TCP_STATE_LAST_ACK: // wait for ACK of our last FIN [resend FIN] if(time>rec->time_backoff) { rec->retrycount++; if(rec->retrycount>=SGIP_TCP_MAXRETRY) { //error rec->errorcode=ETIMEDOUT; rec->tcpstate=SGIP_TCP_STATE_CLOSED; break; } j=rec->time_backoff; j*=2; if(j>SGIP_TCP_BACKOFFMAX) j=SGIP_TCP_BACKOFFMAX; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_FIN,0); rec->time_backoff=j; // preserve backoff } break; case SGIP_TCP_STATE_TIME_WAIT: // wait to ensure remote tcp knows it's been terminated. [reset in 2MSL] if(time>SGIP_TCP_TIMEMS_2MSL) { rec->errorcode=ESHUTDOWN; rec->tcpstate=SGIP_TCP_STATE_CLOSED; } break; } rec=rec->next; } } unsigned long sgIP_TCP_support_seqhash(unsigned long srcip, unsigned long destip, unsigned short srcport, unsigned short destport) { unsigned long hash; hash=destip; hash ^= destport * (0x02041089+sgIP_timems); hash ^= srcport * (0x080810422+(sgIP_timems<<1)); hash ^= srcip * (0x48841221+(sgIP_timems<<3)); hash ^= destip * (0x04020108+(sgIP_timems<<5)); return hash; } int sgIP_TCP_GetUnusedOutgoingPort() { int myport,clear; sgIP_Record_TCP * rec; port_counter+=(sgIP_timems&1023); // semi-random if(port_counter>SGIP_TCP_LASTOUTGOINGPORT) port_counter=SGIP_TCP_FIRSTOUTGOINGPORT; while(1) { rec = tcprecords; myport=port_counter++; if(port_counter>SGIP_TCP_LASTOUTGOINGPORT) port_counter=SGIP_TCP_FIRSTOUTGOINGPORT; clear=1; while(rec) { if(rec->srcport==myport && rec->tcpstate!=SGIP_TCP_STATE_CLOSED && rec->tcpstate!=SGIP_TCP_STATE_NODATA) { clear=0; break; } rec=rec->next; } if(clear) return myport; } } int sgIP_TCP_CalcChecksum(sgIP_memblock * mb, unsigned long srcip, unsigned long destip, int totallength) { int checksum; if(!mb) return 0; if(mb->totallength&1) mb->datastart[mb->totallength]=0; checksum=sgIP_memblock_IPChecksum(mb,0,mb->totallength); // add in checksum of "faux header" checksum+=(destip&0xFFFF); checksum+=(destip>>16); checksum+=(srcip&0xFFFF); checksum+=(srcip>>16); checksum+=htons(totallength); checksum+=(6)<<8; checksum= (checksum&0xFFFF) +(checksum>>16); checksum= (checksum&0xFFFF) +(checksum>>16); return checksum; } int sgIP_TCP_ReceivePacket(sgIP_memblock * mb, unsigned long srcip, unsigned long destip) { if(!mb) return 0; sgIP_Header_TCP * tcp; int delta1,delta2, delta3,datalen, shouldReply; unsigned long tcpack,tcpseq; tcp = (sgIP_Header_TCP *) mb->datastart; // 01234567890123456789012345678901 // TCPxxxxxxxx-xxxxxxxx,xxxx-xxxx //SGIP_DEBUG_MESSAGE(("TCP%08X-%08X,%04X-%04X",srcip,destip,tcp->srcport,tcp->destport)); // -Lxxxx,Cxxxx,Fxx,hx,Axxxxxxxx //SGIP_DEBUG_MESSAGE(("-L%04X,C%04X,F%02X,h%X,A%08X",mb->totallength,tcp->checksum,tcp->tcpflags,tcp->dataofs_>>4,tcp->acknum)); if(tcp->checksum!=0x0000 && sgIP_TCP_CalcChecksum(mb,srcip,destip,mb->totallength)!=0xFFFF) { // checksum is invalid! SGIP_DEBUG_MESSAGE(("TCP receive checksum incorrect")); sgIP_memblock_free(mb); return 0; } sgIP_Record_TCP * rec; rec=tcprecords; // find associated block. while(rec) { if(rec->srcport==tcp->destport && (rec->srcip==destip || rec->srcip==0)) { if((rec->tcpstate==SGIP_TCP_STATE_LISTEN && (tcp->tcpflags&SGIP_TCP_FLAG_SYN)) || rec->destport==tcp->srcport) break; } rec=rec->next; } if(!rec) { // could be completion of an incoming connection? tcpack=htonl(tcp->acknum); if(tcp->tcpflags&SGIP_TCP_FLAG_ACK) { int i,j; for(i=0;imaxlisten;j++) if(!rec->listendata[j]) break; // find last entry in listen queue if(j==rec->maxlisten) { rec=0; break; } // discard this connection! we have no space in the listen queue. rec->listendata[j]=sgIP_TCP_AllocRecord(); j++; if(j!=rec->maxlisten) rec->listendata[j]=0; rec=rec->listendata[j-1]; // fill in data about the connection. rec->tcpstate=SGIP_TCP_STATE_ESTABLISHED; rec->time_last_action=sgIP_timems; rec->time_backoff=SGIP_TCP_GENRETRYMS; // backoff timer rec->srcip=destip; rec->destip=srcip; rec->srcport=tcp->destport; rec->destport=tcp->srcport; rec->sequence=htonl(tcp->acknum); rec->ack=htonl(tcp->seqnum); rec->sequence_next=rec->sequence; rec->rxwindow=rec->ack+1400; // last byte in receive window rec->txwindow=rec->sequence+htons(tcp->window); sgIP_memblock_free(mb); return 0; } } } } if(!rec) { // we don't have a clue what this one is. #ifndef SGIP_TCP_STEALTH // send a RST sgIP_TCP_SendSynReply(SGIP_TCP_FLAG_RST,ntohl(tcp->acknum),0,destip,srcip,tcp->destport,tcp->srcport,0); #endif sgIP_memblock_free(mb); return 0; } // check sequence and ACK numbers, to ensure they're in range. tcpack=htonl(tcp->acknum); tcpseq=htonl(tcp->seqnum); datalen=mb->totallength-(tcp->dataofs_>>4)*4; shouldReply=0; if(tcp->tcpflags&SGIP_TCP_FLAG_RST) { // verify if rst is legit, and act on it. // check seq against receive window delta1=(int)(tcpseq-rec->ack); delta2=(int)(rec->rxwindow-tcpseq); if(delta1<0 || delta2<0 || rec->tcpstate==SGIP_TCP_STATE_LISTEN) { // out of range, ignore } else { // in range! reset connection. rec->errorcode=ECONNRESET; rec->tcpstate=SGIP_TCP_STATE_CLOSED; } sgIP_memblock_free(mb); return 0; } if((tcp->tcpflags&SGIP_TCP_FLAG_ACK) && !(tcp->tcpflags&SGIP_TCP_FLAG_SYN)) { // doesn't work very well with SYN. // verify ack value (checking ack sequence vs transmit window) delta1=(int)(tcpack-rec->sequence); delta2=(int)(rec->txwindow-tcpack); if(delta1<0 || delta2<0) { // invalid ack range, discard packet sgIP_memblock_free(mb); return 0; } delta2=tcpack-rec->sequence; rec->sequence=tcpack; delta2+=rec->buf_tx_in; if(delta2>=SGIP_TCP_TRANSMITBUFFERLENGTH) delta2-=SGIP_TCP_TRANSMITBUFFERLENGTH; rec->buf_tx_in=delta2; if(delta1>0) shouldReply=1; } rec->txwindow=rec->sequence+htons(tcp->window); // now, decide what to do with our nice new shiny memblock... // for most states, receive data switch(rec->tcpstate) { case SGIP_TCP_STATE_NODATA: // newly allocated case SGIP_TCP_STATE_UNUSED: // allocated & BINDed case SGIP_TCP_STATE_CLOSED: // Block is unused. case SGIP_TCP_STATE_LISTEN: // listening case SGIP_TCP_STATE_TIME_WAIT: // wait to ensure remote tcp knows it's been terminated. case SGIP_TCP_STATE_SYN_SENT: // connect initiated case SGIP_TCP_STATE_CLOSE_WAIT: // got FIN, wait for user code to close socket & send FIN case SGIP_TCP_STATE_CLOSING: // got FIN, waiting for ACK of our FIN case SGIP_TCP_STATE_LAST_ACK: // wait for ACK of our last FIN break; case SGIP_TCP_STATE_SYN_RECEIVED: // spawned from listen socket; or from syn sent. case SGIP_TCP_STATE_ESTABLISHED: // syns have been exchanged case SGIP_TCP_STATE_FIN_WAIT_1: // sent a FIN, haven't got FIN or ACK yet. case SGIP_TCP_STATE_FIN_WAIT_2: // got ACK for our FIN, haven't got FIN yet. if(tcp->tcpflags&SGIP_TCP_FLAG_ACK) { // check end of incoming data against receive window delta1=(int)(tcpseq+datalen-rec->ack); // check end of data vs start of window (>=0, end of data is equal to or after start of unreceived data) delta2=(int)(rec->rxwindow-tcpseq-datalen); // check end of data vs end of window (>=0, end of data is equal to or before end of rx window) delta3=(int)(rec->ack-tcpseq); // check start of data vs start of window (>=0, start of data is equal or before the next expected byte) if(delta1<0 || delta2<0 || delta3<0) { if(delta1>-SGIP_TCP_RECEIVEBUFFERLENGTH) { // ack it anyway, they got lost on the retard bus. sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); } break; // out of range, they should know better. } { int datastart=(tcp->dataofs_>>4)*4; delta1=(int)(tcpseq-rec->ack); if(delta1<0) { // data is partly ack'd...just copy what we need. datastart-=delta1; datalen+=delta1; } // copy data into the fifo rec->ack+=datalen; delta1=datalen; while(datalen>0) { // don't actually need to check the rx buffer length, if the ack check approved it, it will be in range (not overflow) by default delta2=SGIP_TCP_RECEIVEBUFFERLENGTH-rec->buf_rx_out; // number of bytes til the end of the buffer if(datalenbuf_rx+rec->buf_rx_out,datastart,delta2); datalen-=delta2; datastart+=delta2; rec->buf_rx_out += delta2; if(rec->buf_rx_out>=SGIP_TCP_RECEIVEBUFFERLENGTH) rec->buf_rx_out-=SGIP_TCP_RECEIVEBUFFERLENGTH; } if(rec->tcpstate==SGIP_TCP_STATE_FIN_WAIT_1 || rec->tcpstate==SGIP_TCP_STATE_FIN_WAIT_2) break; if(shouldReply || delta1>=0) { // send a packet in reply, ha! delta1=rec->buf_tx_out-rec->buf_tx_in; if(delta1<0) delta1+=SGIP_TCP_TRANSMITBUFFERLENGTH; delta2=(int)(rec->txwindow-rec->sequence); if(delta1>delta2) delta1=delta2; delta2=sgIP_IP_MaxContentsSize(rec->destip)-20; // max tcp data size if(delta1>delta2) delta1=delta2; if(delta1>=0) { // could be less than 0, but very odd. sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,delta1); } } } } } // decide what to do with the others switch(rec->tcpstate) { case SGIP_TCP_STATE_NODATA: // newly allocated case SGIP_TCP_STATE_UNUSED: // allocated & BINDed case SGIP_TCP_STATE_CLOSED: // Block is unused. break; // can't do anything in these states. case SGIP_TCP_STATE_LISTEN: // listening if(tcp->tcpflags&SGIP_TCP_FLAG_SYN) { // other end requesting a connection if(numsynlist==SGIP_TCP_MAXSYNS) { numsynlist--; for(delta1=0;delta1destport; myseq=sgIP_TCP_support_seqhash(srcip,destip,tcp->srcport,myport); // send relevant synack sgIP_TCP_SendSynReply(SGIP_TCP_FLAG_SYN|SGIP_TCP_FLAG_ACK,myseq,tcpseq+1,destip,srcip,myport,tcp->srcport,-1); synlist[numsynlist].localseq=myseq; synlist[numsynlist].timebackoff=SGIP_TCP_SYNRETRYMS; synlist[numsynlist].timenext=SGIP_TCP_SYNRETRYMS; synlist[numsynlist].linked=rec; synlist[numsynlist].remoteseq=tcpseq+1; synlist[numsynlist].remoteip=srcip; synlist[numsynlist].localip=destip; synlist[numsynlist].localport=myport; synlist[numsynlist].remoteport=tcp->srcport; numsynlist++; } } break; case SGIP_TCP_STATE_SYN_SENT: // connect initiated switch(tcp->tcpflags&(SGIP_TCP_FLAG_SYN|SGIP_TCP_FLAG_ACK)) { case SGIP_TCP_FLAG_SYN | SGIP_TCP_FLAG_ACK: // both flags set rec->ack=tcpseq+1; rec->sequence=tcpack; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); rec->tcpstate=SGIP_TCP_STATE_ESTABLISHED; rec->retrycount=0; break; case SGIP_TCP_FLAG_SYN: // just got a syn... rec->ack=tcpseq+1; rec->sequence=tcpack; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); rec->tcpstate=SGIP_TCP_STATE_SYN_RECEIVED; rec->retrycount=0; break; } break; case SGIP_TCP_STATE_SYN_RECEIVED: // spawned from listen socket; or from syn sent. if(tcp->tcpflags&SGIP_TCP_FLAG_ACK) { rec->tcpstate=SGIP_TCP_STATE_ESTABLISHED; rec->retrycount=0; } break; case SGIP_TCP_STATE_ESTABLISHED: // syns have been exchanged if(tcp->tcpflags&SGIP_TCP_FLAG_FIN) { // check sequence against next ack number delta1=(int)(tcpseq-rec->ack); //delta2=(int)(rec->rxwindow-tcpseq); if(delta1<0 || delta1>0) break; // out of range, they should know better. // this is the end... rec->tcpstate=SGIP_TCP_STATE_CLOSE_WAIT; rec->ack=tcpseq+datalen+1; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); } break; case SGIP_TCP_STATE_FIN_WAIT_1: // sent a FIN, haven't got FIN or ACK yet. switch(tcp->tcpflags&(SGIP_TCP_FLAG_FIN|SGIP_TCP_FLAG_ACK)) { case SGIP_TCP_FLAG_FIN: // check sequence against next ack number delta1=(int)(tcpseq-rec->ack); //delta2=(int)(rec->rxwindow-tcpseq); if(delta1<0 || delta1>0) break; // out of range, they should know better. rec->tcpstate=SGIP_TCP_STATE_CLOSING; rec->ack=tcpseq+datalen+1; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); break; case SGIP_TCP_FLAG_ACK: // already checked ack against appropriate window rec->tcpstate=SGIP_TCP_STATE_FIN_WAIT_2; break; case (SGIP_TCP_FLAG_FIN | SGIP_TCP_FLAG_ACK): // already checked ack, check sequence though // check sequence against next ack number delta1=(int)(tcpseq-rec->ack); //delta2=(int)(rec->rxwindow-tcpseq); if(delta1<0 || delta1>0) break; // out of range, they should know better. rec->tcpstate=SGIP_TCP_STATE_TIME_WAIT; rec->ack=tcpseq+datalen+1; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); break; } break; case SGIP_TCP_STATE_FIN_WAIT_2: // got ACK for our FIN, haven't got FIN yet. if(tcp->tcpflags&SGIP_TCP_FLAG_FIN) { // check sequence against next ack number delta1=(int)(tcpseq-rec->ack); //delta2=(int)(rec->rxwindow-tcpseq); if(delta1<0 || delta1>0) break; // out of range, they should know better. rec->tcpstate=SGIP_TCP_STATE_TIME_WAIT; rec->ack=tcpseq+datalen+1; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); } break; case SGIP_TCP_STATE_CLOSE_WAIT: // got FIN, wait for user code to close socket & send FIN if(tcp->tcpflags&SGIP_TCP_FLAG_FIN) { // check sequence against next ack number delta1=(int)(tcpseq-rec->ack); //delta2=(int)(rec->rxwindow-tcpseq); if(delta1<0 || delta1>0) break; // out of range, they should know better. sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); // they still don't seem to have got our ack, we'll send it again. } break; case SGIP_TCP_STATE_CLOSING: // got FIN, waiting for ACK of our FIN switch(tcp->tcpflags&(SGIP_TCP_FLAG_FIN|SGIP_TCP_FLAG_ACK)) { case SGIP_TCP_FLAG_FIN: // check sequence against receive window delta1=(int)(tcpseq-rec->ack); delta2=(int)(rec->rxwindow-tcpseq); if(delta1<1 || delta2<0) break; // out of range, they should know better. sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); // resend their ack. break; case SGIP_TCP_FLAG_ACK: // already checked ack against appropriate window rec->tcpstate=SGIP_TCP_STATE_TIME_WAIT; break; case (SGIP_TCP_FLAG_FIN | SGIP_TCP_FLAG_ACK): // already checked ack, check sequence though // check sequence against receive window delta1=(int)(tcpseq-rec->ack); delta2=(int)(rec->rxwindow-tcpseq); if(delta1<1 || delta2<0) break; // out of range, they should know better. rec->tcpstate=SGIP_TCP_STATE_TIME_WAIT; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); break; } break; case SGIP_TCP_STATE_LAST_ACK: // wait for ACK of our last FIN if(tcp->tcpflags&SGIP_TCP_FLAG_ACK) { rec->tcpstate=SGIP_TCP_STATE_TIME_WAIT; } break; case SGIP_TCP_STATE_TIME_WAIT: // wait to ensure remote tcp knows it's been terminated. if(tcp->tcpflags&SGIP_TCP_FLAG_FIN) { // check sequence against receive window delta1=(int)(tcpseq-rec->ack); delta2=(int)(rec->rxwindow-tcpseq); if(delta1<1 || delta2<0) break; // out of range, they should know better. sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); // send 'em a grat ACK } break; } sgIP_memblock_free(mb); return 0; } sgIP_memblock * sgIP_TCP_GenHeader(sgIP_Record_TCP * rec, int flags, int datalength) { sgIP_memblock * mb = sgIP_memblock_alloc(datalength+20+sgIP_IP_RequiredHeaderSize()); int windowlen; if(!mb) return 0; sgIP_memblock_exposeheader(mb,-sgIP_IP_RequiredHeaderSize()); // hide IP header space for later sgIP_Header_TCP * tcp = (sgIP_Header_TCP *) mb->datastart; tcp->srcport=rec->srcport; tcp->destport=rec->destport; tcp->seqnum=htonl(rec->sequence); tcp->acknum=htonl(rec->ack); tcp->tcpflags=flags; tcp->urg_ptr=0; // no support for URG data atm. tcp->checksum=0; tcp->dataofs_=5<<4; // header length == 20 (5*32bit) windowlen=rec->buf_rx_out-rec->buf_rx_in; if(windowlen<0) windowlen+=SGIP_TCP_RECEIVEBUFFERLENGTH; // we now have the amount in the buffer windowlen = SGIP_TCP_RECEIVEBUFFERLENGTH-windowlen-1; if(windowlen<0) windowlen=0; if(flags&SGIP_TCP_FLAG_ACK) rec->want_reack = windowlen65535) windowlen=65535; if(windowlen>1400) windowlen=1400; // don't want to deal with IP fragmentation. rec->rxwindow=rec->ack+windowlen; // last byte in receive window tcp->window=htons(windowlen); return mb; } void sgIP_TCP_FixChecksum(unsigned long srcip, unsigned long destip, sgIP_memblock * mb) { int checksum; if(!mb) return; sgIP_Header_TCP * tcp; tcp = (sgIP_Header_TCP *) mb->datastart; tcp->checksum=0; checksum=sgIP_memblock_IPChecksum(mb,0,mb->totallength); // add in checksum of "faux header" checksum+=(destip&0xFFFF); checksum+=(destip>>16); checksum+=(srcip&0xFFFF); checksum+=(srcip>>16); checksum+=htons(mb->totallength); checksum+=(6)<<8; checksum=(checksum&0xFFFF) + (checksum>>16); checksum=(checksum&0xFFFF) + (checksum>>16); checksum = ~checksum; if(checksum==0) checksum=0xFFFF; tcp->checksum=checksum; } int sgIP_TCP_SendPacket(sgIP_Record_TCP * rec, int flags, int datalength) { // data sent is taken directly from the TX fifo. int i,j,k; if(!rec) return 0; SGIP_INTR_PROTECT(); j=rec->buf_tx_out-rec->buf_tx_in; if(j<0) j+=SGIP_TCP_TRANSMITBUFFERLENGTH; if(datalength>j) datalength=j; sgIP_memblock * mb =sgIP_TCP_GenHeader(rec,flags,datalength); if(!mb) { SGIP_INTR_UNPROTECT(); return 0; } j=20; // destination offset in memblock for data rec->sequence_next=rec->sequence+datalength; k=rec->buf_tx_in; while(datalength>0) { i=SGIP_TCP_TRANSMITBUFFERLENGTH-rec->buf_tx_in; if(i>datalength)i=datalength; sgIP_memblock_CopyFromLinear(mb,rec->buf_tx+k,j,i); k+=i; if(k>=SGIP_TCP_TRANSMITBUFFERLENGTH) k-=SGIP_TCP_TRANSMITBUFFERLENGTH; j+=i; datalength-=i; } sgIP_TCP_FixChecksum(rec->srcip,rec->destip,mb); sgIP_IP_SendViaIP(mb,6,rec->srcip,rec->destip); rec->time_last_action=sgIP_timems; // semi-generic timer. rec->time_backoff=SGIP_TCP_GENRETRYMS; // backoff timer SGIP_INTR_UNPROTECT(); return 0; } int sgIP_TCP_SendSynReply(int flags,unsigned long seq, unsigned long ack, unsigned long srcip, unsigned long destip, int srcport, int destport, int windowlen) { SGIP_INTR_PROTECT(); sgIP_memblock * mb = sgIP_memblock_alloc(20+sgIP_IP_RequiredHeaderSize()); if(!mb) { SGIP_INTR_UNPROTECT(); return 0; } sgIP_memblock_exposeheader(mb,-sgIP_IP_RequiredHeaderSize()); // hide IP header space for later sgIP_Header_TCP * tcp = (sgIP_Header_TCP *) mb->datastart; tcp->srcport=srcport; tcp->destport=destport; tcp->seqnum=htonl(seq); tcp->acknum=htonl(ack); tcp->tcpflags=flags; tcp->urg_ptr=0; // no support for URG data atm. tcp->checksum=0; tcp->dataofs_=5<<4; // header length == 20 (5*32bit) if(windowlen<0 || windowlen>1400) windowlen=1400; // don't want to deal with IP fragmentation. tcp->window=htons(windowlen); sgIP_TCP_FixChecksum(srcip,destip,mb); sgIP_IP_SendViaIP(mb,6,srcip,destip); SGIP_INTR_UNPROTECT(); return 0; } sgIP_Record_TCP * sgIP_TCP_AllocRecord() { SGIP_INTR_PROTECT(); sgIP_Record_TCP * rec; rec = sgIP_malloc(sizeof(sgIP_Record_TCP)); if(rec) { rec->buf_oob_in=0; rec->buf_oob_out=0; rec->buf_rx_in=0; rec->buf_rx_out=0; rec->buf_tx_in=0; rec->buf_tx_out=0; rec->tcpstate=0; rec->next=tcprecords; tcprecords=rec; rec->maxlisten=0; rec->srcip=0; rec->retrycount=0; rec->errorcode=0; rec->listendata=0; rec->want_shutdown=0; rec->want_reack=0; } SGIP_INTR_UNPROTECT(); return rec; } void sgIP_TCP_FreeRecord(sgIP_Record_TCP * rec) { if(!rec) return; SGIP_INTR_PROTECT(); sgIP_Record_TCP * t; int i,j; rec->tcpstate=0; if(tcprecords==rec) { tcprecords=rec->next; } else { t=tcprecords; while(t) { if(t->next==rec) { t->next=rec->next; break; } t=t->next; } } if(rec->listendata) { for(i=0;imaxlisten;i++) { if(!rec->listendata[i]) break; sgIP_TCP_FreeRecord(rec->listendata[i]); } // kill any possible waiting elements in the SYN chain. j=0; for(i=0;ilistendata); } sgIP_free(rec); SGIP_INTR_UNPROTECT(); } int sgIP_TCP_Bind(sgIP_Record_TCP * rec, int srcport, unsigned long srcip) { if(!rec) return 0; SGIP_INTR_PROTECT(); if(rec->tcpstate==SGIP_TCP_STATE_NODATA) { rec->srcip=srcip; rec->srcport=srcport; rec->tcpstate=SGIP_TCP_STATE_UNUSED; } SGIP_INTR_UNPROTECT(); return 0; } int sgIP_TCP_Listen(sgIP_Record_TCP * rec, int maxlisten) { if(!rec) return SGIP_ERROR(EINVAL); if(rec->tcpstate!=SGIP_TCP_STATE_UNUSED) return SGIP_ERROR(EINVAL); int err; SGIP_INTR_PROTECT(); if(rec->maxlisten!=0) { // we're *already* listening. err=0; } else { err=0; if(maxlisten<=0) maxlisten=1; rec->maxlisten=maxlisten; rec->listendata = (sgIP_Record_TCP **) sgIP_malloc(maxlisten*4); // pointers to TCP records, 0-terminated list. if(!rec->listendata) { rec->maxlisten=0; err=0; } else {rec->tcpstate=SGIP_TCP_STATE_LISTEN; rec->listendata[0]=0;} } SGIP_INTR_UNPROTECT(); return err; } sgIP_Record_TCP * sgIP_TCP_Accept(sgIP_Record_TCP * rec) { if(!rec) return (sgIP_Record_TCP *)SGIP_ERROR0(EINVAL); if(rec->tcpstate!=SGIP_TCP_STATE_LISTEN) return (sgIP_Record_TCP *)SGIP_ERROR0(EINVAL); int err,i; sgIP_Record_TCP * t; SGIP_INTR_PROTECT(); if(!rec->listendata) err=SGIP_ERROR0(EINVAL); else { if(!rec->listendata[0]) { err=SGIP_ERROR0(EWOULDBLOCK); } else { t=rec->listendata[0]; for(i=1;imaxlisten;i++) { rec->listendata[i-1]=rec->listendata[i]; } rec->listendata[i-1]=0; SGIP_INTR_UNPROTECT(); return t; } } SGIP_INTR_UNPROTECT(); return 0; } int sgIP_TCP_Close(sgIP_Record_TCP * rec) { if(!rec) return SGIP_ERROR(EINVAL); SGIP_INTR_PROTECT(); if(rec->want_shutdown==0) rec->want_shutdown=1; SGIP_INTR_UNPROTECT(); return 0; } int sgIP_TCP_Connect(sgIP_Record_TCP * rec, unsigned long destip, int destport) { if(!rec) return SGIP_ERROR(EINVAL); SGIP_INTR_PROTECT(); if(rec->tcpstate==SGIP_TCP_STATE_NODATA) { // need to bind a local address rec->srcip=sgIP_IP_GetLocalBindAddr(0,destip); rec->srcport=htons(sgIP_TCP_GetUnusedOutgoingPort()); rec->destip=destip; rec->destport=destport; } else if(rec->tcpstate==SGIP_TCP_STATE_UNUSED) { // already bound to a local address. rec->srcip=sgIP_IP_GetLocalBindAddr(rec->srcip,destip); rec->destip=destip; rec->destport=destport; } else { SGIP_INTR_UNPROTECT(); return SGIP_ERROR(EINVAL); } // send a SYN packet, and advance the state of the connection rec->sequence=sgIP_TCP_support_seqhash(rec->srcip,rec->destip,rec->srcport,rec->destport); sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_SYN,0); rec->retrycount=0; rec->tcpstate=SGIP_TCP_STATE_SYN_SENT; SGIP_INTR_UNPROTECT(); return 0; } int sgIP_TCP_Send(sgIP_Record_TCP * rec, const char * datatosend, int datalength, int flags) { if(!rec || !datatosend) return SGIP_ERROR(EINVAL); if(rec->want_shutdown) return SGIP_ERROR(ESHUTDOWN); SGIP_INTR_PROTECT(); int bufsize; bufsize=rec->buf_tx_out-rec->buf_tx_in; if(bufsize<0) bufsize+=SGIP_TCP_TRANSMITBUFFERLENGTH; if(bufsize==0) { rec->time_last_action=sgIP_timems; rec->time_backoff=SGIP_TCP_GENRETRYMS; } // first byte sent, set up delay before sending bufsize=SGIP_TCP_TRANSMITBUFFERLENGTH-bufsize-1; // space left in buffer if(datalength>bufsize) datalength=bufsize; int i,j; j=rec->buf_tx_out; for(i=0;ibuf_tx[j++]=datatosend[i]; if(j==SGIP_TCP_TRANSMITBUFFERLENGTH) j=0; } rec->buf_tx_out = j; // check for immediate transmit j=rec->buf_tx_out-rec->buf_tx_in; if(j<0) j+=SGIP_TCP_TRANSMITBUFFERLENGTH; j+=(int)(rec->sequence-rec->sequence_next); if(j>SGIP_TCP_TRANSMIT_IMMTHRESH && rec->tcpstate==SGIP_TCP_STATE_ESTABLISHED) { j=(int)(rec->sequence_next-rec->sequence); if(j<1000) { // arbitrary constant. j=rec->buf_tx_out-rec->buf_tx_in; if(j<0) j+=SGIP_TCP_TRANSMITBUFFERLENGTH; i=(int)(rec->txwindow-rec->sequence); if(j>i) j=i; i=sgIP_IP_MaxContentsSize(rec->destip)-20; // max tcp data size if(j>i) j=i; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,i); rec->retrycount=0; } } SGIP_INTR_UNPROTECT(); if(datalength==0) return SGIP_ERROR(EWOULDBLOCK); return datalength; } int sgIP_TCP_Recv(sgIP_Record_TCP * rec, char * databuf, int buflength, int flags) { if(!rec || !databuf) return SGIP_ERROR(EINVAL); //error if(rec->buf_rx_in==rec->buf_rx_out) { if(rec->tcpstate==SGIP_TCP_STATE_CLOSED || rec->tcpstate==SGIP_TCP_STATE_CLOSE_WAIT) { if(rec->errorcode) return SGIP_ERROR(rec->errorcode); return SGIP_ERROR0(ESHUTDOWN); } return SGIP_ERROR(EWOULDBLOCK); //error no data } SGIP_INTR_PROTECT(); int rxlen = rec->buf_rx_out - rec->buf_rx_in; if(rxlen<0) rxlen+=SGIP_TCP_RECEIVEBUFFERLENGTH; if(buflength>rxlen) buflength=rxlen; int i,j; j=rec->buf_rx_in; for(i=0;ibuf_rx[j++]; if(j==SGIP_TCP_RECEIVEBUFFERLENGTH) j=0; } if(!(flags&MSG_PEEK)) { rec->buf_rx_in=j; if(rec->want_reack) { i=rec->buf_rx_out-rec->buf_rx_in; if(i<0) i+=SGIP_TCP_RECEIVEBUFFERLENGTH; // we now have the amount in the buffer i = SGIP_TCP_RECEIVEBUFFERLENGTH-i-1; if(i<0) i=0; if(i>SGIP_TCP_REACK_THRESH) { rec->want_reack=0; sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); } } } SGIP_INTR_UNPROTECT(); return buflength; }