diff options
Diffstat (limited to 'c/src/lib/libbsp/arm/nds/dswifi/arm9/source/sgIP_TCP.c')
-rw-r--r-- | c/src/lib/libbsp/arm/nds/dswifi/arm9/source/sgIP_TCP.c | 915 |
1 files changed, 0 insertions, 915 deletions
diff --git a/c/src/lib/libbsp/arm/nds/dswifi/arm9/source/sgIP_TCP.c b/c/src/lib/libbsp/arm/nds/dswifi/arm9/source/sgIP_TCP.c deleted file mode 100644 index 92e63ec6ba..0000000000 --- a/c/src/lib/libbsp/arm/nds/dswifi/arm9/source/sgIP_TCP.c +++ /dev/null @@ -1,915 +0,0 @@ -// 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;i<numsynlist;i++) { - if(synlist[i].timenext<=time) { - j=time-synlist[i].timenext; - synlist[i].timebackoff*=2; - if(synlist[i].timebackoff>SGIP_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;i<numsynlist;i++) { - if(synlist[i].localseq+1==tcpack) { // oki! this is probably legit ;) - rec=synlist[i].linked; // we have the data we need. - // remove entry from synlist - numsynlist--; - i*=3; - for(;i<numsynlist;i++) { - synlist[i]=synlist[i+1]; // assume struct copy - } - for(j=0;j<rec->maxlisten;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(datalen<delta2) delta2=datalen; - sgIP_memblock_CopyToLinear(mb,rec->buf_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;delta1<numsynlist;delta1++) { - synlist[delta1]=synlist[delta1+1]; // assume struct copy - } - } - { - unsigned long myseq,myport; - myport=tcp->destport; - 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 = windowlen<SGIP_TCP_REACK_THRESH; // indicate an additional ack should be sent when we have more space in the buffer. - if(windowlen>65535) 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;i<rec->maxlisten;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;i<numsynlist;i++) { - if(j!=i) { - synlist[j]=synlist[i]; - } - if(synlist[i].linked==rec) j--; - j++; - } - numsynlist=j; - sgIP_free(rec->listendata); - } - 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;i<rec->maxlisten;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;i<datalength;i++) { - rec->buf_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;i<buflength;i++) { - databuf[i]=rec->buf_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; -} |