⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tcp.c

📁 LUFA (Lightweight USB Framework for AVRs) is my first foray into the world of USB. Originally based
💻 C
📖 第 1 页 / 共 2 页
字号:
}

/** Retrieves the connection info structure of a given connection to a host.
 *
 *  \param Port           TCP port on the device in the connection, specified in big endian
 *  \param RemoteAddress  Remote protocol IP address of the connected host
 *  \param 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(uint16_t Port, IP_Address_t RemoteAddress, 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 IPHeaderInStart    Pointer to the start of the incoming packet's IP header
 *  \param TCPHeaderInStart   Pointer to the start of the incoming packet's TCP header
 *  \param 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)
		{
			TCPHeaderOUT->Flags = (TCP_FLAG_RST | TCP_FLAG_ACK);				
			PacketResponse = true;
			
			TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress,
			                       TCPHeaderIN->SourcePort, TCP_Connection_Closed);			
		}
		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 when closed starts a connection with a peer */

						TCPHeaderOUT->Flags = (TCP_FLAG_SYN | TCP_FLAG_ACK);				
						PacketResponse      = true;
									
						TCP_SetConnectionState(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort,
											   TCP_Connection_SYNReceived);
											   
						ConnectionInfo = TCP_GetConnectionInfo(TCPHeaderIN->DestinationPort, IPHeaderIN->SourceAddress, TCPHeaderIN->SourcePort);

						ConnectionInfo->SequenceNumberIn  = (SwapEndian_32(TCPHeaderIN->SequenceNumber) + 1);
						ConnectionInfo->SequenceNumberOut = 0;
						ConnectionInfo->Buffer.InUse      = false;
					}
					
					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 TCPHeaderOutStart  Pointer to the start of the packet's outgoing TCP header
 *  \param SourceAddress      Source protocol IP address of the outgoing IP header
 *  \param SourceAddress      DestinationAddress protocol IP address of the outgoing IP header
 *  \param 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, IP_Address_t SourceAddress,
                               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 (uint8_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;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -