X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=Modules%2FIPStack%2Ftcp.c;h=9c26767bc36533c0897df0de80a6117f7fb20731;hb=923a9dc473851ec2bb1c94021bbf139724e7e8a5;hp=9225ffafa4f9fb8e8a4ea6478a8bae1bc81267d2;hpb=b3fa9a08edcbc459bd8e9df73186e292470ebfc3;p=tpg%2Facess2.git diff --git a/Modules/IPStack/tcp.c b/Modules/IPStack/tcp.c index 9225ffaf..9c26767b 100644 --- a/Modules/IPStack/tcp.c +++ b/Modules/IPStack/tcp.c @@ -6,15 +6,17 @@ #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); @@ -46,8 +48,9 @@ Uint32 gaTCP_PortBitmap[0x800]; // === CODE === /** - * \fn void TCP_Initialise() * \brief Initialise the TCP Layer + * + * Registers the client and server files and the GetPacket callback */ void TCP_Initialise() { @@ -58,6 +61,7 @@ void TCP_Initialise() /** * \brief Open a connection to another host using TCP + * \param Conn Connection structure */ void TCP_StartConnection(tTCPConnection *Conn) { @@ -67,12 +71,12 @@ 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 ; } @@ -81,7 +85,7 @@ void TCP_StartConnection(tTCPConnection *Conn) * \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 ) { @@ -90,7 +94,7 @@ 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; @@ -99,14 +103,17 @@ void TCP_SendPacket( tTCPConnection *Conn, size_t Length, tTCPHeader *Data ) 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) { @@ -114,26 +121,34 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe tTCPListener *srv; tTCPConnection *conn; - Log("[TCP ] sizeof(tTCPHeader) = %i", sizeof(tTCPHeader)); - Log("[TCP ] DestPort = %i", ntohs(hdr->DestPort)); - Log("[TCP ] DestPort = %i", ntohs(hdr->DestPort)); - Log("[TCP ] SequenceNumber = %i", ntohl(hdr->SequenceNumber)); - Log("[TCP ] AcknowlegementNumber = %i", ntohl(hdr->AcknowlegementNumber)); + 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 ) @@ -143,16 +158,21 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe // 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)) @@ -160,16 +180,71 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe 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) - //TODO - - break; + if(hdr->Flags != TCP_FLAG_SYN) { + Log("[TCP ] Packet is not a SYN"); + return ; + } + + // TODO: Check for halfopen max + + conn = calloc(1, sizeof(tTCPConnection)); + conn->State = TCP_ST_HALFOPEN; + 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; + } + + conn->NextSequenceRcv = ntohl( hdr->SequenceNumber ) + 1; + conn->NextSequenceSend = rand(); + + // Create node + conn->Node.NumACLs = 1; + conn->Node.ACLs = &gVFS_ACL_EveryoneRW; + conn->Node.ImplInt = srv->NextID ++; + conn->Node.Read = TCP_Client_Read; + conn->Node.Write = TCP_Client_Write; + //conn->Node.Close = TCP_SrvConn_Close; + + // Hmm... Theoretically, this lock will never have to wait, + // 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); + 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 + hdr->Flags |= TCP_FLAG_ACK; + hdr->AcknowlegementNumber = htonl(conn->NextSequenceRcv); + hdr->SequenceNumber = htonl(conn->NextSequenceSend); + hdr->DestPort = hdr->SourcePort; + hdr->SourcePort = htons(srv->Port); + hdr->DataOffset = (sizeof(tTCPHeader)/4) << 4; + TCP_SendPacket( conn, sizeof(tTCPHeader), hdr ); + + return ; } } @@ -182,7 +257,7 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe 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)) @@ -190,17 +265,156 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe 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 ++; + } } /** @@ -270,6 +484,8 @@ tVFS_Node *TCP_Server_Init(tInterface *Interface) 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; @@ -298,13 +514,27 @@ char *TCP_Server_ReadDir(tVFS_Node *Node, int Pos) 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; } @@ -315,9 +545,32 @@ char *TCP_Server_ReadDir(tVFS_Node *Node, int Pos) */ 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; @@ -348,6 +601,9 @@ int TCP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data) 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; @@ -359,6 +615,9 @@ void TCP_Server_Close(tVFS_Node *Node) } // --- Client +/** + * \brief Create a client node + */ tVFS_Node *TCP_Client_Init(tInterface *Interface) { tTCPConnection *conn = malloc( sizeof(tTCPConnection) ); @@ -385,9 +644,49 @@ tVFS_Node *TCP_Client_Init(tInterface *Interface) 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)