-/*\r
- LUFA Library\r
- Copyright (C) Dean Camera, 2009.\r
- \r
- dean [at] fourwalledcubicle [dot] com\r
- www.fourwalledcubicle.com\r
-*/\r
-\r
-/*\r
- Copyright 2009 Dean Camera (dean [at] fourwalledcubicle [dot] com)\r
-\r
- Permission to use, copy, modify, and distribute this software\r
- and its documentation for any purpose and without fee is hereby\r
- granted, provided that the above copyright notice appear in all\r
- copies and that both that the copyright notice and this\r
- permission notice and warranty disclaimer appear in supporting\r
- documentation, and that the name of the author not be used in\r
- advertising or publicity pertaining to distribution of the\r
- software without specific, written prior permission.\r
-\r
- The author disclaim all warranties with regard to this\r
- software, including all implied warranties of merchantability\r
- and fitness. In no event shall the author be liable for any\r
- special, indirect or consequential damages or any damages\r
- whatsoever resulting from loss of use, data or profits, whether\r
- in an action of contract, negligence or other tortious action,\r
- arising out of or in connection with the use or performance of\r
- this software.\r
-*/\r
-\r
-/** \file\r
- *\r
- * Transmission Control Protocol (TCP) packet handling routines. This protocol handles the reliable in-order transmission\r
- * and reception of packets to and from devices on a network, to "ports" on the device. It is used in situations where data\r
- * delivery must be reliable and correct, e.g. HTTP, TELNET and most other non-streaming protocols.\r
- */\r
- \r
-#define INCLUDE_FROM_TCP_C\r
-#include "TCP.h"\r
-\r
-/* Global Variables: */\r
-/** Port state table array. This contains the current status of TCP ports in the device. To save on space, only open ports are\r
- * stored - closed ports may be overwritten at any time, and the system will assume any ports not present in the array are closed. This\r
- * allows for MAX_OPEN_TCP_PORTS to be less than the number of ports used by the application if desired.\r
- */\r
-TCP_PortState_t PortStateTable[MAX_OPEN_TCP_PORTS];\r
-\r
-/** Connection state table array. This contains the current status of TCP connections in the device. To save on space, only active\r
- * (non-closed) connections are stored - closed connections may be overwritten at any time, and the system will assume any connections\r
- * not present in the array are closed.\r
- */\r
-TCP_ConnectionState_t ConnectionStateTable[MAX_TCP_CONNECTIONS];\r
-\r
-\r
-/** Task to handle the calling of each registered application's callback function, to process and generate TCP packets at the application\r
- * level. If an application produces a response, this task constructs the appropriate Ethernet frame and places it into the Ethernet OUT\r
- * buffer for later transmission.\r
- */\r
-void TCP_Task(void)\r
-{\r
- /* Run each application in sequence, to process incoming and generate outgoing packets */\r
- for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)\r
- {\r
- /* Find the corresponding port entry in the port table */\r
- for (uint8_t PTableEntry = 0; PTableEntry < MAX_OPEN_TCP_PORTS; PTableEntry++)\r
- {\r
- /* Run the application handler for the port */\r
- if ((PortStateTable[PTableEntry].Port == ConnectionStateTable[CSTableEntry].Port) && \r
- (PortStateTable[PTableEntry].State == TCP_Port_Open))\r
- {\r
- PortStateTable[PTableEntry].ApplicationHandler(&ConnectionStateTable[CSTableEntry],\r
- &ConnectionStateTable[CSTableEntry].Info.Buffer);\r
- }\r
- }\r
- }\r
- \r
- /* Bail out early if there is already a frame waiting to be sent in the Ethernet OUT buffer */\r
- if (FrameOUT.FrameInBuffer)\r
- return;\r
- \r
- /* Send response packets from each application as the TCP packet buffers are filled by the applications */\r
- for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)\r
- {\r
- /* For each completely received packet, pass it along to the listening application */\r
- if ((ConnectionStateTable[CSTableEntry].Info.Buffer.Direction == TCP_PACKETDIR_OUT) &&\r
- (ConnectionStateTable[CSTableEntry].Info.Buffer.Ready))\r
- {\r
- Ethernet_Frame_Header_t* FrameOUTHeader = (Ethernet_Frame_Header_t*)&FrameOUT.FrameData;\r
- IP_Header_t* IPHeaderOUT = (IP_Header_t*)&FrameOUT.FrameData[sizeof(Ethernet_Frame_Header_t)];\r
- TCP_Header_t* TCPHeaderOUT = (TCP_Header_t*)&FrameOUT.FrameData[sizeof(Ethernet_Frame_Header_t) +\r
- sizeof(IP_Header_t)];\r
- void* TCPDataOUT = &FrameOUT.FrameData[sizeof(Ethernet_Frame_Header_t) +\r
- sizeof(IP_Header_t) +\r
- sizeof(TCP_Header_t)];\r
-\r
- uint16_t PacketSize = ConnectionStateTable[CSTableEntry].Info.Buffer.Length;\r
-\r
- /* Fill out the TCP data */\r
- TCPHeaderOUT->SourcePort = ConnectionStateTable[CSTableEntry].Port;\r
- TCPHeaderOUT->DestinationPort = ConnectionStateTable[CSTableEntry].RemotePort;\r
- TCPHeaderOUT->SequenceNumber = SwapEndian_32(ConnectionStateTable[CSTableEntry].Info.SequenceNumberOut);\r
- TCPHeaderOUT->AcknowledgmentNumber = SwapEndian_32(ConnectionStateTable[CSTableEntry].Info.SequenceNumberIn);\r
- TCPHeaderOUT->DataOffset = (sizeof(TCP_Header_t) / sizeof(uint32_t));\r
- TCPHeaderOUT->WindowSize = SwapEndian_16(TCP_WINDOW_SIZE);\r
-\r
- TCPHeaderOUT->Flags = TCP_FLAG_ACK;\r
- TCPHeaderOUT->UrgentPointer = 0;\r
- TCPHeaderOUT->Checksum = 0;\r
- TCPHeaderOUT->Reserved = 0;\r
-\r
- memcpy(TCPDataOUT, ConnectionStateTable[CSTableEntry].Info.Buffer.Data, PacketSize);\r
- \r
- ConnectionStateTable[CSTableEntry].Info.SequenceNumberOut += PacketSize;\r
-\r
- TCPHeaderOUT->Checksum = TCP_Checksum16(TCPHeaderOUT, ServerIPAddress,\r
- ConnectionStateTable[CSTableEntry].RemoteAddress,\r
- (sizeof(TCP_Header_t) + PacketSize));\r
-\r
- PacketSize += sizeof(TCP_Header_t);\r
-\r
- /* Fill out the response IP header */\r
- IPHeaderOUT->TotalLength = SwapEndian_16(sizeof(IP_Header_t) + PacketSize);\r
- IPHeaderOUT->TypeOfService = 0;\r
- IPHeaderOUT->HeaderLength = (sizeof(IP_Header_t) / sizeof(uint32_t));\r
- IPHeaderOUT->Version = 4;\r
- IPHeaderOUT->Flags = 0;\r
- IPHeaderOUT->FragmentOffset = 0;\r
- IPHeaderOUT->Identification = 0;\r
- IPHeaderOUT->HeaderChecksum = 0;\r
- IPHeaderOUT->Protocol = PROTOCOL_TCP;\r
- IPHeaderOUT->TTL = DEFAULT_TTL;\r
- IPHeaderOUT->SourceAddress = ServerIPAddress;\r
- IPHeaderOUT->DestinationAddress = ConnectionStateTable[CSTableEntry].RemoteAddress;\r
- \r
- IPHeaderOUT->HeaderChecksum = Ethernet_Checksum16(IPHeaderOUT, sizeof(IP_Header_t));\r
- \r
- PacketSize += sizeof(IP_Header_t);\r
- \r
- /* Fill out the response Ethernet frame header */\r
- FrameOUTHeader->Source = ServerMACAddress;\r
- FrameOUTHeader->Destination = (MAC_Address_t){{0x02, 0x00, 0x02, 0x00, 0x02, 0x00}};\r
- FrameOUTHeader->EtherType = SwapEndian_16(ETHERTYPE_IPV4);\r
-\r
- PacketSize += sizeof(Ethernet_Frame_Header_t);\r
-\r
- /* Set the response length in the buffer and indicate that a response is ready to be sent */\r
- FrameOUT.FrameLength = PacketSize;\r
- FrameOUT.FrameInBuffer = true;\r
- \r
- ConnectionStateTable[CSTableEntry].Info.Buffer.Ready = false;\r
- \r
- break;\r
- }\r
- }\r
-}\r
-\r
-/** Initializes the TCP protocol handler, clearing the port and connection state tables. This must be called before TCP packets are\r
- * processed.\r
- */\r
-void TCP_Init(void)\r
-{\r
- /* Initialize the port state table with all CLOSED entries */\r
- for (uint8_t PTableEntry = 0; PTableEntry < MAX_OPEN_TCP_PORTS; PTableEntry++)\r
- PortStateTable[PTableEntry].State = TCP_Port_Closed;\r
-\r
- /* Initialize the connection table with all CLOSED entries */\r
- for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)\r
- ConnectionStateTable[CSTableEntry].State = TCP_Connection_Closed;\r
-}\r
-\r
-/** Sets the state and callback handler of the given port, specified in big endian to the given state.\r
- *\r
- * \param[in] Port Port whose state and callback function to set, specified in big endian\r
- * \param[in] State New state of the port, a value from the TCP_PortStates_t enum\r
- * \param[in] Handler Application callback handler for the port\r
- *\r
- * \return Boolean true if the port state was set, false otherwise (no more space in the port state table)\r
- */\r
-bool TCP_SetPortState(uint16_t Port, uint8_t State, void (*Handler)(TCP_ConnectionState_t*, TCP_ConnectionBuffer_t*))\r
-{\r
- /* Note, Port number should be specified in BIG endian to simplify network code */\r
-\r
- /* Check to see if the port entry is already in the port state table */\r
- for (uint8_t PTableEntry = 0; PTableEntry < MAX_TCP_CONNECTIONS; PTableEntry++)\r
- {\r
- /* Find existing entry for the port in the table, update it if found */\r
- if (PortStateTable[PTableEntry].Port == Port)\r
- {\r
- PortStateTable[PTableEntry].State = State;\r
- PortStateTable[PTableEntry].ApplicationHandler = Handler;\r
- return true;\r
- }\r
- }\r
-\r
- /* Check if trying to open the port -- if so we need to find an unused (closed) entry and replace it */\r
- if (State == TCP_Port_Open)\r
- {\r
- for (uint8_t PTableEntry = 0; PTableEntry < MAX_TCP_CONNECTIONS; PTableEntry++)\r
- {\r
- /* Find a closed port entry in the table, change it to the given port and state */\r
- if (PortStateTable[PTableEntry].State == TCP_Port_Closed)\r
- {\r
- PortStateTable[PTableEntry].Port = Port;\r
- PortStateTable[PTableEntry].State = State;\r
- PortStateTable[PTableEntry].ApplicationHandler = Handler;\r
- return true;\r
- }\r
- }\r
- \r
- /* Port not in table and no room to add it, return failure */\r
- return false;\r
- }\r
- else\r
- {\r
- /* Port not in table but trying to close it, so operation successful */\r
- return true;\r
- }\r
-}\r
-\r
-/** Retrieves the current state of a given TCP port, specified in big endian.\r
- *\r
- * \param[in] Port TCP port whose state is to be retrieved, given in big-endian\r
- *\r
- * \return A value from the TCP_PortStates_t enum\r
- */\r
-uint8_t TCP_GetPortState(uint16_t Port)\r
-{\r
- /* Note, Port number should be specified in BIG endian to simplify network code */\r
-\r
- for (uint8_t PTableEntry = 0; PTableEntry < MAX_TCP_CONNECTIONS; PTableEntry++)\r
- {\r
- /* Find existing entry for the port in the table, return the port status if found */\r
- if (PortStateTable[PTableEntry].Port == Port)\r
- return PortStateTable[PTableEntry].State;\r
- }\r
- \r
- /* Port not in table, assume closed */\r
- return TCP_Port_Closed;\r
-}\r
-\r
-/** Sets the connection state of the given port, remote address and remote port to the given TCP connection state. If the\r
- * connection exists in the connection state table it is updated, otherwise it is created if possible.\r
- *\r
- * \param[in] Port TCP port of the connection on the device, specified in big endian\r
- * \param[in] RemoteAddress Remote protocol IP address of the connected device\r
- * \param[in] RemotePort TCP port of the remote device in the connection, specified in big endian\r
- * \param[in] State TCP connection state, a value from the TCP_ConnectionStates_t enum\r
- *\r
- * \return Boolean true if the connection was updated or created, false otherwise (no more space in the connection state table)\r
- */\r
-bool TCP_SetConnectionState(uint16_t Port, IP_Address_t RemoteAddress, uint16_t RemotePort, uint8_t State)\r
-{\r
- /* Note, Port number should be specified in BIG endian to simplify network code */\r
-\r
- for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)\r
- {\r
- /* Find port entry in the table */\r
- if ((ConnectionStateTable[CSTableEntry].Port == Port) &&\r
- IP_COMPARE(&ConnectionStateTable[CSTableEntry].RemoteAddress, &RemoteAddress) &&\r
- ConnectionStateTable[CSTableEntry].RemotePort == RemotePort)\r
- {\r
- ConnectionStateTable[CSTableEntry].State = State;\r
- return true;\r
- }\r
- }\r
- \r
- for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)\r
- {\r
- /* Find empty entry in the table */\r
- if (ConnectionStateTable[CSTableEntry].State == TCP_Connection_Closed)\r
- {\r
- ConnectionStateTable[CSTableEntry].Port = Port;\r
- ConnectionStateTable[CSTableEntry].RemoteAddress = RemoteAddress; \r
- ConnectionStateTable[CSTableEntry].RemotePort = RemotePort;\r
- ConnectionStateTable[CSTableEntry].State = State;\r
- return true;\r
- }\r
- }\r
- \r
- return false;\r
-}\r
-\r
-/** Retrieves the current state of a given TCP connection to a host.\r
- *\r
- * \param[in] Port TCP port on the device in the connection, specified in big endian\r
- * \param[in] RemoteAddress Remote protocol IP address of the connected host\r
- * \param[in] RemotePort Remote TCP port of the connected host, specified in big endian\r
- *\r
- * \return A value from the TCP_ConnectionStates_t enum\r
- */\r
-uint8_t TCP_GetConnectionState(uint16_t Port, IP_Address_t RemoteAddress, uint16_t RemotePort)\r
-{\r
- /* Note, Port number should be specified in BIG endian to simplify network code */\r
-\r
- for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)\r
- {\r
- /* Find port entry in the table */\r
- if ((ConnectionStateTable[CSTableEntry].Port == Port) &&\r
- IP_COMPARE(&ConnectionStateTable[CSTableEntry].RemoteAddress, &RemoteAddress) &&\r
- ConnectionStateTable[CSTableEntry].RemotePort == RemotePort)\r
- \r
- {\r
- return ConnectionStateTable[CSTableEntry].State;\r
- }\r
- }\r
- \r
- return TCP_Connection_Closed;\r
-}\r
-\r
-/** Retrieves the connection info structure of a given connection to a host.\r
- *\r
- * \param[in] Port TCP port on the device in the connection, specified in big endian\r
- * \param[in] RemoteAddress Remote protocol IP address of the connected host\r
- * \param[in] RemotePort Remote TCP port of the connected host, specified in big endian\r
- *\r
- * \return ConnectionInfo structure of the connection if found, NULL otherwise\r
- */\r
-TCP_ConnectionInfo_t* TCP_GetConnectionInfo(uint16_t Port, IP_Address_t RemoteAddress, uint16_t RemotePort)\r
-{\r
- /* Note, Port number should be specified in BIG endian to simplify network code */\r
-\r
- for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)\r
- {\r
- /* Find port entry in the table */\r
- if ((ConnectionStateTable[CSTableEntry].Port == Port) &&\r
- IP_COMPARE(&ConnectionStateTable[CSTableEntry].RemoteAddress, &RemoteAddress) &&\r
- ConnectionStateTable[CSTableEntry].RemotePort == RemotePort)\r
- {\r
- return &ConnectionStateTable[CSTableEntry].Info;\r
- }\r
- }\r
- \r
- return NULL;\r
-}\r
-\r
-/** Processes a TCP packet inside an Ethernet frame, and writes the appropriate response\r
- * to the output Ethernet frame if one is created by a application handler.\r
- *\r
- * \param[in] IPHeaderInStart Pointer to the start of the incoming packet's IP header\r
- * \param[in] TCPHeaderInStart Pointer to the start of the incoming packet's TCP header\r
- * \param[out] TCPHeaderOutStart Pointer to the start of the outgoing packet's TCP header\r
- *\r
- * \return The number of bytes written to the out Ethernet frame if any, NO_RESPONSE if no\r
- * response was generated, NO_PROCESS if the packet processing was deferred until the\r
- * next Ethernet packet handler iteration\r
- */\r
-int16_t TCP_ProcessTCPPacket(void* IPHeaderInStart, void* TCPHeaderInStart, void* TCPHeaderOutStart)\r
-{\r
- IP_Header_t* IPHeaderIN = (IP_Header_t*)IPHeaderInStart;\r
- TCP_Header_t* TCPHeaderIN = (TCP_Header_t*)TCPHeaderInStart;\r
- TCP_Header_t* TCPHeaderOUT = (TCP_Header_t*)TCPHeaderOutStart;\r
-\r
- TCP_ConnectionInfo_t* ConnectionInfo;\r
- \r
- DecodeTCPHeader(TCPHeaderInStart);\r
-\r
- bool PacketResponse = false;\r
- \r
- /* Check if the destination port is open and allows incoming connections */\r
- if (TCP_GetPortState(TCPHeaderIN->DestinationPort) == TCP_Port_Open)\r
- {\r
- /* Detect SYN from host to start a connection */\r
- if (TCPHeaderIN->Flags & TCP_FLAG_SYN)\r
- TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort, TCP_Connection_Listen);\r
-\r
- /* Detect RST from host to abort existing connection */\r
- if (TCPHeaderIN->Flags & TCP_FLAG_RST)\r
- {\r
- if (TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort, TCP_Connection_Closed))\r
- {\r
- TCPHeaderOUT->Flags = (TCP_FLAG_RST | TCP_FLAG_ACK); \r
- PacketResponse = true; \r
- }\r
- }\r
- else\r
- {\r
- /* Process the incoming TCP packet based on the current connection state for the sender and port */\r
- switch (TCP_GetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort))\r
- {\r
- case TCP_Connection_Listen:\r
- if (TCPHeaderIN->Flags == TCP_FLAG_SYN)\r
- {\r
- /* SYN connection starts a connection with a peer */\r
- if (TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort, TCP_Connection_SYNReceived))\r
- {\r
- TCPHeaderOUT->Flags = (TCP_FLAG_SYN | TCP_FLAG_ACK); \r
-\r
- ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort);\r
-\r
- ConnectionInfo->SequenceNumberIn = (SwapEndian_32(TCPHeaderIN->SequenceNumber) + 1);\r
- ConnectionInfo->SequenceNumberOut = 0;\r
- ConnectionInfo->Buffer.InUse = false;\r
- }\r
- else\r
- {\r
- TCPHeaderOUT->Flags = TCP_FLAG_RST;\r
- }\r
- \r
- PacketResponse = true;\r
- }\r
- \r
- break;\r
- case TCP_Connection_SYNReceived:\r
- if (TCPHeaderIN->Flags == TCP_FLAG_ACK)\r
- {\r
- /* ACK during the connection process completes the connection to a peer */\r
-\r
- TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort, TCP_Connection_Established);\r
-\r
- ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort);\r
- \r
- ConnectionInfo->SequenceNumberOut++;\r
- }\r
- \r
- break;\r
- case TCP_Connection_Established:\r
- if (TCPHeaderIN->Flags == (TCP_FLAG_FIN | TCP_FLAG_ACK))\r
- {\r
- /* FIN ACK when connected to a peer starts the finalization process */\r
- \r
- TCPHeaderOUT->Flags = (TCP_FLAG_FIN | TCP_FLAG_ACK); \r
- PacketResponse = true;\r
- \r
- TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort, TCP_Connection_CloseWait);\r
-\r
- ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort);\r
-\r
- ConnectionInfo->SequenceNumberIn++;\r
- ConnectionInfo->SequenceNumberOut++;\r
- }\r
- else if ((TCPHeaderIN->Flags == TCP_FLAG_ACK) || (TCPHeaderIN->Flags == (TCP_FLAG_ACK | TCP_FLAG_PSH)))\r
- {\r
- ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort);\r
-\r
- /* Check if the buffer is currently in use either by a buffered data to send, or receive */ \r
- if ((ConnectionInfo->Buffer.InUse == false) && (ConnectionInfo->Buffer.Ready == false))\r
- { \r
- ConnectionInfo->Buffer.Direction = TCP_PACKETDIR_IN;\r
- ConnectionInfo->Buffer.InUse = true;\r
- ConnectionInfo->Buffer.Length = 0;\r
- }\r
- \r
- /* Check if the buffer has been claimed by us to read in data from the peer */\r
- if ((ConnectionInfo->Buffer.Direction == TCP_PACKETDIR_IN) &&\r
- (ConnectionInfo->Buffer.Length != TCP_WINDOW_SIZE))\r
- {\r
- uint16_t IPOffset = (IPHeaderIN->HeaderLength * sizeof(uint32_t));\r
- uint16_t TCPOffset = (TCPHeaderIN->DataOffset * sizeof(uint32_t));\r
- uint16_t DataLength = (SwapEndian_16(IPHeaderIN->TotalLength) - IPOffset - TCPOffset);\r
-\r
- /* Copy the packet data into the buffer */\r
- memcpy(&ConnectionInfo->Buffer.Data[ConnectionInfo->Buffer.Length],\r
- &((uint8_t*)TCPHeaderInStart)[TCPOffset],\r
- DataLength);\r
-\r
- ConnectionInfo->SequenceNumberIn += DataLength;\r
- ConnectionInfo->Buffer.Length += DataLength;\r
- \r
- /* Check if the buffer is full or if the PSH flag is set, if so indicate buffer ready */\r
- if ((!(TCP_WINDOW_SIZE - ConnectionInfo->Buffer.Length)) || (TCPHeaderIN->Flags & TCP_FLAG_PSH))\r
- {\r
- ConnectionInfo->Buffer.InUse = false;\r
- ConnectionInfo->Buffer.Ready = true;\r
-\r
- TCPHeaderOUT->Flags = TCP_FLAG_ACK;\r
- PacketResponse = true;\r
- }\r
- }\r
- else\r
- {\r
- /* Buffer is currently in use by the application, defer processing of the incoming packet */\r
- return NO_PROCESS;\r
- }\r
- }\r
- \r
- break;\r
- case TCP_Connection_Closing:\r
- ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort);\r
-\r
- TCPHeaderOUT->Flags = (TCP_FLAG_ACK | TCP_FLAG_FIN);\r
- PacketResponse = true;\r
- \r
- ConnectionInfo->Buffer.InUse = false;\r
- \r
- TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort, TCP_Connection_FINWait1);\r
-\r
- break;\r
- case TCP_Connection_FINWait1:\r
- if (TCPHeaderIN->Flags == (TCP_FLAG_FIN | TCP_FLAG_ACK))\r
- {\r
- ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort);\r
-\r
- TCPHeaderOUT->Flags = TCP_FLAG_ACK;\r
- PacketResponse = true;\r
-\r
- ConnectionInfo->SequenceNumberIn++;\r
- ConnectionInfo->SequenceNumberOut++;\r
- \r
- TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort, TCP_Connection_Closed);\r
- }\r
- else if (TCPHeaderIN->Flags == TCP_FLAG_ACK)\r
- {\r
- TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort, TCP_Connection_FINWait2);\r
- }\r
- \r
- break;\r
- case TCP_Connection_FINWait2:\r
- if (TCPHeaderIN->Flags == (TCP_FLAG_FIN | TCP_FLAG_ACK))\r
- {\r
- ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort);\r
-\r
- TCPHeaderOUT->Flags = TCP_FLAG_ACK;\r
- PacketResponse = true;\r
-\r
- ConnectionInfo->SequenceNumberIn++;\r
- ConnectionInfo->SequenceNumberOut++;\r
- \r
- TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort, TCP_Connection_Closed);\r
- }\r
- \r
- break;\r
- case TCP_Connection_CloseWait:\r
- if (TCPHeaderIN->Flags == TCP_FLAG_ACK)\r
- {\r
- TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort, TCP_Connection_Closed);\r
- }\r
- \r
- break;\r
- }\r
- }\r
- }\r
- else\r
- {\r
- /* Port is not open, indicate via a RST/ACK response to the sender */\r
- TCPHeaderOUT->Flags = (TCP_FLAG_RST | TCP_FLAG_ACK); \r
- PacketResponse = true;\r
- }\r
- \r
- /* Check if we need to respond to the sent packet */\r
- if (PacketResponse)\r
- {\r
- ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,\r
- TCPHeaderIN->SourcePort);\r
-\r
- TCPHeaderOUT->SourcePort = TCPHeaderIN->DestinationPort;\r
- TCPHeaderOUT->DestinationPort = TCPHeaderIN->SourcePort;\r
- TCPHeaderOUT->SequenceNumber = SwapEndian_32(ConnectionInfo->SequenceNumberOut);\r
- TCPHeaderOUT->AcknowledgmentNumber = SwapEndian_32(ConnectionInfo->SequenceNumberIn);\r
- TCPHeaderOUT->DataOffset = (sizeof(TCP_Header_t) / sizeof(uint32_t));\r
- \r
- if (!(ConnectionInfo->Buffer.InUse))\r
- TCPHeaderOUT->WindowSize = SwapEndian_16(TCP_WINDOW_SIZE);\r
- else\r
- TCPHeaderOUT->WindowSize = SwapEndian_16(TCP_WINDOW_SIZE - ConnectionInfo->Buffer.Length);\r
-\r
- TCPHeaderOUT->UrgentPointer = 0;\r
- TCPHeaderOUT->Checksum = 0;\r
- TCPHeaderOUT->Reserved = 0;\r
- \r
- TCPHeaderOUT->Checksum = TCP_Checksum16(TCPHeaderOUT, IPHeaderIN->DestinationAddress,\r
- IPHeaderIN->SourceAddress, sizeof(TCP_Header_t)); \r
-\r
- return sizeof(TCP_Header_t); \r
- }\r
-\r
- return NO_RESPONSE;\r
-}\r
-\r
-/** Calculates the appropriate TCP checksum, consisting of the addition of the one's compliment of each word,\r
- * complimented.\r
- *\r
- * \param[in] TCPHeaderOutStart Pointer to the start of the packet's outgoing TCP header\r
- * \param[in] SourceAddress Source protocol IP address of the outgoing IP header\r
- * \param[in] DestinationAddress Destination protocol IP address of the outgoing IP header\r
- * \param[in] TCPOutSize Size in bytes of the TCP data header and payload\r
- *\r
- * \return A 16-bit TCP checksum value\r
- */\r
-static uint16_t TCP_Checksum16(void* TCPHeaderOutStart, IP_Address_t SourceAddress,\r
- IP_Address_t DestinationAddress, uint16_t TCPOutSize)\r
-{\r
- uint32_t Checksum = 0;\r
- \r
- /* TCP/IP checksums are the addition of the one's compliment of each word including the IP pseudo-header,\r
- complimented */\r
- \r
- Checksum += ((uint16_t*)&SourceAddress)[0];\r
- Checksum += ((uint16_t*)&SourceAddress)[1];\r
- Checksum += ((uint16_t*)&DestinationAddress)[0];\r
- Checksum += ((uint16_t*)&DestinationAddress)[1];\r
- Checksum += SwapEndian_16(PROTOCOL_TCP);\r
- Checksum += SwapEndian_16(TCPOutSize);\r
-\r
- for (uint8_t CurrWord = 0; CurrWord < (TCPOutSize >> 1); CurrWord++)\r
- Checksum += ((uint16_t*)TCPHeaderOutStart)[CurrWord];\r
- \r
- if (TCPOutSize & 0x01)\r
- Checksum += (((uint16_t*)TCPHeaderOutStart)[TCPOutSize >> 1] & 0x00FF);\r
- \r
- while (Checksum & 0xFFFF0000)\r
- Checksum = ((Checksum & 0xFFFF) + (Checksum >> 16));\r
- \r
- return ~Checksum;\r
-}\r
+/*
+ LUFA Library
+ Copyright (C) Dean Camera, 2011.
+
+ dean [at] fourwalledcubicle [dot] com
+ www.lufa-lib.org
+*/
+
+/*
+ Copyright 2011 Dean Camera (dean [at] fourwalledcubicle [dot] com)
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that the copyright notice and this
+ permission notice and warranty disclaimer appear in supporting
+ documentation, and that the name of the author not be used in
+ advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission.
+
+ The author disclaim all warranties with regard to this
+ software, including all implied warranties of merchantability
+ and fitness. In no event shall the author be liable for any
+ special, indirect or consequential damages or any damages
+ whatsoever resulting from loss of use, data or profits, whether
+ in an action of contract, negligence or other tortious action,
+ arising out of or in connection with the use or performance of
+ this software.
+*/
+
+/** \file
+ *
+ * Transmission Control Protocol (TCP) packet handling routines. This protocol handles the reliable in-order transmission
+ * and reception of packets to and from devices on a network, to "ports" on the device. It is used in situations where data
+ * delivery must be reliable and correct, e.g. HTTP, TELNET and most other non-streaming protocols.
+ */
+
+#define INCLUDE_FROM_TCP_C
+#include "TCP.h"
+
+/** Port state table array. This contains the current status of TCP ports in the device. To save on space, only open ports are
+ * stored - closed ports may be overwritten at any time, and the system will assume any ports not present in the array are closed. This
+ * allows for MAX_OPEN_TCP_PORTS to be less than the number of ports used by the application if desired.
+ */
+TCP_PortState_t PortStateTable[MAX_OPEN_TCP_PORTS];
+
+/** Connection state table array. This contains the current status of TCP connections in the device. To save on space, only active
+ * (non-closed) connections are stored - closed connections may be overwritten at any time, and the system will assume any connections
+ * not present in the array are closed.
+ */
+TCP_ConnectionState_t ConnectionStateTable[MAX_TCP_CONNECTIONS];
+
+
+/** Task to handle the calling of each registered application's callback function, to process and generate TCP packets at the application
+ * level. If an application produces a response, this task constructs the appropriate Ethernet frame and places it into the Ethernet OUT
+ * buffer for later transmission.
+ */
+void TCP_Task(void)
+{
+ /* Run each application in sequence, to process incoming and generate outgoing packets */
+ for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)
+ {
+ /* Find the corresponding port entry in the port table */
+ for (uint8_t PTableEntry = 0; PTableEntry < MAX_OPEN_TCP_PORTS; PTableEntry++)
+ {
+ /* Run the application handler for the port */
+ if ((PortStateTable[PTableEntry].Port == ConnectionStateTable[CSTableEntry].Port) &&
+ (PortStateTable[PTableEntry].State == TCP_Port_Open))
+ {
+ PortStateTable[PTableEntry].ApplicationHandler(&ConnectionStateTable[CSTableEntry],
+ &ConnectionStateTable[CSTableEntry].Info.Buffer);
+ }
+ }
+ }
+
+ /* Bail out early if there is already a frame waiting to be sent in the Ethernet OUT buffer */
+ if (FrameOUT.FrameLength)
+ return;
+
+ /* Send response packets from each application as the TCP packet buffers are filled by the applications */
+ for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)
+ {
+ /* For each completely received packet, pass it along to the listening application */
+ if ((ConnectionStateTable[CSTableEntry].Info.Buffer.Direction == TCP_PACKETDIR_OUT) &&
+ (ConnectionStateTable[CSTableEntry].Info.Buffer.Ready))
+ {
+ Ethernet_Frame_Header_t* FrameOUTHeader = (Ethernet_Frame_Header_t*)&FrameOUT.FrameData;
+ IP_Header_t* IPHeaderOUT = (IP_Header_t*)&FrameOUT.FrameData[sizeof(Ethernet_Frame_Header_t)];
+ TCP_Header_t* TCPHeaderOUT = (TCP_Header_t*)&FrameOUT.FrameData[sizeof(Ethernet_Frame_Header_t) +
+ sizeof(IP_Header_t)];
+ void* TCPDataOUT = &FrameOUT.FrameData[sizeof(Ethernet_Frame_Header_t) +
+ sizeof(IP_Header_t) +
+ sizeof(TCP_Header_t)];
+
+ uint16_t PacketSize = ConnectionStateTable[CSTableEntry].Info.Buffer.Length;
+
+ /* Fill out the TCP data */
+ TCPHeaderOUT->SourcePort = ConnectionStateTable[CSTableEntry].Port;
+ TCPHeaderOUT->DestinationPort = ConnectionStateTable[CSTableEntry].RemotePort;
+ TCPHeaderOUT->SequenceNumber = SwapEndian_32(ConnectionStateTable[CSTableEntry].Info.SequenceNumberOut);
+ TCPHeaderOUT->AcknowledgmentNumber = SwapEndian_32(ConnectionStateTable[CSTableEntry].Info.SequenceNumberIn);
+ TCPHeaderOUT->DataOffset = (sizeof(TCP_Header_t) / sizeof(uint32_t));
+ TCPHeaderOUT->WindowSize = SwapEndian_16(TCP_WINDOW_SIZE);
+
+ TCPHeaderOUT->Flags = TCP_FLAG_ACK;
+ TCPHeaderOUT->UrgentPointer = 0;
+ TCPHeaderOUT->Checksum = 0;
+ TCPHeaderOUT->Reserved = 0;
+
+ memcpy(TCPDataOUT, ConnectionStateTable[CSTableEntry].Info.Buffer.Data, PacketSize);
+
+ ConnectionStateTable[CSTableEntry].Info.SequenceNumberOut += PacketSize;
+
+ TCPHeaderOUT->Checksum = TCP_Checksum16(TCPHeaderOUT, ServerIPAddress,
+ ConnectionStateTable[CSTableEntry].RemoteAddress,
+ (sizeof(TCP_Header_t) + PacketSize));
+
+ PacketSize += sizeof(TCP_Header_t);
+
+ /* Fill out the response IP header */
+ IPHeaderOUT->TotalLength = SwapEndian_16(sizeof(IP_Header_t) + PacketSize);
+ IPHeaderOUT->TypeOfService = 0;
+ IPHeaderOUT->HeaderLength = (sizeof(IP_Header_t) / sizeof(uint32_t));
+ IPHeaderOUT->Version = 4;
+ IPHeaderOUT->Flags = 0;
+ IPHeaderOUT->FragmentOffset = 0;
+ IPHeaderOUT->Identification = 0;
+ IPHeaderOUT->HeaderChecksum = 0;
+ IPHeaderOUT->Protocol = PROTOCOL_TCP;
+ IPHeaderOUT->TTL = DEFAULT_TTL;
+ IPHeaderOUT->SourceAddress = ServerIPAddress;
+ IPHeaderOUT->DestinationAddress = ConnectionStateTable[CSTableEntry].RemoteAddress;
+
+ IPHeaderOUT->HeaderChecksum = Ethernet_Checksum16(IPHeaderOUT, sizeof(IP_Header_t));
+
+ PacketSize += sizeof(IP_Header_t);
+
+ /* Fill out the response Ethernet frame header */
+ FrameOUTHeader->Source = ServerMACAddress;
+ FrameOUTHeader->Destination = (MAC_Address_t){{0x02, 0x00, 0x02, 0x00, 0x02, 0x00}};
+ FrameOUTHeader->EtherType = SwapEndian_16(ETHERTYPE_IPV4);
+
+ PacketSize += sizeof(Ethernet_Frame_Header_t);
+
+ /* Set the response length in the buffer and indicate that a response is ready to be sent */
+ FrameOUT.FrameLength = PacketSize;
+
+ ConnectionStateTable[CSTableEntry].Info.Buffer.Ready = false;
+
+ break;
+ }
+ }
+}
+
+/** Initializes the TCP protocol handler, clearing the port and connection state tables. This must be called before TCP packets are
+ * processed.
+ */
+void TCP_Init(void)
+{
+ /* Initialize the port state table with all CLOSED entries */
+ for (uint8_t PTableEntry = 0; PTableEntry < MAX_OPEN_TCP_PORTS; PTableEntry++)
+ PortStateTable[PTableEntry].State = TCP_Port_Closed;
+
+ /* Initialize the connection table with all CLOSED entries */
+ for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)
+ ConnectionStateTable[CSTableEntry].State = TCP_Connection_Closed;
+}
+
+/** Sets the state and callback handler of the given port, specified in big endian to the given state.
+ *
+ * \param[in] Port Port whose state and callback function to set, specified in big endian
+ * \param[in] State New state of the port, a value from the \ref TCP_PortStates_t enum
+ * \param[in] Handler Application callback handler for the port
+ *
+ * \return Boolean true if the port state was set, false otherwise (no more space in the port state table)
+ */
+bool TCP_SetPortState(const uint16_t Port,
+ const uint8_t State,
+ void (*Handler)(TCP_ConnectionState_t*, TCP_ConnectionBuffer_t*))
+{
+ /* Note, Port number should be specified in BIG endian to simplify network code */
+
+ /* Check to see if the port entry is already in the port state table */
+ for (uint8_t PTableEntry = 0; PTableEntry < MAX_TCP_CONNECTIONS; PTableEntry++)
+ {
+ /* Find existing entry for the port in the table, update it if found */
+ if (PortStateTable[PTableEntry].Port == Port)
+ {
+ PortStateTable[PTableEntry].State = State;
+ PortStateTable[PTableEntry].ApplicationHandler = Handler;
+ return true;
+ }
+ }
+
+ /* Check if trying to open the port -- if so we need to find an unused (closed) entry and replace it */
+ if (State == TCP_Port_Open)
+ {
+ for (uint8_t PTableEntry = 0; PTableEntry < MAX_TCP_CONNECTIONS; PTableEntry++)
+ {
+ /* Find a closed port entry in the table, change it to the given port and state */
+ if (PortStateTable[PTableEntry].State == TCP_Port_Closed)
+ {
+ PortStateTable[PTableEntry].Port = Port;
+ PortStateTable[PTableEntry].State = State;
+ PortStateTable[PTableEntry].ApplicationHandler = Handler;
+ return true;
+ }
+ }
+
+ /* Port not in table and no room to add it, return failure */
+ return false;
+ }
+ else
+ {
+ /* Port not in table but trying to close it, so operation successful */
+ return true;
+ }
+}
+
+/** Retrieves the current state of a given TCP port, specified in big endian.
+ *
+ * \param[in] Port TCP port whose state is to be retrieved, given in big-endian
+ *
+ * \return A value from the \ref TCP_PortStates_t enum
+ */
+uint8_t TCP_GetPortState(const uint16_t Port)
+{
+ /* Note, Port number should be specified in BIG endian to simplify network code */
+
+ for (uint8_t PTableEntry = 0; PTableEntry < MAX_TCP_CONNECTIONS; PTableEntry++)
+ {
+ /* Find existing entry for the port in the table, return the port status if found */
+ if (PortStateTable[PTableEntry].Port == Port)
+ return PortStateTable[PTableEntry].State;
+ }
+
+ /* Port not in table, assume closed */
+ return TCP_Port_Closed;
+}
+
+/** Sets the connection state of the given port, remote address and remote port to the given TCP connection state. If the
+ * connection exists in the connection state table it is updated, otherwise it is created if possible.
+ *
+ * \param[in] Port TCP port of the connection on the device, specified in big endian
+ * \param[in] RemoteAddress Remote protocol IP address of the connected device
+ * \param[in] RemotePort TCP port of the remote device in the connection, specified in big endian
+ * \param[in] State TCP connection state, a value from the \ref TCP_ConnectionStates_t enum
+ *
+ * \return Boolean true if the connection was updated or created, false otherwise (no more space in the connection state table)
+ */
+bool TCP_SetConnectionState(const uint16_t Port,
+ const IP_Address_t RemoteAddress,
+ const uint16_t RemotePort,
+ const uint8_t State)
+{
+ /* Note, Port number should be specified in BIG endian to simplify network code */
+
+ for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)
+ {
+ /* Find port entry in the table */
+ if ((ConnectionStateTable[CSTableEntry].Port == Port) &&
+ IP_COMPARE(&ConnectionStateTable[CSTableEntry].RemoteAddress, &RemoteAddress) &&
+ ConnectionStateTable[CSTableEntry].RemotePort == RemotePort)
+ {
+ ConnectionStateTable[CSTableEntry].State = State;
+ return true;
+ }
+ }
+
+ for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)
+ {
+ /* Find empty entry in the table */
+ if (ConnectionStateTable[CSTableEntry].State == TCP_Connection_Closed)
+ {
+ ConnectionStateTable[CSTableEntry].Port = Port;
+ ConnectionStateTable[CSTableEntry].RemoteAddress = RemoteAddress;
+ ConnectionStateTable[CSTableEntry].RemotePort = RemotePort;
+ ConnectionStateTable[CSTableEntry].State = State;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/** Retrieves the current state of a given TCP connection to a host.
+ *
+ * \param[in] Port TCP port on the device in the connection, specified in big endian
+ * \param[in] RemoteAddress Remote protocol IP address of the connected host
+ * \param[in] RemotePort Remote TCP port of the connected host, specified in big endian
+ *
+ * \return A value from the \ref TCP_ConnectionStates_t enum
+ */
+uint8_t TCP_GetConnectionState(const uint16_t Port,
+ const IP_Address_t RemoteAddress,
+ const uint16_t RemotePort)
+{
+ /* Note, Port number should be specified in BIG endian to simplify network code */
+
+ for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)
+ {
+ /* Find port entry in the table */
+ if ((ConnectionStateTable[CSTableEntry].Port == Port) &&
+ IP_COMPARE(&ConnectionStateTable[CSTableEntry].RemoteAddress, &RemoteAddress) &&
+ ConnectionStateTable[CSTableEntry].RemotePort == RemotePort)
+
+ {
+ return ConnectionStateTable[CSTableEntry].State;
+ }
+ }
+
+ return TCP_Connection_Closed;
+}
+
+/** Retrieves the connection info structure of a given connection to a host.
+ *
+ * \param[in] Port TCP port on the device in the connection, specified in big endian
+ * \param[in] RemoteAddress Remote protocol IP address of the connected host
+ * \param[in] RemotePort Remote TCP port of the connected host, specified in big endian
+ *
+ * \return ConnectionInfo structure of the connection if found, NULL otherwise
+ */
+TCP_ConnectionInfo_t* TCP_GetConnectionInfo(const uint16_t Port,
+ const IP_Address_t RemoteAddress,
+ const uint16_t RemotePort)
+{
+ /* Note, Port number should be specified in BIG endian to simplify network code */
+
+ for (uint8_t CSTableEntry = 0; CSTableEntry < MAX_TCP_CONNECTIONS; CSTableEntry++)
+ {
+ /* Find port entry in the table */
+ if ((ConnectionStateTable[CSTableEntry].Port == Port) &&
+ IP_COMPARE(&ConnectionStateTable[CSTableEntry].RemoteAddress, &RemoteAddress) &&
+ ConnectionStateTable[CSTableEntry].RemotePort == RemotePort)
+ {
+ return &ConnectionStateTable[CSTableEntry].Info;
+ }
+ }
+
+ return NULL;
+}
+
+/** Processes a TCP packet inside an Ethernet frame, and writes the appropriate response
+ * to the output Ethernet frame if one is created by a application handler.
+ *
+ * \param[in] IPHeaderInStart Pointer to the start of the incoming packet's IP header
+ * \param[in] TCPHeaderInStart Pointer to the start of the incoming packet's TCP header
+ * \param[out] TCPHeaderOutStart Pointer to the start of the outgoing packet's TCP header
+ *
+ * \return The number of bytes written to the out Ethernet frame if any, NO_RESPONSE if no
+ * response was generated, NO_PROCESS if the packet processing was deferred until the
+ * next Ethernet packet handler iteration
+ */
+int16_t TCP_ProcessTCPPacket(void* IPHeaderInStart,
+ void* TCPHeaderInStart,
+ void* TCPHeaderOutStart)
+{
+ IP_Header_t* IPHeaderIN = (IP_Header_t*)IPHeaderInStart;
+ TCP_Header_t* TCPHeaderIN = (TCP_Header_t*)TCPHeaderInStart;
+ TCP_Header_t* TCPHeaderOUT = (TCP_Header_t*)TCPHeaderOutStart;
+
+ TCP_ConnectionInfo_t* ConnectionInfo;
+
+ DecodeTCPHeader(TCPHeaderInStart);
+
+ bool PacketResponse = false;
+
+ /* Check if the destination port is open and allows incoming connections */
+ if (TCP_GetPortState(TCPHeaderIN->DestinationPort) == TCP_Port_Open)
+ {
+ /* Detect SYN from host to start a connection */
+ if (TCPHeaderIN->Flags & TCP_FLAG_SYN)
+ TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort, TCP_Connection_Listen);
+
+ /* Detect RST from host to abort existing connection */
+ if (TCPHeaderIN->Flags & TCP_FLAG_RST)
+ {
+ if (TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort, TCP_Connection_Closed))
+ {
+ TCPHeaderOUT->Flags = (TCP_FLAG_RST | TCP_FLAG_ACK);
+ PacketResponse = true;
+ }
+ }
+ else
+ {
+ /* Process the incoming TCP packet based on the current connection state for the sender and port */
+ switch (TCP_GetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort))
+ {
+ case TCP_Connection_Listen:
+ if (TCPHeaderIN->Flags == TCP_FLAG_SYN)
+ {
+ /* SYN connection starts a connection with a peer */
+ if (TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort, TCP_Connection_SYNReceived))
+ {
+ TCPHeaderOUT->Flags = (TCP_FLAG_SYN | TCP_FLAG_ACK);
+
+ ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort);
+
+ ConnectionInfo->SequenceNumberIn = (SwapEndian_32(TCPHeaderIN->SequenceNumber) + 1);
+ ConnectionInfo->SequenceNumberOut = 0;
+ ConnectionInfo->Buffer.InUse = false;
+ }
+ else
+ {
+ TCPHeaderOUT->Flags = TCP_FLAG_RST;
+ }
+
+ PacketResponse = true;
+ }
+
+ break;
+ case TCP_Connection_SYNReceived:
+ if (TCPHeaderIN->Flags == TCP_FLAG_ACK)
+ {
+ /* ACK during the connection process completes the connection to a peer */
+
+ TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort, TCP_Connection_Established);
+
+ ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort);
+
+ ConnectionInfo->SequenceNumberOut++;
+ }
+
+ break;
+ case TCP_Connection_Established:
+ if (TCPHeaderIN->Flags == (TCP_FLAG_FIN | TCP_FLAG_ACK))
+ {
+ /* FIN ACK when connected to a peer starts the finalization process */
+
+ TCPHeaderOUT->Flags = (TCP_FLAG_FIN | TCP_FLAG_ACK);
+ PacketResponse = true;
+
+ TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort, TCP_Connection_CloseWait);
+
+ ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort);
+
+ ConnectionInfo->SequenceNumberIn++;
+ ConnectionInfo->SequenceNumberOut++;
+ }
+ else if ((TCPHeaderIN->Flags == TCP_FLAG_ACK) || (TCPHeaderIN->Flags == (TCP_FLAG_ACK | TCP_FLAG_PSH)))
+ {
+ ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort);
+
+ /* Check if the buffer is currently in use either by a buffered data to send, or receive */
+ if ((ConnectionInfo->Buffer.InUse == false) && (ConnectionInfo->Buffer.Ready == false))
+ {
+ ConnectionInfo->Buffer.Direction = TCP_PACKETDIR_IN;
+ ConnectionInfo->Buffer.InUse = true;
+ ConnectionInfo->Buffer.Length = 0;
+ }
+
+ /* Check if the buffer has been claimed by us to read in data from the peer */
+ if ((ConnectionInfo->Buffer.Direction == TCP_PACKETDIR_IN) &&
+ (ConnectionInfo->Buffer.Length != TCP_WINDOW_SIZE))
+ {
+ uint16_t IPOffset = (IPHeaderIN->HeaderLength * sizeof(uint32_t));
+ uint16_t TCPOffset = (TCPHeaderIN->DataOffset * sizeof(uint32_t));
+ uint16_t DataLength = (SwapEndian_16(IPHeaderIN->TotalLength) - IPOffset - TCPOffset);
+
+ /* Copy the packet data into the buffer */
+ memcpy(&ConnectionInfo->Buffer.Data[ConnectionInfo->Buffer.Length],
+ &((uint8_t*)TCPHeaderInStart)[TCPOffset],
+ DataLength);
+
+ ConnectionInfo->SequenceNumberIn += DataLength;
+ ConnectionInfo->Buffer.Length += DataLength;
+
+ /* Check if the buffer is full or if the PSH flag is set, if so indicate buffer ready */
+ if ((!(TCP_WINDOW_SIZE - ConnectionInfo->Buffer.Length)) || (TCPHeaderIN->Flags & TCP_FLAG_PSH))
+ {
+ ConnectionInfo->Buffer.InUse = false;
+ ConnectionInfo->Buffer.Ready = true;
+
+ TCPHeaderOUT->Flags = TCP_FLAG_ACK;
+ PacketResponse = true;
+ }
+ }
+ else
+ {
+ /* Buffer is currently in use by the application, defer processing of the incoming packet */
+ return NO_PROCESS;
+ }
+ }
+
+ break;
+ case TCP_Connection_Closing:
+ ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort);
+
+ TCPHeaderOUT->Flags = (TCP_FLAG_ACK | TCP_FLAG_FIN);
+ PacketResponse = true;
+
+ ConnectionInfo->Buffer.InUse = false;
+
+ TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort, TCP_Connection_FINWait1);
+
+ break;
+ case TCP_Connection_FINWait1:
+ if (TCPHeaderIN->Flags == (TCP_FLAG_FIN | TCP_FLAG_ACK))
+ {
+ ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort);
+
+ TCPHeaderOUT->Flags = TCP_FLAG_ACK;
+ PacketResponse = true;
+
+ ConnectionInfo->SequenceNumberIn++;
+ ConnectionInfo->SequenceNumberOut++;
+
+ TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort, TCP_Connection_Closed);
+ }
+ else if (TCPHeaderIN->Flags == TCP_FLAG_ACK)
+ {
+ TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort, TCP_Connection_FINWait2);
+ }
+
+ break;
+ case TCP_Connection_FINWait2:
+ if (TCPHeaderIN->Flags == (TCP_FLAG_FIN | TCP_FLAG_ACK))
+ {
+ ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort);
+
+ TCPHeaderOUT->Flags = TCP_FLAG_ACK;
+ PacketResponse = true;
+
+ ConnectionInfo->SequenceNumberIn++;
+ ConnectionInfo->SequenceNumberOut++;
+
+ TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort, TCP_Connection_Closed);
+ }
+
+ break;
+ case TCP_Connection_CloseWait:
+ if (TCPHeaderIN->Flags == TCP_FLAG_ACK)
+ {
+ TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort, TCP_Connection_Closed);
+ }
+
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* Port is not open, indicate via a RST/ACK response to the sender */
+ TCPHeaderOUT->Flags = (TCP_FLAG_RST | TCP_FLAG_ACK);
+ PacketResponse = true;
+ }
+
+ /* Check if we need to respond to the sent packet */
+ if (PacketResponse)
+ {
+ ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
+ TCPHeaderIN->SourcePort);
+
+ TCPHeaderOUT->SourcePort = TCPHeaderIN->DestinationPort;
+ TCPHeaderOUT->DestinationPort = TCPHeaderIN->SourcePort;
+ TCPHeaderOUT->SequenceNumber = SwapEndian_32(ConnectionInfo->SequenceNumberOut);
+ TCPHeaderOUT->AcknowledgmentNumber = SwapEndian_32(ConnectionInfo->SequenceNumberIn);
+ TCPHeaderOUT->DataOffset = (sizeof(TCP_Header_t) / sizeof(uint32_t));
+
+ if (!(ConnectionInfo->Buffer.InUse))
+ TCPHeaderOUT->WindowSize = SwapEndian_16(TCP_WINDOW_SIZE);
+ else
+ TCPHeaderOUT->WindowSize = SwapEndian_16(TCP_WINDOW_SIZE - ConnectionInfo->Buffer.Length);
+
+ TCPHeaderOUT->UrgentPointer = 0;
+ TCPHeaderOUT->Checksum = 0;
+ TCPHeaderOUT->Reserved = 0;
+
+ TCPHeaderOUT->Checksum = TCP_Checksum16(TCPHeaderOUT, IPHeaderIN->DestinationAddress,
+ IPHeaderIN->SourceAddress, sizeof(TCP_Header_t));
+
+ return sizeof(TCP_Header_t);
+ }
+
+ return NO_RESPONSE;
+}
+
+/** Calculates the appropriate TCP checksum, consisting of the addition of the one's compliment of each word,
+ * complimented.
+ *
+ * \param[in] TCPHeaderOutStart Pointer to the start of the packet's outgoing TCP header
+ * \param[in] SourceAddress Source protocol IP address of the outgoing IP header
+ * \param[in] DestinationAddress Destination protocol IP address of the outgoing IP header
+ * \param[in] TCPOutSize Size in bytes of the TCP data header and payload
+ *
+ * \return A 16-bit TCP checksum value
+ */
+static uint16_t TCP_Checksum16(void* TCPHeaderOutStart,
+ const IP_Address_t SourceAddress,
+ const IP_Address_t DestinationAddress,
+ uint16_t TCPOutSize)
+{
+ uint32_t Checksum = 0;
+
+ /* TCP/IP checksums are the addition of the one's compliment of each word including the IP pseudo-header,
+ complimented */
+
+ Checksum += ((uint16_t*)&SourceAddress)[0];
+ Checksum += ((uint16_t*)&SourceAddress)[1];
+ Checksum += ((uint16_t*)&DestinationAddress)[0];
+ Checksum += ((uint16_t*)&DestinationAddress)[1];
+ Checksum += SwapEndian_16(PROTOCOL_TCP);
+ Checksum += SwapEndian_16(TCPOutSize);
+
+ for (uint16_t CurrWord = 0; CurrWord < (TCPOutSize >> 1); CurrWord++)
+ Checksum += ((uint16_t*)TCPHeaderOutStart)[CurrWord];
+
+ if (TCPOutSize & 0x01)
+ Checksum += (((uint16_t*)TCPHeaderOutStart)[TCPOutSize >> 1] & 0x00FF);
+
+ while (Checksum & 0xFFFF0000)
+ Checksum = ((Checksum & 0xFFFF) + (Checksum >> 16));
+
+ return ~Checksum;
+}
+