diff options
| author | Jack Humbert <[email protected]> | 2017-07-07 11:55:23 -0400 | 
|---|---|---|
| committer | Jack Humbert <[email protected]> | 2017-07-07 11:55:23 -0400 | 
| commit | 60b30c036397cb5627fa374bb930794b225daa29 (patch) | |
| tree | e156c2d6b27f01db6985f2cc560c9c345d48fdac /Demos/Device/ClassDriver/RNDISEthernet/Lib/TCP.c | |
| download | qmk_firmware-60b30c036397cb5627fa374bb930794b225daa29.tar.gz qmk_firmware-60b30c036397cb5627fa374bb930794b225daa29.zip | |
Squashed 'lib/lufa/' content from commit 385d40300
git-subtree-dir: lib/lufa
git-subtree-split: 385d4030035dbaf41591309dbde47653bd03841b
Diffstat (limited to 'Demos/Device/ClassDriver/RNDISEthernet/Lib/TCP.c')
| -rw-r--r-- | Demos/Device/ClassDriver/RNDISEthernet/Lib/TCP.c | 632 | 
1 files changed, 632 insertions, 0 deletions
| diff --git a/Demos/Device/ClassDriver/RNDISEthernet/Lib/TCP.c b/Demos/Device/ClassDriver/RNDISEthernet/Lib/TCP.c new file mode 100644 index 0000000000..dcc527aa97 --- /dev/null +++ b/Demos/Device/ClassDriver/RNDISEthernet/Lib/TCP.c @@ -0,0 +1,632 @@ +/* +             LUFA Library +     Copyright (C) Dean Camera, 2017. + +  dean [at] fourwalledcubicle [dot] com +           www.lufa-lib.org +*/ + +/* +  Copyright 2017  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 disclaims 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_TCPTask(USB_ClassInfo_RNDIS_Device_t* const RNDISInterfaceInfo, +		         Ethernet_Frame_Info_t* const FrameOUT) +{ +	/* 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 \c true if the port state was set, \c 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_OPEN_TCP_PORTS; 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_OPEN_TCP_PORTS; 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_OPEN_TCP_PORTS; 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 \c true if the connection was updated or created, \c 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; +} + | 
