#include "ipv4.h"
#include "tcp.h"
-#define TCP_MIN_DYNPORT 0x1000
+#define TCP_MIN_DYNPORT 0xC000
#define TCP_MAX_HALFOPEN 1024 // Should be enough
// === PROTOTYPES ===
void TCP_Initialise();
void TCP_StartConnection(tTCPConnection *Conn);
-void TCP_SendPacket( tTCPConnection *Conn, size_t Length, tTCPHeader *Data );
+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);
+void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length);
+void TCP_INT_AppendRecieved(tTCPConnection *Connection, tTCPStoredPacket *Ptk);
+void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection);
Uint16 TCP_GetUnusedPort();
int TCP_AllocatePort(Uint16 Port);
int TCP_DeallocatePort(Uint16 Port);
// === CODE ===
/**
- * \fn void TCP_Initialise()
* \brief Initialise the TCP Layer
+ *
+ * Registers the client and server files and the GetPacket callback
*/
void TCP_Initialise()
{
/**
* \brief Open a connection to another host using TCP
+ * \param Conn Connection structure
*/
void TCP_StartConnection(tTCPConnection *Conn)
{
hdr.DestPort = Conn->RemotePort;
Conn->NextSequenceSend = rand();
hdr.SequenceNumber = Conn->NextSequenceSend;
- hdr.DataOffset = (sizeof(tTCPHeader)+3)/4;
+ hdr.DataOffset = (sizeof(tTCPHeader)/4) << 4;
hdr.Flags = TCP_FLAG_SYN;
hdr.WindowSize = 0; // TODO
hdr.Checksum = 0; // TODO
hdr.UrgentPointer = 0;
- // SEND PACKET
+
TCP_SendPacket( Conn, sizeof(tTCPHeader), &hdr );
return ;
}
* \brief Sends a packet from the specified connection, calculating the checksums
* \param Conn Connection
* \param Length Length of data
- * \param Data Packet data
+ * \param Data Packet data (cast as a TCP Header)
*/
void TCP_SendPacket( tTCPConnection *Conn, size_t Length, tTCPHeader *Data )
{
switch( Conn->Interface->Type )
{
case 4: // Append IPv4 Pseudo Header
- buflen = 4 + 4 + 4 + ((Length+1)&1);
+ buflen = 4 + 4 + 4 + ((Length+1)&~1);
buf = malloc( buflen );
buf[0] = Conn->Interface->IP4.Address.L;
buf[1] = Conn->RemoteIP.v4.L;
memcpy( &buf[3], Data, Length );
Data->Checksum = IPv4_Checksum( buf, buflen );
free(buf);
- IPv4_SendPacket(Conn->Interface, Conn->RemoteIP.v4, IP4PROT_TCP, 0, buflen, buf);
+ IPv4_SendPacket(Conn->Interface, Conn->RemoteIP.v4, IP4PROT_TCP, 0, Length, Data);
break;
}
}
/**
- * \fn void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
* \brief Handles a packet from the IP Layer
+ * \param Interface Interface the packet arrived from
+ * \param Address Pointer to the addres structure
+ * \param Length Size of packet in bytes
+ * \param Buffer Packet data
*/
void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
{
tTCPListener *srv;
tTCPConnection *conn;
- Log("[TCP ] sizeof(tTCPHeader) = %i", sizeof(tTCPHeader));
- Log("[TCP ] SourcePort = %i", ntohs(hdr->SourcePort));
- Log("[TCP ] DestPort = %i", ntohs(hdr->DestPort));
+ Log("[TCP ] SourcePort = %i, DestPort = %i",
+ ntohs(hdr->SourcePort), ntohs(hdr->DestPort));
Log("[TCP ] SequenceNumber = 0x%x", ntohl(hdr->SequenceNumber));
Log("[TCP ] AcknowlegementNumber = 0x%x", ntohl(hdr->AcknowlegementNumber));
Log("[TCP ] DataOffset = %i", hdr->DataOffset >> 4);
Log("[TCP ] Flags = {");
- Log("[TCP ] CWR = %B", !!(hdr->Flags & TCP_FLAG_CWR));
- Log("[TCP ] ECE = %B", !!(hdr->Flags & TCP_FLAG_ECE));
- Log("[TCP ] URG = %B", !!(hdr->Flags & TCP_FLAG_URG));
- Log("[TCP ] ACK = %B", !!(hdr->Flags & TCP_FLAG_ACK));
- Log("[TCP ] PSH = %B", !!(hdr->Flags & TCP_FLAG_PSH));
- Log("[TCP ] RST = %B", !!(hdr->Flags & TCP_FLAG_RST));
- Log("[TCP ] SYN = %B", !!(hdr->Flags & TCP_FLAG_SYN));
- Log("[TCP ] FIN = %B", !!(hdr->Flags & TCP_FLAG_FIN));
+ Log("[TCP ] CWR = %B, ECE = %B",
+ !!(hdr->Flags & TCP_FLAG_CWR), !!(hdr->Flags & TCP_FLAG_ECE));
+ Log("[TCP ] URG = %B, ACK = %B",
+ !!(hdr->Flags & TCP_FLAG_URG), !!(hdr->Flags & TCP_FLAG_ACK));
+ Log("[TCP ] PSH = %B, RST = %B",
+ !!(hdr->Flags & TCP_FLAG_PSH), !!(hdr->Flags & TCP_FLAG_RST));
+ Log("[TCP ] SYN = %B, FIN = %B",
+ !!(hdr->Flags & TCP_FLAG_SYN), !!(hdr->Flags & TCP_FLAG_FIN));
Log("[TCP ] }");
Log("[TCP ] WindowSize = %i", htons(hdr->WindowSize));
Log("[TCP ] Checksum = 0x%x", htons(hdr->Checksum));
Log("[TCP ] UrgentPointer = 0x%x", htons(hdr->UrgentPointer));
+ if( Length > (hdr->DataOffset >> 4)*4 )
+ {
+ Debug_HexDump(
+ "[TCP ] Packet Data = ",
+ (Uint8*)hdr + (hdr->DataOffset >> 4)*4,
+ Length - (hdr->DataOffset >> 4)*4
+ );
+ }
+
// Check Servers
{
for( srv = gTCP_Listeners; srv; srv = srv->Next )
// Check the interface
if(srv->Interface && srv->Interface != Interface) continue;
// Check the destination port
- if(srv->Port != hdr->DestPort) continue;
-
+ if(srv->Port != htons(hdr->DestPort)) continue;
+
+ Log("[TCP ] Matches server %p", srv);
// Is this in an established connection?
for( conn = srv->Connections; conn; conn = conn->Next )
{
+ Log("[TCP ] conn->Interface(%p) == Interface(%p)",
+ conn->Interface, Interface);
// Check that it is coming in on the same interface
if(conn->Interface != Interface) continue;
// Check Source Port
- if(conn->RemotePort != hdr->SourcePort) continue;
+ Log("[TCP ] conn->RemotePort(%i) == hdr->SourcePort(%i)",
+ conn->RemotePort, ntohs(hdr->SourcePort));
+ if(conn->RemotePort != ntohs(hdr->SourcePort)) continue;
// Check Source IP
if(conn->Interface->Type == 6 && !IP6_EQU(conn->RemoteIP.v6, *(tIPv6*)Address))
if(conn->Interface->Type == 4 && !IP4_EQU(conn->RemoteIP.v4, *(tIPv4*)Address))
continue;
+ Log("[TCP ] Matches connection %p", conn);
// We have a response!
- TCP_INT_HandleConnectionPacket(conn, hdr);
+ TCP_INT_HandleConnectionPacket(conn, hdr, Length);
return;
}
+ Log("[TCP ] Opening Connection");
// Open a new connection (well, check that it's a SYN)
if(hdr->Flags != TCP_FLAG_SYN) {
Log("[TCP ] Packet is not a SYN");
- continue;
+ return ;
}
+ // TODO: Check for halfopen max
+
conn = calloc(1, sizeof(tTCPConnection));
conn->State = TCP_ST_HALFOPEN;
conn->LocalPort = srv->Port;
- conn->RemotePort = hdr->SourcePort;
+ conn->RemotePort = ntohs(hdr->SourcePort);
conn->Interface = Interface;
switch(Interface->Type)
case 6: conn->RemoteIP.v6 = *(tIPv6*)Address; break;
}
- conn->NextSequenceRcv = ntohl( hdr->SequenceNumber );
+ conn->NextSequenceRcv = ntohl( hdr->SequenceNumber ) + 1;
conn->NextSequenceSend = rand();
// Create node
// as the interface is locked to the watching thread, and this
// runs in the watching thread. But, it's a good idea to have
// it, just in case
+ // Oh, wait, there is a case where a wildcard can be used
+ // (srv->Interface == NULL) so having the lock is a good idea
LOCK(&srv->lConnections);
- conn->Next = srv->Connections;
- srv->Connections = conn;
+ if( !srv->Connections )
+ srv->Connections = conn;
+ else
+ srv->ConnectionsTail->Next = conn;
+ srv->ConnectionsTail = conn;
+ if(!srv->NewConnections)
+ srv->NewConnections = conn;
RELEASE(&srv->lConnections);
// Send the SYN ACK
- Log("[TCP ] TODO: Sending SYN ACK");
hdr->Flags |= TCP_FLAG_ACK;
- hdr->AcknowlegementNumber = hdr->SequenceNumber;
- hdr->SequenceNumber = conn->NextSequenceSend;
+ hdr->AcknowlegementNumber = htonl(conn->NextSequenceRcv);
+ hdr->SequenceNumber = htonl(conn->NextSequenceSend);
hdr->DestPort = hdr->SourcePort;
- hdr->SourcePort = srv->Port;
+ hdr->SourcePort = htons(srv->Port);
+ hdr->DataOffset = (sizeof(tTCPHeader)/4) << 4;
TCP_SendPacket( conn, sizeof(tTCPHeader), hdr );
- break;
+ return ;
}
}
if(conn->Interface != Interface) continue;
// Check Source Port
- if(conn->RemotePort != hdr->SourcePort) continue;
+ if(conn->RemotePort != ntohs(hdr->SourcePort)) continue;
// Check Source IP
if(conn->Interface->Type == 6 && !IP6_EQU(conn->RemoteIP.v6, *(tIPv6*)Address))
if(conn->Interface->Type == 4 && !IP4_EQU(conn->RemoteIP.v4, *(tIPv4*)Address))
continue;
- TCP_INT_HandleConnectionPacket(conn, hdr);
+ TCP_INT_HandleConnectionPacket(conn, hdr, Length);
+ return ;
}
}
+
+ Log("[TCP ] No Match");
}
/**
* \brief Handles a packet sent to a specific connection
+ * \param Connection TCP Connection pointer
+ * \param Header TCP Packet pointer
+ * \param Length Length of the packet
*/
-void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header)
+void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length)
+{
+ tTCPStoredPacket *pkt;
+ int dataLen;
+
+ Connection->State = TCP_ST_OPEN;
+ if(Header->Flags & TCP_FLAG_SYN) {
+ Connection->NextSequenceRcv = Header->SequenceNumber + 1;
+ }
+
+ // Get length of data
+ dataLen = Length - (Header->DataOffset>>4)*4;
+ Log("[TCP ] HandleConnectionPacket - dataLen = %i", dataLen);
+
+ if(Header->Flags & TCP_FLAG_ACK) {
+ // TODO: Process an ACKed Packet
+ Log("[TCP ] Conn %p, Packet 0x%x ACKed", Connection, Header->AcknowlegementNumber);
+ }
+
+ if(dataLen == 0) return ;
+
+ // NOTES:
+ // Flags
+ // PSH - Has Data?
+ // /NOTES
+
+ // Allocate and fill cached packet
+ pkt = malloc( dataLen + sizeof(tTCPStoredPacket) );
+ pkt->Next = NULL;
+ pkt->Sequence = ntohl(Header->SequenceNumber);
+ pkt->Length = dataLen;
+ memcpy(pkt->Data, (Uint8*)Header + (Header->DataOffset>>4)*4, dataLen);
+
+ // Is this packet the next expected packet?
+ if( pkt->Sequence != Connection->NextSequenceRcv )
+ {
+ tTCPStoredPacket *tmp, *prev = NULL;
+
+ Log("[TCP ] Out of sequence packet (0x%08x != 0x%08x)",
+ pkt->Sequence, Connection->NextSequenceRcv);
+
+ // No? Well, let's cache it and look at it later
+ LOCK( &Connection->lFuturePackets );
+ for(tmp = Connection->FuturePackets;
+ tmp;
+ prev = tmp, tmp = tmp->Next)
+ {
+ if(tmp->Sequence > pkt->Sequence) break;
+ }
+ if(prev)
+ prev->Next = pkt;
+ else
+ Connection->FuturePackets = pkt;
+ pkt->Next = tmp;
+ RELEASE( &Connection->lFuturePackets );
+ }
+ else
+ {
+ // Ooh, Goodie! Add it to the recieved list
+ TCP_INT_AppendRecieved(Connection, pkt);
+ Connection->NextSequenceRcv ++;
+
+ // 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.
+ TCP_INT_UpdateRecievedFromFuture(Connection);
+ }
+
+ // TODO: Check ACK code validity
+ Header->AcknowlegementNumber = ntohl(pkt->Sequence);
+ Header->SequenceNumber = ntohl(Connection->NextSequenceSend);
+ Header->Flags &= TCP_FLAG_SYN;
+ Header->Flags = TCP_FLAG_ACK;
+ TCP_SendPacket( Connection, sizeof(tTCPHeader), Header );
+}
+
+/**
+ * \brief Appends a packet to the recieved list
+ * \param Connection Connection structure
+ * \param Pkt Packet structure on heap
+ */
+void TCP_INT_AppendRecieved(tTCPConnection *Connection, tTCPStoredPacket *Pkt)
{
+ LOCK( &Connection->lRecievedPackets );
+ if(Connection->RecievedPackets)
+ {
+ Connection->RecievedPacketsTail->Next = Pkt;
+ Connection->RecievedPacketsTail = Pkt;
+ }
+ else
+ {
+ Connection->RecievedPackets = Pkt;
+ Connection->RecievedPacketsTail = Pkt;
+ }
+ RELEASE( &Connection->lRecievedPackets );
+}
+/**
+ * \brief Updates the connections recieved list from the future list
+ * \param Connection Connection structure
+ *
+ * Updates the recieved packets list with packets from the future (out
+ * of order) packets list that are now able to be added in direct
+ * sequence.
+ */
+void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection)
+{
+ tTCPStoredPacket *pkt, *prev;
+ for(;;)
+ {
+ prev = NULL;
+ // Look for the next expected packet in the cache.
+ LOCK( &Connection->lFuturePackets );
+ for(pkt = Connection->FuturePackets;
+ pkt && pkt->Sequence < Connection->NextSequenceRcv;
+ prev = pkt, pkt = pkt->Next);
+
+ // If we can't find the expected next packet, stop looking
+ if(!pkt || pkt->Sequence > Connection->NextSequenceRcv) {
+ RELEASE( &Connection->lFuturePackets );
+ return;
+ }
+
+ // Delete packet from future list
+ if(prev)
+ prev->Next = pkt->Next;
+ else
+ Connection->FuturePackets = pkt->Next;
+
+ // Release list
+ RELEASE( &Connection->lFuturePackets );
+
+ // Looks like we found one
+ TCP_INT_AppendRecieved(Connection, pkt);
+ Connection->NextSequenceRcv ++;
+ }
}
/**
srv->NextID = 0;
srv->Connections = NULL;
srv->Next = NULL;
+ srv->Node.Flags = VFS_FFLAG_DIRECTORY;
+ srv->Node.Size = -1;
srv->Node.ImplPtr = srv;
srv->Node.NumACLs = 1;
srv->Node.ACLs = &gVFS_ACL_EveryoneRW;
tTCPConnection *conn;
char *ret;
- while( srv->NewConnections == NULL ) Threads_Yield();
+ Log("[TCP ] Thread %i waiting for a connection", Threads_GetTID());
+ for(;;)
+ {
+ LOCK( &srv->lConnections );
+ if( srv->NewConnections != NULL ) break;
+ RELEASE( &srv->lConnections );
+ Threads_Yield();
+ continue;
+ }
+
+ // Increment the new list (the current connection is still on the
+ // normal list
conn = srv->NewConnections;
srv->NewConnections = conn->Next;
+
+ RELEASE( &srv->lConnections );
ret = malloc(9);
- itoa(ret, Node->ImplInt, 16, '0', 8);
+ itoa(ret, Node->ImplInt, 16, 8, '0');
+ Log("TCP_Server_ReadDir: RETURN '%s'", ret);
return ret;
}
*/
tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, char *Name)
{
- return NULL;
+ tTCPConnection *conn;
+ tTCPListener *srv = Node->ImplPtr;
+ char tmp[9];
+ int id = atoi(Name);
+
+ // Sanity Check
+ itoa(tmp, id, 16, 8, '0');
+ if(strcmp(tmp, Name) != 0) return NULL;
+
+ // Search
+ LOCK( &srv->lConnections );
+ for(conn = srv->Connections;
+ conn && conn->Node.ImplInt != id;
+ conn = conn->Next);
+ RELEASE( &srv->lConnections );
+
+ // If not found, ret NULL
+ if(!conn) return NULL;
+
+ // Return node
+ return &conn->Node;
}
+/**
+ * \brief Handle IOCtl calls
+ */
int TCP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data)
{
tTCPListener *srv = Node->ImplPtr;
srv->Port = TCP_GetUnusedPort();
else // Else, mark this as used
TCP_AllocatePort(srv->Port);
+
+ Log("[TCP ] Server %p listening on port %i", srv, srv->Port);
+
return srv->Port;
}
return 0;
}
// --- Client
+/**
+ * \brief Create a client node
+ */
tVFS_Node *TCP_Client_Init(tInterface *Interface)
{
tTCPConnection *conn = malloc( sizeof(tTCPConnection) );
return &conn->Node;
}
+/**
+ * \brief Wait for a packet and return it
+ * \note If \a Length is smaller than the size of the packet, the rest
+ * of the packet's data will be discarded.
+ */
Uint64 TCP_Client_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
{
- return 0;
+ tTCPConnection *conn = Node->ImplPtr;
+ tTCPStoredPacket *pkt;
+
+ Log("TCP_Client_Read: (Length=%i)", Length);
+
+ // Check if connection is open
+ if( conn->State != TCP_ST_OPEN ) return 0;
+
+ // Poll packets
+ for(;;)
+ {
+ // Lock list and check if there is a packet
+ LOCK( &conn->lRecievedPackets );
+ if( conn->RecievedPackets == NULL ) {
+ // If not, release the lock, yield and try again
+ RELEASE( &conn->lRecievedPackets );
+ Threads_Yield();
+ continue;
+ }
+
+ // Get packet pointer
+ pkt = conn->RecievedPackets;
+ conn->RecievedPackets = pkt->Next;
+ // Release the lock (we don't need it any more)
+ RELEASE( &conn->lRecievedPackets );
+
+ Log("TCP_Client_Read: pkt->Length = %i", pkt->Length);
+
+ // Copy Data
+ if(Length > pkt->Length) Length = pkt->Length;
+ memcpy(Buffer, pkt->Data, Length);
+
+ // Free packet and return
+ free(pkt);
+ return Length;
+ }
}
Uint64 TCP_Client_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)