X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=KernelLand%2FModules%2FIPStack%2Ftcp.c;h=ac58ec3721c185f485a832040315d7e8e2dd6a6a;hb=7c63df053c650f4331949b5c1d8f64588fe98620;hp=2bb90d1bbb69a624e4809e658a616306a921d2a0;hpb=29707ccdcce3383cc8ae264b644643714418b03f;p=tpg%2Facess2.git diff --git a/KernelLand/Modules/IPStack/tcp.c b/KernelLand/Modules/IPStack/tcp.c index 2bb90d1b..ac58ec37 100644 --- a/KernelLand/Modules/IPStack/tcp.c +++ b/KernelLand/Modules/IPStack/tcp.c @@ -2,16 +2,14 @@ * Acess2 IP Stack * - TCP Handling */ -#define DEBUG 0 +#define DEBUG 1 #include "ipstack.h" #include "ipv4.h" #include "ipv6.h" #include "tcp.h" -#define USE_SELECT 1 #define HEXDUMP_INCOMING 0 #define HEXDUMP_OUTGOING 0 -#define CACHE_FUTURE_PACKETS_IN_BYTES 1 // Use a ring buffer to cache out of order packets #define TCP_MIN_DYNPORT 0xC000 #define TCP_MAX_HALFOPEN 1024 // Should be enough @@ -22,32 +20,39 @@ #define TCP_DACK_THRESHOLD 4096 #define TCP_DACK_TIMEOUT 500 +#define TCP_DEBUG 0 // Set to non-0 to enable TCP packet logging + // === PROTOTYPES === void TCP_Initialise(void); void TCP_StartConnection(tTCPConnection *Conn); void TCP_SendPacket(tTCPConnection *Conn, tTCPHeader *Header, size_t DataLen, const void *Data); +void TCP_int_SendPacket(tInterface *Interface, const void *Dest, tTCPHeader *Header, size_t Length, const void *Data); void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer); void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length); int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t Length); void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection); -void TCP_INT_SendACK(tTCPConnection *Connection); +void TCP_int_SendDelayedACK(void *ConnPtr); +void TCP_INT_SendACK(tTCPConnection *Connection, const char *Reason); Uint16 TCP_GetUnusedPort(); int TCP_AllocatePort(Uint16 Port); int TCP_DeallocatePort(Uint16 Port); +tTCPConnection *TCP_int_CreateConnection(tInterface *Interface, enum eTCPConnectionState State); +void TCP_int_FreeTCB(tTCPConnection *Connection); // --- Server tVFS_Node *TCP_Server_Init(tInterface *Interface); int TCP_Server_ReadDir(tVFS_Node *Node, int Pos, char Name[FILENAME_MAX]); -tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, const char *Name); +tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, const char *Name, Uint Flags); int TCP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data); void TCP_Server_Close(tVFS_Node *Node); // --- Client tVFS_Node *TCP_Client_Init(tInterface *Interface); -size_t TCP_Client_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer); -size_t TCP_Client_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer); +size_t TCP_Client_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags); +size_t TCP_Client_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags); int TCP_Client_IOCtl(tVFS_Node *Node, int ID, void *Data); void TCP_Client_Close(tVFS_Node *Node); // --- Helpers int WrapBetween(Uint32 Lower, Uint32 Value, Uint32 Higher, Uint32 MaxValue); +Uint32 GetRelative(Uint32 Base, Uint32 Value); // === TEMPLATES === tSocketFile gTCP_ServerFile = {NULL, "tcps", TCP_Server_Init}; @@ -84,7 +89,7 @@ Uint32 gaTCP_PortBitmap[0x800]; */ void TCP_Initialise(void) { - giTCP_NextOutPort += rand()%32; + giTCP_NextOutPort += rand()%128; IPStack_AddFile(&gTCP_ServerFile); IPStack_AddFile(&gTCP_ClientFile); IPv4_RegisterCallback(IP4PROT_TCP, TCP_GetPacket); @@ -98,6 +103,11 @@ void TCP_Initialise(void) * \param Data Packet data (cast as a TCP Header) */ void TCP_SendPacket( tTCPConnection *Conn, tTCPHeader *Header, size_t Length, const void *Data ) +{ + TCP_int_SendPacket(Conn->Interface, &Conn->RemoteIP, Header, Length, Data); +} + +void TCP_int_SendPacket(tInterface *Interface, const void *Dest, tTCPHeader *Header, size_t Length, const void *Data ) { tIPStackBuffer *buffer; Uint16 checksum[3]; @@ -109,8 +119,8 @@ void TCP_SendPacket( tTCPConnection *Conn, tTCPHeader *Header, size_t Length, co IPStack_Buffer_AppendSubBuffer(buffer, sizeof(*Header), 0, Header, NULL, NULL); LOG("Sending %i+%i to %s:%i", sizeof(*Header), Length, - IPStack_PrintAddress(Conn->Interface->Type, &Conn->RemoteIP), - Conn->RemotePort + IPStack_PrintAddress(Interface->Type, Dest), + ntohs(Header->DestPort) ); Header->Checksum = 0; @@ -119,38 +129,59 @@ void TCP_SendPacket( tTCPConnection *Conn, tTCPHeader *Header, size_t Length, co // TODO: Fragment packet - switch( Conn->Interface->Type ) + switch( Interface->Type ) { case 4: // Get IPv4 pseudo-header checksum { Uint32 buf[3]; - buf[0] = ((tIPv4*)Conn->Interface->Address)->L; - buf[1] = Conn->RemoteIP.v4.L; - buf[2] = (htons(packlen)<<16) | (6<<8) | 0; + buf[0] = ((tIPv4*)Interface->Address)->L; + buf[1] = ((tIPv4*)Dest)->L; + buf[2] = htonl( (packlen) | (IP4PROT_TCP<<16) | (0<<24) ); checksum[0] = htons( ~IPv4_Checksum(buf, sizeof(buf)) ); // Partial checksum } // - Combine checksums Header->Checksum = htons( IPv4_Checksum(checksum, sizeof(checksum)) ); - IPv4_SendPacket(Conn->Interface, Conn->RemoteIP.v4, IP4PROT_TCP, 0, buffer); + IPv4_SendPacket(Interface, *(tIPv4*)Dest, IP4PROT_TCP, 0, buffer); break; case 6: // Append IPv6 Pseudo Header { Uint32 buf[4+4+1+1]; - memcpy(buf, Conn->Interface->Address, 16); - memcpy(&buf[4], &Conn->RemoteIP, 16); + memcpy(buf, Interface->Address, 16); + memcpy(&buf[4], Dest, 16); buf[8] = htonl(packlen); - buf[9] = htonl(6); + buf[9] = htonl(IP4PROT_TCP); checksum[0] = htons( ~IPv4_Checksum(buf, sizeof(buf)) ); // Partial checksum } Header->Checksum = htons( IPv4_Checksum(checksum, sizeof(checksum)) ); // Combine the two - IPv6_SendPacket(Conn->Interface, Conn->RemoteIP.v6, IP4PROT_TCP, Length, Data); + IPv6_SendPacket(Interface, *(tIPv6*)Dest, IP4PROT_TCP, buffer); break; } } +void TCP_int_SendRSTTo(tInterface *Interface, void *Address, size_t Length, const tTCPHeader *Header) +{ + tTCPHeader out_hdr = {0}; + + out_hdr.DataOffset = (sizeof(out_hdr)/4) << 4; + out_hdr.DestPort = Header->SourcePort; + out_hdr.SourcePort = Header->DestPort; + + size_t data_len = Length - (Header->DataOffset>>4)*4; + out_hdr.AcknowlegementNumber = htonl( ntohl(Header->SequenceNumber) + data_len ); + if( Header->Flags & TCP_FLAG_ACK ) { + out_hdr.Flags = TCP_FLAG_RST; + out_hdr.SequenceNumber = Header->AcknowlegementNumber; + } + else { + out_hdr.Flags = TCP_FLAG_RST|TCP_FLAG_ACK; + out_hdr.SequenceNumber = 0; + } + TCP_int_SendPacket(Interface, Address, &out_hdr, 0, NULL); +} + /** * \brief Handles a packet from the IP Layer * \param Interface Interface the packet arrived from @@ -161,9 +192,8 @@ void TCP_SendPacket( tTCPConnection *Conn, tTCPHeader *Header, size_t Length, co void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) { tTCPHeader *hdr = Buffer; - tTCPListener *srv; - tTCPConnection *conn; + #if TCP_DEBUG Log_Log("TCP", "TCP_GetPacket: :%i from [%s]:%i, Flags = %s%s%s%s%s%s%s%s", ntohs(hdr->DestPort), IPStack_PrintAddress(Interface->Type, Address), @@ -177,6 +207,7 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe (hdr->Flags & TCP_FLAG_SYN) ? "SYN " : "", (hdr->Flags & TCP_FLAG_FIN) ? "FIN " : "" ); + #endif if( Length > (hdr->DataOffset >> 4)*4 ) { @@ -191,7 +222,7 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe } // Check Servers - for( srv = gTCP_Listeners; srv; srv = srv->Next ) + for( tTCPListener *srv = gTCP_Listeners; srv; srv = srv->Next ) { // Check if the server is active if(srv->Port == 0) continue; @@ -202,7 +233,7 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe Log_Log("TCP", "TCP_GetPacket: Matches server %p", srv); // Is this in an established connection? - for( conn = srv->Connections; conn; conn = conn->Next ) + for( tTCPConnection *conn = srv->Connections; conn; conn = conn->Next ) { // Check that it is coming in on the same interface if(conn->Interface != Interface) continue; @@ -227,38 +258,41 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe return; } - Log_Log("TCP", "TCP_GetPacket: Opening Connection"); - // Open a new connection (well, check that it's a SYN) - if(hdr->Flags != TCP_FLAG_SYN) { - Log_Log("TCP", "TCP_GetPacket: Packet is not a SYN"); + + if( hdr->Flags & TCP_FLAG_RST ) { + LOG("RST, ignore"); + return ; + } + else if( hdr->Flags & TCP_FLAG_ACK ) { + LOG("ACK, send RST"); + TCP_int_SendRSTTo(Interface, Address, Length, hdr); return ; } + else if( !(hdr->Flags & TCP_FLAG_SYN) ) { + LOG("Other, ignore"); + return ; + } + Log_Log("TCP", "TCP_GetPacket: Opening Connection"); // TODO: Check for halfopen max - conn = calloc(1, sizeof(tTCPConnection)); - conn->State = TCP_ST_SYN_RCVD; + tTCPConnection *conn = TCP_int_CreateConnection(Interface, TCP_ST_SYN_RCVD); conn->LocalPort = srv->Port; conn->RemotePort = ntohs(hdr->SourcePort); - conn->Interface = Interface; switch(Interface->Type) { case 4: conn->RemoteIP.v4 = *(tIPv4*)Address; break; case 6: conn->RemoteIP.v6 = *(tIPv6*)Address; break; + default: ASSERTC(Interface->Type,==,4); return; } - conn->RecievedBuffer = RingBuffer_Create( TCP_RECIEVE_BUFFER_SIZE ); - conn->NextSequenceRcv = ntohl( hdr->SequenceNumber ) + 1; + conn->HighestSequenceRcvd = conn->NextSequenceRcv; conn->NextSequenceSend = rand(); + conn->LastACKSequence = ntohl( hdr->SequenceNumber ); - // Create node - conn->Node.NumACLs = 1; - conn->Node.ACLs = &gVFS_ACL_EveryoneRW; - conn->Node.ImplPtr = conn; conn->Node.ImplInt = srv->NextID ++; - conn->Node.Type = &gTCP_ClientNodeType; // TODO: Special type for the server end? // Hmm... Theoretically, this lock will never have to wait, // as the interface is locked to the watching thread, and this @@ -267,15 +301,22 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe // Oh, wait, there is a case where a wildcard can be used // (srv->Interface == NULL) so having the lock is a good idea SHORTLOCK(&srv->lConnections); - if( !srv->Connections ) - srv->Connections = conn; - else + conn->Server = srv; + conn->Prev = srv->ConnectionsTail; + if(srv->Connections) { + ASSERT(srv->ConnectionsTail); srv->ConnectionsTail->Next = conn; + } + else { + ASSERT(!srv->ConnectionsTail); + srv->Connections = conn; + } srv->ConnectionsTail = conn; if(!srv->NewConnections) srv->NewConnections = conn; VFS_MarkAvaliable( &srv->Node, 1 ); SHORTREL(&srv->lConnections); + Semaphore_Signal(&srv->WaitingConnections, 1); // Send the SYN ACK hdr->Flags |= TCP_FLAG_ACK; @@ -291,7 +332,7 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe // Check Open Connections { - for( conn = gTCP_OutbountCons; conn; conn = conn->Next ) + for( tTCPConnection *conn = gTCP_OutbountCons; conn; conn = conn->Next ) { // Check that it is coming in on the same interface if(conn->Interface != Interface) continue; @@ -311,6 +352,11 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe } Log_Log("TCP", "TCP_GetPacket: No Match"); + // If not a RST, send a RST + if( !(hdr->Flags & TCP_FLAG_RST) ) + { + TCP_int_SendRSTTo(Interface, Address, Length, hdr); + } } /** @@ -335,8 +381,12 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head if(Header->Flags & TCP_FLAG_SYN) { // TODO: What if the packet also has data? if( Connection->LastACKSequence != Connection->NextSequenceRcv ) - TCP_INT_SendACK(Connection); + TCP_INT_SendACK(Connection, "SYN"); Connection->NextSequenceRcv = ntohl(Header->SequenceNumber); + // TODO: Process HighestSequenceRcvd + // HACK! + if( Connection->HighestSequenceRcvd == 0 ) + Connection->HighestSequenceRcvd = Connection->NextSequenceRcv; Connection->LastACKSequence = Connection->NextSequenceRcv; } @@ -349,7 +399,9 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head // Get length of data dataLen = Length - (Header->DataOffset>>4)*4; LOG("dataLen = %i", dataLen); -// Log_Debug("TCP", "State %i, dataLen = %x", Connection->State, dataLen); + #if TCP_DEBUG + Log_Debug("TCP", "State %i, dataLen = %x", Connection->State, dataLen); + #endif // // State Machine @@ -366,25 +418,22 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head case TCP_ST_SYN_SENT: if( Header->Flags & TCP_FLAG_SYN ) { + if( Connection->HighestSequenceRcvd == Connection->NextSequenceRcv ) + Connection->HighestSequenceRcvd ++; Connection->NextSequenceRcv ++; - Header->DestPort = Header->SourcePort; - Header->SourcePort = htons(Connection->LocalPort); - Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv); - Header->SequenceNumber = htonl(Connection->NextSequenceSend); - Header->WindowSize = htons(TCP_WINDOW_SIZE); - Header->Flags = TCP_FLAG_ACK; - Header->DataOffset = (sizeof(tTCPHeader)/4) << 4; - TCP_SendPacket( Connection, Header, 0, NULL ); if( Header->Flags & TCP_FLAG_ACK ) { Log_Log("TCP", "ACKing SYN-ACK"); Connection->State = TCP_ST_OPEN; + VFS_MarkFull(&Connection->Node, 0); + TCP_INT_SendACK(Connection, "SYN-ACK"); } else { Log_Log("TCP", "ACKing SYN"); Connection->State = TCP_ST_SYN_RCVD; + TCP_INT_SendACK(Connection, "SYN"); } } break; @@ -394,8 +443,9 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head if( Header->Flags & TCP_FLAG_ACK ) { // TODO: Handle max half-open limit - Connection->State = TCP_ST_OPEN; Log_Log("TCP", "Connection fully opened"); + Connection->State = TCP_ST_OPEN; + VFS_MarkFull(&Connection->Node, 0); } break; @@ -406,12 +456,11 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head if( Header->Flags & TCP_FLAG_FIN ) { Log_Log("TCP", "Conn %p closed, recieved FIN", Connection); VFS_MarkError(&Connection->Node, 1); + Connection->NextSequenceRcv ++; + TCP_INT_SendACK(Connection, "FIN Received"); Connection->State = TCP_ST_CLOSE_WAIT; -// Header->Flags &= ~TCP_FLAG_FIN; - // CLOSE WAIT requires the client to close (or does it?) - #if 0 - - #endif + // CLOSE WAIT requires the client to close + return ; } // Check for an empty packet @@ -421,14 +470,12 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head Log_Log("TCP", "ACK only packet"); return ; } - Connection->NextSequenceRcv ++; // TODO: Is this right? (empty packet counts as one byte) + // TODO: Is this right? (empty packet counts as one byte) + if( Connection->HighestSequenceRcvd == Connection->NextSequenceRcv ) + Connection->HighestSequenceRcvd ++; + Connection->NextSequenceRcv ++; Log_Log("TCP", "Empty Packet, inc and ACK the current sequence number"); - Header->DestPort = Header->SourcePort; - Header->SourcePort = htons(Connection->LocalPort); - Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv); - Header->SequenceNumber = htonl(Connection->NextSequenceSend); - Header->Flags |= TCP_FLAG_ACK; - TCP_SendPacket( Connection, Header, 0, NULL ); + TCP_INT_SendACK(Connection, "Empty"); return ; } @@ -439,7 +486,7 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head sequence_num = ntohl(Header->SequenceNumber); - LOG("TCP", "0x%08x <= 0x%08x < 0x%08x", + LOG("0x%08x <= 0x%08x < 0x%08x", Connection->NextSequenceRcv, ntohl(Header->SequenceNumber), Connection->NextSequenceRcv + TCP_WINDOW_SIZE @@ -459,6 +506,8 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head break; } LOG("0x%08x += %i", Connection->NextSequenceRcv, dataLen); + if( Connection->HighestSequenceRcvd == Connection->NextSequenceRcv ) + Connection->HighestSequenceRcvd += dataLen; Connection->NextSequenceRcv += dataLen; // TODO: This should be moved out of the watcher thread, @@ -469,81 +518,44 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head #if 1 // - Only send an ACK if we've had a burst - if( Connection->NextSequenceRcv > (Uint32)(TCP_DACK_THRESHOLD + Connection->LastACKSequence) ) + Uint32 bytes_since_last_ack = Connection->NextSequenceRcv - Connection->LastACKSequence; + LOG("bytes_since_last_ack = 0x%x", bytes_since_last_ack); + if( bytes_since_last_ack > TCP_DACK_THRESHOLD ) { - TCP_INT_SendACK(Connection); + TCP_INT_SendACK(Connection, "DACK Burst"); // - Extend TCP deferred ACK timer Time_RemoveTimer(Connection->DeferredACKTimer); } // - Schedule the deferred ACK timer (if already scheduled, this is a NOP) Time_ScheduleTimer(Connection->DeferredACKTimer, TCP_DACK_TIMEOUT); #else - TCP_INT_SendACK(Connection); + TCP_INT_SendACK(Connection, "RX"); #endif } // Check if the packet is in window - else if( WrapBetween(Connection->NextSequenceRcv, sequence_num, - Connection->NextSequenceRcv+TCP_WINDOW_SIZE, 0xFFFFFFFF) ) + else if( sequence_num - Connection->NextSequenceRcv < TCP_WINDOW_SIZE ) { Uint8 *dataptr = (Uint8*)Header + (Header->DataOffset>>4)*4; - #if CACHE_FUTURE_PACKETS_IN_BYTES - Uint32 index; - int i; - - index = sequence_num % TCP_WINDOW_SIZE; - for( i = 0; i < dataLen; i ++ ) + Uint32 index = sequence_num % TCP_WINDOW_SIZE; + Uint32 max = Connection->NextSequenceRcv % TCP_WINDOW_SIZE; + if( !(Connection->FuturePacketValidBytes[index/8] & (1 << (index%8))) ) + TCP_INT_SendACK(Connection, "Lost packet"); + for( int i = 0; i < dataLen; i ++ ) { Connection->FuturePacketValidBytes[index/8] |= 1 << (index%8); Connection->FuturePacketData[index] = dataptr[i]; // Do a wrap increment index ++; if(index == TCP_WINDOW_SIZE) index = 0; + if(index == max) break; } - #else - tTCPStoredPacket *pkt, *tmp, *prev = NULL; - - // Allocate and fill cached packet - pkt = malloc( sizeof(tTCPStoredPacket) + dataLen ); - pkt->Next = NULL; - pkt->Sequence = ntohl(Header->SequenceNumber); - pkt->Length = dataLen; - memcpy(pkt->Data, dataptr, dataLen); - - Log_Log("TCP", "We missed a packet, caching", - pkt->Sequence, Connection->NextSequenceRcv); - - // No? Well, let's cache it and look at it later - SHORTLOCK( &Connection->lFuturePackets ); - for(tmp = Connection->FuturePackets; - tmp; - prev = tmp, tmp = tmp->Next) - { - if(tmp->Sequence >= pkt->Sequence) break; - } - - // Add if before first, or sequences don't match - if( !tmp || tmp->Sequence != pkt->Sequence ) + Uint32 rel_highest = Connection->HighestSequenceRcvd - Connection->NextSequenceRcv; + Uint32 rel_this = index - Connection->NextSequenceRcv; + LOG("Updating highest this(0x%x) > highest(%x)", rel_this, rel_highest); + if( rel_this > rel_highest ) { - if(prev) - prev->Next = pkt; - else - Connection->FuturePackets = pkt; - pkt->Next = tmp; + Connection->HighestSequenceRcvd = index; } - // Replace if larger - else if(pkt->Length > tmp->Length) - { - if(prev) - prev->Next = pkt; - pkt->Next = tmp->Next; - free(tmp); - } - else - { - free(pkt); // TODO: Find some way to remove this - } - SHORTREL( &Connection->lFuturePackets ); - #endif } // Badly out of sequence packet else @@ -551,7 +563,7 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head Log_Log("TCP", "Fully out of sequence packet (0x%08x not between 0x%08x and 0x%08x), dropped", sequence_num, Connection->NextSequenceRcv, Connection->NextSequenceRcv+TCP_WINDOW_SIZE); // Spec says we should send an empty ACK with the current state - TCP_INT_SendACK(Connection); + TCP_INT_SendACK(Connection, "Bad Seq"); } break; @@ -569,7 +581,7 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head { Connection->State = TCP_ST_FINISHED; // Connection completed Log_Log("TCP", "LAST-ACK to CLOSED - Connection remote closed"); - // TODO: Destrory the TCB + TCP_int_FreeTCB(Connection); } break; @@ -581,14 +593,7 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head Log_Debug("TCP", "Conn %p closed, sent FIN and recieved FIN", Connection); VFS_MarkError(&Connection->Node, 1); - // ACK Packet - Header->DestPort = Header->SourcePort; - Header->SourcePort = htons(Connection->LocalPort); - Header->AcknowlegementNumber = Header->SequenceNumber; - Header->SequenceNumber = htonl(Connection->NextSequenceSend); - Header->WindowSize = htons(TCP_WINDOW_SIZE); - Header->Flags = TCP_FLAG_ACK; - TCP_SendPacket( Connection, Header, 0, NULL ); + TCP_INT_SendACK(Connection, "FINWAIT-1 FIN"); break ; } @@ -606,15 +611,8 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head if( Header->Flags & TCP_FLAG_FIN ) { Connection->State = TCP_ST_TIME_WAIT; - Log_Debug("TCP", "FIN sent and recieved, ACKing and going into TIME WAIT %p FINWAIT-2 -> TIME WAIT", Connection); - // Send ACK - Header->DestPort = Header->SourcePort; - Header->SourcePort = htons(Connection->LocalPort); - Header->AcknowlegementNumber = Header->SequenceNumber; - Header->SequenceNumber = htonl(Connection->NextSequenceSend); - Header->WindowSize = htons(TCP_WINDOW_SIZE); - Header->Flags = TCP_FLAG_ACK; - TCP_SendPacket( Connection, Header, 0, NULL ); + Log_Debug("TCP", "Conn %p FINWAIT-2 -> TIME WAIT", Connection); + TCP_INT_SendACK(Connection, "FINWAIT-2 FIN"); } break; @@ -684,114 +682,87 @@ int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t */ void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection) { - #if CACHE_FUTURE_PACKETS_IN_BYTES - int i, length = 0; - Uint32 index; - // Calculate length of contiguous bytes - length = Connection->HighestSequenceRcvd - Connection->NextSequenceRcv; - index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE; - for( i = 0; i < length; i ++ ) + const int length = Connection->HighestSequenceRcvd - Connection->NextSequenceRcv; + Uint32 index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE; + size_t runlength = length; + LOG("length=%i, index=0x%x", length, index); + for( int i = 0; i < length; i ++ ) { - if( Connection->FuturePacketValidBytes[i / 8] == 0xFF ) { - i += 7; index += 7; - continue; - } - else if( !(Connection->FuturePacketValidBytes[i / 8] & (1 << (i%8))) ) + int bit = index % 8; + Uint8 bitfield_byte = Connection->FuturePacketValidBytes[index / 8]; + if( (bitfield_byte & (1 << bit)) == 0 ) { + runlength = i; + LOG("Hit missing, break"); break; - - index ++; + } + + if( bitfield_byte == 0xFF ) { + int inc = 8 - bit; + i += inc - 1; + index += inc; + } + else { + index ++; + } if(index > TCP_WINDOW_SIZE) index -= TCP_WINDOW_SIZE; } - length = i; index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE; + Connection->NextSequenceRcv += runlength; // Write data to to the ring buffer - if( TCP_WINDOW_SIZE - index > length ) + if( TCP_WINDOW_SIZE - index > runlength ) { // Simple case - RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, length ); + RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, runlength ); } else { int endLen = TCP_WINDOW_SIZE - index; // 2-part case RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, endLen ); - RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData, endLen - length ); + RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData, endLen - runlength ); } // Mark (now saved) bytes as invalid // - Align index - while(index % 8 && length) + while(index % 8 && runlength > 0) { Connection->FuturePacketData[index] = 0; - Connection->FuturePacketData[index/8] &= ~(1 << (index%8)); + Connection->FuturePacketValidBytes[index/8] &= ~(1 << (index%8)); index ++; if(index > TCP_WINDOW_SIZE) index -= TCP_WINDOW_SIZE; - length --; + runlength --; } - while( length > 7 ) + while( runlength > 7 ) { Connection->FuturePacketData[index] = 0; Connection->FuturePacketValidBytes[index/8] = 0; - length -= 8; + runlength -= 8; index += 8; if(index > TCP_WINDOW_SIZE) index -= TCP_WINDOW_SIZE; } - while(length) + while( runlength > 0) { Connection->FuturePacketData[index] = 0; Connection->FuturePacketData[index/8] &= ~(1 << (index%8)); index ++; if(index > TCP_WINDOW_SIZE) index -= TCP_WINDOW_SIZE; - length --; - } - - #else - tTCPStoredPacket *pkt; - for(;;) - { - SHORTLOCK( &Connection->lFuturePackets ); - - // Clear out duplicates from cache - // - If a packet has just been recieved, and it is expected, then - // (since NextSequenceRcv = rcvd->Sequence + rcvd->Length) all - // packets in cache that are smaller than the next expected - // are now defunct. - pkt = Connection->FuturePackets; - while(pkt && pkt->Sequence < Connection->NextSequenceRcv) - { - tTCPStoredPacket *next = pkt->Next; - free(pkt); - pkt = next; - } - - // If there's no packets left in cache, stop looking - if(!pkt || pkt->Sequence > Connection->NextSequenceRcv) { - SHORTREL( &Connection->lFuturePackets ); - return; - } - - // Delete packet from future list - Connection->FuturePackets = pkt->Next; - - // Release list - SHORTREL( &Connection->lFuturePackets ); - - // Looks like we found one - TCP_INT_AppendRecieved(Connection, pkt); - Connection->NextSequenceRcv += pkt->Length; - free(pkt); + runlength --; } - #endif } -void TCP_INT_SendACK(tTCPConnection *Connection) +void TCP_int_SendDelayedACK(void *ConnPtr) +{ + TCP_INT_SendACK(ConnPtr, "DACK Timeout"); +} + +void TCP_INT_SendACK(tTCPConnection *Connection, const char *Reason) { tTCPHeader hdr; // ACK Packet @@ -804,7 +775,7 @@ void TCP_INT_SendACK(tTCPConnection *Connection) hdr.Flags = TCP_FLAG_ACK; // TODO: Determine if SYN is wanted too hdr.Checksum = 0; // TODO: Checksum hdr.UrgentPointer = 0; - Log_Debug("TCP", "Sending ACK for 0x%08x", Connection->NextSequenceRcv); + Log_Debug("TCP", "Sending ACK for 0x%08x (%s)", Connection->NextSequenceRcv, Reason); TCP_SendPacket( Connection, &hdr, 0, NULL ); //Connection->NextSequenceSend ++; Connection->LastACKSequence = Connection->NextSequenceRcv; @@ -867,6 +838,79 @@ int TCP_DeallocatePort(Uint16 Port) return 1; } +tTCPConnection *TCP_int_CreateConnection(tInterface *Interface, enum eTCPConnectionState State) +{ + tTCPConnection *conn = calloc( sizeof(tTCPConnection) + TCP_WINDOW_SIZE + TCP_WINDOW_SIZE/8, 1 ); + + conn->State = State; + conn->Interface = Interface; + conn->LocalPort = -1; + conn->RemotePort = -1; + + conn->Node.ReferenceCount = 1; + conn->Node.ImplPtr = conn; + conn->Node.NumACLs = 1; + conn->Node.ACLs = &gVFS_ACL_EveryoneRW; + conn->Node.Type = &gTCP_ClientNodeType; + conn->Node.BufferFull = 1; // Cleared when connection opens + + conn->RecievedBuffer = RingBuffer_Create( TCP_RECIEVE_BUFFER_SIZE ); + #if 0 + conn->SentBuffer = RingBuffer_Create( TCP_SEND_BUFFER_SIZE ); + Semaphore_Init(conn->SentBufferSpace, 0, TCP_SEND_BUFFER_SIZE, "TCP SentBuffer", conn->Name); + #endif + + #if CACHE_FUTURE_PACKETS_IN_BYTES + // Future recieved data (ahead of the expected sequence number) + conn->FuturePacketData = (Uint8*)conn + sizeof(tTCPConnection); + conn->FuturePacketValidBytes = conn->FuturePacketData + TCP_WINDOW_SIZE; + #endif + + conn->DeferredACKTimer = Time_AllocateTimer( TCP_int_SendDelayedACK, conn); + return conn; +} + +void TCP_int_FreeTCB(tTCPConnection *Connection) +{ + ASSERTC(Connection->State, ==, TCP_ST_FINISHED); + ASSERTC(Connection->Node.ReferenceCount, ==, 0); + + if( Connection->Server ) + { + tTCPListener *srv = Connection->Server; + SHORTLOCK(&srv->lConnections); + if(Connection->Prev) + Connection->Prev->Next = Connection->Next; + else + srv->Connections = Connection->Next; + if(Connection->Next) + Connection->Next->Prev = Connection->Prev; + else { + ASSERT(srv->ConnectionsTail == Connection); + srv->ConnectionsTail = Connection->Prev; + } + SHORTREL(&srv->lConnections); + } + else + { + SHORTLOCK(&glTCP_OutbountCons); + if(Connection->Prev) + Connection->Prev->Next = Connection->Next; + else + gTCP_OutbountCons = Connection->Next; + if(Connection->Next) + Connection->Next->Prev = Connection->Prev; + else + ; + SHORTREL(&glTCP_OutbountCons); + } + + RingBuffer_Free(Connection->RecievedBuffer); + Time_FreeTimer(Connection->DeferredACKTimer); + // TODO: Force VFS to close handles? (they should all be closed); + free(Connection); +} + // --- Server tVFS_Node *TCP_Server_Init(tInterface *Interface) { @@ -915,15 +959,9 @@ int TCP_Server_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX]) ENTER("pNode iPos", Node, Pos); Log_Log("TCP", "Thread %i waiting for a connection", Threads_GetTID()); - for(;;) - { - SHORTLOCK( &srv->lConnections ); - if( srv->NewConnections != NULL ) break; - SHORTREL( &srv->lConnections ); - Threads_Yield(); // TODO: Sleep until poked - } + Semaphore_Wait( &srv->WaitingConnections, 1 ); - + SHORTLOCK(&srv->lConnections); // Increment the new list (the current connection is still on the // normal list) conn = srv->NewConnections; @@ -950,7 +988,7 @@ int TCP_Server_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX]) * \param Node Server node * \param Name Hexadecimal ID of the node */ -tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, const char *Name) +tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, const char *Name, Uint Flags) { tTCPConnection *conn; tTCPListener *srv = Node->ImplPtr; @@ -1065,34 +1103,13 @@ void TCP_Server_Close(tVFS_Node *Node) */ tVFS_Node *TCP_Client_Init(tInterface *Interface) { - tTCPConnection *conn = calloc( sizeof(tTCPConnection) + TCP_WINDOW_SIZE + TCP_WINDOW_SIZE/8, 1 ); - - conn->State = TCP_ST_CLOSED; - conn->Interface = Interface; - conn->LocalPort = -1; - conn->RemotePort = -1; - - conn->Node.ImplPtr = conn; - conn->Node.NumACLs = 1; - conn->Node.ACLs = &gVFS_ACL_EveryoneRW; - conn->Node.Type = &gTCP_ClientNodeType; - - conn->RecievedBuffer = RingBuffer_Create( TCP_RECIEVE_BUFFER_SIZE ); - #if 0 - conn->SentBuffer = RingBuffer_Create( TCP_SEND_BUFFER_SIZE ); - Semaphore_Init(conn->SentBufferSpace, 0, TCP_SEND_BUFFER_SIZE, "TCP SentBuffer", conn->Name); - #endif - - #if CACHE_FUTURE_PACKETS_IN_BYTES - // Future recieved data (ahead of the expected sequence number) - conn->FuturePacketData = (Uint8*)conn + sizeof(tTCPConnection); - conn->FuturePacketValidBytes = conn->FuturePacketData + TCP_WINDOW_SIZE; - #endif - - conn->DeferredACKTimer = Time_AllocateTimer( (void(*)(void*)) TCP_INT_SendACK, conn); + tTCPConnection *conn = TCP_int_CreateConnection(Interface, TCP_ST_CLOSED); SHORTLOCK(&glTCP_OutbountCons); + conn->Server = NULL; + conn->Prev = NULL; conn->Next = gTCP_OutbountCons; + gTCP_OutbountCons->Prev = conn; gTCP_OutbountCons = conn; SHORTREL(&glTCP_OutbountCons); @@ -1104,7 +1121,7 @@ tVFS_Node *TCP_Client_Init(tInterface *Interface) * \note If \a Length is smaller than the size of the packet, the rest * of the packet's data will be discarded. */ -size_t TCP_Client_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer) +size_t TCP_Client_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags) { tTCPConnection *conn = Node->ImplPtr; size_t len; @@ -1112,14 +1129,9 @@ size_t TCP_Client_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffe ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); LOG("conn = %p {State:%i}", conn, conn->State); - // Check if connection is estabilishing - // - TODO: Sleep instead (maybe using VFS_SelectNode to wait for the - // data to be availiable - while( conn->State == TCP_ST_SYN_RCVD || conn->State == TCP_ST_SYN_SENT ) - Threads_Yield(); - - // If the conneciton is not open, then clean out the recieved buffer - if( conn->State != TCP_ST_OPEN ) + // If the connection has been closed (state > ST_OPEN) then clear + // any stale data in the buffer (until it is empty (until it is empty)) + if( conn->State > TCP_ST_OPEN ) { Mutex_Acquire( &conn->lRecievedPackets ); len = RingBuffer_Read( Buffer, conn->RecievedBuffer, Length ); @@ -1127,6 +1139,7 @@ size_t TCP_Client_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffe if( len == 0 ) { VFS_MarkAvaliable(Node, 0); + errno = 0; LEAVE('i', -1); return -1; } @@ -1136,7 +1149,17 @@ size_t TCP_Client_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffe } // Wait - VFS_SelectNode(Node, VFS_SELECT_READ|VFS_SELECT_ERROR, NULL, "TCP_Client_Read"); + { + tTime *timeout = NULL; + tTime timeout_zero = 0; + if( Flags & VFS_IOFLAG_NOBLOCK ) + timeout = &timeout_zero; + if( !VFS_SelectNode(Node, VFS_SELECT_READ|VFS_SELECT_ERROR, timeout, "TCP_Client_Read") ) { + errno = EWOULDBLOCK; + LEAVE('i', -1); + return -1; + } + } // Lock list and read as much as possible (up to `Length`) Mutex_Acquire( &conn->lRecievedPackets ); @@ -1161,6 +1184,11 @@ void TCP_INT_SendDataPacket(tTCPConnection *Connection, size_t Length, const voi { char buf[sizeof(tTCPHeader)+Length]; tTCPHeader *packet = (void*)buf; + + // - Stop Delayed ACK timer (as this data packet ACKs) + Time_RemoveTimer(Connection->DeferredACKTimer); + + // TODO: Don't exceed window size packet->SourcePort = htons(Connection->LocalPort); packet->DestPort = htons(Connection->RemotePort); @@ -1170,6 +1198,7 @@ void TCP_INT_SendDataPacket(tTCPConnection *Connection, size_t Length, const voi packet->AcknowlegementNumber = htonl(Connection->NextSequenceRcv); packet->SequenceNumber = htonl(Connection->NextSequenceSend); packet->Flags = TCP_FLAG_PSH|TCP_FLAG_ACK; // Hey, ACK if you can! + packet->UrgentPointer = 0; memcpy(packet->Options, Data, Length); @@ -1186,7 +1215,7 @@ void TCP_INT_SendDataPacket(tTCPConnection *Connection, size_t Length, const voi /** * \brief Send some bytes on a connection */ -size_t TCP_Client_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer) +size_t TCP_Client_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags) { tTCPConnection *conn = Node->ImplPtr; size_t rem = Length; @@ -1198,16 +1227,27 @@ size_t TCP_Client_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void // Buffer, Length); // #endif - // Check if connection is open - while( conn->State == TCP_ST_SYN_RCVD || conn->State == TCP_ST_SYN_SENT ) - Threads_Yield(); - - if( conn->State != TCP_ST_OPEN ) { + // Don't allow a write to a closed connection + if( conn->State > TCP_ST_OPEN ) { VFS_MarkError(Node, 1); + errno = 0; LEAVE('i', -1); return -1; } + // Wait + { + tTime *timeout = NULL; + tTime timeout_zero = 0; + if( Flags & VFS_IOFLAG_NOBLOCK ) + timeout = &timeout_zero; + if( !VFS_SelectNode(Node, VFS_SELECT_WRITE|VFS_SELECT_ERROR, timeout, "TCP_Client_Write") ) { + errno = EWOULDBLOCK; + LEAVE('i', -1); + return -1; + } + } + do { int len = (rem < TCP_MAX_PACKET_SIZE) ? rem : TCP_MAX_PACKET_SIZE; @@ -1312,13 +1352,10 @@ int TCP_Client_IOCtl(tVFS_Node *Node, int ID, void *Data) LEAVE_RET('i', 0); { - tTime timeout_end = now() + conn->Interface->TimeoutDelay; + tTime timeout = conn->Interface->TimeoutDelay; TCP_StartConnection(conn); - // TODO: Wait for connection to open - while( conn->State == TCP_ST_SYN_SENT && timeout_end > now() ) { - Threads_Yield(); - } + VFS_SelectNode(&conn->Node, VFS_SELECT_WRITE, &timeout, "TCP Connection"); if( conn->State == TCP_ST_SYN_SENT ) LEAVE_RET('i', 0); } @@ -1340,6 +1377,16 @@ void TCP_Client_Close(tVFS_Node *Node) ENTER("pNode", Node); + ASSERT(Node->ReferenceCount != 0); + + if( Node->ReferenceCount > 1 ) { + Node->ReferenceCount --; + LOG("Dereference only"); + LEAVE('-'); + return ; + } + Node->ReferenceCount --; + if( conn->State == TCP_ST_CLOSE_WAIT || conn->State == TCP_ST_OPEN ) { packet.SourcePort = htons(conn->LocalPort); @@ -1354,24 +1401,29 @@ void TCP_Client_Close(tVFS_Node *Node) TCP_SendPacket( conn, &packet, 0, NULL ); } + Time_RemoveTimer(conn->DeferredACKTimer); + switch( conn->State ) { + case TCP_ST_CLOSED: + Log_Warning("TCP", "Closing connection that was never opened"); + TCP_int_FreeTCB(conn); + break; case TCP_ST_CLOSE_WAIT: conn->State = TCP_ST_LAST_ACK; break; case TCP_ST_OPEN: conn->State = TCP_ST_FIN_WAIT1; - while( conn->State == TCP_ST_FIN_WAIT1 ) Threads_Yield(); + while( conn->State == TCP_ST_FIN_WAIT1 ) + Threads_Yield(); + // No free, freed after TIME_WAIT break; default: - Log_Warning("TCP", "Unhandled connection state in TCP_Client_Close"); + Log_Warning("TCP", "Unhandled connection state %i in TCP_Client_Close", + conn->State); break; } - Time_RemoveTimer(conn->DeferredACKTimer); - Time_FreeTimer(conn->DeferredACKTimer); - free(conn); - LEAVE('-'); } @@ -1412,3 +1464,10 @@ int WrapBetween(Uint32 Lower, Uint32 Value, Uint32 Higher, Uint32 MaxValue) return 0; } +Uint32 GetRelative(Uint32 Base, Uint32 Value) +{ + if( Value < Base ) + return Value - Base + 0xFFFFFFFF; + else + return Value - Base; +}