#define TCP_MAX_PACKET_SIZE 1024
#define TCP_WINDOW_SIZE 0x2000
-#define TCP_RECIEVE_BUFFER_SIZE 0x4000
+#define TCP_RECIEVE_BUFFER_SIZE 0x8000
+#define TCP_DACK_THRESHOLD 4096
+#define TCP_DACK_TIMEOUT 500
// === PROTOTYPES ===
void TCP_Initialise(void);
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);
Uint16 TCP_GetUnusedPort();
int TCP_AllocatePort(Uint16 Port);
int TCP_DeallocatePort(Uint16 Port);
// --- Server
tVFS_Node *TCP_Server_Init(tInterface *Interface);
-char *TCP_Server_ReadDir(tVFS_Node *Node, int Pos);
+ int TCP_Server_ReadDir(tVFS_Node *Node, int Pos, char Name[FILENAME_MAX]);
tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, const char *Name);
int TCP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data);
void TCP_Server_Close(tVFS_Node *Node);
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;
// Syncronise sequence values
if(Header->Flags & TCP_FLAG_SYN) {
// TODO: What if the packet also has data?
+ if( Connection->LastACKSequence != Connection->NextSequenceRcv )
+ TCP_INT_SendACK(Connection);
Connection->NextSequenceRcv = ntohl(Header->SequenceNumber);
+ Connection->LastACKSequence = Connection->NextSequenceRcv;
}
// Ackowledge a sent packet
// Get length of data
dataLen = Length - (Header->DataOffset>>4)*4;
LOG("dataLen = %i", dataLen);
+ Log_Debug("TCP", "State %i, dataLen = %x", Connection->State, dataLen);
//
// State Machine
if( Header->Flags & TCP_FLAG_SYN )
{
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);
}
else
{
Log_Log("TCP", "ACKing SYN");
Connection->State = TCP_ST_SYN_RCVD;
}
+ 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 );
}
break;
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;
}
Connection->NextSequenceRcv ++; // TODO: Is this right? (empty packet counts as one byte)
Log_Log("TCP", "Empty Packet, inc and ACK the current sequence number");
+ TCP_INT_SendACK(Connection);
+ #if 0
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 );
+ #endif
return ;
}
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
dataLen
);
if(rv != 0) {
+ Log_Notice("TCP", "TCP_INT_AppendRecieved rv %i", rv);
break;
}
LOG("0x%08x += %i", Connection->NextSequenceRcv, dataLen);
// 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
- // TODO: Implement delayed ACK sending
- 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_SYN; // Eliminate all flags save for SYN
- Header->Flags |= TCP_FLAG_ACK; // Add ACK
- LOG("TCP", "Sending ACK for 0x%08x", Connection->NextSequenceRcv);
- TCP_SendPacket( Connection, Header, 0, NULL );
- //Connection->NextSequenceSend ++;
+
+ #if 1
+ // - Only send an ACK if we've had a burst
+ if( Connection->NextSequenceRcv > (Uint32)(TCP_DACK_THRESHOLD + Connection->LastACKSequence) )
+ {
+ TCP_INT_SendACK(Connection);
+ // - 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);
+ #endif
}
// Check if the packet is in window
else if( WrapBetween(Connection->NextSequenceRcv, sequence_num,
{
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);
- // TODO: Spec says we should send an empty ACK with the current state
+ // Spec says we should send an empty ACK with the current state
+ TCP_INT_SendACK(Connection);
}
break;
#endif
}
+void TCP_INT_SendACK(tTCPConnection *Connection)
+{
+ tTCPHeader hdr;
+ // ACK Packet
+ hdr.DataOffset = (sizeof(tTCPHeader)/4) << 4;
+ hdr.DestPort = htons(Connection->RemotePort);
+ hdr.SourcePort = htons(Connection->LocalPort);
+ hdr.AcknowlegementNumber = htonl(Connection->NextSequenceRcv);
+ hdr.SequenceNumber = htonl(Connection->NextSequenceSend);
+ hdr.WindowSize = htons(TCP_WINDOW_SIZE);
+ 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);
+ TCP_SendPacket( Connection, &hdr, 0, NULL );
+ //Connection->NextSequenceSend ++;
+ Connection->LastACKSequence = Connection->NextSequenceRcv;
+}
+
/**
* \fn Uint16 TCP_GetUnusedPort()
* \brief Gets an unused port and allocates it
* \param Node Server node
* \param Pos Position (ignored)
*/
-char *TCP_Server_ReadDir(tVFS_Node *Node, int Pos)
+int TCP_Server_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX])
{
tTCPListener *srv = Node->ImplPtr;
tTCPConnection *conn;
- char *ret;
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;
LOG("srv->NewConnections = %p", srv->NewConnections);
LOG("srv->ConnectionsTail = %p", srv->ConnectionsTail);
- ret = malloc(9);
- itoa(ret, conn->Node.ImplInt, 16, 8, '0');
- Log_Log("TCP", "Thread %i got '%s'", Threads_GetTID(), ret);
- LEAVE('s', ret);
- return ret;
+ itoa(Dest, conn->Node.ImplInt, 16, 8, '0');
+ Log_Log("TCP", "Thread %i got connection '%s'", Threads_GetTID(), Dest);
+ LEAVE('i', 0);
+ return 0;
}
/**
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->FuturePacketValidBytes = conn->FuturePacketData + TCP_WINDOW_SIZE;
#endif
+ conn->DeferredACKTimer = Time_AllocateTimer( (void(*)(void*)) TCP_INT_SendACK, conn);
+
SHORTLOCK(&glTCP_OutbountCons);
conn->Next = gTCP_OutbountCons;
gTCP_OutbountCons = conn;
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 );
// 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);
LEAVE('i', -1);
return -1;
}
+ // Wait
+ VFS_SelectNode(Node, VFS_SELECT_WRITE|VFS_SELECT_ERROR, NULL, "TCP_Client_Write");
+
do
{
int len = (rem < TCP_MAX_PACKET_SIZE) ? rem : TCP_MAX_PACKET_SIZE;
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);
}
while( conn->State == TCP_ST_FIN_WAIT1 ) Threads_Yield();
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('-');