#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
#define TCP_RECIEVE_BUFFER_SIZE 0x4000
// === PROTOTYPES ===
-void TCP_Initialise();
+void TCP_Initialise(void);
void TCP_StartConnection(tTCPConnection *Conn);
void TCP_SendPacket(tTCPConnection *Conn, size_t Length, tTCPHeader *Data);
void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer);
void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length);
-void TCP_INT_AppendRecieved(tTCPConnection *Connection, tTCPStoredPacket *Ptk);
+int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t Length);
void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection);
Uint16 TCP_GetUnusedPort();
int TCP_AllocatePort(Uint16 Port);
*
* Registers the client and server files and the GetPacket callback
*/
-void TCP_Initialise()
+void TCP_Initialise(void)
{
+ giTCP_NextOutPort += rand()%32;
IPStack_AddFile(&gTCP_ServerFile);
IPStack_AddFile(&gTCP_ClientFile);
IPv4_RegisterCallback(IP4PROT_TCP, TCP_GetPacket);
+ IPv6_RegisterCallback(IP4PROT_TCP, TCP_GetPacket);
}
/**
*/
void TCP_SendPacket( tTCPConnection *Conn, size_t Length, tTCPHeader *Data )
{
- size_t buflen;
- Uint32 *buf;
+ Uint16 checksum[2];
+
+ Data->Checksum = 0;
+ checksum[1] = htons( ~IPv4_Checksum( (void*)Data, Length ) ); // Partial checksum
+ if(Length & 1)
+ ((Uint8*)Data)[Length] = 0;
+
+ // TODO: Fragment packet
+
switch( Conn->Interface->Type )
{
- case 4: // Append IPv4 Pseudo Header
- buflen = 4 + 4 + 4 + ((Length+1)&~1);
- buf = malloc( buflen );
- buf[0] = ((tIPv4*)Conn->Interface->Address)->L;
- buf[1] = Conn->RemoteIP.v4.L;
- buf[2] = (htons(Length)<<16) | (6<<8) | 0;
- Data->Checksum = 0;
- memcpy( &buf[3], Data, Length );
- Data->Checksum = htons( IPv4_Checksum( buf, buflen ) );
- free(buf);
+ case 4:
+ // Append IPv4 Pseudo Header
+ {
+ Uint32 buf[3];
+ buf[0] = ((tIPv4*)Conn->Interface->Address)->L;
+ buf[1] = Conn->RemoteIP.v4.L;
+ buf[2] = (htons(Length)<<16) | (6<<8) | 0;
+ checksum[0] = htons( ~IPv4_Checksum(buf, sizeof(buf)) ); // Partial checksum
+ }
+ Data->Checksum = htons( IPv4_Checksum(checksum, 2*2) ); // Combine the two
IPv4_SendPacket(Conn->Interface, Conn->RemoteIP.v4, IP4PROT_TCP, 0, Length, Data);
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);
+ buf[8] = htonl(Length);
+ buf[9] = htonl(6);
+ checksum[0] = htons( ~IPv4_Checksum(buf, sizeof(buf)) ); // Partial checksum
+ }
+ Data->Checksum = htons( IPv4_Checksum(checksum, 2*2) ); // Combine the two
+ IPv6_SendPacket(Conn->Interface, Conn->RemoteIP.v6, IP4PROT_TCP, Length, Data);
+ break;
}
}
tTCPListener *srv;
tTCPConnection *conn;
- Log_Log("TCP", "TCP_GetPacket: SourcePort = %i, DestPort = %i",
- ntohs(hdr->SourcePort), ntohs(hdr->DestPort));
-/*
- Log_Log("TCP", "TCP_GetPacket: SequenceNumber = 0x%x", ntohl(hdr->SequenceNumber));
- Log_Log("TCP", "TCP_GetPacket: AcknowlegementNumber = 0x%x", ntohl(hdr->AcknowlegementNumber));
- Log_Log("TCP", "TCP_GetPacket: DataOffset = %i", hdr->DataOffset >> 4);
- Log_Log("TCP", "TCP_GetPacket: WindowSize = %i", htons(hdr->WindowSize));
- Log_Log("TCP", "TCP_GetPacket: Checksum = 0x%x", htons(hdr->Checksum));
- Log_Log("TCP", "TCP_GetPacket: UrgentPointer = 0x%x", htons(hdr->UrgentPointer));
-*/
- Log_Log("TCP", "TCP_GetPacket: Flags = %s%s%s%s%s%s%s%s",
+ Log_Log("TCP", "TCP_GetPacket: <Local>:%i from [%s]:%i, Flags= %s%s%s%s%s%s%s%s",
+ ntohs(hdr->SourcePort),
+ IPStack_PrintAddress(Interface->Type, Address),
+ ntohs(hdr->DestPort),
(hdr->Flags & TCP_FLAG_CWR) ? "CWR " : "",
(hdr->Flags & TCP_FLAG_ECE) ? "ECE " : "",
(hdr->Flags & TCP_FLAG_URG) ? "URG " : "",
if( Length > (hdr->DataOffset >> 4)*4 )
{
Log_Log("TCP", "TCP_GetPacket: SequenceNumber = 0x%x", ntohl(hdr->SequenceNumber));
+#if HEXDUMP_INCOMING
Debug_HexDump(
- "[TCP ] Packet Data = ",
+ "TCP_GetPacket: Packet Data = ",
(Uint8*)hdr + (hdr->DataOffset >> 4)*4,
Length - (hdr->DataOffset >> 4)*4
);
+#endif
}
// Check Servers
// Is this in an established connection?
for( conn = srv->Connections; conn; conn = conn->Next )
{
- Log_Log("TCP", "TCP_GetPacket: conn->Interface(%p) == Interface(%p)",
- conn->Interface, Interface);
// Check that it is coming in on the same interface
if(conn->Interface != Interface) continue;
if(conn->RemotePort != ntohs(hdr->SourcePort)) continue;
// Check Source IP
- if(conn->Interface->Type == 6 && !IP6_EQU(conn->RemoteIP.v6, *(tIPv6*)Address))
- continue;
- if(conn->Interface->Type == 4 && !IP4_EQU(conn->RemoteIP.v4, *(tIPv4*)Address))
- continue;
+ if( IPStack_CompareAddress(conn->Interface->Type, &conn->RemoteIP, Address, -1) != 0 )
+ continue ;
Log_Log("TCP", "TCP_GetPacket: Matches connection %p", conn);
// We have a response!
* \param Length Length of the packet
*/
void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length)
-{
- tTCPStoredPacket *pkt;
+{
int dataLen;
+ Uint32 sequence_num;
// Silently drop once finished
// TODO: Check if this needs to be here
//
switch( Connection->State )
{
- // Pre-init conneciton?
+ // Pre-init connection?
case TCP_ST_CLOSED:
Log_Log("TCP", "Packets to a closed connection?!");
break;
// - Handle State changes
//
if( Header->Flags & TCP_FLAG_FIN ) {
- Log_Log("TCP", "Conn %p closed, recieved FIN, acknowledging", Connection);
+ Log_Log("TCP", "Conn %p closed, recieved FIN", Connection);
VFS_MarkError(&Connection->Node, 1);
Connection->State = TCP_ST_CLOSE_WAIT;
+// Header->Flags &= ~TCP_FLAG_FIN;
// CLOSE WAIT requires the client to close (or does it?)
#if 0
// PSH - Has Data?
// /NOTES
- // Allocate and fill cached packet
- pkt = malloc( sizeof(tTCPStoredPacket) + dataLen );
- pkt->Next = NULL;
- pkt->Sequence = ntohl(Header->SequenceNumber);
- pkt->Length = dataLen;
- memcpy(pkt->Data, (Uint8*)Header + (Header->DataOffset>>4)*4, dataLen);
+ sequence_num = ntohl(Header->SequenceNumber);
Log_Log("TCP", "0x%08x <= 0x%08x < 0x%08x",
Connection->NextSequenceRcv,
- pkt->Sequence,
+ ntohl(Header->SequenceNumber),
Connection->NextSequenceRcv + TCP_WINDOW_SIZE
);
// Is this packet the next expected packet?
- if( pkt->Sequence == Connection->NextSequenceRcv )
+ if( sequence_num == Connection->NextSequenceRcv )
{
+ int rv;
// Ooh, Goodie! Add it to the recieved list
- TCP_INT_AppendRecieved(Connection, pkt);
- free(pkt);
+ rv = TCP_INT_AppendRecieved(Connection,
+ (Uint8*)Header + (Header->DataOffset>>4)*4,
+ dataLen
+ );
+ if(rv != 0) {
+ break;
+ }
Log_Log("TCP", "0x%08x += %i", Connection->NextSequenceRcv, dataLen);
Connection->NextSequenceRcv += dataLen;
// TODO: This should be moved out of the watcher thread,
// so that a single lost packet on one connection doesn't cause
// all connections on the interface to lag.
+ // - Meh, no real issue, as the cache shouldn't be that large
TCP_INT_UpdateRecievedFromFuture(Connection);
// ACK Packet
//Connection->NextSequenceSend ++;
}
// Check if the packet is in window
- else if( WrapBetween(Connection->NextSequenceRcv, pkt->Sequence, Connection->NextSequenceRcv+TCP_WINDOW_SIZE, 0xFFFFFFFF) )
+ else if( WrapBetween(Connection->NextSequenceRcv, sequence_num,
+ Connection->NextSequenceRcv+TCP_WINDOW_SIZE, 0xFFFFFFFF) )
{
- tTCPStoredPacket *tmp, *prev = NULL;
+ 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 ++ )
+ {
+ Connection->FuturePacketValidBytes[index/8] |= 1 << (index%8);
+ Connection->FuturePacketData[index] = dataptr[i];
+ // Do a wrap increment
+ index ++;
+ if(index == TCP_WINDOW_SIZE) index = 0;
+ }
+ #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);
}
else
{
- free(pkt);
+ free(pkt); // TODO: Find some way to remove this
}
SHORTREL( &Connection->lFuturePackets );
+ #endif
}
// Badly out of sequence packet
else
{
Log_Log("TCP", "Fully out of sequence packet (0x%08x not between 0x%08x and 0x%08x), dropped",
- pkt->Sequence, Connection->NextSequenceRcv, Connection->NextSequenceRcv+TCP_WINDOW_SIZE);
- free(pkt);
+ sequence_num, Connection->NextSequenceRcv, Connection->NextSequenceRcv+TCP_WINDOW_SIZE);
// TODO: Spec says we should send an empty ACK with the current state
}
break;
// ACK Packet
Header->DestPort = Header->SourcePort;
Header->SourcePort = htons(Connection->LocalPort);
- Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv);
+ Header->AcknowlegementNumber = Header->SequenceNumber;
Header->SequenceNumber = htonl(Connection->NextSequenceSend);
Header->WindowSize = htons(TCP_WINDOW_SIZE);
Header->Flags = TCP_FLAG_ACK;
// Send ACK
Header->DestPort = Header->SourcePort;
Header->SourcePort = htons(Connection->LocalPort);
- Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv);
+ Header->AcknowlegementNumber = Header->SequenceNumber;
Header->SequenceNumber = htonl(Connection->NextSequenceSend);
Header->WindowSize = htons(TCP_WINDOW_SIZE);
Header->Flags = TCP_FLAG_ACK;
/**
* \brief Appends a packet to the recieved list
* \param Connection Connection structure
- * \param Pkt Packet structure on heap
+ * \param Data Packet contents
+ * \param Length Length of \a Data
*/
-void TCP_INT_AppendRecieved(tTCPConnection *Connection, tTCPStoredPacket *Pkt)
+int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t Length)
{
Mutex_Acquire( &Connection->lRecievedPackets );
- if(Connection->RecievedBuffer->Length + Pkt->Length > Connection->RecievedBuffer->Space )
+
+ if(Connection->RecievedBuffer->Length + Length > Connection->RecievedBuffer->Space )
{
- Log_Error("TCP", "Buffer filled, packet dropped (%s)",
- // TCP_INT_DumpConnection(Connection)
- ""
+ VFS_MarkAvaliable(&Connection->Node, 1);
+ Log_Error("TCP", "Buffer filled, packet dropped (:%i) - %i + %i > %i",
+ Connection->LocalPort, Connection->RecievedBuffer->Length, Length,
+ Connection->RecievedBuffer->Space
);
- return ;
+ Mutex_Release( &Connection->lRecievedPackets );
+ return 1;
}
- RingBuffer_Write( Connection->RecievedBuffer, Pkt->Data, Pkt->Length );
+ RingBuffer_Write( Connection->RecievedBuffer, Data, Length );
- #if USE_SELECT
VFS_MarkAvaliable(&Connection->Node, 1);
- #endif
Mutex_Release( &Connection->lRecievedPackets );
+ return 0;
}
/**
*/
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 ++ )
+ {
+ if( Connection->FuturePacketValidBytes[i / 8] == 0xFF ) {
+ i += 7; index += 7;
+ continue;
+ }
+ else if( !(Connection->FuturePacketValidBytes[i / 8] & (1 << (i%8))) )
+ break;
+
+ index ++;
+ if(index > TCP_WINDOW_SIZE)
+ index -= TCP_WINDOW_SIZE;
+ }
+ length = i;
+
+ index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE;
+
+ // Write data to to the ring buffer
+ if( TCP_WINDOW_SIZE - index > length )
+ {
+ // Simple case
+ RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, length );
+ }
+ 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 );
+ }
+
+ // Mark (now saved) bytes as invalid
+ // - Align index
+ while(index % 8 && length)
+ {
+ Connection->FuturePacketData[index] = 0;
+ Connection->FuturePacketData[index/8] &= ~(1 << (index%8));
+ index ++;
+ if(index > TCP_WINDOW_SIZE)
+ index -= TCP_WINDOW_SIZE;
+ length --;
+ }
+ while( length > 7 )
+ {
+ Connection->FuturePacketData[index] = 0;
+ Connection->FuturePacketValidBytes[index/8] = 0;
+ length -= 8;
+ index += 8;
+ if(index > TCP_WINDOW_SIZE)
+ index -= TCP_WINDOW_SIZE;
+ }
+ while(length)
+ {
+ 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(;;)
{
Connection->NextSequenceRcv += pkt->Length;
free(pkt);
}
+ #endif
}
/**
*/
tVFS_Node *TCP_Client_Init(tInterface *Interface)
{
- tTCPConnection *conn = malloc( sizeof(tTCPConnection) );
+ 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;
- memset( &conn->RemoteIP, 0, sizeof(conn->RemoteIP) );
conn->Node.ImplPtr = conn;
conn->Node.NumACLs = 1;
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
SHORTLOCK(&glTCP_OutbountCons);
conn->Next = gTCP_OutbountCons;
ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
LOG("conn = %p {State:%i}", conn, conn->State);
- // Check if connection is open
+ // 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 )
{
Mutex_Acquire( &conn->lRecievedPackets );
}
// Wait
- VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "TCP_Client_Read");
+ VFS_SelectNode(Node, VFS_SELECT_READ|VFS_SELECT_ERROR, NULL, "TCP_Client_Read");
- // Lock list and read
+ // Lock list and read as much as possible (up to `Length`)
Mutex_Acquire( &conn->lRecievedPackets );
-
- // Attempt to read all `Length` bytes
len = RingBuffer_Read( Buffer, conn->RecievedBuffer, Length );
if( len == 0 || conn->RecievedBuffer->Length == 0 ) {
packet->SourcePort = htons(Connection->LocalPort);
packet->DestPort = htons(Connection->RemotePort);
packet->DataOffset = (sizeof(tTCPHeader)/4)*16;
- packet->WindowSize = TCP_WINDOW_SIZE;
+ packet->WindowSize = htons(TCP_WINDOW_SIZE);
packet->AcknowlegementNumber = htonl(Connection->NextSequenceRcv);
packet->SequenceNumber = htonl(Connection->NextSequenceSend);
memcpy(packet->Options, Data, Length);
Log_Debug("TCP", "Send sequence 0x%08x", Connection->NextSequenceSend);
- Debug_HexDump("[TCP ] TCP_INT_SendDataPacket: Data = ",
- Data, Length);
+#if HEXDUMP_OUTGOING
+ Debug_HexDump("TCP_INT_SendDataPacket: Data = ", Data, Length);
+#endif
TCP_SendPacket( Connection, sizeof(tTCPHeader)+Length, packet );
ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+// #if DEBUG
+// Debug_HexDump("TCP_Client_Write: Buffer = ",
+// Buffer, Length);
+// #endif
+
// Check if connection is open
while( conn->State == TCP_ST_SYN_RCVD || conn->State == TCP_ST_SYN_SENT )
Threads_Yield();
TCP_INT_SendDataPacket(conn, len, Buffer);
Buffer += len;
- rem += len;
+ rem -= len;
} while( rem > 0 );
LEAVE('i', Length);
Conn->NextSequenceSend ++;
Conn->State = TCP_ST_SYN_SENT;
+
return ;
}
if(conn->RemotePort == -1)
LEAVE_RET('i', 0);
- TCP_StartConnection(conn);
+ {
+ tTime timeout_end = now() + conn->Interface->TimeoutDelay;
+
+ TCP_StartConnection(conn);
+ // TODO: Wait for connection to open
+ while( conn->State == TCP_ST_SYN_SENT && timeout_end > now() ) {
+ Threads_Yield();
+ }
+ if( conn->State == TCP_ST_SYN_SENT )
+ LEAVE_RET('i', 0);
+ }
+
LEAVE_RET('i', 1);
// Get recieve buffer length
packet.AcknowlegementNumber = 0;
packet.SequenceNumber = htonl(conn->NextSequenceSend);
- packet.Flags = TCP_FLAG_FIN|TCP_FLAG_ACK;
+ packet.Flags = TCP_FLAG_FIN;
TCP_SendPacket( conn, sizeof(tTCPHeader), &packet );
}
switch( conn->State )
{
case TCP_ST_CLOSE_WAIT:
- conn->State = TCP_ST_CLOSED;
+ conn->State = TCP_ST_LAST_ACK;
break;
case TCP_ST_OPEN:
conn->State = TCP_ST_FIN_WAIT1;